当前位置:   article > 正文

Qt下使用modbus-c库实现PLC线圈/保持寄存器的读写_qt读写modbus 读写寄存器

qt读写modbus 读写寄存器

系列文章目录

提示:这里是该系列文章的所有文章的目录
第一章:Qt下使用ModbusTcp通信协议进行PLC线圈/保持寄存器的读写(32位有符号数)
第二章:Qt下使用modbus-c库实现PLC线圈/保持寄存器的读写



前言

在上一篇文章中提到了使用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.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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

这里会看到将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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38

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<<"成功!";
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98

三、使用ModbusLib类

工程结构如图,下面可以直接在主界面使用封装好的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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29

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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36

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);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76

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通讯

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/羊村懒王/article/detail/196814
推荐阅读
  

闽ICP备14008679号