Qt之串口设计-线程实现(十二)

Qt开发 系列文章 - Serial-port(十二)


目录

前言

一、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文库。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/890351.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

1.gitlab 服务器搭建流程

前提条件&#xff1a; 一、服务器硬件水平 搭建gitlab服务器最低配置要求2核4G,低于这个配置的服务器运行效果很差。 gitlab官网&#xff1a;https://about.gitlab.com/ 下载地址&#xff1a;gitlab/gitlab-ce - Packages packages.gitlab.com 本机ubuntu 二、安装依赖 su…

powerhsell 初认识

免责声明 本文是学习与泷羽Sec B站课程的课程笔记内容&#xff0c;仅作学习使用&#xff0c;如有破坏网络安全的行为&#xff0c;本人概不负责 B站链接&#xff1a;https://space.bilibili.com/350329294 资源自取&#xff1a;https://pan.quark.cn/s/b2718e905db8 powerhsel…

自我维护和保养

学习运动两不误&#xff01; 本人学习过程中&#xff0c;长期久坐导致各种身体问题&#xff08;特别是腰间盘突出&#xff0c;右侧肩胛骨翘等问题&#xff01;&#xff09;&#xff0c;希望给有类似烦恼的人们带去福音&#xff01;&#xff01;&#xff01; 我的椎间盘损伤历…

详解磁盘IO、网络IO、零拷贝IO、BIO、NIO、AIO、IO多路复用(select、poll、epoll)

1、什么是I/O 在计算机操作系统中&#xff0c;所谓的I/O就是输入&#xff08;Input&#xff09;和输出&#xff08;Output&#xff09;&#xff0c;也可以理解为读&#xff08;Read&#xff09;和写&#xff08;Write)&#xff0c;针对不同的对象&#xff0c;I/O模式可以划分为…

Cursor的重磅功能Agent登场

今天看了一些介绍&#xff0c;cursor有一个新功能agent ,试用了一下非常好用。再也不用头痛地选择相关的上下文&#xff0c;真是懒人利器。 Agant特性&#xff1a; 可以自主选择上下文能够使用终端可以独立完成整个任务 赶紧介绍给大家&#xff0c;使用时&#xff0c;需要在c…

5G -- 空口关键技术

前言&#xff1a; 手机(UE)和5G基站(gNodeB)之间的空中接口 新技术的特点&#xff1a; 1、提升速率&#xff1a;大带宽、新编码、高阶调制、F-OFDM、M-MIMO 2、降低时延&#xff1a;灵活帧结构、自包含时隙、免授权调度、D2D 3、提升覆盖&#xff1a;上下行解耦、EN-DC(双连…

常用Python自动化测试框架有哪些?

随着技术的进步和自动化技术的出现&#xff0c;市面上出现了一些自动化测试框架。只需要进行一些适用性和效率参数的调整&#xff0c;这些自动化测试框架就能够开箱即用&#xff0c;大大节省了测试时间。而且由于这些框架被广泛使用&#xff0c;他们具有很好的健壮性&#xff0…

【自用】通信内网部署rzgxxt项目_01,后端pipeDemo部署(使用nssm.exe仿照nohup)

做完这些工作之后&#xff0c;不要忘记打开 Windows Server 的防火墙端口&#xff0c;8181、8081、8080、22、443、1521 做完这些工作之后&#xff0c;不要忘记打开 Windows Server 的防火墙端口&#xff0c;8181、8081、8080、22、443、1521 做完这些工作之后&#xff0c;不要…

jmeter 接口性能测试 学习笔记

目录 说明工具准备工具配置jmeter 界面汉化配置汉化步骤汉化结果图 案例1&#xff1a;测试接口接口准备线程组添加线程组配置线程组值线程数&#xff08;Number of Threads&#xff09;Ramp-Up 时间&#xff08;Ramp-Up Period&#xff09;循环次数&#xff08;Loop Count&…

Iris简单实现Go web服务器

