QT串口接收数据并进行波形显示(含源码)

**使用QT在串口调试助手基础上实现波形显示(含源码)

评论比较多留言需要源码的,逐个发邮箱比较麻烦也不能及时回复,现将源码上传至链接(无需积分下载)https://download.csdn.net/download/m0_51294753/87743394,下载不下来可以私信我留邮箱。

一、前言

背景:使用ADS1255对模拟信号进行采样,并将转换的数据通过串口发送给电脑,使用QT编写上位机软件接收串口数据并实现采样波形的显示。因为没有具体的需求,只是进行简单测试,程序不尽完善,简单记录一下过程,方便刚接触的同伴一起学习。

二、测试效果

界面是在串口助手基础上改的,具有串口调试助手的基本功能,加了一个折线图显示,但是重新整理了上次串口的程序,显示效果如下:

20230302_104834

在这里插入图片描述

采样的板子设计的不太好,模拟输入端开路时本底噪声基本在0.6mV左右,设计输入电压是±2.5V。采用的串口输入,波特率为1500000,输入的数据具有固定的格式,数据输入形式如下:

在这里插入图片描述
三、实现过程遇到的问题

使用QT的serialport 和 charts库,简单过程不再说明,源程序在文末给出,下面简述一下我在实现中遇到的问题:

1.串口的定时扫描和串口名更新

原本只在程序开始时进行串口扫描,但随后发现如果设备在程序运行后就检测不到串口,串口如果被占用也得不到更新。通过定时器和关联槽函数来定时(500ms)扫描串口,但是串口禁用那行代码还没整明白是怎么回事(有知道的欢迎在评论区告诉我),具体实现看源代码。

