You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

26 KiB

title author top cover toc mathjax summary tags categories reprintPolicy abbrlink date coverImg img password
ModbusTcp TianZD true true true false ModbusTcp通讯相关学习笔记,参考多篇博文,其中有些已经找不到原文链接 [modbus 学习笔记] [Modbus 转载] cc_by c7022bb7 2022-04-29 13:49:25 <nil> <nil> <nil>

[toc]

ModBusTCP

术语

寄存器

寄存器的功能是存储二进制代码,它是由具有存储功能的触发器组合起来构成的。一个触发器可以存储1位二进制代码,故存放n位二进制代码的寄存器,需用n个触发器来构成。

PLC中输入寄存器输出寄存器均为16位,2个字节

00:一个字节:0

00 0A:两个字节:10

校验码

校验码是由前面的数据通过某种算法得出的,用以检验该组数据的正确性。代码作为数据在向计算机或其它设备进行输入时,容易产生输入错误,为了减少这种输入错误,编码专家发明了各种校验检错方法,并依据这些方法设置了校验码。

常用的校验有:累加和校验SUM、字节异或校验XOR、纵向冗余校验LRC、循环冗余校验CRC……

离散量输入

主要用来读取单个位的数据,如IO的状态

线圈

开关输出信号,主要用来写入单个位的数据,与离散量构成组成对位的操作;

ModBus

概述

一种应用层协议

Modbus通信协议,是Modicon PLC所制定的资料交换通信接口标准,于1979年首先制定串行通信标准(含Modbus异步及Modbus Plus同步),于1997年制定网络通信标准(Modbus/TCP)。是属于OSI所定义的通信层的第七层应用层(Application Layer)。是为Client/Server或称为 Master/Slave型式的通信协议。由于Modbus的通信协议简单容易设计,结果广被许多控制设备或外围信号设备所采用,因此无形中成为自控业界的标准。Modbus异步的硬件架构简单,被使用的比率最高。Modbus Plus同步的协议可以提供高速的通信速度,适合主控制设备间大量资料交换。Modbus/TCP则是因应Ethernet网络的架构,近年来被大量使用的通信协议,也因为其速度及资料传送量远比Modbus Plus更快更大,所以已渐渐取代其功能。

Modbus通信协议基本上是遵循MasterandSlave的通信步骤,有一方扮演Master角色采取主动询问方式,送出QueryMessage给Slave方,然后由Slave方依据接到的QueryMessage内容准备ResponseMessage回传给Master。即使目前硬件通信已经可以达到双方互相主动通信的能力,但是于Modbus通信协议的规定,必须一方为Master,另一方为Slave不能互换角色。一般使用上,监控系统(HMI)都为Master,PLC、电表、仪表等都为Slave,HMI系统一直PollingSlave的各种relayandregister最新数值,然后做显示及各种逻辑计算及控制调整等处理。

Modbus大家庭,有三个兄弟:大哥(Modbus-RTU协议)、二哥(Modbus-ASCII协议)和ModbutTcp,都活跃在工业通信领域。大哥和二哥擅长串行通信,比如基于RS485或者RS232的通信,而ModbutTcp则擅长基于以太网的通信。由于底层所使用的结构不同,应用数据单元(Application Data Unit,ADU)有所不同。

Modbus协议的数据模型

深入理解Modbus协议的数据模型和地址模型

数据模型是对可访问数据的一种抽象,Modbus协议的数据模型定义了四种可访问的数据,分别是:

  • 离散量输入(Discrete Input);

  • 线圈(Coils);

  • 输入寄存器(Input registers);

  • 保持寄存器(Holding registers);

其中,离散量输入和线圈只支持以位(bit)的方式进行访问,输入寄存器和保持寄存器只支持以字(WORD)的方式进行访问;离散量输入和输入寄存器只支持以只读的方式进行访问,而线圈和保持寄存器既可以读也可以写;

数据模型中成员的特点如下面的表格:

image-20220126154248046

既然数据模型是一种抽象,在实际使用时必须将其映射到真实的物理存储区才能被访问。

Modbus协议允许设备将四种数据分别映射到不同的存储区块中,各个区块之间相互独立,使用不同的功能码可读取到不同的数值,如下图所示:

image-20220126154419939

Modbus协议也允许设备将四种数据映射到同一存储区块中,这样通过不同的功能码读取数据可能会得到相同的数据(比如:输入寄存器和保持寄存器为同一物理区块),如下图所示:

