Qt|串口通信之同步数据 发送数据并等待返回值

2024.4.8更新:发送数据并等待返回值的函数,在不断尝试和百度AI的帮助下彻底实现同步。
2024.4.3更新:发现PortRead函数无法读到数据,重新换了一种方式,原方式也保留,详见下方代码。
2022.2.14更新:发现打断点调试可以实现数据同步,不打断点收到的数据时序会乱,网上有很多解决方法,因为需求限制,我使用了发送数据后Sleep(200)毫秒对该问题进行了避规。

参考:【Qt】串口通讯
QT串口QSerialPort类循环接收可能导致的数据接收不到问题
本文只写实现,原理见上方参考连接,搜了很多资料都是异步通信的,需求是需要同步通信,故写此文记录。

需要依赖serialport,如图所示:
在这里插入图片描述
运行结果:
在这里插入图片描述
ui文件部署:
在这里插入图片描述
运行环境win10、vs2015、qt5工程目录结构:
在这里插入图片描述
主函数:

#include "SerialCommunication.h"
#include <QtWidgets/QApplication>
#include <qDebug>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    SerialCommunication w;
	// 使用示例
#if 0
	QByteArray str;
	str.append("123");
	int data_size = w.PortWrite(str.data(), str.size());
	Sleep(200);
	int size = w.PortRead(SerialCommunication::ebpc);
	if (size == 0)
	{
		qDebug()<<("未接收到回复,下发数据失败!");
		//return;	// 如果收到正确回复继续后续操作,未收到可根据需求中断跳转
	}
#endif
    w.show();
    return a.exec();
}

串口通信头文件:

#pragma once

#include <QtWidgets/QWidget>
#include <QSerialPort>
#include <memory>
#include "ui_SerialCommunication.h"

class SerialCommunication : public QWidget
{
    Q_OBJECT

public:
    SerialCommunication(QWidget *parent = Q_NULLPTR);
	//!  一个串口参数类. 
	/*!
	串口相关参数.
	*/
	struct Param
	{
		QString serialPortName;//! 串口名.串口号 COM1
		qint32 baudRate;//! 串口波特率. 115200
		QSerialPort::DataBits dataBits;//! 数据位. 8 7
		QSerialPort::Parity parity;//! 奇偶位. 校验位 none even odd
		QSerialPort::StopBits stopBits;//! 停止位. 1 0
	};

	//! 串口的初始化. 
	void PortInit();
	//! 打开串口. 
	bool PortOpen();
	//! 关闭串口. 
	bool PortClose();
	//! 发送数据. 
	int PortWrite(char* buff, unsigned int length);
	//! 发送数据并等待返回值的函数
	QByteArray sendSerialCommand(const QByteArray &command);
	
	// 读取数据所需枚举 例子可根据需求自由修改
	enum PortReadEnum
	{
		ebpc,		// 回复OK>
		sdpc,		// 回复OK>
		lbpc,		// 回复C
		oneData,	// 发出一包数据回复 06为正确
	};

	//! 读取数据. 返回值 1为成功 0为失败
	int PortRead(PortReadEnum _read_enum);
	// signals:
	// 读取数据信号. 
	// 	void readyRead(QString str);
// 2024.4.8更新
signals:	
	// 接收回调返回值
	void responseReceived(const QByteArray &data);
public slots:
	void onReadyRead();
	
protected:
	//! 存储监听的数据. 没有使用监听功能可以根据需求自行修改
	//inline void setListenData(QByteArray& data) { m_Data += data; }

	//! 监听. 
	void PortListen();
	//! 清除发送buff. 
	int clearSendBuff();
	//! 清除接受buff. 
	int clearRecvBuff();
	//! 设置串口参数. 
	int setParam(void* param);
private:
    Ui::SerialCommunicationClass ui;
	QSharedPointer<QSerialPort> m_pSerialPort;	// 串口通信
	bool m_isOpen;	// 串口通信是否打开
	//QByteArray m_Data;
};

串口通信实现类cpp:

#include "SerialCommunication.h"
#include <QtSerialPort/QSerialPortInfo>
#include <qDebug>

SerialCommunication::SerialCommunication(QWidget *parent)
    : QWidget(parent)
{
    ui.setupUi(this);
	PortInit();
	connect(ui.pushButton, &QPushButton::clicked, this, &SerialCommunication::PortOpen);
}

void SerialCommunication::PortInit()
{
	m_pSerialPort = QSharedPointer<QSerialPort>(new QSerialPort);
	//获取可以用的串口
	QList<QSerialPortInfo> serialPortInfos = QSerialPortInfo::availablePorts();
	for (auto one_port : serialPortInfos)
	{
		m_pSerialPort->setPort(one_port);                      // 在对象中设置串口
		if (m_pSerialPort->open(QIODevice::ReadWrite))      // 以读写方式打开串口
		{
			ui.serial_port_comboBox->addItem(one_port.portName()); // 添加计算机中的端口
			m_pSerialPort->close();                        // 关闭
		}
		else
		{
			qDebug() << "串口打开失败,请重试";
		}
	}
	// 根据QSerialPort::NoParity枚举值赋的data
	ui.check_bit_comboBox->addItem("None", 0);
	ui.check_bit_comboBox->addItem("Even", 2);
	ui.check_bit_comboBox->addItem("Odd", 3);
}

