QT上位机:串口调试助手

前言

  上位机的简单编写可以帮我们测试并完善平台,QT作为一款跨平台的GUI开发框架,提供了非常丰富的常用串口api。本文先从最简单的串口调试助手开始,编写平台软件的串口控制界面

工程配置

  QT 串口通信基于QT的QSerialPort类,先在项目文件pro中添加QT += serialport。
  避免默认的widget类和ui命名,将串口部分的ui命名为ui_serial,类命名为serial

界面设置

  在UI界面可以直接搭建我们想要的界面显示方案,并自动生成相应代码添加头文件,用起来还是非常方便的,注意不要和代码里手动添加的混淆就好了
  需要配置的可以选择QComboBox,添加常用的配置项,比如串口号,波特率,数据校验停止位等
  添加led指示灯QLabel,可以用来美化界面
  点击选中的可以选择QRadioButton,用以单选,直观的给以感受
  收发的界面选择文本框QPlainTextEdit,可以多行显示
  手动加的短文本框可以用QLineEdit,可以单行显示
  其余的用最基础的pushbutton就可以了
  为了界面不会被放大缩小导致排版混乱,可以添加弹簧或者直接写死窗口大小
  设计完成后,会保存为对应名称比如serial.ui
在这里插入图片描述

  构造函数里需要将有交互的部分connect起来,将界面按键的初始值做设置
  我们可以通过继承QSerialPort类,也可以直接继承Qwidget类,在里面申明Qserialport

class serial : public QWidget
{
    Q_OBJECT

public:
    explicit serial(QWidget *parent = 0);
    QSerialPort *serialPort;
    ~serial();
private slots:
    void serialPortReadyRead_Slot();
    void on_OpenBt_clicked();
    void on_SendBt_clicked();
    void on_SaveBt_clicked();
    void on_ClearBt_clicked();

    void on_pushButton_clicked();

private:
    QPushButton *m_button;
    void LED(bool changeColor); //串口连接指示灯
    void closeEvent(QCloseEvent *event);  /***关闭***/
    void on_comStatus(QString name, bool flag);
    Ui::serial *ui_serial;

};
serial::serial(QWidget *parent) :

    QWidget(parent),
	ui_serial(new Ui::serial)
{
    ui_serial->setupUi(this);

    setWindowTitle("串口控制界面-HX");
    this->setFixedSize(980, 600); // 固定窗口的大小

    /***************定义接收格式数据按钮********************/
    QButtonGroup *btnGroupRev=new QButtonGroup(this);
    btnGroupRev->addButton(ui_serial->ASCII_Receive_Box,0); // 将给定的按钮添加到按钮组
    btnGroupRev->addButton(ui_serial->HEX_Receive_Box,1);
    ui_serial->ASCII_Receive_Box->setChecked(true); // 设置默认模式
    /***************定义发送格式数据按钮********************/
    QButtonGroup *btnGroupSend=new QButtonGroup(this);
    btnGroupSend->addButton(ui_serial->ASCII_Send_Box,0); // 将给定的按钮添加到按钮组
    btnGroupSend->addButton(ui_serial->HEX_Send_Box,1);
    ui_serial->ASCII_Send_Box->setChecked(true);  // 设置默认模式

    serialPort = new QSerialPort(this);

    connect(serialPort, SIGNAL(readyRead()), this, SLOT(serialPortReadyRead_Slot()));
    /***************串口热插拔********************/
    ComChange::getInstance()->setHWND((HWND)this->winId());
    connect(ComChange::getInstance(), &ComChange::comStatus, this, &serial::on_comStatus);
    QStringList strName = ComChange::getAvailablePort();              // 获取所有可用串口
    ui_serial->SerialCb->addItems(strName);

    /*连接rtsp界面*/
//    m_button = findChild<QPushButton*>("pushButton_3"); // 查找已有的pushButton3对象
//    qDebug()<<"m_button is :"<<m_button;
//    connect(this, &serial::buttonClicked_ctrl_left, this, &serial::on_pushButton_3_clicked);
// 连接信号与槽//

}

  考虑到热插拔的问题封装getAvailablePort获取当前可用串口

ComChange* ComChange::m_comChange = nullptr;
ComChange *ComChange::getInstance()
{
    if(m_comChange == nullptr)
    {
        static QMutex mutex;         //实例互斥锁。
        QMutexLocker locker(&mutex); //加互斥锁。
        if(m_comChange == nullptr)
        {
            m_comChange = new ComChange();
        }
    }
    return m_comChange;

}

