Modbus通信协议

Admin·9/25/2024·0 views
C++网络编程

Modbus

Modbus协议的相关知识

概要

Modbus 是一主多从的通信协议,最多有247个从设备

  1. 单播模式 主请求,从相应
  2. 广播模式 主请求,从事务处理而不要求返回应答。所以请求指令必须是Modbus标准功能中的写指令

Modbus寄存器

寄存器可以指具体的物理寄存器,也指一块内存区域

  1. RTU

2. ASCII

对于串行链路来说:ASCII || RTU (串行链路:信息的各位数据被逐位按顺序传送的线路)

3. TCP

ModBus ASCII(不常用)消息帧格式

每8bit都作为两个ASCII字符发送

起始

地址

功能代码

数据

LRC校验

结束

1字符

2字符

2字符

0~2*252字符

2字符

2字符
CR,LF

ModBus RTU 消息帧格式

注意:

  1. 区别前后两帧:在RTU模式中,消息的发送和接收以至少3.5个字符时间的停顿间隔为标志,当波特率大于19200bps,为了减轻CPU的负担,时间间隔使用固定值,如1.5个字符时间
  2. 确定连续:在一帧报文中,必须以连续的字符流发送整个报文帧,如果两个字符间隔大于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,帧结构略有不同:

  1. 帧结构 : 每个Modbus TCP帧由以下部分组成:
  • 事务标识符 : 2字节,用于事务的唯一标识。

  • 协议标识符 : 2字节,通常为0,表示Modbus协议。

  • 长度字段 : 2字节,表示后续字节的长度(包括功能码和数据字段)。

  • 在Modbus协议及许多其他通信协议中,长度信息通常使用两个字节(16位)表示,这是因为

  1. 标识范围
  • 使用两个字节可以表示的长度范围是 0 到 65535(2^16 - 1)。这使得协议能够支持更大的数据量。
  • 如果只使用一个字节(8位),最大长度只能表示到 255(2^8 - 1),这在某些应用中可能不够用。
  1. 兼容性与标准化
  • 使用高位和低位分开表示长度信息是一种标准做法,符合许多网络协议的设计原则。这种设计使得协议在处理多字节数据时保持一致性。
  • 在计算机内存中,数据通常以字节为单位存储,多个字节的数据(如16位、32位等)需要分开处理。
  1. 便于网络传输
  • 在网络传输中,数据通常以字节流的形式发送。将长度信息分为高位和低位可以方便地在不同的系统之间进行传输。
  • 一些系统可能采用大端字节序(Big Endian),而另一些可能采用小端字节序(Little Endian)。将长度信息分为两个字节可以适应不同的字节序。
  1. 提高容错率
  • 将长度信息分为高八位和低八位是为了表示更大的数据范围,符合协议标准化,便利网络传输,并提高数据的容错性。这种设计在许多通信协议中是常见的做法。

  • 功能码 : 1字节,指定要执行的操作。

  • 在Modbus协议中,异常响应的功能码是通过将请求的功能码按位或(OR)运算与 0x80(即10000000)结合而成的

pBuf[7] = pFun | 0x80;        // 将功能码 pFun 的高位设置为1,以指示这是一个异常响应

  • 数据字段 : 可变长度,包含与功能码相关的数据
  1. 帧示例
+----------+----------+----------+----------+----------+
| 事务标识 | 协议标识 | 长度     | 功能码   | 数据字段 |
+----------+----------+----------+----------+----------+

对于C++代码来说,用来检查可以先定义一个header

    U8View header = ToU8View(co_await pSock->Recv(6, sc_ModbusTimeoutMs));

  1. 检查协议标识:
if (header[2] != 0 || header[3] != 0) {
        Warn(fmt::format("{}Wrong protocol idenifier.", pConn));
        co_return false;
    }

  1. 检查长度标识
 if (header[4] != 0 || header[5] < 6) {
        Warn(fmt::format("{}Wrong length idenifier.", pConn));
        co_return false;
    }

Modbus 功能码详解

概要

取值范围:1~127

异常时:功能码+0x80(十进制128)

  1. 公共功能码
  2. 用户自定义功能码
    6572和100110
  3. 保留功能码

01(0x01)读取线圈/离散量输出状态

功能

读取从设备的线圈或离散量的输出状态(DO, Discrete Output离散输出)ON/OFF的状态

02 (0x02) 读取离散量输入值

功能

读取离散量输入,即DI(Discrete Input)的ON/OFF状态

Comments (0)