bool SerialCommunication::PortOpen()
{
	if (ui.pushButton->text().compare(QString::fromLocal8Bit("打开串口"))==0)
	{
		ui.pushButton->setText(QString::fromLocal8Bit("关闭串口"));
	}
	else
	{
		PortClose();
		ui.pushButton->setText(QString::fromLocal8Bit("打开串口"));
		return false;
	}
	if (m_pSerialPort->isOpen())
	{
		return true;
	}
	bool isOpen = m_pSerialPort->open(QIODevice::ReadWrite);
	m_isOpen = isOpen;
	std::shared_ptr<Param> p = std::make_shared<Param>();
	p->serialPortName = ui.serial_port_comboBox->currentText();
	p->baudRate = ui.baud_rate_comboBox->currentText().toInt();//115200;
	//QSerialPort::Data8;
	p->dataBits = (QSerialPort::DataBits)ui.data_bits_comboBox->currentText().toInt();
	//QSerialPort::NoParity;
	p->parity = (QSerialPort::Parity)ui.check_bit_comboBox->currentData().toInt();
	p->stopBits = ui.stop_bit_comboBox->currentText().toInt() == 0 ? 
	QSerialPort::UnknownStopBits: QSerialPort::OneStop;//QSerialPort::UnknownStopBits;
	setParam(p.get());
	return isOpen;
}

bool SerialCommunication::PortClose()
{
	m_pSerialPort->close();
	m_isOpen = m_pSerialPort->isOpen();
	return !m_pSerialPort->isOpen();
}

int SerialCommunication::PortWrite(char* buff, unsigned int length)
{
	int data_size = m_pSerialPort->write(buff, length);
	m_pSerialPort->waitForBytesWritten(10000);
	return data_size;
}
// 2024.4.8更新
QByteArray SerialCommunication::sendSerialCommand(const QByteArray &command)
{
	QEventLoop loop;
	QTimer run_timer_;
	// 设置定时器,超时时间为10000毫秒(10秒)
	run_timer_.setSingleShot(true); // 单次触发
	run_timer_.setInterval(10000);
	// 连接定时器超时信号到QEventLoop的quit槽
	QObject::connect(&run_timer_, &QTimer::timeout, &loop, &QEventLoop::quit);
	QByteArray response;
	response.append("ERROR");
	QMetaObject::Connection m_res;
	// 当有数据来自串口时,这个信号会被触发
	m_res = connect(this, &SerialCommunication::responseReceived,[&](const QByteArray &receivedData) {
		response = receivedData;
		loop.quit();
	});
	m_pSerialPort->write(command); // 发送命令
	 // 启动定时器
	run_timer_.start();
	loop.exec(); // 进入事件循环,等待返回值
	disconnect(m_res);
	return response;
}

// 1
int SerialCommunication::PortRead(PortReadEnum _read_enum)
{
	// 关键代码获取回执消息时等待三秒未收到消息自动跳出循环qt自带函数无需自行实现
	while (m_pSerialPort->waitForReadyRead(3000))
	{
		QByteArray data = m_pSerialPort->readAll();
		if (data.size() <= 0)
			return 0;
		switch (_read_enum)
		{
		case SerialCommunication::ebpc:
		case SerialCommunication::sdpc:
			if (std::strstr(data.data(), "OK>"))
				return 1;
		case SerialCommunication::lbpc:
			if (std::strstr(data.data(), "C"))
				return 1;
		case SerialCommunication::oneData:
			if (data.at(0) == 0x06)
				return 1;
		default:
			return 0;
		}
	}
	return 0;
}

// 2
int SerialCommunication::PortRead(PortReadEnum _read_enum)
{
	QByteArray data;
	m_pSerialPort->waitForReadyRead(1);
	if (m_pSerialPort->bytesAvailable()) {//判断串口缓存区内部是否存在数据,存在数据再将数据读出来
		data = m_pSerialPort->readAll();
		if (data.size() <= 0)
			return 0;
	}
	return data;
}

void SerialCommunication::PortListen()
{
	connect(m_pSerialPort.data(), &QSerialPort::readyRead, this, [&]() {
		QByteArray data = m_pSerialPort->readAll();
		//setListenData(data); //缓存
		//emit readyRead(data); //显示
		qDebug() << data.data();
		//addLogText(data.data());
	});
}

int SerialCommunication::clearSendBuff()
{
	return m_pSerialPort->clear(QSerialPort::Output);
}

int SerialCommunication::clearRecvBuff()
{
	return m_pSerialPort->clear(QSerialPort::Input);
}

int SerialCommunication::setParam(void* param)
{
	Param* p = static_cast<Param*>(param);
	m_pSerialPort->setPortName(p->serialPortName);
	m_pSerialPort->setBaudRate(p->baudRate);
	m_pSerialPort->setDataBits(p->dataBits);
	m_pSerialPort->setParity(p->parity);
	m_pSerialPort->setStopBits(p->stopBits);
	return 0;
}

void SerialCommunication::onReadyRead()
{
	// 读取串口数据
	QByteArray data = m_pSerialPort->readAll();
	// 发送信号通知主线程
	emit responseReceived(data);
}

  • 2
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

奇树谦

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值