从零开始,教你写一个串口调试助手

news2024/11/28 16:31:08

摘要:相信很多小伙伴都没接触过QT,如果想用QT写一个调试助手,首先是要会一点C++语法。只要能看得懂C++的代码,就能很快的写一个串口调试助手。

下面先推荐两个视频教程,感兴趣的可以看一看!

1、B站Jomse工

B站Jomse工

看完你基本就知道串口调试助手大概是个啥样了,有了大致的了解,然后再去看和修改别人的代码。

2、B站lililanglang

B站lililanglang

把这两个视频看完后,基本你就能够自己写一个串口调试助手了,视频很短没有废话,全程都是干货,而且别人也给出了源码。

不过,为了大家方便理解,我还是写一篇手把手教你学QT串口调试助手吧!

3、新建工程

1、首先,你要安装QT的开发工具Qt Creator。安装过程这里就不再重复说了,大家可以看我的CSDN博客号前面发的一些文章,有介绍的。

然后,打开QtCreator新建一个项目:

项目名称根据需求自己指定即可,在指定项目的存储路径的时候,路径中不能包含中文,不能包含中文,不能包含中文。

直接写一步:

Class name可以随便写一个,一般默认不变。

Base nameQMainWindowQWidgetQDialog随便选一个即可。

编译套件用于项目文件的编译,如果安装了多个编译套件,在这里选择其中一个就可以了。

版本控制工具没有的话可以不选,主要用于团队开发,点击完成。

这样我们就新建好了一个项目工程!

双击ui文件即可进入设计模式,进入界面设计器Qt Designer编辑状态,开始进行设计器( Qt Designer )编程。

在这个界面,我们可以拖动左边的常用界面的空间到编辑窗口,然后生成可视化的界面。

样式1

样式2

可以看到,通过拖动控件与修改控件属性就可以做出上面这样的界面,我个人觉得还是非常的美观nice的。这里只是给大家一个参考的界面和模板,排版和配色大家按照自己喜欢的就可以了!

一、导入库

1、在项目.pro文件中加入serialport

QT       += core gui
QT       += serialport

2、引入qt中串口通信需要的头文件。

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

二、配置串口初始化

1、查找可用的串口

通过创建一个comobox,将可用串口的列表展示出来,并用于配置时选择要连接的串口。

//查找可用串口,刷新串口信息
void MainWindow::GetAveriablePort()
{
     ui->uartReadPlain->insertPlainText("串口初始化:\r\n");
 
     //先清除所有串口列表
      ui->portBox->clear();
 
    foreach(const QSerialPortInfo &info,QSerialPortInfo::availablePorts())
    {
        QSerialPort serial;
        serial.setPort(info);
 
        if(serial.open(QIODevice::ReadWrite))
        {
            ui->uartReadPlain->insertPlainText("可用:"+serial.portName()+"\r\n");
            ui->portBox->addItem(serial.portName());
            serial.close();
        }
        else
        {
            ui->uartReadPlain->insertPlainText("不可用:"+serial.portName()+"\r\n");
        }
    }
}

2、配置串口

(1)串口的配置至少应当包含串口号、波特率、数据位、停止位、奇偶校验位、流控,这些都可以通过串口实例调用函数配置。可以加入几个comobox或者文本框来选择,也可以默认初始化时就配好。

//配置串口初始化
void MainWindow::PortConfigureInit()
{
    //填入串口选项
    ui->rateBox->addItem("115200","115200");
    ui->rateBox->addItem("38400","38400");
    ui->rateBox->addItem("19200","19200");
    ui->rateBox->addItem("9600","9600");
 
    ui->dataBox->addItem("8",8);
    ui->dataBox->addItem("7",7);
 
    ui->checkBox->addItem("无校验",0);
 
    ui->stopBox->addItem("1位",1);
    ui->stopBox->addItem("2位",2); 
}

(2)加入一个打开关闭串口的按钮,文本显示“打开串口”时,点击可以关闭串口。文本显示“关闭串口“则相反。

(3)打开串口时,把配置的项的box都disable,使其不可修改,关闭时恢复。

