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 只读输入寄存器。

Modbus 常用功能码:请求 → 响应 / 异常 主站请求 地址 + 功能码 + 数据 + CRC 位读取 01 读线圈 02 读离散输入 寄存器读取 03 读保持寄存器 04 读输入寄存器 单位写入 05 写单个线圈 06 写单个寄存器 批量写入 15 写多个线圈 16 写多个寄存器 正常响应 功能码原样返回 数据域:读取值 / 写入确认 01/02:字节数 + 位图 03/04:字节数 + 寄存器值 05/06/15/16:地址 + 数量 异常响应 功能码 + 0x80 后跟 1 字节异常码 01 非法功能 02 非法数据地址 03 非法数据值 04 从站设备故障 05/06 确认 / 忙 报文关键字段 起始地址 数量 / 字节数 线圈值 0xFF00 / 0x0000 寄存器值 2 字节 CRC 低字节在前 位图按字节打包 低位对应低地址 异常码 1 字节 功能码最高位置 1 数量范围 1~2000 寄存器 1~125 写入 1~123

二、方法流程:八个功能码的请求与响应格式

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 个寄存器,数量超限。拆分为两次写入后正常。

排查建议:先看功能码是否被 +0x80,确认是异常响应;再读异常码,区分“不支持”“地址错”“值错”“设备故障”“忙”;最后核对地址范围、数量上限、字节序和 CRC。
常见误区:把 01 和 02 混用、把 03 和 04 混用;忽略 05 输出值必须是 0xFF00 或 0x0000;忽略 15/16 的数量上限;把异常码 0x05 当作失败,其实它表示从站已确认但需要继续等待。

掌握这八个功能码的请求与响应格式,并建立“功能码 + 异常码 + 地址/数量约束”的联合判断习惯,就能在调试和运维中快速定位大多数 Modbus 通信问题,为高可靠通信与异常处理打下坚实基础。