QT技巧之快速搭建串口收发平台

Qt 串口模块(QtSerialPort)是 Qt 框架中用于与串口设备通信的组件,提供了跨平台的串口访问能力。

QtSerialPort 模块包含两个核心类:

  1. QSerialPort

    • 提供对串口的配置和数据传输功能(打开 / 关闭串口、读写数据)。
    • 支持异步和同步通信模式。
  2. QSerialPortInfo

    • 用于枚举系统中可用的串口设备(如 COM1、/dev/ttyUSB0)。
    • 获取串口详细信息(描述、制造商、序列号等)。

一、环境配置

  1. 安装 Qt

    下载并安装 Qt SDK(包含 Qt Creator IDE),推荐 Qt 5.15 或更高版本。

  2. 启用串口模块

    在项目的.pro文件中添加:

    复制代码
    QT += serialport
  3. 包含必要头文件

    复制代码
    #include <QSerialPort>        // 串口类
    #include <QSerialPortInfo>    // 串口信息类

二、界面设计(UI 文件)

使用 Qt Designer 设计界面,通常包含:

  1. 串口设置区

    • 串口下拉框(选择 COM 口)
    • 波特率下拉框(如 9600、115200)
    • 数据位、停止位、校验位下拉框
    • 打开 / 关闭串口按钮
  2. 数据收发区

    • 接收文本框(显示接收到的数据)
    • 发送文本框(输入要发送的数据)
    • 发送按钮

三、核心代码实现

1. 初始化串口管理类
复制代码
// mainwindow.h
#include <QMainWindow>
#include <QSerialPort>
#include <QSerialPortInfo>

QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

private slots:
    void on_pushButton_open_clicked();  // 打开/关闭串口按钮槽函数
    void on_pushButton_send_clicked();  // 发送数据按钮槽函数
    void readData();                   // 读取串口数据的槽函数

private:
    Ui::MainWindow *ui;
    QSerialPort *serialPort;           // 串口对象指针
};

2. 构造函数初始化

复制代码
// mainwindow.cpp
MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    serialPort = new QSerialPort(this);
    
    // 扫描并添加可用串口到下拉框
    foreach (const QSerialPortInfo &info, QSerialPortInfo::availablePorts()) {
        ui->comboBox_port->addItem(info.portName());
    }
    
    // 添加常用波特率选项
    ui->comboBox_baud->addItems({"9600", "115200", "19200", "38400"});
    
    // 设置默认选项
    ui->comboBox_baud->setCurrentText("115200");
    ui->comboBox_dataBits->setCurrentText("8");
    ui->comboBox_stopBits->setCurrentText("1");
    ui->comboBox_parity->setCurrentText("None");
    
    // 连接信号和槽
    connect(serialPort, &QSerialPort::readyRead, this, &MainWindow::readData);
}
3. 打开 / 关闭串口功能
复制代码
void MainWindow::on_pushButton_open_clicked()
{
    if (serialPort->isOpen()) {
        // 关闭串口
        serialPort->close();
        ui->pushButton_open->setText("打开串口");
        ui->statusBar->showMessage("串口已关闭");
    } else {
        // 设置串口参数
        serialPort->setPortName(ui->comboBox_port->currentText());
        serialPort->setBaudRate(ui->comboBox_baud->currentText().toInt());
        
        // 设置数据位
        switch (ui->comboBox_dataBits->currentText().toInt()) {
            case 5: serialPort->setDataBits(QSerialPort::Data5); break;
            case 6: serialPort->setDataBits(QSerialPort::Data6); break;
            case 7: serialPort->setDataBits(QSerialPort::Data7); break;
            case 8: serialPort->setDataBits(QSerialPort::Data8); break;
            default: serialPort->setDataBits(QSerialPort::Data8); break;
        }
        
        // 设置停止位和校验位(类似逻辑)
        // ...
        
        // 尝试打开串口
        if (serialPort->open(QIODevice::ReadWrite)) {
            ui->pushButton_open->setText("关闭串口");
            ui->statusBar->showMessage("串口已打开");
        } else {
            ui->statusBar->showMessage("串口打开失败: " + serialPort->errorString());
        }
    }
}
4. 数据接收功能
复制代码
void MainWindow::readData()
{
    QByteArray data = serialPort->readAll();
    // 显示接收到的数据
    ui->textEdit_receive->append(data);
}
5. 数据发送功能
复制代码
void MainWindow::on_pushButton_send_clicked()
{
    if (serialPort->isOpen()) {
        QString sendData = ui->textEdit_send->toPlainText();
        serialPort->write(sendData.toUtf8());
        ui->statusBar->showMessage("数据已发送");
    } else {
        ui->statusBar->showMessage("请先打开串口");
    }
}

