【Qt5】多线程串口

news/2024/11/22 5:34:47/

文章目录

  • 原版代码工程
  • 增加QCustomplot实时画图的源码工程
  • 源码

原版代码工程

源码下载链接:
链接:https://pan.baidu.com/s/15pWzadPwOx_OfJGtvL-MjA
提取码:lief
–来自百度网盘超级会员V5的分享

在这里插入图片描述

增加QCustomplot实时画图的源码工程

在这里插入图片描述源码:
链接:https://pan.baidu.com/s/1DJKV7RY3C_1nQ1K2iNP61w
提取码:lief
–来自百度网盘超级会员V5的分享

下位机arduino测试代码:

#include <Arduino.h>unsigned char data_to_send[100] = {0};int acc_data[3] = {0};
int gyro_data[3] = {0};/**********为了匿名四轴上位机的协议定义的变量****************************/
//cup为小端模式存储,也就是在存储的时候,低位被存在0字节,高位在1字节
#define BYTE0(dwTemp)       (*(char *)(&dwTemp))     //取出int型变量的低字节
#define BYTE1(dwTemp)       (*((char *)(&dwTemp) + 1))     //    取存储在此变量下一内存字节的内容,高字节
#define BYTE2(dwTemp)       (*((char *)(&dwTemp) + 2))
#define BYTE3(dwTemp)       (*((char *)(&dwTemp) + 3))/* 
发送给上位机的数据帧定义 
@桢头--功能字--长度--数据(一个或多个,具体看协议说明)-校验
@前2个字节为帧头0xAAAA 
@第3个字节为帧ID,应设置为0xF1~0xFA中的一个 
@第4个字节为报文数据长度(dlc) 
@第5个字节开始到第5+dlc-1个字节为要传输的数据内容段,每个数据场为高字节在前,地字节在后 
@第5+dlc个字节为CheckSum,为第1个字节到第5+dlc-1个字节所有字节的值相加后,保留结果的低八位作为CheckSum 
*/  
void Data_Send_Senser(unsigned int pst)  //按照协议的要求处理数据,pst为包含多个数据的数组,但这里为一个数据
{unsigned char  _cnt=0;unsigned char  i=0;unsigned int j=0,temp;unsigned char sum = 0;//char 类型为两个字节data_to_send[_cnt++]=0xAA;  //桢头data_to_send[_cnt++]=0xAA;data_to_send[_cnt++]=0x02;  //功能字data_to_send[_cnt++]=0;     //需要发送数据的字节数,暂时给0,后面在赋值。data_to_send[_cnt++]=pst&0x00ff;  //数据的低8位data_to_send[_cnt++]=pst>>8;  //数据的高8位data_to_send[3] = _cnt-4;   //长度for(i=0; i<_cnt; i++){sum = sum+ data_to_send[i];  //校验位}data_to_send[_cnt++] =sum; //计算校验位Serial.write(data_to_send,_cnt);  
}/* 
发送给上位机的数据帧定义 
@桢头--功能字--长度--数据(一个或多个,具体看协议说明)-校验
@前2个字节为帧头0xAAAA 
@第3个字节为帧ID,应设置为0xF1~0xFA中的一个 
@第4个字节为报文数据长度(dlc) 
@第5个字节开始到第5+dlc-1个字节为要传输的数据内容段,每个数据场为高字节在前,地字节在后 
@第5+dlc个字节为CheckSum,为第1个字节到第5+dlc-1个字节所有字节的值相加后,保留结果的低八位作为CheckSum 
*/  
void data_to_computer(int* acc_data,int* gyro_data)
{unsigned char  data_to_send[23] = {0};unsigned char i = 0;unsigned char cnt = 0;unsigned char sum = 0;data_to_send[cnt++]=0xAA;     //帧头:AAAAdata_to_send[cnt++]=0xAA;data_to_send[cnt++]=0x02;     //功能字:OXF2data_to_send[cnt++]=0;         //需要发送数据的字节数,暂时给0,后面在赋值。data_to_send[cnt++] = BYTE1(acc_data[0]);//取data[0]数据的高字节,data_to_send[cnt++] = BYTE0(acc_data[0]);data_to_send[cnt++] = BYTE1(acc_data[1]);data_to_send[cnt++] = BYTE0(acc_data[1]);data_to_send[cnt++] = BYTE1(acc_data[2]);data_to_send[cnt++] = BYTE0(acc_data[2]);data_to_send[cnt++] = BYTE1(gyro_data[0]);//取data[0]数据的高字节,data_to_send[cnt++] = BYTE0(gyro_data[0]);data_to_send[cnt++] = BYTE1(gyro_data[1]);data_to_send[cnt++] = BYTE0(gyro_data[1]);data_to_send[cnt++] = BYTE1(gyro_data[2]);data_to_send[cnt++] = BYTE0(gyro_data[2]);data_to_send[cnt++]=0;data_to_send[cnt++]=0;data_to_send[cnt++]=0;data_to_send[cnt++]=0;data_to_send[cnt++]=0;data_to_send[cnt++]=0;data_to_send[3] = cnt-4;//计算总数据的字节数。for(i=0;i<cnt;i++) //对于for语句,当不写大括号的时候,只执行到下面第一个分号结束。{sum+=data_to_send[i];}data_to_send[cnt++] = sum;    //计算校验位Serial.write(data_to_send,cnt);
}void setup() {// put your setup code here, to run once:Serial.begin(115200);randomSeed(analogRead(0));pinMode(8,OUTPUT); 
}void loop() {// unsigned char buff[6];// unsigned char i;// for(i = 0;i<6;i++)// {//   buff[i] = random(65535);// }//   acc_data[0] = (buff[0] << 8) | buff[1];//x轴高8位左移8位或上低8位//   acc_data[1] = (buff[2] << 8) | buff[3];//   acc_data[2] = (buff[4] << 8) | buff[5];//   for(i = 0;i<6;i++)//   {//       buff[i] = random(65535);//   }//   gyro_data[0] = (buff[0] << 8) | buff[1];//x轴高8位左移8位或上低8位//   gyro_data[1] = (buff[2] << 8) | buff[3];//   gyro_data[2] = (buff[4] << 8) | buff[5];// data_to_computer(acc_data, gyro_data);digitalWrite(8,HIGH);//   unsigned int num = random(100);
//   Data_Send_Senser(num);for(int i = 0; i < 255; i++){Data_Send_Senser(i);delay(2); // ms      }digitalWrite(8,LOW);//delayMicroseconds(1500); // us
}

