2、Modbus协议详解:Modbus RTU帧结构、Modbus TCP帧结构、功能码详解(01/02/03/04/05/06/15/16)
Modbus协议是工业自动化领域应用最广泛的通信协议之一,它简单、开放且易于实现。在将其转换为OPC UA之前,深入理解Modbus协议的底层细节至关重要。本节将重点剖析两种最常用的传输模式:Modbus RTU和Modbus TCP,并详细解释核心功能码的用法。
2.1 Modbus RTU帧结构
Modbus RTU(Remote Terminal Unit)采用二进制编码,数据以字节流形式传输,通常通过RS-232或RS-485串行接口进行通信。其帧结构紧凑,效率高,是现场总线中最常见的模式。
RTU帧结构(无校验时):
| 字段 | 长度(字节) | 描述 |
|---|---|---|
| 地址码 | 1 | 从站设备地址(1-247),0为广播地址 |
| 功能码 | 1 | 指示操作类型(如读线圈、读寄存器) |
| 数据区 | N | 包含起始地址、数据数量或具体数据值 |
| CRC校验 | 2 | 循环冗余校验(低字节在前,高字节在后) |
关键特性:
- 帧间隔:RTU要求帧与帧之间至少有3.5个字符时间的静默间隔,用于帧同步。
- 字节格式:每个字节包含1个起始位、8个数据位、1个停止位(通常无校验位或偶校验)。
- CRC计算:CRC-16(Modbus多项式:0x8005)覆盖地址码、功能码和数据区。
示例:读取从站地址为1的保持寄存器,起始地址0,读取2个寄存器。
请求帧(主站 -> 从站):
地址码: 0x01
功能码: 0x03
起始地址高字节: 0x00
起始地址低字节: 0x00
寄存器数量高字节: 0x00
寄存器数量低字节: 0x02
CRC低字节: 0xC4
CRC高字节: 0x0B
响应帧(从站 -> 主站):
地址码: 0x01
功能码: 0x03
字节数: 0x04
数据1高字节: 0x00
数据1低字节: 0x0A
数据2高字节: 0x01
数据2低字节: 0x2C
CRC低字节: 0x?? (根据实际数据计算)
CRC高字节: 0x??
2.2 Modbus TCP帧结构
Modbus TCP基于以太网,使用TCP/IP协议栈(默认端口502)。它在RTU的基础上移除了地址码和CRC校验,增加了MBAP报文头,以适应网络传输。
TCP帧结构:
| 字段 | 长度(字节) | 描述 |
|---|---|---|
| 事务处理标识符 | 2 | 用于请求/响应配对,通常由客户端递增 |
| 协议标识符 | 2 | 固定为0x0000(表示Modbus协议) |
| 长度 | 2 | 后续字节数(从单元标识符开始到数据结束) |
| 单元标识符 | 1 | 相当于RTU的地址码,用于网关路由 |
| 功能码 | 1 | 同RTU |
| 数据区 | N | 同RTU |
关键特性:
- 无CRC:TCP/IP协议本身提供可靠传输和校验,因此不再需要CRC。
- 单元标识符:在直接以太网连接时通常设为0xFF或0x00;当通过网关连接多个串行设备时,用于区分不同从站。
- 事务处理标识符:客户端必须为每个请求生成唯一ID,服务器在响应中复制该ID,用于处理并发请求。
示例:与RTU示例相同的请求(读取保持寄存器)。
请求帧(客户端 -> 服务器):
事务处理标识符: 0x0001
协议标识符: 0x0000
长度: 0x0006 (后续6个字节)
单元标识符: 0xFF
功能码: 0x03
起始地址: 0x0000
寄存器数量: 0x0002
响应帧(服务器 -> 客户端):
事务处理标识符: 0x0001
协议标识符: 0x0000
长度: 0x0007 (后续7个字节)
单元标识符: 0xFF
功能码: 0x03
字节数: 0x04
数据: 0x000A 0x012C
2.3 核心功能码详解
功能码定义了主站(或客户端)要对从站(或服务器)执行的操作。以下是在协议转换中最常用的8个功能码。
2.3.1 位操作功能码
功能码01:读线圈(Read Coils)
- 作用:读取从站的离散输出(线圈)状态,0或1。
- 请求数据:起始地址(2字节)+ 线圈数量(2字节,1-2000)。
- 响应数据:字节数(1字节)+ 线圈状态(每个线圈占1位,不足补0)。
- 应用场景:读取继电器、指示灯等开关量输出。
功能码02:读离散输入(Read Discrete Inputs)
- 作用:读取从站的离散输入状态,只读。
- 请求数据:起始地址(2字节)+ 输入数量(2字节,1-2000)。
- 响应数据:字节数(1字节)+ 输入状态(位映射)。
- 应用场景:读取按钮、限位开关等传感器信号。
功能码05:写单个线圈(Write Single Coil)
- 作用:强制一个线圈输出为ON或OFF。
- 请求数据:输出地址(2字节)+ 输出值(2字节:0xFF00表示ON,0x0000表示OFF)。
- 响应数据:与请求完全相同(回显)。
- 应用场景:控制单个继电器或阀门。
功能码15:写多个线圈(Write Multiple Coils)
- 作用:连续写入多个线圈状态。
- 请求数据:起始地址(2字节)+ 线圈数量(2字节)+ 字节数(1字节)+ 输出值(位映射)。
- 响应数据:起始地址(2字节)+ 线圈数量(2字节)。
- 应用场景:批量设置一组输出设备的状态。
2.3.2 寄存器操作功能码
功能码03:读保持寄存器(Read Holding Registers)
- 作用:读取从站的保持寄存器(16位,可读写)。
- 请求数据:起始地址(2字节)+ 寄存器数量(2字节,1-125)。
- 响应数据:字节数(1字节)+ 寄存器值(每个寄存器2字节,高字节在前)。
- 应用场景:读取模拟量输出值、PID参数、设备配置等。
功能码04:读输入寄存器(Read Input Registers)
- 作用:读取从站的输入寄存器(16位,只读)。
- 请求数据:起始地址(2字节)+ 寄存器数量(2字节,1-125)。
- 响应数据:字节数(1字节)+ 寄存器值。
- 应用场景:读取模拟量输入(如温度、压力传感器值)。
功能码06:写单个寄存器(Write Single Register)
- 作用:写入一个保持寄存器的值。
- 请求数据:寄存器地址(2字节)+ 寄存器值(2字节)。
- 响应数据:与请求完全相同(回显)。
- 应用场景:设定单个模拟量输出或修改单个参数。
功能码16:写多个寄存器(Write Multiple Registers)
- 作用:连续写入多个保持寄存器。
- 请求数据:起始地址(2字节)+ 寄存器数量(2字节)+ 字节数(1字节)+ 寄存器值(N*2字节)。
- 响应数据:起始地址(2字节)+ 寄存器数量(2字节)。
- 应用场景:批量更新设备参数表或下载配置数据。
2.4 协议转换中的关键注意事项
在将Modbus数据映射到OPC UA时,需要特别注意以下几点:
- 地址偏移:Modbus协议中地址通常从0开始(如保持寄存器40001对应地址0x0000),而OPC UA节点ID通常从1开始或自定义。转换时需处理地址偏移。
- 数据类型映射:Modbus寄存器是16位无符号整数。32位浮点数、32位整数或字符串需要跨寄存器组合。例如,一个32位浮点数占用两个连续的保持寄存器(如地址0和1),转换时需按IEEE 754标准解析。
- 字节序:Modbus默认大端序(高字节在前)。但某些设备可能使用小端序。在转换引擎中必须支持字节序配置。
- 错误处理:Modbus异常响应(功能码+0x80)需映射为OPC UA的BadStatus或异常状态码。
- 性能优化:对于大量连续地址的读取,优先使用功能码03/04的批量读取,而非逐个读取,以减少网络开销。