You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Qt串口读写异常求助:空返回与随机段错误

串口通信随机异常排查建议

问题描述

两台硬件通过串口通信,各自持有SerialPortClass实例,出现两个随机异常:

  1. WaitForLineAvailable()偶尔返回空字符串;
  2. Write()方法中WaitForBytesWritten()行有时触发段错误。

排查思路

针对问题1:WaitForLineAvailable()返回空字符串

  • 数据覆盖问题:当前实现中每次读取到数据会直接覆盖Rx变量,若响应分多帧到达,后续读取会覆盖前面的内容,最终可能因未凑齐终止符导致返回空。需将Rx = QString::fromUtf8(responseData)改为Rx += QString::fromUtf8(responseData),实现数据追加。
  • 终止符不匹配:调用WaitForLineAvailable()时未指定终止符,若硬件返回的换行符(如\r\n)与函数默认终止符不一致,会导致循环超时,最终返回空。需明确硬件实际使用的终止符,调用时传入对应参数(如serialPort.WaitForLineAvailable(m_sRxMsg, 5, "\r\n"))。
  • 缓冲区清理时机不当:run()循环开头调用ClearReadBuffer(),可能误清除刚收到的部分响应数据。建议调整为发送命令前清理缓冲区,避免丢失有效数据。
  • 重试次数不足:若WaitForLineAvailable()的默认iWaitTime值过小,会导致重试次数不足,未等到完整响应就超时。需确认默认超时参数是否合理,根据硬件响应速度调整。

针对问题2:Write()中WaitForBytesWritten()触发段错误

  • 指针悬空风险:OpenCommunicationsPort()中,若多次调用打开端口,旧的SerialPort指针未被delete就重新赋值,会导致内存泄漏或悬空指针。需修改逻辑:
    if(SerialPort != nullptr)
    {
        if(SerialPort->isOpen()) SerialPort->close();
        delete SerialPort; // 添加这行释放旧指针
        SerialPort = nullptr;
    }
    SerialPort = new QSerialPort(port);
    
  • 线程安全冲突:虽然读写方法加了mutexSerialPort,但OpenCommunicationsPort()使用独立的mutexOpenCommunicationsPort,需确保打开端口操作不会与读写操作并发执行,避免SerialPort指针被异步修改导致访问无效内存。
  • 设备状态检查缺失:Write()中未先检查SerialPort是否有效且已打开,若串口已断开,调用waitForBytesWritten()会触发段错误。需添加前置检查:
    if (!SerialPort || !SerialPort->isOpen()) {
        return false;
    }
    
  • processEvents()的风险:在子线程中调用QCoreApplication::processEvents()可能引发事件循环混乱,间接导致资源竞争。建议移除该行代码,串口同步操作无需处理事件。

相关代码

SerialPortClass.cpp

bool SerialPortClass::OpenCommunicationsPort(QString port, QString params)
{
QMutexLocker locker(&mutexOpenCommunicationsPort);

if(SerialPort != nullptr)
{
    if(SerialPort->isOpen()) SerialPort->close();

    SerialPort->setPortName(port);
}
else
    SerialPort = new QSerialPort(port);

// split up params "230400 N 8 1"
QStringList paramsList = params.split(" ");

if(paramsList.size() == 4)
{
    SerialPort->setBaudRate(paramsList.value(0).toInt());
    if (paramsList.value(1) == "N") { SerialPort->setParity(QSerialPort::NoParity); }
    if (paramsList.value(1) == "E") { SerialPort->setParity(QSerialPort::EvenParity); }
    if (paramsList.value(1) == "O") { SerialPort->setParity(QSerialPort::OddParity); }
    if (paramsList.value(2) == "8") { SerialPort->setDataBits(QSerialPort::Data8); }
    if (paramsList.value(2) == "7") { SerialPort->setDataBits(QSerialPort::Data7); }
    if (paramsList.value(3) == "1") { SerialPort->setStopBits(QSerialPort::OneStop); }
    if (paramsList.value(3) == "2") { SerialPort->setStopBits(QSerialPort::TwoStop); }
    if (mydebug == 1) { qDebug() << "Port Opened on " << port << " " << params; }
    return SerialPort->open(QIODevice::ReadWrite);
}

return false;
}

bool SerialPortClass::Write(QString Tx, bool bDelayPerChar)
{
    QMutexLocker locker(&mutexSerialPort);

    QCoreApplication::processEvents();

    bool status = true;

    QByteArray baTxMsg = Tx.QString::toUtf8();

    foreach(char c, baTxMsg)
        {
            SerialPort->write(&c, 1);           
            SerialPort->waitForBytesWritten(200);
            if(bDelayPerChar) { QThread::msleep(5); }
        }

    qDebug() << "Tx:" << Tx;

    return status;
}

bool SerialPortClass::WaitForLineAvailable(QString& Rx, int iWaitTime, QString terminator)
{
    QMutexLocker locker(&mutexSerialPort);

    Rx = "";
    int delay = 500;
    int retries = (iWaitTime *1000)/delay;
    /*int retries = 30;
    char buffer[128];*/

    while ( (!Rx.contains(terminator) ) && (retries > 0) )
    {            
        if (SerialPort->waitForReadyRead(delay))
            //if (SerialPort->waitForReadyRead(5000))
        {
            QByteArray responseData = SerialPort->readAll();

            while (SerialPort->waitForReadyRead(50))
            {
                responseData += SerialPort->readAll();
            }
            Rx = QString::fromUtf8(responseData);
        }
        retries--;
    }
    qDebug() << "Rx:" << Rx << "" << retries;

    if (retries > 0)
        return true;

    return false;
}