四、完整实现示例

以下是一个完整的串口收发平台代码:

复制代码
// mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QSerialPort>
#include <QSerialPortInfo>

QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

private slots:
    void on_pushButton_open_clicked();
    void on_pushButton_send_clicked();
    void readData();

private:
    Ui::MainWindow *ui;
    QSerialPort *serialPort;
};
#endif // MAINWINDOW_H

// mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    serialPort = new QSerialPort(this);

    // 扫描可用串口
    foreach (const QSerialPortInfo &info, QSerialPortInfo::availablePorts()) {
        ui->comboBox_port->addItem(info.portName());
    }

    // 设置波特率选项
    ui->comboBox_baud->addItems({"9600", "115200", "19200", "38400"});
    ui->comboBox_baud->setCurrentText("115200");

    // 设置数据位选项
    ui->comboBox_dataBits->addItems({"5", "6", "7", "8"});
    ui->comboBox_dataBits->setCurrentText("8");

    // 设置停止位选项
    ui->comboBox_stopBits->addItems({"1", "1.5", "2"});
    ui->comboBox_stopBits->setCurrentText("1");

    // 设置校验位选项
    ui->comboBox_parity->addItems({"None", "Even", "Odd", "Space", "Mark"});
    ui->comboBox_parity->setCurrentText("None");

    // 连接信号和槽
    connect(serialPort, &QSerialPort::readyRead, this, &MainWindow::readData);
}

MainWindow::~MainWindow()
{
    if (serialPort->isOpen()) {
        serialPort->close();
    }
    delete ui;
}

void MainWindow::on_pushButton_open_clicked()
{
    if (serialPort->isOpen()) {
        serialPort->close();
        ui->pushButton_open->setText("打开串口");
        ui->statusBar->showMessage("串口已关闭");
    } else {
        // 设置串口参数
        serialPort->setPortName(ui->comboBox_port->currentText());
        serialPort->setBaudRate(ui->comboBox_baud->currentText().toInt());

        // 设置数据位
        switch (ui->comboBox_dataBits->currentText().toInt()) {
            case 5: serialPort->setDataBits(QSerialPort::Data5); break;
            case 6: serialPort->setDataBits(QSerialPort::Data6); break;
            case 7: serialPort->setDataBits(QSerialPort::Data7); break;
            case 8: serialPort->setDataBits(QSerialPort::Data8); break;
            default: serialPort->setDataBits(QSerialPort::Data8); break;
        }

        // 设置停止位
        if (ui->comboBox_stopBits->currentText() == "1") {
            serialPort->setStopBits(QSerialPort::OneStop);
        } else if (ui->comboBox_stopBits->currentText() == "1.5") {
            serialPort->setStopBits(QSerialPort::OneAndHalfStop);
        } else if (ui->comboBox_stopBits->currentText() == "2") {
            serialPort->setStopBits(QSerialPort::TwoStop);
        }

        // 设置校验位
        if (ui->comboBox_parity->currentText() == "None") {
            serialPort->setParity(QSerialPort::NoParity);
        } else if (ui->comboBox_parity->currentText() == "Even") {
            serialPort->setParity(QSerialPort::EvenParity);
        } else if (ui->comboBox_parity->currentText() == "Odd") {
            serialPort->setParity(QSerialPort::OddParity);
        }

        // 打开串口
        if (serialPort->open(QIODevice::ReadWrite)) {
            ui->pushButton_open->setText("关闭串口");
            ui->statusBar->showMessage("串口已打开");
        } else {
            ui->statusBar->showMessage("串口打开失败: " + serialPort->errorString());
        }
    }
}

