本节摘要:点表写"40001",报文里却填 0——偏移 1 之差,是 Modbus 现场第一大坑。本节讲清 0-based 与 1-based 两种寻址、PLC 地址方言的换算,以及寄存器内 16 位数据的字节序陷阱。位置:2.1 认识了四类对象,本节处理"地址怎么写才对"。
Modbus 报文里的寄存器地址是 0 起始的偏移量(0x0000-0xFFFF);而 PLC/组态软件习惯用 1 起始的"40001"式地址。换算规则:
PLC 地址 40001 → 数据模型偏移 0(减 1) PLC 地址 40020 → 偏移 19(减 1) 3x 输入寄存器 30001 → 偏移 0,功能码 04 0x 线圈 00001 → 偏移 0,功能码 01
多数工具(组态软件、调试器)会替你换算,但手写报文或自己写驱动时,偏移差 1 是最常见的"能通但数据全错"的根源:功能码对、校验对、设备也应答了,读回来的却是隔壁寄存器的值——因为它把"读偏移 19"理解成"读第 20 个"。
某电能表点表节选(同一数据,三种写法):
| 数据 | PLC 写法 | 数据模型偏移 | 功能码 |
|---|---|---|---|
| 相电压 A | 40001 | 0 | 03 读保持 |
| 相电流 A | 40003 | 2 | 03 |
| 有功功率 | 40005 | 4 | 03 |
| 开关量输入状态 | 10001 | 0 | 02 读离散 |
注意功率占 2 个寄存器(4、5 两字合成 32 位浮点)——多寄存器数据跨越的地址段要一次读完,否则读到半截值。第 6 章会讲字节序与 32 位数据的完整拼法。
一个寄存器 16 位 = 2 字节。串口发送时先高字节后低字节(大端,Modbus 默认),但有些设备实现成低字节在前,或 32 位浮点的高低字对调。字节序错误的表现:单寄存器数值离谱(如 1000 变成 7720);32 位浮点完全乱掉。诊断方法:读一个已知值(如额定电压 220),把返回字节按高/低/高字/低字四种组合各解一次,看哪个落在合理范围。
把现场最常见的地址写法整理成一张对照表,调试时直接查:
| PLC 习惯 | Modbus 数据模型 | 偏移计算 | 典型功能码 |
|---|---|---|---|
| 00001 | Coil 0 | 00001-1=0 | 01 读 / 05 写单 / 15 写多 |
| 10001 | Discrete Input 0 | 10001-10001=0 | 02 读 |
| 30001 | Input Register 0 | 30001-30001=0 | 04 读 |
| 40001 | Holding Register 0 | 40001-40001=0 | 03 读 / 06 写单 / 16 写多 |
| 400001 | Holding Register 0(扩展 6 位) | 400001-400001=0 | 同上(长地址设备) |
注意:3x 与 4x 的"减 1"规则与 0x 相同,都是地址号减 1 得到偏移。有些组态软件的 3x/4x 地址已经内置了减 1,另一些没有——同样的"40001"在不同软件里可能指偏移 0 也可能指偏移 1,跨软件对接时先确认对方的地址口径。
超过 16 位的数值(32 位整数、IEEE754 浮点)要占两个寄存器,拼装顺序是现场事故高发区。四种可能的排列:
| 方案 | 高字在前/后 | 高字节在前/后 | 典型厂商 |
|---|---|---|---|
| A | 高字在前 | 高字节在前(大端字大端节) | Modbus 规范默认、多数欧洲设备 |
| B | 高字在前 | 低字节在前 | 部分变送器 |
| C | 低字在前 | 高字节在前 | 部分国产仪表 |
| D | 低字在前 | 低字节在前(小端字小端节) | 部分 PLC 转接实现 |
诊断口诀:读一个已知的浮点(如 1.0 = 0x3F800000),把收到的 4 字节按 A/B/C/D 各拼一次,落在合理范围的就是正确方案。厂商手册"数据格式"一节通常直接写明,先看手册再调试,能省半天。
点表经常出现"从偏移 0 开始、但 0 号寄存器是保留/版本号"的情况。读连续段时要注意:从保留区开始连续读会把版本号当数据;跨数据类型读(比如 0-1 是版本、2-3 才是温度)会导致解析错位。工程做法:严格按点表定义的"起始偏移 + 长度"分组读,不贪图"从 0 一口气读完"。
要点回顾:报文地址是 0-based 偏移,PLC 习惯地址要减 1;多寄存器数据一次读完;字节序按手册确定并验证。地址方言理清后,下一节实战读一张完整厂商点表。
某水处理项目,PLC 侧点表写"保持寄存器 40001-40010 = 10 台泵的状态",程序里按偏移 0-9 读,一切正常。加装第 11 台泵后,厂商把新泵的状态放在 40011,程序改成读偏移 0-10——结果第 11 台永远读不到。排查发现:厂商点表里的"40011"对应偏移 10,而程序把"数量 11"当成了"读到 40011"(偏移 11),少读了一个。把 PLC 地址当偏移用是这类事故的共性:地址要减 1 才是偏移,数量与末地址不是一回事。
某能源管理项目读三相电表的功率(float32),读回原始字节 00 00 A0 41。按 IEEE754 试解:若按大端字大端节 → 0x0000A041 = 20.0625;若按小端字 → 0x41A00000 = 20.0。电表面板显示 20.0kW,于是确定该表是低字在前(方案 C/D)。把这一结论写进驱动配置后,全厂两百块电表的功率读数全部正确。这个案例说明:一个已知值 + 四种排列试解,比翻遍手册更快确定字序。