1、背景
工作需要,需要使用Modbus-RTU实现RS485通信,于是简单学习并实践了一下。
2、参考资料
3、协议说明
3.1、协议类型
当前设备采用Modbus-RTU协议,采用CRC-16_Modbus校验算法,数据链路层使用用标准串口协议,物理层采用RS485进行数据传输。
3.2、帧结构
帧结构 | 说明 |
---|---|
地址码(1) | 指定待通信的设备地址,可以是广播地址、单设备地址或多设备地址。地址码占1字节,有效值范围是1~247。 |
功能码 (1) | 指令类型,详细说明程序执行的操作是读取、写入还是查询串口通信协议。功能码占1字节,有效值范围是1~255 |
起始地址(2) | 寄存器地址,2个字节 |
数据(N) | 根据执行的命令指令来封装数据内容,数据长度为多字节 |
CRC校验码(2) | 数据传输前需要计算校验和,检验数据是否传输正确。CRC校验占2字节 |
3.3、功能码
功能码 | 说明 |
---|---|
01H | 读取输出线圈 |
02H | 读取输入线圈 |
03H | 读取保持寄存器 |
04H | 读取输入寄存器 |
05H | 写入单线圈 |
06H | 写入单寄存器 |
0FH | 写入多线圈 |
10H | 写入多寄存器 |
4、协议主、从机数据交互
4.1、数据帧格式
4.1.1、读数据 读单个或多个寄存器03H
1)主机发送
从机地址 | 功能码 | 寄存器起始地址 | 寄存器个数 | CRC16校验 |
---|---|---|---|---|
1Byte | 1Byte | 2Byte,高字节在前 | 2Byte,高字节在前 | 2Byte,低字节在前 |
2)从机接收
从机地址 | 功能码 | 数据长度(字节数) | 寄存器数据 | CRC16校验 |
---|---|---|---|---|
1Byte | 1Byte | 1Byte | N,高字节在前 | 2Byte,低字节在前 |
4.1.2、写数据 写单个寄存器06H
1)主机发送
从机地址 | 功能码 | 寄存器起始地址 | 寄存器数据 | CRC16校验 |
---|---|---|---|---|
1Byte | 1Byte | 2Byte,高字节在前 | N,高字节在前 | 2Byte,低字节在前 |
2)从机接收
从机地址 | 功能码 | 寄存器起始地址 | 寄存器数据 | CRC16校验 |
---|---|---|---|---|
1Byte | 1Byte | 2Byte,高字节在前 | N,高字节在前 | 2Byte,低字节在前 |
4.1.3、写数据 写多个寄存器10H
1)主机发送
从机地址 | 功能码 | 寄存器起始地址 | 寄存器数量 | 寄存器数据总字节数(2*寄存器数量) | 数据 | CRC16校验 |
---|---|---|---|---|---|---|
1Byte | 1Byte | 2Byte,高字节在前 | 2Byte,高字节在前 | 1Byte | N,高字节在前 | 2Byte,低字节在前 |
2)从机接收
从机地址 | 功能码 | 寄存器起始地址 | 寄存器数量 | CRC16校验 |
---|---|---|---|---|
1Byte | 1Byte | 2Byte,高字节在前 | 2Byte,高字节在前 | 2Byte,低字节在前 |
5、协议示例
5.1、寄存器地址划分
5.2 、查询03H
5.2.1 、查询两个寄存器
单个寄存器对应寄存器数量设为01H,可以自行实现
- 主机发送帧
- 从机回复帧
5.2.2 查询N个寄存器
- 主机发送帧
- 从机回复帧
5.3、设置
5.3.1、主机设置单个寄存器的值
就是写数据帧
- 主机发送帧
- 从机回复帧
5.3.2、主机设置两个寄存器的值
- 主机发送数据帧
- 从机回复数据帧
5.3.3 、主机设置N个寄存器的值
-
主机发送数据帧
-
从机回复数据帧
6、计算校验值
6.1、crc16在线计算校验值
RC校验(循环冗余校验)小知识 CRC即循环冗余校验码(Cyclic Redundancy
Check):是数据通信领域中最常用的一种查错校验码,其特征是信息字段和校验字段的长度可以任意选定。循环冗余检查(CRC)是一种数据传输检错功能,对数据进行多项式计算,并将得到的结果附在帧的后面,接收设备也执行类似的算法,以保证数据传输的正确性和完整性。CRC算法参数模型解释: NAME:参数模型名称。 WIDTH:宽度,即CRC比特数。
POLY:生成项的简写,以16进制表示。例如:CRC-32即是0x04C11DB7,忽略了最高位的"1",即完整的生成项是0x104C11DB7。
INIT:这是算法开始时寄存器(crc)的初始化预置值,十六进制表示。 REFIN:待测数据的每个字节是否按位反转,True或False。
REFOUT:在计算后之后,异或输出之前,整个数据是否按位反转,True或False。
XOROUT:计算结果与此参数异或后得到最终的CRC值。其他相关工具:LRC校验BCC校验
提示:复制数据时请注意字符串首尾的一些不可见字符!
注意:要注意校验值高字节低字节的顺序,必要时需要转换位置
其他参考资料:
主机实现:
Modbus主机实现
从机实现:
Modbus从机实现
https://blog.csdn.net/weixin_31301271/article/details/120334602
6.2、代码实现
6.2.1、crc16校验
注意:注意高低字节位置
#include <stdio.h>
/* CRC余式表 */
const unsigned int crc_table[256] = {
0x0000, 0xc0c1, 0xc181, 0x0140, 0xc301, 0x03c0, 0x0280, 0xc241,
0xc601, 0x06c0,