Qt5实现Windows平台串口通信

一、环境配置

  1. 开发环境: Qt 5.15.2+ (MSVC 2019编译器) Windows 10/11

  2. 依赖配置

    # .pro文件配置
    QT += serialport widgets
    LIBS += -lQt5SerialPort
    

二、界面设计

1. 控件布局

<!-- mainwindow.ui -->
<widget class="QMainWindow" name="MainWindow">
    <layout class="QGridLayout">
        <!-- 串口配置区 -->
        <item row="0" column="0">
            <QLabel text="串口号"/>
        </item>
        <item row="0" column="1">
            <QComboBox objectName="cmbPort"/>
        </item>
        
        <!-- 波特率设置 -->
        <item row="1" column="0">
            <QLabel text="波特率"/>
        </item>
        <item row="1" column="1">
            <QComboBox objectName="cmbBaudRate"/>
        </item>
        
        <!-- 控制按钮 -->
        <item row="2" column="0" colspan="2">
            <QPushButton text="打开串口" objectName="btnOpen"/>
        </item>
        
        <!-- 数据收发区 -->
        <item row="3" column="0">
            <QPlainTextEdit objectName="txtSend"/>
        </item>
        <item row="3" column="1">
            <QPlainTextEdit objectName="txtRecv"/>
        </item>
        
        <!-- 发送按钮 -->
        <item row="4" column="0" colspan="2">
            <QPushButton text="发送数据" objectName="btnSend"/>
        </item>
    </layout>
</widget>

三、核心代码实现

1. 头文件定义

// mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QSerialPort>

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_btnOpen_clicked();
    void on_btnSend_clicked();
    void serialRead();

private:
    void initSerialPort();
    void populatePorts();

    Ui::MainWindow *ui;
    QSerialPort *serial;
};

#endif // MAINWINDOW_H

2. 实现文件

// mainwindow.cpp
#include "mainwindow.h"
#include <QSerialPortInfo>
#include <QByteArray>

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    
    // 初始化串口
    serial = new QSerialPort(this);
    initSerialPort();
    populatePorts();

    // 信号槽连接
    connect(ui->btnOpen, &QPushButton::clicked, this, &MainWindow::on_btnOpen_clicked);
    connect(ui->btnSend, &QPushButton::clicked, this, &MainWindow::on_btnSend_clicked);
    connect(serial, &QSerialPort::readyRead, this, &MainWindow::serialRead);
}

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

void MainWindow::initSerialPort()
{
    // 配置默认参数
    serial->setBaudRate(QSerialPort::Baud9600);
    serial->setDataBits(QSerialPort::Data8);
    serial->setParity(QSerialPort::NoParity);
    serial->setStopBits(QSerialPort::OneStop);
    serial->setFlowControl(QSerialPort::NoFlowControl);
}

void MainWindow::populatePorts()
{
    ui->cmbPort->clear();
    foreach(const QSerialPortInfo &info, QSerialPortInfo::availablePorts()) {
        ui->cmbPort->addItem(info.portName());
    }
}

void MainWindow::on_btnOpen_clicked()
{
    if(serial->isOpen()) {
        serial->close();
        ui->btnOpen->setText("打开串口");
    } else {
        serial->setPortName(ui->cmbPort->currentText());
        if(serial->open(QIODevice::ReadWrite)) {
            ui->btnOpen->setText("关闭串口");
        } else {
            QMessageBox::critical(this, "错误", serial->errorString());
        }
    }
}

void MainWindow::on_btnSend_clicked()
{
    QByteArray data = ui->txtSend->toPlainText().toUtf8();
    if(!data.isEmpty()) {
        qint64 bytes = serial->write(data);
        if(bytes == -1) {
            QMessageBox::warning(this, "发送失败", serial->errorString());
        }
    }
}

void MainWindow::serialRead()
{
    QByteArray buffer = serial->readAll();
    if(!buffer.isEmpty()) {
        ui->txtRecv->appendPlainText(QString::fromUtf8(buffer));
    }
}

四、功能扩展

1. 十六进制支持

// 在发送函数中添加HEX转换
void MainWindow::on_btnSend_clicked()
{
    QString text = ui->txtSend->toPlainText();
    QByteArray data;
    
    if(ui->chkHexSend->isChecked()) {
        bool ok;
        data = QByteArray::fromHex(text.toUtf8(), &ok);
        if(!ok) {
            QMessageBox::warning(this, "错误", "无效的HEX格式");
            return;
        }
    } else {
        data = text.toUtf8();
    }
    
    serial->write(data);
}

2. 数据校验

// 在配置函数中添加校验位设置
void MainWindow::setParity(QSerialPort::Parity parity) {
    serial->setParity(parity);
}

// 示例:CRC16校验
quint16 MainWindow::calculateCRC(const QByteArray &data) {
    quint16 crc = 0xFFFF;
    for(auto byte : data) {
        crc ^= (quint16)byte << 8;
        for(int i=0; i<8; i++) {
            if(crc & 0x8000) crc = (crc << 1) ^ 0x1021;
            else crc <<= 1;
        }
    }
    return crc;
}

五、调试技巧

  1. 虚拟串口测试: 使用VSPD创建虚拟串口对(如COM3 <-> COM4) 在程序中选择不同端口测试双向通信

  2. 数据监控

    // 添加调试输出
    qDebug() << "Received:" << buffer.toHex(' ') << "Length:" << buffer.size();
    
  3. 流量控制

    // 启用硬件流控制
    serial->setFlowControl(QSerialPort::HardwareControl);
    

六、编译与运行

  1. 构建配置

    qmake mainwindow.pro
    make
    
  2. 运行验证: 确保COM端口未被占用 通过设备管理器确认串口参数匹配

参考代码 Qt5串口通信程序 www.youwenfan.com/contentcnl/69949.html

七、常见问题处理

现象 解决方案
无法打开串口 检查端口号是否正确,关闭占用程序
数据丢失 增加接收缓冲区,优化读取频率
显示乱码 统一使用UTF-8编码,添加校验逻辑
高波特率不稳定 降低波特率,检查线缆质量

八、工程结构

SerialDemo/
├── Src/
│   ├── main.cpp
│   ├── mainwindow.cpp
│   └── mainwindow.h
├── Res/
│   └── mainwindow.ui
└── SerialDemo.pro
posted @ 2025-11-19 11:50  老夫写代码  阅读(5)  评论(0)    收藏  举报