当前位置:   article > 正文

Windows端,libmodbus + Qt 上位机测试+报文解析_modbus windows

modbus windows

Windows端,libmodbus + Qt 上位机测试+报文解析

本文是在上一篇VS上实现modbus通讯的基础上,在Qt上做了一些简单的修改,以实现Qt上modbus的通讯。(也算是曲线救国了)

1 理清思路

一直以来,我对于Modbus-Master(主机端)和Modbus-Slave(从机端)通讯中,关于到底是在哪一端对寄存器赋值有点纠结。
主要原因:

  • 1.是自己对通讯概念的不清晰,主机端和从机端都可以传递数据,都可对寄存器进行赋值;
  • 2.参考网上的资料,大都是在Qt端建立Master与Slave的连接,而赋值是在工具Modbus Slave上完成的,所以造成误解

嵌入式STM32学习笔记(8)——libmodbus+Qt上位机测试
文中所展示的功能即是在Modbus Slave工具中对寄存器进行赋值,连接成功后,在命令窗口可看到值的传递情况。
但是对于我的实际情况中,Qt作为上位机,要通过对Slave端传递数值,以达到发送指令的效果。所以需要,对其中的代码做一些修改才能为我所用。

2 验证思路

在写代码前,需要对思路进行验证,看是否合理,首先借助Modbus辅助工具。

2.1 Modbus 辅助工具测试

借用辅助工具,建立Modbus Poll和Modbus Slave连接,然后分别以Modbus Poll端传递数据给Modbus Slave端,接着以Modbus Slave端传递数据给Modbus Poll端。
1.从Modbus Poll端分别传递1值给Modbus Slave端,如下图1所示。
在这里插入图片描述
相应的报文为:
(1)Modbus Poll:
Tx: 01 03 00 00 00 01 84 0A
Rx: 01 03 02 00 01 79 84
(2)Modbus Slave:
Rx: 01 03 00 00 00 01 84 0A
Tx: 01 03 02 00 01 79 84
其中,Tx表示发送,Rx表示接收。
报文格式:01 03 00 00 00 01 84 0A
01——从机地址
03——功能码(读取数据)
00——高位地址
00——低位地址
00——寄存器个数高位
01——寄存器个数低位
84——CRC高位
0A——CRC低位

报文格式:01 03 02 00 01 79 84
01——从机地址
03——功能码(读取数据)
02——数据长度
00——数据高位字节
01——数据低位字节
79——CRC高位
84——CRC低位

这个过程这样描述:
Modbus Poll端,手动输入数值后,主机端读取该地址处输入的数据值,发送给从机端,从机端响应主机端(我收到了)。

2.从Modbus Slave端传递2值给Modbus Poll端,如下图2所示。
在这里插入图片描述
相应的报文为:
(1)Modbus Poll:
Tx: 01 03 00 00 00 00 01 84 0A
Rx: 01 03 02 00 02 39 85
(2)Modbus Slave:
Rx: 01 03 00 00 00 00 01 84 0A
Tx: 01 03 02 00 02 39 85
其中,Tx表示发送,Rx表示接收。
相应的报文格式同上。
与上一样,说明寄存器赋值的地方不影响通讯方式。

3 Qt上位机测试

以上位机Qt作Modbus-Master(主机端),并在主机端,对寄存器进行赋值,采用了随机采样赋值方法。
先贴上代码:
1.mywidget.h

#ifndef MYWIDGET_H
#define MYWIDGET_H

#include <QWidget>
#include <libmodbus/modbus.h>

#define ADDRESS_START 0
#define ADDRESS_END 9
#define LOOP  1

QT_BEGIN_NAMESPACE
namespace Ui { class myWidget; }
QT_END_NAMESPACE

class myWidget : public QWidget
{
    Q_OBJECT

public:
    myWidget(QWidget *parent = nullptr);
    ~myWidget();

private slots:
    void on_pushButton_clicked();

private:
    Ui::myWidget *ui;
    modbus_t *ctx;
    uint16_t *tab_reg;
    uint16_t *send_tab_reg;
    int nb;
    int addr;
    modbus_mapping_t *mb_mapping;
    int nb_fail;
    int nb_loop;
};
#endif // MYWIDGET_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.mywidget.cpp

#include "mywidget.h"
#include "ui_mywidget.h"

#include "libmodbus/modbus.h"
#include "QThread"
#include <QtDebug>
#include <errno.h>
#include <stdlib.h>
#include <malloc.h>


myWidget::myWidget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::myWidget)
{
    ui->setupUi(this);
}

myWidget::~myWidget()
{
    delete ui;
}


