qt下串口接收数据线程实现

文章介绍了如何使用QSerialCommThrd类实现串口通信,包括串口初始化、数据接收、缓冲区管理以及数据解析并保存到文件的过程。该类继承自QObject,并利用QSerialPort进行操作,通过线程控制数据处理并发性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、串口通信类的实现

        创建一个实现串口通信的类QSerialCommThrd,继承自QObject,没错是QObject而不是QThread。在类内定义一个QSerialPort指针,进行串口的读写。类的具体定义如下:

#ifndef QSERIALCOMMTHRD_H
#define QSERIALCOMMTHRD_H

#include <QObject>
#include <QtSerialPort>
#include <QFile>

#define  BUFF_MAX_LEN  8192000

class QSerialCommThrd : public QObject
{
    Q_OBJECT
public:
    explicit QSerialCommThrd(QObject *parent = nullptr);
    ~QSerialCommThrd();

    //解析数据、保存到文件
    void  AnalysisData();

protected:
    QSerialPort *m_serial;    //串口指针

    unsigned char*  m_readBuff;    //数据缓冲区指针
    unsigned char*  m_data;

    uint16_t      m_iReadPos;        //缓冲区读指针
    uint16_t      m_iWritePos;       //缓冲区写指针
    uint64_t      m_llRecvTotal;    //接收数据总字节数
    uint64_t      m_llReadLen;      //处理数据总字节数

    QFile         m_fileDrilling;
    char          m_fileData[256];

    char*         m_pszData ; 

private:
     void  SaveData(unsigned char* pdata,int len);



public slots:
    void handleError(QSerialPort::SerialPortError error);
    void readData();
    //打开串口、发送数据、关闭串口
    void  OpenSerial(QString strCom,QString strFile);
    void  OnRecvSendData(QByteArray dataArray);
    void  OnCloseSerial();
};


#endif // QSERIALCOMMTHRD_H

在QSerialCommThrd类构造函数内,进行串口指针初始化,用connect连接本地槽函数。

QSerialCommThrd::QSerialCommThrd(QObject *parent) : QObject(parent)
{
    m_llReadLen =0;
    m_llRecvTotal =0;
    m_iReadPos  =0;
    m_iWritePos =0;


    m_pszData = new char[READ_TMP_BUF+1];  //读数据用
    m_data  = new unsigned char[WRT_MAXLEN_PERTIME+1];   //解析数据用数据指针

    m_readBuff = new unsigned char[BUFF_MAX_LEN];
    m_serial = new QSerialPort();
    connect(m_serial, &QSerialPort::errorOccurred, this, &QSerialCommThrd::handleError);
    connect(m_serial, &QSerialPort::readyRead, this, &QSerialCommThrd::readData);

}

串口号和保存数据的文件名,由调用者传入,在OpenSerial函数内进行串口初始化和文件的创建。

void QSerialCommThrd::OpenSerial(QString strCom,QString strFile)
{
    if(m_serial->isOpen()){
        m_serial->close();
    }

    m_serial->setPortName(strCom);
    m_serial->setBaudRate(921600);
    m_serial->setDataBits(QSerialPort::Data8);
    m_serial->setParity(QSerialPort::NoParity);
    m_serial->setStopBits(QSerialPort::OneStop);
    m_serial->setFlowControl(QSerialPort::NoFlowControl);
    if (!m_serial->open(QIODevice::ReadWrite)) {
        QMessageBox::critical(nullptr,tr("open serial"),tr("打开串口错误."));
        return ;
    }

    memset(m_readBuff,0,BUFF_MAX_LEN);
    m_llReadLen =0;
    m_llRecvTotal =0;

    if(m_fileDrilling.isOpen()){
        m_fileDrilling.close();
    }
    m_fileDrilling.setFileName(strFile);
    bool bOpen = m_fileDrilling.open(QIODevice::WriteOnly);
    if(!bOpen){
        QMessageBox::warning(nullptr,tr("测试"),tr("创建数据文件错误!"),QMessageBox::Ok);
    }

    m_iReadPos  =0;
    m_iWritePos =0;
}

收到读数据信号,在readData槽函数内从串口读取所有数据,保存到缓冲区。