//串口开关按钮
void MainWindow::on_openSerialButton_clicked()
{
    //尝试打开串口
    if(ui->openSerialButton->text() == tr("打开串口"))
    {
        if(ui->portBox->currentText() == "" )
        {
            QMessageBox::warning(NULL, "警告", "无可开启串口!\r\n\r\n");
            return;
        }
 
        serial = new QSerialPort;
        //设置串口名
        serial->setPortName(ui->portBox->currentText());
        //打开串口
        serial->open(QIODevice::ReadWrite);
        //设置波特率
        serial->setBaudRate(ui->rateBox->currentText().toInt());
        //设置数据位
        switch (ui->dataBox->currentData().toInt())
        {
            case 8:
                serial->setDataBits(QSerialPort::Data8);
                break;
            case 7:
                serial->setDataBits(QSerialPort::Data7);
                break;
            default:
                break;
        }
        //设置校验位
        switch (ui->checkBox->currentIndex())
        {
            case 0:
                serial->setParity(QSerialPort::NoParity);
                break;
            default:
                break;
        }
        //设置停止位
        switch(ui->stopBox->currentIndex())
        {
            case 0:
                serial->setStopBits(QSerialPort::OneStop);
                break;
            case 1:
                serial->setStopBits(QSerialPort::TwoStop);
                break;
            default:
                break;
        }
        //设置流控制
        serial->setFlowControl(QSerialPort::NoFlowControl); //设置为无流控制
 
        //关闭设置菜单使能
        ui->portBox->setEnabled(false);
        ui->dataBox->setEnabled(false);
        ui->checkBox->setEnabled(false);
        ui->stopBox->setEnabled(false);
        ui->rateBox->setEnabled(false);
        ui->openSerialButton->setText("关闭串口");
 
        fTimeCounter.restart();  //计时器重新计数
 
        //连接信号和槽函数,串口有数据可读时,调用ReadData()函数读取数据并处理。
        QObject::connect(serial,&QSerialPort::readyRead,this,&MainWindow::ReadData);
    }
    else
    {
        uartRecDataTimer->stop () ; //定时器停止
 
        if(serial->isOpen())       //原先串口打开,则关闭串口
        {
            serial->close();
        }
 
        //释放串口
        delete serial;
        serial = NULL;
 
        //恢复使能
        ui->portBox->setEnabled(true);
        ui->rateBox->setEnabled(true);
        ui->dataBox->setEnabled(true);
        ui->checkBox->setEnabled(true);
        ui->stopBox->setEnabled(true);
        ui->openSerialButton->setText("打开串口");
    }
}

三、读取串口数据

1、为了读取数据,要创建一个定时器和一个计时器。因为要解决两个问题:

一是我们需要一个超时间隔,用于在串口一定时间收不到数据时,判断一次接收完成,处理数据并清空buff

二是需要一个计数,统计串口已经连续接收了多久,即使数据一直不断,我们也要在一个固定时时间点强制判断一次接收完成,处理数据并清空buff,否则可能会导致数据永远得不到处理。

1、初始化定时器

    //设置uart接收缓冲超时定时器
    uartRecDataTimer = new QTimer(this);
    uartRecDataTimer->stop();
    uartRecDataTimer->setInterval(uartRecOvertimeCount*1000);                     //设置定时周期,单位:毫秒
    uartRecDataTimer->setSingleShot(true);                                        //设置为单次触发
    connect(uartRecDataTimer,SIGNAL(timeout()),this,SLOT(uartRec_timeout()));     //设置槽

2、实现ReadData

在计时器超出一个指定间隔后,强制处理已经接收完的buff缓冲,其余时间则是把数据放进缓冲中,重启定时器。

//读取串口接收消息
void MainWindow::ReadData()
{
    //串口可读数据长度
    int byteLen = serial->bytesAvailable();
    if(byteLen < 0)
    {
        return;
    }
 
    rec_buf_len += byteLen;
    uart_rec_ss.append(serial->readAll());  //读取数据
 
    //计时器超过最大间隔仍未填入数据,强制填入
    if(fTimeCounter.elapsed() >2000 && uart_rec_ss.size()>0)
    {
        ui->uartReadPlain->moveCursor(QTextCursor::End);        //光标移动到结尾
        ui->uartReadPlain->insertPlainText(uart_rec_ss);
        ui->uartReadPlain->moveCursor(QTextCursor::End);        //光标移动到结尾
        uart_rec_ss.clear();
    }
 
    //定时器开始工作、定时器重启
    uartRecDataTimer->start();
}

