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

界面设计参考了野火串口调试助手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

与硬件通信的程序基本上要用到串口,虽然qt5以后集成了串口通信类,但是个人觉得那个串口通信类有点问题,在linux上表现很好,windows上大数据会有怪怪的问题出现,而且只能在qt5以上的版本才能用,无奈大部分的嵌入式linux上还停留在4.7.1到4.8.5左右的版本,所以本人一直喜欢用第三方的串口通信类做处理。 程序调试中经常需要串口调试,甚至还需要模拟设备数据回复,甚至串口转网络出去,特意将这些常用功能都做到一个串口调试助手中去。 基本功能: 1:支持16进制数据发送与接收。 2:支持windows下COM9以上的串口通信。 3:实时显示收发数据字节大小以及串口状态。 4:支持任意qt版本,亲测4.7.0 4.8.5 4.8.7 5.4.1 5.7.0 5.8.0。 5:支持串口转网络数据收发。 高级功能: 1:可自由管理需要发送的数据,每次只要从下拉框中选择数据即可,无需重新输入数据。 2:可模拟设备回复数据,需要在主界面开启模拟设备回复数据。当接收到设置好的指令时,立即回复设置的回复指令。例如指定收到0x16 0x00 0xFF 0x01需要回复0x16 0x00 0xFE 0x01,则只需要在SendData.txt中添加一条数据16 00 FF 01:16 00 FE 01即可。 3:可定时发送数据和保存数据到文本文件:,默认间隔5秒钟,可更改间隔时间。 4:在不断接收到大量数据时,可以暂停显示数据来查看具体数据,后台依然接收数据但不处理,无需关闭串口来查看已接收到的数据。 5:每次收到的数据都是完整的一条数据,而不是脱节的,做了延时处理。 6:一套源码随处编译,无需更改串口通信类,已在XP/WIN7/UBUNTU/ARMLINUX系统下成功编译并运行。
评论 21
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值