开源 C++ QT Widget 开发(五)通讯--串口调试

文章的目的为了记录使用C++ 进行QT Widget 开发学习的经历。临时学习,完成app的开发。开发流程和要点有些记忆模糊,赶紧记录,防止忘记。

相关链接:

开源 C++ QT Widget 开发(一)工程文件结构-CSDN博客

开源 C++ QT Widget 开发(二)基本控件应用-CSDN博客

开源 C++ QT Widget 开发(三)图表--波形显示器-CSDN博客

开源 C++ QT Widget 开发(四)文件--二进制文件查看编辑-CSDN博客

开源 C++ QT Widget 开发(五)通讯--串口调试-CSDN博客

开源 C++ QT Widget 开发(六)通讯--TCP调试-CSDN博客

推荐链接:

开源 java android app 开发(一)开发环境的搭建-CSDN博客

开源 java android app 开发(二)工程文件结构-CSDN博客

开源 java android app 开发(三)GUI界面布局和常用组件-CSDN博客

开源 java android app 开发(四)GUI界面重要组件-CSDN博客

开源 java android app 开发(五)文件和数据库存储-CSDN博客

开源 java android app 开发(六)多媒体使用-CSDN博客

开源 java android app 开发(七)通讯之Tcp和Http-CSDN博客

开源 java android app 开发(八)通讯之Mqtt和Ble-CSDN博客

开源 java android app 开发(九)后台之线程和服务-CSDN博客

开源 java android app 开发(十)广播机制-CSDN博客

开源 java android app 开发(十一)调试、发布-CSDN博客

开源 java android app 开发(十二)封库.aar-CSDN博客

推荐链接:

开源C# .net mvc 开发(一)WEB搭建_c#部署web程序-CSDN博客

开源 C# .net mvc 开发(二)网站快速搭建_c#网站开发-CSDN博客

开源 C# .net mvc 开发(三)WEB内外网访问(VS发布、IIS配置网站、花生壳外网穿刺访问)_c# mvc 域名下不可訪問內網,內網下可以訪問域名-CSDN博客

开源 C# .net mvc 开发(四)工程结构、页面提交以及显示_c#工程结构-CSDN博客

开源 C# .net mvc 开发(五)常用代码快速开发_c# mvc开发-CSDN博客

本章主要内容:实现了一个串口调试工具,参数可设,支持实时数据收发、格式转换、参数配置及通信过程监控等功能。可以广泛应用于工业控制、单片机开发、智能设备调试等场景,可通过调节波特率(110bps至256000bps)、校验方式等参数实现高速串口通讯。

一、代码分析

1.1 串口配置模块

// 串口参数配置

QComboBox *portComboBox; // 串口选择

QComboBox *baudComboBox; // 波特率选择(9600-115200)

QComboBox *stopBitsComboBox; // 停止位(1, 1.5, 2)

QComboBox *parityComboBox; // 校验位(无, 奇校验, 偶校验)

数据接收模块

cpp

QTextEdit *receiveEdit; // 接收数据显示区域

QCheckBox *hexDisplayCheckBox; // 16进制显示开关

数据发送模块

cpp

QTextEdit *sendEdit; // 发送数据输入区域

QCheckBox *hexSendCheckBox; // 16进制发送开关

1.2 核心功能实现分析

  1. 串口管理

cpp

void MainWindow::onOpenButtonClicked()

{

// 实现串口的打开/关闭切换

// 配置串口参数:端口名、波特率、数据位、停止位、校验位

}

  1. 数据发送处理

cpp

void MainWindow::onSendButtonClicked()

{

// 支持两种发送模式:

// - 文本模式:直接发送UTF-8文本

// - 16进制模式:将输入的16进制字符串转换为字节数据

// 包含数据验证:16进制数据长度校验、字符合法性检查

}

  1. 数据接收处理

cpp

void MainWindow::onSerialReadyRead()

{

// 支持两种显示模式:

// - 文本模式:直接显示接收到的文本

// - 16进制模式:将字节数据转换为16进制字符串显示

// 自动滚动到底部功能

}