image-20220126154513588

数据模型中的每一种数据都最多允许有65536个元素(编号1~65536),元素的地址编号从0开始,因此地址的范围为:0~65535;

需要说明的是:65536只是协议允许的最大元素范围,但并不要求全部实现。Modbus协议允许设备根据自己的实际情况实现部分元素,甚至不要求实现模型中全部四种数据;

Modbus协议的地址模型

为了简化数据模型与设备存储区的对应关系,引入了一种地址模型。该模型通过编号的方式对不同类型数据进行区分,各数据的地址编号请看下面的表格:

image-20220126154701232

Modbus地址模型的编号从1开始。

由于每一种数据都最大支持65536个元素,因此理论上,

对于线圈型数据来说,其地址范围为:000001~065536;

类似的,

离散量输入,其地址范围为:100001~165536;

输入寄存器,其地址范围为:300001~365536;

保持寄存器,其地址范围为:400001~465536;

由于65536是比较大的数值,实际应用一般不需要这么大的存储区,因此PLC厂家普遍采用的是10000以内的地址范围,即:

线圈地址范围:00001~09999;

离散量输入地址范围:10001~19999;

输入寄存器地址范围:30001~39999;

保持寄存器地址范围:40001~49999;

有了该地址模型,我们就可以从Modbus寄存器的地址判断要访问的区块的类型。比如本文开头提到到地址40001就是保持存储器的第一个值的地址,而10001就是离散量输入的第一个值的地址;要注意的是,保持寄存器和输入寄存器的每个值的大小为16bits(字),而线圈和离散量输入每个值的大小为1bit(位);

各PLC厂家根据PLC的实际情况,将Modbus的地址模型映射到实际的存储区。一般来说,线圈对应过程输出映像区(Q);离散量输入对应过程输入映像区(I);输入寄存器对应模拟量输入(AI);保持寄存器对应数据块或V存储区或M存储区。

协议数据单元(Protocol Data Unit,PDU)

协议数据单元由功能码+数据构成,如下面这张图:

img

功能码的长度为1个字节,它表示要执行的功能。比如常见的:01读取线圈;02读取离散量输入值;03读取保持寄存器值;05写单个线圈等

数据部分的长度为0~252个字节,它表示要读的地址或者要写入的值,不同的功能码对应的数据有所不同。比如01功能码,其数据为4个字节,其中前两个字节表示要读取的线圈的地址,后两个字节表示要读取线圈的数量;而对于05功能码,其数据也是4个字节,前两个字节表示要写入线圈的地址,后面两字节表示要写入的值。

协议数据单元有三种类型:请求型协议数据单元(Request PDU)应答型协议数据单元(Response PDU)、及异常应答型协议数据单元(Exception Response PDU)

协议数据单元是家族的通用数据结构,它与底层物理结构无关,三兄弟都使用相同的协议数据单元。

Modbus协议定义了一个与基础通信层无关的简单协议数据单元(PDU)。特定总线或网线路上的Modbus协议映射能够在应用数据单元(ADU)上引入一些附加域。

img

Modbus常用功能码

https://zhuanlan.zhihu.com/p/145546574

https://blog.csdn.net/sgmcumt/article/details/87435191?spm=1001.2014.3001.5502

功能码的长度为1个字节,它表示要执行的功能。比如常见的:01读取线圈;02读取离散量输入值;03读取保持寄存器值;05写单个线圈等

image-20220126163451792

01(0x01)读线圈

1)功能:读取从站(远程设备)的1~2000个连续线圈的状态数值;读取采用起始地址+线圈数量的方式; 2)操作方式:位操作; 3)说明:Modbus1号线圈的地址为0,2号线圈的地址为1,以此类推;因此,假设要读取1~10号线圈的值,其寄存器地址范围为:0~9;(相对地址??) 4)发送指令示例: 假设从站地址为0x03,要读取编号从33~42的10个连续线圈的状态值,其寄存器地址范围为:0x0020~0x0029,则发送指令下图所示:

image-20220126155544480