/**
 * @brief   获取系统中所有可用的串口名
 * @return
 */
QStringList ComChange::getAvailablePort()
{
    QStringList strName;
    foreach(const QSerialPortInfo& info, QSerialPortInfo::availablePorts())
    {
        QSerialPort port(info);
        if(port.open(QIODevice::ReadWrite))
        {
            strName << info.portName();
            port.close();
        }
    }
    return strName;
}

串口的热插拔

/*
    函   数:on_comStatus
    描   述:串口热插拔操作。有comchange的h文件、cpp文件和该函数,才能支持串口热插拔操作,缺一不可。移植请注意。
    输   入:串口名称:name, 连接标志:flag
    输   出:无
*/
void serial::on_comStatus(QString name, bool flag)
{
    if(flag)  ui_serial->SerialCb->addItem(name); // 串口插入时自动添加串口名

    else
    {
        ui_serial->SerialCb->removeItem(ui_serial->SerialCb->findText(name)); // 串口拔出时自动移除串口名
        LED(false); //红色LED 表示关闭串口
        serialPort->close(); //关闭串口
        ui_serial->OpenBt->setText("打开串口");
    }

}

串口的初始化

  在点击打开串口的时候做初始化,使用open(QIODevice::ReadWrite)用ReadWrite 的模式尝试打开串口,打开成功后设置串口通信的波特率,校验方式等配置。(打开方式有多种,只读(r/o)、只写(w/o)或读写(r/w)模式)
直接使用serialPort的相关API
  setPortName将当前串口的名字设置为系统可用串口的名字
  setBaudRate设置波特率
  setDataBits设置数据位
  setStopBits设置停止位
  setParity设置校验位
  close关闭串口
  注意:串口始终以独占访问方式打开(即没有其他进程或线程可以访问已打开的串口)。

/*
    函   数:on_OpenBt_clicked
    描   述:打开串口时初始化串口
    输   入:无
    输   出:无
*/
void serial::on_OpenBt_clicked()
{
    if(ui_serial->OpenBt->text()=="打开串口")
    {
        serialPort->setPortName(ui_serial->SerialCb->currentText()); // 将当前串口的名字设置为系统可用串口的名字
        qint32 baudrate = ui_serial->BaundCb->currentText().toInt(); // 获取期望的波特率
        serialPort->setBaudRate(baudrate);  // 设置波特率

        //设置数据位
        switch(ui_serial->DataCb->currentText().toInt())
        {
        case 8:
             serialPort->setDataBits(QSerialPort::Data8); break;
        case 7:
             serialPort->setDataBits(QSerialPort::Data7); break;
        case 6:
             serialPort->setDataBits(QSerialPort::Data6); break;
        case 5:
             serialPort->setDataBits(QSerialPort::Data5); break;
        }

        //设置停止位
        if(ui_serial->StopCb->currentText() == "1")
        {
            serialPort->setStopBits(QSerialPort::OneStop);
        }
        else if(ui_serial->StopCb->currentText() == "1.5")
        {
            serialPort->setStopBits(QSerialPort::OneAndHalfStop);
        }
        else if(ui_serial->StopCb->currentText() == "2")
        {
            serialPort->setStopBits(QSerialPort::TwoStop);
        }

        //设置校验位
        if(ui_serial->CheckCb->currentText() == "None")
        {
            serialPort->setParity(QSerialPort::NoParity);
        }
        else if(ui_serial->CheckCb->currentText() == "Even")
        {
            serialPort->setParity(QSerialPort::EvenParity);
        }
        else if(ui_serial->CheckCb->currentText() == "Odd")
        {
            serialPort->setParity(QSerialPort::OddParity);
        }
        LED(true); //绿色LED 表示打开串口

        //串口连接失败提示
        if(serialPort->open(QIODevice::ReadWrite) == false)
        {
           QMessageBox::critical(this, "提示", "串口连接失败");
           LED(false); //红色LED 表示关闭串口
        }
        ui_serial->OpenBt->setText("关闭串口");
    }
    else
    {
        LED(false); //红色LED 表示关闭串口
        serialPort->close(); //关闭串口
        ui_serial->OpenBt->setText("打开串口");
    }
}

串口的收发

  根据hex和ASCII在收发时做判断serialPort->readAll();可以接收所有信息,serialPort->write(SendTextByte); /可以通过串口将数据发送出去

