系列文章目錄
提示:這里是該系列文章的所有文章的目錄
第一章:Qt下使用ModbusTcp通信協議進行PLC線圈/保持寄存器的讀寫(32位有符號數)
第二章:Qt下使用modbus-c庫實現PLC線圈/保持寄存器的讀寫
文章目錄
- 系列文章目錄
- 前言
- 一、下載modbus-c庫
- 二、實現ModbusLib類
- 三、使用ModbusLib類
- 四、下載鏈接
- 總結
前言
在上一篇文章中提到了使用Qt下的Modbus模塊來進行ModbusTcp的通信,采用QModbusTcpClient類作為Modbus客戶端(主站)與PLC讀寫,正常情況下是可以滿足讀寫需求的,但是使用過程中發現讀寫頻率較高時會出現寫入延遲等問題,后面發現使用這個C語言寫的第三方Modbus庫來與PLC通信會更加穩定,性能更優越,這里將結合相應的示例進行講解,以便大家學習,如有錯誤之處,歡迎大家批評指正。
項目效果
提示:以下是本篇文章正文內容,下面案例可供參考
一、下載modbus-c庫
通過參考博客中的下載鏈接到官網下載:https://sourceforge.net/projects/libmodbus/(似乎失效了,下載不了),或者通過下文中我的百度網盤鏈接下載壓縮包,文件內容如下,其中包含相關源碼和ws2_32.dll(在lib中,依賴該動態庫)
二、實現ModbusLib類
這里我實現了自己的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.hSOURCES += \$$PWD/modbus.c \$$PWD/modbus-data.c \#$$PWD/modbus-rtu.c \$$PWD/modbus-tcp.c \$$PWD/mymodbuslib.cppLIBS += -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_OBJECTpublic: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類
工程結構如圖,下面可以直接在主界面使用封裝好的ModbusLib類,詳細內容見代碼:
1.ModbusTest.pro
QT += core guigreaterThan(QT_MAJOR_VERSION, 4): QT += widgetsCONFIG += c++11DEFINES += 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.cppHEADERS += \mainwindow.hFORMS += \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_NAMESPACEclass MainWindow : public QMainWindow
{Q_OBJECTpublic: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通訊