4. Modbus RTU帧结构与串行链路参数配置

Modbus RTU 是农业设备通信中最常见的串行协议形式,温室控制器、水肥一体机、土壤墒情采集器、变频水泵和电表大多以 RS-485 半双工方式运行 RTU。调试时“通信不上”的原因,往往不是设备坏了,而是帧结构理解偏差或串口参数不一致。本节把一帧 RTU 报文拆开看,再把串口参数、帧间隔和 CRC 校验串起来,形成可落地的配置与排查思路。

一、RTU 帧的四个组成部分

一帧标准 RTU 报文由地址域、功能码、数据域和 CRC 校验四段构成,帧内字节之间没有起始符和结束符,帧的边界完全依靠总线上的静默时间(3.5 个字符时间)来界定。这是 RTU 与 ASCII 模式最本质的区别,也是现场最容易忽略的一点。

  • 地址域(1 字节):取值 1~247 表示从站地址,0 为广播地址(从站执行但不回应),248~255 保留。农业现场常把同一棚区的传感器编为连续地址,便于轮询。
  • 功能码(1 字节):03 读保持寄存器、04 读输入寄存器、06 写单个寄存器、16(0x10)写多个寄存器、01/02 读线圈与离散输入。从站正常应答时功能码最高位为 0;若出错,会把功能码最高位置 1(如 0x83),并在数据域返回异常码。
  • 数据域(N 字节):随功能码变化。以 03 为例,请求帧数据域为“起始寄存器地址高字节、低字节 + 寄存器数量高字节、低字节”;应答帧数据域为“字节数 + 各寄存器值”。
  • CRC 校验(2 字节):CRC-16/MODBUS,低字节在前、高字节在后,覆盖地址到数据域的全部字节。

以读取从站地址 1、起始寄存器 0x0000、数量 2 为例,请求帧为:01 03 00 00 00 02 C4 0B。其中 C4 0B 是 CRC 计算结果,注意发送顺序是低字节 C4 在前。若从站返回 01 03 04 00 64 00 C8 ...,表示两个寄存器值分别为 100 和 200(示例数据,用于说明字节序与解析方式)。

Modbus RTU 请求帧结构与串行链路参数 一帧 RTU 报文(读保持寄存器 03,从站地址 01) 地址域 01 功能码 03 数据域(起始地址 + 寄存器数量) 00 00 00 02 CRC 校验 C4 0B(低字节在前) 帧边界 前后 ≥3.5 字符静默 寄存器值按“高字节在前”传输;CRC 按“低字节在前”传输,两者字节序相反,是解析错误的常见来源。 串行链路参数(主从必须完全一致) 波特率 9600 / 19200 / 38400 决定 3.5 字符时间长度 数据位 / 停止位 8 数据位,1 或 2 停止位 农业设备多为 8N1 校验位 无 / 偶 / 奇校验 与 RTU 的 CRC 是两回事 超时 应答超时 300~1000 ms 重试 2~3 次 帧间隔与超时的量化关系 字符时间 = 11 位 ÷ 波特率(8N1 共 11 位) 9600 bps → 约 1.146 ms/字符 3.5 字符 ≈ 4.0 ms,作为帧结束判据 应答超时应大于“从站处理时间 + 整帧传输时间” 示例:8 字节应答 @9600 ≈ 9.2 ms,取 500 ms 余量充足 超时过短会误判丢包,过长会拖慢轮询周期

二、串口参数必须“四对齐”

RS-485 链路上,主站与每个从站的波特率、数据位、停止位、校验位必须完全一致,任何一项不同都会表现为乱码、CRC 错误或完全无响应。农业现场常见组合是 9600 bps、8 数据位、无校验、1 停止位(8N1),部分电表和水泵变频器使用 8E1(偶校验)。配置时建议先在主站侧固定参数,再逐台核对从站铭牌或参数菜单,不要凭经验假设。

需要特别区分两个“校验”:串口参数中的奇偶校验是 UART 硬件对每个字符做的位级校验,只能发现单比特错误;而 RTU 帧尾的 CRC 是对整帧字节序列做的校验,能发现绝大多数传输错误。两者同时存在、互不替代。调试时若串口助手显示“收到数据但 CRC 错”,说明波特率、数据位、停止位基本正确,问题更可能出在校验位设置、线路干扰或帧边界判断上。

三、帧间隔与超时的实际取值

RTU 规定帧内字符间隔不得超过 1.5 个字符时间,否则接收方会丢弃该帧;帧与帧之间至少静默 3.5 个字符时间。字符时间按“起始位 + 数据位 + 校验位 + 停止位”的总位数计算,8N1 为 11 位。以 9600 bps 为例,单字符约 1.146 ms,3.5 字符约 4.0 ms;19200 bps 时约 2.0 ms。主站软件通常自动处理帧间隔,但在自行编写轮询程序或使用某些 PLC 自由口通信时,必须显式保证发送完成后等待足够静默再发下一帧。

应答超时是主站等待从站回复的最长时间,应大于“从站内部处理时间 + 应答帧传输时间”。工程上常取 300~1000 ms,重试 2~3 次。超时设得过短,慢速设备(如带继电器输出的水肥机)会被误判为离线;设得过长,一台故障从站会拖慢整个轮询周期。建议先用串口助手手动发一帧,观察实际应答延迟,再据此设定。

四、CRC 校验原理与手算方法

Modbus CRC 采用 CRC-16/MODBUS:初始值 0xFFFF,多项式 0xA001(即 0x8005 的反向表示),输入输出均反射,结果不取反。计算步骤为:把 CRC 寄存器置 0xFFFF;对每个待校验字节与 CRC 低字节异或,然后右移 8 次,每次若最低位为 1 则异或 0xA001;全部字节处理完后,寄存器值即为 CRC,发送时低字节在前。

// CRC-16/MODBUS 计算(C 语言示意)
uint16_t crc16_modbus(const uint8_t *buf, uint16_t len) {
    uint16_t crc = 0xFFFF;
    for (uint16_t i = 0; i < len; i++) {
        crc ^= buf[i];
        for (uint8_t b = 0; b < 8; b++) {
            if (crc & 0x0001) crc = (crc >> 1) ^ 0xA001;
            else               crc >>= 1;
        }
    }
    return crc;   // 发送时先低字节、后高字节
}

以请求帧前 6 字节 01 03 00 00 00 02 计算,得到 CRC 为 0x0BC4,因此发送序列为 01 03 00 00 00 02 C4 0B。接收方对“地址到数据域 + 收到的 CRC”整体再算一次,若结果为 0 则校验通过。现场排查时,可用串口助手把收到的完整帧(含 CRC)输入 CRC 计算工具,结果为 0 说明帧完整;不为 0 则优先怀疑波特率偏差、校验位设置或线路干扰。

调试建议:先用串口助手以 8N1、9600 手动发送一帧已知正确的请求,确认从站有应答;再接入主站程序。这样能把“链路问题”和“程序问题”分开定位,避免在参数和代码之间反复猜测。
注意:RS-485 为半双工总线,主站发送期间必须关闭接收或做好方向控制(DE/RE 引脚),否则会把自身发送的数据回读成“应答”,造成帧错乱。多从站时还应保证地址唯一、终端电阻匹配,长距离或强干扰环境建议加隔离收发器。

把帧结构、串口参数、帧间隔和 CRC 四件事对齐后,Modbus RTU 的调试就有了清晰的判断链条:先看物理层与参数是否一致,再看帧边界与超时是否合理,最后用 CRC 结果确认帧完整性。后续的寄存器读写与异常码处理,都建立在这一层稳定通信的基础之上。