Qt PLC Modbus通信


项目介绍
有一个项目需要PC和PLC通信,PLC通信协议是Modbus协议。前两天研究了一下,QT源码也有例程,不过源码读了有点懵,参考了别人的博客,实现了一个简单的通信Demo,测试可以对PLC内部寄存器和中间继电器读写。

软件版本和程序总览

QT:5.12.12
Kits:MSVC2017
PLC:信捷XD5

软件预览:

软件介绍

pro文件增加串口和modbus配置

QT += core gui serialbus serialport

串口配置代码

主要对串口的参数进行配置,包括串口号、波特率、数据位、停止位、校验位、超时时间和重试次数。刷新按钮实现对PC串口的检测,使用QSerialPortInfo::availablePorts();获取串口信息,函数放到初始化中。

1 QList<QSerialPortInfo> port_list = QSerialPortInfo::availablePorts(); 2 ui>cboBox_c>clear(); 3 foreach(const QSerialPortInfo & info,port_list) 4

用结构体存储串口设置参数

1 //串口参数 2 struct Settings ;

每次打开串口时重新刷新参数

1 //初始化串口参数信息 2 m_settings.serialPort = ui>cboBox_c>currentText(); 3 m_settings.parity = ui>cboBox_parity>currentIndex(); 4 if (m_settings.parity > 0) 5 m_settings.parity++; 6 m_settings.baud = ui>cboBox_baud>currentText().toInt(); 7 m_settings.dataBits = ui>cboBox_databits>currentText().toInt(); 8 m_settings.stopBits = ui>cboBox_stopbits>currentText().toInt(); 9 m_settings.responseTime = ui>spinBox_timeout>value(); 10 m_settings.numberOfRetries = ui>spinBox_retries>value();

打开串口

先获取设置的串口参数,然后实例化串口设备对象,这里使用串口线连接,使用QModbusRtuSerialMaster,最后设置参数连接串口,连接成功设置按钮状态。

1 //获取串口数据 2 getCParameter(); 3 if (modbusDevice) 4 9 10 modbusDevice = new QModbusRtuSerialMaster(this); 11 connect(modbusDevice, &QModbusClient::errorOccurred, [this](QModbusDevice::Error) ); 14 15 //配置串口参数 16 modbusDevice>setConnectionParameter(QModbusDevice::SerialPortNameParameter,m_settings.serialPort); 17 modbusDevice>setConnectionParameter(QModbusDevice::SerialParityParameter,m_settings.parity); 18 modbusDevice>setConnectionParameter(QModbusDevice::SerialBaudRateParameter,m_settings.baud); 19 modbusDevice>setConnectionParameter(QModbusDevice::SerialDataBitsParameter,m_settings.dataBits); 20 modbusDevice>setConnectionParameter(QModbusDevice::SerialStopBitsParameter,m_settings.stopBits); 21 modbusDevice>setTimeout(m_settings.responseTime); // 配置请求超时时间 22 modbusDevice>setNumberOfRetries(m_settings.numberOfRetries); // 配置失败重试次数 23 24 if(!modbusDevice>connectDevice()) 25 28 else 29

关闭串口

断开串口连接,析构串口设备对象,设置按钮状态。

1 if (!modbusDevice) 2 return; 3 modbusDevice>disconnectDevice(); 4 delete modbusDevice; 5 modbusDevice = nullptr; 6 7 qDebug() << "Modbus close Success!"; 8 ui>pushButton_openC>setEnabled(true); 9 ui>pushButton_closeC>setEnabled(false); 10 ui>pushButton_refreshC>setEnabled(true); 11 12 ui>cboBox_c>setEnabled(true); 13 ui>cboBox_baud>setEnabled(true); 14 ui>cboBox_databits>setEnabled(true); 15 ui>cboBox_stopbits>setEnabled(true); 16 ui>cboBox_parity>setEnabled(true); 17 ui>spinBox_timeout>setEnabled(true); 18 ui>spinBox_retries>setEnabled(true);

写串口数据

设置一个下拉框来选择读写的数据存储类型,例如线圈或者寄存器。读写数据的时候按照需要选择
这里只用测试了Coils和Holding Registers,其他两个没有测试。

