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

news2024/12/23 8:37:05

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,COM2
        int baud,       //波特率:常用取值:CBR_9600、CBR_19200、CBR_38400、CBR_115200、CBR_230400、CBR_460800
        int byteSize,   //数位大小:可取值7、8;
        int parity,     //校验方式:可取值NOPARITY、ODDPARITY、EVENPARITY、MARKPARITY、SPACEPARITY
        int 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,COM2
    int baud,       //波特率:常用取值:CBR_9600、CBR_19200、CBR_38400、CBR_115200、CBR_230400、CBR_460800
    int byteSize,   //数位大小:可取值7、8;
    int parity,     //校验方式:可取值NOPARITY、ODDPARITY、EVENPARITY、MARKPARITY、SPACEPARITY
    int 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 Sharing
        NULL,         // No Security
        OPEN_EXISTING,// Open existing port only
//        FILE_ATTRIBUTE_NORMAL,            // Non Overlapped I/O
        0, //同步方式
        NULL);        // Null for Comm Devices
    if (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.coloradmin.cn/o/2264142.html

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

相关文章

STM32F407ZGT6-UCOSIII笔记12: 事件标志组

有时一个任务需要与多个事件同步&#xff0c;这就要用到事件标志组 本文学习与程序编写基于 正点原子的 STM32F1 UCOS开发手册 文章提供测试代码讲解、完整工程下载、测试效果图 目录 事件标志组&#xff1a; 定义与初始化事件标志组&#xff1a; #include "Public.h&quo…

聊一聊 C#前台线程 如何阻塞程序退出

一&#xff1a;背景 1. 讲故事 这篇文章起源于我的 C#内功修炼训练营里的一位朋友提的问题&#xff1a;后台线程的内部是如何运转的 ? &#xff0c;犹记得C# Via CLR这本书中 Jeffery 就聊到了他曾经给别人解决一个程序无法退出的bug&#xff0c;最后发现是有一个 Backgrond…

JVM性能优化一:初识内存泄露-内存溢出-垃圾回收

本文主要是让你充分的认识到什么叫做内存泄露&#xff0c;什么叫做内存溢出&#xff0c;别再傻傻分不清了&#xff0c;别再动不动的升级服务器的内存了。 文章目录 1.基本概念1.1.内存泄露1.2.内存溢出1.3.垃圾回收1.4.内存泄露-垃圾回收-内存溢出三者的关系关系 2.代码示例2.…

为什么使用环形队列

1.看以下两种情况。第一种不会出现问题&#xff0c;当主流程读取次数比较慢时&#xff0c;数据会被覆盖。 2.扩大空间。不可取。 3.什么是队列

【WRF教程第3.6期】预处理系统 WPS 详解:以4.5版本为例

预处理系统 WPS 详解&#xff1a;以4.5版本为例 Geogrid/Metgrid 插值选项详解1. 插值方法的工作机制2. 插值方法的详细说明2.1 四点双线性插值&#xff08;four_pt&#xff09;2.2 十六点重叠抛物线插值&#xff08;sixteen_pt&#xff09;2.3 简单四点平均插值&#xff08;av…

批量提取zotero的论文构建知识库做问答的大模型(可选)——含转存PDF-分割统计PDF等

文章目录 提取zotero的PDF上传到AI平台保留文件名代码分成20个PDF视频讲解 提取zotero的PDF 右键查看目录 发现目录为 C:\Users\89735\Zotero\storage 写代码: 扫描路径‘C:\Users\89735\Zotero\storage’下面的所有PDF文件,全部复制一份汇总到"C:\Users\89735\Downl…

Java模拟Mqtt客户端连接Mqtt Broker

Java模拟Mqtt客户端基本流程 引入Paho MQTT客户端库 <dependency><groupId>org.eclipse.paho</groupId><artifactId>org.eclipse.paho.mqttv5.client</artifactId><version>1.2.5</version> </dependency>设置mqtt配置数据 …

boost asio 异步服务器

boost网络框架使用方法 boost绑定 首先介绍io_context&#xff0c;可以理解为这是操作系统和应用层数据交互的桥梁。有了它不必关注内核态的缓冲区&#xff0c;只需要关注自己定义在用户态的缓冲区&#xff0c;因为它会通过桥梁运输到用户态的缓冲区。 boost::asio::io_contex…

图解HTTP-HTTP协议

HTTP HTTP是一种不保存状态&#xff0c;即无状态的协议。HTTP协议自身不对请求和响应之间的通信进行保存。为了保存状态因此后面也有一些技术产生比如Cookies技术。 HTTP是通过URI定位网上的资源&#xff0c;理论上将URI可以访问互联网上的任意资源。 如果不是访问特定的资源…

【Go】-限流器的四种实现方法

目录 关于限流和限流器 固定窗口限流器 滑动窗口限流器 漏桶限流器 令牌桶限流器 总结 关于限流和限流器 限流&#xff08;Rate Limiting&#xff09;是一种控制资源使用率的机制&#xff0c;通常用于防止系统过载和滥用。 限流器&#xff08;Rate Limiter&#xff09;是…

CTF_1

CTF_Show 萌新赛 1.签到题 <?php if(isset($_GET[url])){system("curl https://".$_GET[url].".ctf.show"); }else{show_source(__FILE__); }?> 和 AI 一起分析 1.if(isset($_GET[url]))检查GET请求中是否存在名为url的参数。 curl 2.curl…

[文献阅读] Unsupervised Deep Embedding for Clustering Analysis (无监督的深度嵌入式聚类)

文章目录 Abstract:摘要聚类深度聚类 KL散度深度嵌入式聚类(DEC)KL散度聚类软分配&#xff08;soft assignment&#xff09;KL散度损失训练编码器的初始化聚类中心的初始化 实验评估总结 Abstract: This week I read Unsupervised Deep Embedding for Clustering Analysis .It…

记录:virt-manager配置Ubuntu arm虚拟机

virt-manager&#xff08;Virtual Machine Manager&#xff09;是一个图形用户界面应用程序&#xff0c;通过libvirt管理虚拟机&#xff08;即作为libvirt的图形前端&#xff09; 因为要在Linux arm环境做测试&#xff0c;记录下virt-manager配置arm虚拟机的过程 先在VMWare中…

使用C语言编写UDP循环接收并打印消息的程序

使用C语言编写UDP循环接收并打印消息的程序 前提条件程序概述伪代码C语言实现编译和运行C改进之自由设定端口注意事项在本文中,我们将展示如何使用C语言编写一个简单的UDP服务器程序,该程序将循环接收来自指定端口的UDP消息,并将接收到的消息打印到控制台。我们将使用POSIX套…

Spring Boot 教程之三十六:实现身份验证

如何在 Spring Boot 中实现简单的身份验证&#xff1f; 在本文中&#xff0c;我们将学习如何使用 Spring设置和配置基本身份验证。身份验证是任何类型的安全性中的主要步骤之一。Spring 提供依赖项&#xff0c;即Spring Security&#xff0c;可帮助在 API 上建立身份验证。有很…

什么样的LabVIEW控制算自动控制?

自动控制是指系统通过预先设计的算法和逻辑&#xff0c;在无人工干预的情况下对被控对象的状态进行实时监测、决策和调整&#xff0c;达到预期目标的过程。LabVIEW作为一种图形化编程工具&#xff0c;非常适合开发自动控制系统。那么&#xff0c;什么样的LabVIEW控制算作“自动…

GFPS扩展技术原理(七)-音频切换消息流

音频切换消息流 Seeker和Provider通过消息流来同步音频切换能力&#xff0c;触发连接做切换&#xff0c;获取或设置音频切换偏好&#xff0c;通知连接状态等等。为此专门定义了音频切换消息流Message Group 为0x07&#xff0c;Message codes如下&#xff1a; MAC of Audio s…

视频直播点播平台EasyDSS与无人机技术的森林防火融合应用

随着科技的飞速发展&#xff0c;无人机技术以其独特的优势在各个领域得到了广泛应用&#xff0c;特别是在森林防火这一关键领域&#xff0c;EasyDSS视频平台与无人机技术的融合应用更是为传统森林防火手段带来很大的变化。 一、无人机技术在森林防火中的优势 ‌1、快速响应与高…

机器人路径规划和避障算法matlab仿真,分别对比贪婪搜索,最安全距离,RPM以及RRT四种算法

目录 1.程序功能描述 2.测试软件版本以及运行结果展示 3.核心程序 4.本算法原理 4.1贪婪搜索算法原理 4.2最安全距离算法原理 4.3RPM 算法原理 4.4 RRT 算法原理 5.完整程序 1.程序功能描述 机器人路径规划和避障算法matlab仿真,分别对比贪婪搜索,最安全距离,RPM以及R…

【论文笔记】Visual Alignment Pre-training for Sign Language Translation

&#x1f34e;个人主页&#xff1a;小嗷犬的个人主页 &#x1f34a;个人网站&#xff1a;小嗷犬的技术小站 &#x1f96d;个人信条&#xff1a;为天地立心&#xff0c;为生民立命&#xff0c;为往圣继绝学&#xff0c;为万世开太平。 基本信息 标题: Visual Alignment Pre-tra…