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)是…...

CSS学习记录18
CSS渐变 CSS渐变您可以显示两种或多种指定颜色之间的平滑过渡。 CSS定义了两种渐变类型: 线性渐变(向下/向上/向左/向右/对角线)径向渐变(由其中心定义) CSS线性渐变 如需创建线性渐变,您必须至少两个色…...

实验13 C语言连接和操作MySQL数据库
一、安装MySQL 1、使用包管理器安装MySQL sudo apt update sudo apt install mysql-server2、启动MySQL服务: sudo systemctl start mysql3、检查MySQL服务状态: sudo systemctl status mysql二、安装MySQL开发库 sudo apt-get install libmysqlcli…...

90度Floating B to B 高速连接器信号完整性仿真
在180度 B to B Connector 信号完整性仿真时,不会碰到端口设置不方便问题,但在做90度B to B Connector信号完整性仿真时就会碰到端口设置问题。如下面的90度B to B Connector。 公座 母座 公母对插后如下: 客户要求改Connector需符合PCI-E3.…...

【踩坑】Pytorch与CUDA版本的关系及安装
Pytorch、CUDA和CUDA Toolkit区分 查看当前环境常用shell命令python脚本 Driver API CUDA(nvidia-smi)Runtime API CUDA(nvcc --version)pytorch选择CUDA版本的顺序安装需要的CUDA,多版本共存和自由切换 本文参考 http…...

信息隐藏 数字图像空域隐写与分析技术的实现
数字图像隐写与分析 摘要 随着信息技术的发展,隐写术作为一种信息隐藏技术,越来越受到关注。本文介绍了一种基于最低有效位(LSB)方法的数字图像隐写技术,并实现了隐写数据的嵌入与提取。通过卡方检验分析隐写图像的统计特性,评估隐写数据对图像的影响。实验结果表明,该…...

halcon单相机+机器人*眼在手外标定心得
目的 得到相机坐标系下的点与机器人底座base的转换关系,camera_in_base 两个不确定的定量 1,相机与机器人底座base之间的相对位置是固定的,既camera_in_base 2,机械手末端与标定物 tool_in_obj是固定的 辅助确定量 工作台与相…...

pytest入门十:配置文件
pytest.ini:pytest的主配置文件,可以改变pytest的默认行为conftest.py:测试用例的一些fixture配置 pytest.ini marks mark 打标的执行 pytest.mark.add add需要些marks配置否则报warning [pytest] markersadd:测试打标 测试用例中添加了 p…...

基于Clinical BERT的医疗知识图谱自动化构建方法,双层对比框架
基于Clinical BERT的医疗知识图谱自动化构建方法,双层对比框架 论文大纲理解1. 确认目标2. 目标-手段分析3. 实现步骤4. 金手指分析 全流程核心模式核心模式提取压缩后的系统描述核心创新点 数据分析第一步:数据收集第二步:规律挖掘第三步&am…...

介绍 Html 和 Html 5 的关系与区别
HTML(HyperText Markup Language)是构建网页的标准标记语言,而 HTML5 是 HTML 的最新版本,包含了一些新的功能、元素、API 和属性。HTML5 相对于早期版本的 HTML(比如 HTML4)有许多重要的改进和变化。以下是…...

C05S13-MySQL数据库备份与恢复
一、MySQL数据备份 1. 数据备份概述 数据备份的主要目的是灾难恢复,也就是当数据库等出现故障导致数据丢失,能够通过备份恢复数据。 数据备份可以分为物理备份和逻辑备份。物理备份,又称为冷备份,需要关闭数据库进行备份&#…...