
简介这是一份面向工业自动化工程师与PLC编程初学者的技术文档聚焦TIA博途中S7-1200与上位机通信时浮点数高低字节、高低字顺序相反导致读数异常的问题系统整理出四种可实现字节序调换的编程思路。压缩包共1个文件为docx格式大小约747KB以图文并茂的方式讲解间接寻址与片段访问、SWAP指令、AT变量覆盖、循环左移16位这四种方法并对WORD级调换所需的循环左移8位作了说明。文档按步骤给出FC块接口定义、SCL程序编写及在OB1中调用并连接数据块变量的完整过程读者可对照示例验证16#11112222与16#22221111的互换效果同时获得变量显示格式的设置提示与排错参考。目前已有2308人学习下载适合需要解决跨系统数据存储差异、希望比较不同方案可读性与适用场景的工程人员参考借鉴。1. 从一次 Modbus TCP 读数说起S7-1200 里的高低字节为什么总错位现场常见的一幕S7-1200 通过 Modbus TCP 去读第三方仪表的累计流量寄存器地址没错、功能码没错、连接也建起来了可读回来的 REAL 是 1.2e-38 这种明显不合理的值。把四个寄存器单独拎出来看每一位都对只是顺序被翻了个面。这类问题在 TIA 博途里排查不费劲解决也不费劲难的是先搞清楚该翻字节还是该翻字。S7-1200 内部按大端存放数据高字节在低地址而上位机、脚本语言和部分小端控制器按相反顺序解析。Modbus 协议规定寄存器是大端但 32 位数据跨两个寄存器时先高字还是先低字各家实现并不统一于是就有了 WORD 内字节颠倒、DWORD 内高低字颠倒、四字节整体反转这三种典型错位。下面按从现成指令到手工拼装、再到结构体覆盖和批量循环的顺序给出四种可复现的调换做法并说明各自适用的数据规模与坑在哪。2. 方法一用 SWAP 指令直接调换高低字节TIA 博途的指令树里SWAP 归在「基本指令 → 移动操作」下面是 S7-1200 上最省事的字节调换手段不占中间变量、不写逻辑拖一个盒子上来输入输出一接就完事。代价是它的能力被数据类型卡死只做字节层面的重排做不了字层面的重排这个边界后面单独说。2.1 SWAP 指令对不同数据类型到底做了什么很多人把 SWAP 当成万能翻转器接上去发现 DWORD 的结果还是不对就是因为没看清它按什么粒度动作。它只认输入引脚上标注的类型按该类型的字节长度做整段反转输入类型输入示例输出实际动作BYTE16#1216#12单字节无变化WORD16#123416#3412高低字节互换DWORD16#1122334416#44332211四字节整体反转注意 DWORD 那一行它反的是字节序不是高低字。16#11223344 的高低字调换结果应该是 16#33441122而 SWAP 给的是 16#44332211。把这两个数写在一起对比一次比记十遍定义管用。2.2 在 LAD/FBD 里接一个 SWAP 的最小步骤操作顺序如下每一步都可以在离线状态下完成下载前先编译一遍确认没有类型告警在 OB1 或自建 FC 里从右侧指令树把 SWAP 拖到网络中间。IN 端接要处理的变量例如某个 DWord 类型的 DB 变量或 M 区双字。OUT 端接结果变量类型必须与 IN 一致。点击指令盒上方的数据类型标注明确选 WORD 还是 DWORD别依赖默认值。SWAP 是纯组合逻辑没有使能端只要所在网络被扫描到就执行。所以不要把它放在会被条件跳过的分支后面还指望结果保持上一次的值——扫描不到就不会更新这个行为和带 EN 的指令完全不同。2.3 在 SCL 里调不到 SWAP 时怎么绕不同固件版本、不同编辑语言下SCL 编辑器里不一定能直接敲出 SWAP 这个函数名。稳妥做法是把 LAD 版本的逻辑封装进一个 FC接口声明成 Word 进、Word 出然后在 SCL 里当普通函数调用// FC ByteSwap16内部由一个 SWAP 指令实现 // 接口声明IN : Word ; OUT : Word // SCL 侧调用 #wResult : ByteSwap16(IN : #wRaw);这样逻辑只有一份LAD 和 SCL 两种语言都能复用代价是多一层调用开销对扫描周期极敏感的高频循环里不如直接用手工移位。参数说明IN 与 OUT 的类型必须严格一致WORD 进 WORD 出DWORD 进 DWORD 出。如果 IN 直接接 REAL 变量编译会报类型不匹配。想把 REAL 交给 SWAP 处理先把它 MOVE 给一个 DWORD 中转变量交换完再 MOVE 回 REAL——这两个 MOVE 都是按位拷贝不做数值转换千万不能误用 CONV 之类的转换指令否则浮点格式会被改掉数值直接失真。3. 方法二用移位和逻辑运算手工拼装高低字节与高低字SWAP 解决不了的场景很具体一是不方便调用外部 FC 的场合二是需要同时做字节与字两种调换的场合三是输入类型不确定、需要在同一段逻辑里参数化处理的场合。这时用手工移位拼装逻辑完全展开可控性最强。3.1 拆字原理掩码取位、移位归位、按位或拼合用移位做调换思路是把一个字拆成两半各自移到对面的位置上再拼回去。以 16 位数据为例低字节 原值 AND 16#00FF高字节 原值右移 8 位结果 低字节左移 8 位 OR 高字节。掩码的作用是隔离移位的左右是归位OR 是拼合三步缺一不可。省掉掩码会让高字节的残留位混进低字节位置结果看着像对、实则多出几个 bit。3.2 SCL 里实现 16 位高低字节调换FUNCTION SwapBytes16 : Word VAR_INPUT inVal : Word; // 待调换的 16 位数据 END_VAR VAR_TEMP loByte : Word; // 临时存放低字节 hiByte : Word; // 临时存放高字节 END_VAR BEGIN loByte : inVal AND 16#00FF; // 取出低 8 位 hiByte : SHR(IN : inVal, N : 8); // 高 8 位右移到低位 loByte : SHL(IN : loByte, N : 8); // 低 8 位左移到高位 SwapBytes16 : loByte OR hiByte; // 按位或拼成结果 END_FUNCTION逻辑说明SHR 和 SHL 都是无符号移位移出的位直接丢弃不循环回绕所以右移之后高位自然补 0不需要再做额外掩码清除。参数说明N 的取值范围是 0 到数据位宽取 16 或更大在编译阶段就会告警。输入如果声明成 Int 而不是 Word右移会带着符号位一起移动负数输入的结果会完全错误这是这一段最容易踩的坑类型栏里务必选无符号。3.3 扩展到 32 位高低字调换FUNCTION SwapWords32 : DWord VAR_INPUT inVal : DWord; END_VAR VAR_TEMP loWord : DWord; hiWord : DWord; END_VAR BEGIN loWord : inVal AND 16#0000FFFF; // 取出低 16 位 hiWord : SHR(IN : inVal, N : 16); // 高 16 位下移 loWord : SHL(IN : loWord, N : 16); // 低 16 位上移 SwapWords32 : loWord OR hiWord; // 拼合 END_FUNCTION关键是把 16 位当成整体来搬掩码 16#0000FFFF 保证高字不会被带进来。如果要同时做「字内字节交换 字间交换」先调 SwapWords32再把结果送进方法一的 SWAP两步串起来就能覆盖四字节的全部排法。3.4 两种调换的组合与 Modbus 常见排法对照抓到的四个寄存器属于哪种排法先手算一遍再决定调哪个函数比在程序里反复试快得多。以 16#11223344 为基准常见排法对应关系如下Modbus 常见排法字节顺序相对基准需要做的操作ABCD高字高字节在前不做处理DCBA整体反转SWAPDWORD 类型CDAB交换高低字SwapWords32BADC每个字内交换字节对两个字分别做字节交换这张表可以直接当排查清单先按基准手算目标值再用在线监控比对两种操作都不会白试。4. 方法三用 DB 里的 AT 覆盖把 DWORD 拆成字节数组重排前两种方法都在做位运算处理 REAL 时需要先转成整数类型再转回来容易出错。AT 覆盖换了个思路不做任何运算只是让同一段内存用两种类型去看改完位置后按原始位模式读出来浮点值自然正确。4.1 AT 覆盖的声明方式与前提条件声明格式是基础变量在前紧跟一行 AT 叠加视图两者占用空间必须严格相等// 在全局 DB dbComm 中声明该 DB 需关闭优化的块访问 rawValue : DWord; // 基础视图4 字节 rawBytes AT rawValue : Array[0..3] OF Byte; // 覆盖视图4 字节 rawWords AT rawValue : Array[0..1] OF Word; // 覆盖视图4 字节AT 覆盖要求 DB 关闭「优化的块访问」。在 DB 属性里取消该选项后变量才有固定偏移叠加视图才对得上。这是 S7-1200 上的硬约束不关就是编译报错没有绕过去的写法。4.2 用 AT 覆盖调换 REAL 的高低字// 前提dbComm 已关闭优化访问rawValue 中是 Modbus 读回的原始位模式 #tmpW : dbComm.rawWords[0]; // 暂存低地址处的字 dbComm.rawWords[0] : dbComm.rawWords[1]; // 高字写入低字位置 dbComm.rawWords[1] : #tmpW; // 原低字写入高字位置 // 此时 rawValue 作为 DWord 已经是调换后的位模式 #rResult : dbComm.rawValue; // 按 DWord 取出赋给 REAL 变量逻辑说明整个过程没有做任何数值运算纯粹按内存位置搬字节所以对 REAL 完全安全不会因为浮点格式被改动而丢精度。参数说明数组下标从 0 开始rawWords[0] 位于低地址。S7-1200 是大端存储低地址放的是高字所以第 0 个元素在数值上是高字。这个对应关系搞反了结果就是原样不动或者双重调换看着像没生效。4.3 AT 覆盖的类型匹配规则与三个坑叠加视图的类型不是随便配的长度必须完全相等基础类型合法覆盖类型非法示例DWordArray[0..3] OF Byte、Array[0..1] OF WordArray[0..7] OF ByteReal同上也可覆盖成 DWordArray[0..3] OF WordWordArray[0..1] OF ByteArray[0..3] OF Byte第一个坑AT 声明必须紧跟在被覆盖变量之后中间插入别的变量会导致偏移错位。第二个坑优化访问的 DB 不支持 AT。第三个坑数据类型长度必须严格相等差一个字节直接编译失败。提示项目里大量使用优化访问 DB又不想为了调换把优化关掉那就放弃 AT 覆盖改用方法二的移位或方法四的循环处理代价是多写几行位运算。5. 方法四用 FOR 循环批量调换高低字节与高低字前面三种都是针对单个变量的。报文一旦有几十上百个 16 位数据要调换逐个调函数既臃肿又难维护用 FOR 循环遍历数组是唯一现实的做法。5.1 用 FOR 循环处理整个 Word 数组// 入参awData 为 Word 数组iUpper 由 UPPER_BOUND 计算得到 FOR #i : 0 TO #iUpper DO #wTmp : #awData[#i]; #awData[#i] : SHL(IN : #wTmp AND 16#00FF, N : 8) OR SHR(IN : #wTmp, N : 8); END_FOR;逻辑说明循环体那一行做的事和方法二完全相同只是对每个元素重复一遍原地写回不额外占用存储区。参数说明循环上界不要写死数字用 LOWER_BOUND 和 UPPER_BOUND 指令取当前数组的边界数组长度改了下界也会跟着变。元素是 DWord 时循环体换成调用SwapWords32。循环次数大时把这段放在主循环里执行别塞进对实时性要求高的循环中断 OB否则会拉长扫描周期影响其他周期任务。5.2 用监控表和强制表验证调换结果写完别急着下结论先拿一组已知位模式验证。在监控表里强制一个 DWord 变量为 16#11223344观察调换后是不是 16#33441122。如果在线修改的值立刻被程序覆盖就把它放进只在首次扫描时执行的 MOVE或者临时注释掉调用。接着看 REAL 的显示值。调换正确时应该落在合理量程内比如 25.6、1234.5 这类如果显示成 1.2e-38 或一串 NaN 开头的内容说明排法还没对上回到第 3.4 节的表格重新对照一次。批量场景下更省事的做法是抽头尾两个元素单独比对确认循环边界没写错——常见的低级错误是上界写成元素个数结果多处理了一个不存在的元素触发数组越界。5.3 四种方法的选型对照方法适用数据量能否处理 REAL依赖优化访问设置典型场景SWAP 指令单个需 DWord 中转否单个寄存器字节反转移位逻辑单个到中等可以按 Word/DWord 处理否SCL 内联处理需要参数化AT 覆盖单个直接处理不丢精度是需关闭优化DB 中现成的 REAL 字段调换循环加数组大批量可以否整段报文重排选型时优先看两点数据是一批还是零星几个所在的 DB 有没有开优化访问。批量的走循环零星的走 SWAP 或移位DB 里现成的 REAL 字段且不介意关优化就用 AT 覆盖。三种调换动作和四种实现方式交叉组合基本能覆盖 S7-1200 上会遇到的全部字节序问题。本文还有配套的精品资源点击获取