3、Modbus RTU协议详解:串行帧格式、CRC校验计算、主从站通信机制
好,咱们今天来聊聊Modbus RTU。说实话,这协议在工业现场太常见了。我入行那会儿,第一个项目就是拿它连PLC和变频器。当时觉得这东西简单,后来踩了不少坑才明白——越是基础的协议,越要把它吃透。
Modbus RTU,说白了就是串行线上的一种“对话规则”。它规定了谁先说话、怎么说、说完了怎么确认对方听懂了。咱们从最核心的三个部分入手:帧格式、校验码、主从通信。
3.1 串行帧格式:数据到底怎么装?
Modbus RTU的帧结构,我习惯把它想象成一个“信封”。信封外面写地址,里面装指令和数据,最后贴个防伪标签(CRC校验)。
一个完整的RTU帧长这样:
| 地址码 | 功能码 | 数据区 | CRC校验(低) | CRC校验(高) |
| 1字节 | 1字节 | N字节 | 1字节 | 1字节 |
每个字段都有讲究:
- 地址码:1个字节,范围0x01~0xF7。0x00是广播地址,所有从站都得听,但不回复。我见过有人把地址设成0x00当普通站用,结果总线乱成一锅粥——嗯,这是个坑。
- 功能码:1个字节,告诉从站要干啥。比如0x03读保持寄存器,0x06写单个寄存器。常用的也就十来个,别贪多。
- 数据区:长度可变,具体看功能码。比如读寄存器时,这里放起始地址和数量;写寄存器时,放地址和要写的数据。
- CRC校验:2个字节,低字节在前,高字节在后。这是整个帧的“防伪码”,算错了从站直接不理你。
关键点:RTU帧里没有帧头帧尾标记,靠的是“空闲时间”来区分。总线静默3.5个字符时间以上,表示上一帧结束、下一帧开始。这个时间跟波特率有关——9600波特率下,3.5个字符大约3.6ms。我曾经遇到一个项目,因为线缆干扰导致空闲时间误判,整个系统频繁丢帧。后来加了终端电阻才解决。
3.2 CRC校验计算:别怕,有现成算法
CRC校验,全称循环冗余校验。很多新手一听“循环冗余”就头大。其实你不用自己手算,直接用查表法或者现成的函数就行。
Modbus RTU用的是CRC-16,多项式是0x8005(实际计算时用0xA001,因为要反转)。计算步骤我简单说一下:
- 初始化CRC寄存器为0xFFFF。
- 取第一个字节与CRC低8位异或,结果放回CRC。
- 右移1位,高位补0。如果移出的最低位是1,则CRC与0xA001异或。
- 重复步骤3,直到处理完8位。
- 取下一个字节,重复步骤2~4,直到所有字节处理完。
- 最终CRC寄存器的高8位和低8位互换,作为校验码发送。
你想想看,要是每个帧都这么算,CPU得累死。所以实际项目中都用查表法。我给你一个C语言的查表实现:
// CRC-16 查表法(Modbus RTU)
uint16_t crc16_modbus(uint8_t *data, uint16_t len) {
uint16_t crc = 0xFFFF;
uint16_t i;
for (i = 0; i < len; i++) {
crc ^= data[i];
for (uint8_t j = 0; j < 8; j++) {
if (crc & 0x0001) {
crc = (crc >> 1) ^ 0xA001;
} else {
crc >>= 1;
}
}
}
// 高低字节互换
return (crc >> 8) | (crc << 8);
}
个人经验:我建议你在调试时,先用串口助手抓一帧正常通信的数据,然后用这个算法算一遍CRC,跟抓到的对比。如果对不上,八成是字节顺序搞反了——Modbus RTU是先发低字节,再发高字节。这个细节坑过不少人。
3.3 主从站通信机制:一问一答,规矩严格
Modbus RTU是典型的主从架构。总线上只有一个主站(通常是PLC或上位机),其他都是从站(传感器、执行器)。主站不发话,从站不能主动开口。
通信流程很简单:
- 主站发送请求帧(包含从站地址、功能码、数据、CRC)。
- 所有从站都收到这个帧,但只有地址匹配的那个才响应。
- 从站处理请求,然后发送响应帧(包含相同地址、功能码、返回数据、CRC)。
- 主站收到响应,校验CRC,确认无误后继续下一轮。
这里有几个要点:
- 超时机制:主站发送请求后,不能无限等。一般设个100~500ms的超时。如果超时没收到响应,主站可以重发。我习惯设3次重试,再失败就报错。
- 广播帧:地址0x00是广播,所有从站都执行指令,但不回复。这个适合同步操作,比如“所有设备同时复位”。但要注意——广播帧没有确认,可靠性靠应用层保证。
- 异常响应:如果从站收到非法指令,会返回异常帧。功能码最高位置1(比如请求0x03,异常返回0x83),后面跟一个异常码。常见的异常码有:01(非法功能码)、02(非法数据地址)、03(非法数据值)。
避坑指南:我曾经在一个项目里,主站连续发送请求,从站响应正常,但偶尔会丢一帧。查了三天,发现是从站的接收缓冲区太小,数据还没处理完就被新帧覆盖了。解决办法是:主站发送完一帧后,必须等待从站响应,或者至少等待3.5个字符时间,才能发下一帧。别把RTU当以太网用,它没那么快。
3.4 实际应用中的几个建议
说了这么多理论,最后给几个实战建议:
- 波特率别太高:9600或19200最稳。115200虽然快,但线缆一长就容易出错。我一般不超过38400。
- 终端电阻:RS485总线两端各加一个120Ω电阻。不加的话,信号反射会导致数据错位。这个钱不能省。
- 隔离:如果现场有电机、变频器这类强干扰源,建议用带隔离的RS485模块。我吃过亏——一次没隔离,通信偶尔乱码,换了隔离模块后世界清净了。
- 调试工具:串口助手+Modbus Poll(主站模拟)+Modbus Slave(从站模拟),这三件套能解决90%的通信问题。
好了,Modbus RTU的核心内容就这些。下一章咱们聊Modbus TCP,看看它跟RTU有什么不同,以及怎么在以太网上跑Modbus。到时候见。