void MainWindow::on_pushButton_send_clicked()
{
    if (serialPort->isOpen()) {
        QString sendData = ui->textEdit_send->toPlainText();
        serialPort->write(sendData.toUtf8());
        ui->statusBar->showMessage("数据已发送");
    } else {
        ui->statusBar->showMessage("请先打开串口");
    }
}

void MainWindow::readData()
{
    QByteArray data = serialPort->readAll();
    ui->textEdit_receive->append(data);
}

// main.cpp
#include "mainwindow.h"
#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    MainWindow w;
    w.show();
    return a.exec();
}

五、关键特性技巧

1. 异步通信
  • 通过信号槽机制处理数据接收(推荐方式):

    复制代码
    connect(&serial, &QSerialPort::readyRead, this, [&]() {
        // 处理接收到的数据
    });
2. 同步通信
  • 使用waitForReadyRead()waitForBytesWritten()等阻塞函数(不推荐在 UI 线程中使用):

    复制代码
    if (serial.waitForReadyRead(1000)) {
        QByteArray data = serial.readAll();
    }
3. 错误处理
  • 通过error()信号或errorString()获取错误信息:

    复制代码
    connect(&serial, &QSerialPort::errorOccurred, this, [](QSerialPort::SerialPortError error) {
        qDebug() << "串口错误:" << error;
    });
4. 超时设置
  • 可设置读写超时时间(仅对同步模式有效):

    复制代码
    serial.setReadBufferSize(1024);  // 设置接收缓冲区大小

六、注意事项

  1. 跨平台兼容性

    QtSerialPort 支持 Windows、Linux、macOS 等,但不同系统的串口名称格式不同(如 COM1、/dev/ttyUSB0)。

  2. 线程安全

    串口数据接收是异步的,在复杂应用中需注意线程安全问题(可使用信号槽机制)。

  3. 性能优化

    大量数据收发时,需考虑缓冲区处理和界面刷新频率,避免 UI 卡顿。

相关推荐
猪蹄手10 分钟前
单例模式详细讲解
开发语言·c++·单例模式
失因20 分钟前
H3CNE 综合实验二解析与实施指南
运维·开发语言·网络·智能路由器·php
晓131336 分钟前
JavaScript进阶篇——第七章 原型与构造函数核心知识
开发语言·javascript·ecmascript
暴躁茹1 小时前
Qt 将触摸事件转换为鼠标事件(Qt4和Qt5及以上版本)
开发语言·qt·计算机外设
我是唐青枫2 小时前
C#.NET 泛型详解
开发语言·c#·.net
SoniaChen332 小时前
Rust基础[part4]_基本类型,所有权
开发语言·后端·rust
晓13132 小时前
JavaScript进阶篇——第八章 原型链、深浅拷贝与原型继承全解析
开发语言·javascript·原型模式
Yasin Chen2 小时前
C# StringBuilder源码分析
开发语言·c#
电饭叔2 小时前
《python语言程序设计》2018版第8章8题编写函数实现二进制转十进制(字符串变整数)!!整数没法进行下标
开发语言·python
R-G-B2 小时前
【46】MFC入门到精通——MFC显示实时时间,获取系统当前时间GetCurrentTime()、获取本地时间GetLocalTime()
c++·mfc·mfc显示实时时间·mfc获取系统当前时间·getcurrenttime·getlocaltime