Qt 中使用QSerialPort类进行串口数据收发,在接收数据时,使用 QSerialPort类的readyRead() 信号,只要有数据发送过来就发送改信号,这就导致一条数据可能分多次抛出。由于正常的数据没有固定的开头和结尾,这就导致无法获取正常的一组数据。如下图所示,上位机给下位机发送FREQ?,下位机反馈FREQ=50.00,但是上位机接收到的数据不是一组数据,而是分为两次接收。
针对以上问题,提出以下解决办法:
方法一:增加接收延时功能,把多次读取的数据保存到缓冲区,延时结束后,将缓冲区的数据一次性全部读出。
示例:
timer = new QTimer();
connect(timer, &QTimer::timeout, this, &servoMotorCameraControl::timeUpdate);
connect(SerialPort3, &QSerialPort::readyRead, this, &servoMotorCameraControl::readMotorControl);//serialPort_motor串口接收到数据
实现:
void servoMotorCameraControl::readMotorControl() {//SerialPort3的反馈
/* //定时器在关闭之后才能重新启动,所以使用该方法必须在100Ms内将一帧数据发送完毕
if (timer->isActive()==false)
{
timer->start(100);//启动定时器,接收100毫秒数据,100Ms内接收的数据为一帧
}*/
timer->start(100);//启动定时器,每次有数据来时都会重新启动定时器,所以使用该方法时,一帧数据的每一部分在100Ms内发送完毕即可,不要求100Ms内将一帧数据发送完毕
buffer.append(SerialPort3->readAll());
}
void servoMotorCameraControl::timeUpdate() {
timer->stop();
qDebug() << buffer.data()<<endl;
qDebug() << buffer.mid(5);
buffer.clear();
}
采用方法一后,上位机接受的数据如下图,并将频率提取出来。
方法二:
如果可以固定数据的开头和结尾,串口通信双方在通信前应制定好通信协议,规定好数据的起始和结束标志,串口当读到完整的起始和结束标志之后,才认定读完一条完整的数据。
示例:
规定发送的数据以“{”开头,以“}”结尾:
定义变量:
QByteArray ReadData;
QByteArray PasteData;
信号与槽函数连接:
connect(SerialPort3, &QSerialPort::readyRead, this, &servoMotorCameraControl::readMotorControl);//serialPort_motor串口接收到数据
接受数据的函数:
//SerialPort3的反馈
void servoMotorCameraControl::readMotorControl() {
static QByteArray sumData;
QByteArray tempData = SerialPort3->readAll();
qDebug() << "tempData:" << tempData.data();
if (!tempData.isEmpty())
{
sumData.append(tempData);
if (sumData.contains("\n")) // 检测到换行符
{
qDebug() << "sumData:" << sumData.data();
do_DataHandler(sumData); // 数据解析
sumData.clear();
}
}
tempData.clear();
}
void servoMotorCameraControl::do_DataHandler(QByteArray BufferData)
{
//异常类:无头且变量为空,已丢失头部,数据不可靠,直接返回
if ((!BufferData.contains("{"))&&(PasteData.isNull()))
{
return;
}
//第一种:有头无尾,先清空原有内容,再附加
if ((BufferData.contains("{"))&&(!BufferData.contains("}")))
{
PasteData.clear();
PasteData.append(BufferData);
}
//第二种:无头无尾且变量已有内容,数据中段部分,继续附加即可
if ((!BufferData.contains("{"))&&(!BufferData.contains("}"))&(!PasteData.isNull()))
{
PasteData.append(BufferData);
}
//第三种:无头有尾且变量已有内容,已完整读取,附加后输出数据,并清空变量
if ((!BufferData.contains("{"))&&(BufferData.contains("}"))&(!PasteData.isNull()))
{
PasteData.append(BufferData);
ReadData = PasteData;
PasteData.clear();
}
//第四种:有头有尾(一段完整的内容),先清空原有内容,再附加,然后输出,最后清空变量
if ((BufferData.contains("{"))&&(BufferData.contains("}")))
{
PasteData.clear();
PasteData.append(BufferData);
ReadData = PasteData;
PasteData.clear();
}
if (ReadData.length()>0)
{
qDebug() << "ReadData:" << ReadData.data();
qDebug() << ReadData.mid(10, 6);
}
ReadData.clear();
}
//判断十六进制类:以“0x69”开始,以“0xf7”结束:
void servoMotorCameraControl::do_DataHandler(QByteArray BufferData)
{
//异常类:无头且变量为空,已丢失头部,数据不可靠,直接返回
if ((!BufferData.contains(0x69))&&(PasteData.isNull()))
{
return;
}
//第一种:有头无尾,先清空原有内容,再附加
if ((BufferData.contains(0x69))&&(!BufferData.contains(0xf7)))
{
PasteData.clear();
PasteData.append(BufferData);
}
//第二种:无头无尾且变量已有内容,数据中段部分,继续附加即可
if ((!BufferData.contains(0x69))&&(!BufferData.contains(0xf7))&(!PasteData.isNull()))
{
PasteData.append(BufferData);
}
//第三种:无头有尾且变量已有内容,已完整读取,附加后输出数据,并清空变量
if ((!BufferData.contains(0x69))&&(BufferData.contains(0xf7))&(!PasteData.isNull()))
{
PasteData.append(BufferData);
ReadData = PasteData;
PasteData.clear();
}
//第四种:有头有尾(一段完整的内容),先清空原有内容,再附加,然后输出,最后清空变量
if ((BufferData.contains(0x69))&&(BufferData.contains(0xf7)))
{
PasteData.clear();
PasteData.append(BufferData);
ReadData = PasteData;
PasteData.clear();
}
if (ReadData.length() > 0)
{
qDebug() << "ReadData:" << ReadData.toHex();
//qDebug() << ReadData.mid(10, 6);
}
ReadData.clear();
}