在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;如下:
step 2 设置Modbus Slave,设置连接属性和从站信息如下:
设置连接属性,com2->com1,波特率等:115200,8,n,1,mode:rtu;
设置从站信息:从站为1,功能码03,寄存器开始位为0;数量10;
step 3 配置qt+libmodbus,打开下载的libmodbus文件夹复制src到qt工程文件,如下:
复制config.h.in到工程文件下,把“.in”去除;
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中的信息:
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);//获取当前设置的超时
*
*/