【Qt】串口调试工具

前言

Qt框架式一个可以用c++语言来开发的框架,对于嵌入式开发来说,Qt框架可以用来开发上位机,用来对设备进行操纵,也可以用来开发图形界面,当做Linux系统的gui。所以说,基于Qt框架来开发软件,是嵌入式工程师必备的一项技能。
这里写的是一款所有嵌入式工程师都要用到的软件,串口调试工具。这里我是用c++写的,我写的功能比较简陋,也就只有串口收发之类的简单功能。对比一些成熟的串口调试工具肯定是不如的,但是不妨当作练手的小项目。

一、功能设计

项目要实现什么功能,最好在一开始就能定下来,写起来也方便,bug也少。
在实际开发中,很多时候都会在写完后加需求,或者在项目进行中加需求,这很麻烦,因为会打乱事先设计好的软件框架。

1串口接收数据

2串口发送数据

3带有时间戳

4不论是收发都能在窗口看到

5能设置波特率、数据网、验证位、停止位等功能

6能把接收数据转化为十六进制或者ascii字符格式

二、UI布局

在这里插入图片描述创建widget任务,先把UI界面弄好,Qt提供了方便快速的界面设计方式,很多东西用代码来实现是很复杂的,既麻烦,效果还不好。
直接在widget里,用拖动控件的方法,很容易就把这个UI搞好了。
也就用到了几个很基础的控件,pushbutton、textedit、tatel、checkbox。

三、写代码

widget.cpp中的代码,注释都差不多写了。
就是有个奇怪的问题,本来想改个背景图片,变得好看点的,但加入图片之后,运行就会变得很卡,也不知道为什么。

#include "widget.h"
#include "ui_widget.h"
#include <QDebug>
#include <QMessageBox>

Widget::Widget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::Widget)
{
    ui->setupUi(this);

    resize(750,500);//窗口大小

    QPixmap pixmap(":/image/image/yp.jpg"); // 加载图片
    QPalette palette;
    palette.setBrush(this->backgroundRole(), QBrush(pixmap.scaled(this->size(), Qt::IgnoreAspectRatio, Qt::SmoothTransformation))); // 调整图片大小以适应窗口大小
    this->setPalette(palette); // 设置调色板
    this->setAutoFillBackground(true); // 自动填充背景
    ui->textEdit->setStyleSheet("QTextEdit { background-color: rgba(255, 255, 255, 128); }");
    ui->textEdit_2->setStyleSheet("QTextEdit { background-color: rgba(255, 255, 255, 128); }");

    setWindowTitle("串口调试助手(●′ω`●)");//窗口标题
    time1= new QTimer(this);
    serial=new QSerialPort(this);
    time1->start(500);//开启一个200ms的定时器
    connect(time1,&QTimer::timeout,this,&Widget::timerEvent);//时间到后,发出timeout信号,触发槽函数timerevent
    ui->comboBox_2->setCurrentIndex(4);
    ui->comboBox_3->setCurrentIndex(3);
    ui->comboBox_4->setCurrentIndex(2);
    ui->comboBox_5->setCurrentIndex(0);
    rx_len=0;
    tx_len=0;
    connect(serial,QSerialPort::readyRead,this,&Widget::serial_port);//当串口有数据时,转到槽
    ui->checkBox->setCheckState(Qt::Checked);//默认选中ascii

}

Widget::~Widget()
{
    delete ui;
}


void Widget::timerEvent()
{

    QStringList newPortStringList;//创建容器
    newPortStringList.clear();
    foreach(const QSerialPortInfo &info,QSerialPortInfo::availablePorts())//foreach关键字,遍历列表,放到info中
    {
        newPortStringList+=info.portName();//将可用串口的名字放到字符串容器中

        //qDebug()<<newPortStringList;
    }

    if(newPortStringList.size()!=PortStringList.size())
    {
        PortStringList=newPortStringList;//PortStringList是私有成员,是全局的,保存上一次的串口名字
        ui->comboBox->clear();//将下拉栏清空
        ui->comboBox->addItems(PortStringList);//上次串口名字加入
    }


}