3、数据内容做填充

定时器接收完成处理(一段时间没有数据接收,定时器超时)根据时间戳是否被选择,将数据内容做填充,插入到存放数据的文本框里。

//定时器触发打印串口数据
void MainWindow::uartRec_timeout()
{
    if(!uart_rec_ss.isEmpty())
    {
        curDateTime = QDateTime::currentDateTime();
        ui->uartReadPlain->moveCursor(QTextCursor::End);            //光标移动到结尾
 
        if(ui->timeZoneCheckBox->isChecked())
        {
            ui->uartReadPlain->insertPlainText("\r\n"+curDateTime.toString("[yyyy-MM-dd hh:mm:ss]")+"R:");
            ui->uartReadPlain->moveCursor(QTextCursor::End);        //光标移动到结尾
            ui->uartReadPlain->insertPlainText(uart_rec_ss);
        }
        else
        {
            ui->uartReadPlain->insertPlainText(uart_rec_ss);
        }
        ui->uartReadPlain->moveCursor(QTextCursor::End);            //光标移动到结尾
        uart_rec_ss.clear();
        fTimeCounter.restart();
        ui->RXLenLabel->setText(QString::number(rec_buf_len)+"bytes");
    }
}

4、配置超时间隔的选项

(1)初始化时加入一个配置框

//设置时间输入框只允许使用数字
ui->overTimeRecEdit->setValidator(new QRegExpValidator(QRegExp("^([0-9]{1,4}(.[0-9]{1,3})?)$")));
ui->overTimeRecEdit->setText(QString::number(uartRecOvertimeCount));

(2)运行中配置超时间隔

//超时间隔设置
void MainWindow::on_overTimeRecEdit_returnPressed()
{
    if(ui->overTimeRecEdit->text().toFloat()>60)
    {
        QMessageBox::warning(NULL,"警告","超时时间不要超过1分钟");
        ui->overTimeRecEdit->setText("0.1");
        return;
    }
    uartRecOvertimeCount = ui->overTimeRecEdit->text().toFloat();
    ui->uartReadPlain->insertPlainText("设置超时时间为:"+QString::number(uartRecOvertimeCount*1000)+"ms");
    uartRecDataTimer->setInterval(uartRecOvertimeCount*1000);                       //设置定时周期,单位:毫秒
 
    fTimeCounter.restart();
    uartRecDataTimer->start();
}

四、发送数据

简单的发送数据没有什么要额外配置的,调用write函数就可以了,可以根据自己的实际情况做一些配置或是校验处理,比如加回车换行什么的。

//发送串口数据
void MainWindow::on_sendDataButton_clicked()
{
    //未打开串口则不准发送
    if(ui->openSerialButton->text() == "打开串口")
    {
        QMessageBox::warning(NULL, "警告", "未打开可用串口,无法发送数据!\r\n\r\n");
        return;
    } 
    //获取发送的命令,并选择在结尾加上换行,AT的命令结尾必须有回车换行
    QString command = ui->uartWritePlain->toPlainText();
    if(ui->changeLineCheckBox->isChecked())
    {
        command += "\r\n";
    } 
    if(ui->timeZoneCheckBox->isChecked())
    {
         curDateTime = QDateTime::currentDateTime();
         ui->uartReadPlain->insertPlainText("\r\n"+curDateTime.toString("[yyyy-MM-dd hh:mm:ss]")+"SEND:"+command);
    } 
    send_buf_len += command.length();
    ui->TXLenLabel->setText(QString::number(send_buf_len)+"bytes"); 
    serial->write(command.toLatin1());
}

至此,一个最基本的串口调试工具就完成了,下面就是给它添加功能和优化了。你可以加入一些功能比如修改样式、保存数据等等。