/*
    函   数:serialPortReadyRead_Slot
    描   述:上位机接收数据
    输   入:无
    输   出:无
*/
void serial::serialPortReadyRead_Slot()
{
    QByteArray buf = serialPort->readAll(); //从串口读取信息
    if(ui_serial->ASCII_Receive_Box->isChecked()) //如果设置接收ASCII
    {
        ui_serial->ReceiveEdit->insertPlainText(QString::fromLocal8Bit(buf)); // 对串口接收的数据进行编码
    }
    else if(ui_serial->HEX_Receive_Box->isChecked()) //如果设置接收HEX
    {
        QDataStream out(&buf, QIODevice::ReadWrite); //读取数据
        while(!out.atEnd()) //读取是否完成
        {
            qint8 outChar = 0;
            out >> outChar;
            QString str = QString("%1").arg(outChar&0xFF,2,16,QLatin1Char('0')); //转换16进制
            ui_serial->ReceiveEdit->insertPlainText(str+" "); //每显示一次后面加一个空格
        }
    }
}

/*
    函   数:on_SendBt_clicked
    描   述:上位机发送数据
    输   入:无
    输   出:无
*/
void serial::on_SendBt_clicked()
{
    QString sendstr=ui_serial->SendEdit->toPlainText();  //获取将要发送数据
    if(ui_serial->ASCII_Send_Box->isChecked())  // 如果发送ASCII模式
    {
        QByteArray SendTextByte = sendstr.toLocal8Bit(); //将发送的数据转换格式
        serialPort->write(SendTextByte); // 通过串口将数据发送出去
    }
    else if(ui_serial->HEX_Send_Box->isChecked())  // 如果发送HEX
    {
        QByteArray SendTextByte = QByteArray::fromHex(sendstr.toLatin1());  // 转换数据格式
        serialPort->write(SendTextByte); // 通过串口将数据发送出去
    }
}

接收区清除

  为了直观的读取接收区数据,也需要增加清除当前数据的功能

/*
    函   数:on_ClearBt_clicked
    描   述:清空接收区的数据
    输   入:无
    输   出:无
*/
void serial::on_ClearBt_clicked()
{
    ui_serial->ReceiveEdit->clear();
}

接收数据的保存

  数据量大的时候经常需要保存接收数据的log,所以存log的功能也是必不可少的
file.open(QFile::WriteOnly | QFile::Text)可以将文本数据框取出并按行排列

/*
    函   数:on_SaveBt_clicked
    描   述:以TXT格式保存接收区数据
    输   入:无
    输   出:无
*/
void serial::on_SaveBt_clicked()
{
    QString textFile = QFileDialog::getSaveFileName(this,tr("Save txt"),
                                                            "",tr("text (*.txt)")); //选择路径
     //将文本框数据取出并按行排列
    QFile file(textFile);//文件命名
    if (!file.open(QFile::WriteOnly | QFile::Text))     //检测文件是否打开
    {
        QMessageBox::information(this, "Error Message", "Please Select a Text File!");
        return;
    }
    QTextStream out(&file);                 //分行写入文件
    out << ui_serial->ReceiveEdit->toPlainText();
}

指示灯的使用

  为了美化界面,直观显示串口是否正常连接,可以加个指示灯,设置绿色表示开红色表示关

/*
    函   数:closeEvent
    描   述:关闭窗口时若未关闭串口,则要关闭串口
    输   入:无
    输   出:无
*/
void serial::closeEvent(QCloseEvent *event)
{
    if(serialPort->isOpen())   // 串口处于打开状态
    {
        LED(false); //红色LED 表示关闭串口
        serialPort->close();  // 串口关闭
        event->accept();  // 界面关闭
    }
}

/*
    函   数:LED
    描   述:串口指示灯
    输   入:bool changeColor
    输   出:无
*/
void  serial::LED(bool changeColor)
{
    if(changeColor == true)
    {
        // 显示绿色
        ui_serial->LED->setStyleSheet("background-color: qradialgradient(spread:pad, cx:0.5, cy:0.5, radius:0.5, fx:0.5, fy:0.5, stop:0 rgba(0, 229, 0, 255), stop:1 rgba(255, 255, 255, 255));border-radius:9px;");
    }
    else
    {
        // 显示红色
        ui_serial->LED->setStyleSheet("background-color: qradialgradient(spread:pad, cx:0.5, cy:0.5, radius:0.5, fx:0.5, fy:0.5, stop:0 rgba(255, 0, 0, 255), stop:1 rgba(255, 255, 255, 255));border-radius:9px;");
    }
}

窗口退出

  为了避免退出的时候占用串口,关闭窗口的时候需要关闭串口

