一套qt c++的串口通信

实现了创建线程使用串口的功能

具备功能:
1.线程使用串口
2.定时发送队列内容,防止粘包
3.没处理接收粘包,根据你的需求来,handleReadyRead函数中,可以通过m_receiveBuffer来缓存接收,然后拆分数据来处理

源码

serialportmanager.h

#ifndef SERIALPORTMANAGER_H
#define SERIALPORTMANAGER_H#include <QObject>
#include <QtSerialPort/QSerialPort>
#include <QtSerialPort/QSerialPortInfo>
#include <QObject>
#include <QSerialPort>
#include <QSerialPortInfo>
#include <QThread> // 添加 QThread 头文件
#include <QMetaObject> // 添加 QMetaObject 头文件
#include <QQueue> // 添加 QQueue 头文件
#include <QTimer> // 添加 QTimer 头文件class SerialPortManager : public QObject
{Q_OBJECT
public:explicit SerialPortManager(QObject *parent = nullptr);~SerialPortManager();// 打开串口 (调用将在工作线程执行)Q_INVOKABLE bool openPort(const QString &portName, int baudRate, QSerialPort::DataBits dataBits = QSerialPort::Data8,QSerialPort::Parity parity = QSerialPort::NoParity, QSerialPort::StopBits stopBits = QSerialPort::OneStop);// 关闭串口 (调用将在工作线程执行)Q_INVOKABLE void closePort();// 写入数据 (调用将在工作线程执行)Q_INVOKABLE qint64 writeData(const QByteArray &data);Q_INVOKABLE qint64 writeString(const QString &str);//带参数的Q_INVOKABLE qint64 writeCommnd(const QString &baseCommad,QStringList strParas);// 检查串口是否打开 (调用将在工作线程执行)Q_INVOKABLE bool isOpen();// 获取可用串口列表 (静态方法,主线程安全)static QStringList availablePorts();signals:void dataReceived(const QByteArray &data);void portError(const QString &errorDescription);void portOpened();void portClosed();// 内部信号,用于触发工作线程中的初始化void initSerialPortSignal();public slots:void _closePortSlot();
private slots:// 这些槽函数将在工作线程中执行void _openPortSlot(const QString &portName, int baudRate, QSerialPort::DataBits dataBits,QSerialPort::Parity parity, QSerialPort::StopBits stopBits, bool *result);void _writeDataSlot(const QByteArray &data, qint64 *result);void _isOpenSlot(bool *result) const;void handleReadyRead();void handleError(QSerialPort::SerialPortError error);void _initSerialPort(); // 在工作线程中初始化串口对象void _cleanupSlot();    // 在工作线程退出前执行清理void sendDataFromQueue(); // 定时器超时槽函数,发送队列中的数据private:QSerialPort *m_serialPort; // 将在工作线程中创建和使用QThread *m_workerThread;   // 串口操作的工作线程QQueue<QByteArray> m_dataQueue; // 数据发送队列QTimer *m_sendTimer; // 发送定时器QByteArray m_receiveBuffer; // 接收数据缓冲区
};#endif // SERIALPORTMANAGER_H

serialportmanager.cpp

