4. 常用功能码解析

Modbus RTU 从站开发的本质,是把主站发来的“功能码 + 数据”翻译成对本地寄存器或线圈的操作,再把结果按协议格式回填。功能码决定了操作对象、数据长度和字节序,因此理解 01/02/03/04/05/06/15/16 这八个常用功能码的请求与响应格式,是从站协议栈能否正确收发数据的关键。下面先给出整体数据流,再逐一拆解。

Modbus RTU 常用功能码请求/响应数据流 主站请求 地址+功能码+数据+CRC 从站响应 地址+功能码+数据+CRC 功能码分类 01 / 02 读线圈/离散量 03 / 04 读保持/输入寄存器 05 / 06 写单线圈/单寄存器 15 / 16 写多线圈/多寄存器 从站解析与执行 校验地址 → 校验CRC → 判断功能码 → 操作寄存器/线圈 → 组帧 正常响应 功能码最高位为 0 返回数据或回显请求 异常响应 功能码最高位置 1(+0x80) 数据域为 1 字节异常码 常见异常码 0x01 非法功能码 0x02 非法数据地址 0x03 非法数据值 0x04 从站设备故障 0x05 确认(处理中) 0x06 从站设备忙

一、读操作:01/02/03/04 的请求与响应

01 和 02 分别读取线圈和离散输入,请求格式相同:从站地址 + 功能码 + 起始地址(2 字节)+ 数量(2 字节)+ CRC。响应由“字节数 + 数据字节”组成,数据按位打包,低位在前。例如读取 10 个线圈,需要 2 个数据字节,最后一个字节的高 6 位补 0。

03 和 04 分别读取保持寄存器和输入寄存器,请求格式与 01/02 一致,但数量以“寄存器个数”计,响应中字节数 = 寄存器个数 × 2,每个寄存器 2 字节,高字节在前。这是初学者最容易出错的地方:位操作按位打包,寄存器操作按字打包,两者不能混用。

读保持寄存器请求(03):
01 03 00 00 00 02 C4 0B
从站地址=01,功能码=03,起始地址=0x0000,数量=2,CRC=C40B

正常响应:
01 03 04 00 0A 00 0B 7A 3D
字节数=04,寄存器0=0x000A,寄存器1=0x000B

二、写操作:05/06 与 15/16 的区别

05 写单个线圈,请求中数据域为 0xFF00 表示置位(ON),0x0000 表示复位(OFF),其他值视为非法。06 写单个保持寄存器,数据域为 2 字节写入值。两者的正常响应都是“原样回显请求”,主站据此确认写入成功。

15 写多个线圈,请求格式为:起始地址 + 数量 + 字节数 + 数据字节 + CRC,响应只回显起始地址和数量。16 写多个寄存器,请求格式为:起始地址 + 数量 + 字节数 + 数据字节 + CRC,响应同样只回显起始地址和数量。注意 15/16 的响应不包含写入数据,主站不能靠响应判断数据内容,只能靠 CRC 和回显地址确认。

功能码 操作对象 请求关键字段 正常响应
01线圈起始地址、数量字节数 + 位数据
02离散输入起始地址、数量字节数 + 位数据
03保持寄存器起始地址、数量字节数 + 寄存器值
04输入寄存器起始地址、数量字节数 + 寄存器值
05单线圈地址、0xFF00/0x0000回显请求
06单寄存器地址、写入值回显请求
15多线圈地址、数量、字节数、数据回显地址和数量
16多寄存器地址、数量、字节数、数据回显地址和数量

三、异常响应:功能码最高位置 1

当从站无法正常执行请求时,会返回异常响应。异常响应的结构非常固定:从站地址 + 功能码(原功能码 + 0x80)+ 异常码(1 字节)+ CRC。例如 03 的异常响应功能码为 0x83,06 的异常响应功能码为 0x86。主站收到后,用“功能码 & 0x80”判断是否为异常,再读取异常码定位原因。

常见误区:很多初学者在从站代码中直接返回原功能码加错误数据,导致主站无法识别异常。正确做法是:功能码最高位置 1,数据域只放 1 字节异常码,不要附加其他数据。

四、案例:从站处理 03 与 16 的完整流程

假设从站地址为 0x01,保持寄存器 0x0000~0x0009 有效。主站发送 03 读取 0x0000 开始的 2 个寄存器,从站校验地址和 CRC 后,判断功能码为 03,检查起始地址和数量是否越界,然后按高字节在前组帧返回。若主站发送 16 写入 0x0000 开始的 2 个寄存器,从站先校验字节数是否等于数量 × 2,再逐个写入,最后回显起始地址和数量。

写多个寄存器请求(16):
01 10 00 00 00 02 04 00 0A 00 0B 7B 8C
从站地址=01,功能码=10(16),起始地址=0x0000,数量=2,字节数=04,数据=000A 000B

正常响应:
01 10 00 00 00 02 41 0B
回显起始地址=0x0000,数量=2

异常响应(地址越界):
01 90 02 C4 0B
功能码=0x90(0x10+0x80),异常码=0x02(非法数据地址)

五、复盘:从站实现中的检查清单

  • 地址校验:从站地址不匹配时不应响应,避免总线冲突。
  • CRC 校验:CRC 错误直接丢弃,不返回任何数据。
  • 功能码支持:不支持的功能码返回异常码 0x01。
  • 地址范围:起始地址 + 数量超出有效范围返回 0x02。
  • 数据值:05 的数据不是 0xFF00/0x0000 时返回 0x03。
  • 字节数:15/16 的字节数必须与数量匹配,否则返回 0x03。
  • 响应长度:异常响应固定为 5 字节,正常响应按功能码计算。

调试建议:用串口助手抓取原始帧,先看地址和 CRC,再看功能码最高位,最后看数据长度。把 01/02 的位打包和 03/04 的字打包分开验证,能快速定位大多数从站解析错误。

掌握这八个功能码的请求与响应格式,等于掌握了 Modbus RTU 从站数据面的主干。后续无论是扩展 0x0F/0x10 的边界处理,还是实现异常码的精细化返回,都可以在这套格式框架上直接叠加。