4. Modbus功能码详解
Modbus 的互操作性建立在“功能码 + 数据单元”的严格约定之上。主站发出的请求帧中,功能码决定从站要执行什么操作;从站返回的响应帧中,功能码与数据区共同说明操作结果。理解 01、02、03、04、05、06、15、16 这八个常用功能码的请求与响应格式,是排查通信异常、解析报文和设计高可靠通信程序的基础。
一、核心原理:功能码如何组织请求与响应
Modbus 应用层协议数据单元(PDU)由功能码和数据域组成。串行链路再在 PDU 前加上从站地址,在末尾加上 CRC 校验,形成 ADU。主站请求时,功能码表示“读”或“写”;从站正常响应时,功能码原样返回,数据域携带读取结果或写入确认;从站异常响应时,功能码最高位置 1,即“原功能码 + 0x80”,随后跟一个异常码,说明失败原因。
八个常用功能码可分为四类:位读取(01 读线圈、02 读离散输入)、寄存器读取(03 读保持寄存器、04 读输入寄存器)、单位写入(05 写单个线圈、06 写单个寄存器)、批量写入(15 写多个线圈、16 写多个寄存器)。其中 01、05、15 操作的是位对象,02 只读离散输入,03、06、16 操作保持寄存器,04 只读输入寄存器。
二、方法流程:八个功能码的请求与响应格式
01 读线圈与 02 读离散输入:请求格式为“功能码 + 起始地址(2 字节)+ 数量(2 字节)”。响应为“功能码 + 字节数 + 位图数据”,位图按字节打包,最低位对应起始地址。01 用于可读写的线圈,02 用于只读的离散输入。
03 读保持寄存器与 04 读输入寄存器:请求格式为“功能码 + 起始地址 + 寄存器数量”。响应为“功能码 + 字节数 + 寄存器值”,每个寄存器 2 字节,高字节在前。03 对应可读写保持寄存器,04 对应只读输入寄存器。
05 写单个线圈:请求为“功能码 + 线圈地址 + 输出值”,输出值 0xFF00 表示置 ON,0x0000 表示置 OFF。正常响应原样返回请求,便于主站确认。
06 写单个寄存器:请求为“功能码 + 寄存器地址 + 写入值(2 字节)”。正常响应同样原样返回请求。
15 写多个线圈:请求为“功能码 + 起始地址 + 数量 + 字节数 + 位图数据”。响应为“功能码 + 起始地址 + 数量”,用于确认写入范围。
16 写多个寄存器:请求为“功能码 + 起始地址 + 数量 + 字节数 + 寄存器值”。响应为“功能码 + 起始地址 + 数量”。
示例:读保持寄存器请求(从站 0x01,起始 0x0000,数量 0x0002)
01 03 00 00 00 02 C4 0B
正常响应(2 个寄存器,共 4 字节)
01 03 04 00 0A 00 14 4A 3D
异常响应(功能码 0x83,异常码 0x02 非法数据地址)
01 83 02 C0 F1
三、异常码含义与典型场景
异常码是定位问题的第一线索。常见异常码包括:01 非法功能,从站不支持该功能码;02 非法数据地址,请求的地址超出从站支持范围;03 非法数据值,数量或写入值不合法;04 从站设备故障,从站执行时发生内部错误;05 确认,从站已接收请求但需要较长时间处理;06 从站忙,从站正在处理长任务,主站应稍后重试。
| 功能码 | 操作对象 | 请求关键字段 | 正常响应关键字段 | 常见异常码 |
|---|---|---|---|---|
| 01 | 线圈 | 起始地址、数量 | 字节数、位图 | 01、02、03 |
| 02 | 离散输入 | 起始地址、数量 | 字节数、位图 | 01、02、03 |
| 03 | 保持寄存器 | 起始地址、数量 | 字节数、寄存器值 | 01、02、03 |
| 04 | 输入寄存器 | 起始地址、数量 | 字节数、寄存器值 | 01、02、03 |
| 05 | 单个线圈 | 地址、0xFF00/0x0000 | 原样返回 | 01、02、03 |
| 06 | 单个寄存器 | 地址、写入值 | 原样返回 | 01、02、03 |
| 15 | 多个线圈 | 起始地址、数量、字节数、位图 | 起始地址、数量 | 01、02、03 |
| 16 | 多个寄存器 | 起始地址、数量、字节数、值 | 起始地址、数量 | 01、02、03、04 |
四、案例与复盘:从异常码反推配置问题
某现场主站使用 03 功能码读取从站保持寄存器,起始地址 0x1000,数量 10,从站返回 01 83 02 ...。功能码 0x83 表示 03 的异常响应,异常码 0x02 表示非法数据地址。检查发现从站保持寄存器实际只映射到 0x0000~0x00FF,主站地址超出范围。将起始地址改为 0x0000 后通信恢复。
另一案例中,主站用 16 功能码批量写入 130 个寄存器,从站返回异常码 0x03。原因是 Modbus 规范中 16 功能码单次最多写 123 个寄存器,数量超限。拆分为两次写入后正常。
掌握这八个功能码的请求与响应格式,并建立“功能码 + 异常码 + 地址/数量约束”的联合判断习惯,就能在调试和运维中快速定位大多数 Modbus 通信问题,为高可靠通信与异常处理打下坚实基础。