开源代码

最后想说的是,用QT写上位机的源码例程很多,初学者可以先借鉴别人的代码参考修改,按照自己的喜好以及编程风格进行排版和布局配色即可!

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

项目建议书怎么写?

Why 为什么需要项目建议书? 行政、国企、事业单位间的买卖需要按国家规定的“政府采购法”进行(个体户,私企间的买卖不需要按“政府采购法”进行),不是个人的消费,有一整套流程,《项目建议书》这套流程中的产物之一。 即国家规定,纳税人的钱不能乱花,买之前要想好买…

南卡和Snowkids电容笔哪款更值得入手?口碑最佳的国产电容笔

随着苹果推出了Apple Pencil之后后&#xff0c;平替电容笔的产品也随之出现了不少&#xff0c;其中大部分都是没很高知名度的牌子&#xff0c;价格也是五花八门&#xff0c;有便宜的也有昂贵的&#xff0c;让消费者有了更多的选择&#xff0c;对于第一次购买电容笔的小白来说但…

【Python】【期末复习题】

文章目录一、单选题&#xff08;20分&#xff09;二、判断题&#xff08;10分&#xff09;三、填空题&#xff08;10分&#xff09;四、问答题&#xff08;共30分&#xff0c;6题&#xff0c;每题5分&#xff09;五、程序题&#xff08;3题&#xff0c;每题10分&#xff0c;共3…

WIFI智能电子标牌的优势

一、与市面产品对比有什么优化升级? 使用环境标准wifi通信&#xff0c;避免单独安装基站&#xff0c;减少部署的麻烦。 二、刷新一次消耗多少电量&#xff0c;平均每秒消耗多少? 0.09879mAh&#xff0c;每秒平均电流105uA。 三、充满电需要多久&#xff0c;充一次电可以用…

【JS020】原生JS实现AJAX

日期&#xff1a;2022年12月14日 作者&#xff1a;Commas 签名&#xff1a;(ง •_•)ง 积跬步以致千里,积小流以成江海…… 注释&#xff1a;如果您觉得有所帮助&#xff0c;帮忙点个赞&#xff0c;也可以关注我&#xff0c;我们一起成长&#xff1b;如果有不对的地方&#x…

TFT显示模组是什么?tft显示模组有什么功能?

TFT显示模组的出现&#xff0c;让手机屏幕成为一种刚需。但随着技术的发展&#xff0c;越来越多的厂商开始推出折叠屏手机&#xff0c;这也意味着手机屏幕的形态正在发生变化。在此背景下&#xff0c;折叠屏手机市场迎来爆发期。根据中国信通院数据&#xff0c;2020年上半年&am…

Kubernetes包管理器Helm的本质

“本质”类的文章&#xff0c;通常很难带流量。而且写起来非常吃力。那我为什么还要写&#xff1f;写作是对自己的锻炼。写作是让自己的思想更有深度的一种有效方式。如果你觉得这篇文章对你有帮助&#xff0c;也你麻烦你转发这篇文章&#xff0c;这是对我的帮助。谢谢。Kubern…

socket.io 使用protobuf 协议发送消息

一、服务端 1、maven引入netty-socketio <dependency><groupId>com.corundumstudio.socketio</groupId><artifactId>netty-socketio</artifactId><version>1.7.22</version></dependency> 2、服务端java代码 Service public…

艾美捷内皮细胞生长添加剂不同研究工具推荐

艾美捷Relia Tech内皮细胞生长添加剂可用于培养没有饲养细胞的杂交瘤细胞&#xff08;Pintus&#xff0c;1983&#xff09;。这些脑提取物中的活性因子已被鉴定为aFGF的变体&#xff08;Burgesse&#xff0c;1985&#xff09;。该ECGF产品补充了生理浓度的人rec.VEGF-A&#xf…

初识 Markdown编辑器

这里写自定义目录标题欢迎使用Markdown编辑器新的改变功能快捷键合理的创建标题&#xff0c;有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格设定内容居中、居左、居右SmartyPants创建一个自定义列表如何创建一个注…

真兰仪表通过深交所注册:拟募资17.4亿 上半年净利下降27%

