一、问题描述
在串口通信时,会经常遇到,在接收一帧数据时,QSerialPort::readyRead()
会触发多次,即接收一包数据需要多次接收才能完整得到数据帧。
二、解决思路
延迟接收:
QSerialPort::readyRead()
触发时不要立刻去接收数据,而是等待readyRead的最后一次触发时读数据。
如何等待最后一次readyRead: 用一个单触发定时器,readyRead
触发时启动定时器,当定时器timeout()
,可以认为是最后一次readyRead()
。
三、贴代码思路
//!
m_serial = new QSerialPort;
connect(m_serial,&QSerialPort::readyRead,this,&MainWindow::slot_serialport_readyRead);
//! 轮询定时器-周期性发送数据
pTimerSend = new QTimer(this);
pTimerSend->setTimerType(Qt::PreciseTimer);
connect(pTimerSend, &QTimer::timeout, this, &MainWindow::slot_com_timeout_send);
// pTimerSend->start(200); //串口连接成功后启动,此处仅为体现轮询周期为200ms
//! 串口模式-数据延迟接收-保证数据完整
pTimerRecv = new QTimer(this);
pTimerRecv->setTimerType(Qt::PreciseTimer);
pTimerRecv->setSingleShot(true); //只触发一次
connect(pTimerRecv, &QTimer::timeout, this, &MainWindow::slot_serialport_delay_recv_timeout);
//! 串口接收信号
void MainWindow::slot_serialport_readyRead()
{
//! 定时器重新启动,直到该函数不再触发(超过50ms),定时器触发
pTimerRecv->start(50); //4K数据,50ms的延迟接收完全没问题
}
//! 串口延迟接收
void MainWindow::slot_serialport_delay_recv_timeout()
{
QByteArray Recv = m_serial->readAll();
//! qDebug()<<"=="<<Recv;
//! qDebug()<<"###"<<Recv.toHex(' ');
//! 处理接收的数据
com_recv_data_process(Recv);
}
四、注意事项
- 延迟接收时长问题: 延迟时长应小于轮询周期
- 通信状况复杂不适用:若通信的串口中会打印调试信息或其他信息时,延迟接收可能出现问题,例如:由于串口中有打印的调试信息,
readyRead()
信号不断发生,导致pTimerRecv
不断重启而无法触发延迟接收函数,从而导致收不到或收到远超应收字节数的数据。
五、总结
以上描述方式仅适合干净的通信环境,若想一劳永逸的适配各种环境,这里提供一个思路。如下:开辟一个缓存,实时接收串口数据(仅存),开一个子线程,识别缓存中的数据,去掉无法识别的数据,取出已识别数据。