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。计算步骤:

  1. 初始化CRC寄存器为0xFFFF
  2. 对每个字节,与CRC低字节异或
  3. 右移一位,如果移出的位是1,则与0xA001异或
  4. 重复8次,处理下一个字节
  5. 最终结果低字节在前,高字节在后

给你看个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 知识体系 Modbus RTU 帧结构 地址码 + 功能码 + 数据区 + CRC 功能码解析 0x01~0x10 读/写线圈、寄存器 CRC校验 CRC-16 多项式 0x8005 异常响应 异常码 0x01~0x06 功能码 + 0x80 掌握这四个模块,Modbus RTU调试不再头疼

好了,关于Modbus RTU的帧结构、功能码、CRC校验和异常处理,我就讲到这里。这些东西看着多,其实用熟了就是肌肉记忆。下次调试时遇到通信问题,按这个思路排查,基本都能搞定。

一句话总结:帧结构是骨架,功能码是动作,CRC是防伪标签,异常码是故障指示灯。四者缺一不可。


专注资料整理