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

这篇具有很好参考价值的文章主要介绍了QT串口接收数据并进行波形显示(含源码)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

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

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

一、前言

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

二、测试效果

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

20230302_104834

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

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

QT串口接收数据并进行波形显示(含源码)
三、实现过程遇到的问题

使用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       += charts

greaterThan(QT_MAJOR_VERSION, 4): QT += widgets

CONFIG += 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.0

SOURCES += \
    main.cpp \
    mainwindow.cpp

HEADERS += \
    mainwindow.h

FORMS += \
    mainwindow.ui

# Default rules for deployment.
qnx: target.path = /tmp/$${TARGET}/bin
else: unix:!android: target.path = /opt/$${TARGET}/bin
!isEmpty(target.path): INSTALLS += target

RESOURCES += \
    res.qrc

2.ui设计文件,按照自己需求布局,给控件命名
QT串口接收数据并进行波形显示(含源码)
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_NAMESPACE

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    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);
    }
    else
        serial->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实现串口调试助手没太大区别,只是修改了一下控件的命名便于理解,把上次冗余的部分代码变简洁一下,加入了图表。程序需要自行理解修改一下才能运行,否则下位机发来的数据与此次字符串格式不一致会使程序发生错误或强制退出。刚入门还存在诸多问题,请各位见谅。文章来源地址https://www.toymoban.com/news/detail-429804.html

到了这里,关于QT串口接收数据并进行波形显示(含源码)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Qt6使用QChartView类与鼠标事件实现波形的缩放、平移、坐标轴单轴缩放与鼠标悬停显示点的数据

            说在前面,本人也是近段时间刚开始学习Qt,实现上述功能的方法可能并不是最优,写此篇文章也是记录下学习的过程,也与大家分享一下。(在此先描述,后面会附上代码)(前面说的会比较基础)         首先,要使用QChartView类得现在.pro文件中加入:(得确保

    2024年02月09日
    浏览(39)
  • FPGA接收串口数据并通过LCD1602显示

    一、前言 在学习《FPGA设计与Verilog HDL实现》第九章内容Verilog驱动常用I/O外设时,书中有一个驱动LCD1602的例程,但其是通过状态机显示固定的几个字符。本着动手实践的原则,决定利用手头的硬件实现FPGA接收串口数据并在LCD1602上显示,下面记录项目开始的过程。因为刚接触

    2024年02月06日
    浏览(43)
  • Qt+C++串口调试接收发送数据曲线图

    程序示例精选 Qt+C++串口调试接收发送数据曲线图 如需安装运行环境或远程调试,见文章底部个人 QQ 名片,由专业技术人员远程协助! 这篇博客针对Qt+C++串口调试接收发送数据曲线图编写代码,代码整洁,规则,易读。 学习与应用推荐首选。 一、所需工具软件 二、使用步骤

    2024年02月11日
    浏览(46)
  • FPGA_数码管显示UART串口接收的数据

          实验目标 :通过电脑调试助手向FPGA的UART串口接收模块发送数据,然后数据可以稳定显示 在数码管上。       实验目的 : 练习UART串口模块和数码管的使用。之前已经有文章详细讲解了串口和数码管的开发,故这里直接提供设计思路供大家参考。 (串口文章链接)ht

    2024年02月13日
    浏览(46)
  • QT三驾马车(一)——实现上位机(串口数据发送和接收)

    以后同学们做项目一定会用到QT的三驾马车,QT的三驾马车即QT的串口编程,QT的网络编程和QT的GPIO,今天我们通过一个项目来介绍第一部分,QT的串口编程。 之前看过很多相关的文章,但是按照顺序来编译总是会出错,可是我自己还找不到原因,对于我这种新手小白来说极其

    2024年02月15日
    浏览(44)
  • Qt进行UDP通讯,创建一个收线程这样可以进行接收数据

    在.pro中增加一句话 绘制界面 .h文件内容: 构造函数内容 对于绑定按钮的定义函数: 接收信号的槽函数(UDP接收到数据显示) quitThreaSlot函数: 退出按钮定义: 使用的receivethread.h就是将run函数重写(循环发送定义的信号延迟即可),在定义一个信号即可。 以上即功能的所有

    2024年02月20日
    浏览(40)
  • 【STM32CubeMX+HAL库】hmi串口屏显示波形

    usart hmi是淘晶驰开发的一款基于串口通信并采用指令集控制的可触摸屏幕,集成了多种控件,如按钮控件,滑块控件等,大大减轻了开发的难度。           曲线控件学习入口 下面我们来讲解一下cubeMX的配置以及输出正弦波代码的编写(三角波、方波同理) 一、CubeMX配置

    2023年04月12日
    浏览(43)
  • 关于Qt用多线程实现usb温度传感器(串口通信)的数据接收中遇到的问题及猜想(不一定正确)

    由于是初学,仅仅对串口编程有个了解,大概的功能是通过两个按钮实现串口数据的接收和暂停,其他的功能暂不深入研究。 通过串口调试助手发现,该串口的属性设置如左所示,接收的数据转为字符串后显示格式如右所示。这里是打算将右边的温度显示在一个LCD控件中,效

    2024年02月01日
    浏览(40)
  • 使用环形缓冲区ringbuffer实现串口数据接收

    环形缓冲区(ringbuffer),实际上就是一种队列数据结构,只不过它不是线性队列,而是环形队列。 关于环形缓冲区(ringbuffer)的详细介绍,网上一搜一大把,这里不重复介绍了,我这里直接上代码。 详细介绍可以参考下面链接里面的介绍: https://en.wikipedia.org/wiki/Circular_b

    2023年04月19日
    浏览(42)
  • Qt 的网络模块和串口模块来实现 WiFi 传输和数据显示

    在头文件中引入相关库: 然后,在主窗口类中定义相关变量: 其中, tcpSocket 为 TCP 套接字, serialPort 为串口对象。 在构造函数中初始化 tcpSocket 和 serialPort : 在点击“连接”按钮时,我们需要连接 WiFi,并建立 TCP 连接。这里我们需要先输入 WiFi 的名称和密码,然后使用

    2024年02月04日
    浏览(49)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包