package mainimport ("github.com/kataras/iris" )func main() {app : iris.New() // 实例一个iris对象//配置路由app.Get("/", func(ctx iris.Context) {ctx.WriteString("Hello Iris")})app.Get("/aa", func(ctx iris.Context) {ct…

tryhackme-Pre Security-HTTP in Detail(HTTP的详细内容)

任务一&#xff1a;What is HTTP(S)?&#xff08;什么是http&#xff08;s&#xff09;&#xff09; 1.What is HTTP? (HyperText Transfer Protocol)&#xff08;什么是 HTTP&#xff1f;&#xff08;超文本传输协议&#xff09;&#xff09; http是你查看网站的时候遵循的…

【C++11】可变模板参数

目录 可变模板的定义方式 参数包的展开方式 递归的方式展开参数包 STL中的emplace相关接口函数 STL容器中emplace相关插入接口函数 ​编辑 模拟实现&#xff1a;emplace接口 C11的新特性可变参数模板能够让您创建可以接受可变参数的函数模板和类模板&#xff0c;相比 C9…

springmvc的拦截器,全局异常处理和文件上传

拦截器: 拦截不符合规则的&#xff0c;放行符合规则的。 等价于过滤器。 拦截器只拦截controller层API接口。 如何定义拦截器。 定义一个类并实现拦截器接口 public class MyInterceptor implements HandlerInterceptor {public boolean preHandle(HttpServletRequest reque…

ECharts热力图-笛卡尔坐标系上的热力图,附视频讲解与代码下载

引言&#xff1a; 热力图&#xff08;Heatmap&#xff09;是一种数据可视化技术&#xff0c;它通过颜色的深浅变化来表示数据在不同区域的分布密集程度。在二维平面上&#xff0c;热力图将数据值映射为颜色&#xff0c;通常颜色越深表示数据值越大&#xff0c;颜色越浅表示数…

EE308FZ_Sixth Assignment_Beta Sprint_Sprint Essay 3

Assignment 6Beta SprintCourseEE308FZ[A] — Software EngineeringClass Link2401_MU_SE_FZURequirementsTeamwork—Beta SprintTeam NameFZUGOObjectiveSprint Essay 3_Day5-Day6 (12.15-12.16)Other Reference1. WeChat Mini Program Design Guide 2. Javascript Style Guid…

JVM 详解

一. JVM 内存区域的划分 1. 程序计数器 程序计数器是JVM中一块比较小的空间, 它保存下一条要执行的指令的地址. [注]: 与CPU的程序计数器不同, 这里的下一条指令不是二进制的机器语言, 而是Java字节码. 2. 栈 保存方法中的局部变量, 方法的形参, 方法之间的调用关系. 栈又…

基于 uniapp 开发 android 播放 webrtc 流

一、播放rtsp协议流 如果 webrtc 流以 rtsp 协议返回&#xff0c;流地址如&#xff1a;rtsp://127.0.0.1:5115/session.mpg&#xff0c;uniapp的 <video> 编译到android上直接就能播放&#xff0c;但通常会有2-3秒的延迟。 二、播放webrtc协议流 如果 webrtc 流以 webrt…

frp内网穿透部署及使用

frp是什么 frp 是一款开源的高性能的反向代理应用&#xff0c;专注于内网穿透&#xff0c;它采用 C/S 模式&#xff0c;将服务端部署在具有公网 IP 的机器上&#xff0c;客户端部署在内网或防火墙内的机器上&#xff0c;通过访问暴露在服务器上的端口&#xff0c;反向代理到处…

基于MATLAB的图像增强

目录 一、背景及意义介绍背景图像采集过程中的局限性 意义 二、概述三、代码结构及说明&#xff08;一&#xff09;整体结构&#xff08;二&#xff09;亮度增强部分&#xff08;三&#xff09;对比度增强部分&#xff08;四&#xff09;锐度增强部分 四、复现步骤&#xff08;…

本地部署webrtc应用怎么把http协议改成https协议?

环境&#xff1a; WSL2 Ubuntu22.04 webrtc视频聊天应用 问题描述&#xff1a; 本地部署webrtc应用怎么把http协议改成https协议&#xff1f; http协议在安卓手机浏览器上用不了麦克风本&#xff0c;来地应用webrtc 本来是http协议&#xff0c;在安卓手机上浏览器不支持使…