Modbus通信协议
Modbus
Modbus协议的相关知识
概要
Modbus 是一主多从的通信协议,最多有247个从设备
- 单播模式 主请求,从相应
- 广播模式 主请求,从事务处理而不要求返回应答。所以请求指令必须是Modbus标准功能中的写指令
Modbus寄存器
寄存器可以指具体的物理寄存器,也指一块内存区域
- RTU
2. ASCII
对于串行链路来说:ASCII || RTU (串行链路:信息的各位数据被逐位按顺序传送的线路)
3. TCP
ModBus ASCII(不常用)消息帧格式
每8bit都作为两个ASCII字符发送
起始
地址
功能代码
数据
LRC校验
结束
1字符
2字符
2字符
0~2*252字符
2字符
2字符
CR,LF
ModBus RTU 消息帧格式
注意:
- 区别前后两帧:在RTU模式中,消息的发送和接收以至少3.5个字符时间的停顿间隔为标志,当波特率大于19200bps,为了减轻CPU的负担,时间间隔使用固定值,如1.5个字符时间
- 确定连续:在一帧报文中,必须以连续的字符流发送整个报文帧,如果两个字符间隔大于1.5个字符时间,则认为报文信息不完整,被丢弃
波特率(串行通信):每秒传输二进制位的个数
地址域
地址字段即设备的地址
0
1~247
248~255
广播地址
从站地址
保留
功能码域
1字节(1~255)
正常情况下返回的响应消息帧设置同样功能码,异常情况下最高位(MSB)置1
数据域
存放功能码需要操作的具体数据
字节序和大小端
大端(Big-Endian):数据的低位保存在高地址
小端(Small-Endian):数据的低位保存在低地址
Modbus TCP/IP 消息帧格式
ADU > PDU
PDU = Function code + Data
ADU = PDU + Additional address + Error check
\= Additional address + Function code + Data + Error check
聚焦Modbus TCP:定义MBAP(Modbus Application Header)
Modbus TCP/IP ADU = MBAP Header + PDU
\=MBAP Header + Function code + Data
Modbus TCP/IP 协议的最大帧数据长度为260个字节,0~6字节构成MBAP报头
Modbus TCP协议格式
对于Modbus TCP,帧结构略有不同:
- 帧结构 : 每个Modbus TCP帧由以下部分组成:
-
事务标识符 : 2字节,用于事务的唯一标识。
-
协议标识符 : 2字节,通常为0,表示Modbus协议。
-
长度字段 : 2字节,表示后续字节的长度(包括功能码和数据字段)。
-
在Modbus协议及许多其他通信协议中,长度信息通常使用两个字节(16位)表示,这是因为
- 标识范围
- 使用两个字节可以表示的长度范围是 0 到 65535(2^16 - 1)。这使得协议能够支持更大的数据量。
- 如果只使用一个字节(8位),最大长度只能表示到 255(2^8 - 1),这在某些应用中可能不够用。
- 兼容性与标准化
- 使用高位和低位分开表示长度信息是一种标准做法,符合许多网络协议的设计原则。这种设计使得协议在处理多字节数据时保持一致性。
- 在计算机内存中,数据通常以字节为单位存储,多个字节的数据(如16位、32位等)需要分开处理。
- 便于网络传输
- 在网络传输中,数据通常以字节流的形式发送。将长度信息分为高位和低位可以方便地在不同的系统之间进行传输。
- 一些系统可能采用大端字节序(Big Endian),而另一些可能采用小端字节序(Little Endian)。将长度信息分为两个字节可以适应不同的字节序。
- 提高容错率
-
将长度信息分为高八位和低八位是为了表示更大的数据范围,符合协议标准化,便利网络传输,并提高数据的容错性。这种设计在许多通信协议中是常见的做法。
-
功能码 : 1字节,指定要执行的操作。
-
在Modbus协议中,异常响应的功能码是通过将请求的功能码按位或(OR)运算与 0x80(即10000000)结合而成的
pBuf[7] = pFun | 0x80; // 将功能码 pFun 的高位设置为1,以指示这是一个异常响应
- 数据字段 : 可变长度,包含与功能码相关的数据
- 帧示例
+----------+----------+----------+----------+----------+
| 事务标识 | 协议标识 | 长度 | 功能码 | 数据字段 |
+----------+----------+----------+----------+----------+
对于C++代码来说,用来检查可以先定义一个header
U8View header = ToU8View(co_await pSock->Recv(6, sc_ModbusTimeoutMs));
- 检查协议标识:
if (header[2] != 0 || header[3] != 0) {
Warn(fmt::format("{}Wrong protocol idenifier.", pConn));
co_return false;
}
- 检查长度标识
if (header[4] != 0 || header[5] < 6) {
Warn(fmt::format("{}Wrong length idenifier.", pConn));
co_return false;
}
Modbus 功能码详解
概要
取值范围:1~127
异常时:功能码+0x80(十进制128)
- 公共功能码
- 用户自定义功能码
6572和100110 - 保留功能码
01(0x01)读取线圈/离散量输出状态
功能
读取从设备的线圈或离散量的输出状态(DO, Discrete Output离散输出)ON/OFF的状态
02 (0x02) 读取离散量输入值
功能
读取离散量输入,即DI(Discrete Input)的ON/OFF状态