Qt串口读写异常求助:空返回与随机段错误
串口通信随机异常排查建议
问题描述
两台硬件通过串口通信,各自持有SerialPortClass实例,出现两个随机异常:
WaitForLineAvailable()偶尔返回空字符串;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
相关产品推荐
相关产品推荐

