QT串口单独线程高速接收

本文详细介绍了在QT程序中遇到的串口通信问题,当串口数据发送速度过快时,程序因无法及时处理导致崩溃。作者通过创建单独线程持续读取串口数据,并引入事件循环来解决这个问题。经过调整,程序现在能够以4ms的事件循环稳定读取200Hz速率的数据,避免了数据丢失,实现了有效通信。
摘要由CSDN通过智能技术生成

        之前做了个串口程序,利用QT程序自带的readReady信号来读取接收到的串口信息。给别人使用的时候,发现了一个问题:就是对方串口发送速度太快,程序一运行就自动退出。可能原因分析:readReady无法快速响应。对方速率是约是200Hz,每一包数据为23个字节。将接收到的数据打印出来,一下读取的数据太多,其中我有个操作是放进QString中,因为readReady产生速度慢,缓存区就会缓存很多数据,下一次读取过多,超了Qstring的长度,程序就自动退出了。

        后来,我想使用一个单独线程在那不停读取,然后将读取的数据通过信号发送出去。于是我就利用原始的QserialPort修改了myserialport类。

        myserialport.h

#ifndef MYSERIALPORT_H
#define MYSERIALPORT_H

#include <QThread>
#include <QDebug>
#include <QSerialPort>
#include <QEventLoop>
#include <QTimer>
#include <QDateTime>            //===时间

class MySerialPort:public QThread
{
    Q_OBJECT
public:
    MySerialPort();
    QSerialPort *serialport;
public:
    void setPortName(QString portname);
    bool open(QIODevice::OpenMode mode);
    bool setBaudRate(qint32 baudrate);
    bool setParity(QSerialPort::Parity parity);
    bool setDataBits(QSerialPort::DataBits databits);
    bool setFlowControl(QSerialPort::FlowControl flowcontrol) ;
    bool setStopBits(QSerialPort::StopBits stopbits);
    bool setDataTerminalReady(bool ready);
    void setStopFlag(bool flag);
    bool mySerialPortIsOpen();
private:
    bool stopFlag;
signals:
    void uartSendData(QByteArray data);
private:
    void run() override;
};

#endif // MYSERIALPORT_H

        myserialport.cpp

#include "myserialport.h"

MySerialPort::MySerialPort()
{
    serialport = new QSerialPort();
    stopFlag = false;
}

void MySerialPort::run()
{
    //qDebug()<<"parity:"<<serialport->parity();
    while(!stopFlag)
    {
        //===接收数据
        QByteArray buffer = serialport->readAll();
        if(buffer.size()>0)
        {
            //qDebug()<<buffer.toHex()<<endl;
            emit uartSendData(buffer);
        }

        //===事件循环
        QEventLoop loop;    // 非常重要, 没有事件循环数据无法发送.如果读取的话,不加一些等待,读取的数据都是空
        QTimer::singleShot(5, &loop, SLOT(quit()));
        loop.exec();

        //===测试发送数据
        /*QByteArray dataToSend;
        dataToSend.resize(23);
        dataToSend[0]=0xCA;
        dataToSend[1]=0xAA;
        serialport->write(dataToSend);
        QDateTime current_datetime = QDateTime::currentDateTime();*/
        //qDebug()<<current_datetime.toString("yyyy-MM-dd hh:mm:ss.zzz")<<endl;
    }
    serialport->close();
}

void MySerialPort::setStopFlag(bool flag)
{
    stopFlag = flag;
}

void MySerialPort::setPortName(QString portname)
{
    serialport->setPortName(portname);
}
bool MySerialPort::open(QIODevice::OpenMode iodevice)
{
    if(serialport->open(iodevice))
    {
        return true;
    }
    return false;
}
bool MySerialPort::setBaudRate(qint32 baudrate)
{
    if(serialport->setBaudRate(baudrate))
    {
        return true;
    }
    return false;
}
bool MySerialPort::setParity(QSerialPort::Parity parity)
{
    if(serialport->setParity(parity))
    {
        return true;
    }
    return false;
}
bool MySerialPort::setDataBits(QSerialPort::DataBits databits)
{
    if(serialport->setDataBits(databits))
    {
        return true;
    }
    return false;
}
bool MySerialPort::setFlowControl(QSerialPort::FlowControl flowcontrol)
{
    if(serialport->setFlowControl(flowcontrol))
    {
        return true;
    }
    return false;
}
bool MySerialPort::setStopBits(QSerialPort::StopBits stopbits)
{
    if(serialport->setStopBits(stopbits))
    {
        return true;
    }
    return false;
}

bool MySerialPort::mySerialPortIsOpen()
{
    return serialport->isOpen();
}

bool MySerialPort::setDataTerminalReady(bool ready)
{
    if(serialport->setDataTerminalReady(ready))
    {
        return true;
    }
    return false;
}

        调用程序片段