二、所有源码

2.1 mainwindow.h源码

复制代码
#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QSerialPort>
#include <QSerialPortInfo>
#include <QComboBox>
#include <QTextEdit>
#include <QCheckBox>
#include <QPushButton>
#include <QLabel>
#include <QGroupBox>
#include <QVBoxLayout>

class MainWindow : public QMainWindow
{
    Q_OBJECT

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

private slots:
    void onOpenButtonClicked();
    void onRefreshButtonClicked();
    void onSendButtonClicked();
    void onSerialReadyRead();
    void onClearReceiveButtonClicked();
    void onClearSendButtonClicked();

private:
    void createSerialConfigGroup();
    void createReceiveGroup();
    void createSendGroup();
    void initSerialPort();
    void refreshSerialPorts();
    void connectSignals();

    // 串口配置控件
    QComboBox *portComboBox;
    QComboBox *baudComboBox;
    QComboBox *stopBitsComboBox;
    QComboBox *parityComboBox;
    QPushButton *refreshButton;
    QPushButton *openButton;
    QLabel *statusLabel;

    // 接收区域控件
    QTextEdit *receiveEdit;
    QCheckBox *hexDisplayCheckBox;
    QPushButton *clearReceiveButton;

    // 发送区域控件
    QTextEdit *sendEdit;
    QCheckBox *hexSendCheckBox;
    QPushButton *clearSendButton;
    QPushButton *sendButton;

    // 串口对象
    QSerialPort *serialPort;

    // 布局和容器
    QWidget *centralWidget;
    QVBoxLayout *mainLayout;
    QGroupBox *configGroup;
    QGroupBox *receiveGroup;
    QGroupBox *sendGroup;
};

#endif // MAINWINDOW_H

2.2 mainwindow.cpp源码

复制代码
#include "mainwindow.h"
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QMessageBox>
#include <QScrollBar>

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
{
    // 设置窗口大小
    this->resize(1000, 600);
    this->setWindowTitle("QT串口调试助手");

    // 创建中央部件
    centralWidget = new QWidget(this);
    setCentralWidget(centralWidget);

    // 主布局
    mainLayout = new QVBoxLayout(centralWidget);

    // 创建各个功能区域
    createSerialConfigGroup();
    createReceiveGroup();
    createSendGroup();

    // 将各个区域添加到主布局
    mainLayout->addWidget(configGroup);
    mainLayout->addWidget(receiveGroup);
    mainLayout->addWidget(sendGroup);

    // 初始化串口
    initSerialPort();

    // 刷新可用串口
    refreshSerialPorts();

    // 连接信号槽
    connectSignals();
}

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

void MainWindow::createSerialConfigGroup()
{
    configGroup = new QGroupBox("串口配置");
    QHBoxLayout *configLayout = new QHBoxLayout(configGroup);

    // 串口选择
    QLabel *portLabel = new QLabel("串口:");
    portComboBox = new QComboBox();
    configLayout->addWidget(portLabel);
    configLayout->addWidget(portComboBox);

    // 波特率选择
    QLabel *baudLabel = new QLabel("波特率:");
    baudComboBox = new QComboBox();
    baudComboBox->addItems(QStringList() << "9600" << "19200" << "38400" << "57600" << "115200");
    baudComboBox->setCurrentText("115200");
    configLayout->addWidget(baudLabel);
    configLayout->addWidget(baudComboBox);

    // 停止位选择
    QLabel *stopBitsLabel = new QLabel("停止位:");
    stopBitsComboBox = new QComboBox();
    stopBitsComboBox->addItems(QStringList() << "1" << "1.5" << "2");
    configLayout->addWidget(stopBitsLabel);
    configLayout->addWidget(stopBitsComboBox);

    // 校验位选择
    QLabel *parityLabel = new QLabel("校验位:");
    parityComboBox = new QComboBox();
    parityComboBox->addItems(QStringList() << "无" << "奇校验" << "偶校验");
    configLayout->addWidget(parityLabel);
    configLayout->addWidget(parityComboBox);

    // 刷新按钮
    refreshButton = new QPushButton("刷新");
    configLayout->addWidget(refreshButton);

    // 打开/关闭按钮
    openButton = new QPushButton("打开串口");
    configLayout->addWidget(openButton);

    // 状态标签
    statusLabel = new QLabel("串口未打开");
    configLayout->addWidget(statusLabel);

    configLayout->addStretch();
}

