首页 > 编程语言 >ModBus协议原理、Modbus Slave以及基于C++和Qt的代码实现

ModBus协议原理、Modbus Slave以及基于C++和Qt的代码实现

时间:2023-09-26 11:13:37浏览次数:47  
标签:0000 Qt Modbus 地址 reply Slave MainWindow 字节

ModBus 协议目的:

  规定与PLC交互的指令,其数据帧包括两部分:报文头(MBAP)和帧结构(PDU)。

  报文头(MBAP)(分为6个部分):

  • 1. 事务处理标识:即报文序列号,一般每次通信之后就要加1以区别不同的通信数据报文,长度2字节。
  • 2. 协议标识符:有串口的RTU协议和TCP协议,如0000表示ModbusTCP协议,长度2字节。
  • 3. 长度:单元标识符字节长度、功能码字节长度、数据域字节长度三者的总和,长度2字节。
  • 4. 设备地址:长度1字节。
  • 5. 功能码:包含有:0x01 读继电器/线圈、0x05 写继电器/线圈、0x0F 写多个继电器/线圈、0x02 读离散量输入、0x04 读输入寄存器、0x03 读保持寄存器数据、0x06 写单个保持寄存器、0x10 写多个保持寄存器,长度1字节。
  • 6. 具体参数:长度不定。

帧结构(PDU)(主要是将上面的报文头进行举例)

  • 示例1:读取继电器/线圈的帧结构,由事务处理标识+协议标识符+长度+设备地址+功能码+两个字节的起始地址+两个字节的读取个数组成,假设现在要读取从0开始的10个继电器/线圈的状态,则指令为:0000 0000 0006 01 01 0000 000A
  • 示例2:写单个继电器/线圈的帧结构,由事务处理标识+协议标识符+长度+设备地址+功能码+两个字节的地址+两个字节的目标状态组成,假设现在要打开地址0线圈,则指令为:0000 0000 0006 01 05 0000 FF00
  • 示例3:读取离散量输入的帧结构,由事务处理标识+协议标识符+长度+设备地址+功能码+两个字节的地址+两个字节的读取个数组成,假设现在要读取从0开始的10个离散输入的状态,则指令为:0000 0000 0006 01 02 0000 000A
  • 示例4:读取保持寄存器的帧结构,由事务处理标识+协议标识符+长度+设备地址+功能码+两个字节的地址+两个字节的读取个数组成,假设现在要读取从0开始的10个保持寄存器的状态,则指令为:0000 0000 0006 01 03 0000 000A

需要注意的是:十六进制的两位比如FF(255)就是一个字节,而四位比如FFFF(65535)则占两个字节。

软件仿真

  软件介绍:Modbus Slave是模拟Modbus协议从机的上位机软件。该软件内部封装标准Modbus协议栈,通过图形化界面使得操作更为简便。与之成套存在的另一个软件--Modbus Poll则是一个模拟Modbus协议主机的上位机软件,主要用于模拟测试跟其他从机设备通信的过程。目前软件支持01、02、03、04、05、06、15、16功能码,异常报文检测,原始报文查看,数据记录等功能,是调试Modbus协议栈的好帮手。

使用:

1. 首先点击Connection连接PLC,这里我们选择TCP/IP协议(注:选择"Serial Port",表示当前是用串口通信,如果使用的是Modbus/TCP,则选择“TCP/IP”)。

2. 点击Setup的Slave Definition,可以设置如下参数:Slave ID:可以配置从机地址。Function:可以配置功能码。Address:可以配置读/写的寄存器/线圈起始地址。Quantity:可以配置读/写的寄存器/线圈个数。Scan Rate:可以配置帧的扫描周期。Rows:可以选择该窗口一列可以显示多少行,数字是对应的行数,最后一个选项"Fit to Quantity"是可以根据前面设置的"Quantity"数量自动匹配行数。Hide Alias Columns:可以选择是否隐藏"Alias"列。PLC Addresses(Base 1):可以选择通信的首地址是从0开始还是从nX开始。

3. 双击数据的位置,可快速调出编辑写指令的窗口,输入需要修改的数值,点击OK,即可完成一次写入。此外,右键可以修改显示的数据类型。

4. 最后,关于界面的状态显示:ID:表示当前窗口通信的从机地址(Slave ID);F:表示当前窗口的功能码(Function);No Connection等红字:表示当前窗口的异常通信状态。
Qt 5.13.2的实现,以TCP为例:

