之前做了个串口程序,利用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。