QT使用libmodbus库与施耐德PLC通信

本文详细介绍了如何利用libmodbus库在QT环境下与施耐德PLC进行Modbus通信,包括环境配置、Modbus协议简介、libmodbus库的使用、PLC地址映射以及程序源码展示,涉及读写QX输出线圈、IX离散输入、MW寄存器等功能码的实现。作者提供了调试过程及注意事项,帮助读者理解和应用Modbus通信。
摘要由CSDN通过智能技术生成

**

QT使用libmodbus库与施耐德PLC通信

**
1.环境配置
2.modbus简介
3.libmodbus简介
4.施耐德PLC地址映射
5.程序源码

1.环境配置
我当前使用的环境为WIN10 + MSVC2013 X64 + QT 5.9.9 +QT Creator 4.11.0
2.modbus简介
modbus作为一种通信协议,支持RS232,RS485以及以太网等设备。请求建立连接的称为主站master,
作出响应的称为从站slave,主站可以单独与一个从站进行通讯,也可以以广播的方式进行通信。
modbus的数据传输支持两种方式:一种是ASCLL方式,一种是RTU方式。
两种方式的主要区别是;前者以ascll码的方式进行传输,调试起来更加直观,但是传输效率低,因为一个ascll码的二进制是8位,相反,后者以16位数据传输,效率要高得多。以传输数据0xA8为例,ascll传输需要将‘A’和’8’分开传输,也即0x41和0x38,而rtu直接传输0xA8即可。
从站地址从0-247,0是广播地址所有从机都可以识别,所以实际上从机的寻址范围是1-247,主从通信时从站地址设为1即可
modbus的功能码
在这里插入图片描述
modbus通信数据格式,只介绍用到的几个:
在这里插入图片描述
在这里插入图片描述
关于CRC(Modbus)校验,这里贴一个链接,做调试用
CRC在线校验
3.libmodbus简介

关于libmodbus的使用网上很多,我也看了许多,有两个写的还是很全的
libmodbus的编译
libmodbus的使用

4.施耐德PLC地址映射
在这里插入图片描述
其他PLC的地址映射见这里
5.程序源码
在.pro文件中链接WS2_32.LIB库,以及头文件路径

LIBS  += -L$$PWD/lib/ -lWS2_32
INCLUDEPATH += $$PWD/libmodbus

这是整个工程文件
在这里插入图片描述
libmodbus文件夹中有
在这里插入图片描述
在.h文件中声明modbus对象

modbus_t *mb;

.c文件构造函数中添加

    mb = modbus_new_rtu("COM5", 38400, 'N', 8, 1);
    modbus_set_slave(mb, 1); //设置modbus从机地址
    modbus_set_response_timeout(mb, 0, 1000000);/* 超时时间1s */

析构函数中添加

MainWindow::~MainWindow()
{
    modbus_close(mb);
    modbus_free(mb);
    delete ui;
}

设备连接

    if(modbus_connect(mb) < 0)
    {
        ui->textEdit->append(QString::fromLocal8Bit("连接失败"));
    }
    else {
        ui->textEdit->append(QString::fromLocal8Bit("连接成功"));
    }

0x01码 (读QX输出线圈)

void MainWindow::on_pushButton_01_clicked()
{
    qDebug() << "0x01";
    uint8_t dest[8] = {0};
    if(modbus_read_bits(mb, 0, 8, dest) != 8 )/* 读输出端 */
    {
        qDebug() << "Read Coils Error";
    }else{
        qDebug() << "Read Coils Success";
    }
}

0x02码 (读取IX离散输入)

void MainWindow::on_pushButton_02_clicked()   
{
    qDebug() << "0x02";
    uint8_t dest_2[8] = {0};
    modbus_read_input_bits(mb, 0, 8, dest_2); /* 读输入端 */

    qDebug() << dest_2[0] << ":" << dest_2[1] << ":" << dest_2[2] << ":" << dest_2[3];
    qDebug() << dest_2[4] << ":" << dest_2[5] << ":" << dest_2[6] << ":" << dest_2[7];
}

0x03码(读取MW寄存器)

void MainWindow::on_pushButton_03_clicked()
{
    qDebug() << "0x03";
    uint16_t reg[5];
    int rc = modbus_read_registers(mb, 0, 5, reg);

    for(int i = 0; i < rc; i++) {
        QString str = QString::fromLocal8Bit("寄存器reg[%1]的值为:%2").arg(i+300).arg(decTobin(QString::number(reg[i])));  /* 读多个保持寄存器 */
        ui->textEdit->append(str);
    }

}