1. 在pro文件中添加:QT += serialbus
2. 引入头文件:#include <QModbusTcpClient> 注意:软件是客户端,PLC是服务端,这个得搞清楚
3. .h文件代码:

 1 #ifndef MAINWINDOW_H
 2 #define MAINWINDOW_H
 3  
 4 #include <QMainWindow>
 5 #include <QModbusTcpClient> // 软件是客户端,PLC是服务端,这个得搞清楚
 6 namespace Ui {
 7 class MainWindow;
 8 }
 9  
10 class MainWindow : public QMainWindow
11 {
12     Q_OBJECT
13  
14 public:
15     explicit MainWindow(QWidget *parent = nullptr);
16     ~MainWindow();
17  
18 private slots:
19     void on_pushButton_clicked();
20  
21     void on_pushButton_2_clicked();
22     void replyData(); //读取异步槽函数
23 private:
24     Ui::MainWindow *ui;
25     QModbusTcpClient *client; // 声明一个客户端实例作为子类成员
26 };
27  
28 #endif // MAINWINDOW_H

4. .cpp文件代码(有注释):

 1 #include "mainwindow.h"
 2 #include "ui_mainwindow.h"
 3 #include <QDebug>
 4 MainWindow::MainWindow(QWidget *parent) :
 5     QMainWindow(parent),
 6     ui(new Ui::MainWindow)
 7 {
 8     ui->setupUi(this);
 9     client = new QModbusTcpClient(this);//实例化对象
10     client->setConnectionParameter(QModbusDevice::NetworkAddressParameter,"127.0.0.1");// 设置连接信息:服务端IP
11     client->setConnectionParameter(QModbusDevice::NetworkPortParameter,502);// 设置连接信息:服务端端口
12     client->connectDevice();
13 }
14  
15 MainWindow::~MainWindow()
16 {
17     if(client->state()==QModbusTcpClient::ConnectedState) //如果设备连接,则要断开
18     {
19         client->disconnectDevice();
20     }
21     delete ui;
22 }
23  
24 void MainWindow::on_pushButton_clicked() // 读
25 {
26     QModbusDataUnit unit(QModbusDataUnit::HoldingRegisters,500,2);// 定义数据单元:保持寄存器类型、PLC的开始地址和地址读取数
27     QModbusReply* reply = client->sendReadRequest(unit,1); // 参数二为设备号
28     if(reply)
29     {
30         if(!reply->isFinished())
31         {
32             connect(reply,&QModbusReply::finished,this,&MainWindow::replyData); //异步处理槽函数
33             return;
34         }
35         reply->deleteLater(); // 针对广播消息
36     }
37 }
38  
39 void MainWindow::on_pushButton_2_clicked() // 写
40 {
41     QModbusDataUnit unit(QModbusDataUnit::HoldingRegisters,500,1);// 定义数据单元:保持寄存器类型、PLC的开始地址和地址读取数
42     unit.setValue(0,ui->lineEdit->text().toUInt()); // 向一个地址写一个值
43 //     QVector<quint16> data;
44 //     data<<100<<124;
45 //     QModbusDataUnit unit(QModbusDataUnit::HoldingRegisters,500,data);
46 //    unit.setValues(data);//向连续地址写多个值 集合的元素个数需要和unit中的地址读取数保持一致
47     QModbusReply* reply = client->sendWriteRequest(unit,1); // 参数二为设备号
48     if(reply)
49     {
50         reply->deleteLater(); // 针对广播消息
51     }
52 }
53  
54 void MainWindow::replyData() // 处理读的数据
55 {
56     QModbusReply* reply = (QModbusReply*)(sender());
57     if(reply)
58     {
59         QModbusDataUnit unit = reply->result(); // 提取reply数据
60         reply->deleteLater(); // 释放内存
61         if(unit.valueCount()>0)
62         {
63             QVector<quint16> data = unit.values();
64             QString s;
65             Q_FOREACH(quint16 i,data)
66             {
67                 s.append(QString::number(i)).append(" "); // 把所有值放到一个字符串中
68             }
69             ui->lineEdit->setText(s);
70             //ui->lineEdit->setText(QString::number(unit.value(0)));
71         }
72     }
73 }

5.  运行效果,可以看到在Modbus Slave的帮助下,能够正确读写寄存器。

