问题从哪来
Modbus 规范把数据单元固定为 16 位寄存器,发送时高字节在前,这一点没有歧义。但现场物理量常常是 32 位:单精度浮点、累计流量、电能、双字计数器都要占两个连续寄存器,而规范并未规定这两个寄存器谁先出现。厂商各写各的,于是同一台设备在 A 软件里显示 25.5,在 B 软件里显示天文数字,这是工业现场最常见的低级错误之一。
基本信息
| 适用协议 | Modbus RTU / ASCII / TCP 规则相同 |
|---|---|
| 寄存器宽度 | 16 位,规范固定 |
| 字节序 | 大端,高字节先发,规范固定 |
| 字序 | 厂商自定,常见 ABCD / CDAB / BADC / DCBA |
| 易出问题时 | 32 位整数、单精度浮点、64 位量(占 4 个寄存器) |
典型使用场景
- 对接电表、流量计、称重仪表:这类设备大量用两个寄存器表达浮点值
- 自研脚本或组态软件读 PLC 的 REAL,需要确认两侧的字序约定
- 旧采集程序迁移后数值全错:老代码里隐含了字序假设,换库就暴露
- 排查读数异常:应先怀疑字序,再怀疑量纲换算和标定系数
- 编写通用驱动时把字序做成配置项,避免每接一台新设备改一次代码
四种常见排列
| 排列 | 含义 | 常见于 |
|---|---|---|
| ABCD | 高字在前,字内大端 | 多数 PLC 与西门子风格设备 |
| CDAB | 低字在前,字内大端 | 部分电表、流量计 |
| BADC | 高字在前,字内小端 | 少量仪表 |
| DCBA | 字节完全颠倒 | 个别变频器与老仪表 |
按字序解码
按字序解码
# 读到两个寄存器 A、B(各 16 位),字序决定拼接方式
raw_abcd = struct.pack('>HH', A, B) # ABCD:高字在前,再按大端解
value = struct.unpack('>f', raw_abcd)[0]
value_cdab = struct.unpack('>f', struct.pack('>HH', B, A))[0] # 先换字序
print(hex(A), hex(B), value) # 记录原始寄存器值,便于与设备面板比对判定字序最可靠的办法是实测:写入一个已知值(例如 100.0),把两个原始寄存器值抄下来逐字节比对;靠配置文件注释或旧文档猜,往往要浪费半天。
把两个 16 位寄存器拼成 32 位整数再套浮点解释,是常见的错误写法:整数拼接正确不代表浮点解释正确,两者必须分别验证。