3、Modbus协议详解:Modbus RTU帧结构、功能码解析、CRC校验计算、异常响应处理
各位同行,今天我们来聊聊Modbus RTU。说实话,这个协议在风电行业里太常见了。主控与变桨、主控与变频器、主控与上位机……到处都有它的影子。我最早接触Modbus是在2008年,那时候调试一个1.5MW机组的变桨系统,通信老是断,折腾了整整一周。后来发现,问题出在CRC校验上——嗯,从那以后我再也不敢小看这个协议了。
3.1 Modbus RTU帧结构——说白了就是“谁在说话”
Modbus RTU的帧结构其实很简单。你想想看,两个设备要通信,总得有个规矩吧?谁先开口?说什么?怎么知道说完了?
一个标准的RTU帧长这样:
| 地址码 | 功能码 | 数据区 | CRC校验 |
| 1字节 | 1字节 | N字节 | 2字节 |
具体来说:
- 地址码:从站地址,范围1-247。0是广播地址,所有从站都收,但不回复。
- 功能码:告诉从站要干什么。读线圈?读寄存器?还是写数据?
- 数据区:具体参数。比如读哪个寄存器、读几个、写什么值。
- CRC校验:两个字节,低字节在前,高字节在后。这个后面细说。
关键点:帧与帧之间要有至少3.5个字符时间的静默间隔。我见过不少新手在这个地方栽跟头——连续发帧,中间间隔不够,从站直接当成一帧处理,结果数据全乱套。
3.2 功能码解析——主站到底想干啥?
功能码是Modbus的灵魂。说白了,就是主站给从站下的一道命令。风电里常用的就那么几个:
| 功能码 | 名称 | 风电典型用途 |
|---|---|---|
| 0x01 | 读线圈 | 读取开关状态(如急停、手自动切换) |
| 0x02 | 读离散输入 | 读取数字量传感器(如限位开关) |
| 0x03 | 读保持寄存器 | 读取模拟量(风速、功率、转速等) |
| 0x04 | 读输入寄存器 | 读取只读参数(如设备序列号) |
| 0x06 | 写单个寄存器 | 下发控制指令(如启动、停机) |
| 0x10 | 写多个寄存器 | 批量设置参数(如PID参数整定) |
我个人习惯,调试时先用0x03读几个关键寄存器,确认通信链路通了,再用0x06下发指令。千万别上来就写,万一地址搞错了,后果你懂的。
避坑指南:我曾经遇到过一台机组,用0x03读功率寄存器,返回值总是0。查了半天,发现是寄存器地址偏移了1。原来厂家手册里写的地址是40001,但实际Modbus地址是0x0000。记住:Modbus地址和PLC地址不是一回事!
3.3 CRC校验计算——别怕,其实就两步
CRC校验,很多人一听就头大。其实说白了,就是给数据算个“指纹”。接收方收到后,用同样的算法算一遍,如果指纹对不上,说明数据在传输过程中被污染了。
Modbus RTU用的是CRC-16,多项式是0x8005。计算步骤:
- 初始化CRC寄存器为0xFFFF
- 对每个字节,与CRC低字节异或
- 右移一位,如果移出的位是1,则与0xA001异或
- 重复8次,处理下一个字节
- 最终结果低字节在前,高字节在后
给你看个C语言实现,我在项目里一直用这个:
uint16_t crc16_modbus(uint8_t *data, uint16_t len)
{
uint16_t crc = 0xFFFF;
for (uint16_t 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;
}
注意:发送时先发CRC低字节,再发高字节。这个顺序搞反了,从站会直接丢弃帧。我当年调试变桨系统时,就是被这个顺序坑了整整两天。
3.4 异常响应处理——从站说“不”的时候
不是每次通信都一帆风顺。从站收到错误请求时,会返回异常响应。格式是:
| 地址码 | 功能码+0x80 | 异常码 | CRC校验 |
| 1字节 | 1字节 | 1字节 | 2字节 |
常见的异常码:
| 异常码 | 含义 | 典型原因 |
|---|---|---|
| 0x01 | 非法功能码 | 从站不支持该功能 |
| 0x02 | 非法数据地址 | 寄存器地址超出范围 |
| 0x03 | 非法数据值 | 写入值超出允许范围 |
| 0x04 | 从站设备故障 | 硬件异常或正在处理其他任务 |
| 0x06 | 从站忙 | 正在处理上一个请求,稍后再试 |
举个例子:你发0x03读寄存器,地址是0x9999,从站没有这个地址,就会返回0x83 + 0x02。收到这个,你就该检查地址对不对了。
我的经验:异常码0x04最让人头疼。它不告诉你具体哪里坏了,只说“我出问题了”。遇到这种情况,我一般先检查从站电源和通信线,再不行就重启从站。80%的问题都能解决。
3.5 知识体系总览
下面这张图,是我自己总结的Modbus RTU知识结构。你看一遍,心里就有谱了:
好了,关于Modbus RTU的帧结构、功能码、CRC校验和异常处理,我就讲到这里。这些东西看着多,其实用熟了就是肌肉记忆。下次调试时遇到通信问题,按这个思路排查,基本都能搞定。
一句话总结:帧结构是骨架,功能码是动作,CRC是防伪标签,异常码是故障指示灯。四者缺一不可。