赞
踩
提示:这里是该系列文章的所有文章的目录
第一章:Qt下使用ModbusTcp通信协议进行PLC线圈/保持寄存器的读写(32位有符号数)
第二章:Qt下使用modbus-c库实现PLC线圈/保持寄存器的读写
在上一篇文章中提到了使用Qt下的Modbus模块来进行ModbusTcp的通信,采用QModbusTcpClient类作为Modbus客户端(主站)与PLC读写,正常情况下是可以满足读写需求的,但是使用过程中发现读写频率较高时会出现写入延迟等问题,后面发现使用这个C语言写的第三方Modbus库来与PLC通信会更加稳定,性能更优越,这里将结合相应的示例进行讲解,以便大家学习,如有错误之处,欢迎大家批评指正。
项目效果
提示:以下是本篇文章正文内容,下面案例可供参考
通过参考博客中的下载链接到官网下载:https://sourceforge.net/projects/libmodbus/(似乎失效了,下载不了),或者通过下文中我的百度网盘链接下载压缩包,文件内容如下,其中包含相关源码和ws2_32.dll(在lib中,依赖该动态库)
这里我实现了自己的ModbusLib类的封装,使用了pri子模块的方式,也是方便日后进行此模块的复用
这里我使用的是tcp的方式来通信,pri内容如下:
ModbusLib.pri
HEADERS += \ $$PWD/modbus.h \ $$PWD/modbus-private.h \ #$$PWD/modbus-rtu.h \ #$$PWD/modbus-rtu-private.h \ $$PWD/modbus-tcp.h \ $$PWD/modbus-tcp-private.h \ $$PWD/mymodbuslib.h SOURCES += \ $$PWD/modbus.c \ $$PWD/modbus-data.c \ #$$PWD/modbus-rtu.c \ $$PWD/modbus-tcp.c \ $$PWD/mymodbuslib.cpp LIBS += -L$$PWD/lib/ -lws2_32
这里会看到将modbus-c库的源码加入到工程中,所以添加好相关的头文件就可以调用库函数了,这里相关函数的使用见下文代码:
1.mymodbusLib.h
#ifndef MYMODBUSLIB_H #define MYMODBUSLIB_H #include <QObject> #include <QDateTime> #include <QTimer> #include <QThread> #include <QEventLoop> #include <QCoreApplication> #include <QDebug> #include "modbus.h" #include "modbus-private.h" #include "modbus-tcp.h" #include "modbus-tcp-private.h" #define LOGDEBUG qDebug()<<QTime::currentTime().toString("[hh:mm:ss:zzz]") class MyModbusLib : public QObject { Q_OBJECT public: explicit MyModbusLib(QObject *parent = nullptr); ~MyModbusLib(); bool initConnect(QString tcpIP,int tcpPort); bool readCoil(int readAdd); void writeCoil(int writeAdd,int writeNum); int readRegister(int writeAdd); void writeRegister32(int writeAdd,int writeNum); private: modbus_t *m_modbus; }; #endif // MYMODBUSLIB_H
2.mymodbusLib.cpp
#include "mymodbuslib.h" MyModbusLib::MyModbusLib(QObject *parent) : QObject(parent) { } MyModbusLib::~MyModbusLib() { modbus_close(m_modbus); modbus_free(m_modbus); } //初始化 bool MyModbusLib::initConnect(QString tcpIP,int tcpPort) { m_modbus = modbus_new_tcp(tcpIP.toLatin1().data(),tcpPort); modbus_set_slave(m_modbus,1); if(modbus_connect(m_modbus) == -1) { return false; } //设置modbus超时时间为1000毫秒 struct timeval t; t.tv_sec = 0; t.tv_usec = 1000000; modbus_set_response_timeout(m_modbus,t.tv_sec,t.tv_usec); return true; } bool MyModbusLib::readCoil(int readAdd) { uint8_t bitsr[1]={0}; int rNum = modbus_read_bits(m_modbus,readAdd,1,bitsr); if(rNum == -1) { LOGDEBUG<<"读取线圈"<<readAdd<<"失败!"; return false; } else { //LOGDEBUG<<"读取线圈"<<readAdd<<"成功!"; if(bitsr[0] == 1) { return true; } } return false; } void MyModbusLib::writeCoil(int writeAdd,int writeNum) { uint8_t bitsw[1]={0}; bitsw[0] = writeNum; int rNum = modbus_write_bits(m_modbus,writeAdd,1,bitsw); if(rNum == -1) { LOGDEBUG<<"线圈"<<writeAdd<<"写入"<<writeNum<<"失败!"; } else { LOGDEBUG<<"线圈"<<writeAdd<<"写入"<<writeNum<<"成功!"; } } int MyModbusLib::readRegister(int readAdd) { uint16_t regsr[2]={0}; int rNum = modbus_read_registers(m_modbus,readAdd,2,regsr); if(rNum == -1) { LOGDEBUG<<"读取寄存器"<<readAdd<<"失败!"; } else { //LOGDEBUG<<"读取寄存器"<<readAdd<<"成功!"; int readNum = regsr[0] | (regsr[1] << 16); return readNum; } return 0; } void MyModbusLib::writeRegister32(int writeAdd,int writeNum) { uint16_t regsw[2]={0}; regsw[0] = writeNum & 0xffff; regsw[1] = (writeNum >> 16) & 0xffff; int rNum = modbus_write_registers(m_modbus,writeAdd,2,regsw); if(rNum == -1) { LOGDEBUG<<"寄存器"<<writeAdd<<"写入"<<writeNum<<"失败!"; } else { LOGDEBUG<<"寄存器"<<writeAdd<<"写入"<<writeNum<<"成功!"; } }
工程结构如图,下面可以直接在主界面使用封装好的ModbusLib类,详细内容见代码:
1.ModbusTest.pro
QT += core gui greaterThan(QT_MAJOR_VERSION, 4): QT += widgets CONFIG += c++11 DEFINES += QT_DEPRECATED_WARNINGS #设置字符 contains( CONFIG,"msvc" ):QMAKE_CXXFLAGS += /source-charset:utf-8 /execution-charset:utf-8 contains( CONFIG,"msvc" ):QMAKE_CFLAGS +=/source-charset:utf-8 /execution-charset:utf-8 #包含子模块 include (./ModbusLib/ModbusLib.pri) #libmodbus库 SOURCES += \ main.cpp \ mainwindow.cpp HEADERS += \ mainwindow.h FORMS += \ mainwindow.ui # Default rules for deployment. qnx: target.path = /tmp/$${TARGET}/bin else: unix:!android: target.path = /opt/$${TARGET}/bin !isEmpty(target.path): INSTALLS += target
2.mainwindow.h
#ifndef MAINWINDOW_H #define MAINWINDOW_H #include <QMainWindow> #include <QTime> #include <QDebug> #include "ModbusLib/mymodbuslib.h" QT_BEGIN_NAMESPACE namespace Ui { class MainWindow; } QT_END_NAMESPACE class MainWindow : public QMainWindow { Q_OBJECT public: MainWindow(QWidget *parent = nullptr); ~MainWindow(); void initWidget(); private slots: void on_pb_connect_clicked(); void on_pb_readC_clicked(); void on_pb_writeC_clicked(); void on_pb_readH_clicked(); void on_pb_writeH_clicked(); private: Ui::MainWindow *ui; MyModbusLib *m_myModbus; }; #endif // MAINWINDOW_H
3.mainwindow.cpp
#include "mainwindow.h" #include "ui_mainwindow.h" MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) , ui(new Ui::MainWindow) { ui->setupUi(this); this->initWidget(); } MainWindow::~MainWindow() { delete ui; } void MainWindow::initWidget() { //使能按钮 ui->pb_readC->setEnabled(false); ui->pb_writeC->setEnabled(false); ui->pb_readH->setEnabled(false); ui->pb_writeH->setEnabled(false); //创建modbus对象 m_myModbus = new MyModbusLib(); } void MainWindow::on_pb_connect_clicked() { QString tcpIP = ui->le_ip->text(); int tcpPort = ui->le_port->text().toInt(); if(m_myModbus->initConnect(tcpIP,tcpPort)) { ui->lb_state->setText("连接成功"); LOGDEBUG<<"ModbusTCP连接成功!"; //使能按钮 ui->pb_readC->setEnabled(true); ui->pb_writeC->setEnabled(true); ui->pb_readH->setEnabled(true); ui->pb_writeH->setEnabled(true); } else { ui->lb_state->setText("连接失败"); LOGDEBUG<<"ModbusTCP连接失败!"; } } void MainWindow::on_pb_readC_clicked() { int readAdd = ui->le_addC->text().toInt(); ui->lb_numC->setText(QString::number(m_myModbus->readCoil(readAdd))); } void MainWindow::on_pb_writeC_clicked() { int writeAdd = ui->le_addC->text().toInt(); int writeNum = ui->le_writeNumC->text().toInt(); m_myModbus->writeCoil(writeAdd,writeNum); } void MainWindow::on_pb_readH_clicked() { int readAdd = ui->le_addH->text().toInt(); ui->lb_numH->setText(QString::number(m_myModbus->readRegister(readAdd))); } void MainWindow::on_pb_writeH_clicked() { int writeAdd = ui->le_addH->text().toInt(); int writeNum = ui->le_writeNumH->text().toInt(); m_myModbus->writeRegister32(writeAdd,writeNum); }
4.mainwindow.ui
modbus-c库下载链接:https://pan.baidu.com/s/1rBQzOhwPIrRxL_f2CofJxQ
提取码:xxcj
后续在使用modbus-c库的测试中,发现读写的效率是比Qt自带的modbus模块要高并且稳定的,建议在需要与PLC进行tcp通信时使用这种方式。这里我的ModbusLib类封装在一个文件夹内,要调用直接在工程pro中添加pri子模块就可以使用,比较方便。
hello:
共同学习,共同进步,如果还有相关问题,可在评论区留言进行讨论。
参考博客:QT 使用第三方C库实现Modbus通讯
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。