基于Qt的串口调试助手

项目名称:基于Qt的串口调试助手

项目描述

开发了一款跨平台的串口通信调试工具,支持多种波特率、数据位、校验位配置,实现数据的收发、十六进制显示与发送、数据清空等核心功能。
技术栈

Qt/C++、QSerialPort、QTimer、Qt Designer
主要职责/实现功能

设计图形用户界面,实现串口参数(串口号、波特率、数据位等)的动态配置

实现串口数据的异步接收与实时显示,支持文本/十六进制两种显示模式

实现数据发送功能,支持十六进制格式发送

添加系统时间/日期实时显示,提升用户体验

通过定时器实现串口数据的轮询读取,保证数据不丢失
项目成果

工具运行稳定,可满足嵌入式开发中的基本串口调试需求。
软件架构图

界面布局:

完整代码:

cpp文件:

cpp 复制代码
// mainwindow.h 对应的实现文件
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QSerialPort>
#include <QSerialPortInfo>

// 构造函数:初始化界面、定时器、串口参数列表
MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    // 1. 创建并启动系统时间刷新定时器
    timer = new QTimer(this);
    timer->start();                     // 启动定时器(默认间隔)
    connect(timer, SIGNAL(timeout()), this, SLOT(SetTime()));

    // 2. 显示当前日期(格式:2025年01月01日 星期三)
    QDate dateNow = QDate::currentDate();
    ui->label_date->setText(QString("日期:%1").arg(dateNow.toString("yyyy年MM月dd日 dddd")));
    
    // 3. 初始化串口状态标签
    ui->label_serialPortStaut->setText("串口状态:关闭");

    // 4. 创建串口读取定时器(每200ms检查一次数据)
    ReadTimer = new QTimer(this);
    ReadTimer->setInterval(200);        // 设置时间间隔200毫秒
    connect(ReadTimer, SIGNAL(timeout()), this, SLOT(ReadCom()));

    // 5. 初始状态下发送按钮不可用(串口未打开)
    ui->pushButton_send->setEnabled(false);
    
    // 6. 默认勾选十六进制显示和发送
    ui->checkBox_resieveHex->setCheckState(Qt::Checked);
    ui->checkBox_sendHex->setCheckState(Qt::Checked);

    // 7. 初始化串口参数下拉框
    QStringList portList;      // 串口号列表
    QStringList baudList;      // 波特率列表
    QStringList parityList;    // 校验位列表
    QStringList dataBitsList;  // 数据位列表
    QStringList stopBitsList;  // 停止位列表

    // 添加常用串口号(COM1 ~ COM10)
    portList << "COM1" << "COM2" << "COM3" << "COM4" << "COM5" << "COM6"
             << "COM7" << "COM8" << "COM9" << "COM10";
    ui->comboBox_portName->addItems(portList);
    ui->comboBox_portName->setCurrentIndex(0);  // 默认选中COM1

    // 添加常用波特率(从50到256000)
    baudList << "50" << "75" << "100" << "134" << "150" << "200" << "300"
             << "600" << "1200" << "1800" << "2400" << "4800" << "9600"
             << "14400" << "19200" << "38400" << "56000" << "57600"
             << "76800" << "115200" << "128000" << "256000";
    ui->comboBox_baudRate->addItems(baudList);
    ui->comboBox_baudRate->setCurrentIndex(12);  // 默认选中9600(索引12)

    // 校验位:无、奇、偶
    parityList << "无" << "奇" << "偶";
    ui->comboBox_parity->addItems(parityList);
    ui->comboBox_parity->setCurrentIndex(0);     // 默认无校验

    // 数据位:5、6、7、8
    dataBitsList << "5" << "6" << "7" << "8";
    ui->comboBox_dataBits->addItems(dataBitsList);
    ui->comboBox_dataBits->setCurrentIndex(3);   // 默认8位数据

    // 停止位:1、1.5、2
    stopBitsList << "1" << "1.5" << "2";
    ui->comboBox_stopBits->addItems(stopBitsList);
    ui->comboBox_stopBits->setCurrentIndex(0);   // 默认1位停止位
}

// 析构函数:关闭串口并释放UI资源
MainWindow::~MainWindow()
{
    if (serial && serial->isOpen())
        serial->close();    // 关闭串口
    delete ui;              // 释放UI对象
}

// 定时器槽函数:更新时间显示
void MainWindow::SetTime()
{
    QTime timeNow = QTime::currentTime();
    ui->label_time->setText(QString("时间:%1").arg(timeNow.toString()));
}

// 发送按钮点击槽函数
void MainWindow::on_pushButton_send_clicked()
{
    if (!serial->isOpen())  // 串口未打开则直接返回
        return;
    this->WriteCom();       // 调用实际发送函数
}