5)应答格式: 应答数据包括:从站地址+功能码+返回字节数+数据值+校验码 其中,线圈的状态以位的形式返回。状态为ON时,其值为1;状态为OFF时,其值为0; 数据以小端(Little Endian)的形式进行组织。即先存放LSB(最低权重位),再存放MSB。 每8个位组成一个字节,当线圈的数量不是8的倍数时,剩余的位数添0补位。 本例程读取10个线圈,10/8商1余2,因此需要2个字节存放应答数据。 字节1存放线圈编号33~40的数值(小端字节序,线圈40的值存放在bit7,线圈33的值存放在bit0); 字节2存放线圈编号41~42的数值,剩余位数添0补位; 假设线圈状态及数值如下面两张图所示:

image-20220126155705332

则,应答字节1的值为:11001011=0xCB; 应答字节2的值为:10=0x02 应答消息帧下图所示:

image-20220126155736705

02(0x02)读离散量输入

1)功能:读取从站1~2000个连续离散量输入的状态值;读取采用起始地址+通道数量的方式; 2)操作方式:位操作; 3)离散量输入通道地址编号从1开始,寄存器地址编号从0开始; 4)发送指令示例: 假设要读取从站地址为0x03的第110~119个数字量输入通道的数值,则发送指令如下图所示:

image-20220126155950411

5)应答:应答格式与功能码01H类似 应答数据包括:从站地址+功能码+返回字节数+数据值+校验码 假设应答字节1的数据如下图所示:

image-20220126160101013

应答字节2的内容如下图所示:

image-20220126160117915

应答消息帧如下图所示:

image-20220126160140799

03(0x03)读保持寄存器

1)功能:读取远程从站若干个保持寄存器(Holding Register)的数值; 2)操作方式:每个保持存储器的数值以字(2个字节)的形式进行应答; 3)发送指令: 假设要读取从机地址0x03的108~110保持存储器的数值,其寄存器地址范围为:0x006B~0x006D,指令格式如下图所示:

image-20220126160219357

4)应答: 从站应答数据包括:从站地址+功能码+应答字节数+寄存器1高字节+寄存器1低字节+...+寄存器N高字节+寄存器N低字节 假设编号108~110保持寄存器的数值如下图所示:

image-20220126160302415

04(0x04)读输入寄存器

1)功能:读1~125个连续输入寄存器(Input Register)的数值; 2)操作方式:每个输入寄存器存储器的数值以字(2个字节)的形式进行应答; 3)发送指令: 假设要读取从机地址0x03的9-10号输入存储器的数值,其寄存器地址范围为:0x0008~0x0009,指令格式如下图所示:

image-20220126160421236

4)应答: 从站应答数据包括:从站地址+功能码+应答字节数+寄存器1高字节+寄存器1低字节+...+寄存器N高字节+寄存器N低字节(与功能码03H类似) 假设寄存器的数据如下图所示:

image-20220126160435075

05(0x05)写单个线圈

1)功能:对单个线圈进行写操作。线圈编号从1开始,地址从0开始。写值0xFF00表示将线圈置为ON,写值0x0000表示将线圈置为OFF,其它值是无效的; 2)操作方式:位操作 3)发送指令: 假设要将从站地址0x03的第33个线圈(地址:0x0020)的值设置ON,指令如下图所示:

image-20220126160554223

4)应答: 从站应答数据包括:从站地址+功能码+寄存器地址+写入值 如果数据成功写入,则应答数据与请求数据一样,如下图所示:

image-20220126160827485

06(0x06)写单个寄存器

写单个寄存器

在一个远程设备中,使用该功能码写单个保持寄存器。

请求 PDU 说明了被写入寄存器的地址。从零开始寻址寄存器。因此,寻址寄存器 1 为 0。

正常响应是请求的应答,在写入寄存器内容之后返回这个正常响应。

image-20220126163833967

这是一个请求将十六进制 00 03 写入寄存器2的实例:

image-20220126163905446

img

15 (0x0F) 写多个线圈

在一个远程设备中,使用该功能码强制线圈序列中的每个线圈为 ON 或 OFF。请求 PDU 说明了强制的线圈参考。从零开始寻址线圈。因此,寻址线圈 1 为 0。

请求数据域的内容说明了被请求的 ON/OFF 状态。域比特位置中的逻辑“1”请求相应输出为ON。域比特位置中的逻辑“0”请求相应输出为 OFF。

正常响应返回功能码、起始地址和强制的线圈数量。

image-20220126163712250

这是一个请求从线圈 20 开始写入 10 个线圈的实例:

请求的数据内容为两个字节:十六进制 CD 01 (二进制 1100 1101 0000 0001)。使用下列方法,二进制比特对应输出。