#include "serialportmanager.h"
#include <QDebug> // 用于调试SerialPortManager::SerialPortManager(QObject *parent) : QObject(nullptr), // 关键: 初始化时设置父对象为nullptr,以允许moveToThreadm_serialPort(nullptr), // 初始化为 nullptr,将在工作线程中创建m_workerThread(new QThread())  // m_workerThread 创建时不指定父对象,其生命周期将单独管理
{Q_UNUSED(parent); // 如果传递了parent,明确表示它在这里没有被使用来设置父子关系// 将 SerialPortManager 对象本身移动到工作线程this->moveToThread(m_workerThread);// 连接信号以在工作线程启动后初始化串口connect(m_workerThread, &QThread::started, this, &SerialPortManager::_initSerialPort);// 连接信号以在工作线程结束时自动删除 QThread 对象connect(m_workerThread, &QThread::finished, m_workerThread, &QObject::deleteLater);// 启动工作线程m_workerThread->start();
}SerialPortManager::~SerialPortManager()
{if (m_workerThread->isRunning()) {// 连接 cleanupSlot 以在线程完成前执行清理// 使用 Qt::DirectConnection 确保 _cleanupSlot 在 m_workerThread 中执行connect(m_workerThread, &QThread::finished, this, &SerialPortManager::_cleanupSlot, Qt::DirectConnection);// 请求退出线程m_workerThread->quit();// 等待线程结束,m_workerThread 会通过之前连接的 deleteLater 自行删除if (!m_workerThread->wait(5000)) { // 增加等待时间以确保清理完成qWarning() << u8"SerialPortManager: 工作线程未及时完成,尝试终止。";m_workerThread->terminate(); // 强制终止作为最后手段m_workerThread->wait();      // 等待终止完成}qDebug() << u8"SerialPortManager: 工作线程已完成。";} else {// 如果线程没有运行,但 m_workerThread 对象仍然存在,也应该安排删除// (虽然理论上 connect m_workerThread finished to deleteLater 应该处理了,但作为安全措施)if (m_workerThread) {m_workerThread->deleteLater();}}qDebug() << u8"SerialPortManager 已销毁。";
}void SerialPortManager::_cleanupSlot()
{if (m_serialPort && m_serialPort->isOpen()) {m_serialPort->close();qDebug() << u8"串口在 _cleanupSlot 中关闭。";}
}void SerialPortManager::_initSerialPort()
{// 这个函数在 m_workerThread 中执行m_serialPort = new QSerialPort(this); // parent 为 this,确保随 SerialPortManager 销毁connect(m_serialPort, &QSerialPort::readyRead, this, &SerialPortManager::handleReadyRead);connect(m_serialPort, &QSerialPort::errorOccurred, this, &SerialPortManager::handleError);m_sendTimer = new QTimer(this); // parent 为 this,确保随 SerialPortManager 销毁connect(m_sendTimer, &QTimer::timeout, this, &SerialPortManager::sendDataFromQueue);m_sendTimer->start(100); // 0.1秒 (100毫秒) 执行一次qDebug() << u8"SerialPortManager 在线程中初始化:" << QThread::currentThreadId();
}bool SerialPortManager::openPort(const QString &portName, int baudRate, QSerialPort::DataBits dataBits,QSerialPort::Parity parity, QSerialPort::StopBits stopBits)
{if (QThread::currentThread() == m_workerThread) {// 如果已经在工作线程,直接调用bool result = false;_openPortSlot(portName, baudRate, dataBits, parity, stopBits, &result);return result;}bool result = false;// 使用 BlockingQueuedConnection 确保调用完成并获取结果QMetaObject::invokeMethod(this, "_openPortSlot", Qt::BlockingQueuedConnection,Q_ARG(QString, portName),Q_ARG(int, baudRate),Q_ARG(QSerialPort::DataBits, dataBits),Q_ARG(QSerialPort::Parity, parity),Q_ARG(QSerialPort::StopBits, stopBits),Q_ARG(bool*, &result));return result;
}void SerialPortManager::_openPortSlot(const QString &portName, int baudRate, QSerialPort::DataBits dataBits,QSerialPort::Parity parity, QSerialPort::StopBits stopBits, bool *result)
{if (!m_serialPort) {*result = false;emit portError(u8"串口对象未初始化。");return;}if (m_serialPort->isOpen()) {m_serialPort->close();}m_serialPort->setPortName(portName);m_serialPort->setBaudRate(baudRate);m_serialPort->setDataBits(dataBits);m_serialPort->setParity(parity);m_serialPort->setStopBits(stopBits);m_serialPort->setFlowControl(QSerialPort::NoFlowControl);if (m_serialPort->open(QIODevice::ReadWrite)) {emit portOpened();*result = true;} else {emit portError(m_serialPort->errorString());*result = false;}
}void SerialPortManager::sendDataFromQueue()
{if (m_serialPort && m_serialPort->isOpen() && !m_dataQueue.isEmpty()) {QByteArray dataToSend = m_dataQueue.dequeue();qDebug() << u8"从队列发送数据:" << dataToSend;m_serialPort->write(dataToSend);}
}void SerialPortManager::closePort()
{if (QThread::currentThread() == m_workerThread) {_closePortSlot();return;}QMetaObject::invokeMethod(this, "_closePortSlot", Qt::QueuedConnection);}void SerialPortManager::_closePortSlot()
{m_dataQueue.clear(); // 清空队列中的剩余数据if (m_serialPort && m_serialPort->isOpen()) {m_serialPort->close();emit portClosed();}
}qint64 SerialPortManager::writeData(const QByteArray &data)
{if (QThread::currentThread() == m_workerThread) {qint64 result = -1;_writeDataSlot(data, &result);return result;}qint64 result = -1;QMetaObject::invokeMethod(this, "_writeDataSlot", Qt::BlockingQueuedConnection,Q_ARG(QByteArray, data),Q_ARG(qint64*, &result));return result;
}qint64 SerialPortManager::writeString(const QString &str)
{return writeData(str.toUtf8());
}qint64 SerialPortManager::writeCommnd(const QString &baseCommad, QStringList strParas)
{QString strSendData = "<";strSendData+=baseCommad;for(QString para : strParas){strSendData+=" ";strSendData +=para;}strSendData +=">";return writeString(strSendData);
}void SerialPortManager::_writeDataSlot(const QByteArray &data, qint64 *result)
{// 将数据添加到队列,而不是直接发送m_dataQueue.enqueue(data);qDebug() << u8"数据已添加到发送队列,当前队列大小:" << m_dataQueue.size();*result = data.size(); // 返回添加到队列的数据大小
}bool SerialPortManager::isOpen()
{if (QThread::currentThread() == m_workerThread) {bool localResult = false;_isOpenSlot(&localResult);return localResult;}bool result = false;// 使用 const_cast 是因为 invokeMethod 的槽函数参数不能是 const 的,但逻辑上是 constQMetaObject::invokeMethod(const_cast<SerialPortManager*>(this), "_isOpenSlot", Qt::BlockingQueuedConnection,Q_ARG(bool*, &result));return result;
}void SerialPortManager::_isOpenSlot(bool *result) const
{if (m_serialPort) {*result = m_serialPort->isOpen();} else {*result = false;}
}QStringList SerialPortManager::availablePorts()
{QStringList portNames;const auto infos = QSerialPortInfo::availablePorts();for (const QSerialPortInfo &info : infos) {portNames.append(info.portName());}return portNames;
}void SerialPortManager::handleReadyRead()
{if (!m_serialPort || !m_serialPort->isOpen() || !m_serialPort->bytesAvailable())return;QByteArray data = m_serialPort->readAll();if (data.isEmpty()) {return;}emit dataReceived(data);}
}void SerialPortManager::handleError(QSerialPort::SerialPortError error)
{if (!m_serialPort) return;// NoError 也是一个 error 枚举值,但通常我们只关心实际的错误if (error != QSerialPort::NoError) {emit portError(m_serialPort->errorString());}
}

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

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

相关文章

设计模式-发布订阅

文章目录 发布订阅概念发布订阅 vs 监听者例子代码 发布订阅概念 发布/订阅者模式最大的特点就是实现了松耦合&#xff0c;也就是说你可以让发布者发布消息、订阅者接受消息&#xff0c;而不是寻找一种方式把两个分离 的系统连接在一起。当然这种松耦合也是发布/订阅者模式最大…

windows-cmd 如何查询cpu、内存、磁盘的使用情况

在 Windows 中&#xff0c;您可以使用命令提示符&#xff08;CMD&#xff09;通过一些命令来查询 CPU、内存和磁盘的使用情况。以下是常用的命令和方法&#xff1a; 1. 查询 CPU 使用情况 使用 wmic 命令 wmic cpu get loadpercentage 这个命令会显示当前 CPU 的使用百分比…

allWebPlugin中间件VLC专用版之截图功能介绍

背景 VLC控件原有接口具有视频截图方法&#xff0c;即video对象的takeSnapshot方法&#xff0c;但是该方法返回的是一个IPicture对象&#xff0c;不适合在谷歌等现代浏览器上使用。因此&#xff0c;本人增加一个新的视频截图方法takeSnapshot2B64方法&#xff0c;直接将视频截图…

第Y5周:yolo.py文件解读

&#x1f368; 本文为&#x1f517;365天深度学习训练营 中的学习记录博客&#x1f356; 原作者&#xff1a;K同学啊 本次任务&#xff1a;将YOLOv5s网络模型中的C3模块按照下图方式修改形成C2模块&#xff0c;并将C2模块插入第2层与第3层之间&#xff0c;且跑通YOLOv5s。 任务…

宝塔安装ssh证书报错:/usr/bin/curl: symbol lookup error: curl_easy_header

原因&#xff1a; 你当前的 curl 命令版本是 7.70.0&#xff08;不是系统默认版本&#xff0c;应该是你手动安装的&#xff09;。它链接的是 /usr/local/lib/libcurl.so.4&#xff0c;而不是 CentOS 系统默认的 /usr/lib64/libcurl.so.4。/usr/local/lib/libcurl.so.4 很可能是…

Apache SeaTunnel 引擎深度解析:原理、技术与高效实践

Apache SeaTunnel 作为新一代高性能分布式数据集成平台&#xff0c;其核心引擎设计融合了现代大数据处理架构的精髓。 Apache SeaTunnel引擎通过分布式架构革新、精细化资源控制及企业级可靠性设计&#xff0c;显著提升了数据集成管道的执行效率与运维体验。其模块化设计允许用…

测试用例及黑盒测试方法

一、测试用例 1.1 基本要素 测试用例&#xff08;Test Case&#xff09;是为了实施测试而向被测试的系统提供的一组集合&#xff0c;这组集合包含&#xff1a;测试环境、操作步骤、测试数据、预期结果等4个主要要素。 1.1.1 测试环境 定义&#xff1a;测试执行所需的软硬件…

硬件工程师笔记——运算放大电路Multisim电路仿真实验汇总

目录 1 运算放大电路基础 1.1 概述 1.1.1 基本结构 1.1.2 理想特性 1.2 运算放大分析方法 1.2.1 虚短 1.2.2虚断 1.2.3 叠加定理 2 同向比例运算放大电路 2.1 概述 2.1.1 基本电路结构 2.1.2 电路原理 2.2 仿真分析 2.2.1 电压增益 2.2.2 相位分析 3 反向比例运…

板凳-------Mysql cookbook学习 (九)

第4章&#xff1a;表管理 4.0 引言 MySQL &#xff1a;&#xff1a; 员工样例数据库 &#xff1a;&#xff1a; 3 安装 https://dev.mysql.com/doc/employee/en/employees-installation.html Employees 数据库与几种不同的 存储引擎&#xff0c;默认情况下启用 InnoDB 引擎。编…

MySQL省市区数据表

数据结构简单展示一下 具体的可以点击文章最后的链接地址下载 连接地址中有两个文件一个是详细的另一个是简洁的 SET NAMES utf8mb4; SET FOREIGN_KEY_CHECKS 0;-- ---------------------------- -- Table structure for ln_new_region -- ---------------------------- DROP…

无人机报警器探测模块技术解析!

一、运行方式 1. 频谱监测与信号识别 全频段扫描&#xff1a;模块实时扫描900MHz、1.5GHz、2.4GHz、5.8GHz等无人机常用频段&#xff0c;覆盖遥控、图传及GPS导航信号。 多路分集技术&#xff1a;采用多传感器阵列&#xff0c;通过信号加权合并提升信噪比&#xff0c;…

Oracle 导入导出 dmp 数据文件实战

一、DMP文件基础知识​​ 1. ​​DMP文件定义​​ DMP&#xff08;Data Pump Dump File&#xff09;是Oracle数据库专用的二进制格式文件&#xff0c;由expdp/impdp或旧版exp/imp工具生成。它包含数据库对象的元数据&#xff08;表结构、索引等&#xff09;和实际数据&#x…

Coursier:安装sbt

命令 ./cs launch sbt -- --version 的含义是​​通过 Coursier&#xff08;cs&#xff09;工具启动 SBT&#xff08;Scala 构建工具&#xff09;&#xff0c;并查询其版本信息​​。具体解析如下&#xff1a; ​​1. 命令结构解析​​ ​​./cs​​&#xff1a; 这是 Coursie…

【深度学习】12. VIT与GPT 模型与语言生成:从 GPT-1 到 GPT4

VIT与GPT 模型与语言生成&#xff1a;从 GPT-1 到 GPT4 本教程将介绍 GPT 系列模型的发展历程、结构原理、训练方式以及人类反馈强化学习&#xff08;RLHF&#xff09;对生成对齐的改进。内容涵盖 GPT-1、GPT-2、GPT-3、GPT-3.5&#xff08;InstructGPT&#xff09;、ChatGPT …

项目更改权限后都被git标记为改变,怎么去除

❗问题描述&#xff1a; 当你修改了项目中的文件权限&#xff08;如使用 chmod 改了可执行权限&#xff09;&#xff0c;Git 会把这些文件标记为“已更改”&#xff0c;即使内容并没有发生任何改变。 ✅ 解决方法&#xff1a; ✅ 方法一&#xff1a;告诉 Git 忽略权限变化&am…

openfeignFeign 客户端禁用 SSL

要针对特定的 Feign 客户端禁用 SSL 验证&#xff0c;可以通过自定义配置类实现。以下是完整解决方案&#xff1a; 1. 创建自定义配置类&#xff08;禁用 SSL 验证&#xff09; import feign.Client; import feign.httpclient.ApacheHttpClient; import org.apache.http.conn…

移动端 UI自动化测试学习之Appium框架(包含adb调试工具介绍)

文章目录 前言adb调试工具adb组成常用命令获取程序的包名和界面名文件传输发送文件到手机从手机中拉取文件 获取app启动时间获取手机日志其他命令 Appium 简介工作原理图 环境搭建安装客户端库&#xff08;appium lib&#xff09;安装Appium Server安装JDK&#xff08;自行下载…

【论文解读】DETR: 用Transformer实现真正的End2End目标检测

1st authors: About me - Nicolas Carion‪Francisco Massa‬ - ‪Google Scholar‬ paper: [2005.12872] End-to-End Object Detection with Transformers ECCV 2020 code: facebookresearch/detr: End-to-End Object Detection with Transformers 1. 背景 目标检测&#…

性能测试-jmeter实战1

课程&#xff1a;B站大学 记录软件测试-性能测试学习历程、掌握前端性能测试、后端性能测试、服务端性能测试的你才是一个专业的软件测试工程师 性能测试-jmeter实战1 为什么需要性能测试呢&#xff1f;性能测试的作用&#xff1f;性能测试体系性能测试基础性能测试工具性能监控…

HTML、XML、JSON 是什么?有什么区别?又是做什么的?

在学习前端开发或者理解互联网工作原理的过程中&#xff0c;我们经常会遇到三个非常重要的概念&#xff1a;HTML、XML 和 JSON。它们看起来有点像&#xff0c;但其实干的事情完全不同。 &#x1f3c1; 一、他们是谁&#xff1f;什么时候诞生的&#xff1f; 名称全称诞生时间谁…