HardwareThread1.h

#ifndef HARDWARETHREAD1_H
#define HARDWARETHREAD1_H

#include <QObject>
#include <QThread>
#include <QMessageBox>

#include "serialportclass.h"

class HardwareThread1 : public QThread
{
Q_OBJECT

public:
explicit HardwareThread1 ( QObject *parent = nullptr );
~HardwareThread1 ();

void go();
void run();
void stop();
void CloseSerialComms();
private:
SerialPortClass serialPort;

bool    GetStopControllerTestFlag();
bool    GetIsShutdownSequenceRunningFlag();
bool    EstablishComms();
bool    m_bCommsEstablished = false;    
int     m_iPSUStatus;
int     m_iIEEEAddress;
QString m_sComPort = "";
QString m_sPortParams = "";
QString m_sRxMsg = "";

signals:
void signalDisplayPowered(int status, QString sHardware);
void signalDisplayErrorMessage(QString sMessageBoxWindowTitle, QString sMessageBoxWindowText);
void signalUserMessageBox(QString sText1, QString sText2, int iIcon, int iBtn = QMessageBox::Close);
void signalToRunShutdownSequence();
};

#endif // HARDWARETHREAD1_H

HardwareThread1.cpp

#include "hardwarethread1.h"
#include "globals.h"
#include "programsettingsclass.h"

#include <QDebug>
#include <QMessageBox>

//extern variables//
extern ProgramSettingsClass *pProgramSettingsClassObj;

HardwareThread1::HardwareThread1(QObject *parent) :
QThread(parent)
{
bool bStatus;
m_bCommsEstablished = false;

bStatus = EstablishComms();   
}

/*!

@brief Destructor for the thread.
*/
HardwareThread1::~HardwareThread1()
{
this->terminate();
this->wait();
}
/*!

@brief Attempt to open serial port.

@param in port COM port number to open.

@param in params Serial port parameters.

@return Port open successfully
*/
bool HardwareThread1::EstablishComms()
{
if (m_bCommsEstablished == false)
{
this->m_sComPort = pProgramSettingsClassObj->GetCOMPortSetting("ARDUINO_UNO_HW"); // Get port number for this PSU from settings file list.

     this->m_sPortParams = pProgramSettingsClassObj->GetCOMPortParameters("ARDUINO_UNO_HW");

     if (serialPort.OpenCommunicationsPort(this->m_sComPort, m_sPortParams)) // Attempt to open COM port.
         {
             m_bCommsEstablished =  true;
         }
     else // Could not open COM port.
         {                    
             m_bCommsEstablished =  false;
         }
 }

 return m_bCommsEstablished;
}

/*!

@brief Close Serial Port Comms
*/
void HardwareThread1::CloseSerialComms()
{
serialPort.Close();
}
void HardwareThread1::go()
{
this->start();
}

/*!

@brief Retrieve 'change in Stop Controller Flag'.
@return Flag
*/
bool HardwareThread1::GetStopControllerTestFlag()
{
bool bGet;
mutexGlobalVariable.lock();
bGet = g_bStopControllerTestFlag;
mutexGlobalVariable.unlock();
return bGet;
}
bool HardwareThread1::GetIsShutdownSequenceRunningFlag()
{
bool bGet;
mutexShutdownSequenceVariable.lock();
bGet = g_bIsShutdownSequenceRunning;
mutexShutdownSequenceVariable.unlock();
return bGet;
}

/*!

@brief Timed PSU monitoring.

Checks and sets voltage and current.
*/
void HardwareThread1::run()
{
while(true)
{
bool status = serialPort.IsOpen();

 if ((status) && (!g_bManualValveBeingOperatedFlag) && (!GetStopControllerTestFlag()))
{           
    serialPort.ClearReadBuffer();
              
    serialPort.Write("Clear\n",true);
              
    serialPort.Write("HOOD?\n",true);
              
    serialPort.WaitForLineAvailable(m_sRxMsg); // Wait for response.
               
    m_sRxMsg.remove("\r");
    m_sRxMsg.remove("\n");
    int iHoodStatus = m_sRxMsg.toInt();

    //qDebug() << "HOOD_STAT=" << m_sRxMsg << "\n";

    if((iHoodStatus == 0) && (m_sRxMsg != "")) //The hood is open, hence cut power supply and vent all pressure //
    {
        //Set the global flag//
        mutexGlobalVariable.lock();
        g_bStopControllerTestFlag = true;
        mutexGlobalVariable.unlock();

        //qDebug() << "HOOD_STAT=" << iHoodStatus << "\n";
        emit signalDisplayErrorMessage("Hood Status", "Hood Open Detected!\n \nThe Program will Quit Now.");
         
        CloseArduinoSerialComms();                            
    }
}
QThread::msleep(500);
}
}

void HardwareThread1::stop()
{
this->terminate();
this->wait();
}

内容的提问来源于stack exchange,提问作者coder

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.14 11:42:37