void readWindow::on_uartPushButton_clicked()
{
    if(fileName.isNull())
    {
        QMessageBox::critical(this,"(╯︵╰)","对不起,我尝试读取协议文件,但是我好像迷路了,没有找到协议文件");
        return;
    }

    if(!fileTextSave.isOpen())
    {
        QMessageBox::critical(this,"(╯︵╰)","实在抱歉,没有创建新的实验记录,我无法区分每个实验的不同区别");
        return;
    }

    if(ui->uartPushButton->text() == "打开串口")
    {

        QDateTime current_datetime = QDateTime::currentDateTime();
        ui->operationTextBrowser->insertPlainText(current_datetime.toString("yyyy-MM-dd hh:mm:ss.zzz")+":");
        ui->operationTextBrowser->insertPlainText("打开串口\n");

        //===获取串口设置信息
        QString comStr;
        comStr = ui->comComboBox->currentText();

        //===串口初始化
        m_myserialport->setPortName(comStr);
        if(m_myserialport->open(QIODevice::ReadWrite))
        {
            //===设置波特率
            m_myserialport->setBaudRate(ui->rateComboBox->currentText().toInt());

            //===设置校验位
            switch(ui->checkComboBox->currentIndex())
            {
            case 0:
                m_myserialport->setParity(QSerialPort::NoParity);
                break;
            case 1:
                m_myserialport->setParity(QSerialPort::OddParity);
                break;
            case 2:
                m_myserialport->setParity(QSerialPort::EvenParity);
                break;
            default:
                break;
            }

            //===设置数据位
            switch(ui->dataComboBox->currentIndex())
            {
            case 0:
                m_myserialport->setDataBits(QSerialPort::Data5);
                break;
            case 1:
                m_myserialport->setDataBits(QSerialPort::Data6);
                break;
            case 2:
                m_myserialport->setDataBits(QSerialPort::Data7);
                break;
            case 3:
                m_myserialport->setDataBits(QSerialPort::Data8);
                break;
            default:
                break;
            }

            //===设置流控制
            m_myserialport->setFlowControl(QSerialPort::NoFlowControl);

            //===设置停止位
            switch(ui->stopComboBox->currentIndex())
            {
            case 0:
                m_myserialport->setStopBits(QSerialPort::OneStop);
                break;
            case 1:
                m_myserialport->setStopBits(QSerialPort::OneAndHalfStop);
                break;
            case 2:
                m_myserialport->setStopBits(QSerialPort::TwoStop);
                break;
            default:
                break;
            }
            m_myserialport->setDataTerminalReady(false);//===关闭readyRead信号
            connect(m_myserialport,SIGNAL(uartSendData(QByteArray)),this,SLOT(uartReadData(QByteArray)));
            m_myserialport->start();
            //m_myserialport->setDataTerminalReady(true);//===设置“管脚控制状态”产生readyRead()信号
            qDebug()<<"连接成功"<<endl;

            ui->operationStatusBar->showMessage("串口打开成功!");//===状态栏显示操作信息
            ui->uartPushButton->setText("关闭串口");
            //connect(m_serialport,SIGNAL(readyRead()),this,SLOT(uartReadData()));

            //===开启线程
            if(databaseSaveFlag)
            {
                wbThread = new writeDatabaseThread();
                wbThread->setQuery(query);
                wbThread->setStopFlag(false);
                wbThread->start();
                connect(this,SIGNAL(sendCommand(QString)),wbThread,SLOT(getQueryCommand(QString)));
            }
        }
        else
        {
            QMessageBox::critical(this,"(╯︵╰)","对不起,我无法打开这个"+comStr+"串口,不知道发生了什么");
        }
    }
    else if(ui->uartPushButton->text() == "关闭串口")
    {

        QDateTime current_datetime = QDateTime::currentDateTime();
        ui->operationTextBrowser->insertPlainText(current_datetime.toString("yyyy-MM-dd hh:mm:ss.zzz")+":");
        ui->operationTextBrowser->insertPlainText("关闭串口\n");

        if(databaseSaveFlag)
        {
            wbThread->setStopFlag(true);
            wbThread->quit();
        }
        if(fileTextSave.isOpen())
        {
            fileTextSave.close();
        }
        m_myserialport->setStopFlag(true);
        //m_serialport->close();
        ui->operationStatusBar->showMessage("关闭串口!");//===状态栏显示操作信息
        ui->uartPushButton->setText("打开串口");
    }
}

        出现问题:修改完了之后,我进行测试,发现readAll函数一直读不出来东西,一直不知道什么原因。测试发送数据,发送数据是正常的,说明串口设置等操作都是正确的,那么问题在哪儿呢?后来不停的搜,发现有的程序加了事件循环:说了读太快,内容是读不出来了。

        解决方式:我就在程序里加了个4ms的事件循环,因为他们需要的200Hz的,我就读的更快一点。

        修改后整体程序测试:利用自己的程序,按照5ms的事件循环去发送数据,然后按照4ms的事件循环进行读取,发现是可以正常读取的,漏帧情况很少。

        下图为发送端情况,发送的时间间隔大致为5-6ms。

 

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值