Modbus以及上位机软件实际运用

发布时间 2024-01-03 19:31:06作者: IGary

Modbus以及上位机软件实际运用

转自:https://blog.csdn.net/magicchz/article/details/128900304

 

Demo代码
Git代码示例:https://github.com/chenheze90/Learning01_Modbus/tree/master

Modbus简介
Modbus是一种串行通讯协议,通常运用在电子设备之间的通讯上。在许多制造行业中应用极其广泛;进过多年的发展Modbus已经成为工业领域通信协议的业界标准(De facto),并且现在是工业电子设备之间常用的连接方式。
Modbus原本设计是针对PLC通讯问题而设计,目前Modbus协议主要用在串口、以太网和其他互联网协议的网络,基本上占大部分的Modbus是通过TCP或者485串口进行信息交互。
如下所示,Modbus是在应用层封装、解析、传递消息的。

协议版本
ModbusRTU
Modbus RTU是一种紧凑的,采用二进制表示数据的方式,使用串行通信(serial communication)方式。RTU格式后续的命令/数据带有循环冗余校验的校验和(CRC)。被配置为RTU变种的节点不会和设置为ASCII变种的节点通信,反之亦然。

ModbusASCII
Modbus ASCII是一种人类可读的,冗长的表示方式。使用串行通信(serial communication)方式。ASCII格式采用纵向冗余校验的校验和(LRC)。相比之下,RTU格式的协议较为常用。

Modbus/TCP
对于通过TCP/IP(例如以太网)的连接,存在多个Modbus/TCP变种,这种方式不需要校验和计算,目前较为广泛运用的是Modbus/TCP。

通讯简析
Modbus协议是一个master/slave(或者server/client)架构的协议,简单点理解就是主从结构。有且仅有一个节点是主节点,其他使用Modbus协议参与通信的节点是子节点。每一个子设备都有一个唯一的地址。在以太网上,任何一个设备都能发送一个Modbus命令,但是通常也只有一个主节点设备启动指令。
一个ModBus命令包含了打算执行的设备的Modbus地址。所有设备都会收到命令,但只有指定位置的设备会执行及回应指令(地址0例外,指定地址0的指令是广播指令,所有收到指令的设备都会运行,不过不回应指令)。所有的Modbus命令包含了检查码,以确定到达的命令没有被破坏。基本的ModBus命令能指令一个RTU改变它的寄存器的某个值,控制或者读取一个I/O端口,以及指挥设备回送一个或者多个其寄存器中的数据。
有许多modems和网关支持Modbus协议,因为Modbus协议很简单而且容易复制。它们当中一些为这个协议特别设计的。有使用有线、无线通信甚至短消息和GPRS的不同实现。不过设计者需要克服一些包括高延迟和时序的问题。

错误通讯
当主机传送的报文不符合格式、从机不支援此功能等问题时,这时从机就会回复一个错误信息,例子如下
请求数据:
从机地址 功能码 数据1 数据2 … 数据n CRCL CRCH
0x01 0x03 0x01 0x01 0x02 0xd5 0xdd
返回数据:
从机地址 功能码 异常码 CRCL CRCH
0x01 0x03 0x02 0xd5 0xdd

其中的异常码如下表所示:

功能码 名称 说明
01 ILLEGAL FUNCTION 不支援的功能
02 LLEGAL DATA ADDRESS 不合法的地址
03 ILLEGAL DATA VALUE 不合法的数值
04 SLAVE DEVICE FAILURE Slave 失效
05 ACKNOWLEDGE 命令执行中
06 SLAVE DEVICE BUSY Slave 忙碌

寄存器/暂存器
寄存器分四类

类别 区号 读写 值范围
线圈状态 0区 可读可写布尔量 00001-09999
离散输入状态 1区 只读布尔量 10001-19999
输入寄存器 3区 只读寄存器 30001-39999
保持寄存器 4区 可读可写寄存器 40001-49999
并且Modbus还给每个区都划分了地址范围 主机向从机获取数据时,只需要告诉从机数据的起始地址,还有获取多少字节的数据,从机就可以发送数据给主机。
Modbus数据模型规定了具体的地址范围,每一个从机,都有实际的物理存储,跟modbus的存储区相对应,主机读写从机的存储区,实际上就是对从机设备对应的实际存储空间进行读写。

RTU协议帧结构
RTU是常用的一种协议,一个报文就是一帧数据,实际上是一串有组织的数据串。

帧结构:主要由从机地址/功能吗/数据/校验组成。
从机地址: 每个从机都有一个地址,占用一个字节,范围0-255,其中有效范围是1-247,其中255是广播地址,剩余的地址用于高级开发。
功能码: 占用一个字节,下文会介绍。
数据: 各种参数,配合功能码实现功能,功能码比作方法,数据就是方法的参数。
校验: 对上面的数据进行一个有规律的计算得出的值就是CRC校验码,CRC检验数据是否完整。

