Modbus RTU 报文结构详解
调试 Modbus 时最耗时间的往往不是协议本身,而是地址对不上、CRC 字节序写反、异常码看不懂。这篇把一帧报文拆到字节级别,配一个真实例子。
一帧报文由四部分组成
从站地址
1 字节
功能码
1 字节
数据区
N 字节
CRC 校验
2 字节 · 低字节在前
| 字段 | 长度 | 含义 |
|---|---|---|
| 从站地址 | 1 字节 | 0 为广播(从站不回复);1–247 为从站设备地址。地址重复是"多台设备一起动"的常见原因 |
| 功能码 | 1 字节 | 告诉从站要做什么操作,见下表 |
| 数据区 | N 字节 | 请求里是起始地址和数量,响应里是实际读到的数据 |
| CRC 校验 | 2 字节 | 对前面所有字节计算 CRC-16/MODBUS,低字节先发 |
逐字节拆解一个读寄存器请求
主机要读 1 号从站、起始地址 0、共 10 个保持寄存器,完整报文是:
01 03 00 00 00 0A C5 CD
| 字节 | 值 | 含义 |
|---|---|---|
| 1 | 01 | 从站地址 1 |
| 2 | 03 | 功能码 03 = 读保持寄存器 |
| 3–4 | 00 00 | 起始地址 0x0000 |
| 5–6 | 00 0A | 读 10 个寄存器(0x000A = 10) |
| 7–8 | C5 CD | CRC = 0xCDC5,按 Modbus 规定低字节在前写成 C5 CD |
从站正常响应:
01 03 14 00 64 00 C8 ... xx xx
| 字节 | 值 | 含义 |
|---|---|---|
| 1 | 01 | 从站地址原样返回 |
| 2 | 03 | 功能码原样返回 |
| 3 | 14 | 数据字节数 0x14 = 20 |
| 4–23 | 00 64 00 C8 … | 20 字节数据(10 个寄存器 × 2 字节),每个寄存器 16 位、高位在前 |
| 末 2 | xx xx | CRC,低字节在前 |
注意寄存器是 16 位的。每个寄存器占 2 字节,高位在前(大端)。0x0064 就是十进制 100,不是两个独立字节。很多"数值差 256 倍"的问题就出在这里。
常见功能码
| 功能码 | 名称 | 作用 | 数据区含义 |
|---|---|---|---|
| 01 | 读线圈 | 读开关量输出状态 | 起始地址(2) + 数量(2) |
| 02 | 读离散输入 | 读开关量输入状态 | 起始地址(2) + 数量(2) |
| 03 | 读保持寄存器 | 读可读写的参数、设定值 | 起始地址(2) + 数量(2) |
| 04 | 读输入寄存器 | 读只读的测量值 | 起始地址(2) + 数量(2) |
| 05 | 写单个线圈 | 控制一个开关量 | 地址(2) + 0xFF00/0x0000 |
| 06 | 写单个寄存器 | 改一个设定值 | 地址(2) + 数值(2) |
| 0F | 写多个线圈 | 批量控制开关量 | 地址(2) + 数量(2) + 字节数(1) + 数据 |
| 10 | 写多个寄存器 | 批量写参数 | 地址(2) + 数量(2) + 字节数(1) + 数据 |
注意 0F 和 10 是十六进制写法,对应十进制的 15 和 16。文档里常写"功能码 15""功能码 16",指的就是这两个。
40001 到底对应哪个地址
这是现场最高频的困惑。Modbus 有两套地址体系,必须分清:
- PLC 地址(参考地址):40001、40002 这种 5 位编号,从 1 开始计数,是给人看的
- 协议地址(PDU 地址):真正写进报文里的,从 0 开始计数
换算关系:协议地址 = PLC 地址 − 40001。所以 40001 → 0x0000、40002 → 0x0001、40100 → 0x0063。
常见前缀规则:0xxxx 线圈(功能码 01/05)、1xxxx 离散输入(02)、3xxxx 输入寄存器(04)、4xxxx 保持寄存器(03/06/10)。 但注意——有些厂家的手册直接给的就是协议地址(从 0 开始),有些给的是 PLC 地址(从 1 开始),还有的干脆自己定义。**当数值整体差 1 的时候,先怀疑这个偏移。**
异常响应怎么读
从站出错时,会把功能码最高位置 1(即加 0x80),后面跟一个异常码。例如功能码 03 执行失败:
01 83 02 C0 F1
| 字节 | 值 | 含义 |
|---|---|---|
| 1 | 01 | 从站地址 |
| 2 | 83 | 0x83 = 0x03 + 0x80,说明功能码 03 执行失败 |
| 3 | 02 | 异常码 02 = 非法数据地址 |
| 4–5 | C0 F1 | CRC |
| 异常码 | 名称 | 通常原因 |
|---|---|---|
| 01 | 非法功能 | 从站不支持这个功能码,或该功能被禁用 |
| 02 | 非法数据地址 | 地址超出范围,或地址偏移算错了(最常见) |
| 03 | 非法数据值 | 写入的值超出允许范围,或数量字段超限 |
| 04 | 从站设备故障 | 从站内部异常,检查设备本身 |
| 05 | 确认 | 从站已接受请求,但需要较长时间处理 |
| 06 | 从站忙 | 从站正在处理上一条命令,稍后重试 |
如果完全没响应,通常是这几类:从站地址错、波特率/校验位不一致、A/B 线接反、CRC 字节序写反、终端电阻缺失导致信号反射。
算一下你手上的报文
把报文粘进计算器,选 CRC-16/MODBUS,会自动算出校验值并检查末尾两字节对不对。