一、串口通信类的实现
创建一个实现串口通信的类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;
}
点击 下载串口通信类。