/*
    函   数:closeEvent
    描   述:关闭窗口时若未关闭串口,则要关闭串口
    输   入:无
    输   出:无
*/
void serial::closeEvent(QCloseEvent *event)
{
    if(serialPort->isOpen())   // 串口处于打开状态
    {
        LED(false); //红色LED 表示关闭串口
        serialPort->close();  // 串口关闭
        event->accept();  // 界面关闭
    }
}

具体功能

  剩下的就是根据协议规定点击pushbutton发送相应的内容了

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/374830.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

python_蓝桥杯刷题记录_笔记_全AC代码_入门5

前言 关于入门地刷题到现在就结束了。 题单目录 1.P1579 哥德巴赫猜想&#xff08;升级版&#xff09; 2.P1426 小鱼会有危险吗 1.P1579 哥德巴赫猜想&#xff08;升级版&#xff09; 一开始写的代码是三重循环&#xff0c;结果提交上去一堆地TLE&#xff0c;然后我就给减少…

六轴机器人奇异点

1 奇异点说明 有着6个自由度的KUKA机器人具有3个不同的奇点位置。即便在给定状态和步骤顺序的情况下,也无法通过逆向变换(将笛卡尔坐标转换成极坐标值)得出唯一数值时,即可认为是一个奇点位置。这种情况下,或者当最小的笛卡尔变化也能导致非常大的轴角度变化时,即为奇点位置…

如何使用C#调用LabVIEW算法

新建一个工程 这是必须的&#xff1b; 创建项目 项目 点击完成&#xff1b; 将项目另存为&#xff1b;方便后续的使用&#xff1b; 创建 一个测试VI 功能很简单&#xff0c;用的一个加法&#xff1b;将加数A&#xff0c;B设置为输入&#xff0c;和C设置为输出&#xff0c;…

燃油宝行业分析:前景广阔市场潜力巨大

燃油宝&#xff0c;是第三代汽油清净剂的一种俗称(也被称为&#xff0c;汽油清净剂或燃油添加剂)&#xff0c;其目的是解决电喷车型燃油系统的积碳问题&#xff0c;快速恢复发动机最佳工况。 燃油宝对于清洁燃油系统、改善喷油雾化和燃油的充分燃烧、降低排放确实有显著效果&am…

【PyQt】05-多线程

文章目录 前言一、什么是单线程、多线程二、代码现象示例多线程代码运行结果 总结 前言 文章开始还是解释一下&#xff0c;这是跟着王铭东老师学习的。 提示&#xff1a;以下是本篇文章正文内容&#xff0c;下面案例可供参考 一、什么是单线程、多线程 单线程 在Python中&am…

Leetcode第383场周赛

Leetcode第383场周赛 本人水平有限&#xff0c;只做前3道。 一、边界上的蚂蚁 边界上有一只蚂蚁&#xff0c;它有时向 左 走&#xff0c;有时向 右 走。 给你一个 非零 整数数组 nums 。蚂蚁会按顺序读取 nums 中的元素&#xff0c;从第一个元素开始直到结束。每一步&#…

百面嵌入式专栏(面试题)内存管理相关面试题1.0

沉淀、分享、成长,让自己和他人都能有所收获!😄 📢本篇我们将介绍内存管理相关面试题 。 一、内存管理相关面试题 page数据结构中的_refcount和_mapcount有什么区别?匿名页面和高速缓存页面有什么区别?page数据结构中有一个锁,我们称为页锁,请问trylock_page()和loc…

20240206三次握手四次挥手

TCP和UDP异同点 相同点&#xff1a;同属于传输层的协议 不同点&#xff1a; TCP ----> 稳定 1> 提供面向连接的&#xff0c;可靠的数据传输服务 2> 传输过程中&#xff0c;数据无误、数据无丢失、数据无失序、数据无重复 1、TCP会给每个数据包编上编号&#xff…

HGAME 2024 WEEK1 WP

文章目录 WEBezHTTPBypass itSelect Courses2048*16jhat REezASMezPYCezUPXezIDA PWNEzSignIn CRYPTO奇怪的图片ezRSAezMathezPRNG MISCSignIn来自星尘的问候simple_attack希儿希儿希尔签到 放假比较闲&#xff0c;打打比赛 WEB ezHTTP 来自vidar.club、UA要求阿巴阿巴阿巴…

(每日持续更新)jdk api之ObjectInputFilter基础、应用、实战

博主18年的互联网软件开发经验&#xff0c;从一名程序员小白逐步成为了一名架构师&#xff0c;我想通过平台将经验分享给大家&#xff0c;因此博主每天会在各个大牛网站点赞量超高的博客等寻找该技术栈的资料结合自己的经验&#xff0c;晚上进行用心精简、整理、总结、定稿&…

