3、Modbus RTU详解:RTU帧结构、CRC校验原理与计算、RTU的时序要求
好,咱们进入Modbus RTU的实战环节。
说实话,在风电现场,RTU模式比TCP模式要“皮实”得多。为什么?因为风电场里电磁环境复杂,RS485差分信号抗干扰能力强,而且RTU的帧结构紧凑,传输效率高。我最早在海上风电项目调试时,就吃过TCP丢包的亏,后来换成RTU,稳得很。
3.1 RTU帧结构:一个字节都不能错
RTU的帧结构,说白了就是一套“通信暗号”。主站发一个请求,从站必须按规矩回复。规矩乱了,谁都不认谁。
一个标准的RTU帧长这样:
| 字段 | 长度(字节) | 说明 |
|---|---|---|
| 地址码 | 1 | 从站地址,范围1-247 |
| 功能码 | 1 | 如03读保持寄存器、06写单个寄存器 |
| 数据区 | N | 寄存器地址、数量、数据值等 |
| CRC校验 | 2 | 低字节在前,高字节在后 |
嗯,这里要注意:CRC校验是低字节在前,高字节在后。我见过不少新手工程师,自己写上位机时把CRC高低字节搞反了,结果怎么调都通不了。我当时排查了整整一个下午,最后发现是字节序的问题——那种感觉,真是又气又好笑。
核心要点:RTU帧没有起始位和停止位,靠的是“帧间隔”来区分。发送完一帧后,必须等待至少3.5个字符时间,才能发下一帧。
3.2 CRC校验原理:别怕,其实很简单
CRC校验,全称是循环冗余校验。很多工程师一听“循环冗余”就头大,其实你把它当成一个“数学指纹”就行。
Modbus RTU用的是CRC-16,多项式是 0x8005(实际计算时用 0xA001 反转形式)。计算过程是这样的:
- 初始化CRC寄存器为
0xFFFF - 对每个字节,与CRC寄存器的低8位异或
- 右移1位,如果移出的位是1,则与多项式
0xA001异或 - 重复8次,处理完所有字节
- 最终结果取反,得到CRC值
为什么会这样设计?说白了就是为了用硬件移位寄存器高效实现。早期单片机性能弱,这种算法算起来飞快。
3.3 CRC计算实战:手写代码
我个人习惯用查表法,速度快,适合风电PLC这种实时性要求高的场景。下面是我在项目中常用的C代码:
// CRC-16查表法实现
uint16_t crc16_modbus(uint8_t *data, uint16_t len)
{
uint16_t crc = 0xFFFF;
uint16_t i;
while (len--)
{
crc ^= *data++;
for (i = 0; i < 8; i++)
{
if (crc & 0x0001)
crc = (crc >> 1) ^ 0xA001;
else
crc >>= 1;
}
}
return crc;
}
避坑指南:我曾经在变桨系统调试时,发现CRC计算结果总是不对。查了半天,原来是数据指针传错了,多传了一个字节的地址码。记住:CRC计算范围是从地址码开始,到数据区结束,不包括CRC本身。
3.4 RTU的时序要求:时间就是生命
RTU的时序要求,是很多工程师容易忽略的地方。你想想看,如果时序不对,数据就会错乱。
Modbus RTU定义了三个关键时间参数:
| 参数 | 定义 | 典型值(9600bps) |
|---|---|---|
| T1.5 | 字符间超时时间 | 约1.5ms |
| T3.5 | 帧间隔时间 | 约3.5ms |
| 响应超时 | 从站最大响应时间 | 通常100-1000ms |
这里有个容易踩的坑:T3.5时间不能太短,也不能太长。太短了,从站来不及处理;太长了,主站会误判为帧结束。我建议在程序中用定时器中断来检测,而不是用软件延时——软件延时在中断多的时候会不准。
重要提醒:在风电场这种强电磁干扰环境下,建议把T3.5适当放宽到4-5个字符时间。我遇到过因为电缆过长导致信号畸变,T3.5设得太严,结果频繁丢帧。后来放宽了一点,问题就解决了。
3.5 知识体系总览
为了让你更直观地理解RTU的整个知识结构,我画了一张图:
这张图把RTU的三个核心模块串起来了。你想想看,帧结构是骨架,CRC校验是血液,时序要求是心跳——少了哪一个,通信都跑不起来。
总结一下:RTU看起来简单,但细节决定成败。我见过太多项目因为CRC算错、时序设错、帧结构理解错而导致调试卡壳。记住:先理解原理,再动手写代码,最后在现场验证。这个顺序,一个都不能少。
公众号:蓝海资料掘金营,微信deep3321