3、RTU模式详解:RTU帧结构、CRC校验原理与计算、RTU模式下的字符间隔与帧间隔
3.1 RTU帧结构——比ASCII更紧凑的“数据包裹”
RTU模式,说白了就是Modbus的“二进制模式”。它不像ASCII那样把每个字节拆成两个字符发送,而是直接发送原始字节。这样做的好处很明显——同样的数据量,RTU的传输效率比ASCII高一倍。
我个人习惯把RTU帧看作一个“数据包裹”,它由四个部分组成:
- 地址域(1字节):从站地址,范围1-247。0是广播地址。
- 功能码(1字节):告诉从站要干什么,比如03是读保持寄存器。
- 数据域(N字节):具体的数据内容,长度可变。
- CRC校验(2字节):16位循环冗余校验,低字节在前,高字节在后。
你想想看,整个帧没有起始标记,也没有结束标记。那接收方怎么知道一帧结束了?全靠“静默时间”来判断。这就是RTU和ASCII最大的不同点。
RTU帧结构示例(读保持寄存器):
从站地址: 0x01
功能码: 0x03
起始地址: 0x00 0x6B
寄存器数: 0x00 0x03
CRC校验: 0x?? 0x??
总共8个字节,干净利落。
我在项目中遇到过有人把RTU帧和ASCII帧搞混,结果通信死活不通。嗯,这里要注意——RTU帧里没有冒号、没有回车换行,全是二进制数据。
3.2 CRC校验原理——为什么Modbus选了这个算法?
CRC校验,全称是循环冗余校验。说白了,就是把整个报文当作一个巨大的二进制数,然后除以一个固定的“生成多项式”,余数就是CRC值。
Modbus RTU用的是CRC-16,生成多项式是0x8005(也就是x^16 + x^15 + x^2 + 1)。为什么选这个?因为它在16位校验中错误检测能力很强,而且硬件实现简单。
我记得刚入行时,总觉得CRC很神秘。后来自己手算了一遍,发现其实就是“异或”和“移位”的反复操作。没什么高深的。
CRC校验的核心步骤:
- 初始化CRC寄存器为0xFFFF
- 对每个字节,与CRC寄存器的低8位异或
- 右移1位,如果移出的位是1,则与0xA001异或
- 重复8次(一个字节处理完)
- 所有字节处理完后,CRC寄存器取反,得到最终CRC值
小技巧:实际工程中没人手算CRC。我建议直接用查表法,提前算好256个字节对应的CRC值,运行时直接查表。速度能快几十倍。
3.3 CRC计算实战——手把手教你算一遍
光讲理论没意思,我们来算一个实际的例子。假设我们要发送01 03 00 6B 00 03这6个字节,CRC怎么算?
我习惯用C语言写一个简单的CRC计算函数:
unsigned int crc16_modbus(unsigned char *buf, int len) {
unsigned int crc = 0xFFFF;
int i, j;
for (i = 0; i < len; i++) {
crc ^= buf[i];
for (j = 0; j < 8; j++) {
if (crc & 0x0001) {
crc = (crc >> 1) ^ 0xA001;
} else {
crc = crc >> 1;
}
}
}
return crc;
}
把01 03 00 6B 00 03丢进去算,得到CRC值是0x3C 0x88。注意,发送时低字节在前,所以实际发送的是88 3C。
避坑指南:我曾经在项目里犯过一个低级错误——把CRC的高低位搞反了。结果从站一直回复异常码。查了两天才发现,原来是字节序的问题。记住:Modbus RTU的CRC是低字节在前,高字节在后。
3.4 RTU模式下的字符间隔与帧间隔——时间就是一切
RTU模式没有帧头帧尾,那接收方怎么知道一帧结束了?答案是:靠时间间隔。
Modbus协议规定了两类时间间隔:
| 间隔类型 | 时间要求 | 说明 |
|---|---|---|
| 字符间隔 | ≤ 1.5个字符时间 | 同一帧内相邻字符的最大间隔 |
| 帧间隔 | ≥ 3.5个字符时间 | 两帧之间的最小间隔 |
“字符时间”怎么算?很简单:1个字符时间 = 11位 / 波特率。为什么是11位?因为Modbus RTU通常使用1个起始位 + 8个数据位 + 1个校验位 + 1个停止位,总共11位。
举个例子,9600波特率下:
- 1个字符时间 = 11 / 9600 ≈ 1.146ms
- 1.5个字符时间 ≈ 1.719ms
- 3.5个字符时间 ≈ 4.010ms
你想想看,如果接收方在收到一个字节后,超过1.719ms还没收到下一个字节,它就认为这一帧结束了。然后开始检查CRC,看看数据是否完整。
实际工程中的经验值:
我个人习惯把帧间隔设得比协议要求稍微宽松一点,比如4ms(9600波特率下)。因为有些从站的时钟不太准,太严格反而容易丢帧。但字符间隔一定要严格,超过1.5个字符时间就视为帧结束。
3.5 避坑指南——RTU模式下的常见问题
做了这么多年Modbus,我总结了几条RTU模式下的血泪教训:
- CRC计算要放在发送缓冲区里统一算:别边发边算,容易出错。
- 接收时用定时器监控字符间隔:别用延时函数,会阻塞其他任务。
- 帧间隔要留余量:3.5个字符时间是最低要求,实际可以设到5个字符时间。
- 注意波特率偏差:主从站的波特率偏差不能超过±2%,否则字符间隔会乱。
我记得有一次在现场调试,从站老是丢帧。查了半天,发现是主站的晶振精度不够,9600波特率实际跑成了9500。偏差超过5%,字符间隔全乱了。后来换了高精度晶振,问题解决。
嗯,RTU模式就讲到这里。下一章我们聊聊ASCII模式,看看它和RTU到底该怎么选。