第2章:Modbus RTU协议详解

各位工程师朋友,咱们今天来啃Modbus RTU这块硬骨头。说实话,这协议看起来简单,但坑不少。我在现场调试时,十次通信故障有八次是RTU帧结构没搞对。来,咱们一步步拆解。

2.1 报文帧结构:四个核心字段

Modbus RTU的报文帧,说白了就四个部分:地址码、功能码、数据区、CRC校验。我习惯把它想象成一个快递包裹——地址码是收件人,功能码是你要干啥,数据区是货物,CRC是防拆封条。

字段 长度 说明
地址码 1字节 从站地址,范围1-247
功能码 1字节 操作类型,如03读保持寄存器
数据区 N字节 寄存器地址、数量、数据值
CRC校验 2字节 低字节在前,高字节在后

2.2 地址码:别小看这1个字节

地址码范围是1到247,0是广播地址。嗯,这里要注意——广播地址所有从站都会响应,但实际项目中我很少用。为什么?因为一旦广播写操作,所有设备同时动作,电流冲击能把电源干趴下。我曾经在一个工厂里,就因为广播写继电器,把24V电源模块烧了三个。

我个人习惯:每个从站分配唯一地址,从1开始顺序编号。别用跳线帽,用拨码开关,现场调试时改地址方便得多。

2.3 功能码:常用的就那几个

Modbus定义了20多个功能码,但实际项目中常用的不超过5个。我列个表,你心里有数就行:

功能码 名称 用途
01 读线圈 读取开关量输出
02 读离散输入 读取开关量输入
03 读保持寄存器 读取模拟量/参数(最常用)
04 读输入寄存器 读取只读模拟量
05 写单线圈 控制单个开关量
06 写单寄存器 写入单个参数
15 写多线圈 批量控制开关量
16 写多寄存器 批量写入参数

你想想看,一个温控器项目,无非就是读温度(03功能码)、写设定值(06功能码)。搞复杂了反而容易出问题。

2.4 数据区:大端还是小端?这是个问题

数据区里最坑的就是字节序。Modbus RTU规定:多字节数据采用大端模式(Big-Endian),也就是高字节在前。但很多国产设备不按套路出牌,我遇到过用小端模式的PLC,也遇到过用中端模式的仪表。

避坑指南: 我曾经在调试一个水处理项目时,读回来的pH值总是7.8,实际是8.7。折腾了两天,最后发现是设备用了小端模式。从那以后,我每接一个新设备,第一件事就是读一个已知值,验证字节序。

举个例子,读取保持寄存器地址0x0000,返回数据0x1234:

// 大端模式(标准)
高字节 0x12 → 低字节 0x34
实际值 = 0x1234 = 4660

// 小端模式(非标)
高字节 0x34 → 低字节 0x12
实际值 = 0x3412 = 13330

2.5 CRC校验:最后一道防线

CRC校验是Modbus RTU的防错机制。它用CRC-16算法,多项式是0x8005。说白了,就是计算整个报文(地址码+功能码+数据区)的校验值,放在报文末尾。

我见过不少工程师自己手写CRC算法,其实没必要。标准库函数一调用就行。但有一点要注意:CRC的低字节在前,高字节在后。这个顺序搞反了,通信就废了。

核心要点: CRC校验失败时,从站不会响应任何数据。如果你发现发出去的报文石沉大海,先检查CRC对不对。

2.6 主从通信机制:一问一答

Modbus RTU是典型的主从架构。主机(通常是PLC或上位机)发起请求,从机(传感器、执行器)响应。一次完整的通信流程是这样的:

  1. 主机发送请求帧(地址码+功能码+数据区+CRC)
  2. 从机接收并校验CRC
  3. 从机执行操作(读或写)
  4. 从机发送响应帧(地址码+功能码+数据区+CRC)
  5. 主机接收并校验CRC

这里有个关键点:帧间隔时间。RTU协议要求帧与帧之间至少有3.5个字符时间的空闲。我习惯用9600波特率时,间隔至少4ms。如果间隔太短,从机可能把两帧当成一帧处理,数据就乱了。

实战技巧: 我在调试时,会用示波器抓RS485的波形。看帧间隔是否满足3.5字符时间。如果波形上两帧之间没有明显的空闲,那就是间隔太短了。

2.7 典型报文抓包分析

光说不练假把式。咱们来抓个包看看。假设我要读取地址为01的从机,保持寄存器起始地址0x0000,读取2个寄存器:

请求帧(主机→从机):
01 03 00 00 00 02 C4 0B

逐字节解析:
01    → 地址码,从机地址1
03    → 功能码,读保持寄存器
00 00 → 起始地址,0x0000
00 02 → 读取数量,2个寄存器
C4 0B → CRC校验

响应帧(从机→主机):
01 03 04 12 34 56 78 3A 9C

逐字节解析:
01    → 地址码,从机地址1
03    → 功能码,读保持寄存器
04    → 数据区长度,4个字节(2个寄存器×2字节)
12 34 → 第一个寄存器值,0x1234
56 78 → 第二个寄存器值,0x5678
3A 9C → CRC校验

你看,是不是很清晰?我刚开始学的时候,就是对着这种报文逐字节分析,慢慢就熟了。建议你也拿个串口助手,抓几组报文自己分析分析。

2.8 异常响应:通信出问题怎么办

如果从机无法执行请求,会返回异常响应。异常响应的功能码是请求功能码+0x80,后面跟一个异常码。常见的异常码有:

异常码 含义 常见原因
01 非法功能码 从机不支持该功能
02 非法数据地址 寄存器地址超出范围
03 非法数据值 写入值超出允许范围
04 从机设备故障 硬件或固件异常

举个例子,如果请求读地址0xFFFF(超出范围),响应可能是:

异常响应帧:
01 83 02 C0 F1

解析:
01    → 地址码
83    → 功能码(03+0x80=0x83)
02    → 异常码,非法数据地址
C0 F1 → CRC校验

嗯,这里要注意:收到异常响应时,别急着重发。先查查是不是地址或数据范围搞错了。我曾经在一个项目中,程序一直重发请求,结果从机被频繁的异常请求搞死机了。

2.9 我的调试心得

最后分享几个实用技巧:

  • 先用串口助手验证:别一上来就写代码。用Modbus Poll或串口助手手动发几帧,确认通信正常再编程。
  • 注意RS485的A/B线:A接A,B接B,别接反了。我见过有人把A接B,B接A,结果通信时好时坏。
  • 终端电阻不能省:长距离通信时,在总线两端各加一个120Ω电阻。不加的话,信号反射会导致数据错乱。
  • 波特率要统一:所有设备波特率必须一致。我习惯用9600,兼容性最好。

好了,Modbus RTU的核心内容就这些。下一章咱们讲Modbus TCP,看看它和RTU有什么不同。记住,协议是死的,人是活的。多动手抓包分析,比看十遍文档都管用。