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       += 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设计文件,按照自己需求布局,给控件命名
在这里插入图片描述
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实现串口调试助手没太大区别,只是修改了一下控件的命名便于理解,把上次冗余的部分代码变简洁一下,加入了图表。程序需要自行理解修改一下才能运行,否则下位机发来的数据与此次字符串格式不一致会使程序发生错误或强制退出。刚入门还存在诸多问题,请各位见谅。

Qt串口通信的接收数据进行波形显示,可以按照以下步骤进行: 1. 首先,需要引入Qt相关的串口通信库。可以使用Qt的QSerialPort类来进行串口通信操作。 2. 设置串口参数。通过QSerialPort类的setPortName()方法设置串口号,例如COM1、COM2等。然后通过setBaudRate()方法设置波特率,setParity()方法设置奇偶校验位,setDataBits()方法设置数据位,setStopBits()方法设置停止位等。 3. 打开串口。通过QSerialPort类的open()方法打开串口。 4. 设置数据接收的方式。可以选择使用信号槽机制接收串口数据。使用QSerialPort类的readyRead信号,当串口接收数据时会自动发送该信号,然后在槽函数中读取接收到的数据。 5. 解析接收到的数据。对于串口通信而言,接收到的数据可能是原始的字节数组或者字符串。根据实际情况,可以将数据解析为需要显示的数值。 6. 进行波形显示。可以通过Qt自带的绘图类进行波形显示,例如QGraphicsView类,QChart类等。在槽函数中将解析后的数据添加到波形图中,并实时刷新显示。 7. 关闭串口。在结束串口通信时,通过QSerialPort类的close()方法关闭串口。 需要注意的是,对于串口通信而言,可能需要考虑数据的校验、数据的完整性等问题。此外,还需要处理异常情况,例如串口打开失败、接收数据异常等情况。 以上是一个简单的Qt串口通信接收数据进行波形显示的基本步骤,具体的实现方式和细节还需根据实际需求进行调整和补充。
评论 83
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值