Qt串口数据接收不完整

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();
}
  • 12
    点赞
  • 96
    收藏
    觉得还不错? 一键收藏
  • 7
    评论
Qt串口接收数据可以通过Qt的SerialPort模块来实现。首先需要在Qt工程中添加SerialPort模块的头文件,然后创建一个QSerialPort对象来进行串口的配置和数据接收。 具体步骤如下: 1. 引入SerialPort模块的头文件: #include <QtSerialPort/QtSerialPort> 2. 创建一个QSerialPort对象: QSerialPort serial; 3. 配置串口参数: serial.setPortName("串口名称"); // 设置串口名称,如"COM1"或"/dev/ttyUSB0"等 serial.setBaudRate(QSerialPort::Baud9600); // 设置波特率 serial.setDataBits(QSerialPort::Data8); // 设置数据位数 serial.setParity(QSerialPort::NoParity); // 设置奇偶校验 serial.setStopBits(QSerialPort::OneStop); // 设置停止位 serial.setFlowControl(QSerialPort::NoFlowControl); // 设置流控制 4. 打开串口: serial.open(QIODevice::ReadOnly); // 以只读方式打开串口 5. 监听串口数据的到达: connect(&serial, SIGNAL(readyRead()), this, SLOT(readData())); 6. 实现数据接收槽函数: void MyClass::readData() { QByteArray data = serial.readAll(); // 读取串口缓冲区中的所有数据 // 对接收到的数据进行处理 ... } 通过以上步骤,可以实现Qt串口数据接收接收到的数据可以通过读取QByteArray对象的方法进行处理,比如获取数据长度、将数据转换为字符串等。 需要注意的是,在使用串口通信时,还需要注意安装操作系统相关的串口驱动程序,并且确认所选用的串口号及串口参数与对应的设备匹配。此外,如果需要发送数据,还可以通过QSerialPort的write方法来实现。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值