Modbus RTU 协议介绍
异步串行传输方式,采用二进制格式,适用于串行通讯(如RS-485),效率高,是工业现场的主流选择。
主站是Master,从站是Slave。
Modbus RTU 协议格式
帧结构
- 地址码(1字节):从设备的地址,取值1-247。
- 功能码(1字节):指定操作类型,如读取或写入。
- 数据区(长度可变):包含具体的操作数据。
- CRC校验(2字节):循环冗余校验码,确保数据无误。
功能码
功能码 | 功能 | 用途 |
---|---|---|
01 | 读线圈状态 | 获取开关量输出状态 |
03 | 读保持寄存器 | 获取16位可读写数据 |
04 | 读输入寄存器 | 获取16位只读数据 |
05 | 写单个线圈 | 控制单个开关量输出 |
06 | 写单个寄存器 | 修改单个16位数据 |
16(0x10 十六进制) | 写多个寄存器 | 修改多个16位数据 |
CRC 校验
CRC(循环冗余校验)是Modbus RTU的关键,用于检测传输错误。发送方计算CRC并附加在帧尾,接收方重新计算并比对。
CRC16校验算法示例:
const unsigned short wCRCTalbeAbs[] =
{0x0000, 0xCC01, 0xD801, 0x1400, 0xF001, 0x3C00, 0x2800, 0xE401, 0xA001, 0x6C00, 0x7800, 0xB401, 0x5000, 0x9C01, 0x8801, 0x4400,
};unsigned short mt_api_crc16(unsigned char *ptr, unsigned int len)
{unsigned short wCRC = 0xFFFF;unsigned short i;unsigned char chChar;unsigned char temp[2];for (i = 0; i < len; i++){chChar = *ptr++;wCRC = wCRCTalbeAbs[(chChar ^ wCRC) & 15] ^ (wCRC >> 4);wCRC = wCRCTalbeAbs[((chChar >> 4) ^ wCRC) & 15] ^ (wCRC >> 4);}temp[0] = wCRC&0xFF; temp[1] = (wCRC>>8)&0xFF;wCRC = (temp[0]<<8)|temp[1];return wCRC;
}
0x01 功能码(读线圈状态)
请求数据
01 01 00 00 00 08 9C 03
- 01:从设备地址(1-247)
- 01:功能码(读线圈状态)
- 00 00:起始线圈地址(2 字节)
- 00 08:线圈个数(读取 8 个线圈)
- 9C 03:CRC校验
更详细的格式如下:
从设备地址 + 功能码 + 起始线圈地址(高位) + 起始线圈地址(低位) + 线圈数量(高位) + 线圈数量(低位) + CRC(高位) + CRC(低位),一共 8 个字节。
响应数据
01 01 01 55 78 3A
- 01:从设备地址(1-247)
- 01:功能码(读线圈状态)
- 01:线圈状态数据的总字节数(
<font style="color:rgb(0, 0, 0);">ceil(线圈数量/8)</font>
,8 个线圈 = 1 字节) - 55:线圈状态数据(1 个字节)
- 78 3A:CRC 校验
更详细的格式如下:
从设备地址 + 功能码 + 数据长度 + [线圈状态数据(N 字节)] + CRC(高位) + CRC(低位)
0x03 功能码(读保持寄存器)
请求数据
01 03 00 00 00 01 C4 0B
- 01:从设备地址(1-247)
- 03:功能码(读保持寄存器)
- 00 00:起始寄存器地址(2 字节)
- 00 01:寄存器个数(读取 1 个寄存器)
- C4 0B:CRC校验
更详细的格式如下:
从设备地址 + 功能码 + 起始寄存器地址(高位) + 起始寄存器地址(低位) + 寄存器数量(高位) + 寄存器数量(低位) + CRC(高位) + CRC(低位),一共 8 个字节。
响应数据
01 03 02 19 98 B2 7E
- 01:从设备地址(1-247)
- 03:功能码(读保持寄存器)
- 02:后面数据的字节数,一个寄存器有 2 个字节,所以后面的字节数等于 2*查询的寄存器数量
- 19 98:寄存器的值是 19 98
- B2 7E:CRC 校验
更详细的格式如下:
从设备地址 + 功能码 + 数据长度 + 寄存器数据 + CRC 校验码
字节数:1+1+1+2*N+2
0x04 功能码(读输入寄存器)
请求数据
01 04 00 01 00 03 60 0A
- 01:从设备地址(1-247)
- 04:功能码(写输入寄存器)
- 00 01:起始寄存器地址(2 字节)
- 00 03:输入寄存器数量
- 60 0A:CRC 校验
更详细的格式如下:
从设备地址 + 功能码 + 起始寄存器地址(高位) + 起始寄存器地址(低位) +寄存器数量(高位) + 寄存器数量(低位) + CRC 校验(高位) + CRC 校验(高位),一共 8 个字节。
响应数据
01 04 06 00 0A 01 F4 03 E8 75 3B
- 01:从设备地址(1-247)
- 04:功能码(写输入寄存器)
- 06:输入寄存器数据的总字节数(寄存器数量 ×2,3×2=6 字节)
- 00 0A:第一个输入寄存器值
- 01 F4:第二个输入寄存器值
- 03 E8:第三个输入寄存器值
- 75 3B:CRC 校验
更详细的格式如下:
从设备地址 + 功能码 + 数据长度 + [N 组寄存器数据(每组 2 字节)] + CRC 校验(高位) + CRC 校验(高位)
0x05 功能码(写单个线圈)
请求数据
01 05 00 03 FF 00 8C 3A
- 01:从设备地址(1-247)
- 05:功能码(写单个线圈)
- 00 03:目标线圈地址
- FF 00:线圈状态值
- 8C 3A:CRC 校验
更详细的格式如下:
从设备地址 + 功能码 + 目标线圈地址(高位) + 目标线圈地址(低位) + 线圈状态值(高位) + 线圈状态值(低位) + CRC 校验(高位) + CRC 校验(高位),一共 8 个字节。
响应数据
01 05 00 03 FF 00 8C 3A
- 01:从设备地址(1-247)
- 05:功能码(写单个线圈)
- 00 03:目标线圈地址
- FF 00:线圈状态值
- 8C 3A:CRC 校验
更详细的格式如下:
从设备地址 + 功能码 + 目标线圈地址(高位) + 目标线圈地址(低位) + 线圈状态值(高位) + 线圈状态值(低位) + CRC 校验(高位) + CRC 校验(高位),一共 8 个字节。
0x06 功能码( 写单个保持寄存器)
请求数据
01 06 00 00 00 01 48 0A
- 01:从设备地址(1-247)
- 06:功能码(写保持寄存器)
- 00 00:起始寄存器地址(2 字节)
- 00 01:写入的值
- 48 0A:CRC校验
更详细的格式如下:
从设备地址 + 功能码 + 起始寄存器地址(高位) + 起始寄存器地址(低位) + 写入值(高位) + 写入值(低位) + CRC(高位) + CRC(低位),一共 8 个字节。
响应数据
01 06 00 00 00 01 48 0A
- 01:从设备地址(1-247)
- 06:功能码(写保持寄存器)
- 00 00:起始寄存器地址(2 字节)
- 00 01:写入的值
- 48 0A:CRC校验
更详细的格式如下:
从设备地址 + 功能码 + 起始寄存器地址(高位) + 起始寄存器地址(低位) + 写入值(高位) + 写入值(低位) + CRC(高位) + CRC(低位),一共 8 个字节。
0x010 功能码( 写多个保持寄存器)
请求数据
01 10 00 02 00 03 06 00 0A 01 02 03 04 75 F5
- 01:从设备地址(1-247)
- 10:功能码(写保持寄存器)
- 00 02:起始寄存器地址(2 字节)
- 00 03:寄存器数量
- 06:后续数据的总字节数(寄存器数量 x 2)
- 00 0A:写入数据 1(2 字节)
- 01 02:写入数据 3(2 字节)
- 03 04:写入数据 3(2 字节)
- 75 F5:CRC 校验
更详细的格式如下:
从设备地址 + 功能码 + 起始寄存器地址(高位)+ 起始寄存器地址(低位) + 寄存器数量(高位) + 寄存器数量(低位) + 数据总字节数 + [N 组写入值(每组 2 字节)] + CRC 高位 + CRC 低位
响应数据
01 10 00 02 00 03 A7 3C
- 01:从设备地址(1-247)
- 10:功能码(写保持寄存器)
- 00 02:起始寄存器地址(2 字节)
- 00 03:寄存器数量
- A7 3C:CRC 校验
更详细的格式如下:
从设备地址 + 功能码 + 起始寄存器地址(高位)+ 起始寄存器地址(低位) + 寄存器数量(高位) + 寄存器数量(低位) + CRC 高位 + CRC 低位。