0x10码 (写MW寄存器)

void MainWindow::on_pushButton_10_clicked()
{
    /* MW0 40001 */
    /* MW359  40360*/
    qDebug() << "0x10";
    uint16_t tab_regs[5] = {206,0,1,0,0};
    if(modbus_write_registers(mb, 360, 5, tab_regs) == (-1)) 
    {
        qDebug() << "write error";
    }
}

以下是我修改后的使用0X01,0X02,0X03,0X10的例子供大家参考

void MainWindow::on_btnConfirm_clicked()
{
    byte keyValue = 1 << 5;
    qDebug() << keyValue;
    byte mode = ui->comboBoxMode->currentIndex(); /* 0 1 2 3 */
    qDebug() << mode;
    int plateNum = ui->lineEditNum->text().toUInt();
    qDebug() << plateNum;
    byte cmode = 0;
    switch (mode) {
    case 0: cmode = 1 << 0; break;
    case 1: cmode = 1 << 1; break;
    case 2: cmode = 1 << 2; break;
    case 3: cmode = 1 << 3; break;
    default:cmode = 0;      break;
    }
    qDebug() << cmode;
    uint16_t tab_reg[5] = {plateNum, 0, cmode, keyValue, 0};/* 数组实际为如 101,0,1,32,0 */
    byte rc = modbus_write_registers(mb, 360, 5, tab_reg); /* 判断写入的数据长度是不是5个,点对点通信时从站是有返回值的 */
    if(rc == sizeof (tab_reg)/sizeof (tab_reg[0])){
        qDebug() << "write success";
    }
}
void MainWindow::on_btnViewPlate_clicked() /* 查看扩展板输入输出状态 */
{
    uint16_t reg[2];
    int rc = modbus_read_registers(mb, 0, 2, reg);

    for(byte i = 0; i < rc; i++){
        ui->textEdit->append(QString::fromLocal8Bit("扩展板0%1").arg(i+1));
        ui->textEdit->append("----------------------------");
        for(byte j = 0; j < 16; j++){
            QString str = QString("%1 : %2").arg(j+1).arg( (reg[rc] >> j) & 0x0001);/* 获取位状态 */
            ui->textEdit->append(str);
        }
    }


}

void MainWindow::on_btnViewPlc_clicked()  /* 查看PLC输入输出点状态 */
{
    uint8_t dest[8] = {0};
    byte rc = modbus_read_input_bits(mb, 0, 8, dest); /* 读输入端 */

    for(byte i = 0; i < rc; i++){

        QString in = QString("IX0.%1 = %2").arg(i).arg(dest[i]);
        ui->textEdit->append(in);
    }

    memset(dest, '\0', sizeof (dest));
    ui->textEdit->append("---------------------------");
    rc = modbus_read_bits(mb, 0, 8, dest);

    for(byte i = 0; i < rc; i++){

        QString in = QString("QX0.%1 = %2").arg(i).arg(dest[i]);
        ui->textEdit->append(in);
    }
    memset(dest, '\0', sizeof (dest));
}

点击查看PLCIO按钮
点击查看扩展板按钮,实际上读的是MW0和MW1
输入车位号206,点击确认,打开modscan32查看一下PLC内部值是否改变
在这里插入图片描述
可以看到已经写入成功

最后,说一下我调试的过程,首先先写好一个测试用的DEMO,我把常用的几个功能码都使用了用按钮触发,就是上面贴的代码,有几个我没贴,然后使用软件VSPD虚拟两个串口出来,再用软件Modbus Slave来模拟从站,就可以对代码进行调试了。如果有硬件,可以使用两个USB转485并联与PLC相连,打开串口调试软件比如我用的XCOM2.0,和你自己写的软件分别连接一个COM口,这样你发送给PLC的数据就可以显示在串口调试软件上了,这里要注意的是,点对点测试的时候,串口调试软件会收到两条数据,一条是你发送给PLC的数据,一条是PLC返回的数据,一开始我以为是我发送的数据格式有误。好了,今天就写到这里,关于工程文件已经上传CSDN了,积分窘迫的同学可以评论或者私信我,分享快乐。

评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值