// 打开/关闭串口按钮槽函数
void MainWindow::on_pushButton_serialPortOprate_clicked()
{
    // 当前状态为"打开串口",准备打开
    if (ui->pushButton_serialPortOprate->text() == "打开串口")
    {
        QString portName = ui->comboBox_portName->currentText();  // 获取选择的串口号
        serial = new QSerialPort(portName);                       // 创建串口对象

        // 尝试以读写方式打开串口
        if (serial->open(QIODevice::ReadWrite))
        {
            serial->flush();                                      // 清空缓冲区
            // 设置波特率(从下拉框获取字符串并转为整数)
            serial->setBaudRate((QSerialPort::BaudRate)ui->comboBox_baudRate->currentText().toInt());
            // 设置数据位
            serial->setDataBits((QSerialPort::DataBits)ui->comboBox_dataBits->currentText().toInt());
            // 设置校验位(根据索引映射:0->无,1->奇,2->偶)
            serial->setParity((QSerialPort::Parity)ui->comboBox_parity->currentIndex());
            // 设置停止位(根据索引映射:0->1,1->1.5,2->2)
            serial->setStopBits((QSerialPort::StopBits)ui->comboBox_stopBits->currentIndex());
            // 关闭流控制(硬件/软件流控都不用)
            serial->setFlowControl(QSerialPort::NoFlowControl);

            // 更新界面状态
            ui->pushButton_serialPortOprate->setText("关闭串口");
            ui->label_serialPortStaut->setText("串口状态:打开");
            this->ReadTimer->start();      // 启动读取定时器
            ui->pushButton_send->setEnabled(true);  // 启用发送按钮
        }
    }
    else  // 当前状态为"关闭串口",执行关闭操作
    {
        if (serial && serial->isOpen())
            serial->close();               // 关闭串口
        ui->pushButton_serialPortOprate->setText("打开串口");
        ui->label_serialPortStaut->setText("串口状态:关闭");
        this->ReadTimer->stop();           // 停止读取定时器
        ui->pushButton_send->setDisabled(true);  // 禁用发送按钮
    }
}

// 定时读取串口数据(每200ms调用一次)
void MainWindow::ReadCom()
{
    // 如果没有数据可读,直接返回
    if (serial->bytesAvailable() <= 0)
        return;
    
    QByteArray buffer = serial->readAll();  // 读取所有可用数据
    
    // 根据十六进制显示开关决定显示格式
    if (IsHexReceive)   // 十六进制模式
    {
        QString hexData = buffer.toHex();   // 将字节数组转为十六进制字符串
        ui->textEdit->append(QString("接收:%1 时间:%2")
                               .arg(hexData)
                               .arg(QTime::currentTime().toString("HH:mm:ss")));
    }
    else                // 普通文本模式
    {
        QString normalData = QString(buffer);  // 直接按文本解释
        ui->textEdit->append(QString("接收:%1 时间:%2")
                               .arg(normalData)
                               .arg(QTime::currentTime().toString("HH:mm:ss")));
    }
}

// 发送数据函数
void MainWindow::WriteCom()
{
    QString str = ui->lineEdit->text();  // 获取发送框内容
    qDebug() << str;                     // 调试输出
    
    if (!serial->isOpen())               // 串口未打开则返回
        return;

    QByteArray outData = str.toLatin1(); // QString转Latin1编码的字节数组

    // 根据十六进制发送开关决定发送格式
    if (IsHexSend)                       // 十六进制模式
    {
        outData = outData.toHex();       // 转为十六进制字符串
        serial->write(outData);
    }
    else                                 // 普通文本模式
    {
        serial->write(outData);
    }
    qDebug() << outData;                 // 调试输出发送内容
}

// 清空接收区按钮槽函数
void MainWindow::on_pushButton_clearRecieveData_clicked()
{
    ui->textEdit->clear();               // 清空接收显示区域
}

// 清空发送区按钮槽函数
void MainWindow::on_pushButton_clearSendData_clicked()
{
    ui->lineEdit->clear();               // 清空发送输入框
}

// 接收区十六进制复选框状态改变槽函数
void MainWindow::on_checkBox_resieveHex_stateChanged(int argHex)
{
    IsHexReceive = (argHex == 0 ? false : true);  // 非0即为true
}

// 发送区十六进制复选框状态改变槽函数
void MainWindow::on_checkBox_sendHex_stateChanged(int argHex)
{
    IsHexSend = (argHex == 0 ? false : true);
}

h文件:

cpp 复制代码
#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QTime>
#include <QTimer>
#include <QSerialPort>
#include <QSerialPortInfo>
#include <QDebug>
namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    explicit MainWindow(QWidget *parent = 0);
    ~MainWindow();

    void initSerialPort();

private:
    Ui::MainWindow *ui;
    QSerialPort *serial;
    QTimer *timer;
    QTimer *ReadTimer; //定时读取串口数据
    QTimer *SendTimer; //定时发送串口数据
    bool IsHexSend = false;//是否16进制数据发送
    bool IsHexReceive = false;//是否16进制数据接收

private slots:
    void SetTime(); //更新时间
    void ReadCom(); //读取串口数据
    void WriteCom(); //写串口数据
    void on_pushButton_send_clicked();
    void on_pushButton_serialPortOprate_clicked();
    void on_pushButton_clearRecieveData_clicked();
    void on_pushButton_clearSendData_clicked();
    void on_checkBox_resieveHex_stateChanged(int arg1);
    void on_checkBox_sendHex_stateChanged(int arg1);
};

#endif // MAINWINDOW_H
相关推荐
果汁华5 小时前
Typer:基于类型提示的现代Python CLI框架
开发语言·网络·python
赵药师5 小时前
多进程-生产者消费者C++实现
java·开发语言·jvm
雾岛听蓝5 小时前
Linux线程基础
linux·开发语言·经验分享
zhangzeyuaaa5 小时前
Python 异常机制深度剖析
开发语言·python
whitelbwwww5 小时前
C++基础--类型、函数、作用域、指针、引用、文件
开发语言·c++
leaves falling5 小时前
C/C++ const:修饰变量和指针的区别(和引用底层关系)
c语言·开发语言·c++
比昨天多敲两行5 小时前
C++11新特性
开发语言·c++
xiaoye-duck6 小时前
【C++:C++11】核心特性实战:详解C++11列表初始化、右值引用与移动语义
开发语言·c++·c++11
希望永不加班6 小时前
SpringBoot 事件机制:ApplicationEvent 与监听器
java·开发语言·spring boot·后端·spring