4. 常用功能码解析
Modbus RTU 从站开发的本质,是把主站发来的“功能码 + 数据”翻译成对本地寄存器或线圈的操作,再把结果按协议格式回填。功能码决定了操作对象、数据长度和字节序,因此理解 01/02/03/04/05/06/15/16 这八个常用功能码的请求与响应格式,是从站协议栈能否正确收发数据的关键。下面先给出整体数据流,再逐一拆解。
一、读操作: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 的边界处理,还是实现异常码的精细化返回,都可以在这套格式框架上直接叠加。