void myWidget::on_pushButton_clicked()
{
    int rc;
    int i;

    ctx = modbus_new_rtu("COM3", 115200, 'N', 8, 1);
    modbus_set_slave(ctx,1);

    modbus_set_debug(ctx,TRUE);

    if (modbus_connect(ctx)==-1)
        {
            fprintf(stderr, "Connection failed: %s\n",modbus_strerror(errno));
            modbus_free(ctx);
        }


        /*
        uint32_t old_response_to_sec;//秒
        uint32_t old_response_to_usec;//微秒,1秒=1,000,000微秒

        modbus_get_response_timeout(ctx, &old_response_to_sec, &old_response_to_usec);//获取当前设置的超时

        modbus_set_response_timeout(ctx,0,50000);//设定超时,默认50ms(50,000us)
        qDebug()<<"get timeout sec:"<<old_response_to_sec;
        qDebug()<<"get timeout usec:"<<old_response_to_usec;



        printf("\n----------------\n");*/
        nb=ADDRESS_END-ADDRESS_START;

        tab_reg=(uint16_t *)malloc(nb * sizeof(uint16_t));
        memset(tab_reg,0,nb * sizeof(uint16_t));

        /*写入保持寄存器数值*/
        nb_loop=nb_fail=0;
        while(nb_loop++<LOOP)
        {
            for(addr=ADDRESS_START;addr<ADDRESS_END;addr++)
            {
                int i;
                for(i=0;i<nb;i++)
                {
                    tab_reg[i]=(uint16_t)(65535*rand()/(RAND_MAX+1.0));
                }
                nb=ADDRESS_END-addr;

                rc=modbus_write_register(ctx,addr,tab_reg[0]);
                if(rc !=1)
                {
                    printf("ERROR modbus_write_register(%d)\n",rc);
                    nb_fail++;
                }
                else
                {
                    rc = modbus_read_registers(ctx,addr,1, tab_reg); //03#读取保持寄存器的值,(对象,起始地址,读取数量,存储读取到的值)
                    if (rc != 1)
                    {
                        fprintf(stderr,"%s\n", modbus_strerror(errno));
                        qDebug()<<"rc错误"<<modbus_strerror(errno);
                        nb_fail++;
                    }
                    else
                    {
                        qDebug()<<"链接成功";
                        for (i=0; i<10; i++)
                        {
                            printf("reg[%d] = %d(0x%x)\n", i, tab_reg[i], tab_reg[i]);
                            qDebug()<<"rc收到:"<<tab_reg[i];
                        }
                     }

                 }

               }
        }
        modbus_close(ctx);  //关闭modbus连接
        modbus_free(ctx);   //释放modbus资源,使用完libmodbus需要释放掉
}




  • 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
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108

执行后的效果,如下图所示:
图3
可以看到,寄存器赋的值已经传递进Modbus Slave中,说明此代码在Modbus Master端赋值寄存器的值有效。
另外,在做这一测试时,需要参考我的上两篇文章的内容,即libmodbus库的编辑以及modbus通讯模拟。

4 Modbus通讯数据包解析

实现通讯连接之后,就需要对报文进行解析。如下图所示,单寄存器10位的数值在左侧栏可清楚看到,报文信息在右侧可详细看到,那么,现在就来分析报文与数值是怎么对应的。
报文解析

(1)首地址的数值对应的报文解析:
首地址处的数值为-23
接收到的报文信息是:
01 06 00 00 FF E9 09 B4
其中RTU报文的格式是这样定义的:
报文格式
01——从机地址
06——功能码
00——寄存器高位地址
00——寄存器低位地址
FF——数据高位
E9——数据低位
09——CRC高位
B4——CRC低位
现在主要来计算数据位的计算:
-23,首先23对应的16进制数为0017,那么-23=0-0017=FFFF -0017+1=FFE9
至于CRC的计算,因为有CRC函数就不需要考虑了。
(2)首地址的数值传递的报文解析:
在这里插入图片描述
Rx表示接收,Tx表示传递
过程是这样的:

先是从机端接收到这样一条报文信息,然后将收到的数值以报文的信息再发送回给主机端,(告诉我收到了),主机端读取报文信息,从机端再应答一下。
为了验证一下思路是否正确,再做一个
(3)地址为1的数值传递
地址1处的数值为-21157
接收到的报文信息是:
01 06 00 01 AD 5B E5 61
按照上面报文格式的介绍,只对其中数据高低位进行计算:
21157对应的16进制数为52A5,
-21157=0-52A5=FFFF-52A5+1=AD5B
数据高位 AD 数据低位 5B
(4)地址1处的数值传递报文解析:
在这里插入图片描述
如图所示,数值传递的过程是这样的:
先是从机端接收到这样一条报文,然后对主机端进行应答,发送相应的报文,然后主机端对报文进行读取,即读取01地址处的值,从机端响应主机端,将地址处的信息发回。
接下来要做的是对数据包的处理。
参考1:libmodbus在Windows端Qt 5上的使用注意事项
参考2:Visual Studio 上基于libmodbus库的modbus RTU模式的通讯模拟
参考3:嵌入式STM32学习笔记(8)——libmodbus+Qt上位机测试
参考4:Modbus RTU报文格式

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

闽ICP备14008679号