概述
Modbus 功能码占用一个字节,取值范围是 1~127(即 0x01~0x7F)。同时,使用功能码 + 0x80 表示异常状态,即 129~255 代表异常码。
在 Modbus 标准协议中,一共规定了三类 Modbus 功能码。
公共功能码
被明确定义的功能码;
保证唯一性;
由 Modbus 协会确认,并提供公开的文档;
可进行一致性测试;
包括协议定义的功能码和保留将来使用的功能码。
用户自定义功能码
有两个用户自定义功能码区域,分别是 6572 和 100110;
用户自定义,不保证唯一性。
保留功能码
保留功能码是因为历史遗留原因,某些公司的传统产品上现行使用的功能码不作为公共使用。
本教程主要介绍公共功能码,下表展示了 Modbus 协议中的部分公共功能码。
代码 | 名称 | 英文 | 寄存器 PLC 地址 | 位/字操作 | 操作数量 |
---|---|---|---|---|---|
01 | 读线圈状态 | Read Coils | 00001~09999 | 位操作 | 单个或多个 |
02 | 读离散输入状态 | Read Discrete Inputs | 10001~19999 | 位操作 | 单个或多个 |
03 | 读保持寄存器 | Read Holding Registers | 40001~49999 | 字操作 | 单个或多个 |
04 | 读输入寄存器 | Read Input Registers | 30001~39999 | 字操作 | 单个或多个 |
05 | 写单个线圈 | Write Single Coil | 00001~09999 | 位操作 | 单个 |
06 | 写单个保持寄存器 | Write Single Register | 40001~49999 | 字操作 | 单个 |
15 | 写多个线圈 | Write Multiple Coils | 00001~09999 | 位操作 | 多个 |
16 | 写多个保持寄存器 | Write Multiple Registers | 40001~49999 | 字操作 | 多个 |
功能码的操作可分为两种:
位操作 —— 最小单位为一位(bit),包括读线圈状态功能码 01、读离散输入状态功能码 02、写单个线圈功能码 05 和写多个线圈功能码 15;
字操作 —— 最小单位为两个字节,包括读保持寄存器功能码 03、读输入寄存器功能码 04、写单个保持寄存器功能码 06 和写多个保持寄存器功能码 16。
功能码
0x01 读取线圈/离散量输出值
该功能码用于读取从设备的线圈或离散量输出的状态,即各 DO(Discrete Output,离散输出)的 ON/OFF 状态。
消息帧中指定了需读取的线圈起始地址和线圈数目。
起始地址由 2 个字节构成,取值范围为 0x0000 到 0xFFFF。
线圈数量由 2 个字节构成,取值范围为 0x0001 到 0x07D0(即十进制 1~2000)。
需要注意,在 Modbus 协议规定的 PDU 中,规定所有线圈或寄存器地址从 0 开始计算。
0x02 读取离散量输入值
该功能码用于读取从设备的离散输入,即 DI(Discrete Input)的 ON/OFF 状态。
消息帧中指定了需读取的离散输入寄存器起始地址和数目,可读取 1~2000 个连续的离散量输入状态。
如果从设备接受主设备的请求则回复功能码 02,并返回离散量输入各变量的当前状态(如果返回的 DI 数量不是 8 的整数倍,将用 0 填充最后数据字节的剩余位)。
起始地址由 2 个字节构成,取值范围为 0x0000 到 0xFFFF。
离散量数量由 2 个字节构成,取值范围为 0x0001 到 0x07D0(即十进制 1~2000),最多一次可读取 2000 个离散输入状态值。
0x03 读取保持寄存器值
该功能码用于读取从设备保持寄存器的内容,不支持广播模式。
消息帧中指定了需读取的保持寄存器的起始地址和数目,而保持寄存器中各地址的具体内容和意义则由设备开发者自行规定。
起始地址由 2 个字节构成,取值范围为 0x0000 到 0xFFFF。
寄存器数量由 2 个字节构成,取值范围为 0x0001 到 0x007D(即十进制 1~125),最多一次可连续读取 125 个寄存器值。
需要注意,Modbus 的保持寄存器和输入寄存器是以字(Word)为基本单位的(1Word 等于 2Bytes)。因此,在读取时需要注意字节序(大小端)问题。
0x04 读取输入寄存器值
该功能码用于读取从设备输入寄存器的内容,不支持广播模式。(与 03 功能码类似)
消息帧中指定了需读取的输入寄存器的起始地址和数目,而输入寄存器中各地址的具体内容和意义则由设备开发者自行规定。
起始地址由 2 个字节构成,取值范围为 0x0000 到 0xFFFF。
寄存器数量由 2 个字节构成,取值范围为 0x0001 到 0x007D(即十进制 1~125),最多一次可连续读取 125 个寄存器值。
同样需要注意字节序问题。
0x05 写单个线圈/单个离散输出
该功能码用于将单个线圈寄存器(或离散输出)设置为 ON 或 OFF,支持广播模式。
在广播模式下,所有从站设备的同一地址的值将被统一修改。
消息帧中指定了需要变更的线圈地址和设定的状态值。
起始地址由 2 个字节构成,取值范围为 0x0000 到 0xFFFF。
目标数据(即查询报文中的 ON/OFF 状态)由报文数据字段的常数指定,0xFF00 表示 ON 状态,0x0000 表示 OFF 状态,其余所有值均是非法的。
需要注意,在 Modbus 协议规定的 PDU 中,规定所有线圈或寄存器地址从 0 开始计算。
0x06 写单个保持寄存器
该功能码用于更新从设备的单个保持寄存器的值,支持广播模式。
在广播模式下,所有从站设备的同一地址的值将被统一修改。
消息帧中需要指定从设备地址以及需要变更的保持寄存器地址和设定值。
起始地址由 2 个字节构成,取值范围为 0x0000 到 0xFFFF。
变更目标数据由 2 个字节构成,取值范围为 0x0000 到 0xFFFF。
保持寄存器以字(Word)为基本单位,写入时需要注意目标数据的字节序问题。
0x08 诊断功能
该功能码仅用于串行链路,主要用于检测主设备和从设备之间的通信故障,或检测从设备的各种内部故障,该功能不支持广播。
查询报文中需要指定从设备地址、功能码(Modbus Command)以及子功能码(Diagnostic Sub-function)。其中,子功能码字段为 2 个字节,用于区别各诊断类型。
在正常的响应报文中,从设备将原样回复功能码和子功能码。
常用的 Modbus 诊断子功能码定义如下:
功能码 | 子功能码 | 描述 | 说明 |
---|---|---|---|
08 | 00(0x00) | Return Data Query (Loop-back) | 原样返回查询报文 |
08 | 01(0x01) | Restart Communications | 用于初始化并重新启动从站设备 其中,报文字段 0x00, 0x00 表示保持事件记录 0xFF, 0x00 表示清除事件记录 |
08 | 02(0x02) | Return Diagnostic Register | 返回诊断寄存器内容 |
08 | 03(0x03) | Change ASCII Input Delimiter | |
08 | 04(0x04) | Force Listen Only Mode | 强制被寻址的从站设备进入只听模式 使其与网络中的其他设备断开,不返回响应 |
08 | 10(0x0A) | Clear Counters and Diagnostic Registers | 清除计数器和诊断寄存器 |
08 | 11(0x0B) | Return Bus Message Count | 返回总线报文计数值 |
08 | 12(0x0C) | Return Bus Communication Error Count | 返回总线通信 CRC 出错计数 |
08 | 13(0x0D) | Return Bus Exception Error Count | 返回总线异常计数 |
08 | 14(0x0E) | Return Slave Message Count | 返回从站设备接收的报文数量 |
08 | 15(0x0F) | Return Slave No Response Count | 返回从站设备没有返回响应的报文数量 |
08 | 16(0x10) | Return Slave NAK Count | |
08 | 17(0x11) | Return Slave Busy Count | 返回从站设备响应忙的报文数量 |
08 | 18(0x12) | Return Bus Character Overrun Count | 返回总线字符超限的报文数量 |
08 | 19(0x13) | Return IOP Overrun Count (884) | |
08 | 20(0x14) | Clear Overrun Counter and Flag (884) |
0x0B 获取通信事件计数器
该功能码主要用于获取从设备通信计数器中的状态字和事件计数的值,不支持广播模式。
可以通过在通信报文之前和之后读取通信事件计数值,来确定从设备是否正常处理报文。
对于正常完成报文处理和传输的场合,事件计数器增加 1;而对于异常响应、轮询命令或读事件计数器(即 0x0B 功能码)的场合,则计数器不变。
通过 0x08 诊断功能中的 0x01 子功能和 0x0A 子功能,可以复位事件寄存器。
0x0C 获取通信事件记录
该功能码主要用于从从设备获取状态字、事件计数、报文计数以及事件字节字段。
其中状态字和事件计数与功能码 0x0B 获取的值一致。
报文计数器包含了加电重启、清除计数器之后的报文数量,报文计数与通过 0x08 诊断功能中的 0x0B 子功能码获取的值一致。
事件字节字段包含 0~64 个字节,定义各种事件。
正常情况下响应报文包括一个 2 Bytes 状态字字段、一个 2 Bytes 事件计数字段、一个 2 Bytes 消息计数字段以及 0~64 个字节的事件字段。
由于事件字段是变长的,因此增加了一个 1 Byte 的数据长度字段,以方便读取响应数据。
0x0F 写多个线圈
该功能码用于将连续的多个线圈(或离散输出)设置为 ON 或 OFF,支持广播模式。
在广播模式下,所有从站设备的同一地址的值将被统一修改。
消息帧中指定了需要变更的线圈起始地址和线圈数目。
起始地址由 2 个字节构成,取值范围为 0x0000 到 0xFFFF。
寄存器数量字段由 2 个字节构成,取值范围为 0x0001 到 0x07B0。
数据字段中为逻辑 1 的位对应 ON,逻辑 0 的位对应 OFF。
0x10 写多个保持寄存器
该功能码用于设置或写入从设备保持寄存器的多个连续的地址块(1~123个寄存器),支持广播模式。
在广播模式下,所有从站设备的同一地址的值将被统一修改。
消息帧中需要指定从设备地址以及需要变更的保持寄存器地址和数量。
起始地址由 2 个字节构成,取值范围为 0x0000 到 0xFFFF。
寄存器数量字段由 2 个字节构成,取值范围为 0x0001 到 0x007B(即十进制 1~123)。
在实际开发中,该功能码常用于方便用户写入多字节类型的数据,例如浮点数值。因此,需要注意字节序问题。
0x11 报告从站 ID
该功能码仅适用于串行链路,用于读取从站设备的 ID、类型描述、当前状态以及其他信息,不支持广播模式。
查询报文中没有数据字段。
响应消息的构成由从站设备决定。
总结
常用 Modbus 公共功能码下表所示。
其中,支持广播模式的功能码有:
0x05 写单个线圈
0x06 写单个保持寄存器
0x0F 写多个线圈
0x10 写多个保持寄存器
除了广播模式的报文以外,其他所有查询报文都希望能够获取一个正常的响应报文。如果一切正常,则从站设备将返回一个正常响应报文,该响应报文的功能码与请求报文的功能码一致。
另外,对于字操作的功能码,存在多字节存储的大小端问题,因此主站设备和从站设备必须保持一致的规则处理,约定 Modbus 传输中的数据字段的字节序。