一套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
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/82881.shtml
英文地址,請注明出處:http://en.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; 名稱全稱誕生時間誰…