源码

mainwindows.cpp

#include "mainwindow.h"
#include "ui_mainwindow.h"MainWindow::MainWindow(QWidget *parent): QMainWindow(parent), ui(new Ui::MainWindow)
{ui->setupUi(this);qDebug() <<"main" <<QThread::currentThread();qRegisterMetaType<serialportplus::Settings>("serialportplus::Settings");initUI();initSerialPort();
}MainWindow::~MainWindow()
{delete ui;
}/** 函数功能:完成对串口助手界面的初始化*/
void MainWindow::initUI()
{//增加端口号选择项for (int i = 1; i <= 15; i++){ui->cboPortName->addItem(QString("COM%1").arg(i));}//设置端口号波特率ui->cboBaudrate->addItem(QString("1200"),QSerialPort::Baud1200);ui->cboBaudrate->addItem(QString("2400"),QSerialPort::Baud2400);ui->cboBaudrate->addItem(QString("4800"),QSerialPort::Baud4800);ui->cboBaudrate->addItem(QString("9600"),QSerialPort::Baud9600);ui->cboBaudrate->addItem(QString("19200"),QSerialPort::Baud19200);ui->cboBaudrate->addItem(QString("38400"),QSerialPort::Baud38400);ui->cboBaudrate->addItem(QString("57600"),QSerialPort::Baud57600);ui->cboBaudrate->addItem(QString("115200"),QSerialPort::Baud115200);//设置端口数据位数ui->cboDataBit->addItem("8",QSerialPort::Data8);ui->cboDataBit->addItem("7",QSerialPort::Data7);ui->cboDataBit->addItem("6",QSerialPort::Data6);ui->cboDataBit->addItem("5",QSerialPort::Data5);//设置端口校验方式ui->cboParity->addItem("Odd",QSerialPort::OddParity);ui->cboParity->addItem("Even",QSerialPort::EvenParity);ui->cboParity->addItem("None",QSerialPort::NoParity);//设置端口停止位ui->cboStopBit->addItem("1",QSerialPort::OneStop);ui->cboStopBit->addItem("1.5",QSerialPort::OneAndHalfStop);ui->cboStopBit->addItem("2",QSerialPort::TwoStop);//设置端口流控制ui->cboFlowContral->addItem("None",QSerialPort::NoFlowControl);ui->cboFlowContral->addItem("RTS/CTS",QSerialPort::HardwareControl);ui->cboFlowContral->addItem("XON/XOFF",QSerialPort::SoftwareControl);//设置定时重发时间,单位msui->lineEdit->setText("1000");//定时时间到,则执行timeup函数connect(&m_timer, &QTimer::timeout, this,&MainWindow::timeUp);
}/** 函数功能:完成对串口的初始化,设置串口子线程*/
void MainWindow::initSerialPort()
{//将串口对象的相关操作,放入子线程m_serial.moveToThread(&m_thread);//子线程启动m_thread.start();/*分别是界面对串口发出的启动、停止和发送数据的信号*/connect(this, &MainWindow::sigStart,&m_serial, &serialportplus::startPort);connect(this, &MainWindow::sigStop,&m_serial, &serialportplus::stopPort);connect(this, &MainWindow::sigSend,&m_serial, &serialportplus::sendData);/*分别是串口对界面发出的启动、停止和接收数据的信号*/connect(&m_serial, &serialportplus::sigStarted,this, &MainWindow::started);connect(&m_serial, &serialportplus::sigStop,this, &MainWindow::stoped);connect(&m_serial, &serialportplus::sigReceived,this, &MainWindow::receiveData);
}/** 函数功能:当界面关闭时,子线程也要进行关闭*/
void MainWindow::closeEvent(QCloseEvent *event)
{Q_UNUSED(event);//打印界面主线程地址qDebug() <<"main" <<QThread::currentThread();//向发送串口发送串口停止的信号emit sigStop();//串口子线程关闭m_thread.quit();//等待串口子线程完全关闭m_thread.wait();}/** 函数功能:根据串口打开按钮判断是否打开串口,*         当串口打开时,对串口参数进行设置,并向串口子线程发出串口打开信号,否则发出关闭信号*/
void MainWindow::on_btnOpenPort_clicked()
{//获取串口打开按钮的文本QString text= ui->btnOpenPort->text();//当按钮关闭时,打开串口,并设置相关参数,向串口子线程发送串口打开信号if(text ==  QString::fromLocal8Bit("打开串口")){serialportplus::Settings s;s.name= ui->cboPortName->currentText();s.baudRate= (QSerialPort::BaudRate)ui->cboBaudrate->currentData().toInt();s.dataBits = (QSerialPort::DataBits)ui->cboDataBit->currentData().toInt();s.stopBits= (QSerialPort::StopBits)ui->cboStopBit->currentData().toInt();s.parity= (QSerialPort::Parity)ui->cboParity->currentData().toInt();s.flowControl = (QSerialPort::FlowControl)ui->cboFlowContral->currentData().toInt();emit sigStart(s);}//当按钮打开时,关闭串口,向串口子线程发送串口关闭信号else{emit sigStop();}}/** 函数功能:点击发送按钮时,执行串口发送*/
void MainWindow::on_btnSend_clicked()
{//获取所要发送的文本QString strSend = ui->SendplainTextEdit->toPlainText();//将所要发送的文本存入arr变量QByteArray arr = strSend.toUtf8();//将arr变量通过信号发给串口子线程emit sigSend(arr);
}/** 函数功能:接收串口子线程发出启动的信号,对串口的按钮和参数设置的界面进行管理*/
void MainWindow::started()
{//串口打开按钮文本切换,切换为“关闭串口”ui->btnOpenPort->setText(QString::fromLocal8Bit("关闭串口"));//串口参数处于不可更改的状态ui->groupBox->setEnabled(false);
}/** 函数功能:接收串口子线程发出停止的信号,对串口的按钮和参数设置的界面进行管理*/
void MainWindow::stoped(int status)
{Q_UNUSED(status);//串口打开按钮文本切换,切换为“打开串口”ui->btnOpenPort->setText(QString::fromLocal8Bit("打开串口"));//串口参数处于可更改的状态ui->groupBox->setEnabled(true);
}/** 函数功能:接收串口子线程发出数据接收的信号,界面接收串口子线程发送的数据,并将其显示到界面上*/
void MainWindow::receiveData(QByteArray data)
{//将接收的数据进行转换QString strText = QString(data);//获取当前接收数据的时间QDateTime current_date_time = QDateTime::currentDateTime();//显示当前接收数据的时间QString  t  = current_date_time.toString("yyyy-MM-dd hh:mm:ss.zzz : ");//显示当前接收的数据ui->RecveeiveplainTextEdit->appendPlainText( t + strText + "\n" );
}/** 函数功能:定时发送状态改变响应函数*/
void MainWindow::on_checkBox_stateChanged(int arg1)
{//当定时发送被勾选时if(arg1){//定时器启动m_timer.start(ui->lineEdit->text().toInt());}//当定时发送未被勾选时else{//定时器停止m_timer.stop();}
}/** 函数功能:定时时间到时,执行串口发送函数*/
void MainWindow::timeUp()
{qDebug() << "timeup";//串口发送函数on_btnSend_clicked();
}

mainwindows.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H#include <QMainWindow>#include<QSerialPort>
#include<QDateTime>
#include<QTimer>
#include<QDebug>
#include<QThread>#include"serialportplus.h"QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACEclass MainWindow : public QMainWindow
{Q_OBJECTpublic:MainWindow(QWidget *parent = nullptr);~MainWindow();//界面初始化函数void initUI();//端口初始化函数void initSerialPort();//界面关闭事件void closeEvent(QCloseEvent *event);private:Ui::MainWindow *ui;//创建一个自定义的串口类对象serialportplus m_serial;//创建一个线程对象QThread m_thread;//创建一个定时器对象QTimer m_timer;signals:// 分别是界面对串口发出的启动、停止和发送数据的信号void sigStart(serialportplus::Settings s);void sigStop();void sigSend(QByteArray data);public slots:// 分别是串口对界面发出的启动、停止和接收数据的信号时,所要执行的操作void started();void stoped(int status);void receiveData(QByteArray data);private slots:void timeUp();void on_btnOpenPort_clicked();void on_btnSend_clicked();void on_checkBox_stateChanged(int arg1);
};
#endif // MAINWINDOW_H

serialportplus.cpp

#include "serialportplus.h"/** 函数功能:对串口接收到数据的信号进行响应,并向界面主线程发送接收到的数据*/
serialportplus::serialportplus()
{//当串口子线程接收到数据时,向界面主线程发出数据接收的信号connect(this, &QSerialPort::readyRead, [this](){qDebug() << QString::fromLocal8Bit ("接收") << QThread::currentThread();//读取串口接收的数据QByteArray arr = readAll();//将数据通过信号发给界面主线程emit sigReceived(arr);});
}/** 函数功能:接收界面主线程发出开始信号,接收串口参数并对设置,*         同时根据串口状态发出启动或停止信号。*/
void serialportplus::startPort(serialportplus::Settings sets)
{/*设置串口参数*/setPortName(sets.name);setParity(sets.parity);setBaudRate(sets.baudRate);setDataBits(sets.dataBits);setStopBits(sets.stopBits);setFlowControl(sets.flowControl);qDebug() << QString::fromLocal8Bit ("启动") <<QThread::currentThread();//判断串口是否处于可读写状态if(open(QIODevice::ReadWrite)){//串口处于可读写状态,发出向界面主线程启动信号emit sigStarted();}else{//串口不处于可读写状态,发出向界面主线程停止信号emit sigStop(1);}
}/** 函数功能:接收界面主线程发送的停止信号,串口关闭*/
void serialportplus::stopPort()
{qDebug() << QString::fromLocal8Bit ("停止") <<QThread::currentThread();//判断串口是否关闭if(isOpen()){//关闭串口close();}//向界面主线程发出停止信号emit sigStop(0);
}/** 函数功能:接收界面主线程发出的数据发送信号,串口进行数据发送*/
void serialportplus::sendData(QByteArray arr)
{qDebug() << QString::fromLocal8Bit ("发送") <<QThread::currentThread();//判断串口是否关闭if(isOpen()){//发送数据write(arr);}
}

serialportplus.h

#ifndef SERIALPORTPLUS_H
#define SERIALPORTPLUS_H#include <QSerialPort>
#include<QThread>
#include<QDebug>class serialportplus : public QSerialPort
{Q_OBJECT
public://串口接收数据的函数serialportplus();//串口设置struct Settings{QString name;//端口名称COM1、COM2BaudRate baudRate;//波特率DataBits dataBits;//数据位Parity parity;//奇偶校验StopBits stopBits;//停止位FlowControl flowControl;//流控制};public slots:// 分别是界面对串口发出的启动、停止和发送数据的信号时,所要执行的操作void startPort(Settings sets);void stopPort();void sendData(QByteArray arr);signals:// 分别是串口对界面发出的启动、停止和接收数据的信号void sigStarted();void sigStop(int status);void sigReceived(QByteArray data);
};#endif // SERIALPORTPLUS_H

http://www.ppmy.cn/news/59498.html

相关文章

Vue第一章:基本概念

一.关于Vue的说法正确的是 v-for指令基于一个数组来渲染一个列表v-for指令需要使用item in items形式的特殊语法&#xff0c;其中items是源数据数组&#xff0c;而item则是被迭代的数组元素的别名v-for在使用的时候最好添加key&#xff0c;且key最好不是索引可以用v-for来遍历…

USB协议分析仪

1 ULPI PHY passive sniffing mode 概念: non driving, no pull-up, no pull-down Function Control.opMode 1; // non-Driving OTG Control.DpPulldown 0; // no pull-down OTG Control.DmPulldown 0; // no pull-down USB IO.ChargerPullupEnDP 0; // no pull-up USB IO.…

JavaEE 第二周

计算机Z20-第2周作业 总分&#xff1a;100分 得分&#xff1a;100.0分 1 . 单选题 中等 15分 下面关于JSP注释的说法中&#xff0c;正确的是&#xff08;&#xff09; A.JSP注释语法格式&#xff1a;<!-- 注释信息 --> B.JSP注释不会发送到…

Java 的简要介绍及开发环境的搭建(超级详细)

图片来源于互联网 目录 | CONTENT Java 简介 一、什么是 Java 二、认识 Java 版本 三、选择哪个版本比较好 搭建 Java 开发环境 一、下载 Java 软件开发工具包 JDK 二、配置环境变量 自动配置 手动配置 三、下载合适的 IDE IntelliJ IDEA Visual Studio Code Eclip…

第十六章 预制件prefab(上)

本章节我们介绍一下“预制件”&#xff0c;也有人叫“预制体”&#xff0c;也就是Prefab。在游戏世界中&#xff0c;那些自然环境的游戏对象&#xff0c;我们可以提前创建在场景中&#xff0c;这个大家能够理解。但是&#xff0c;有些游戏对象&#xff0c;需要根据游戏逻辑来通…

matlab循环语句详解

MATLAB中的循环语句是一种方便的编程结构&#xff0c;可以帮助处理迭代过程中的数据和控制流。MATLAB提供了三种基本类型的循环语句&#xff1a;for循环、while循环和do-while循环。这篇文章将介绍这些循环语句&#xff0c;并提供一些易于实践的示例。 1. for循环 for循环是一…

MySQL性能监控全掌握,快来get关键指标及采集方法!

数据库中间件监控实战&#xff0c;MySQL中哪些指标比较关键以及如何采集这些指标了。帮助提早发现问题&#xff0c;提升数据库可用性。 1 整体思路 监控哪类指标&#xff1f; 如何采集数据&#xff1f; 第10讲监控方法论如何落地&#xff1f; 这些就可以在MySQL中应用起来。…

vue3 - 超详细头像裁剪并上传到服务器,支持按照自定义比例裁切图片效果组件插件(详细示例源码教程,一键复制运行开箱即用)

效果图 大部分都贼难用,而且全是bug。。并且很少有 vue3的,全是 vue2。。 本博客实现了在 vue3.js 项目中,实现图像上传后并按一定的比例进行裁剪的示例功能源码,支持各种参数、样式修改, 示例有 Element Plus + Vue3.js 版本,也有纯 Vue3.js 版本(无 UI 框架搭配),按…