3. 数据链路层分析:帧结构解析、CRC校验原理与计算、地址冲突检测、重传机制
数据链路层,说白了就是让数据在物理线路上「好好说话」的那一层。我刚开始搞现场总线那会儿,总觉得物理层搞定了就万事大吉,结果被各种奇奇怪怪的通讯故障折磨得够呛。后来才明白,数据链路层才是现场总线稳定性的真正命门。
今天咱们就掰开揉碎,把Modbus RTU的数据链路层彻底讲透。你想想看,一条报文发出去,对方怎么知道该不该收?收了怎么知道对不对?错了怎么办?这些问题,全在这一层解决。
3.1 帧结构解析:报文长什么样?
Modbus RTU的帧结构其实特别简单,就四个部分:地址码、功能码、数据区、校验码。我习惯把它比作一封信——地址码是收件人,功能码是「你要干啥」,数据区是具体内容,校验码就是信封上的火漆封印。
| 字段 | 长度 | 说明 |
|---|---|---|
| 地址码 | 1字节 | 从站地址,范围1-247,0为广播地址 |
| 功能码 | 1字节 | 如03读保持寄存器、06写单个寄存器 |
| 数据区 | N字节 | 寄存器地址、数量、具体数值等 |
| CRC校验 | 2字节 | 低字节在前,高字节在后 |
这里有个坑,我当年在调试一个污水处理项目时就踩过。Modbus RTU的帧之间必须有至少3.5个字符时间的静默间隔。什么意思呢?就是发完一帧后,总线必须「安静」一段时间,才能发下一帧。如果间隔太短,接收方会把两帧当成一帧处理,直接导致解析错误。
关键点:Modbus RTU的帧结束标志不是某个特殊字符,而是「时间间隔」。3.5字符时间的静默,就是帧结束的信号。这个设计很巧妙,但也容易出问题。
3.2 CRC校验原理与计算:数据到底对不对?
CRC校验,全称是循环冗余校验。说白了,就是发送方给数据算一个「指纹」,接收方收到后再算一遍,对比指纹是否一致。不一致?那数据肯定在传输过程中被篡改了。
Modbus RTU用的是CRC-16,多项式是0x8005。我个人习惯用查表法来计算,效率高,适合嵌入式环境。你想想看,PLC的CPU本来就不快,要是每个字节都去移位异或,那通讯效率就太低了。
下面是我常用的CRC计算代码,C语言实现,直接复制就能用:
// CRC-16查表法计算
// 多项式:0x8005,初始值:0xFFFF
uint16_t crc16_modbus(uint8_t *data, uint16_t len) {
uint16_t crc = 0xFFFF;
uint16_t i, j;
for (i = 0; i < len; i++) {
crc ^= data[i];
for (j = 0; j < 8; j++) {
if (crc & 0x0001) {
crc = (crc >> 1) ^ 0xA001; // 0xA001是0x8005的反转
} else {
crc = crc >> 1;
}
}
}
return crc;
}
避坑指南:我曾经在风电项目上遇到一个诡异问题——CRC算出来总是对不上。查了两天才发现,Modbus RTU的CRC是「低字节在前,高字节在后」。也就是说,如果你算出来的CRC是0x1234,发送时先发0x34,再发0x12。这个顺序搞反了,校验永远过不了。
3.3 地址冲突检测:谁在抢着说话?
地址冲突,是现场总线里最让人头疼的问题之一。Modbus是一主多从架构,理论上每个从站地址必须唯一。但实际项目中,尤其是老旧设备改造时,经常出现两个设备设成同一个地址的情况。
为什么会这样?我遇到过几种情况:
- 新设备默认地址没改:很多设备出厂默认地址是1,两台新设备直接怼上去,必冲突。
- 拨码开关设置错误:有次在化工厂,工人把拨码开关的二进制数看反了,地址设成了16而不是1。
- 软件配置与硬件不一致:上位机配置的地址和从站实际地址对不上。
地址冲突的表现是什么?主站发一条查询报文,总线上两个从站都响应,数据就乱套了。我习惯用示波器抓一下总线波形,如果看到多个从站在同一时间拉低总线,那基本就是地址冲突了。
注意:Modbus没有内置的地址冲突检测机制。也就是说,从站不会主动告诉你「我地址重复了」。你必须通过超时、数据异常等现象来反推。这也是为什么我建议在项目调试阶段,先用Modbus扫描工具把所有在线设备的地址扫一遍。
3.4 重传机制:错了怎么办?
Modbus的重传机制,说白了就是「超时重发」。主站发出一条请求后,启动一个定时器。如果在规定时间内没收到响应,就认为报文丢失了,重新发一次。
这里有几个参数需要设置:
| 参数 | 说明 | 建议值 |
|---|---|---|
| 响应超时 | 等待从站响应的最长时间 | 50-1000ms,视波特率而定 |
| 重试次数 | 超时后重新发送的次数 | 3次,超过则报通讯故障 |
| 帧间隔 | 两次重发之间的等待时间 | 至少3.5字符时间 |
嗯,这里要注意。重传次数不是越多越好。我见过有人把重试次数设成10次,结果一个通讯故障要等好几秒才报出来,现场操作员都急疯了。我个人习惯设3次,如果3次都超时,那说明线路确实有问题,赶紧报警让维护人员去查。
另外,重传时一定要遵守帧间隔要求。有些工程师图省事,超时后立刻重发,结果因为帧间隔不够,从站还在处理上一帧,直接把新帧当垃圾数据丢了。这种问题用逻辑分析仪一看就明白,波形上两个帧之间几乎没有间隔。
实战经验:我在一个光伏电站项目中,发现通讯偶尔会中断几秒钟。排查了很久,最后发现是某个从站的响应时间不稳定——有时候50ms就回了,有时候要800ms。而主站的超时时间设的是200ms,所以经常超时重发。解决方案很简单:把超时时间放宽到1000ms,问题就解决了。所以,超时时间的设置要结合现场最慢的那个从站来定。
好了,数据链路层的内容就讲到这里。帧结构让你知道报文长什么样,CRC校验保证数据完整性,地址冲突检测帮你排查多站通讯问题,重传机制则是最后的保底手段。这四个知识点,是现场总线故障诊断的基石。下一章咱们聊聊应用层,看看Modbus的功能码到底是怎么工作的。