void MainWindow::createReceiveGroup()
{
    receiveGroup = new QGroupBox("接收数据");
    QVBoxLayout *receiveLayout = new QVBoxLayout(receiveGroup);

    // 接收文本框
    receiveEdit = new QTextEdit();
    receiveEdit->setReadOnly(true);
    receiveLayout->addWidget(receiveEdit);

    // 接收选项
    QHBoxLayout *receiveOptionsLayout = new QHBoxLayout();
    hexDisplayCheckBox = new QCheckBox("16进制显示");
    clearReceiveButton = new QPushButton("清空接收");
    receiveOptionsLayout->addWidget(hexDisplayCheckBox);
    receiveOptionsLayout->addWidget(clearReceiveButton);
    receiveOptionsLayout->addStretch();

    receiveLayout->addLayout(receiveOptionsLayout);
}

void MainWindow::createSendGroup()
{
    sendGroup = new QGroupBox("发送数据");
    QVBoxLayout *sendLayout = new QVBoxLayout(sendGroup);

    // 发送文本框
    sendEdit = new QTextEdit();
    sendEdit->setMaximumHeight(100);
    sendLayout->addWidget(sendEdit);

    // 发送选项
    QHBoxLayout *sendOptionsLayout = new QHBoxLayout();
    hexSendCheckBox = new QCheckBox("16进制发送");
    clearSendButton = new QPushButton("清空发送");
    sendButton = new QPushButton("发送");
    sendOptionsLayout->addWidget(hexSendCheckBox);
    sendOptionsLayout->addWidget(clearSendButton);
    sendOptionsLayout->addWidget(sendButton);
    sendOptionsLayout->addStretch();

    sendLayout->addLayout(sendOptionsLayout);
}

void MainWindow::initSerialPort()
{
    serialPort = new QSerialPort(this);
}

void MainWindow::refreshSerialPorts()
{
    portComboBox->clear();
    QList<QSerialPortInfo> ports = QSerialPortInfo::availablePorts();
    for(const QSerialPortInfo &port : ports)
    {
        portComboBox->addItem(port.portName());
    }
}

void MainWindow::connectSignals()
{
    connect(openButton, &QPushButton::clicked, this, &MainWindow::onOpenButtonClicked);
    connect(refreshButton, &QPushButton::clicked, this, &MainWindow::onRefreshButtonClicked);
    connect(sendButton, &QPushButton::clicked, this, &MainWindow::onSendButtonClicked);
    connect(clearReceiveButton, &QPushButton::clicked, this, &MainWindow::onClearReceiveButtonClicked);
    connect(clearSendButton, &QPushButton::clicked, this, &MainWindow::onClearSendButtonClicked);
    connect(serialPort, &QSerialPort::readyRead, this, &MainWindow::onSerialReadyRead);
}

