3. Modbus RTU协议详解:帧结构、主从通信与实战功能码
大家好,我是老张。今天咱们来啃Modbus RTU协议这块硬骨头。说实话,这协议在工业现场太常见了,从PLC到变频器,从温控表到智能仪表,几乎都在用。我个人习惯把Modbus RTU比作工业通信的“普通话”——虽然不花哨,但大家都听得懂。
核心要点:Modbus RTU是一种主从式、半双工的串行通信协议。它简单、可靠、成本低,是工业自动化领域应用最广泛的协议之一。
3.1 帧结构:四个关键部分
一个完整的Modbus RTU报文帧,说白了就四个部分:地址码、功能码、数据区、CRC校验。我刚开始接触时总觉得这结构太简单,后来才发现,越简单的东西越不容易出错。
| 字段 | 长度 | 说明 |
|---|---|---|
| 地址码 | 1字节 | 从站地址,范围1-247,0为广播地址 |
| 功能码 | 1字节 | 指示操作类型,如03读保持寄存器 |
| 数据区 | N字节 | 包含起始地址、寄存器数量、数据值等 |
| CRC校验 | 2字节 | 循环冗余校验,低字节在前 |
3.2 地址码:给每个设备一个身份证
地址码是1个字节,范围1到247。0是广播地址,所有从站都会接收但不回复。248到255是保留地址,别乱用。
避坑指南:我曾经在一个项目中,把两个温控器都设成了地址1。结果主站发指令时,两个设备同时响应,数据全乱套了。排查了整整半天才发现是地址冲突。所以我的建议是:设备地址一定要在项目文档里登记清楚,最好用标签贴在设备上。
3.3 功能码:告诉设备要干什么
功能码就1个字节,但含义丰富。常用的功能码就那么几个:
- 03 (0x03):读保持寄存器。最常用的功能码,读取多个寄存器的值。
- 06 (0x06):写单个寄存器。写一个16位数据到指定寄存器。
- 16 (0x10):写多个寄存器。一次写多个连续寄存器。
你想想看,现场90%以上的Modbus通信,其实就靠这三个功能码撑着。其他的像01、02、04、05、15这些,虽然也有用,但频率低得多。
3.4 数据区:真正的信息载体
数据区是帧结构中最灵活的部分。以03功能码为例,数据区包含:
- 起始地址(2字节):要读取的第一个寄存器地址。注意,Modbus地址是从0开始的,但很多设备手册里写的是从1开始的地址。嗯,这里要注意:手册里的地址通常要减1才是协议里的地址。
- 寄存器数量(2字节):连续读取的寄存器个数。最大125个,因为一帧报文不能太长。
举个例子,要读取从站地址1的寄存器40001到40003(手册地址),实际协议地址是0x0000到0x0002,数量是3。报文是这样的:
请求:01 03 00 00 00 03 05 CB
响应:01 03 06 00 01 00 02 00 03 3A 9B
响应报文里,06表示后面有6个字节数据(3个寄存器,每个2字节),00 01、00 02、00 03就是三个寄存器的值。
3.5 CRC校验:数据完整性的守护神
CRC校验是Modbus RTU的“防错机制”。它用2个字节,对整个报文(地址码+功能码+数据区)进行校验。计算方法是CRC-16/MODBUS,多项式是0x8005。
我个人习惯用查表法计算CRC,速度快,适合嵌入式系统。现场通信时,如果CRC校验不过,从站会直接丢弃报文,不会回复。我曾经遇到过一台变频器,通信时好时坏,最后发现是CRC计算芯片坏了,换了块板子就好了。
小技巧:调试时可以用串口助手抓报文,手动计算CRC来验证。网上有很多在线CRC计算工具,很方便。
3.6 主从站通信机制:一问一答
Modbus RTU是典型的主从通信。主站(通常是PLC或上位机)发起请求,从站(传感器、执行器等)响应。同一时刻只能有一个主站,多个从站。
通信流程是这样的:
- 主站发送请求帧,包含目标从站地址、功能码、数据。
- 所有从站都收到这个帧,但只有地址匹配的那个从站会处理。
- 从站执行操作,然后发送响应帧给主站。
- 主站收到响应,解析数据,完成一次通信。
如果从站没响应,主站通常会重试3次,超时时间一般设500ms到1s。我建议超时时间别设太短,有些老设备反应慢,容易误判为通信故障。
3.7 03功能码实战:读保持寄存器
03功能码是最常用的。比如我要读取一个温度传感器的当前值,寄存器地址是40001(协议地址0x0000)。
请求帧:
01 03 00 00 00 01 84 0A
解释:01是从站地址,03是功能码,00 00是起始地址,00 01是读取1个寄存器,84 0A是CRC校验。
响应帧:
01 03 02 01 2C B8 1B
解释:01是从站地址,03是功能码,02是数据长度(2字节),01 2C是寄存器值(十进制300,表示30.0℃),B8 1B是CRC校验。
3.8 06功能码实战:写单个寄存器
06功能码用于写单个寄存器。比如我要把变频器的频率设为50.0Hz,寄存器地址是40001(协议地址0x0000),值设为500(表示50.0Hz)。
请求帧:
01 06 00 00 01 F4 48 0A
解释:01是从站地址,06是功能码,00 00是寄存器地址,01 F4是写入值(十进制500),48 0A是CRC校验。
响应帧:
01 06 00 00 01 F4 48 0A
注意:06功能码的响应帧和请求帧完全一样,这叫“回显”。如果不一样,说明写失败了。
警告:写寄存器操作要谨慎!我曾经在调试时不小心写错了值,把变频器的最大频率设成了0Hz,结果电机直接停了。所以写操作前一定要确认数据正确,最好加个“写前确认”的逻辑。
3.9 16功能码实战:写多个寄存器
16功能码(0x10)用于一次写多个连续寄存器。比如我要同时设置变频器的频率和加减速时间,寄存器地址40001和40002。
请求帧:
01 10 00 00 00 02 04 01 F4 00 64 9A 1B
解释:01是从站地址,10是功能码,00 00是起始地址,00 02是写2个寄存器,04是后面数据长度(4字节),01 F4是第一个寄存器值(500,频率50.0Hz),00 64是第二个寄存器值(100,加速时间10.0秒),9A 1B是CRC校验。
响应帧:
01 10 00 00 00 02 41 C8
解释:响应帧只返回起始地址和寄存器数量,不返回具体数据。这样报文更短,效率更高。
3.10 知识体系总览
下面这张图是我自己画的,把Modbus RTU的核心知识点串起来了。你看一遍,心里就有谱了。
好了,Modbus RTU的核心内容就这些。帧结构、主从通信、三个实战功能码,你只要把这几个点吃透了,现场调试基本不会卡壳。记住我一句话:协议是死的,人是活的。多动手抓报文,多分析异常情况,慢慢就熟练了。
老张的总结:Modbus RTU之所以能活这么多年,靠的就是简单可靠。别把它想得太复杂,它就是一套“谁说话、说什么、怎么确认”的规则。你按规则来,设备就听话。
公众号:蓝海资料掘金营,微信deep3321