“SET key value [EX seconds] [PX milliseconds] [NX|XX]“和redis分布式锁

一、可选参数介绍 EX second &#xff1a;设置键的过期时间为 second 秒。 SET key value EX second 效果等同于 SETEX key second value 。PX millisecond &#xff1a;设置键的过期时间为 millisecond 毫秒。 SET key value PX millisecond 效果等同于 PSETEX key milliseco…

【QT】VS-code报错:LNK2019: 无法解析的外部符号

目录 0.环境 1.问题简述 2.分析报错原因 3.解决方法 1&#xff09;set() 相关语句 2&#xff09;target_link_libraries() 相关语句 4.参考 0.环境 windows11 、 vs-code 、 qt 、 c、编译器为vs2019-x86_amd64 1.问题简述 项目编译release版本时会报错&#xff1a;报错…

代码随想录算法训练营第十四天 | 110.平衡二叉树、257.二叉树的所有路径、404.左叶子之和

代码随想录算法训练营第十四天 | 110.平衡二叉树、257.二叉树的所有路径、404.左叶子之和 文章目录 代码随想录算法训练营第十四天 | 110.平衡二叉树、257.二叉树的所有路径、404.左叶子之和1 LeetCode 110.平衡二叉树2 LeetCode 257.二叉树的所有路径3 LeetCode 404.左叶子之和…

浅析一款非驱动考试网关程序(一)

前言 监考程序需要对网络流量进行过滤&#xff0c;不允许除了考试网站以外的其他网站的访问。其实就是实现了一个小型的网关程序&#xff0c;一般地有驱动实现和非驱动实现两种方式。本文只针对一款简易的非驱动实现的监考程序进行分析。 注意&#xff1a;本文通过对某考试监…

第十篇【传奇开心果系列】Python的OpenCV技术点案例示例:图像分割

传奇开心果短博文系列 系列短博文目录Python的OpenCV技术点案例示例系列短博文目录一、前言二、OpenCV图像分割介绍三、OpenCV分割算法示例代码四、归纳总结系列短博文目录 Python的OpenCV技术点案例示例系列 短博文目录 一、前言 OpenCV是一个广泛应用于计算机视觉和图像处…

2023年09月CCF-GESP编程能力等级认证Python编程五级真题解析

Python等级认证GESP(1~6级)全部真题・点这里 一、单选题(共15题,共30分) 第1题 近年来,线上授课变得普遍,很多有助于改善教学效果的设备也逐渐流行,其中包括比较常用的手写板,那么它属于哪类设备?( ) A:输入 B:输出 C:控制 D:记录 答案:A 第2题 以下关于…

计算机服务器中了mkp勒索病毒如何解密,mkp勒索病毒解密流程

随着网络技术的不断发展与应用&#xff0c;越来越多的企业走向数字化办公模式&#xff0c;计算机极大地方便了企业的正常生产运营&#xff0c;但网络威胁的手段也不断增加。近期&#xff0c;云天数据恢复接到很多企业的求助&#xff0c;企业的计算机服务器遭到了mkp勒索病毒攻击…

Qt 常见容器类用法(一)

目录 QMap类 QHash类 QVector类 QMap类 QMap<key,T>提供一个从类型为Key的键到类型为T的值的映射。通常&#xff0c;QMap存储的数据形式是一个键对应一个值&#xff0c;并且按照键Key的次序存储数据。为了能够支持一键多值的情况&#xff0c;QMap提供QMap<key,T&g…

相机图像质量研究(4)常见问题总结:光学结构对成像的影响--焦距

系列文章目录 相机图像质量研究(1)Camera成像流程介绍 相机图像质量研究(2)ISP专用平台调优介绍 相机图像质量研究(3)图像质量测试介绍 相机图像质量研究(4)常见问题总结&#xff1a;光学结构对成像的影响--焦距 相机图像质量研究(5)常见问题总结&#xff1a;光学结构对成…

MPLS——多协议标签交换

目录 1 多协议标签交换 MPLS 1.1 MPLS 的工作原理 1.1.1 MPLS 工作特点 1.1.2 MPLS 协议的基本原理 1.1.3 MPLS 的基本工作过程 1.2 转发等价类 FEC 1.2.1 FEC 用于负载平衡 1.3 MPLS 首部的位置与格式 1.3.1 MPLS 首部的位置 1.3.2 MPLS 首部的格式 1.4 新一代的…