void MainWindow::onOpenButtonClicked()
{
    if(serialPort->isOpen())
    {
        serialPort->close();
        openButton->setText("打开串口");
        statusLabel->setText("串口已关闭");
    }
    else
    {
        // 配置串口参数
        serialPort->setPortName(portComboBox->currentText());
        serialPort->setBaudRate(baudComboBox->currentText().toInt());

        // 设置数据位
        serialPort->setDataBits(QSerialPort::Data8);

        // 设置停止位
        QString stopBits = stopBitsComboBox->currentText();
        if(stopBits == "1") serialPort->setStopBits(QSerialPort::OneStop);
        else if(stopBits == "1.5") serialPort->setStopBits(QSerialPort::OneAndHalfStop);
        else if(stopBits == "2") serialPort->setStopBits(QSerialPort::TwoStop);

        // 设置校验位
        QString parity = parityComboBox->currentText();
        if(parity == "无") serialPort->setParity(QSerialPort::NoParity);
        else if(parity == "奇校验") serialPort->setParity(QSerialPort::OddParity);
        else if(parity == "偶校验") serialPort->setParity(QSerialPort::EvenParity);

        // 打开串口
        if(serialPort->open(QIODevice::ReadWrite))
        {
            openButton->setText("关闭串口");
            statusLabel->setText("串口已打开: " + portComboBox->currentText());
        }
        else
        {
            QMessageBox::critical(this, "错误", "无法打开串口: " + serialPort->errorString());
        }
    }
}

void MainWindow::onRefreshButtonClicked()
{
    refreshSerialPorts();
}

void MainWindow::onSendButtonClicked()
{
    if(!serialPort->isOpen())
    {
        QMessageBox::warning(this, "警告", "请先打开串口");
        return;
    }

    QString sendText = sendEdit->toPlainText();
    if(sendText.isEmpty()) return;

    QByteArray sendData;
    if(hexSendCheckBox->isChecked())
    {
        // 16进制发送
        sendText = sendText.trimmed();
        sendText.replace(" ", "");

        if(sendText.length() % 2 != 0)
        {
            QMessageBox::warning(this, "警告", "16进制数据长度必须为偶数");
            return;
        }

        bool ok;
        for(int i = 0; i < sendText.length(); i += 2)
        {
            QString byteStr = sendText.mid(i, 2);
            uint8_t byte = byteStr.toUShort(&ok, 16);
            if(!ok)
            {
                QMessageBox::warning(this, "警告", "包含非法的16进制字符");
                return;
            }
            sendData.append(byte);
        }
    }
    else
    {
        // 文本发送
        sendData = sendText.toUtf8();
    }

    // 发送数据
    serialPort->write(sendData);
}

void MainWindow::onSerialReadyRead()
{
    QByteArray data = serialPort->readAll();

    if(hexDisplayCheckBox->isChecked())
    {
        // 16进制显示
        QString hexData;
        for(int i = 0; i < data.size(); i++)
        {
            hexData += QString("%1 ").arg((uint8_t)data[i], 2, 16, QLatin1Char('0')).toUpper();
        }
        receiveEdit->insertPlainText(hexData);
    }
    else
    {
        // 文本显示
        QString text = QString::fromUtf8(data);
        receiveEdit->insertPlainText(text);
    }

    // 自动滚动到底部
    QScrollBar *scrollbar = receiveEdit->verticalScrollBar();
    scrollbar->setValue(scrollbar->maximum());
}

void MainWindow::onClearReceiveButtonClicked()
{
    receiveEdit->clear();
}

void MainWindow::onClearSendButtonClicked()
{
    sendEdit->clear();
}

2.3 .prog工程文件

复制代码
QT       += core gui
QT       += core gui serialport widgets
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets

CONFIG += c++11

# The following define makes your compiler emit warnings if you use
# any Qt feature that has been marked deprecated (the exact warnings
# depend on your compiler). Please consult the documentation of the
# deprecated API in order to know how to port your code away from it.
DEFINES += QT_DEPRECATED_WARNINGS

# You can also make your code fail to compile if it uses deprecated APIs.
# In order to do so, uncomment the following line.
# You can also select to disable deprecated APIs only up to a certain version of Qt.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000    # disables all the APIs deprecated before Qt 6.0.0

SOURCES += \
    main.cpp \
    mainwindow.cpp

HEADERS += \
    mainwindow.h

FORMS += \
    mainwindow.ui

# Default rules for deployment.
qnx: target.path = /tmp/$${TARGET}/bin
else: unix:!android: target.path = /opt/$${TARGET}/bin
!isEmpty(target.path): INSTALLS += target

三、显示效果