QT在PC开发中多串口通信有哪些方法

方法

方案一: 在单独的线程中创建并管理QSerialPort对象,但不要让该对象成为任何其他对象的子对象。在该线程中启动一个事件循环,并使用信号和槽来跨线程通信。如果您的应用程序涉及到复杂的串口通信,并且需要处理大量的数据或保持UI的响应性,您可能需要考虑将串口通信放在单独的线程中

方案二: 使用Qt的并发框架(如QtConcurrent)来处理与串口通信相关的数据处理任务,但让QSerialPort对象保持在主线程或专用的IO线程中。

方案三(不推荐,但可行): 为每个串口创建一个独立的线程,在每个线程中处理相应串口的通信任务。这种方法可以避免多个串口同时操作时可能出现的阻塞问题,确保每个串口的通信能够及时响应。示例代码可参考:利用 Qt 多线程机制实现双路串口数据流的接收和发送(附工程源代码) ,如果你确实需要将QSerialPort对象移动到另一个线程,请确保你完全理解Qt的线程模型,并且你能够正确地管理对象的生命周期和线程间的同步。这通常是不必要的,也是复杂的,并且容易出错。

方案四:(类似方案一,比较简单,推荐) 在主线程中持有多个QSerialPort的连接对象,QT 中的 QSerialPort 类提供了对串口操作的支持。可以创建多个 QSerialPort 对象来分别处理不同的串口。在使用时,需设置串口的参数(如波特率、数据位、停止位等),然后通过信号与槽机制来实现数据的接收和发送。例如,连接 readyRead 信号来接收数据,调用 write 函数发送数据。在Qt中,信号和槽的连接是自动的,只要对象没有被销毁,并且信号在对象生命周期内被正确发出,槽函数就会被调用。但是,如果槽函数是跨线程的,那么您需要确保信号和槽的参数是线程安全的,或者使用Qt的元对象系统(meta-object system)来进行线程间的通信。方案四的串口对象在同一个线程中,因此不需要担心这个问题。

实现

方案一:

首先,你需要一个类来封装QSerialPort,这个类将负责串口的配置(如波特率、数据位、停止位等)、打开串口、关闭串口、发送数据和接收数据。

#include <QSerialPort>  
#include <QObject>  
  
class SerialPortManager : public QObject {  
    Q_OBJECT  
  
public:  
    SerialPortManager(QObject *parent = nullptr) : QObject(parent), serial(new QSerialPort(this)) {  
        connect(serial, &QSerialPort::readyRead, this, &SerialPortManager::readData);  
    }  
  
    void openSerialPort(const QString &portName, int baudRate) {  
        serial->setPortName(portName);  
        serial->setBaudRate(baudRate);  
        // 设置其他参数,如数据位、停止位等  
        if (serial->open(QIODevice::ReadWrite)) {  
            qDebug() << "Serial port opened successfully";  
        } else {  
            qDebug() << "Failed to open serial port";  
        }  
    }  
  
    void closeSerialPort() {  
        if (serial->isOpen()) {  
            serial->close();  
            qDebug() << "Serial port closed";  
        }  
    }  
  
    void writeData(const QByteArray &data) {  
        if (serial->isOpen()) {  
            serial->write(data);  
        }  
    }  
  
private slots:  
    void readData() {  
        QByteArray data = serial->readAll();  
        // 处理接收到的数据  
        qDebug() << "Received data:" << data;  
    }  
  
private:  
    QSerialPort *serial;  
};

然后,你需要一个或多个线程来运行串口管理类。这可以通过继承QThread来实现,但更好的做法是使用QThread的run方法来启动一个事件循环,并在其中运行你的串口管理类。

#include <QThread>  
  
class SerialPortThread : public QThread {  
    Q_OBJECT  
  
public:  
    SerialPortThread(QObject *parent = nullptr) : QThread(parent), manager(new SerialPortManager(this)) {}  
  
    void run() override {  
        exec(); // 启动事件循环  
    }  
  
    void openPort(const QString &portName, int baudRate) {  
        manager->openSerialPort(portName, baudRate);  
    }  
  
    // 其他必要的方法...  
  
private:  
    SerialPortManager *manager;  
};

最后,在你的主程序或主窗口中,你可以创建多个SerialPortThread实例,并为每个实例配置不同的串口。

#include <QCoreApplication>  
#include "SerialPortThread.h"  
  
int main(int argc, char *argv[]) {  
    QCoreApplication a(argc, argv);  
  
    SerialPortThread *thread1 = new SerialPortThread();  
    thread1->openPort("COM1", 9600);  
    thread1->start(); // 实际上在SerialPortThread中已经隐式启动事件循环  
  
    // 对于额外的串口,创建更多的SerialPortThread实例  
  
    return a.exec();  
}

需要注意的是:

  1. 确保在适当的时机关闭串口和线程。
  2. 考虑使用Qt的信号和槽机制来处理线程间的通信,避免直接调用线程内部的对象方法。
  3. 线程安全:确保在多个线程中访问共享资源时采取适当的同步措施。
  4. 错误处理:在实际应用中,应该添加更多的错误处理和异常捕获机制。
方案二:

没有实际的使用过

方案三:

