3. Modbus RTU帧结构
Modbus RTU 是运行在串行链路(RS-485/RS-232)上的二进制协议,它不像 Modbus TCP 那样有 MBAP 报文头,而是依靠“静默间隔”来划分一帧的边界。理解 RTU 帧结构,是驱动开发中收发状态机、超时判定和 CRC 校验实现的根基。一帧完整的 RTU 报文由四部分组成:从站地址域、功能码、数据域和 CRC 校验域,帧与帧之间还必须满足 3.5 个字符时间的静默间隔。
一、RTU 帧的四个组成部分
RTU 帧采用紧凑的二进制编码,没有起始符和结束符,各字段含义如下:
- 地址域(1 字节):标识目标从站,取值范围 1~247。0 为广播地址,所有从站都接收但不应答;248~255 为保留地址。主站发送时填写目标从站地址,从站应答时回填自身地址。
- 功能码(1 字节):说明要执行的操作,如 0x01 读线圈、0x03 读保持寄存器、0x06 写单个寄存器、0x10 写多个寄存器。从站正常应答时原样返回功能码;出错时返回“功能码 + 0x80”,并在数据域给出异常码。
- 数据域(N 字节):内容随功能码变化。读请求中通常包含起始地址和数量;读应答中包含字节数和寄存器值;写请求中包含地址和待写数据。数据域长度可变,是帧长不固定的主要原因。
- CRC 校验域(2 字节):对地址域、功能码、数据域逐字节计算 CRC-16,低字节在前、高字节在后。接收方重新计算并比对,不一致则丢弃整帧。
下面这张图展示了 RTU 帧的字段排布、字节顺序以及帧间的 3.5 字符静默间隔关系:
二、CRC 校验的计算与验证
Modbus RTU 使用 CRC-16/MODBUS 算法,多项式为 0xA001(反向表示),初始值 0xFFFF。计算时对地址、功能码、数据逐字节异或并移位,最终得到 16 位校验值。驱动中通常用查表法或位运算实现,接收端必须重新计算并比对。
// CRC-16/MODBUS 位运算实现(C 语言)
uint16_t modbus_crc16(const uint8_t *buf, uint16_t len)
{
uint16_t crc = 0xFFFF;
for (uint16_t i = 0; i < len; i++) {
crc ^= buf[i];
for (uint8_t j = 0; j < 8; j++) {
if (crc & 0x0001)
crc = (crc >> 1) ^ 0xA001;
else
crc >>= 1;
}
}
return crc; // 低字节在前,高字节在后
}
例如主站读取从站 1 的 2 个保持寄存器,起始地址 0x0000,请求帧为:01 03 00 00 00 02 C4 0B。其中 C4 0B 就是前 6 字节的 CRC 结果,低字节 0xC4 在前,高字节 0x0B 在后。
三、3.5 字符时间的工程实现
3.5 字符时间(3.5T)是 RTU 帧同步的核心参数。1 个字符时间取决于波特率和帧格式:常见配置为 1 起始位 + 8 数据位 + 无校验 + 1 停止位,共 10 位。因此 1T = 10 / 波特率 秒。下表列出常用波特率下的时间参数:
| 波特率 | 1 字符时间 1T | 1.5T(帧内最大间隔) | 3.5T(帧间最小静默) |
|---|---|---|---|
| 9600 bps | 约 1.042 ms | 约 1.563 ms | 约 3.646 ms |
| 19200 bps | 约 0.521 ms | 约 0.781 ms | 约 1.823 ms |
| 38400 bps | 约 0.260 ms | 约 0.391 ms | 约 0.911 ms |
| 115200 bps | 约 0.087 ms | 约 0.130 ms | 约 0.304 ms |
四、典型场景:一次读保持寄存器的完整交互
假设主站以 9600bps 读取从站 1 的保持寄存器 0x0000~0x0001,交互过程如下:
- 主站等待总线静默超过 3.5T,确认总线空闲。
- 主站发送请求帧:
01 03 00 00 00 02 C4 0B,共 8 字节。 - 从站接收后先检查地址是否匹配,再校验 CRC,然后执行读操作。
- 从站等待 3.5T 后发送应答帧:
01 03 04 00 0A 00 14 XX XX,其中 04 表示 4 个数据字节,后跟两个寄存器值。 - 主站接收应答,校验 CRC 后解析数据,完成一次事务。
如果从站返回异常,例如功能码不支持,应答帧为 01 83 02 XX XX,其中 0x83 = 0x03 + 0x80,0x02 表示非法数据地址。驱动必须能识别这种“功能码最高位置 1”的异常应答。
五、常见误区与复盘
- 误区二:忽略 1.5T 帧内间隔。 如果两个字节之间超过 1.5T,接收方会认为帧已中断,导致整帧被丢弃。高波特率下尤其要注意中断响应延迟。
- 误区三:CRC 字节序写反。 Modbus RTU 规定 CRC 低字节先发、高字节后发,与很多 CRC 库默认的高字节在前不同,必须显式调整。
- 误区四:广播帧等待应答。 地址 0 为广播,从站只执行不应答,主站若等待应答会超时。
- 复盘建议: 用串口抓包工具记录原始字节流,对照帧结构逐字段核对;在驱动中加入 CRC 错误计数、帧间隔超时计数,便于现场定位通信问题。
掌握 RTU 帧结构和 3.5T 规则后,驱动开发的重点就落在“如何可靠地切分帧”和“如何快速校验并分发”上。后续实现中,建议把帧解析做成独立状态机,把 CRC 校验和异常码处理封装成公共函数,这样无论是 RTU 还是 TCP 都能复用大部分业务逻辑。