void Widget::on_pushButton_clicked()
{
    if(ui->pushButton->text()==QString("打开串口"))
    {
        rx_len=0;
        serial->setPortName(ui->comboBox->currentText());//设置串口名字
        serial->setBaudRate(ui->comboBox_2->currentText().toInt());//设置波特率,注意将text转化为int类型
        switch (ui->comboBox_3->currentText().toInt()) {
        case 5:
            serial->setDataBits(QSerialPort::Data5);
            break;
        case 6:
            serial->setDataBits(QSerialPort::Data6);
            break;
        case 7:
            serial->setDataBits(QSerialPort::Data7);
            break;
        case 8:
            serial->setDataBits(QSerialPort::Data8);
        break;
        default:
            serial->setDataBits(QSerialPort::UnknownDataBits);
            break;
        }

        switch(ui->comboBox_4->currentIndex())//校验位
        {
            case 0:serial->setParity(QSerialPort::EvenParity);break;
            case 1:serial->setParity(QSerialPort::OddParity);break;
            case 2:serial->setParity(QSerialPort::NoParity);break;//没有检验
            default:
                serial->setParity(QSerialPort::UnknownParity);break;//位置校验
        }

        switch (ui->comboBox_5->currentIndex()) {
        case 0:serial->setStopBits(QSerialPort::OneStop);break;
        case 1:serial->setStopBits(QSerialPort::OneAndHalfStop);break;
        case 2:serial->setStopBits(QSerialPort::TwoStop);break;

        default:
            serial->setStopBits(QSerialPort::UnknownStopBits);
            break;
        }

        serial->setFlowControl(QSerialPort::NoFlowControl);
        if(!serial->open(QIODevice::ReadWrite))
        {
            QMessageBox::information(this,"错误提示","无法打开串口",QMessageBox::Ok);
            return;
        }

        //打开串口状态,不可调整串口参数
        ui->comboBox->setEnabled(false);
        ui->comboBox_2->setEnabled(false);
        ui->comboBox_3->setEnabled(false);
        ui->comboBox_4->setEnabled(false);
        ui->comboBox_5->setEnabled(false);

        ui->pushButton->setText("关闭串口");
    }
    else
    {
        serial->close();
        ui->comboBox->setEnabled(true);
        ui->comboBox_2->setEnabled(true);
        ui->comboBox_3->setEnabled(true);
        ui->comboBox_4->setEnabled(true);
        ui->comboBox_5->setEnabled(true);
        ui->pushButton->setText("打开串口");
    }
}

void Widget::serial_port()
{
    qDebug()<<"hello world";
    QString last_rx;
    int length;
    // 获取当前日期和时间
    QDateTime currentDateTime = QDateTime::currentDateTime();

    // 将日期和时间格式化为字符串
    //QString timestamp = currentDateTime.toString("hh:mm:ss");
    int my_hour=currentDateTime.time().hour();
    int my_min=currentDateTime.time().minute();
    int my_sec=currentDateTime.time().second();
    int my_ms = currentDateTime.time().msec();

    QString time_temp=QString("%1:%2:%3:%4").arg(my_hour,2,10,QLatin1Char('0')).arg(my_min,2,10,QLatin1Char('0')).arg(my_sec,2,10,QLatin1Char('0')).arg(my_ms,3,10,QLatin1Char('0'));

    // 将时间戳添加到QTextEdit
    //ui->textEdit->append(timestamp + " - Your text here");


    if(ui->checkBox_3->checkState()!=Qt::Checked)//不处于暂停
    {
        last_rx=ui->textEdit->toPlainText();//获取当前数据的方法
        rx_data=serial->readAll();//从串口接收数据


        //rx_data = QString::fromUtf8(rx_data);//转为UTF-8编码的QString



        rx_len+=rx_data.length();//调用qstring中的方法
        ui->label_10->setText(QString::number(rx_len));//将长度转化为整形
        if(ui->checkBox_2->checkState()==Qt::Checked)//hex
        {
            rx_data=rx_data.toLatin1().toHex();//qstring先转字符串,后转为hex格式
            length=rx_data.length();
            for(int i=0;i<length/2;i++)
            {
                rx_data.insert(2+3*i,' ');
            }
        }
        else//ascii
        {
            rx_data=rx_data.toLatin1();//转为ascii字符串的方法
        }
        last_rx=last_rx.append(time_temp+"[收]>>:");//
        last_rx=last_rx.append(rx_data);//最新接受到的追加到后面
        last_rx=last_rx.append("\n");//每次都要换行

        ui->textEdit->setText(last_rx);//填入字符串,输出
        //ui->textEdit->setWordWrapMode(QTextOption::WordWrap);

    }
}

//三者互斥
void Widget::on_checkBox_clicked()//ASCII
{
    ui->checkBox->setCheckState(Qt::Checked);
    ui->checkBox_2->setCheckState(Qt::Unchecked);
    ui->checkBox_3->setCheckState(Qt::Unchecked);
}

void Widget::on_checkBox_2_clicked()//hex
{
    ui->checkBox->setCheckState(Qt::Unchecked);
    ui->checkBox_2->setCheckState(Qt::Checked);
    ui->checkBox_3->setCheckState(Qt::Unchecked);
}

void Widget::on_checkBox_3_clicked()//暂停
{
    ui->checkBox->setCheckState(Qt::Unchecked);
    ui->checkBox_2->setCheckState(Qt::Unchecked);
    ui->checkBox_3->setCheckState(Qt::Checked);
}

