本示例使用功能码 0x14 读取文件记录,按照当前工程只处理一个子请求的格式编写。
从站地址: 1
引用类型: 0x06
文件号: 0
起始记录号: 0
读取数量: 2个寄存器
第一个寄存器值: 0x1234
第二个寄存器值: 0x5678
以下内容可以直接复制到十六进制串口助手发送:
01 14 07 06 00 00 00 00 00 02 78 E5
请求帧长度为:
12字节
| 字节 | 含义 | 说明 |
|---|---|---|
01 |
从站地址 | 从站地址为1 |
14 |
功能码 | 读取文件记录 |
07 |
后续字节数 | 一个子请求固定占7字节 |
06 |
引用类型 | Modbus文件记录规定为0x06 |
00 00 |
文件号 | 文件0 |
00 00 |
起始记录号 | 从记录0开始 |
00 02 |
记录长度 | 读取2个16位寄存器 |
78 E5 |
CRC16 | CRC低字节78在前,高字节E5在后 |
请求帧中参与CRC计算的是:
01 14 07 06 00 00 00 00 00 02
计算得到:
CRC16 = 0xE578
Modbus RTU发送CRC时低字节在前,因此帧尾为:
78 E5
假设文件0的记录0和记录1分别保存:
记录0 = 0x1234
记录1 = 0x5678
01 14 06 05 06 12 34 56 78 92 FE
响应帧长度为:
11字节
| 字节 | 含义 | 说明 |
|---|---|---|
01 |
从站地址 | 响应来自从站1 |
14 |
功能码 | 读取文件记录 |
06 |
响应数据总字节数 | 后续子响应共6字节 |
05 |
子响应长度 | 引用类型1字节加寄存器数据4字节 |
06 |
引用类型 | 固定为0x06 |
12 34 |
第一个寄存器 | 值为0x1234 |
56 78 |
第二个寄存器 | 值为0x5678 |
92 FE |
CRC16 | CRC低字节92在前,高字节FE在后 |
响应帧中参与CRC计算的是:
01 14 06 05 06 12 34 56 78
计算得到:
CRC16 = 0xFE92
因此响应帧尾发送:
92 FE
读取数量为 quantity 时,单子请求的请求帧长度固定为:
12字节
正常响应中的字段为:
子响应长度 = 1 + quantity × 2
响应数据总字节数 = 2 + quantity × 2
完整响应ADU长度 = 7 + quantity × 2
本例读取2个寄存器:
子响应长度 = 1 + 2 × 2 = 5字节
响应数据总字节数 = 2 + 2 × 2 = 6字节
完整响应ADU长度 = 7 + 2 × 2 = 11字节
请求帧中的参数由以下代码解析:
fileNumber = ModbusGetU16Be(&request[4]);
recordNumber = ModbusGetU16Be(&request[6]);
quantity = ModbusGetU16Be(&request[8]);
响应帧字段对应:
ModbusTxFrame[0] = ModbusSlaveAddress;
ModbusTxFrame[1] = 0x14U;
ModbusTxFrame[2] = (uint8_t)(2U + quantity * 2U);
ModbusTxFrame[3] = (uint8_t)(1U + quantity * 2U);
ModbusTxFrame[4] = 0x06U;
如果后续将文件号改为标准的一基编号,即文件号使用 1~16,测试请求中的文件号需要由:
00 00
改为:
00 01
同时重新计算请求帧CRC。
从站地址: 1
引用类型: 0x06
文件号: 0
起始记录号: 0
读取数量: 124个寄存器,即0x007C
以下请求可以直接复制到十六进制串口助手发送:
01 14 07 06 00 00 00 00 00 7C F8 C5
请求帧长度为:
12字节
请求帧解析如下:
| 字节 | 含义 |
|---|---|
01 |
从站地址为1 |
14 |
读取文件记录功能码 |
07 |
当前子请求包含7个字节 |
06 |
引用类型 |
00 00 |
文件号0 |
00 00 |
从记录0开始读取 |
00 7C |
读取124个寄存器 |
F8 C5 |
CRC低字节、CRC高字节 |
参与CRC计算的内容为:
01 14 07 06 00 00 00 00 00 7C
计算结果为:
CRC16 = 0xC5F8
因此请求帧末尾按照低字节在前的顺序发送:
F8 C5
读取124个寄存器时,寄存器数据占用:
124 × 2 = 248字节 = 0xF8字节
子响应长度为:
引用类型1字节 + 寄存器数据248字节
= 249字节
= 0xF9字节
响应数据总字节数为:
子响应长度字段1字节 + 子响应249字节
= 250字节
= 0xFA字节
所以正常响应格式为:
01 14 FA F9 06 [248字节寄存器数据] CRC低 CRC高
完整响应ADU长度为:
从站地址1字节
+ 功能码1字节
+ 响应数据总字节数字段1字节
+ 子响应长度字段1字节
+ 引用类型1字节
+ 寄存器数据248字节
+ CRC 2字节
= 255字节
该响应可以放入当前工程的256字节发送缓冲区:
static uint8_t ModbusTxFrame[256];
假设读取的124个寄存器全部为 0x0000,响应帧的开头为:
01 14 FA F9 06
随后是248个 00 数据字节,帧尾CRC为:
6C 45
即:
01 14 FA F9 06 [248个00] 6C 45
该响应的CRC计算值为:
CRC16 = 0x456C
Modbus RTU按照低字节在前发送,因此CRC字段为:
6C 45
发送缓冲区长度为256字节时,单个0x14子请求最多读取124个寄存器,因此数量检查应为:
if ((quantity == 0U) || (quantity > 124U))
{
ModbusSlaveStatistics.illegalValueCount++;
return ModbusBuildException(
request[1],
MODBUS_EX_ILLEGAL_VALUE);
}
如果每个 MODBUS_HISTORY_ITEM 为520字节,即260个16位寄存器,还必须保证本次读取不超过当前文件:
if (((uint32_t)recordNumber + quantity) > 260UL)
{
ModbusSlaveStatistics.illegalAddressCount++;
return ModbusBuildException(
request[1],
MODBUS_EX_ILLEGAL_ADDRESS);
}