image-20220126163753361

传输的第一字节(十六进制 CD)寻址为输出 27-20,在这种设置中,最低有效比特寻址为最低输出(20)。

传输的下一字节(十六进制 01)寻址为输出 29-28,在这种设置中,最低有效比特寻址为最低输出(28)。

应该用零填充最后数据字节中的未使用比特。

image-20220126163810606

16 (0x10) 写多个寄存器

在一个远程设备中,使用该功能码写连续寄存器块(1 至约 120 个寄存器)。

在请求数据域中说明了请求写入的值。每个寄存器将数据分成两字节。

正常响应返回功能码、起始地址和被写入寄存器的数量。

image-20220126163612204

image-20220126163641740

23 (0x17) 读/写多个寄存器

在一个单独 MODBUS 事务中,这个功能码实现了一个读操作和一个写操作的组合。从零开始寻址保持寄存器。因此,寻址保持寄存器 1-16为0-15。

请求说明了起始地址、被读取的保持寄存器号和起始地址、保持寄存器号以及被写入的数据。在写数据域中,字节数说明随后的字节号。

正常响应包括被读出的寄存器组的数据。在读数据域中,字节数域说明随后的字节数量。

img

这是一个请求从寄存器4开始读六个寄存器并且从寄存器15开始读三个寄存器的实例:

img

ModBusTCP

初识

概述

MODBUS/TCP 使MODBUS_RTU协议运行于以太网,MODBUS TCP使用TCP/IP和以太网在站点间传送MODBUS报文,MODBUS TCP结合了以太网物理网络和网络标准TCP/IP以及以MODBUS作为应用协议标准的数据表示方法。MODBUS TCP通信报文被封装于以太网TCP/IP数据包中。与传统的串口方式,MODBUS TCP插入一个标准的MODBUS报文到TCP报文中,不再带有数据校验和地址。

简单的理解一下Modbus TCP/IP协议的内容,就是去掉了modbus协议本身的CRC校验增加了MBAP 报文头。TCP/IP上的MODBUS的请求/响应如下图所示:

TCPIPmodbus请求和响应


网络模型

通讯使用的以太网参考模型:

Modbus TCP传输过程中使用了TCP/IP以太网参考模型的5层:

第一层:物理层,提供设备物理接口,与市售介质/网络适配器相兼容

第二层:数据链路层,格式化信号到源/目硬件址数据帧

第三层:网络层,实现带有32位IP址IP报文包

第四层:传输层,实现可靠性连接、传输、查错、重发、端口服务、传输调度

第五层:应用层,Modbus协议报文

在网络通信中,通常需要写明IP地址和端口号,为什么ADU中没有相关的内容呢?

其实这是因为是一个应用层的协议,而IP地址和端口号属于传输层/网络层的协议。看图:

image-20220126161301250

逻辑上是在TCP层上的。在发送数据的时候,应用数据单元首先向下传送给传输层,加上TCP协议的报文;再传送给网络层,加上IP协议的报文;再向下传送给数据链路层及物理层;接收的过程正好相反,从物理层一层一层的去掉相应层的报文,最终到达应用层。所以在进行数据传输的时候,是要配合TCP/IP协议来使用的。通常如果你使用电脑编程,就要用到SOCKET技术;如果是使用PLC编程,通常厂家已经把底层通信封装成库指令了,你只要直接调用就好了。比如西门子S7-200 SMART/1200/1500等PLC都有现成的Modbus-TCP指令库。


在Modbus服务器中按缺省协议使用Port 502 通信端口,在Modbus客户器程序中设置任意通信端口,为避免与其他通讯协议的冲突一般建议2000开始可以使用。

通讯过程举例

在读寄存器的过程中,以Modbus TCP请求报文为例,具体的数据传输过程如下:

\1) Modbus TCP客户端实况,用Connect()命令建立目标设备TCP 502端口连接数据通信过程;

\2) 准备Modbus报文,包括7个字节MBAP内请求;

\3) 使用send()命令发送;

\4) 同一连接等待应答;

\5) 同recv()读报文,完成一次数据交换过程;

\6) 当通信任务结束时,关闭TCP连接,使服务器可以为其他服务。

MBAP报文

MBAP是英文"ModBus APlication"的缩写,即"应用数据单元"的意思。

首先来看一下,MBAP 报文头都包括了哪些信息和内容