void QSerialCommThrd::readData(){

    qint64 length = m_serial->bytesAvailable();
    QByteArray info = m_serial->readAll();

    if(length >0)
    {
        if(length +m_iWritePos > BUFF_MAX_LEN)
        {
            memcpy(m_pszData,info.data(),length);
            int part = BUFF_MAX_LEN -m_iWritePos;
            memcpy(m_readBuff+m_iWritePos,m_pszData,part);
            memcpy(m_readBuff,m_pszData+part,length -part);

            m_iWritePos = length -part;

        }else{
            memcpy(m_readBuff+m_iWritePos,info.data(),length);
            m_iWritePos += length;
            m_iWritePos %= BUFF_MAX_LEN;
        }

        m_llRecvTotal += length;
    }
}

AnalysisData供外部调用,且需要使用线程调用,用于从缓冲区获取数据并保存到文件内。

void QSerialCommThrd::AnalysisData()
{
    //MSG_ACCELERATION  包最小长度20字节
    if(m_llReadLen +20 <= m_llRecvTotal)
    {
        int iRdPos = m_iReadPos;
        int iWrtPos = m_iWritePos;
        int len=0;
        if(iRdPos <= iWrtPos){
            if(iRdPos + WRT_MAXLEN_PERTIME <= iWrtPos){
                memcpy(m_data,m_readBuff+iRdPos,WRT_MAXLEN_PERTIME);
                len = WRT_MAXLEN_PERTIME;
            }else{
                len = iWrtPos - iRdPos;
                memcpy(m_data,m_readBuff+iRdPos,len);
            }
            m_iReadPos += len;
        }else{
            if(iRdPos + WRT_MAXLEN_PERTIME <= BUFF_MAX_LEN){
                len = WRT_MAXLEN_PERTIME;
                m_iReadPos += len;
            }else{
                len = BUFF_MAX_LEN - iRdPos;
                m_iReadPos =0;
            }
            memcpy(m_data,m_readBuff+iRdPos,len);
        }
        SaveData(m_data,len);//保存到文件

        m_iReadPos %= BUFF_MAX_LEN;
        m_llReadLen +=len;

    }
}

二、串口通信类的调用

在MainWindow内(或者在你自己的继承自QDialog的类内),定义一个QSerialCommThrd指针

m_pCommThrd,再定义一个QThread的类成员;并在类的构造函数内对m_pCommThrd初始化,连接槽函数,将类的对象move到线程内,启动线程。同时启动一个线程,在线程的回调函数内调用AnylysisData(),进行数据解析和保存。
m_pCommThrd = new QSerialCommThrd();
    if(m_pCommThrd){
        m_pCommThrd->moveToThread(&m_thrdComm);

        connect(this,SIGNAL(transOpenSerial(QString,QString)),m_pCommThrd,SLOT(OpenSerial(QString,QString)));
        connect(this,SIGNAL(transSendData(QByteArray)),m_pCommThrd,SLOT(OnRecvSendData(QByteArray)));
        connect(this,SIGNAL(transCloseSerial()),m_pCommThrd,SLOT(OnCloseSerial()));
        

        m_thrdComm.start();
    }

 m_pthrdAcq = new std::thread(StartAcqProc,this);
void MainWindow::StartAcqProc(MainWindow *pWnd)
{
    QByteArray   dataArray;

    while(pWnd->m_bReadStat)
    {
        pWnd->m_pCommThrd->AnalysisData();

        QThread::usleep(200);  //此处sleep必须有,不然线程占用很高的资源

    }

    pWnd->m_pthrdAcq->detach();
    delete pWnd->m_pthrdAcq;

}

在启动串口传输的菜单函数或按钮函数内,使用emit启动串口,传入文件名。

 m_strFdiFile = QFileDialog::getSaveFileName(this,tr("保存文件"),tr("f:/"),tr("测试文件(*.fdi)"));
    if(m_strFdiFile.isEmpty()){
       m_strFdiFile = tr("c:/1234.fdi");
    }

    ui->labRecv->setText("正在接收数据......");
    emit transOpenSerial(m_strComName,m_strFdiFile);

停止串口数据接收

void MainWindow::StopTest()
{
    m_bReadStat =false;

    emit transCloseSerial();

    m_pthrdStop->detach();
    delete m_pthrdStop;
}

点击  下载串口通信类

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

previewer1024

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

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

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

打赏作者

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

抵扣说明:

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

余额充值