Qt之串口设计-线程实现(十二)
目录
前言
一、SerialPort
二、实现方式
1.创建类
2.相关功能函数
3.用户使用
4.效果演示
5.拓展应用-实时刷新
总结
前言
Qt作为一个跨平台的应用程序开发框架,在串口编程方面提供了方便易用的API,使得开发者能够轻松实现基于串口通信的各种应用。
Qt框架中提供的一个串口通讯类QtSerialPort,它属于Qt自带的模块类,专门用于进行串行通信,使用时只需要在工程文件pro内添加QT += serialport即可,这种实现方式封装程度高、使用简单,与Qt框架集成紧密,利用Qt的信号与槽机制进行事件处理采用,但给我的感觉在一些高速数据处理时,不是很好用。因此本文推荐采用Windows API调用相关串口功能函数,用于从文件或设备中读取数据。
一、SerialPort
在Qt平台中,调用window相关API,例如CreatFile、readFile、writeFile等函数,以实现串口通讯,并采用C语言实现。
二、实现方式
本文将采用线程实现对串口数据的收发,创建一个线程类,将该类作为主窗口的私有变量使用。
在该线程类中,通过Windows,串口被抽象为文件,对串口的读、写,实际上就是对文件的读写。
1.创建类
在Qt项目上创建一个串口类,属于线程public QThread,具体实现如下。
#include <QThread>
#include <stdio.h>
#include <windows.h>
//缓冲区大小
#define BUF_SIZE 1000
class comt4hread : public QThread
{ Q_OBJECT
public:comt4hread();~comt4hread(){}bool start_flag;bool save_flag;QByteArray filebuf;QByteArray sendbuf;const char *ComName;int BaudValue;int BitValue;int ParitySelt;int StopSelt;
protected:HANDLE OpenSerial(const char *com, //串口名称,如COM1,COM2int baud, //波特率:常用取值:CBR_9600、CBR_19200、CBR_38400、CBR_115200、CBR_230400、CBR_460800int byteSize, //数位大小:可取值7、8;int parity, //校验方式:可取值NOPARITY、ODDPARITY、EVENPARITY、MARKPARITY、SPACEPARITYint stopBits);void run(void);void ProData(QByteArray);
};
在上面代码中,需要添加头文件#include <Windows.h>,该函数的具体用法可以参考微软MS的官方文档Win32 应用 |Microsoft 学习,里面比较详细。
2.相关功能函数
该线程类的实现:首先在构造函数初始化相关变量;然后通过OpenSerial函数完成对串口的一系列设置,包括波特率、校验位等;最后启动线程run后,在while循环中实现对串口的读(ReadFile函数)、写(WriteFile函数)。代码如下(示例):
#include "com4thread.h"
comt4hread::comt4hread(){start_flag = false;ComName = "COM4";
}
HANDLE comt4hread::OpenSerial(const char *com, //串口名称,如COM1,COM2int baud, //波特率:常用取值:CBR_9600、CBR_19200、CBR_38400、CBR_115200、CBR_230400、CBR_460800int byteSize, //数位大小:可取值7、8;int parity, //校验方式:可取值NOPARITY、ODDPARITY、EVENPARITY、MARKPARITY、SPACEPARITYint stopBits) //停止位:ONESTOPBIT、ONE5STOPBITS、TWOSTOPBITS;
{DCB dcb;BOOL b = FALSE;COMMTIMEOUTS CommTimeouts;HANDLE comHandle = INVALID_HANDLE_VALUE;//打开串口WCHAR wszClassName[10];memset(wszClassName, 0, sizeof(wszClassName));MultiByteToWideChar(CP_ACP, 0, com, int(strlen(com)+1), wszClassName,sizeof(wszClassName) / sizeof(wszClassName[0]));comHandle = CreateFile(wszClassName, //串口名称GENERIC_READ | GENERIC_WRITE, //可读、可写0, // No SharingNULL, // No SecurityOPEN_EXISTING,// Open existing port only
// FILE_ATTRIBUTE_NORMAL, // Non Overlapped I/O0, //同步方式NULL); // Null for Comm Devicesif (INVALID_HANDLE_VALUE == comHandle) {qDebug() << "CreateFile fail.";return comHandle;}// 设置读写缓存大小b = SetupComm(comHandle, BUF_SIZE, BUF_SIZE);if (!b)qDebug() << "SetupComm fail.";//设定读写超时CommTimeouts.ReadIntervalTimeout = MAXDWORD;//读间隔超时CommTimeouts.ReadTotalTimeoutMultiplier = 0;//读时间系数CommTimeouts.ReadTotalTimeoutConstant = 0;//读时间常量CommTimeouts.WriteTotalTimeoutMultiplier = 1;//写时间系数CommTimeouts.WriteTotalTimeoutConstant = 1;//写时间常量b = SetCommTimeouts(comHandle, &CommTimeouts); //设置超时if (!b)qDebug() << "SetCommTimeouts fail.";//设置串口状态属性GetCommState(comHandle, &dcb); // 获取当前dcb.BaudRate = ulong(baud); // 波特率dcb.ByteSize = uchar(byteSize); // 每个字节有位数dcb.Parity = uchar(parity); // 无奇偶校验位dcb.StopBits = uchar(stopBits); // 一个停止位b = SetCommState(comHandle, &dcb);//设置if (!b)qDebug() << "SetCommState fail.";return comHandle;
}
void comt4hread::run(void)
{BOOL err = FALSE;DWORD wRLen = 0;DWORD wWLen = 0;char buf[BUF_SIZE] = {0};HANDLE comHandle = INVALID_HANDLE_VALUE;//串口句柄QByteArray tempbuf;//打开串口comHandle = OpenSerial(ComName, CBR_115200, 8, NOPARITY, ONESTOPBIT);//comHandle = OpenSerial(ComName, BaudValue, BitValue, ParitySelt, StopSelt);qDebug() << comHandle;if (INVALID_HANDLE_VALUE == comHandle) {qDebug() << "OpenSerial COM fail!";return;}qDebug() << "Open COM Successfully!";//循环接收消息,收到消息后将消息内容while(start_flag){wRLen = 0;//读串口消息err = ReadFile(comHandle, buf, sizeof(buf)-1, &wRLen, NULL);if (err && wRLen > 0) //读成功并且数据大小大于0{tempbuf.append(buf, int(wRLen));if(save_flag)filebuf.append(tempbuf);//处理数据ProData(tempbuf);tempbuf.clear();}//写串口消息if(!sendbuf.isEmpty()){err = WriteFile(comHandle, sendbuf, size_t(sendbuf.size()), &wWLen, NULL);if (!err)qDebug() << "SendData_WriteFile fail.";sendbuf.clear();}}
}
3.用户使用
创建完上面的线程类后,用户需要调用/使用它,首先在构造函数初始化串口、定时器相关变量,然后通过定时器实时获取串口,当串口有效时,启动串口,具体含义实现如下。
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent) :QMainWindow(parent),ui(new Ui::MainWindow){ui->setupUi(this);/****串口初始化*****/Initcomthread(); /****定时器初始化****/InitTimerEvent();
}
MainWindow::~MainWindow()
{delete ui;
}
void MainWindow::Initcomthread(void)
{comth = new comt4hread();comth->start_flag = false;comth->save_flag = false;//comth->start(QThread::HighPriority);//connect(this, SIGNAL(comconfig(QStringList)), comth, SLOT(comconfig(QStringList)));
}
void MainWindow::InitTimerEvent(void)
{timecnt = 0;ftimer_flag = true;show_1s_flag = true;m_pluseTimeid = startTimer(100); // 100毫秒事件处理
}
//发送数据
void MainWindow::on_sendButton_clicked()
{ QByteArray temp = ui->textEdit->toPlainText().toLatin1(); comth->sendbuf.append(temp);
// comth->send_flag = true;ui->listWidgetRecv->addItem("本地发送数据:" + temp.toHex(' ').toUpper());ui->listWidgetRecv->setCurrentRow(ui->listWidgetRecv->count() - 1);
}
void MainWindow::timerEvent(QTimerEvent *t)
{if(t->timerId() == m_pluseTimeid) // 100毫秒事件处理{static quint8 cnt=0;cnt++;if(cnt == 10){//查找可用的串口if(ftimer_flag){if(!(ui->PortBox->currentText().isEmpty())){ui->lineEdit_6->setText("获取可用串口");ui->lineEdit_6->setStyleSheet("color:red;");ftimer_flag = false;show_1s_flag = true;}else{ui->lineEdit_6->setText(QString::number(timecnt));timecnt++;foreach(const QSerialPortInfo &info, QSerialPortInfo::availablePorts()){QSerialPort serialtemp;serialtemp.setPort(info);if(serialtemp.open(QIODevice::ReadWrite)){ui->PortBox->addItem(serialtemp.portName());serialtemp.clear();serialtemp.close();}}}}else{if(ui->PortBox->currentText().isEmpty()){ftimer_flag = true;timecnt = 0;}show_1s_flag = true;}cnt = 0;}}
}
void MainWindow::on_PortBox_activated(const QString &arg1)
{if(arg1.isEmpty())return;comth->ComName = ui->PortBox->currentText().toUtf8();comth->BaudValue = ui->BaudBox->currentText().toInt();comth->BitValue = ui->BitNumBox->currentText().toInt();comth->ParitySelt = ui->ParityBox->currentText().toInt();comth->StopSelt = ui->StopBox->currentText().toInt();comth->start(QThread::HighPriority);
}
void MainWindow::on_clear_clicked()
{ui->listWidgetRecv->clear();
}
4.效果演示
软件搜索到没有串口时,开始计时。

有串口时,选择相应串口,Qt输出栏提示Open COM Successful!

5.拓展应用-实时刷新
一般我们工业上使用串口,为了实现对数据实时采集、并通过曲线显示出来。这时我们一般采取的是,先在线程中将串口收到的数据进行解析处理ProData,然后将解析出来的结果值通过变量传递给主界面上,让其知晓,然后通过主窗口的定时器或者信号槽函数显示在UI界面上。
如果是要绘制曲线,可以参考博文Qt之第三方库QCustomPlot使用(二)-CSDN博客,了解曲线绘制的特性及使用方法。
总结
博文中相应的工程代码Qt-Case.zip 利用Qt开发软件进行编的例程,为博文提供案例-CSDN文库。
相关文章:
Qt之串口设计-线程实现(十二)
Qt开发 系列文章 - Serial-port(十二) 目录 前言 一、SerialPort 二、实现方式 1.创建类 2.相关功能函数 3.用户使用 4.效果演示 5.拓展应用-实时刷新 总结 前言 Qt作为一个跨平台的应用程序开发框架,在串口编程方面提供了方便易用…...
探索 Seaborn Palette 的奥秘:为数据可视化增色添彩
一、引言 在数据科学的世界里,视觉传达是不可或缺的一环。一个好的数据可视化不仅能传递信息,还能引发共鸣。Seaborn 是 Python 中一款广受欢迎的可视化库,而它的调色板(palette)功能,则为我们提供了调配绚…...
Linux创建普通用户和修改主机名
创建修改用户名和用户组 工作组相关命令 功能命令说明切换用户su username注销用户logout新建用户adduser username 创建用户并分配到用户组useradd -g test username 设置用户密码passwd username查看某一用户w username查看登录用户w查看登陆用户并显示IPwho查看登录历史…...
在 Spring Boot 3 中实现基于角色的访问控制
基于角色的访问控制 (RBAC) 是一种有价值的访问控制模型,可增强安全性、简化访问管理并提高效率。它在管理资源访问对安全和运营至关重要的复杂环境中尤其有益。 我们将做什么 我们有一个包含公共路由和受限路由的 Web API。受限路由需要数据库中用户的有效 JWT。 现在用户…...
二八(vue2-04)、scoped、data函数、父子通信、props校验、非父子通信(EventBus、provideinject)、v-model进阶
1. 组件的三大组成部分(结构/样式/逻辑) 1.1 scoped 样式冲突 App.vue <template><!-- template 只能有一个根元素 --><div id"app"><BaseOne></BaseOne><BaseTwo></BaseTwo></div> </template><script…...
配置PostgreSQL用于集成测试的步骤
在进行软件开发时,集成测试是确保各个组件能够协同工作的关键环节。PostgreSQL作为一种强大的开源数据库系统,常被用于集成测试中。下面将详细介绍如何在不同的环境中配置PostgreSQL以支持集成测试。 1. 选择并安装PostgreSQL 首先,你需要根…...
【ComfyUI + 铅笔素描画风】艺术家DaTou发布了的彩色铅笔素描风格生成(真实感超强)
发布时间:2024年12月09日 项目主页:https://hf-mirror.com/Datou1111/shou_xin 基础模型:flux.1-dev comfyui工作流下载:https://pan.baidu.com/s/1FrLQ4o8ldckKwhIrN1Pv7g?pwd1220 自己测试 官方效果 生成猫猫 shou_xin, a m…...
Unity-Editor扩展GUI基本实现一个可拖拉放的格子列表
短短几百行代码,好吧,又是“参考”了国外的月亮 操作,还真地挺自然的。。。。。。国外的实现有点小牛 拖拉,增加+ 一个Element 鼠标左键长按,可以出提示 鼠标右键,清除Element, 有点小bug,不是很自然地完全清除, using System.Collections; using System.Collecti…...
后摩尔定律时代,什么将推动计算机性能优化的发展?
在摩尔定律时代,每两年芯片上的晶体管数量就会翻一番,这一看似不可避免的趋势被称为摩尔定律,它极大地促进了计算机性能的提高。然而,硅基晶体管不可能一直小下去,半导体晶体管的微型化推动了计算机性能的提升…...
SQL进阶技巧:如何计算商品需求与到货队列表进出计划?
目录 0 需求描述 1 数据准备 2 问题分析 3 小结 累计到货数量计算 出货数量计算 剩余数量计算 0 需求描述 假设现有多种商品的订单需求表 DEMO_REQUIREMENT,以及商品的到货队列表 DEMO_ARR_QUEUE,要求按照业务需要,设计一个报表&#…...
linux普通用户使用sudo不需要输密码
1.root用户如果没有密码,先给root用户设置密码 sudo passwd root #设置密码 2.修改visudo配置 su #切换到root用户下 sudo visudo #修改visudo配置文件 用户名 ALL(ALL) NOPASSWD: ALL #下图所示处新增一行配置 用户名需要输入自己当前主机的用户名...
Mac配置 Node镜像源的时候报错解决办法
在Mac电脑中配置国内镜像源的时候报错,提示权限问题,无法写入配置文件。本文提供解决方法,青测有效。 一、原因分析 遇到的错误是由于 .npm 目录下的文件被 root 用户所拥有,导致当前用户无法写入相关配置文件。 二、解决办法 在终端输入以下命令,输入管理员密码即可。 su…...
R语言的数据结构-数据框
【图书推荐】《R语言医学数据分析实践》-CSDN博客 《R语言医学数据分析实践 李丹 宋立桓 蔡伟祺 清华大学出版社9787302673484》【摘要 书评 试读】- 京东图书 (jd.com) R语言医学数据分析实践-R语言的数据结构-CSDN博客 在医学领域中,R语言的数据框(…...
分布式全文检索引擎ElasticSearch-数据的写入存储底层原理
一、数据写入的核心流程 当向 ES 索引写入数据时,整体流程如下: 1、客户端发送写入请求 客户端向 ES 集群的任意节点(称为协调节点,Coordinating Node)发送一个写入请求,比如 index(插入或更…...
react中实现导出excel文件
react中实现导出excel文件 一、安装依赖二、实现导出功能三、自定义列标题四、设置列宽度五、样式优化1、安装扩展库2、设置样式3、扩展样式功能 在 React 项目中实现点击按钮后导出数据为 Excel 文件,可以使用 xlsx 和 file-saver 这两个库。 一、安装依赖 在项目…...
有监督学习 vs 无监督学习:机器学习的两大支柱
有监督学习 vs 无监督学习:机器学习的两大支柱 有监督学习 vs 无监督学习:机器学习的两大支柱一、有无“老师”来指导二、解决的问题类型不同三、模型的输出不同 有监督学习 vs 无监督学习:机器学习的两大支柱 在机器学习的奇妙世界里&#…...
c4d动画怎么导出mp4视频,c4d动画视频格式设置
宝子们,今天来给大家讲讲 C4D 咋导出mp4视频的方法。通过用图文教程的形式给大家展示得明明白白的,让你能轻松理解和掌握,不管是理论基础,还是实际操作和技能技巧,都能学到,快速入门然后提升自己哦。 c4d动…...
差分矩阵(Difference Matrix)与累计和矩阵(Running Sum Matrix)的概念与应用:中英双语
本文是学习这本书的笔记: https://web.stanford.edu/~boyd/vmls/ 差分矩阵(Difference Matrix)与累计和矩阵(Running Sum Matrix)的概念与应用 在线性代数和信号处理等领域中,矩阵运算常被用来表示和计算各种数据变换…...
全面解析 Golang Gin 框架
1. 引言 在现代 Web 开发中,随着需求日益增加,开发者需要选择合适的工具来高效地构建应用程序。对于 Go 语言(Golang)开发者来说,Gin 是一个备受青睐的 Web 框架。它轻量、性能高、易于使用,并且具备丰富的…...
全脐点曲面当且仅当平面或者球面的一部分
S 是全脐点曲面当且仅当 S 是平面或者球面的一部分。 S_\text{ 是全脐点曲面当且仅当 }{S_\text{ 是平面或者球面的一部分。}} S 是全脐点曲面当且仅当 S 是平面或者球面的一部分。 证: 充分性显然,下证必要性。 若 r ( u , v ) r(u,v) r(u,v)是…...
基于ASP.NET+ SQL Server实现(Web)医院信息管理系统
医院信息管理系统 1. 课程设计内容 在 visual studio 2017 平台上,开发一个“医院信息管理系统”Web 程序。 2. 课程设计目的 综合运用 c#.net 知识,在 vs 2017 平台上,进行 ASP.NET 应用程序和简易网站的开发;初步熟悉开发一…...
基于uniapp+WebSocket实现聊天对话、消息监听、消息推送、聊天室等功能,多端兼容
基于 UniApp + WebSocket实现多端兼容的实时通讯系统,涵盖WebSocket连接建立、消息收发机制、多端兼容性配置、消息实时监听等功能,适配微信小程序、H5、Android、iOS等终端 目录 技术选型分析WebSocket协议优势UniApp跨平台特性WebSocket 基础实现连接管理消息收发连接…...
如何将联系人从 iPhone 转移到 Android
从 iPhone 换到 Android 手机时,你可能需要保留重要的数据,例如通讯录。好在,将通讯录从 iPhone 转移到 Android 手机非常简单,你可以从本文中学习 6 种可靠的方法,确保随时保持连接,不错过任何信息。 第 1…...
Robots.txt 文件
什么是robots.txt? robots.txt 是一个位于网站根目录下的文本文件(如:https://example.com/robots.txt),它用于指导网络爬虫(如搜索引擎的蜘蛛程序)如何抓取该网站的内容。这个文件遵循 Robots…...
Linux-07 ubuntu 的 chrome 启动不了
文章目录 问题原因解决步骤一、卸载旧版chrome二、重新安装chorme三、启动不了,报错如下四、启动不了,解决如下 总结 问题原因 在应用中可以看到chrome,但是打不开(说明:原来的ubuntu系统出问题了,这个是备用的硬盘&a…...
鸿蒙中用HarmonyOS SDK应用服务 HarmonyOS5开发一个生活电费的缴纳和查询小程序
一、项目初始化与配置 1. 创建项目 ohpm init harmony/utility-payment-app 2. 配置权限 // module.json5 {"requestPermissions": [{"name": "ohos.permission.INTERNET"},{"name": "ohos.permission.GET_NETWORK_INFO"…...
Android15默认授权浮窗权限
我们经常有那种需求,客户需要定制的apk集成在ROM中,并且默认授予其【显示在其他应用的上层】权限,也就是我们常说的浮窗权限,那么我们就可以通过以下方法在wms、ams等系统服务的systemReady()方法中调用即可实现预置应用默认授权浮…...
零基础在实践中学习网络安全-皮卡丘靶场(第九期-Unsafe Fileupload模块)(yakit方式)
本期内容并不是很难,相信大家会学的很愉快,当然对于有后端基础的朋友来说,本期内容更加容易了解,当然没有基础的也别担心,本期内容会详细解释有关内容 本期用到的软件:yakit(因为经过之前好多期…...
2023赣州旅游投资集团
单选题 1.“不登高山,不知天之高也;不临深溪,不知地之厚也。”这句话说明_____。 A、人的意识具有创造性 B、人的认识是独立于实践之外的 C、实践在认识过程中具有决定作用 D、人的一切知识都是从直接经验中获得的 参考答案: C 本题解…...
重启Eureka集群中的节点,对已经注册的服务有什么影响
先看答案,如果正确地操作,重启Eureka集群中的节点,对已经注册的服务影响非常小,甚至可以做到无感知。 但如果操作不当,可能会引发短暂的服务发现问题。 下面我们从Eureka的核心工作原理来详细分析这个问题。 Eureka的…...
