找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

3782

积分

0

好友

500

主题
发表于 3 小时前 | 查看: 4| 回复: 0

在嵌入式开发和硬件通讯调试领域,一款称手且高度可控的串口工具堪称效率倍增器。今天为大家拆解一个基于Qt框架设计的开源串口调试工具——Serial Master。该项目代码结构清晰、交互直观,非常适合作为 开源实战 的练手之作,方便大家后续根据个性化需求进行二次开发。

项目源码已在 Gitee 开源: https://gitee.com/ErichMoonan/serial-master

1. 概述

在动手编码之前,我们先梳理一下这款软件需要具备的核心模块:

  • 串口参数配置:支持串口号自适应检测,并允许用户灵活调整波特率、数据位等关键通讯参数。
  • 数据发送区:提供多通道输入框与交互按钮,用于发送指令或自定义消息,支持自动轮询发送。
  • 信息接收区:用于直观地滚动展示目标设备反馈的原始报文,支持ASCII和HEX显示。
  • 运行状态显示:通过状态栏实时反馈当前操作的动作是否执行成功。
  • 辅助功能:集成发送计数、接收计数、报文存储等实用特性。

尽管市面上已有不少现成的串口调试工具,但我们仍决定自己动手“造轮子”。一方面是为了打造一个完全契合个人调试习惯的专属工具;另一方面,通过从零开始编码,能够极大地加深对串口通讯底层机制与Qt信号槽机制的理解。

2. 界面设计

基于上述需求,我们设计的软件界面如下。基于 QMainWindow 类构建主框架,包含菜单栏、工具栏和状态栏。中间客户区采用 3行2列 的栅格布局:

  • 左侧一列 自上而下为:串口配置区、接收配置区、发送配置区。
  • 右侧一列 对应为:动态曲线展示区、信息接收显示区、信息发送输入区。

具体的界面设置与控件的对应关系如下图所示:

Serial Master串口调试工具主界面截图

完成布局初步运行起来的效果如下,此时尚未编写具体的功能逻辑:

Serial Master串口调试工具配置与运行界面截图

3. 编码实现

接下来我们将通过代码来逐一实现上述核心功能。

3.1 参数设置与操作功能

配置参数除了串口号外,均可直接通过 ComboBox 下拉框预设。而串口号则需要我们主动检索当前计算机已连接的物理串口或虚拟串口:

//搜索可用的串口,并添加到串口组合框
void MainWindow::SearchSerialPorts()
{
    ui->comboBoxPort->clear();

    foreach(const QSerialPortInfo &info,QSerialPortInfo::availablePorts())
    {
        ui->comboBoxPort->addItem(info.portName());
    }
}

完成参数配置后,点击打开串口,我们需要将硬件设备的数据接收事件与我们的处理函数进行信号槽绑定,同时设置波特率、数据位、停止位等属性:

//打开串口
void MainWindow::on_actionConnect_triggered()
{
    serialPort->setPortName(ui->comboBoxPort->currentText());

    if(serialPort->open(QIODevice::ReadWrite))              //打开串口成功
    {
        serialPort->setBaudRate(ui->comboBoxBaud->currentText().toInt());       //设置波特率

        switch(ui->comboBoxData->currentIndex())                   //设置数据位数
        {
            case 1:serialPort->setDataBits(QSerialPort::Data8);break;
            default: break;
        }

        switch(ui->comboBoxParity->currentIndex())                   //设置奇偶校验
        {
            case 0: serialPort->setParity(QSerialPort::NoParity);break;
            default: break;
        }

        switch(ui->comboBoxStop->currentIndex())                     //设置停止位
        {
            case 1: serialPort->setStopBits(QSerialPort::OneStop);break;
            case 2: serialPort->setStopBits(QSerialPort::TwoStop);break;
            default: break;
        }

        serialPort->setFlowControl(QSerialPort::NoFlowControl);     //设置流控制

        //连接槽函数
        QObject::connect(serialPort, &QSerialPort::readyRead, this, &MainWindow::ReadSerialData);

        // 设置控件可否使用
        ui->actionConnect->setEnabled(false);
        ui->actionClose->setEnabled(true);
        ui->actionRefresh->setEnabled(false);
    }
    else    //打开失败提示
    {
        QMessageBox::information(this,tr("错误"),tr("打开串口失败!"),QMessageBox::Ok);
    }
}

