学习笔记:使用Qt制作一个串口调试助手

发布于:2022-12-17 ⋅ 阅读:(1226) ⋅ 点赞:(0)

界面设计参考了野火串口调试助手C#版

功能基本实现!实现效果看图:
请添加图片描述
请添加图片描述

第一步:制作图形界面

在这里插入图片描述
注意控件命名要又辨识度,不然在编写代码时将是很很痛苦的

第二步:添加相应的库

添加此次工程所需要的串口库,在工程的.pro文件下添加以下代码:

QT       += core gui
QT       += serialport

添加串口使用到的头文件:

#include <QtSerialPort/QSerialPort>
#include <QtSerialPort/QSerialPortInfo>

第三步:编程

看代码注释
搜索可用串口并添加到QComboBox里

void SerialDebugging::getAvailablePort()
{
    ui->portCB->clear();
    foreach (const QSerialPortInfo &info, QSerialPortInfo::availablePorts())
    {
        QSerialPort serialp;
        serialp.setPort(info);
        if(serialp.open(QIODevice::ReadWrite))
        {
            ui->portCB->addItem(serialp.portName());
            serialp.close();
        }
    }
}

打开串口和关闭串口

void SerialDebugging::on_openPortBtn_clicked()
{
    if(ui->openPortBtn->text() == "打开串口")
    {
        if(ui->portCB->currentText() == "")
        {
            ui->stateLabel->setText("无可用串口");
            ui->stateImageLabel->setPixmap(QPixmap(":/image/dot-gray.png"));
            return;
        }
        serial = new QSerialPort;
        serial->setPortName(ui->portCB->currentText());//设置端口
        serial->open(QIODevice::ReadWrite);
        serial->setBaudRate(ui->baudRateCB->currentText().toInt());//设置波特率
        //设置校验位
        switch(ui->parityBitCB->currentIndex())
        {
        case 0:
            serial->setParity(QSerialPort::NoParity);
            break;
        case 1:
            serial->setParity(QSerialPort::OddParity);
            break;
        case 2:
            serial->setParity(QSerialPort::EvenParity);
            break;
        case 3:
            serial->setParity(QSerialPort::MarkParity);
            break;
        case 4:
            serial->setParity(QSerialPort::SpaceParity);
            break;
        default:
            break;
        }
        //设置数据位
        switch(ui->dataBitCB->currentText().toInt())
        {
        case 8:
            serial->setDataBits(QSerialPort::Data8);
            break;
        case 7:
            serial->setDataBits(QSerialPort::Data7);
            break;
        case 6:
            serial->setDataBits(QSerialPort::Data6);
            break;
        case 5:
            serial->setDataBits(QSerialPort::Data5);
            break;
        default:
            break;
        }
        //设置停止位
        switch(ui->stopBitCB->currentIndex())
        {
        case 0:
            serial->setStopBits(QSerialPort::OneStop);
            break;
        case 1:
            serial->setStopBits(QSerialPort::OneAndHalfStop);
            break;
        case 2:
            serial->setStopBits(QSerialPort::TwoStop);
            break;
        default:
            break;
        }
        //设置无流控制
        serial->setFlowControl(QSerialPort::NoFlowControl); 

        ui->portCB->setEnabled(false);
        ui->baudRateCB->setEnabled(false);
        ui->parityBitCB->setEnabled(false);
        ui->dataBitCB->setEnabled(false);
        ui->stopBitCB->setEnabled(false);
        ui->openPortBtn->setText("关闭串口");
        ui->stateLabel->setText("串口已打开");
        ui->stateImageLabel->setPixmap(QPixmap(":/image/dot-cyan.png"));

        connect(serial,&QSerialPort::readyRead,this,&SerialDebugging::serialReceiveData);
    }
    else
    {
        if(serial->isOpen()) //如果原来的串口打开了,先关闭
            serial->close();

        //释放串口
        delete serial;
        serial = NULL;

        ui->portCB->setEnabled(true);
        ui->baudRateCB->setEnabled(true);
        ui->parityBitCB->setEnabled(true);
        ui->dataBitCB->setEnabled(true);
        ui->stopBitCB->setEnabled(true);
        ui->openPortBtn->setText("打开串口");
        ui->stateLabel->setText("串口已关闭");
        ui->stopShowBtn->setText("停止显示");
        ui->stateImageLabel->setPixmap(QPixmap(":/image/dot-gray.png"));
    }
}

串口接收数据并显示在QTextEdit