void Widget::on_pushButton_2_clicked()
{
    //将text_edit2中数据发送出去
//    QByteArray byte_array;
//    tx_data=ui->textEdit_2->toPlainText();//获取当前数据
//    byte_array=tx_data.toLatin1();//将当前数据转换为QByteArray类型
//    serial->write(byte_array);//串口发送
//    rx_len+=tx_data.length();//获得长度
//    ui->label_12->setText(QString::number(rx_len));//显示长度
    // 获取当前日期和时间
    QDateTime currentDateTime = QDateTime::currentDateTime();

    // 将日期和时间格式化为字符串
    //QString timestamp = currentDateTime.toString("hh:mm:ss");
    int my_hour=currentDateTime.time().hour();
    int my_min=currentDateTime.time().minute();
    int my_sec=currentDateTime.time().second();
    int my_ms = currentDateTime.time().msec();

    QString time_temp=QString("%1:%2:%3:%4").arg(my_hour,2,10,QLatin1Char('0')).arg(my_min,2,10,QLatin1Char('0')).arg(my_sec,2,10,QLatin1Char('0')).arg(my_ms,3,10,QLatin1Char('0'));


    tx_data=ui->textEdit_2->toPlainText();
    QByteArray temp=tx_data.toLatin1();
    serial->write(temp);
    rx_len+=tx_data.length();
    ui->label_12->setText(QString::number(rx_len));

    QString last_tx;
    last_tx=ui->textEdit->toPlainText();//获取当前数据的方法
    last_tx=last_tx.append(time_temp+"[发]>>:");//
    last_tx=last_tx.append(tx_data);//最新接受到的追加到后面
    last_tx=last_tx.append("\n");//每次都要换行

    ui->textEdit->setText(last_tx);//填入字符串,输出
}

void Widget::on_pushButton_3_clicked()
{
    ui->textEdit->clear();
}

void Widget::on_pushButton_4_clicked()
{
    ui->textEdit_2->clear();
}

widget.h中的代码,就创建了几个私有变量和槽函数。

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <QtSerialPort/QtSerialPort>
#include <QTimer>
#include <QTimerEvent>
namespace Ui {
class Widget;
}

class Widget : public QWidget
{
    Q_OBJECT

public:
    explicit Widget(QWidget *parent = 0);
    ~Widget();

private:
    Ui::Widget *ui;
    QTimer *time1;
    QStringList PortStringList;
    QSerialPort *serial;//私有成员,对象指针
    QString rx_data,tx_data;
    long rx_len,tx_len;
private slots:
   void timerEvent();

   void on_pushButton_clicked();
   
   void serial_port();
   void on_checkBox_clicked();
   void on_checkBox_2_clicked();
   void on_checkBox_3_clicked();
   void on_pushButton_2_clicked();
   void on_pushButton_3_clicked();
   void on_pushButton_4_clicked();
};

#endif // WIDGET_H

这是工程文件,这里主要是加了一个软件的图标。把这个图片放在工程文件夹里,写上RC_ICONS = 1.ico就OK了,这个1.ico是文件的名字
需要注意的是,这个ico文件的大小好像是又限制的,我用别的bmp文件直接改格式成ico,结果编译就报错了。然后用转换工具转换,改变了像素大小,编译就通过了。

#-------------------------------------------------
#
# Project created by QtCreator 2023-02-28T22:11:46
#
#-------------------------------------------------

QT       += core gui
QT += serialport

greaterThan(QT_MAJOR_VERSION, 4): QT += widgets

TARGET = untitled1
TEMPLATE = app

# The following define makes your compiler emit warnings if you use
# any feature of Qt which as been marked as 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 you use 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 \
        widget.cpp

HEADERS += \
        widget.h

FORMS += \
        widget.ui

RESOURCES += \
    image.qrc

RC_ICONS = 1.ico

四、运行效果

接收

在这里插入图片描述

发送

在这里插入图片描述

打包软件

软件写好之后,还是debug的版本,不是发行版本,体积大,别人也能直接看你源码。所以要打包成发行版本
在这里插入图片描述
在软件界面左下角选择release后运行,在工程文件夹下会生成一个对于的release文件。
在这里插入图片描述这里还不时要最终要发布的,选这里的exe可执行文件,创建一个新的文件夹把它丢进去。然后在qt软件的文件夹里找到这个cmd终端
在这里插入图片描述

输入windeployqt exe的目录,例如windeployqt F:\qt_exp\1_hello_world\first_project 回车之后,库就会自动添加到这个文件夹

然后这个文件夹,就包含了所有的依赖库,就能打包发给别人用了。

https://gitee.com/wangyongwei111/qt.git
项目放在这个git仓库中

总结

以上,基本上完成了串口调试工具的基本功能,就是还有几个遗憾的地方没搞好。
一个是背景图片的问题,不知道为什么加入背景图片后,运行起来就变得很卡,动一下卡一下。
第二是还是不支持中文,用stm32f103发个中文过去,显示是会乱码的。
这两个问题还不知道怎么解决。

  • 3
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值