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 个 批量设定参数或下发配置
提示: 功能码 01、02、03、04 是数据采集的基石,而 05、06、15、16 是实现远程控制的关键。在仪表盘数据源配置中,读功能码用于填充图表数据,写功能码用于实现控制指令下发。

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 模式下的通用报文结构对比:

读操作与写操作报文结构对比 读操作请求 (01/02/03/04) 从站地址 功能码 起始地址 数量 CRC 读操作响应 (01/02/03/04) 从站地址 功能码 字节数 数据 (N字节) CRC 写操作请求 (05/06/15/16) 从站地址 功能码 起始地址 数据/数量 CRC 写操作响应 (05/06/15/16) 从站地址 功能码 起始地址 数量 CRC 说明: • 05/06 响应 = 请求(回显) • 15/16 响应 = 地址 + 数量 • 批量写请求含字节数+数据

4.5 功能码选择策略

在实际的 Modbus 数据采集项目中,合理选择功能码可以优化通信效率。以下是一些实用建议:

  • 连续读取: 尽量使用批量读取(01/02/03/04)代替单点读取,减少报文交互次数。
  • 批量写入: 当需要同时修改多个线圈或寄存器时,优先使用 15/16 功能码,而非循环调用 05/06。
  • 只读与可读写区分: 对于传感器数据(只读),使用 04 功能码;对于可调参数,使用 03/06/16 功能码。
  • 地址规划: 在仪表盘数据源配置中,将需要采集的变量按功能码类型分组,连续排列地址,以支持批量读取。
实战要点: 在编写数据采集程序时,建议先使用 Modbus 调试工具(如 ModScan、Modbus Poll)验证每个功能码的报文交互,确认从站支持的地址范围和数据类型,再集成到仪表盘数据源中。