void SerialDebugging::serialReceiveData()
{
    QByteArray array = serial->readAll();
    revByte += array.size();
    QString revString;
    //这句在这里写了两个次,如果只写一次会有点小毛病,有点强迫症不能忍
    ui->serialRevTextEdit->moveCursor(QTextCursor::End,QTextCursor::MoveAnchor);
    if(!stopShow)
    {
    	//十六进制显示
        if(ui->hexShowCheckBox->isChecked())
        {
            QDataStream outArray(&array,QIODevice::ReadOnly);
            qint8 outChar = 0;
            while(!outArray.atEnd())
            {
                outArray >> outChar;
                revString = QString("%1").arg(outChar&0xFF,2,16,QLatin1Char('0'));
                ui->serialRevTextEdit->insertPlainText(revString.toUpper());
                ui->serialRevTextEdit->insertPlainText(" ");
            }
        }
        else//ASCII显示
        {
            revString = QString::fromLocal8Bit(array);
            if(!ui->autoClearCheckBox->isChecked())//自动清空
                ui->serialRevTextEdit->insertPlainText(revString);
            else
                ui->serialRevTextEdit->setPlainText(revString);
        }
       	//接收字节数
        ui->revByteLabel->setText(QString("%1").arg(revByte));
         //这句在这里写了两个次,如果只写一次会有点小毛病,有点强迫症不能忍
        ui->serialRevTextEdit->moveCursor(QTextCursor::End,QTextCursor::MoveAnchor);
    }
}

串口发送数据

void SerialDebugging::on_sendDataBtn_clicked()
{
    if(ui->openPortBtn->text() == "打开串口")
    {
        ui->stateLabel->setText("请先打开串口!");
        return;
    }
    QString sendData = ui->serialSendTextEdit->toPlainText();
    QByteArray array;

    if(ui->hexSendCheckBox->isChecked())
        array = QByteArray::fromHex(sendData.toUtf8()).data();//字符串转16进制发送
    else
        array = sendData.toLocal8Bit();//ASCII发送
    int sbyte = serial->write(array);
    sendByte += sbyte;
    ui->sendByteLabel->setText(QString("%1").arg(sendByte));//发送字节数
}

还有一些小功能(清除接收、接收转向文件、加载文件等功能)的实现方法就不贴代码上来啦,具体看工程源码,工程源码链接在底部。

附:使用nativeEvent获取Windows事件处理实现串口热插拔

实现串口热插拔可以通过添加一个定时器间隔一秒钟获取一次串口列表。
但是我在网上冲浪学到了使用nativeEvent获取Windows事件,从而可以实现串口热插拔。nativeEvent功能很高级,就是我用的方法很蠢,很搞笑
注意:nativeEvent函数需要编写在窗口的基类才可以触发,比如我的文件结构是这样的
在这里插入图片描述
mainwidget.cpp是主窗口,而串口程序编程在serialdebugging.cpp,当时我把nativeEvent函数编写在serialdebugging.cpp的类里,搞了很久都没有反应,最后在网上冲浪才找到原因(新手踩坑多)。 所以nativeEvent函数需要编写在mainwidget.cpp的mainwidget类里。
如果想要了解nativeEvent,大家可以网上 冲浪学习学习
1.添加头文件

#include <windows.h>
#include <windowsx.h>
#include <dbt.h>

2.头文件定义函数

protected:
    bool nativeEvent(const QByteArray &eventType, void *message, long *result);

3.具体实现

bool MainWidget::nativeEvent(const QByteArray &eventType, void *message, long *result)
{
    MSG* msg = reinterpret_cast<MSG*>(message);
    if(msg->message == WM_DEVICECHANGE)
    {
        PDEV_BROADCAST_HDR lpdb = (PDEV_BROADCAST_HDR)msg->lParam;
        switch (msg->wParam)
        {
        case DBT_DEVICEARRIVAL:             // 插入
        {
            if (lpdb->dbch_devicetype == DBT_DEVTYP_PORT)           // 设备类型为串口
            {
                serialDebug->getAvailablePort();//调用获取串口函数
            }
            break;
        }
        case DBT_DEVICEREMOVECOMPLETE:      // 拔出
        {
            if (lpdb->dbch_devicetype == DBT_DEVTYP_PORT)           // 设备类型为串口
            {
                serialDebug->getAvailablePort();
            }
            break;
        }
        default:
            break;
        }
    }
    return false;
}

串口调试助手的主要功能就完成了
还有一些小功能的实现方法具体看工程源码啦。
工程源码:https://download.csdn.net/download/qq_44793587/85236748
或者链接: https://pan.baidu.com/s/1H7Uxuthjb_urbQL-JkhCCA?pwd=ep8m 提取码: ep8m


网站公告

今日签到

点亮在社区的每一天
去签到

热门文章