for(int i = 0;i<portStringList.size();i++)
{serial->setPortName(portStringList[i]);if(serial->open(QIODevice::ReadWrite))ui->comboSerialPort->addItem(portStringList.at(i));else{ui->comboSerialPort->addItem(portStringList.at(i) + "(不可用)");ui->comboSerialPort->setItemData(i,(QVariant)0,Qt::UserRole-1);     //串口禁用??}serial->close();
}

2.图表显示的内容需要移动,类似示波器的显示波形

对于横坐标,我是通过固定横坐标时间的宽度,改变横坐标的坐标范围来实现的,比如数据输出速率为10,固定横坐标只显示50个点,则设置横坐标宽度为5。

    t += 0.1;qreal value = valueStr.toDouble();serices0->append(t,value);if(t>50)axisX->setRange(t-50,t);

对于纵坐标,通过一个链表把图表显示的50个数据存储起来,再用类似队列的方式先入先出的方式更新队列,找出队列的最大值和最小值更新纵坐标的坐标范围

    if(listvalue.size()<=500)listvalue.push_front(value);else{listvalue.pop_back();listvalue.push_front(value);}qreal minvalue = *std::min_element(listvalue.begin(),listvalue.end());qreal maxvalue = *std::max_element(listvalue.begin(),listvalue.end());axisY->setRange(minvalue-0.00001,maxvalue+0.00001);

3.把数据从发送的字符串中截取出来显示

没想到太好的办法,目前是需要注意发送字符串的形式,按着字符串形式更改程序,比如下位机发送的类型是value:0.0000000V,可以利用字符串截取函数把中间的数据单独拿出来,因为我下位机发送的数据宽度固定,所以我是使用mid()函数直接截取,如果长度 不一致,也可以用split()函数将数据与文本割裂开。

    receiveBuff = serial->readAll();receiveBytes += receiveBuff.length();QByteArray valueStr;valueStr = receiveBuff.mid(QString("value:").size(),QString("-0.000000").size());

4.设置坐标轴的问题

给图表中序列赋坐标轴的时候常用到这样一段代码

chart->setAxisX(axisX,serices0);
chart->setAxisY(axisY,serices0);

但一般都会警告该代码已过时,推荐使用addAxis()函数替代,于是改成

chart->addAxis((QAbstractAxis*)axisX,Qt::AlignBottom);
chart->addAxis((QAbstractAxis*)axisY,Qt::AlignLeft);

黄色警告消失了,编译也没有错误,但是运行起来坐标轴有些许问题,还没明白怎么回事。

四、程序代码

1.pro项目文件,主要是添加两行核心库和资源文件

QT       += core gui
QT       += serialport
QT       += chartsgreaterThan(QT_MAJOR_VERSION, 4): QT += widgetsCONFIG += c++11# You can make your code fail to compile if it uses deprecated APIs.
# In order to do so, uncomment the following line.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000    # disables all the APIs deprecated before Qt 6.0.0SOURCES += \main.cpp \mainwindow.cppHEADERS += \mainwindow.hFORMS += \mainwindow.ui# Default rules for deployment.
qnx: target.path = /tmp/$${TARGET}/bin
else: unix:!android: target.path = /opt/$${TARGET}/bin
!isEmpty(target.path): INSTALLS += targetRESOURCES += \res.qrc

2.ui设计文件,按照自己需求布局,给控件命名
在这里插入图片描述
3.h文件,一些变量和函数声明

#ifndef MAINWINDOW_H
#define MAINWINDOW_H#include <QMainWindow>
#include <QtCharts>
#include <QSerialPort>
#include <QSerialPortInfo>
#include <QStringList>
#include <QMessageBox>
#include <QFileDialog>
#include <QList>using namespace QtCharts;QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACEclass MainWindow : public QMainWindow
{Q_OBJECTpublic:MainWindow(QWidget *parent = nullptr);~MainWindow();private:Ui::MainWindow *ui;private:qreal t;QChart *chart;QLineSeries *serices0;QValueAxis *axisX,*axisY;QSerialPort *serial;                        //串口端口QStringList portStringList;                 //端口链表QTimer *timer;                              //定时器QByteArray sendBuff,receiveBuff;            //发送、接收缓存区long int sendBytes,receiveBytes;            //发送、接收字节数QList <qreal>  listvalue;void InitSerialPort();void InitChart();private slots:void serialPort_readyRead();void portTimerEvent();void on_btnOpenSerial_clicked();void on_btnSend_clicked();void on_btnClearRevBuff_clicked();void on_btnSaveFile_clicked();void on_btnOpenFile_clicked();void on_btnClearSendBuff_clicked();void on_btnResetCount_clicked();void on_chkFixedSend_clicked();void on_lineEditTime_editingFinished();void on_textEditRev_textChanged();
};
#endif // MAINWINDOW_H

4.c文件,函数功能实现

#include "mainwindow.h"
#include "ui_mainwindow.h"MainWindow::MainWindow(QWidget *parent): QMainWindow(parent), ui(new Ui::MainWindow)
{ui->setupUi(this);t = 0;sendBytes = 0;receiveBytes = 0;chart = new QChart();serices0 = new QLineSeries();axisX = new QValueAxis();axisY = new QValueAxis();timer = new QTimer();serial = new QSerialPort(this);QTimer *portTimer = new QTimer(this);connect(portTimer,SIGNAL(timeout()),this,SLOT(portTimerEvent()));connect(serial,SIGNAL(readyRead()),this,SLOT(serialPort_readyRead()));InitSerialPort();InitChart();portTimer->start(500);
}MainWindow::~MainWindow()
{delete ui;
}void MainWindow::InitSerialPort()
{ui->comboSerialPort->clear();portStringList.clear();foreach(const QSerialPortInfo &info, QSerialPortInfo::availablePorts())portStringList += info.portName();for(int i = 0;i<portStringList.size();i++){serial->setPortName(portStringList[i]);if(serial->open(QIODevice::ReadWrite))ui->comboSerialPort->addItem(portStringList.at(i));else{ui->comboSerialPort->addItem(portStringList.at(i) + "(不可用)");ui->comboSerialPort->setItemData(i,(QVariant)0,Qt::UserRole-1);     //串口禁用??}serial->close();}ui->comboBaudRate->setCurrentIndex(5);ui->comboDataBits->setCurrentIndex(3);ui->comboParity->setCurrentIndex(2);ui->comboStop->setCurrentIndex(0);ui->btnSend->setEnabled(false);ui->chkFixedSend->setEnabled(false);ui->lineEditTime->setEnabled(false);ui->lineEditTime->setText("1000");ui->radioTextReceive->setChecked(Qt::Checked);ui->radioTextSend->setChecked(Qt::Checked);
}void MainWindow::InitChart()
{ui->chartView->setChart(chart);QMargins mgs(5,5,5,5);chart->setMargins(mgs);chart->setTitle("数据曲线");//创建折线序列serices0->setName("时间-电压曲线");chart->addSeries(serices0);//创建坐标轴axisX->setRange(0,5);axisX->setTitleText("time(secs)");axisY->setRange(-2,2);axisY->setTitleText("value");chart->setAxisX(axisX,serices0);chart->setAxisY(axisY,serices0);//chart->addAxis((QAbstractAxis*)axisX,Qt::AlignBottom);//chart->addAxis((QAbstractAxis*)axisY,Qt::AlignLeft);
}void MainWindow::serialPort_readyRead()
{QByteArray lastStr;if(!ui->radioStopReceive->isChecked()){lastStr = ui->textEditRev->toPlainText().toUtf8();receiveBuff = serial->readAll();receiveBytes += receiveBuff.length();QByteArray valueStr;valueStr = receiveBuff.mid(QString("value:").size(),QString("-0.000000").size());t += 0.1;qreal value = valueStr.toDouble();serices0->append(t,value);if(t>50)axisX->setRange(t-50,t);if(listvalue.size()<=500)listvalue.push_front(value);else{listvalue.pop_back();listvalue.push_front(value);}qreal minvalue = *std::min_element(listvalue.begin(),listvalue.end());qreal maxvalue = *std::max_element(listvalue.begin(),listvalue.end());axisY->setRange(minvalue-0.00001,maxvalue+0.00001);ui->labRevBytesCount->setText(QString::number(receiveBytes));if(ui->radioHexReceive->isChecked()){receiveBuff = receiveBuff.toHex().toUpper();int length = receiveBuff.length();for(int i = 0;i<=length/2;i++)receiveBuff.insert((2+3*i), QByteArray(" "));}lastStr = lastStr.append(receiveBuff);ui->textEditRev->setText(lastStr);}elseserial->clear(QSerialPort::Input);
}void MainWindow::portTimerEvent()
{QStringList newPortStringList;newPortStringList.clear();foreach(const QSerialPortInfo &info,QSerialPortInfo::availablePorts())newPortStringList += info.portName();if(newPortStringList.size() != portStringList.size()){portStringList = newPortStringList;ui->comboSerialPort->clear();ui->comboSerialPort->addItems(portStringList);}
}void MainWindow::on_btnOpenSerial_clicked()
{if(ui->btnOpenSerial->text() == QString("打开串口")){//串口设置serial->setPortName(ui->comboSerialPort->currentText());serial->setBaudRate(ui->comboBaudRate->currentText().toInt());switch(ui->comboDataBits->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);}switch(ui->comboParity->currentIndex()){case 0: serial->setParity(QSerialPort::EvenParity);break;case 1: serial->setParity(QSerialPort::MarkParity);break;case 2: serial->setParity(QSerialPort::NoParity);break;case 3: serial->setParity(QSerialPort::OddParity);break;default: serial->setParity(QSerialPort::UnknownParity);}switch (ui->comboStop->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);}serial->setFlowControl(QSerialPort::NoFlowControl);if(!serial->open(QIODevice::ReadWrite)){QMessageBox::warning(this,"提示","无法打开串口",QMessageBox::Ok);return;}ui->comboSerialPort->setEnabled(false);ui->comboBaudRate->setEnabled(false);ui->comboDataBits->setEnabled(false);ui->comboParity->setEnabled(false);ui->comboStop->setEnabled(false);ui->btnSend->setEnabled(true);ui->chkFixedSend->setEnabled(true);ui->lineEditTime->setEnabled(true);ui->btnOpenSerial->setText("关闭串口");}else{serial->close();ui->comboSerialPort->setEnabled(true);ui->comboBaudRate->setEnabled(true);ui->comboDataBits->setEnabled(true);ui->comboParity->setEnabled(true);ui->comboStop->setEnabled(true);ui->btnSend->setEnabled(false);ui->chkFixedSend->setEnabled(false);ui->lineEditTime->setEnabled(false);ui->btnOpenSerial->setText("打开串口");}
}void MainWindow::on_btnSend_clicked()
{sendBuff = ui->textEditSend->toPlainText().toUtf8();if(ui->radioHexSend->isChecked())sendBuff = QByteArray::fromHex(sendBuff);if(ui->chkLineFeed->isChecked())sendBuff += '\n';serial->write(sendBuff);sendBytes += sendBuff.length();ui->labSendBytesCount->setText(QString::number(sendBytes));ui->textEditSend->moveCursor(QTextCursor::End);
}void MainWindow::on_btnClearRevBuff_clicked()
{ui->textEditRev->clear();
}void MainWindow::on_btnSaveFile_clicked()
{QString curPath = QDir::currentPath();QString dlgTilte = "保存文件";QString filter = "文本文件(*.txt);;所有文件(*.*)";QString fileName = QFileDialog::getSaveFileName(this,dlgTilte,curPath,filter);if(fileName.isEmpty())return;QFile file(fileName);if(!file.open(QIODevice::ReadWrite | QIODevice::Text))QMessageBox::warning(this,"文档编辑器",tr("无法写入文件 %1:\n%2").arg(fileName,file.errorString()));QTextStream stream(&file);stream.setAutoDetectUnicode(true);stream<<ui->textEditRev->toPlainText().toUtf8();file.close();
}void MainWindow::on_btnOpenFile_clicked()
{QString curPath = QDir::currentPath();QString dlgTilte = "打开文件";QString filter = "文本文件(*.txt);;所有文件(*.*)";QString fileName = QFileDialog::getOpenFileName(this,dlgTilte,curPath,filter);if(fileName.isEmpty())return;QFile file(fileName);if(!file.open(QIODevice::ReadWrite | QIODevice::Text))QMessageBox::warning(this,"文档编辑器",tr("无法读取文件 %1:\n%2").arg(fileName,file.errorString()));ui->textEditSend->setText(file.readAll());file.close();
}void MainWindow::on_btnClearSendBuff_clicked()
{ui->textEditSend->clear();
}void MainWindow::on_btnResetCount_clicked()
{receiveBytes = 0;sendBytes = 0;ui->labRevBytesCount->setText(QString::number(receiveBytes));ui->labSendBytesCount->setText(QString::number(sendBytes));
}void MainWindow::on_chkFixedSend_clicked()
{if(ui->chkFixedSend->isChecked()){int fixedTime = ui->lineEditTime->text().toInt();timer->start(fixedTime);connect(timer,SIGNAL(timeout()),this,SLOT(on_btnSend_clicked()));}else{timer->stop();}
}void MainWindow::on_lineEditTime_editingFinished()
{on_chkFixedSend_clicked();
}void MainWindow::on_textEditRev_textChanged()
{ui->textEditRev->moveCursor(QTextCursor::End);
}

程序基本框架和上一篇发文的基于QT5实现串口调试助手没太大区别,只是修改了一下控件的命名便于理解,把上次冗余的部分代码变简洁一下,加入了图表。程序需要自行理解修改一下才能运行,否则下位机发来的数据与此次字符串格式不一致会使程序发生错误或强制退出。刚入门还存在诸多问题,请各位见谅。

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

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

相关文章

cJSON(API的详细使用教程)

我们今天来学习一般嵌入式的必备库&#xff0c;JSON库 1&#xff0c;json和cJSON 那什么是JSON什么是cJSON&#xff0c;他们之间有什么样的关联呢&#xff0c;让我们一起来探究一下吧。 JSON&#xff08;JavaScript Object Notation&#xff09;是一种轻量级的数据交换格式&…

C和C++内存管理

目录&#xff1a; 一&#xff1a;C和C内存分布 二&#xff1a;C动态内存管理方式 三&#xff1a;C动态内存管理方式 四&#xff1a;operator new与operator delete函数 五&#xff1a;new和delete的实现原理 六&#xff1a;定位new表达式(placement-new) 七&#xff1…

使用YOLOv8训练自己的【目标检测】数据集

文章目录 1.收集数据集1.1 使用开源已标记数据集1.2 爬取网络图像1.3 自己拍摄数据集1.4 使用数据增强生成数据集1.5 使用算法合成图像 2.标注数据集2.1确认标注格式2.2 开始标注 3.划分数据集4.配置训练环境4.1获取代码4.2安装环境 5.训练模型5.1新建一个数据集yaml文件5.2预测…

了解IP地址的基本概念和修改步骤

在数字化时代&#xff0c;IP地址作为网络设备的唯一标识&#xff0c;其重要性不言而喻。无论是为了提升网络性能&#xff0c;还是出于隐私保护的需求&#xff0c;修改IP地址都是网络使用者可能遇到的操作。虎观代理将详细介绍如何修改IP地址&#xff0c;并探讨在修改过程中需要…

python+flask+django文献文件资料搜索系统

后端&#xff1a;python 前端&#xff1a;vue.jselementui 框架&#xff1a;django/flask Python版本&#xff1a;python3.7 数据库&#xff1a;mysql 数据库工具&#xff1a;Navicat 开发软件&#xff1a;PyCharm 本系统在设计过程中&#xff0c;很好地发挥了该开发方式的优…

例47:键盘事件演示

建立一个EXE工程&#xff0c;在默认窗体上放一个Image框和一一个text框。在text的按键事件中输入代码&#xff1a; Function Form1_Text1_WM_KeyDown(hWndForm As hWnd, hWndControl As hWnd,nVirtKey As Long, lKeyData As Long) As LongIf nVirtKey VK_SPACE ThenImage1.Pi…

Linux:IO多路转接之select

文章目录 selecttimeval结构体fd_set 优缺点分析完整代码 本节要介绍的主题是多路转接式IO select 先说结论&#xff0c;这个select是做什么的呢&#xff1f; select是负责在Linux系统中&#xff0c;让一个人可以有多个鱼竿&#xff0c;可以不停的进行轮询&#xff0c;只要有…

Trace链异常检测汇总

微服务应用与单块应用完全不同&#xff0c;一个微服务系统少则有几十个微服务组成&#xff0c;多则可能有上百个服务。比如BAT级别的互联网公司&#xff0c;一般都超过上百个服务&#xff0c;服务之间的依赖关系错综复杂&#xff0c;如果没有有效的监控手段&#xff0c;那么出现…

[计算机知识] 各种小问题思考

哈希算法以及哈希冲突 哈希算法&#xff1a;将任何长度的输入通过散列函数转换成固定长度的字符串 哈希冲突&#xff1a;不同的输入经过哈希函数处理后得到相同的哈希值 因为哈希函数的输出域是有限的 解决哈希冲突&#xff1a; 1. 开放寻址&#xff1a;产生哈希冲突后&…

Vue - 1( 13000 字 Vue 入门级教程)

一&#xff1a;Vue 导语 1.1 什么是 Vue Vue.js&#xff08;通常称为Vue&#xff09;是一款流行的开源JavaScript框架&#xff0c;用于构建用户界面。Vue由尤雨溪在2014年开发&#xff0c;是一个轻量级、灵活的框架&#xff0c;被广泛应用于构建单页面应用&#xff08;SPA&am…

创建和启动线程

概述 Java语言的JVM允许程序运行多个线程&#xff0c;使用java.lang.Thread类代表线程&#xff0c;所有的线程对象都必须是Thread类或其子类的实例。 Thread类的特性 每个线程都是通过某个特定Thread对象的run()方法来完成操作的&#xff0c;因此把run()方法体称为线程执行体。…

使用神经网络-遗传算法优化神经网络-风电预测故障(BP,GABP,matlab)

本项目是故障预测&#xff0c;不是时序预测&#xff0c;本质还是分类问题 1 数据集介绍 特征文件&#xff1a; 标签文件&#xff1a;共计4个标签&#xff0c;其中大多数都是正常的&#xff0c;其他是3个不正常的类别 2 使用BP网络 2.1 读取数据&#xff0c;然后选择几个…

【深度学习】图像自然语言描述生成

案例 6&#xff1a;图像自然语言描述生成&#xff08;让计算机“看图说话”&#xff09; 相关知识点&#xff1a;RNN、Attention 机制、图像和文本数据的处理 1 任务目标 1.1 任务和数据简介 ​ 本次案例将使用深度学习技术来完成图像自然语言描述生成任务&#xff0c;输入…

MySQL复制拓扑2

文章目录 主要内容一.配置基本复制结构1.分别在三台主机上停止mysqld服务&#xff0c;并对状态进行确认&#xff1a;代码如下&#xff08;示例&#xff09;: 2.对三个MySQL服务器的配置文件分别进行编辑&#xff0c;在[mysqld] 选项组中添加以下红色条目&#xff1a;3.在数据目…

深入理解Java异常处理机制(day20)

异常处理 异常处理是程序运行过程产生的异常情况进行恰当的处理技术 在计算机编程里面&#xff0c;异常的情况比所我们所想的异常情况还要多。 Java里面有两种异常处理方式&#xff1b; 1.利用trycatchfinaly语句处理异常&#xff0c;优点是分开了处理异常代码和程序正常代码…

深入浅出 -- 系统架构之负载均衡Nginx反向代理

一、Nginx反向代理-负载均衡 首先通过SpringBootFreemarker快速搭建一个WEB项目&#xff1a;springboot-web-nginx&#xff0c;然后在该项目中&#xff0c;创建一个IndexNginxController.java文件&#xff0c;逻辑如下&#xff1a; Controller public class IndexNginxControl…

【放假第3天】幻兽帕鲁 雾锁王国 我的世界 游戏云服务器选购指南 附最新价格对比表 新手、小白秒懂

更新日期&#xff1a;4月6日&#xff08;半年档 价格回调&#xff0c;京东云采购季持续进行&#xff09; 本文纯原创&#xff0c;侵权必究 【云服务器推荐】价格对比&#xff01;阿里云 京东云 腾讯云 选购指南视频截图 《最新对比表》已更新在文章头部—腾讯云文档&#xf…

前端三剑客 —— CSS (第四节)

目录 内容回顾&#xff1a; 1.常见样式 2.特殊样式 特殊样式 过滤效果 动画效果 动画案例&#xff1a; 渐变效果 其他效果&#xff1a; 多列效果 字体图标&#xff08;icon&#xff09; 内容回顾&#xff1a; 1.常见样式 text-shadow x轴 y轴 阴影的模糊程度 阴影的…

【话题:工作生活】2022年工作总结--疫情下的上海,疫情中的我。

现在是阳历2023年11月27日星期一&#xff0c;我再次开始撰写自己的年终工作总结。希望再过1、2个月&#xff0c;这份年终总结能够出炉&#xff0c;与大家相遇。 给自己定个小目标&#xff0c;年终的工作生活总结坚持写10年。我2017年毕业&#xff0c;之后就开始写每年的年终总结…

软考117-上午题-【计算机网络】-杂题+小结

一、杂题 真题1&#xff1a; 真题2&#xff1a; 真题3&#xff1a; 真题4&#xff1a; 真题5&#xff1a; 真题6&#xff1a; 真题7&#xff1a; 真题8&#xff1a; 真题9&#xff1a; 真题10&#xff1a; 真题11&#xff1a; 真题12&#xff1a; 真题13&#xff1a; 真题14&a…