一、Modbus 事务处理
Modbus 是一个请求与应答的通信协议。它像一次对话,主设备(客户端)发起请求,从设备(服务器端)进行处理并回复响应。
1. 协议分层:PDU 与 ADU
为了适应不同的网络(如串口RS485、以太网TCP/IP),Modbus 协议分为两层:
-
PDU(协议数据单元) :这是协议的核心,与具体网络无关。格式固定为:功能码 + 数据。功能码告诉从设备要做什么(如读或写),数据区提供具体信息(如寄存器地址、数量)。
-
ADU(应用数据单元) :这是在 PDU 基础上,为适应具体传输网络而增加的完整数据包。在串行网络中,它会在 PDU 前加上从设备地址 ,在后面加上差错校验码(如CRC)。
通用 Modbus 帧(ADU)结构如下:
| 地址域 | 功能域 (PDU) | 差错校验 |
|---|---|---|
| 1字节 | 功能码 (1字节) + 数据 (N字节) | 2字节 (如CRC) |
2. 事务处理过程
-
正常处理:主设备发送查询ADU。从设备收到后,执行功能码指定的操作,然后返回一个正常的响应ADU。响应中通常包含原功能码和请求的数据或操作确认。
-
异常处理 :如果从设备接收到的请求有错误(如功能码不支持、寄存器地址不存在),它会返回一个异常响应。异常响应的功能码是在原功能码的最高位加上1(即原功能码值 + 0x80),并附带一个异常码来说明错误原因。
二、Modbus 功能码详解
功能码是 Modbus 协议的灵魂,它定义了操作的类型。功能码主要分为三类:公共功能码(已明确定义)、用户自定义功能码(65-72, 100-110)和保留功能码。
以下是常用的公共功能码详解:
01 (0x01) -- 读取线圈
-
功能 :读取从设备线圈(即可读可写的数字量输出DO)的ON/OFF状态。
-
查询报文:需指定起始线圈地址(2字节,从0开始)和要读取的线圈数量(2字节,1-2000)。
-
响应报文 :返回一个字节计数,后面跟着状态数据。每个线圈的状态用1个比特(bit)表示(1=ON, 0=OFF),8个线圈的状态 packed 成1个字节。字节内的顺序是:第一个字节的最低位(LSB)对应起始地址线圈的状态。
-
举例:读线圈地址19开始的37个状态。响应需要5个字节来存放37个比特的状态信息。
02 (0x02) -- 读取离散量输入
-
功能 :读取从设备离散输入(即只读的数字量输入DI)的ON/OFF状态。
-
报文格式 :与功能码01完全相同,只是功能码不同。同样指定起始地址和数量(1-2000),返回按比特打包的状态字节。
03 (0x03) -- 读取保持寄存器
-
功能 :读取从设备保持寄存器(即可读可写的模拟量或参数,16位)的值。
-
查询报文:需指定起始寄存器地址(2字节)和要读取的寄存器数量(2字节,1-125)。
-
响应报文:返回一个字节计数(=寄存器数量 × 2),后面跟着每个寄存器的数据(每个寄存器2字节,高字节在前)。
-
重要提示 :保持寄存器以字(Word,2字节) 为单位。若要读取32位浮点数,需要连续读取2个寄存器(4字节),并注意主从设备之间字节序(大小端)必须一致。
04 (0x04) -- 读取输入寄存器
-
功能 :读取从设备输入寄存器(即只读的模拟量输入AI,16位)的值。
-
报文格式 :与功能码03完全相同,只是功能码不同。同样需要注意多字节数据的字节序问题。
05 (0x05) -- 写单个线圈
-
功能 :将单个线圈设为ON或OFF。支持广播模式(地址0)。
-
查询报文 :指定线圈地址(2字节)和设定值(2字节,只能为0xFF00(ON)或0x0000(OFF))。
-
响应报文:成功时,原样返回查询报文作为确认。
06 (0x06) -- 写单个保持寄存器
-
功能 :写入单个保持寄存器的值。支持广播模式。
-
查询报文:指定寄存器地址(2字节)和要写入的16位数据(2字节)。
-
响应报文:成功时,原样返回查询报文作为确认。
08 (0x08) -- 诊断功能(仅用于串行链路)
-
功能 :用于通信链路诊断和内部故障检测。通过2字节的子功能码区分具体诊断类型。
-
常见子功能码:
-
0x0000:原样返回查询数据(用于回环测试)。 -
0x0001:重启通信选项。 -
0x000A:清除计数器和诊断寄存器。 -
0x000B-0x0012:返回各种通信错误计数(如CRC错误、异常响应、设备忙等计数)。
-
-
响应报文:通常根据子功能码返回相应的诊断数据。
15 (0x0F) -- 写多个线圈
-
功能 :连续写入多个线圈的状态。支持广播模式。
-
查询报文 :指定起始地址、线圈数量(1-2000),以及一个字节数字段,后面跟着状态数据。状态数据中,每个线圈占1比特,打包成字节。
-
响应报文:返回起始地址和成功写入的线圈数量作为确认。
16 (0x10) -- 写多个保持寄存器
-
功能 :连续写入多个保持寄存器的值。支持广播模式。
-
查询报文 :指定起始地址、寄存器数量(1-123),以及一个字节数字段(=数量 × 2),后面跟着要写入的每个寄存器的数据(每个2字节)。
-
响应报文:返回起始地址和成功写入的寄存器数量作为确认。
-
重要应用 :此功能码常用于写入32位浮点数(需占2个寄存器)或64位双精度浮点数(需占4个寄存器),必须严格约定字节序。
其他功能码摘要
-
11 (0x0B):获取通信事件计数器。返回状态字和事件计数值。
-
12 (0x0C):获取通信事件记录。返回更详细的事件日志,包括状态字、事件计数、消息计数和事件字节。
-
17 (0x11):报告从站ID(仅用于串行链路)。读取从设备的标识信息、运行状态等。
三、Modbus 异常响应
当从设备无法正常处理请求时,会返回异常响应。
-
异常响应格式:
-
功能码 = 原始请求功能码 + 0x80(即将最高位置1)。
-
数据区 = 1字节的异常码。
-
-
常见异常码:
-
01:非法功能码(不支持该功能)。
-
02:非法数据地址(请求的寄存器地址不存在)。
-
03:非法数据值(发送的数据超出有效范围或格式错误)。
-
04:从站设备故障(从设备内部执行错误)。
-
举例 :主设备发送功能码04请求读取不存在的输入寄存器。从设备返回功能码 0x84 (04+0x80) 和异常码 0x02。
四、核心要点与复习提示
-
牢记四种寄存器:结合硬件理解线圈(DO)、离散输入(DI)、保持寄存器(AO/参数)、输入寄存器(AI)。
-
理解通信模型:掌握"地址+功能码+数据+校验"的ADU结构,以及"请求-响应"的事务流程。
-
区分位与字操作:
-
功能码 01, 02, 05, 15 操作位(比特),状态打包在字节里。
-
功能码 03, 04, 06, 16 操作字(2字节),数据以16位为单位传输。
-
-
注意地址起始 :在协议PDU中,所有线圈和寄存器地址都从0开始计数。
-
关注广播模式:功能码05、06、15、16支持广播(地址0),用于同时控制所有从设备。
-
警惕字节序问题 :处理32/64位数据(浮点数)时,必须在主从设备间约定好字节顺序(大端或小端)。
-
看懂异常响应:功能码值大于0x80表示异常,需根据异常码排查问题。
通过分析具体功能码的报文示例,并理解正常与异常的处理路径,可以深入掌握 Modbus 协议的工作机制,为实际开发和调试打下坚实基础。
