C2—Qt实现串口调试助手2021.10.21
2021/10/21 23:13:35
本文主要是介绍C2—Qt实现串口调试助手2021.10.21,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
Qt实现串口调试助手
1.目标
实现串口调试助手,界面风格简明,可配置关键通信参数包括,波特率、停止位、数据位、校验位;可以以16进制发送数据,以16进制或者ascii码接收数据,支持将接收数据保存至本地txt。
注:查看FPGA下位机串口代码实现,请查看FPGA串口通信
下载FPGA串口通信工程文件和代码,请查看FPGA串口通信vivado工程
2.步骤
2.1.pro当中添加,串口模块。
该模块Qt5支持,Qt4不支持;
QT += serialport
2.2串口相关类介绍
#include <QtSerialPort/QSerialPortInfo> #include <QtSerialPort/QSerialPort>
QSerialPortInfo这个类将会获取串口的信息,如下:
qDebug()<<"serialPortName:"<<info.portName(); qDebug() << "Description : " << info.description(); qDebug() << "Manufacturer: " << info.manufacturer(); qDebug() << "Serial Number: " << info.serialNumber(); qDebug() << "System Location: " << info.systemLocation();
QSerialPort提供了多种方法,如基本的open、close、read、write方法,以及错误处理、设置串口参数等多个方法,将协调工作,共同完成串口收发的操作。
2.3界面原型搭建
一些界面布局细节,排版原则在下面无限保真度的界面原型中全部体现,这个搭建不需要手工代码。
2.4界面逻辑
(1)调用Init()函数,初始化函数将完成以下功能
上来先查询当前在线的串口设备,并将其写入下拉框;
初始化波特率选择下拉框,以及校验位、停止位、数据位;
设置默认以16进制显示;
清除接收缓冲区(黑色区域)。
(2)做按钮功能连接
”扫描串口“按钮重新扫描当前串口设备,并将其状态更新到对应下拉框中;
“打开串口”按钮将下发串口配置打开串口,添加打开失败模态提示用户,添加打开成功该按钮变为“关闭串口”;
"关闭串口"按钮将clear串口并关闭串口;
“清除接收”将textEdit clear掉;
“保存窗口”将当前接收框中内容保存至工程目录下的txt,并且清空当前接收框;
“发送”将进行数据处理操作并发送至打开的串口,当然会有串口失败的提示;
“清除发送”将清除当前的发送框;
(3)调整界面逻辑细节
“串口选择”QFrame固定大小,不随界面的大小变化;
“串口选择”中水平元素水平布局之后总体垂直布局;
“接收框”为只读模式;
设置“接收框”样式表,黑色背景,绿色字体;
“发送框”默认显示本人logo;
2.5底层逻辑
(1)实现获取当前配置函数,返回值为枚举类型,方便“打开串口操作”时调用,以获取校验位当前配置为例,如下:
enum QSerialPort::Parity MainWindow::getParity() { QString currentParityBit = ui->checkBit_Sel->currentText(); if (currentParityBit == "None") return QSerialPort::NoParity; else if (currentParityBit == "Odd")//奇数 return QSerialPort::OddParity; else if (currentParityBit == "Even")//偶校验 return QSerialPort::EvenParity; else return QSerialPort::UnknownParity; }
(2)接收数据,串口数据缓冲区收到数据会发出信号&QSerialPort::readyRead,故用户只需写槽函数并将其链接。
notice:
(2.1)比如对方发送 0x11、0x22、0x33、0x44、0x55,的数据,很可能会触发很多次 ReadSerialData() 调用,并不是收完这 5 个数据,才会触发一次,但是读走的数据,下次不会重复读到,这点可以放心;
(2.2)这里需要去判断读取的 QByteArray 的 buffer 是不是空,调试的时候遇到过,空数据,也触发了一次进入这个槽函数;
(2.3)接收(发送)数据类型为QByteArray的类型,接收侧需要做数据类型转换方便显示处理
str.append(QString::number((ary[i]&0xff),16));//QByteArray-->QString
(2.4)接收操作read、readAll,以及发送操作write都是非阻塞操作,即为函数执行,无论数据是否被全部接收(发送),函数都会立即返回,程序将会继续执行。
(2.5)支持16进制的显示、Ascii码的显示(两种数据类型处理)
(3)发送数据
默认按照16进制的方式发送数据,因此将只允许16进制相关的字符输入,其他的字符将不允许输入。
3.代码
3.1.cpp文件代码
#include "mainwindow.h" #include "ui_mainwindow.h" #include <QDebug> #include <QMessageBox> #include <QFile> #include <QTextStream> #include <QByteArray> #include <QTextDecoder> #include <QTextCodec> MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) , ui(new Ui::MainWindow) { ui->setupUi(this); //QAbstractNativeEventFilter QT系统事件过滤器,例如串口的插拔可以识别 //即中断的方式实时检测设备状态 Init();//初始化函数,作用:扫描在线串口;填充波特率、校验位、数据位、停止位窗口部件 //重新扫描在线串口(显示当前设备状态) connect(ui->scanBtn,&QPushButton::clicked,[=](){ ui->comboBox->clear(); foreach(const QSerialPortInfo &info,QSerialPortInfo::availablePorts()) { ui->comboBox->addItem(info.portName()); } }); //打开串口按钮 connect(ui->openBtn,&QPushButton::clicked,[=](){ if(ui->openBtn->text()=="打开串口") { // qDebug()<<getBaud()<<getParity()<<getdataBits()<<getstopBits(); mySerialPort->setPortName(ui->comboBox->currentText()); mySerialPort->setBaudRate(getBaud()); mySerialPort->setParity(getParity()); mySerialPort->setDataBits(getdataBits()); mySerialPort->setStopBits(getstopBits()); // mySerialPort->setFlowControl(QSerialPort::NoFlowControl); // TODO: FlowControl // mySerialPort->setReadBufferSize(0); //缓冲区无限大 if(!(mySerialPort->open(QSerialPort::ReadWrite))) { //标准对话框提示打开串口失败 QMessageBox::warning(this,"警告","打开串口失败"); } else { ui->openBtn->setText("关闭串口"); } } else { mySerialPort->clear(); mySerialPort->close(); ui->openBtn->setText("打开串口"); } }); //保存按钮 connect(ui->saveBtn,&QPushButton::clicked,[=](){ QFile file("UART_Rec.txt"); file.open(QIODevice::Append); //使用QTextStream写入文件 QTextStream tsm(&file); tsm<<ui->reci_Edit->toPlainText(); ui->reci_Edit->clear(); file.close(); }); //清除接收按钮 connect(ui->clearRecBtn,&QPushButton::clicked,[=](){ ui->reci_Edit->clear(); }); //清除发送按钮 connect(ui->clearXferBtn,&QPushButton::clicked,[=](){ ui->xferEdit->clear(); }); //发送按钮 connect(ui->xferBtn, &QPushButton::clicked, this, [=](){ if(!mySerialPort->isOpen()) { QMessageBox::information(this,"提示","请先打开串口"); } else { // //获取发送框字符 QString str = ui->xferEdit->document()->toPlainText(); if (str.isEmpty()) { QMessageBox::information(this,"提示","请先输入发送内容"); } //默认16进制发送,过滤无用字符 包括0x else { char ch; bool flag = false; uint32_t i, len; //去掉无用符号 str = str.replace(' ',""); str = str.replace(',',""); str = str.replace('\r',""); str = str.replace('\n',""); str = str.replace('\t',""); str = str.replace("0x",""); str = str.replace("0X",""); str = str.replace('\\',""); str = str.replace(':',""); str = str.replace('?',""); str = str.replace(';',""); //判断数据合法性 for(i = 0, len = str.length(); i < len; i++) { ch = str.at(i).toLatin1(); if (ch >= '0' && ch <= '9') { flag = false; } else if (ch >= 'a' && ch <= 'f') { flag = false; } else if (ch >= 'A' && ch <= 'F') { flag = false; } else { flag = true; } } if(flag) { QMessageBox::warning(this,"警告","输入内容包含非法16进制字符"); } else { //QString转QByteArray QByteArray xferAry = str.toUtf8(); mySerialPort->write(xferAry); } } } }); //接收Buffer connect(mySerialPort,&QSerialPort::readyRead,[=](){ if(ui->hex_Rtn->isChecked()) { //以16进制显示 QByteArray ary = mySerialPort->readAll(); qDebug()<<ary; QString str; str.clear(); for (int i = 0;i<ary.size()-2 ;i++ ) { str.append(QString::number((ary[i]&0xff),16));//将16进制原样输出 // str.append(" "); } ui->reci_Edit->append(str); // ui->reci_Edit->append("\n"); } else { //以Ascii码显示 Ascii码指的是16进制所对应的字符,就是ascii规定的一个16进制和字符之间的映射 ui->reci_Edit->append(mySerialPort->readAll()); ui->reci_Edit->append("\n"); } }); } MainWindow::~MainWindow() { if(mySerialPort->isOpen()) { mySerialPort->clear(); mySerialPort->close(); } delete mySerialPort; delete ui; } enum QSerialPort::Parity MainWindow::getParity() { QString currentParityBit = ui->checkBit_Sel->currentText(); if (currentParityBit == "None") return QSerialPort::NoParity; else if (currentParityBit == "Odd")//奇数 return QSerialPort::OddParity; else if (currentParityBit == "Even")//偶校验 return QSerialPort::EvenParity; else return QSerialPort::UnknownParity; } enum QSerialPort::BaudRate MainWindow::getBaud() { bool ok; enum QSerialPort::BaudRate ret; switch (ui->baud_Sel->currentText().toLong(&ok, 10)) { case 1200: ret = QSerialPort::Baud1200; break; case 2400: ret = QSerialPort::Baud2400; break; case 4800: ret = QSerialPort::Baud4800; break; case 9600: ret = QSerialPort::Baud9600; break; case 19200: ret = QSerialPort::Baud19200; break; case 38400: ret = QSerialPort::Baud38400; break; case 57600: ret = QSerialPort::Baud57600; break; case 115200: ret = QSerialPort::Baud115200; break; default: ret = QSerialPort::UnknownBaud; break; } return ret; } enum QSerialPort::StopBits MainWindow::getstopBits() { QString currentStopBits = ui->stopBit_Sel->currentText(); if (currentStopBits == "1") return QSerialPort::OneStop; else if (currentStopBits == "1.5") return QSerialPort::OneAndHalfStop; else if (currentStopBits == "2") return QSerialPort::TwoStop; else return QSerialPort::UnknownStopBits; } enum QSerialPort::DataBits MainWindow::getdataBits() { QString currentDataBits = ui->dataBit_Sel->currentText(); if(currentDataBits == "5") return QSerialPort::Data5; else if (currentDataBits == "6") return QSerialPort::Data6; else if (currentDataBits == "7") return QSerialPort::Data7; else if (currentDataBits == "8") return QSerialPort::Data8; else return QSerialPort::UnknownDataBits; } void MainWindow::Init() { //上来先扫描一遍当前串口,初始化COM口选择组件 QStringList myPortName; foreach(const QSerialPortInfo &info,QSerialPortInfo::availablePorts()) { myPortName << info.portName(); qDebug()<<"serialPortName:"<<info.portName(); qDebug() << "Description : " << info.description(); qDebug() << "Manufacturer: " << info.manufacturer(); qDebug() << "Serial Number: " << info.serialNumber(); qDebug() << "System Location: " << info.systemLocation(); } myPortName.sort(); ui->comboBox->addItems(myPortName); //初始化波特率组件 QStringList myBaud; myBaud.clear(); myBaud<<"1200"<<"2400"<<"4800"<<"9600"<<"19200"<<"38400"<<"57600"<<"115200"; ui->baud_Sel->addItems(myBaud); ui->baud_Sel->setCurrentText("9600"); //初始化停止位 QStringList mystopBits; mystopBits.clear(); mystopBits<<"1"<<"1.5"<<"2"; ui->stopBit_Sel->addItems(mystopBits); ui->stopBit_Sel->setCurrentText("1"); //初始化校验位 QStringList mycheckBits; mycheckBits.clear(); mycheckBits<<"None"<<"Even"<<"Odd"; ui->checkBit_Sel->addItems(mycheckBits); ui->checkBit_Sel->setCurrentText("None"); //初始化数据位 QStringList mydataBits; mydataBits.clear(); mydataBits<<"5"<<"6"<<"7"<<"8"; ui->dataBit_Sel->addItems(mydataBits); ui->dataBit_Sel->setCurrentText("8"); ui->hex_Rtn->setChecked(true);//设置默认是16进制显示 ui->reci_Edit->clear(); }
3.2.h代码
#ifndef MAINWINDOW_H #define MAINWINDOW_H #include <QMainWindow> #include <QtSerialPort/QSerialPortInfo> #include <QtSerialPort/QSerialPort> QT_BEGIN_NAMESPACE namespace Ui { class MainWindow; } QT_END_NAMESPACE class MainWindow : public QMainWindow { Q_OBJECT public: MainWindow(QWidget *parent = nullptr); ~MainWindow(); enum QSerialPort::Parity getParity(); enum QSerialPort::BaudRate getBaud(); enum QSerialPort::StopBits getstopBits(); enum QSerialPort::DataBits getdataBits(); void Init(); //创建一个串口对象 QSerialPort *mySerialPort = new QSerialPort(); private: Ui::MainWindow *ui; }; #endif // MAINWINDOW_H
4.效果
使用虚拟串口软件VSPD虚拟串口,使用成熟软件XCOM V2.0(正点原子串口调试助手)与自己的软件通信,可以清晰的看到,软件收发正常。VSPD软件的获取与安装请查看工具类–虚拟串口工具
5.思考
两个想法,1.USB属于热插拔设备,系统可以动态的检测设备的插入与断开,那么如何让自己的软件动态的查看串口设备的连接状态呢?实际上,Qt提供了“中断”接口,可以做做升级版;
2.现在只支持16进制发送,支持16进制、ascii码的接收,那么是否可以考虑利用串口传输汉字呢?实际上,Qt支持多种字符集,可以尝试一番。
这篇关于C2—Qt实现串口调试助手2021.10.21的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-11-15Typescript 类型教程:轻松入门与实践指南
- 2024-11-15AntDesign-icons项目实战:新手入门教程
- 2024-11-14用Scratch编写语言模型:爪爪(Clawed)式简易教程
- 2024-11-14用大型语言模型在Amazon Bedrock上分类Jira工单
- 2024-11-14从数据到行动:亚马逊Bedrock代理如何自动化复杂工作流
- 2024-11-14Databricks与优化后的Snowflake性能大比拼
- 2024-11-14亚马逊 Inspector 解析:提升您的 AWS 负载安全的利器
- 2024-11-14揭秘VS Code for Web - Azure:轻松开发云端应用的新利器
- 2024-11-14揭秘指南:如何让Databricks中的数据为最终用户所用
- 2024-11-14OpenTelemetry扩展进入CI/CD可观测性领域