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,一经查实,立即删除!

相关文章

mysql 8.0 常用函数大全总结,并列出实例

MySQL 8.0提供了丰富的内置函数&#xff0c;这些函数可以大致分为几个类别&#xff1a;字符串函数、数值函数、日期和时间函数、比较函数、逻辑函数、控制流函数等。以下是20个常用的MySQL函数及其简单的使用示例&#xff1a; 1. **CONCAT()**: 字符串连接函数。 - 示例&am…

设计一个可扩展的Python Web框架:并发、性能与安全性考量

设计一个可扩展的Python Web框架&#xff1a;并发、性能与安全性考量 一、引言 随着Web应用的日益复杂和需求的不断增长&#xff0c;一个可扩展的Web框架变得至关重要。在Python生态系统中&#xff0c;虽然存在诸多优秀的Web框架&#xff0c;但设计一个符合自身需求且具备高度…

云服务器4m带宽是什么意思?

云服务器配置4M带宽是什么意思&#xff1f;4M代表该台云服务器的网络传输速度为4Mbps&#xff0c;一般指该台云服务器的公网出方向带宽&#xff0c;即从云服务器下载的公网带宽&#xff0c;4M带宽下载速度为512KB/秒&#xff0c;云服务器吧yunfuwuqiba.com整理常见的云服务器公…

Java之过滤器Filter、拦截器Interceptor与面向切面AOP

Java之过滤器Filter、拦截器Interceptor与面向切面AOP Filter的使用 Component WebFilter(urlPatterns {"/*"}) public class CustomFilter implements Filter {Overridepublic void doFilter(ServletRequest request, ServletResponse response, FilterChain cha…

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;很好地发挥了该开发方式的优…

刷题DAY45 | 70-爬楼梯(进阶) LeetCode 322-零钱兑换 279-完全平方数

70 爬楼梯&#xff08;进阶&#xff09; 假设你正在爬楼梯。需要 n 阶你才能到达楼顶。 每次你可以爬至多m (1 < m < n)个台阶。你有多少种不同的方法可以爬到楼顶呢&#xff1f; 注意&#xff1a;给定 n 是一个正整数。 输入描述 输入共一行&#xff0c;包含两个正整数&…

120.单例模式(C++设计模式)

一、什么是单例模式 单例模式是一种创建型设计模式&#xff0c;用于确保一个类只有一个实例&#xff0c;并提供一个全局访问点来获取这个实例。 在C中实现单例模式通常会遵循以下步骤&#xff1a; 私有构造函数&#xff1a;确保单例类的构造函数是私有的&#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…

【题单】 洛谷哈希题单

这里写目录标题 updata普及-普及/提高-普及/提高提高/省选-省选/NOI−NOI/NOI/CTSC updata 2024.04.06 15:35 发布此文章 普及- P1102 A-B 数对 P1211 [USACO1.3] 牛式 Prime Cryptarithm P2957 [USACO09OCT] Barn Echoes G P3370 【模板】字符串哈希 P4305 [JLOI2011] 不重…

最小花费

题目描述 在 n 个人中&#xff0c;某些人的银行账号之间可以互相转账。这些人之间转账的手续费各不相同。给定这些人之间转账时需要从转账金额里扣除百分之几的手续费&#xff0c;请问 A 最少需要多少钱使得转账后 B 收到 100 元。 输入描述 第一行输入两个正整数 n,m&#…

基础算法06|反转链表

206. 反转链表 - 力扣&#xff08;LeetCode&#xff09; /*** Definition for singly-linked list.* public class ListNode {* int val;* ListNode next;* ListNode() {}* ListNode(int val) { this.val val; }* ListNode(int val, ListNode next) { th…

C:标准日志函数

C&#xff1a;标准日志函数 我们写日志函数通常有以下几个要求&#xff1a; 日志等级自定义输入内容输入内容可以进行格式控制可以使用宏定义控制的日志 有关内容已经在我上一篇写的文章中发过了&#xff0c;除了格式控制&#xff0c;已经基本实现了其他功能&#xff0c;这期…

Linux:IO多路转接之select

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

如何成功转入IT行业:针对零相关背景知识者的指南

0基础如何进入IT行业? 在当今数字化时代,IT行业提供了大量的就业机会和职业发展空间。然而,对于没有任何相关背景知识的人来说,成功进入这个行业可能会面临一些挑战。幸运的是,有一些方法和技巧可以帮助他们实现这一目标。 对于没有IT背景的人来说,要进入这个行业,最重…

Trace链异常检测汇总

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

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

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