vesperW · 7月23日

一个基于Qt的串口调试工具(代码开源)

作者 | moonan

今天给大家分享一个基于QT设计串口调试工具,源码在Gitee,代码简单,可操作性强!
https://gitee.com/ErichMoonan/serial-master

1、概述

在开始软件设计之前,我们来简略地分析一下这样一个小软件其要包含的主要内容有哪些。我们认为软件需要如下几个方面的内容:

  • 串口参数的配置,我们希望串口号能够自动搜索,而相应的配置参数我们可以选择。
  • 发送数据的输入,对于本软件我们需要输入相应的数据以实现命令及消息的发送,所以我们需要设计数据的输入区域以及发送交互按钮等。
  • 接收信息的显示,作为调试工具,我们肯定希望能够一目了然地看到接收到目标设备发送过来的消息,所以我们需要一个显示区域来对接收的区域进行显示。
  • 运行状态的显示, 我们希望对操作的状态进行反馈以指示操作的动作是否执行,所以我们需要状态栏来实现这一需求。
  • 其它辅助功能, 还有如发送计数、接收计数、数据存储等功能有时候也是需要的,所以我们一并考虑。

对于串口工具其实网上就有不少,我们之所以要自己实现这么一个串口调试工具,主要的原因有两点。一是,网上找到的相应工具某一个单独的工具有时候不能完全满足我们的需求,所以我们根据自己的需求设计这个工具能更好的满足我们串口调试的需要。二是,通过这样一个工具的实现,我们能够加深对串口通讯相关知识的理解。

2、界面设计

根据上一节中分析的需求,我们先来设计软件的界面。我们在QT中基于QMainWindow类生成一个操作界面,包括菜单栏、工具栏和状态栏以满足需求中对状态显示及操作命令的要求。

而在中间显示区域,我们将其划分为3行2列。在左边的一列从上到下设置:串口配置操作区域、接收配置区域以及发送配置区域。在右侧的一列从上到下设置:动态曲线显示区域、信息接收显示区域以及信息发送输入区域。具体的界面设置如下图所示:

image.png

完成如上图的布局后,我们可以选择在属性中配置空间的参数,也可以在代码中添加相关的参数,本人习惯于在代码中完成。完成整个布局后我们先试着运行程序,正常运行则出现如下的界面:

image.png

上图就是完成布局后的运行界面,不过我们还没有实现相应的编码,所以目前还不能实现我们第一节中所提出来的功能。

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);
    }
}

同样的,我们除了要打开串口建立连接外,还需要关闭串口断开连接,具体的代码如下:

//关闭串口
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++;
    }
}

手动单次发送则判断是哪一个按钮触发的动作则操作对应的数据输入框,将其中的内容以指定的格式发送出去。具体的操作代码如下:

//按钮触发发送
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、数据的接收与现实功能

在我们的设计中,数据的接收相对要简单一些。当串口接收到数据后就会触发我们的接收数据处理函数,并将以我们设定的格式显示出来,具体的实现代码如下:

//从串口接收数据
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、小结

完成了编码调试后,我们来对开发的这一工具进行一些测试。首先我们安装一个虚拟串口软件用以虚拟我们用于测试的串口。如果有硬件接口最好,但是在我的电脑上没有串口,所以我们使用虚拟串口来模拟一对串口。具体的配置如下图所示:

image.png

我们使用另一个串口工具来实现与我们开发的这一工具实现通讯验证。我们使用以前写的一个串口工具来实现与这一工具的通讯。一个使用使用COM1,一个使用使用COM2。具体的配置如下图所示:

注:使用虚拟串口波特率可以

image.png

声明:本文素材来源网络,版权归原作者所有。如涉及作品版权问题,请与我联系删除。

END 

作者:moonan
来源:嵌入式专栏

推荐阅读

欢迎大家点赞留言,更多Arm技术文章动态请关注极术社区嵌入式客栈专栏欢迎添加极术小姐姐微信(id:aijishu20)加入技术交流群,请备注研究方向。

推荐阅读
关注数
2884
内容数
272
分享一些在嵌入式应用开发方面的浅见,广交朋友
目录
极术微信服务号
关注极术微信号
实时接收点赞提醒和评论通知
安谋科技学堂公众号
关注安谋科技学堂
实时获取安谋科技及 Arm 教学资源
安谋科技招聘公众号
关注安谋科技招聘
实时获取安谋科技中国职位信息