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);
}