Modbus-RTU 是一种主从式串行通信协议,通过 RS-232 或 RS-485 等物理层传输。其核心是二进制编码的紧凑帧,解析的关键在于帧界定、字段解析和 CRC 校验。
📨 帧结构与解析
一个完整的 RTU 帧由以下字段组成:
· 从站地址 (1 字节):目标设备地址(1-247)。主机通过它寻址,从机响应时也填入自己的地址。
· 功能码 (1 字节):指示操作类型,如 0x03 读寄存器。
· 数据域 (N 字节):可变长度(0-252),包含请求参数或响应数据。
· CRC 校验 (2 字节):对整个帧(不含自身)的 CRC-16 校验结果,低字节在前。
解析实例:请求帧 01 03 00 6B 00 03 76 87
· 01: 从站地址
· 03: 功能码(读保持寄存器)
· 00 6B: 起始地址 0x006B (107)
· 00 03: 读取 3 个寄存器
· 76 87: CRC 校验值
🔬 CRC 校验
CRC 校验用于验证帧传输的完整性。接收方计算 CRC 并与帧中的校验码比较,若不匹配则丢弃该帧。
· 算法参数:多项式 0x8005(反转 0xA001),初始值 0xFFFF。
· C语言实现核心逻辑(来自开源参考):
```c
uint16_t ModRTU_CRC(uint8_t buf[], int len) {
uint16_t crc = 0xFFFF;
for (int pos = 0; pos < len; pos++) {
crc ^= (uint16_t)buf[pos];
for (int i = 8; i != 0; i--) {
if ((crc & 0x0001) != 0) {
crc >>= 1;
crc ^= 0xA001;
} else {
crc >>= 1;
}
}
}
return crc; // 注意:此时高低字节已交换,可直接发送
}
```
注意:发送时需先发低字节,再发高字节。
⏱️ 防丢包与帧界定
Modbus-RTU 没有起始/结束字符,靠时间间隔界定帧。
· 帧间间隔 (>3.5 字符):前一帧结束到后一帧开始,总线需空闲至少 3.5 个字符时间。
· 帧内间隔 (<1.5 字符):同一帧内字节间隔必须小于 1.5 个字符时间,否则视为帧结束。
· 字符时间计算:取决于波特率。例如 9600bps、11 位/字符时,约 1.14ms/字符,3.5 字符约 4ms。波特率 > 19200 时,时间间隔通常固定。
🛡️ 防丢包策略
· 硬件与软件基础:使用可靠的 RS-485 硬件、正确的 CRC 校验和匹配的通信参数。
· 发送端:发送前确保总线已空闲至少 3.5 字符。
· 接收端:核心策略是解析功能码,根据协议计算出响应帧的预期长度,然后读取指定字节数,而非单纯依赖时间。超时重传也是常用策略。
· 物理层:检查接线(双绞线、手拉手拓扑)、终端电阻和屏蔽层。
💎 总结
实现可靠的 Modbus-RTU 通信,关键在于:
1. 正确解析:理解并严格按顺序解析地址、功能码、数据域。
2. 精确校验:必须实现并正确使用 CRC-16 校验。
3. 时间敏感:严格遵守 3.5 字符和 1.5 字符的时间间隔规定。
4. 主动防丢:采用“长度感知”接收、超时重传等机制,不能完全依赖时间。
建议在实际开发中参考成熟的开源库(如 libmodbus、nanoMODBUS),它们已妥善处理了上述细节。