文章目录
- [0 背景](#0 背景)
- [1 协议介绍](#1 协议介绍)
-
- [1.1 modbusRTU协议](#1.1 modbusRTU协议)
-
- [1.1.1 简介](#1.1.1 简介)
- [1.1.2 RS485和modbusRTU的关系](#1.1.2 RS485和modbusRTU的关系)
- [1.1.3 modbusRTU 协议格式](#1.1.3 modbusRTU 协议格式)
-
- [1.1.3.1 0x10写多个保持寄存器](#1.1.3.1 0x10写多个保持寄存器)
- [1.1.3.2 0x02读多个离散输入寄存器](#1.1.3.2 0x02读多个离散输入寄存器)
- [1.1.3.3 0x03读多个保持寄存器](#1.1.3.3 0x03读多个保持寄存器)
- [1.1.3.4 0x04读多个输入寄存器](#1.1.3.4 0x04读多个输入寄存器)
- [1.2 ModbusTCP协议](#1.2 ModbusTCP协议)
-
- [1.2.1 ModbusTCP协议介绍](#1.2.1 ModbusTCP协议介绍)
- [1.2.2 ModbusTCP协议和 modbusRTU 协议区别](#1.2.2 ModbusTCP协议和 modbusRTU 协议区别)
- [1.2.3 ModbusTCP协议格式](#1.2.3 ModbusTCP协议格式)
- [2 实践](#2 实践)
-
- [2.1 使用modbus RTU协议](#2.1 使用modbus RTU协议)
- [2.2 使用modbus TCP协议](#2.2 使用modbus TCP协议)
- [2.1 计算CRC校验(modbus TCP不需要计算)](#2.1 计算CRC校验(modbus TCP不需要计算))
- 参考
0 背景
因为需要使用TCP与RS485串口的仪表设备进行通信,所以查阅相关资料,进行实验测试通过后,记录通信过程。
1 协议介绍
1,上位机电脑与RS485串口的设备硬件仪表设备直接通信,使用的是modbusRTU协议。
2,上位机电脑如果使用TCP协议,与RS485串口的设备硬件仪表设备间接通信,则需要借助ModbusTCP协议。然后中间设备把ModbusTCP协议协议中的包体拆解为ModbusRTU协议的内容,传递给硬件仪表设备。
1.1 modbusRTU协议
使用的如下设备,使用四路PWM信号输出的波形信号来驱动仪表。
1.1.1 简介
Modbus是一种串行通信协议,是Modicon公司(现在的施耐德电气 Schneider
Electric)于 1979 年为使用可编程逻辑控制器(PLC)通信而发表。Modbus已经成为工
业领域通信协议的业界标准(De facto),并且现在是工业电子设备之间常用的连接方式。
Modbus-RTU协议是一种总线协议,采用一主多从的结构。即同一个总线网络中,只能一
个主机(主站),多个从机(从站)。
通讯时采用一问一答的方式,主动发送请求帧的是主机,被动回复响应帧的是从机,从机
不会主动发送数据。
1.1.2 RS485和modbusRTU的关系
RS485 是一种传输数据的通讯方式,modbusRTU是一种通讯协议。
两者的关系类似声音和语言的关系,RS485是声音,ModbusRTU是其中一种语言,两个人说话,能听到对方的声音,但是听不懂对方的语言,是无法交流理解的。
1.1.3 modbusRTU 协议格式
设备地址:设备的通讯地址、站号。
功能码:对数据帧的功能编号。
寄存器:存放某类数据的内存区域。一个设备可能有多种寄存器,不同的寄存器存
放不同类别的数据。
寄存器地址:某个数据在寄存器里的编号。不同的设备定义不同。
1.1.3.1 0x10写多个保持寄存器
1.1.3.2 0x02读多个离散输入寄存器
1.1.3.3 0x03读多个保持寄存器
1.1.3.4 0x04读多个输入寄存器
举例:读取设备地址0x01,从输入寄存器0x0000开始,连续读0x0002个寄
存器
请求帧:01 04 00 00 00 02 71 CB
响应帧: 01 04 04 01 60 01 61 3A 1E
数据长度0x04个字节,因为一个寄存器2个字节
第1个寄存器 = 0x0160 = 352
第2个寄存器 = 0x0161 = 353
1.2 ModbusTCP协议
使用如下设备(有人物联网),通过设置做服务器,设置ip和通信方式。
设置ip、端口和协议相关信息。
1.2.1 ModbusTCP协议介绍
ModbusTCP是一种基于以太网的通信协议.ModbusTCP协议由施耐德公司在1996年推出,它继承了ModbusRTU协议的核心功能,但主要通过TCP/IP以太网进行数据传输,实现了设备之间的主从式通信。
对于ModbusTCP,我们通过使用服务器/客户端来定义双方的角色。
1.2.2 ModbusTCP协议和 modbusRTU 协议区别
从上图中,我们可以看到ModbusTCP在Modbus串行通信的基础上,去除了差错校验和附加地址(即从站地址),然后加上MBAP报文头(7 Bytes)。
因为
- 1,ModbusTCP协议一般用 于TCP或UDP通信,而TCP和UDP本身就具备数据校验,因此不需要再加校验了;
- 2,ModbusTCP主要用于以太网通信,因此,不再需要通过附加地址(即从站地址) 来区分不同的设备,因为以太网设备一般会使用IP地址来区分。
1.2.3 ModbusTCP协议格式
2 实践
2.1 使用modbus RTU协议
例如我们要给RS485硬件设备发送01 10 00 04 00 02 04 00 00 00 01 33 9C
的数据(modbusRTU协议的硬件数据)。
01 10 00 04 00 02 04 00 00 00 01 33 9C
数据中,按照modbusRTU协议解析可以得到:01
为设备地址,10
为功能码,00 04
为修改寄存器的起始地址,00 02
为修改的寄存器个数, 00 00 00 01
为数据长度(一个寄存器,两个字节),33 9C
为校验地址。
计算CRC校验的方法见后文。
测试工具:
1,RS485线,;
2,示波器;
2.2 使用modbus TCP协议
例如我们要给RS485硬件设备发送01 10 00 04 00 02 04 00 00 00 01 33 9C
的数据(modbus RTU协议的硬件数据),但是我们使用modbus TCP协议(上位机做客户端,转发硬件左服务器)。需要对数据进行再次封装。在01 10 00 04 00 02 04 00 00 00 01 33 9C
的基础上,去掉差错校验和附加地址,变为10 00 04 00 02 04 00 00 00 01
(长度为10个字节),再加上modbus TCP协议的MBAP报文头(00 01 00 00 00 0B 01
),得到TCP最终发送的数据为:00 01 00 00 00 0B 01 10 00 04 00 02 04 00 00 00 01
。
2.1 计算CRC校验(modbus TCP不需要计算)
cpp
uint16_t crc16Two(unsigned char *data) {
unsigned char i,j;
unsigned short crc = 0xffff;
for(i = 0; i < 11; i++){
crc = crc^*data;
for(j = 0;j < 8;j++){
if(crc&0x01){
crc = (crc>>1)^0xa001;//多项式a001
}else{
crc= (crc>>1);
}
}
data++;
}
return crc;
}
QByteArray calculateCrc(QByteArray data) {
// 计算 CRC
unsigned char *data2 = (unsigned char *)data.data();
uint16_t crc = crc16Two(data2);
return charExpressionToByteArray(QString::number(crc, 16).toUpper().rightJustified(4, '0')); // 转换为大写十六进制并补零
}
因为协议里,是按照大端字节序的顺序,因此还需要把计算出来的字节翻转一下。
cpp
QByteArray frameTail = calculateCrc(resData);
std::reverse(frameTail.begin(), frameTail.end());//翻转数据