3. Modbus RTU 帧结构:地址域、功能码、数据域、CRC校验、报文示例解析、字节传输顺序(LSB/MSB)

Modbus RTU 是一种紧凑、高效的二进制通信协议。理解其帧结构是进行数据采集和仪表盘数据源配置的基础。一个完整的 RTU 报文帧由四个核心域组成:地址域、功能码、数据域和CRC 校验域。每个域都有其特定的长度、功能和传输规则。

3.1 帧结构总览

RTU 帧在物理层上以连续的数据流形式传输,帧与帧之间通过至少 3.5 个字符时间的静默间隔(Silent Interval)来区分。下图展示了标准 RTU 帧的组成结构:

Modbus RTU 帧结构总览 Modbus RTU 报文帧结构 地址域 1 字节 功能码 1 字节 数据域 N 字节(0 ~ 252 字节) CRC 校验 2 字节 帧间隔 ≥ 3.5 字符 关键说明 帧总长度 = 1(地址)+ 1(功能码)+ N(数据)+ 2(CRC)字节。帧间隔用于同步,不属于帧内容。

3.2 地址域(Address Field)

地址域长度为 1 字节,取值范围为 0 ~ 247。其中:

  • 1 ~ 247:分配给从站设备的唯一地址。主站通过该地址与特定从站通信。
  • 0:广播地址。主站发送的报文可被所有从站接收并执行,但从站不回复响应。
实战提示:在配置仪表盘数据源时,务必确保从站地址与设备实际拨码或软件设置一致。地址冲突会导致通信混乱或数据采集失败。

3.3 功能码(Function Code)

功能码长度为 1 字节,用于指示主站希望从站执行的操作类型。常见功能码如下:

功能码 含义 操作对象 典型应用
0x01 读取线圈状态 位(DO) 读取继电器输出状态
0x02 读取离散输入状态 位(DI) 读取按钮、限位开关状态
0x03 读取保持寄存器 字(16-bit) 读取传感器数值、设备参数
0x04 读取输入寄存器 字(16-bit) 读取模拟量输入(AI)
0x05 写单个线圈 位(DO) 控制单个继电器通断
0x06 写单个保持寄存器 字(16-bit) 设定单个参数值
0x0F 写多个线圈 位(DO) 批量控制输出
0x10 写多个保持寄存器 字(16-bit) 批量设定参数
注意:功能码 0x01 ~ 0x04 为读取操作,0x05 ~ 0x10 为写入操作。错误的功能码会导致从站返回异常响应(功能码最高位置 1)。

3.4 数据域(Data Field)

数据域是帧中长度可变的部分,通常包含 起始地址、寄存器数量、数据值 或 字节计数 等信息。其具体结构取决于功能码:

  • 读取请求(如 0x03):数据域包含 2 字节起始地址 + 2 字节寄存器数量。
  • 读取响应(如 0x03):数据域包含 1 字节字节计数 + N 字节寄存器数据。
  • 写入请求(如 0x06):数据域包含 2 字节寄存器地址 + 2 字节写入值。
  • 写入响应(如 0x06):数据域回显请求中的地址和值,用于确认。

数据域的最大长度为 252 字节,因此单次最多可读取约 125 个保持寄存器(每个寄存器 2 字节)。

3.5 CRC 校验域(Cyclic Redundancy Check)

CRC 校验域长度为 2 字节,用于检测帧在传输过程中是否发生错误。RTU 协议采用 CRC-16(Modbus 多项式) 算法,多项式为 0x8005(初始值 0xFFFF)。

校验范围覆盖 地址域、功能码和数据域,不包含 CRC 自身。接收方计算收到的帧(除 CRC 外)的 CRC 值,并与帧尾的 CRC 值比较:

  • 一致:帧有效,执行命令。
  • 不一致:帧无效,丢弃并等待下一帧。
CRC 计算要点:发送时,CRC 的低字节(LSB)先发送,高字节(MSB)后发送。这是 RTU 协议中字节传输顺序的重要体现。

3.6 字节传输顺序:LSB 与 MSB

Modbus RTU 在传输多字节数据时,遵循 小端序(Little-Endian) 规则,即 低字节在前(LSB first),高字节在后(MSB last)。这一规则适用于:

  • 16 位寄存器值:如地址、数据、寄存器数量。
  • CRC 校验值:先发送低 8 位,再发送高 8 位。

下图展示了 16 位数据 0x1234 在 RTU 帧中的字节传输顺序:

Modbus RTU 字节传输顺序(LSB/MSB) 16 位数据 0x1234 的字节传输顺序 高字节 (MSB) 0x12 低字节 (LSB) 0x34 传输方向 先发送 0x34 (LSB) 后发送 0x12 (MSB) 规则:低字节(LSB)先传输,高字节(MSB)后传输。CRC 校验值同样遵循此顺序。

3.7 报文示例解析

以下是一个完整的 Modbus RTU 通信示例,主站读取从站地址 0x01 的保持寄存器(起始地址 0x0000,读取 2 个 寄存器)。

3.7.1 主站请求报文

字节序号 值(十六进制) 域 说明
1 0x01 地址域 从站地址
2 0x03 功能码 读取保持寄存器
3 0x00 数据域(起始地址) 起始地址高字节(MSB)
4 0x00 起始地址低字节(LSB)→ 地址 0x0000
5 0x00 数据域(寄存器数量) 数量高字节(MSB)
6 0x02 数量低字节(LSB)→ 读取 2 个寄存器
7 0x39 CRC 校验 CRC 低字节(LSB)
8 0x84 CRC 高字节(MSB)

完整请求帧:01 03 00 00 00 02 39 84

3.7.2 从站响应报文

字节序号 值(十六进制) 域 说明
1 0x01 地址域 从站地址回显
2 0x03 功能码 功能码回显
3 0x04 数据域(字节计数) 后续数据字节数(4 字节)
4 0x00 数据域(寄存器 1 值) 寄存器 1 高字节(MSB)
5 0x64 寄存器 1 低字节(LSB)→ 值 0x0064 = 100
6 0x01 数据域(寄存器 2 值) 寄存器 2 高字节(MSB)
7 0x2C 寄存器 2 低字节(LSB)→ 值 0x012C = 300
8 0x3D CRC 校验 CRC 低字节(LSB)
9 0xE1 CRC 高字节(MSB)

完整响应帧:01 03 04 00 64 01 2C 3D E1

解析要点:
  • 响应中数据域的字节计数 0x04 表示后续有 4 个数据字节(2 个寄存器 × 2 字节)。
  • 寄存器值 0x0064 和 0x012C 在帧中按 LSB/MSB 顺序传输:先低字节,后高字节。
  • CRC 校验值 0xE13D 在帧中先发送低字节 0x3D,后发送高字节 0xE1。

3.8 帧结构要点总结

域 长度 取值范围 传输顺序 关键作用
地址域 1 字节 0 ~ 247 单字节直接发送 标识目标从站
功能码 1 字节 1 ~ 255(有效码见上表) 单字节直接发送 指定操作类型
数据域 0 ~ 252 字节 取决于功能码 多字节:LSB 先,MSB 后 携带地址、数量、数据值
CRC 校验 2 字节 0x0000 ~ 0xFFFF LSB 先,MSB 后 错误检测
实战建议:在编写 Modbus RTU 采集程序或配置仪表盘数据源时,务必使用支持 CRC 校验的库(如 Python 的 pymodbus 或 minimalmodbus),避免手动计算 CRC。同时,注意字节顺序(LSB/MSB)对数据解析的影响,尤其是在处理 32 位浮点数或长整型时。