首页 5G技术

QT 串口通信开发实战:从入门到精通避坑指南

分类:5G技术
字数: (1836)
阅读: (8758)
内容摘要:QT 串口通信开发实战:从入门到精通避坑指南,

在使用 QT 进行嵌入式设备或者仪器仪表开发时,串口通信是绕不开的一个环节。很多初学者,包括我当年,都会在环境配置、数据收发、多线程处理等方面遇到各种问题。本文将结合我 10 年的后端架构经验,深入剖析 QT 串口开发的底层原理,并分享一些实战中总结的避坑技巧,力求帮助大家快速掌握 QT 的串口开发技术。

串口通信基础概念回顾

在深入代码之前,我们先来回顾一下串口通信的一些基本概念:

QT 串口通信开发实战:从入门到精通避坑指南
  • 串口(Serial Port): 指的是计算机上的一种接口,用于与外部设备进行串行数据传输。
  • 波特率(Baud Rate): 指串口通信的速率,单位为 bit/s,常用的波特率有 9600、115200 等。
  • 数据位(Data Bits): 指每个数据帧中实际用于传输数据的位数,常用的数据位有 5、6、7、8 位。
  • 校验位(Parity): 用于校验数据传输的正确性,常用的校验位有无校验(No Parity)、奇校验(Odd Parity)、偶校验(Even Parity)等。
  • 停止位(Stop Bits): 用于分隔不同的数据帧,常用的停止位有 1 位、1.5 位、2 位。
  • 流控制(Flow Control): 用于控制数据传输的速度,防止数据丢失,常用的流控制有硬件流控制(RTS/CTS、DTR/DSR)和软件流控制(XON/XOFF)。

理解这些概念是进行 QT 串口开发的基础。

QT 串口通信开发实战:从入门到精通避坑指南

QT 串口开发核心类:QSerialPort 和 QSerialPortInfo

QT 提供了 QSerialPortQSerialPortInfo 两个核心类,用于串口通信的实现。

QT 串口通信开发实战:从入门到精通避坑指南
  • QSerialPort: 用于串口的打开、关闭、读写数据等操作。
  • QSerialPortInfo: 用于获取系统中可用的串口信息,例如串口名称、串口描述等。

QSerialPort 的基本使用

下面是一个简单的 QSerialPort 使用示例:

QT 串口通信开发实战:从入门到精通避坑指南
#include <QCoreApplication>
#include <QSerialPort>
#include <QDebug>

int main(int argc, char *argv[]) {
    QCoreApplication a(argc, argv);

    QSerialPort serialPort;
    serialPort.setPortName("COM1"); // 设置串口名称,Linux 下例如 "/dev/ttyUSB0"
    serialPort.setBaudRate(QSerialPort::Baud115200); // 设置波特率
    serialPort.setDataBits(QSerialPort::Data8); // 设置数据位
    serialPort.setParity(QSerialPort::NoParity); // 设置校验位
    serialPort.setStopBits(QSerialPort::OneStop); // 设置停止位
    serialPort.setFlowControl(QSerialPort::NoFlowControl); // 设置流控制

    if (serialPort.open(QIODevice::ReadWrite)) { // 打开串口
        qDebug() << "Serial port opened successfully!";

        QByteArray data = "Hello, Serial Port!";
        serialPort.write(data); // 写入数据
        serialPort.waitForBytesWritten(100); // 等待数据发送完成

        QByteArray readData = serialPort.readAll(); // 读取数据
        qDebug() << "Received data:" << readData;

        serialPort.close(); // 关闭串口
        qDebug() << "Serial port closed!";
    } else {
        qDebug() << "Failed to open serial port:" << serialPort.errorString();
    }

    return a.exec();
}

QSerialPortInfo 的基本使用

可以使用 QSerialPortInfo 获取当前系统所有可用串口的信息:

#include <QCoreApplication>
#include <QSerialPortInfo>
#include <QDebug>

int main(int argc, char *argv[]) {
    QCoreApplication a(argc, argv);

    qDebug() << "Available ports:";
    const auto infos = QSerialPortInfo::availablePorts();
    for (const QSerialPortInfo &info : infos) {
        qDebug() << "Name:" << info.portName();
        qDebug() << "Description:" << info.description();
        qDebug() << "Manufacturer:" << info.manufacturer();
        qDebug() << "System Location:" << info.systemLocation();
        qDebug() << "Busy:" << info.isBusy();
        qDebug() << "------------------";
    }

    return a.exec();
}

QT 串口数据接收的线程处理

串口数据接收通常需要使用多线程,避免阻塞主线程。QT 提供了多种线程处理方式,例如 QThreadQtConcurrent 等。这里我们使用 QThread 方式,因为可以更灵活地控制线程的生命周期。

#include <QObject>
#include <QThread>
#include <QSerialPort>
#include <QDebug>