雷递网 雷建平 12月13日上海真兰仪表科技股份有限公司&#xff08;简称&#xff1a;“真兰仪表”&#xff09;日前通过注册&#xff0c;准备在深交所创业板上市。真兰仪表计划募资17.4亿元。其中&#xff0c;7.76亿用于真兰仪表科技有限公司燃气表产能扩建项目&#xff0c;6.12…

NFA的确定化

一、实验目的 &#xff08;1&#xff09;通过本次实验&#xff0c;加深对正则表达式、NFA、DFA及其识别的语言的理解&#xff1b; &#xff08;2&#xff09;掌握从NFA到DFA的转换&#xff0c;以及用子集法把NFA转换成DFA理论&#xff0c;编程实现将NFA&#xff08;不确定有穷…

SAP 事务代码BD20不能处理状态为51的IDoc

SAP 事务代码BD20不能处理状态为51的IDoc 对于SAP IDoc相关的事务代码比如WE02,WE19,BD87等都比较熟悉&#xff0c;因为使用的比较多。但是对于事务代码BD20却很少使用。 笔者在近期的一个项目上&#xff0c;听到客户的global team有使用该事务代码&#xff0c;设置成了一个job…

Oracle RMAN备份相关信息查询

查询 RMAN 备份状态主要是通过视图V$RMAN_STATUS 来进行&#xff0c;这个视图可以查询到 RMAN 执行的操作。 我们主要查看两列&#xff1a;OPERATION 和 STATUS。 OPERATION 的值有&#xff1a;RMAN、BACKUP、DELETE、CROSSCHECK、DELETE OBSOLETE等。 STATUS的值有&#xff1a…

中国电信携手华为建成全球首个支持5G RedCap联合测试能力的5G开放实验室

近日&#xff0c;中国电信物联网开放实验室宣布与华为共同完成了5G RedCap实验室技术验证&#xff0c;并建成了全球首个具备5G R17标准RedCap联合测试能力的开放实验室。 此次技术验证&#xff0c;为RedCap的规模商用奠定了坚实基础&#xff0c;有助于打造RedCap行业终端生态认…

良心总结!Git 各指令的本质,真是通俗易懂啊

1前言 作为当前世界上最强大的代码管理工具Git相信大家都很熟悉&#xff0c;但据我所知有很大一批人停留在clone、commit、pull、push...的阶段&#xff0c;是不是对rebase心里没底只敢用merge&#xff1f;碰见版本回退就抓瞎&#xff1f;别问我怎么知道的&#xff0c;问就是&…

什么是成熟的自动化运维平台?

本文首发于知乎&#xff0c;由嘉为蓝鲸原创。 商业转载请联系作者获得授权&#xff0c;非商业转载请注明出处。 当企业遇到运维管理对象的急速增长&#xff0c;业务需求频繁变更等传统运维场景问题时&#xff0c;依靠手工运维已经远远满足不了需求&#xff0c;因此我们需要搭建…

2022,itbird的年终总结报告

最近公司要求个人在做年终总结了&#xff0c;趁着这个机会&#xff0c;也想对自己的2022年进行一下回顾总结&#xff0c;最重要的是&#xff0c;对2023的目标&#xff0c;可以有一个指引。 就从工作和生活两方面来讲吧。 1.工作 1.1 行业的状态 本人从事的是android开发工作…

Cuda个别库函数的兼容性 - shuffle\数学库\原子

兼容性针对的是不同的Cuda版本和设备计算能力(compute capability) shuffle 在C\C扩展一节 新版本函数见Cuda12.0 文档 __shfl_sync, __shfl_up_sync, __shfl_down_sync, and __shfl_xor_sync exchange a variable between threads within a warp. Supported by devices of …

信创入围认证详解

信创是一个统称概念&#xff0c;实际是把现有与信息技术相关的行业结合在一起&#xff0c;命名为“信息技术应用创新产业”&#xff0c;简称“信创”。一般来说&#xff0c;信创包括基础硬件、基础软件、应用软件、信息安全四大板块。其中&#xff0c;基础硬件主要包括&#xf…