image-20220126152436499

  • 事务元标识符(2个字节):用于事务处理配对。在响应中,MODBUS服务器复制请求的事务处理标识符。这里在以太网传输中存在一个问题,就是先发后至,我们可以利用这个事务处理标识符做一个TCP序列号,来防止这种情况所造成的数据收发错乱(这里我们先不讨论这种情况,这个事务处理标识符我们统一使用0x00,0x01)

  • 协议标识符(2个字节):modbus协议标识符为0x00,0x00

  • 长度(2个字节):长度域是下一个域的字节数,包括单元标识符和数据域。

  • 单元标识符(1个字节):该设备的编号。(可以使用PLC的IP地址标识)。

对 TCP/IP 来说,利用IP地址寻址MODBUS服务器;因此,MODBUS单元标识符是无用的。必需使用值0xFF。

注:0也可以用作与MODBUS/TCP设备直接通信。

请求和响应

MODBUS请求的生成

在收到来自用户应用的需求后,客户端必须生成一个MODBUS请求,并发送到TCP管理。下表显示MODBUS请求ADU编码:

image-20220126163209642

MODBUS响应的生成

一旦处理请求,MODBUS 服务器必须使用适当的MODBUS服务器事务处理生成一个响应,并且必须将响应发送到TCP管理组件。

根据处理结果,可以生成两类响应:

  • 肯定的MODBUS响应:

    • 响应功能码 = 请求功能码
  • MODBUS异常响应:

    • 目的是为客户机提供与处理过程检测到的错误相关的信息

    • 响应功能码 = 请求功能码+0x80

    • 提供异常码来表明出错的原因。

image-20220126163326097

SIMATIC S7-1200/S7-1500存储区寻址

存储区 Modbus 设备中应用层的地址 传输报文中 Modbus 地址(数据链路层)
线圈(输出) 1 to 9999 0 to 9998
离散输入(输入) 10001 to 19999 0 to 9998
输入寄存器(输入字) 30001 to 39999 0 to 9998
保持寄存器(输出字) 40001 to 49999 400001 to 465536 扩展的地址空间 0 to 9998 0 to 65535

实例

image-20220214153310475

注意:MB_HOLD_REG中的数量不能大于数据块中数据的数量

保持性寄存器示例如下,有word(两个字节,16位)类型和Real(4个字节,32位)两种组成,

注意:一个保持性寄存器为16位,两个字节,因此一个real类型占用两个保持性寄存器

image-20220214151538122

发送读取请求,需要从0位(00 00)开始,读取15(00 0f)个保持性寄存器:

发送请求:

00 00 00 00 00 06 FF 03 00 00 00 0f

收到回复:

00 00 00 00 00 21 FF 03 1E 00 01 00 02 00 03 00 04 00 05 40 D3 33 33 40 F6 66 66 41 0C CC CD 41 1E 66 66 BF 80 00 00 

其中a1-a5为word类型,分别占用一个保持性寄存器,两个字节表示,如a1(1)表示为:00 01

a6-a10为real类型,占用了两个保持性寄存器,用4个字节表示,如a6(6.6)表示为:40 D3 33 33,a10(-1.0)表示为:BF 80 00 00

注意:浮点数表示遵循IEEE 754标准,在线转换:http://www.speedfly.cn/tools/hexconvert/

ModbusTCP QT编程

QModBusTcpClient类

QModBusTcpClient类>>QModbusClient>>QModbusDevice>>QObject

信号

信号
errorOccurred 有错误时发出
stateChanged 每当设备的状态发生变化时,就会发出这个信号。新状态由状态表示。
finished() 该信号在应答完成处理时发出。回复可能仍然返回了一个错误。在发出此信号后,应答的数据将不再有更新。

函数

Error error() 返回设备的错误状态
QString errorString() 返回设备错误的描述性错误文本。
void disconnectDevice() 断开连接的设备。
setConnectionParameter()
setTimeout()
setNumberOfRetries()
bool connectDevice() 用于设备接入Modbus网络。成功时返回true;否则错误。
QMODBUSREPLY sendReadRequest() 发送读请求
sendWriteRequest() 发送一个读取read所指向数据的内容的请求。如果没有错误发生,返回一个新的有效的QModbusReply对象,否则为nullptr。Modbus网络可以有多个服务器,每个服务器都有唯一的serverAddress。
bool isFinished() 当回复完成或中止时返回true。