class SerialPortWorker : public QObject {
    Q_OBJECT
public:
    SerialPortWorker(QSerialPort *port) : m_serialPort(port) {}

signals:
    void dataReceived(const QByteArray &data);

public slots:
    void process() {
        if (m_serialPort->isOpen()) {
            while (true) {
                if (m_serialPort->waitForReadyRead(100)) { // 等待数据可读,超时 100ms
                    QByteArray data = m_serialPort->readAll();
                    emit dataReceived(data);
                } else {
                    // 超时处理,例如检测串口是否断开
                    if(m_serialPort->error() != QSerialPort::NoError) {
                        qDebug() << "Serial port error:" << m_serialPort->errorString();
                        break; // 退出循环,结束线程
                    }
                }
            }
        } else {
            qDebug() << "Serial port is not open!";
        }
        qDebug() << "SerialPortWorker thread finished.";
    }

private:
    QSerialPort *m_serialPort;
};

class MyClass : public QObject {
    Q_OBJECT
public:
    MyClass(QObject *parent = nullptr) : QObject(parent) {
        m_serialPort = new QSerialPort(this);
        m_serialPort->setPortName("COM1");
        m_serialPort->setBaudRate(QSerialPort::Baud115200);
        if (m_serialPort->open(QIODevice::ReadWrite)) {
            m_worker = new SerialPortWorker(m_serialPort);
            m_workerThread = new QThread(this);
            m_worker->moveToThread(m_workerThread);
            connect(m_workerThread, &QThread::started, m_worker, &SerialPortWorker::process);
            connect(m_worker, &SerialPortWorker::dataReceived, this, &MyClass::handleData);
            connect(m_workerThread, &QThread::finished, m_worker, &QObject::deleteLater);
            connect(this, &MyClass::destroyed, m_workerThread, &QThread::quit);
            connect(this, &MyClass::destroyed, m_workerThread, &QObject::deleteLater);
            m_workerThread->start();
        } else {
            qDebug() << "Failed to open serial port:" << m_serialPort->errorString();
        }
    }

    ~MyClass() {
        m_serialPort->close();
    }

public slots:
    void handleData(const QByteArray &data) {
        qDebug() << "Received data from thread:" << data;
        // 在这里处理接收到的数据
    }

private:
    QSerialPort *m_serialPort;
    SerialPortWorker *m_worker;
    QThread *m_workerThread;
};

#include "main.moc" // 包含 moc 文件,否则会报链接错误
int main(int argc, char *argv[]) {
    QCoreApplication a(argc, argv);
    MyClass myObject;

    return a.exec();
}

#include "SerialPortWorker.moc"  //单独生成一个 moc 文件,避免循环依赖

注意: 必须为 SerialPortWorker 类生成 moc 文件,否则会报链接错误。可以使用 moc SerialPortWorker.h -o SerialPortWorker.moc 命令生成。

实战避坑经验总结

  • 串口权限问题: 在 Linux 系统下,需要确保当前用户具有访问串口的权限。可以使用 sudo chmod 666 /dev/ttyUSB0 命令修改串口权限,或者将用户添加到 dialout 组。
  • 波特率设置错误: 确保 QT 程序中设置的波特率与串口设备的波特率一致,否则会导致数据乱码。
  • 数据接收不完整: waitForReadyRead() 函数的超时时间需要根据实际情况调整。如果超时时间过短,可能会导致数据接收不完整。可以适当增加超时时间,或者使用信号槽机制,在接收到数据后立即处理。
  • 数据格式转换: 串口接收到的数据通常是 QByteArray 格式,需要根据实际情况转换为其他格式,例如 QStringintfloat 等。需要注意字符编码问题,可以使用 QString::fromLatin1()QString::fromUtf8() 等函数进行转换。
  • 线程安全问题: 在多线程环境下,需要注意线程安全问题。例如,避免多个线程同时访问同一个 QSerialPort 对象。可以使用互斥锁(QMutex)或者原子操作(QAtomicInt)来保证线程安全。
  • 信号槽机制: 利用 QT 的信号槽机制可以很方便的实现数据接收后的处理,例如更新 UI 界面。注意,信号槽连接需要在同一个线程中进行,或者使用 Qt::QueuedConnection 类型进行跨线程连接。

结语

希望这篇 QT(c++)开发串口通信的自学笔记能够帮助大家少走弯路,快速掌握 QT 串口开发技术。串口通信只是嵌入式开发中的一个环节,还需要掌握 TCP/IP 网络编程、Modbus 协议、CAN 总线等技术。学习永无止境,希望大家能够不断学习,共同进步。

QT 串口通信开发实战:从入门到精通避坑指南

转载请注明出处: 代码一只喵

本文的链接地址: http://m.acea2.store/blog/079366.SHTML

本文最后 发布于2026-04-27 05:24:16,已经过了0天没有更新,若内容或图片 失效,请留言反馈

()
您可能对以下文章感兴趣
评论
  • 柠檬精 11 小时前
    数据格式转换那块很有用,之前一直被编码问题困扰。
  • 单身狗 6 天前
    写得真不错,解决了我的串口数据接收问题!感谢楼主!
  • 秃头程序员 2 天前
    数据格式转换那块很有用,之前一直被编码问题困扰。
  • 背锅侠 1 天前
    写得真不错,解决了我的串口数据接收问题!感谢楼主!
  • 秃头程序员 1 天前
    写得真不错,解决了我的串口数据接收问题!感谢楼主!