CRC校验
CRC占用两个字节包含了一个16位的二进制值,如上表所示的CRCL和CRCH。这两个值在发出的时候已经由发出的设备计算得出,然后附加到数据帧尾部,接收设备在接收数据时重新计算CRC值,然后与接收到的CRC域中的值进行比较,如果这两个值不相等就判断数据发生了错误。

功能码
Modbus规定了多个功能,那么为了方便的使用这些功能,我们给每个功能都设定一个功能码,也就是指代码。
功能码 功能说明
01H 读取输出线圈
02H 读取输入线圈
03H 读取保持寄存器
04H 读取输入寄存器
05H 写入单线圈
06H 写入单寄存器
0FH 写入多线圈
10H 写入多寄存器
Modbus协议同时规定了二十几种功能码,但是常用的只有8种,用于对存储区的读写。

上位机代码实例
ModbusSlave
ModbusSlave是modbus常见的调试工具,可以模拟32个子设备。首先下载ModbusSlave。地址: https://www.pcsoft.com.cn/soft/197832.html
安装过程此处省略,直接点下一步即可。

TCP协议
Modbus-TCP协议与Modbus-RTU协议最大的不同就是多了一个MBAP报文头,这个是TCP独有的特征。
MBAP的长度是7个字节,如下图所示。

事务处理标识符:类似于信息序列号,确认发出和受到的信息属于同一个序列,每次通信后就要加1;
协议标识符:用于系统内的多路复用,当它的值是0 (00 00) ,表示使用Modbus协议;
长度:用于说明此字节之后还有多少个字节的数量;
单元标识符:即设备地址,在slave中对应其ID;
功能码:与Modbus-RTU协议相似
数据:与Modbus-RTU协议相似
地址类别和RTU类似

 

功能码如下:

功能码 功能说明
01H 读取输出线圈
02H 读取输入线圈
03H 读取保持寄存器
04H 读取输入寄存器
05H 写入单线圈
06H 写入单寄存器
0FH 写入多线圈
10H 写入多寄存器

线圈寄存器:实际上就可以类比为开关量(继电器状态),每一个bit对应一个信号的开关状态。所以一个byte就可以同时控制8路的信号。比如控制外部8路io的高低。 线圈寄存器支持读也支持写,写在功能码里面又分为写单个线圈寄存器和写多个线圈寄存器。对应上面的功能码也就是:0x01 0x05 0x0f
离散输入寄存器:如果线圈寄存器理解了这个自然也明白了。离散输入寄存器就相当于线圈寄存器的只读模式,他也是每个bit表示一个开关量,而他的开关量只能读取输入的开关信号,是不能够写的。比如我读取外部按键的按下还是松开。所以功能码也简单就一个读的 0x02
保持寄存器:这个寄存器的单位不再是bit而是两个byte,也就是可以存放具体的数据量的,并且是可读写的。一般对应参数设置,比如我我设置时间年月日,不但可以写也可以读出来现在的时间。写也分为单个写和多个写,所以功能码有对应的三个:0x03 0x06 0x10
输入寄存器:这个和保持寄存器类似,但是也是只支持读而不能写,一般是读取各种实时数据。一个寄存器也是占据两个byte的空间。类比我我通过读取输入寄存器获取现在的AD采集值。对应的功能码也就一个 0x04
Modebus封装——NModbus
在vs中打开nuget,下载NModbus插件

下载完成后之后即可进行操作数据。

读取线圈数据
所有步骤之前,要先联接slave,使模拟器上线。

 

读取线圈数据首先要将模拟器设置成线圈

 


然后设置线圈的值


在代码中读取数据,本次读取的数据是从第1位开始()模拟器有0的位置)开始取5个值


代码执行结果如下图所示


读取离散线圈数据
同样的,读取离散线圈的数据,需要将模拟器的功能转成离散模式


此次模拟 读取0~6的数据


结果如下


读取保存寄存器
读取保存寄存器的值:


我们只要读取从0之后的4个数据,代码如下


结果如下图所示


读取输入寄存器
输入寄存器和离散线圈一样,都是只读。
先设置模拟器数据类型


本次读取从0开始的4个字符


结构如下所示


写入单线圈
首先,将模拟器slave调整成线圈模式


模拟在1这个位置写入1,在方法中1即true,0即false


写入单寄存器
单寄存器是两个byte,主要存储复杂的数据,如时间,文本等。同样要先设置模拟器数据类型。

 

然后模拟在第四位写入一个56,结果如下图所示


写入多线圈
代码示例


写入多寄存器
代码示例

————————————————
版权声明:本文为CSDN博主「无形道长」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/magicchz/article/details/128900304