可以查看博客中的代码,为什么这个方案不推荐:
在Qt中,将QSerialPort对象作为某个类的子对象(即使用new QSerialPort()),并且随后尝试通过moveToThread()将其移动到另一个线程中,这通常是不推荐的,也是不安全的。原因如下:
对象生命周期和所有权: 当你使用new QSerialPort()时,你正在创建一个QSerialPort对象,并将其父对象设置为当前对象。在Qt中,子对象的生命周期是由其父对象管理的。如果父对象被销毁,那么它的所有子对象也会被销毁。然而,当你尝试将子对象移动到另一个线程时,这可能会破坏Qt的对象层次结构和生命周期管理机制。
线程亲和性: QSerialPort(以及大多数Qt的IO类)被设计为在其被创建的线程中执行IO操作。尽管你可以通过moveToThread()改变对象的线程亲和性(即它的事件处理在哪个线程中发生),但这并不意味着你可以安全地在另一个线程中执行IO操作。对于QSerialPort来说,这可能会导致未定义的行为,包括程序崩溃。
Qt的线程模型: Qt的线程模型是基于事件循环的。当你将一个对象移动到另一个线程时,你实际上是在告诉Qt将该对象的事件(如信号和槽的调用)发送到该线程的事件循环中。但是,这并不意味着对象本身可以在该线程中执行非事件驱动的IO操作。

方案四:

首先新建一个串口连接类:

#include "serialcontroller.h"

SerialController::SerialController(QObject *parent) : QObject(parent){

}

/**
 * 析构函数
 * @brief SerialController::~SerialController
 */
SerialController::~SerialController(){
    if (m_serialPort->isOpen()) {
        m_serialPort->close();
        delete m_serialPort;
    }
}

/**
 * 初始化串口连接
 * @brief SerialController::initSerialPort
 * @param id 对象ID
 * @param portName 串口名
 */
void SerialController::initSerialPort(int id,QString portName){
    m_portName = portName;
    m_id = id;
    m_serialPort = new QSerialPort(this);
    m_serialPort->setPortName(m_portName);
    m_serialPort->setBaudRate(QSerialPort::Baud9600,QSerialPort::AllDirections);
    m_serialPort->setDataBits(QSerialPort::Data8);//数据位为8位
    m_serialPort->setFlowControl(QSerialPort::NoFlowControl);//无流控制
    m_serialPort->setParity(QSerialPort::NoParity);	//无校验位
    m_serialPort->setStopBits(QSerialPort::OneStop); //一位停止位

    if(!m_serialPort->open(QIODevice::ReadWrite)){ //用ReadWrite 的模式尝试打开串口
        emit sendUpdateUI(20,0,m_portName+"打开失败");//发送更新UI的信号
        return;
    }
    // 连接信号和槽
    connect(m_serialPort,&QSerialPort::readyRead,this,&SerialController::readData);

}

/**
 * 读取串口信息的槽并发送信号
 * @brief MainWindow::readData
 */
void SerialController::readData(){
    emit sendUpdateUI(20,2,"读取串口数据");
    QByteArray buf;
    buf = m_serialPort->readAll();
    QString hexStr;
    if(!buf.isEmpty()){
        // 将接收到的字节转换为16进制字符串
        for (char byte : buf) {
            hexStr += QString("%1").arg(byte&0xff, 2, 16, QChar('0'));
        }
    }
    buf.clear();
    emit sendDataToMain(hexStr);
}


/**
 * 发送数据
 * @brief SerialController::sendData
 * @param data 要发送的字节数组
 */
void SerialController::sendData(const QByteArray &data){
    m_serialPort->write(data);
}



在MainWindow中新建方法和槽

/**
 * 初始化串口
 * @brief MainWindow::initSerialPort
 */
void MainWindow::initSerialPort(){
    QStringList ports = port.split(" "); //连接指定的1到多个串口
    for (int i=0;i<ports.length();i++) {
        SerialController *con = new SerialController(this);
        connect(con, &SerialController::sendDataToMain, this, &MainWindow::receiveData);
        con->initSerialPort(i,ports[i]);
        serialControllers.append(con);//把对象放到数组中
    }
}



/**
 * 接收串口发来信息的槽
 * @brief MainWindow::receiveData
 * @param data 串口信息
 */
void MainWindow::receiveData(const QString &data){
    //接收到串口发来的信息后进行业务上的处理
}

在对串口发送信息时,可从数组中循环出来,可以群发,也可以根据自定的ID判断对指定的接口发

foreach(SerialController* con,serialControllers){
    con->sendData(QByteArray::fromHex(data));
}
  • 17
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
Qt中实现多线程串口可以使用多种方式。其中一种方式是使用QThread对象,将需要的操作放到线程里。另一种方式是类直接继承QThread类,重写run方法。还可以使用QtConcurrent等工具。在本实例中,我们将介绍第一种方式。具体步骤如下: 1. 设计界面,包括串口相关的控件。 2. 创建串口服务类,用于处理串口的打开、关闭、发送和接收等操作。 3. 使用QThread实现多线程。首先声明一个QThread对象,并将串口服务类的实例移动到该线程中。然后在线程的run方法中执行需要在线程中运行的操作。 4. 在主线程中加载串口多线程。 以上是实现多线程串口的基本步骤。具体的代码实现可以参考引用\[1\]和引用\[2\]中的示例代码。 #### 引用[.reference_title] - *1* *2* [Qt 串口 QThread多线程](https://blog.csdn.net/renburning/article/details/125319680)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] - *3* [QT串口通讯 多线程](https://blog.csdn.net/qq_42590121/article/details/122303826)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

梦里藍天

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值