在stm32开发时候,需要用到一些标准的通讯协议,比如modbus,canopen,tcp/ip等,本次下载实验了一下开源库libmodbus,结合Qt进行简单实验;基本工具需要如下:

1、libmodbus开源库:https://libmodbus.org/download/

2、Qt,开发环境,自行安装下载;

3、虚拟串口工具:Configure Virtual Serial Port Driver;

4、modbus辅助软件:Modbus Poll、Modbus Slave,官网下载即可(30天试用);

step 1 做完上面工作后,打开虚拟串口后,设置如下,添加两个虚拟串口com1和2;如下:

stm32 freemodbus应用实例 stm32 libmodbus_物联网

step 2 设置Modbus Slave,设置连接属性和从站信息如下:

stm32 freemodbus应用实例 stm32 libmodbus_寄存器_02

设置连接属性,com2->com1,波特率等:115200,8,n,1,mode:rtu;

stm32 freemodbus应用实例 stm32 libmodbus_ci_03

设置从站信息:从站为1,功能码03,寄存器开始位为0;数量10;

stm32 freemodbus应用实例 stm32 libmodbus_ci_04

step 3 配置qt+libmodbus,打开下载的libmodbus文件夹复制src到qt工程文件,如下:

stm32 freemodbus应用实例 stm32 libmodbus_嵌入式_05

复制config.h.in到工程文件下,把“.in”去除;

stm32 freemodbus应用实例 stm32 libmodbus_嵌入式_06

step 4 修改qt的配置文件 .pro,添加如下代码:

SOURCES += \
        main.cpp \
        widget.cpp\
        src/modbus.c \
#        src/modbus-ascii.c \ #libmodbus 3.1.6无此文件
        src/modbus-data.c \
        src/modbus-rtu.c \
        src/modbus-tcp.c

HEADERS += \
        widget.h\
        src/modbus.h \
#        src/modbus-ascii.h \ #libmodbus 3.1.6无此文件
#        src/modbus-ascii-private.h \ #libmodbus 3.1.6无此文件
        src/config.h \
        src/modbus-private.h \
        src/modbus-rtu.h \
        src/modbus-rtu-private.h \
        src/modbus-tcp.h \
        src/modbus-tcp-private.h \
        src/modbus-version.h

INCLUDEPATH += $$PWD/src #包含目录
LIBS += -lws2_32 # 这个是libmodbus配置win32环境必须的

step 5 在qt工程里添加 #include “modbus.h”,qmake后,构建一下,正常没有问题,在ui里添加个按钮,编辑按钮事件,我在widget.h中如下:

#include "widget.h"
#include "ui_widget.h"

#include "modbus.h"
#include "QThread"
#include <QtDebug>
#pragma execution_character_set("utf-8")

uint16_t tab_reg[64] = {0}; //定义存放数据的数组
uint16_t send_tab_reg[64] = {0}; //定义存放数据的数组
modbus_t *ctx = nullptr;
int flag_close_md=0;

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

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

void Widget::on_pushButton_clicked()
{

    int rc;
    int i;

    //ctx=modbus_new_ascii("COM1", 115200, 'N', 8, 1);//libmodbus 3.1.6 不支持
    ctx = modbus_new_rtu("COM1", 115200, 'N', 8, 1);
    //ctx= modbus_new_tcp("127.0.0.1", 1502);
    
    if (ctx == nullptr)                //使用UART1,对应的设备描述符为ttySP1
    {
        fprintf(stderr, "Unable to allocate libmodbus contex\n");
        //return 0;
    }
    modbus_set_debug(ctx, 1);      //设置1可看到调试信息
    modbus_set_slave(ctx, 1);      //设置slave ID

    //设置反馈超时检测
    modbus_set_response_timeout(ctx,0,100);//设定超时,默认50ms(50,000us)
    uint32_t old_response_to_sec;//秒
    uint32_t old_response_to_usec;//微秒,1秒=1,000,000微秒
    /* Save original timeout */
    modbus_get_response_timeout(ctx, &old_response_to_sec, &old_response_to_usec);//获取当前设置的超时
    qDebug()<<"get timeout sec:"<<old_response_to_sec;
    qDebug()<<"get timeout usec:"<<old_response_to_usec;

    if (modbus_connect(ctx) == -1) //等待连接设备
    {
        fprintf(stderr, "Connection failed:%s\n", modbus_strerror(errno));
       // return -1;
        qDebug() <<"链接失败";
    }

    printf("\n----------------\n");
    rc = modbus_read_registers(ctx,0,10, tab_reg); //03#读取保持寄存器的值,(对象,起始地址,读取数量,存储读取到的值)
    if (rc == -1)
    {
        fprintf(stderr,"%s\n", modbus_strerror(errno));
         qDebug()<<"rc错误"<<modbus_strerror(errno);
        //return -1;
    }
    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需要释放掉
}

step 6 实验效果如下,点击按钮会读取Modbus Slave中的信息:

stm32 freemodbus应用实例 stm32 libmodbus_数组_07

step 7 总结,libmodbus主要函数简单总结:

/*
 *简单总结
 *modbus_t *ctx = nullptr;//先创建modbus对象
 *ctx = modbus_new_ascii("COM1", 115200, 'N', 8, 1); //设置ascii用
 *ctx = modbus_new_rtu("COM1", 115200, 'N', 8, 1); //设置RTU用
 *ctx = modbus_new_tcp("127.0.0.1", 1502);//设置tcp用
 *modbus_set_slave(ctx, 1); //设置slave ID
 *modbus_connect(ctx) == -1 //检测是否连接可用
 *
 * //#03功能码,读取多个寄存器
 *uint16_t tab_reg[64] = {0}; //定义存放数据的数组,自己定义
 *int rc = modbus_read_registers(ctx,0,10, tab_reg); //03#读取保持寄存器的值,(对象,起始地址,读取数量,存储读取到的值)
 *
 * //#06功能码,写指定寄存器
 *int rc = modbus_write_register(ctx,3,10);//06#功能码,写指定寄存器数值,(对象,地址,写入的值)
 *
 * //#16功能码,写多个寄存器
 *uint16_t send_tab_reg[64] = {0}; //定义存放数据的数组,自己定义
 *send_tab_reg[0] = {19}; //定义存放数据的数组
 *send_tab_reg[1] = {20}; //定义存放数据的数组
 *send_tab_reg[2] = {21}; //定义存放数据的数组
 *send_tab_reg[3] = {22}; //定义存放数据的数组
 *int rc = modbus_write_registers(ctx,0,6,send_tab_reg);//16#功能码,写多个连续寄存器数值,(对象,起始地址,到第几位,写入的值)
 *
 * //关闭modbus对象,释放资源
 * modbus_close(ctx);  //关闭modbus连接
 * modbus_free(ctx);   //释放modbus资源,使用完libmodbus需要释放掉 
 * modbus_set_debug(ctx, 1);      //设置1可看到调试信息
 *
 * //设置反馈超时检测
 * modbus_set_response_timeout(ctx,1,100);//设定超时,默认50ms(50,000us)
 * 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);//获取当前设置的超时
 *
 */