Qt QSerialPort串口通信

1. 示例

在这里插入图片描述

2. 轮询电脑串口设备

    // 查询所有串口设备
    QList<QSerialPortInfo> serialList = QSerialPortInfo::availablePorts();

    QStringList serialPortNameList;
    foreach(const QSerialPortInfo &info,QSerialPortInfo::availablePorts())
    {
        serialPortNameList << info.portName();
        qDebug()<<"serialPortName: "<<info.portName();
    }
    // 展示在下拉列表中
    ui->comboBox->addItems(serialPortNameList);

注意,要使用QSerialPort需要在pro中添加 QT += serialport

3. 串口类

	m_serialPort = new QSerialPort(this);
    connect(m_serialPort, &QSerialPort::readyRead, this, &MainWindow::handleReadyRead);
    connect(m_serialPort, &QSerialPort::errorOccurred, this, &MainWindow::handleError);

readRead 事件: 异步接收串口的数据
errorOccurred 事件: 异常

void MainWindow::handleReadyRead()
{
    m_recvData = m_serialPort->readAll();
    writeMsg("异步收到:" + QString::fromUtf8(m_recvData));

    emit serialDataReadFinished();  // 自定义的事件,为了配合通知异步等待完成
}

void MainWindow::handleError(QSerialPort::SerialPortError serialPortError)
{
    if (serialPortError == QSerialPort::ReadError)
    {
        QString error = QString("An I/O error occurred while reading "
                                        "the data from port %1, error: %2")
                            .arg(m_serialPort->portName())
                            .arg(m_serialPort->errorString());
        writeMsg(error);
    }
}

4. 发送串口数据

4.1 发送串口数据后,不等待, 由handleReadyRead异步接收来自串口的数据

void MainWindow::on_sendMsgButton_clicked()
{
    QString sendData = ui->textEdit->toPlainText();
    writeMsg("发送:" + sendData);

    m_serialPort->write(sendData.toUtf8(), sendData.toUtf8().size());
}

4.2 发送串口数据后,要同步等待串口返回数据

void MainWindow::on_sendMsgButton_clicked()
{
    QString sendData = ui->textEdit->toPlainText();
    writeMsg("发送:" + sendData);

    m_serialPort->write(sendData.toUtf8(), sendData.toUtf8().size());

	if(!m_serialPort->waitForReadyRead(5000))
	{
		writeMsg("等待回复超时");
		return;
	}
	
	QByteArray recvData = m_serialPort->readAll();
	writeMsg("同步收到:" + QString::fromUtf8(recvData));
}

但是,如果我们监听了QSerialPortreadRead 事件,这里waitForReadyRead是等不到串口返回的数据的,一直是超时。



有时我们给串口发送消息后需要及时收到串口返回数据,又要在没有发送消息时,采用异步监听的方式实时接收串口上报的数据。
所以:
a. 需要同步等待读取串口数据;
b. 需要监听readRead 事件,实时接收数据



解决方法 一:

我们可以自己写一个异步等待的方法,使异步转同步

bool MainWindow::waiteForReadyRead(int msec)
{
    QEventLoop eventloop;
    QObject::connect(this, &MainWindow::serialDataReadFinished, &eventloop, &QEventLoop::quit);

    bool isTimeout = false;
    QTimer::singleShot(msec, &eventloop, [&](){
        eventloop.quit();
        isTimeout = true;
    });

    eventloop.exec(QEventLoop::AllEvents);
    return !isTimeout;
}
  • serialDataReadFinished事件是我们自定义的事件,在handleReadyRead槽函数中收到串口数据后,发出此事件通知异步等待结束;
  • QTimer::singleShot计时,超时后,异步等待也结束;
  • 返回值,如果是收到serialDataReadFinished事件结束的异步等待,则是正常结束,返回true;如果是超时结束,则是异常结束,返回false.

于是,发送串口数据后,需要等待结果的,可以写成这样:

void MainWindow::on_sendMsgButton_2_clicked()
{
    QString sendData = ui->textEdit->toPlainText();
    writeMsg("发送:" + sendData);

    m_serialPort->write(sendData.toUtf8(), sendData.toUtf8().size());
    if(!waiteForReadyRead(5000))	// 自定义的异步等待函数
    {
        writeMsg("等待回复超时");
    }
    else
    {
        writeMsg("同步收到: " + m_recvData);
    }
}


解决方法 二:

write之前 disconnect 断开 readRead 事件的连接, 使用串口类自带的 waitForReadyRead 等待串口数据,然后读取串口数据。读完后再次使用 connect 连接 readRead 事件。

void MainWindow::on_sendMsgButton_2_clicked()
{
    QString sendData = ui->textEdit->toPlainText();
    writeMsg("发送:" + sendData);
	
	// 断开readyRead事件
	disconnect(m_serialPort, &QSerialPort::readyRead, this, &MainWindow::handleReadyRead);
	
    m_serialPort->write(sendData.toUtf8(), sendData.toUtf8().size());
    if(!m_serialPort->waitForReadyRead(5000))	// 串口类自带的等待函数
    {
        writeMsg("等待回复超时");
    }
    else
    {
    	m_recvData = m_serialPort->readAll();
        writeMsg("同步收到: " + m_recvData);
    }
    
    // 重新连接readyRead事件
	connect(m_serialPort, &QSerialPort::readyRead, this, &MainWindow::handleReadyRead);
}




以上完整代码:QSerialPortTest



5. 测试

下载一个虚拟串口工具 Virtual Serial Port,创建一个虚拟串口对,例如我创建的串口对是 COM4 和 COM5:
在这里插入图片描述
创建串口对后, 在系统设备管理器中,能看到:

在这里插入图片描述
编译测试程序后, 打开两个进程,一个选COM4,一个选COM5:
在这里插入图片描述

  • 5
    点赞
  • 38
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Qt QSerialPort 是一个用于串口通信的类库,可以方便地实现串口数据的读取和写入。下面是一个基本的读取串口数据的示例代码: ```cpp #include <QCoreApplication> #include <QtSerialPort/QSerialPort> #include <QDebug> int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); // 创建串口对象 QSerialPort serial; // 设置串口号 serial.setPortName("/dev/ttyUSB0"); // 设置波特率 serial.setBaudRate(QSerialPort::Baud9600); // 设置数据位 serial.setDataBits(QSerialPort::Data8); // 设置奇偶校验 serial.setParity(QSerialPort::NoParity); // 设置停止位 serial.setStopBits(QSerialPort::OneStop); // 设置流控制 serial.setFlowControl(QSerialPort::NoFlowControl); // 打开串口 if(!serial.open(QIODevice::ReadOnly)) { qDebug() << "无法打开串口"; return a.exec(); } // 读取串口数据 QByteArray buffer; while(serial.waitForReadyRead(100)) { buffer += serial.readAll(); } // 输出读取到的数据 qDebug() << buffer; // 关闭串口 serial.close(); return a.exec(); } ``` 在上面的代码中,我们首先创建了一个 QSerialPort 对象,并设置了串口号、波特率、数据位、奇偶校验、停止位和流控制等参数。然后使用 `open()` 函数打开串口,如果打开失败,则输出错误信息并退出程序。接着使用 `waitForReadyRead()` 函数等待串口有数据可读,然后使用 `readAll()` 函数读取数据并存储在 `buffer` 中。最后输出读取到的数据并关闭串口。 需要注意的是,在使用 `waitForReadyRead()` 函数时,我们设置了一个超时时间,如果在这个时间内没有数据可读,则函数会返回 false,退出循环。这个超时时间可以根据需要进行调整。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值