4. 常用功能码详解:01读线圈、02读离散输入、03读保持寄存器、04读输入寄存器、05写单线圈、06写单寄存器、15/16批量写操作
Modbus 功能码是主站与从站之间通信的指令核心。本章将详细解析 RTU 模式下最常用的 8 个功能码,涵盖位操作与字操作、读操作与写操作。掌握这些功能码的报文结构、数据范围及典型应用场景,是进行数据采集与仪表盘配置的基础。
4.1 功能码分类总览
Modbus 功能码按操作对象可分为位操作和字操作两大类,按方向可分为读操作和写操作。下表列出了本章涉及的 8 个核心功能码及其基本属性:
| 功能码 | 名称 | 操作对象 | 操作方向 | 最大操作数量 | 典型应用 |
|---|---|---|---|---|---|
| 01 (0x01) | 读线圈 | 位 (Bit) | 读 | 2000 个 | 读取继电器、开关状态 |
| 02 (0x02) | 读离散输入 | 位 (Bit) | 读 | 2000 个 | 读取按钮、限位开关状态 |
| 03 (0x03) | 读保持寄存器 | 字 (Word) | 读 | 125 个 | 读取模拟量输出、参数设置值 |
| 04 (0x04) | 读输入寄存器 | 字 (Word) | 读 | 125 个 | 读取模拟量输入、传感器数据 |
| 05 (0x05) | 写单线圈 | 位 (Bit) | 写 | 1 个 | 控制单个继电器通断 |
| 06 (0x06) | 写单寄存器 | 字 (Word) | 写 | 1 个 | 设定单个参数值 |
| 15 (0x0F) | 写多线圈 | 位 (Bit) | 写 | 1968 个 | 批量控制输出点 |
| 16 (0x10) | 写多寄存器 | 字 (Word) | 写 | 123 个 | 批量设定参数或下发配置 |
4.2 读操作功能码详解
4.2.1 功能码 01:读线圈
读取从站 线圈(Coil)的 ON/OFF 状态。线圈是可读可写的位对象,通常对应物理继电器或数字量输出点。起始地址从 0 开始,对应 PLC 中的 %Q 或 %M 区。
- 请求报文: 从站地址 + 0x01 + 起始地址高字节 + 起始地址低字节 + 数量高字节 + 数量低字节 + CRC
- 响应报文: 从站地址 + 0x01 + 字节数 + 数据字节1...N + CRC
- 数据打包: 每个线圈占用 1 位,按字节打包,最后一个字节不足 8 位时高位补 0。
4.2.2 功能码 02:读离散输入
读取从站 离散输入(Discrete Input)的 ON/OFF 状态。离散输入是只读的位对象,通常对应物理按钮、开关或传感器数字信号。起始地址从 0 开始,对应 PLC 中的 %I 区。
- 请求报文: 从站地址 + 0x02 + 起始地址高字节 + 起始地址低字节 + 数量高字节 + 数量低字节 + CRC
- 响应报文: 从站地址 + 0x02 + 字节数 + 数据字节1...N + CRC
- 与 01 功能码区别: 01 操作可读可写对象,02 操作只读对象;两者报文结构完全相同,仅功能码不同。
4.2.3 功能码 03:读保持寄存器
读取从站 保持寄存器(Holding Register)的当前值。保持寄存器是 16 位可读可写的字对象,用于存储模拟量输出值、设定参数、累计值等。起始地址从 0 开始,对应 PLC 中的 %AQ 或 %MW 区。
- 请求报文: 从站地址 + 0x03 + 起始地址高字节 + 起始地址低字节 + 数量高字节 + 数量低字节 + CRC
- 响应报文: 从站地址 + 0x03 + 字节数 + 寄存器数据高字节+低字节... + CRC
- 数据格式: 每个寄存器占用 2 字节,高字节在前(Big-Endian)。
4.2.4 功能码 04:读输入寄存器
读取从站 输入寄存器(Input Register)的当前值。输入寄存器是 16 位只读的字对象,用于读取模拟量输入信号,如温度、压力、流量等传感器数据。起始地址从 0 开始,对应 PLC 中的 %AI 区。
- 请求报文: 从站地址 + 0x04 + 起始地址高字节 + 起始地址低字节 + 数量高字节 + 数量低字节 + CRC
- 响应报文: 从站地址 + 0x04 + 字节数 + 寄存器数据高字节+低字节... + CRC
- 与 03 功能码区别: 03 操作可读可写对象,04 操作只读对象;两者报文结构完全相同,仅功能码不同。
- 01 与 02:位读取,报文结构相同,区别在于读写属性。
- 03 与 04:字读取,报文结构相同,区别在于读写属性。
- 位读取最大数量为 2000 个(约 250 字节),字读取最大数量为 125 个(250 字节)。
4.3 写操作功能码详解
4.3.1 功能码 05:写单线圈
强制单个线圈为 ON 或 OFF 状态。该操作具有确定性,无论线圈当前状态如何,写入后立即生效。
- 请求报文: 从站地址 + 0x05 + 线圈地址高字节 + 线圈地址低字节 + 数据高字节 + 数据低字节 + CRC
- 数据值约定: ON = 0xFF00,OFF = 0x0000。其他值视为无效。
- 响应报文: 与请求报文完全相同(回显),表示写入成功。
4.3.2 功能码 06:写单寄存器
将单个 16 位值写入保持寄存器。常用于设定单个参数或下发单点控制指令。
- 请求报文: 从站地址 + 0x06 + 寄存器地址高字节 + 寄存器地址低字节 + 数据高字节 + 数据低字节 + CRC
- 响应报文: 与请求报文完全相同(回显),表示写入成功。
4.3.3 功能码 15:写多线圈
批量写入多个线圈的状态。相比多次使用 05 功能码,15 功能码可以大幅提高通信效率。
- 请求报文: 从站地址 + 0x0F + 起始地址高字节 + 起始地址低字节 + 数量高字节 + 数量低字节 + 字节数 + 数据字节1...N + CRC
- 数据打包: 与读线圈相同,按位打包,每个字节低位对应起始地址。
- 响应报文: 从站地址 + 0x0F + 起始地址高字节 + 起始地址低字节 + 数量高字节 + 数量低字节 + CRC
4.3.4 功能码 16:写多寄存器
批量写入多个保持寄存器的值。常用于下发配置参数表或批量设定模拟量输出。
- 请求报文: 从站地址 + 0x10 + 起始地址高字节 + 起始地址低字节 + 数量高字节 + 数量低字节 + 字节数 + 寄存器数据高字节+低字节... + CRC
- 响应报文: 从站地址 + 0x10 + 起始地址高字节 + 起始地址低字节 + 数量高字节 + 数量低字节 + CRC
- 写操作(05、06、15、16)必须确保目标地址支持写入操作,否则从站会返回异常码 02(非法数据地址)。
- 批量写操作(15、16)的起始地址和数量必须连续,不能跨区操作。
- 在仪表盘数据源配置中,写功能码通常用于“控制”或“设定”类组件,如按钮、滑块、输入框。
4.4 功能码报文结构对比
为了更直观地理解不同功能码的报文差异,下图展示了读操作与写操作在 RTU 模式下的通用报文结构对比:
4.5 功能码选择策略
在实际的 Modbus 数据采集项目中,合理选择功能码可以优化通信效率。以下是一些实用建议:
- 连续读取: 尽量使用批量读取(01/02/03/04)代替单点读取,减少报文交互次数。
- 批量写入: 当需要同时修改多个线圈或寄存器时,优先使用 15/16 功能码,而非循环调用 05/06。
- 只读与可读写区分: 对于传感器数据(只读),使用 04 功能码;对于可调参数,使用 03/06/16 功能码。
- 地址规划: 在仪表盘数据源配置中,将需要采集的变量按功能码类型分组,连续排列地址,以支持批量读取。