3. Modbus RTU 帧结构:地址域、功能码、数据域、CRC校验、报文示例解析、字节传输顺序(LSB/MSB)
Modbus RTU 是一种紧凑、高效的二进制通信协议。理解其帧结构是进行数据采集和仪表盘数据源配置的基础。一个完整的 RTU 报文帧由四个核心域组成:地址域、功能码、数据域和CRC 校验域。每个域都有其特定的长度、功能和传输规则。
3.1 帧结构总览
RTU 帧在物理层上以连续的数据流形式传输,帧与帧之间通过至少 3.5 个字符时间的静默间隔(Silent Interval)来区分。下图展示了标准 RTU 帧的组成结构:
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) | 批量设定参数 |
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 值比较:
- 一致:帧有效,执行命令。
- 不一致:帧无效,丢弃并等待下一帧。
3.6 字节传输顺序:LSB 与 MSB
Modbus RTU 在传输多字节数据时,遵循 小端序(Little-Endian) 规则,即 低字节在前(LSB first),高字节在后(MSB last)。这一规则适用于:
- 16 位寄存器值:如地址、数据、寄存器数量。
- CRC 校验值:先发送低 8 位,再发送高 8 位。
下图展示了 16 位数据 0x1234 在 RTU 帧中的字节传输顺序:
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 后 | 错误检测 |
pymodbus 或 minimalmodbus),避免手动计算 CRC。同时,注意字节顺序(LSB/MSB)对数据解析的影响,尤其是在处理 32 位浮点数或长整型时。