标签:0000,Qt,Modbus,地址,reply,Slave,MainWindow,字节
From: https://www.cnblogs.com/ybqjymy/p/17729657.html

相关文章

  • Qt PLC Modbus通信
    项目介绍有一个项目需要PC和PLC通信,PLC通信协议是Modbus协议。前两天研究了一下,QT源码也有例程,不过源码读了有点懵,参考了别人的博客,实现了一个简单的通信Demo,测试可以对PLC内部寄存器和中间继电器读写。软件版本和程序总览QT:5.12.12Kits:MSVC2017PLC:信捷XD5软件预览:软件介绍......
  • Qt 5——对象树及对象树容易出现的内存问题
    对象模型(对象树) 在Qt中创建对象的时候会提供一个Parent对象指针,下面来解释这个parent到底是干什么的。QObject是以对象树的形式组织起来的。当你创建一个QObject对象时,会看到QObject的构造函数接收一个QObject指针作为参数,这个参数就是parent,也就是父对象指针。这相当于,在......
  • PyQt5
    PyQt5是一个用于创建图形用户界面(GUI)应用程序的Python库。它是Python编程语言与Qt应用程序框架的绑定,允许开发人员使用Python语言来创建跨平台的桌面应用程序。Qt是一个功能强大且广泛使用的C++库,用于开发图形界面和应用程序功能。以下是关于PyQt5的一些重要信息和功能:1.跨平台性......
  • Qt 5.12.9 + VS 2019配置并实现与三菱Q系列PLC通讯(1)软件的安装
    本人最近配置了QT5.12.9+VS2019,并实现了与三菱Q系列PLC通讯并实现数据交互的基本功能,在这个对中间遇到的一些问题和过程进行文字说明,以后大家有用到相关功能的话可以避免一些不必要的问题~需要安装的软件有三个:QT5.12.9、VS2019、MXComponetS4.19QT安装首先是对......
  • Qt窗口和视口解析(转)
    目录坐标变换流程世界坐标、窗口坐标和设备坐标窗口和视口世界变换和窗口视口变换QWidget、QGraphicsItem、QGraphicsView绘图窗口与视口绘图测试 坐标变换流程  QPainter.drawRect(QRectF)绘制图形传入的是世界坐标,而后经过变换矩形变为窗口坐标,最后经过窗口-视......
  • Qt/C++音视频开发56-udp推流和拉流/组播和单播推流
    一、前言之前已经实现了rtsp/rtmp推流,rtsp/rtmp/hls/flv/ws-flv/webrtc等拉流,这种一般都需要依赖一个独立的流媒体服务程序,有没有一种更便捷的方式不需要这种依赖,然后又能实现推拉流呢,当然有的那就是udpp推流,其中udp推流还可以是组播或者单播推流,组播一般会选择224.0.0.1这个地址......
  • pyqt5-QTreeWidgetItem
    QTreeWidgetItem树节点项。QTreeWidgetItem(strings:Iterable[str],type:int=QTreeWidgetItem.Type)创建节点时,必须是Iterable[str],表示一行中各列的文本 1、子节点child(self,index:int)->QTreeWidgetItem获取某节点的某子节点childCount(self)->int获......
  • pyqt5-QTreeWidget
    QTreeWidget树组件。1、顶级项addTopLevelItem(self,item:QTreeWidgetItem)末尾添加单个顶级项addTopLevelItems(self,items:Iterable[QTreeWidgetItem])末尾批量添加顶级项insertTopLevelItem(self,index:int,item:QTreeWidgetItem)指定索引插入单个顶级项......
  • QT
    QT讲得好几乎没有,都是过时的那一套,没有讲qml,现在在跟的这门课程不知如何,黑马QT太简单,适合快速过一遍有个印象大丙太杂,而且讲的太细,不如看书,有黑马基础看其实也行c++QTc++中高级,设计c++技术栈的都有讲,就是太细太高端,讲的很好但听起来很吃力,有讲QT以及最后的QT项目,作为简历项目......
  • Qt之QMessageBox的用法
    一、QMessageBox::informationQMessageBox::information 用于创建一个信息对话框,通常用于向用户显示一些重要的信息或通知。这个函数的用法很简单,它接受几个参数来配置对话框的内容和行为,并且通常以模态方式显示对话框,阻塞程序的执行,直到用户关闭对话框。QMessageBox::informa......