1 //值类型 2 ui>cboBox_valueType>addItem(tr("Coils"), QModbusDataUnit::Coils); 3 ui>cboBox_valueType>addItem(tr("Discrete Inputs"), QModbusDataUnit::DiscreteInputs); 4 ui>cboBox_valueType>addItem(tr("Input Registers"), QModbusDataUnit::InputRegisters); 5 ui>cboBox_valueType>addItem(tr("Holding Registers"), QModbusDataUnit::HoldingRegisters);

写串口数据需要对方的设备id,和要写入的寄存器或线圈地址,和写入的数据,
代码支持同时写入多个连续位置,数据按照空格区分,写入的数据是十进制的。对于进制我没有过于追究。

1 if (!modbusDevice) 2 6 7 //获取要写入的寄存器数据 8 QList<quint16> values; 9 QStringList values_list = ui>lineEdit_writeValue>text().split(" "); 10 for(int i = 0 ; i < values_list.size(); i++) 11 14 int id = ui>lineEdit_id>text().toInt(); //设备地址 15 int addr = ui>lineEdit_addr>text().toInt(); //寄存器地址 16 17 //组合写数据帧 table写入的数据类型 寄存器或线圈 18 const auto table = 19 static_cast<QModbusDataUnit::RegisterType> (ui>cboBox_valueType>currentData().toInt()); 20 QModbusDataUnit writeUnit = QModbusDataUnit(table, 21 addr, values.size()); 22 for(int i=0; i<values.size(); i++) 23 26 27 //id 发生给slave的ID 28 if (auto *reply = modbusDevice>sendWriteRequest(writeUnit,id)) 29 39 else if (reply>error() != QModbusDevice::NoError) 40 44 reply>deleteLater(); 45 }); 46 } 47 else 48 51 } 52 else 53

这里是对D0寄存器写入100。

读取串口

读串口数据需要对方的设备id,和要读取的寄存器或线圈地址,读取的个数,支持读取连续的多个寄存器或线圈,读取数值按照空格区分。
读数据要先发送数据帧给从机,然后等待从机返回数据。

1 if (!modbusDevice) 2 6 //清除读窗口信息 7 ui>lineEdit_readValue>clear(); 8 9 //获取设备信息 10 int id = ui>lineEdit_id>text().toInt(); //设备地址 11 int addr = ui>lineEdit_addr>text().toInt(); //寄存器地址 12 int readNum = ui>lineEdit_readNum>text().toInt(); //读取寄存器个数 13 14 //组合写数据帧 table写入的数据类型 寄存器或线圈 15 const auto table = 16 static_cast<QModbusDataUnit::RegisterType> (ui>cboBox_valueType>currentData().toInt()); 17 18 QModbusDataUnit readUint = QModbusDataUnit(table, 19 addr, readNum); 20 //读取数据 21 if (auto *reply = modbusDevice>sendReadRequest(readUint, id)) 22 28 else 29

在槽函数readReady();获取读到的数据。

1 auto reply = qobject_cast<QModbusReply *>(sender()); 2 if (!reply) 3 return; 4 5 if (reply>error() == QModbusDevice::NoError) 6 19 //读取的数据 20 ui>lineEdit_readValue>insert(send_buff); 21 } 22 } 23 else if (reply>error() == QModbusDevice::ProtocolError) 24 29 else 30 35 reply>deleteLater();

这里读取D0 D1寄存器的值,读取值显示为16进制。

数据帧显示

代码发送数据帧和接收数据帧是没有做显示,这里参考通过重定向打印功能将将我们需要的数据显示到缓冲区显示。使用QLoggingCategory
这部分没什么用,我也没有研究过,就不做说明。



上一篇:Mitsubishi 三菱定位模块FX3U-1PG的应用

下一篇:ABB机器人示教器中设置可编程按钮


Qt Modbus
Copyright © 2002-2019 k262电脑网 www.k262.cn 皖ICP备2020016292号
温馨提示:部分文章图片数据来源与网络,仅供参考!版权归原作者所有,如有侵权请联系删除!QQ:251442993 热门搜索 网站地图