同理,断开连接时需要释放串口相关的硬件资源并恢复UI按钮的操作权限:

//关闭串口
void MainWindow::on_actionClose_triggered()
{
    serialPort->clear();
    serialPort->close();

    // 设置控件可否使用
    ui->actionConnect->setEnabled(true);
    ui->actionClose->setEnabled(false);
    ui->actionRefresh->setEnabled(true);
}

3.2 数据的输入与发送功能

我们预留了5个独立的发送通道以便于快速切换调试指令。该模块既支持手动触发,也支持按预设间隔进行自动轮询发送。启动自动循环后,程序会轮番检测哪些通道被勾选,并根据设定的毫秒数依次发送数据:

//定时周期发送
void MainWindow::CycleSendData()
{
    QCheckBox* cbSend;

    while(true)
    {
        snIndex=snIndex>=6?1:snIndex;

        cbSend=ui->groupBoxMessage->findChild<QCheckBox*>(QString("checkBoxSendEnable%1").arg(QString::number(snIndex)));

        if(cbSend->isChecked())
        {
            WriteSerialData(snIndex);
            snIndex++;
            break;
        }

        snIndex++;
    }
}

手动单次发送的逻辑则更为直观,它是通过 Qt 的元对象系统反向定位触发操作的按钮(QPushButton),进而获取对应输入框中的内容并写入串口:

//按钮触发发送
void MainWindow::SingleSendData()
{
    // 判断如果Sender是QPushButton就执行
    if (QPushButton* btn = dynamic_cast<QPushButton*>(sender()))
    {
        QString senderName;
        int sn=0;

        senderName = btn->objectName();
        sn = senderName.replace("pushButtonSend", "").toInt();

        if((0<sn) && (sn<6))
        {
            WriteSerialData(sn);
        }
    }
}

3.3 数据的接收与显示功能

当串口下游数据到达时,会触发 readyRead 信号调用我们绑定好的回调函数。代码中会根据用户设定的格式(ASCII 或 HEX)对二进制原始流进行组装,附加精确的日期时间戳,并以蓝色字体醒目地展示在信息接收区的文本框中:

//从串口接收数据
void MainWindow::ReadSerialData()
{
    QByteArray rxDatas;
    QString context;

    rxDatas=serialPort->readAll();

    if(!rxDatas.isNull())
    {
        if(ui->checkBoxRecieve->isChecked())    //十六进制显示
        {
             context = rxDatas.toHex(' ');
             context=context.toUpper();
        }
        else    //ASCII显示
        {
            context = rxDatas;
        }

        QString timeStrLine="["+QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss")+"][接收]: ";
        context = timeStrLine+context+"\n\r";

        QString content = "<span style=\" color:blue;\">"+context+"</span>";
        ui->textBrowser->append(content);

        receivedBytes=receivedBytes+rxDatas.size();
        ui->lcdNumberRecieve->display(receivedBytes);

        ui->statusbar->showMessage(tr("成功读取%1字节数据").arg(rxDatas.size()));
    }

    rxDatas.clear();
}

4. 测试与小结

代码编写完成后,我们在虚拟环境中对 Serial Master 进行闭环测试。如果没有物理开发板,虚拟串口软件是调试上位机的不二之选。我们首先通过虚拟串口驱动配置了一对相互连接的串口(COM3 和 COM4):

虚拟串口驱动6.9管理端口界面截图

接着,我们将另一款成熟的串口工具接在 COM2,同时让 Serial Master 接在 COM1,进行双向收发通讯验证。实测效果表明,该工具在数据的收发、计数的准确性以及界面响应方面均达到了预期标准。

串口工具双窗口联调对比演示截图

如果你有更好的实现思路,或者借助该源码完成了更强大的扩展功能,欢迎来 云栈社区 与众多开发者一起交流探讨。




上一篇:30B模型冲击奥赛金牌线:不是答题更快,而是学会反复改证明
下一篇:零反射 AOT 就绪:TickerQ .NET 任务调度库快速上手指南
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2026-5-19 04:45 , Processed in 0.643825 second(s), 41 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2026 云栈社区.

快速回复 返回顶部 返回列表