Qt 串口编程-从入门到实战

news2024/12/24 8:42:47

1. Qt 串口通信流程解析

1.1 串行通信和并行通信对比

  • 并行通信适合距离较短的通信,且信号容易受干扰,成本高
  • 串口通讯-设备(蓝牙, wifi, gprs, gps)

在这里插入图片描述

1.2 Qt 串口通信具体流程

  • 1. 创建 QSerialPort 对象
  • 2. 配置属性(波特率, 数据位, 停止, 校验位)
  • 3. 打开设备
  • 4. 发送数据到串口 write
  • 5. 在槽函数中读取数据(当串口有数据可读的时候会发送 readyRead 信号)
1.2.1 serialapp.pro
QT       += core gui serialport
1.2.2 serialapp.h
#ifndef SERIALAPP_H
#define SERIALAPP_H

#include <QWidget>
#include <QSerialPort>
#include <QSerialPortInfo>
#include <QIODevice>
#include <QByteArray>
#include <QString>
#include <QDebug>

QT_BEGIN_NAMESPACE
namespace Ui { class SerialApp; }
QT_END_NAMESPACE

class SerialApp : public QWidget {
    Q_OBJECT

public:
    SerialApp(QWidget *parent = nullptr);
    ~SerialApp();

private slots:
    void on_openBt_clicked();
    void on_sendBt_clicked();
    void read_data();

private:
    Ui::SerialApp *ui;
    // 1、创建 QSerialPort 对象
    QSerialPort mSerial;
};
#endif // SERIALAPP_H
1.2.3 serialapp.cpp
#include "serialapp.h"
#include "ui_serialapp.h"

SerialApp::SerialApp(QWidget *parent) : QWidget(parent), ui(new Ui::SerialApp) {
    ui->setupUi(this);

    // 2、配置设备,波特率,数据位,停止位,校验位
    mSerial.setPortName("COM1");  // 选择对应的端口号
    mSerial.setBaudRate(QSerialPort::Baud115200);
    mSerial.setDataBits(QSerialPort::Data8);
    mSerial.setStopBits(QSerialPort::OneStop);
    mSerial.setParity(QSerialPort::NoParity);

    connect(&mSerial, &QSerialPort::readyRead, this, &SerialApp::read_data);
}

SerialApp::~SerialApp() {
    delete ui;
}

// 3、打开设备 (读写)
void SerialApp::on_openBt_clicked() {
    if (mSerial.open(QIODevice::ReadWrite)) {
        qDebug() << "open success!";
    } else {
        qDebug() << "open failed!";
    }
}

// 4、发送数据到串口 write
void SerialApp::on_sendBt_clicked() {
    QString data = ui->textEdit->toPlainText();
    mSerial.write(data.toUtf8());
}

// 5、读取串口数据 read
void SerialApp::read_data() {
    QByteArray array = mSerial.readAll();
    ui->textBrowser->append(QString(array));
}
1.2.4 serialapp.ui

在这里插入图片描述

2. Qt 虚拟串口调试

2.1 VSPD 创建虚拟串口

  • VSPD (Virtual Serial Port Driver) 是一个虚拟串口驱动程序

    • 它可以模拟多个串口设备,使得应用程序可以通过虚拟串口与物理串口设备进行通信
    • 使用 VSPD 可以方便地进行串口调试、数据采集、数据转发等操作
    • VSPD 还支持多种协议,例如模拟 GPS 设备、模拟调制解调器、与虚拟机通信等
  • VSPD虚拟串口软件安装及使用

在这里插入图片描述

2.2 SecureCRT 连接虚拟串口

  • SecureCRT 是一款安全的终端模拟器,常用于远程访问服务器和网络设备
    • 它可以让用户通过 SSH、Telnet、Rlogin 或者串口等协议连接到远程设备,并在本地进行命令行操作
    • SecureCRT 还提供了多重会话管理、脚本编写、自动登录、加密通信等多种功能
  • SecureCRT安装教程

在这里插入图片描述

2.3 Qt 虚拟串口实现

  • serialapp.h

    #ifndef SERIALAPP_H
    #define SERIALAPP_H
    
    #include <QWidget>
    #include <QSerialPort>
    #include <QSerialPortInfo>
    #include <QIODevice>
    #include <QByteArray>
    #include <QString>
    #include <QDebug>
    #include <QList>
    
    QT_BEGIN_NAMESPACE
    namespace Ui { class SerialApp; }
    QT_END_NAMESPACE
    
    class SerialApp : public QWidget {
        Q_OBJECT
    
    public:
        SerialApp(QWidget *parent = nullptr);
        ~SerialApp();
    
    private slots:
        void on_openBt_clicked();
        void on_sendBt_clicked();
        void read_data();
    
    private:
        Ui::SerialApp *ui;
        // 创建 QSerialPort 对象
        QSerialPort mSerial;
    };
    #endif // SERIALAPP_H
    
  • serialapp.cpp

    #include "serialapp.h"
    #include "ui_serialapp.h"
    
    SerialApp::SerialApp(QWidget *parent) : QWidget(parent), ui(new Ui::SerialApp) {
        ui->setupUi(this);
    
        // 获取当前设备上的所有串口
        QList<QSerialPortInfo> list =  QSerialPortInfo::availablePorts();
        for (int i = 0; i < list.size(); i++) {
            ui->comboBox->addItem(list.at(i).portName());
        }
    
        // 配置设备,波特率,数据位,停止位,校验位
        //mSerial.setPortName("COM1");
        mSerial.setBaudRate(QSerialPort::Baud115200);
        mSerial.setDataBits(QSerialPort::Data8);
        mSerial.setStopBits(QSerialPort::OneStop);
        mSerial.setParity(QSerialPort::NoParity);
    
        connect(&mSerial, &QSerialPort::readyRead, this, &SerialApp::read_data);
    }
    
    SerialApp::~SerialApp() {
        delete ui;
    }
    
    // 打开设备 (读写)
    void SerialApp::on_openBt_clicked() {
        if (mSerial.isOpen()) {
            mSerial.close();
        }
        mSerial.setPortName(ui->comboBox->currentText());  // 设置端口
        if (mSerial.open(QIODevice::ReadWrite)) {
            qDebug() << "open success!";
        } else {
            qDebug() << "open failed!";
        }
    }
    
    // 发送数据到串口 write
    void SerialApp::on_sendBt_clicked() {
        QString data = ui->textEdit->toPlainText();
        mSerial.write(data.toUtf8());
    }
    
    // 读取串口数据 read
    void SerialApp::read_data() {
        QByteArray array = mSerial.readAll();
        ui->textBrowser->append(QString(array));
    }
    
  • serialapp.ui

在这里插入图片描述

2.4 Qt 与 SecureCRT 建立虚拟串口连接

在这里插入图片描述

3. Qt 编写串口调试工具

在这里插入图片描述

3.1 serialportapp.h

#ifndef SERIALPORTAPP_H
#define SERIALPORTAPP_H

#include <QWidget>
#include <QSerialPort>
#include <QSerialPortInfo>
#include <QList>
#include <QStringList>
#include <QString>
#include <QIODevice>
#include <QDebug>
#include <QByteArray>
#include <QMessageBox>
#include <QTimerEvent>
#include <QFileDialog>
#include <QFile>

QT_BEGIN_NAMESPACE
namespace Ui { class SerialPortApp; }
QT_END_NAMESPACE

class SerialPortApp : public QWidget {
    Q_OBJECT

public:
    SerialPortApp(QWidget *parent = nullptr);
    ~SerialPortApp();

    void timerEvent(QTimerEvent *event);

private slots:
    void on_openBt_clicked();
    void on_closeBt_clicked();
    void on_sendBt_clicked();
    void on_autoCheckBox_clicked(bool checked);
    void on_clearSendSizeBt_clicked();
    void on_sendHexCb_clicked(bool checked);
    void on_recvHexCb_clicked(bool checked);

    void read_data();

    void on_clearRecvSizeBt_clicked();
    void on_selectfileBt_clicked();
    void on_sendfileBt_clicked();

    void send_file_text(quint64 size);

private:
    Ui::SerialPortApp *ui;
    QSerialPort mSerial;
    int timerid;
    qint32 sendsize;
    qint32 recvsize;
    QFile file;  // 发送文件
    qint32 sendfilesize;
};
#endif // SERIALPORTAPP_H

3.2 serialportapp.cpp

#include "serialportapp.h"
#include "ui_serialportapp.h"

SerialPortApp::SerialPortApp(QWidget *parent) : QWidget(parent), ui(new Ui::SerialPortApp) {
    ui->setupUi(this);

    // 遍历获取当前设备上的所有串口
    QList<QSerialPortInfo> infos = QSerialPortInfo::availablePorts();
    for (int i = 0; i < infos.size(); i++) {
        ui->comCb->addItem(infos.at(i).portName());
    }

    // 设置波特率显示
    QStringList list;
    list << "1200" << "2400" << "4800" << "9600" << "19200" << "38400" << "57600" << "115200";
    ui->rateCb->addItems(list);
    ui->rateCb->setCurrentIndex(7);  // 设置默认波特率为 115200
    list.clear();

    // 设置数据位
    list << "5" << "6" << "7" << "8" << "-1";
    ui->dataCb->addItems(list);
    ui->dataCb->setCurrentIndex(3);  // 设置默认数据位为 8
    list.clear();

    // 设置停止位
    list << "1" << "3" << "2" << "-1";
    ui->stopCb->addItems(list);
    list.clear();

    // 设置校验位
    list << "None" << "NULL" << "Even" << "Odd" << "Space" << "Mark";
    ui->priCb->addItems(list);
    list.clear();

    // 把关闭按钮设置失效
    ui->closeBt->setEnabled(false);

    // 当串口有数据可读时会发送 readyRead 信号
    connect(&mSerial, &QSerialPort::readyRead, this, &SerialPortApp::read_data);

    // 初始化发送、接收的字节数记录
    sendsize = recvsize = 0;
}

SerialPortApp::~SerialPortApp() {
    delete ui;
}

// 打开
void SerialPortApp::on_openBt_clicked() {
    // 配置端口,波特率,数据位,停止位,校验位
    mSerial.setPortName(ui->comCb->currentText());
    mSerial.setBaudRate(ui->rateCb->currentText().toInt());
    mSerial.setDataBits((QSerialPort::DataBits)ui->dataCb->currentText().toInt());
    mSerial.setStopBits((QSerialPort::StopBits)ui->stopCb->currentText().toInt());
    mSerial.setParity((QSerialPort::Parity)ui->priCb->currentText().toInt());

    // 打开设备
    if (mSerial.open(QIODevice::ReadWrite)) {
        ui->closeBt->setEnabled(true);
        ui->openBt->setEnabled(false);
    }
}

// 关闭
void SerialPortApp::on_closeBt_clicked() {
    // 关闭设备
    mSerial.close();
    ui->closeBt->setEnabled(false);
    ui->openBt->setEnabled(true);
}

// 手动发送数据
void SerialPortApp::on_sendBt_clicked() {
    QString data = ui->sendText->toPlainText();
    if (ui->sendHexCb->isChecked()) {
        // 转十六进制:data = 4142 --> 0x41 0x42
        QByteArray array;
        if (data.size() % 2 != 0) {
            data.insert(0, '0');
        }
        for (int i = 0; i < data.size() / 2; i++) {
            QString t = data.mid(2*i, 2);
            bool ok = false;
            int ihex = t.toInt(&ok, 16);
            array.append(ihex);
        }

        int size = mSerial.write(array);  // 发送数据
        sendsize += size;                 // 累计发送的字节数
    } else {
        int size = mSerial.write(data.toUtf8());  // 发送数据
        sendsize += size;
    }
    // 设置显示已发送的字节数
    ui->sendsizelabel->setText(QString::number(sendsize));
}

// 定时自动发送数据
void SerialPortApp::on_autoCheckBox_clicked(bool checked) {
    if (checked) {
        // 获取定时发送周期
        int ms = ui->autotimeEdit->text().toInt();
        if (ms < 100) {
            QMessageBox::warning(this, "time hint", "time should > 100ms");
            ui->autoCheckBox->setChecked(false);

            return;
        }

        // 启动定时器事件
        timerid = this->startTimer(ms);
    } else {
        // 关闭定时器事件
        this->killTimer(timerid);
    }
}

// 定时器事件
void SerialPortApp::timerEvent(QTimerEvent *event) {
    on_sendBt_clicked();
}

// 清空已发送的字节数
void SerialPortApp::on_clearSendSizeBt_clicked() {
    sendsize = 0;
    ui->sendText->clear();
    ui->sendsizelabel->setText("0");
}

// 发送端:十六进制和十进制转换
void SerialPortApp::on_sendHexCb_clicked(bool checked) {
    if (checked) {  // 十进制 --> 十六进制
        QString data = ui->sendText->toPlainText();
        QByteArray array = data.toUtf8().toHex();
        ui->sendText->setText(QString(array));
    } else {  // 十六进制 --> 十进制
        QString data = ui->sendText->toPlainText();
        QByteArray array;
        if (data.size() % 2 != 0) {
            data.insert(0, '0');
        }
        for (int i = 0; i < data.size() / 2; i++) {
            QString t = data.mid(2*i, 2);
            bool ok = false;
            int ihex = t.toInt(&ok, 16);
            array.append(ihex);
        }
        ui->sendText->setText(QString(array));
    }
}

// 接收端:十六进制和十进制转换
void SerialPortApp::on_recvHexCb_clicked(bool checked) {
    if (checked) {  // 十进制 --> 十六进制
        QString data = ui->recvText->toPlainText();
        QByteArray array = data.toUtf8().toHex();
        ui->recvText->setText(QString(array));
    } else {  // 十六进制 --> 十进制
        QString data = ui->recvText->toPlainText();
        QByteArray array;
        if (data.size() % 2 != 0) {
            data.insert(0, '0');
        }
        for (int i = 0; i < data.size() / 2; i++) {
            QString t = data.mid(2*i, 2);
            bool ok = false;
            int ihex = t.toInt(&ok, 16);
            array.append(ihex);
        }
        ui->recvText->setText(QString(array));
    }
}

// 接收串口数据
void SerialPortApp::read_data() {
    // 读到的数据是一个个字节
    QByteArray array = mSerial.readAll();
    recvsize += array.size();  // 显示已接收到的字节数

    if (ui->recvHexCb->isChecked()) {
        ui->recvText->append(array.toHex());
    } else {
        ui->recvText->append(array);
    }

    // 设置显示已接收到的字节数
    ui->recvsizelabel->setText(QString::number(recvsize));
}

// 清空已接收的字节数
void SerialPortApp::on_clearRecvSizeBt_clicked() {
    recvsize = 0;
    ui->recvText->clear();
    ui->recvsizelabel->setText("0");
}

// 选择要发送的文件
void SerialPortApp::on_selectfileBt_clicked() {
    QString path = QFileDialog::getOpenFileName(this);
    ui->filepathEdit->setText(path);
}

// 发送文件
void SerialPortApp::on_sendfileBt_clicked() {
    // 当数据发送完毕后会发出一个信号 &QSerialPort::bytesWritten
    // 每当有效载荷的数据写入到设备当前的写入通道时,就会发出这个信号
    connect(&mSerial, &QSerialPort::bytesWritten, this, &SerialPortApp::send_file_text);

    // 打开文件
    file.setFileName(ui->filepathEdit->text());
    if (!file.open(QIODevice::ReadOnly)) {
        return;
    }

    // 获取文件大小
    int filesize = file.size();
    ui->progressBar->setMaximum(filesize);

    // 设置进度条显示
    QByteArray array = file.read(128);  // 每次读取 128 字节内容
    sendfilesize = mSerial.write(array);
    ui->progressBar->setValue(sendfilesize);
}

// 循环(每 128 字节)发送文件
void SerialPortApp::send_file_text(quint64 size) {
    // 设置进度条显示
    QByteArray array = file.read(128);
    quint64 mSize = mSerial.write(array);
    sendfilesize += mSize;
    ui->progressBar->setValue(sendfilesize);

    // 判断文件是否发送完毕
    if (sendfilesize == ui->progressBar->maximum()) {
        file.close();
        disconnect(&mSerial, &QSerialPort::bytesWritten, this, &SerialPortApp::send_file_text);
    }
}

3.3 serialportapp.ui

在这里插入图片描述

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

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

相关文章

渗透测试--Nacos系统

免责声明&#xff1a; 文章中涉及的漏洞均已修复&#xff0c;敏感信息均已做打码处理&#xff0c;文章仅做经验分享用途&#xff0c;切勿当真&#xff0c;未授权的攻击属于非法行为&#xff01;文章中敏感信息均已做多层打马处理。传播、利用本文章所提供的信息而造成的任何直…

从零到Kafka:万字带你体验Spring Boot整合消息驱动的奇妙之旅

Spring Boot与Kafka从零开始整合指南 准备工作创建项目 Spring Boot与Kafka的初次邂逅配置生产者消费者模拟测试 消息处理生产者发送消息消费者处理消息自定义序列化器 主页传送门&#xff1a;&#x1f4c0; 传送 准备工作 Spring boot: &#xff5c; 基于Spring的开源框架&a…

scipy 笔记:scipy.spatial.distance

1 pdist 计算n维空间中观测点之间的成对距离。 scipy.spatial.distance.pdist(X, metriceuclidean, *, outNone, **kwargs) 1.1 主要参数 X一个m行n列的数组&#xff0c;表示n维空间中的m个原始观测点metric使用的距离度量out输出数组。如果非空&#xff0c;压缩的距离矩阵…

每日一题:LeetCode-103/107.二叉树的(层序/锯齿形层序)遍历

每日一题系列&#xff08;day 04&#xff09; 前言&#xff1a; &#x1f308; &#x1f308; &#x1f308; &#x1f308; &#x1f308; &#x1f308; &#x1f308; &#x1f308; &#x1f308; &#x1f308; &#x1f308; &#x1f308; &#x1f308; &#x1f50e…

interface previously declared 的bug问题

其实就是重复定义了&#xff0c;只需要加如下的代码即可&#xff1a; 其中把APB的部分改成自己的接口名字就好了。

『亚马逊云科技产品测评』活动征文|EC2 实例安装 docker 与配套软件部署前后端分离的医疗管理后台系统

授权声明&#xff1a;本篇文章授权活动官方亚马逊云科技文章转发、改写权&#xff0c;包括不限于在 Developer Centre, 知乎&#xff0c;自媒体平台&#xff0c;第三方开发者媒体等亚马逊云科技官方渠道 目录 一、AWS 产品类别选择 &#xff08;1&#xff09;应用服务器选择…

【接口技术】实验1:简易汇编程序调试

实验1 简易汇编程序调试 一、实验目的 1&#xff1a;掌握在集成开发软件环境中汇编程序的编写、调试方法。 2&#xff1a;学会用DOS功能调用&#xff08;1号和2号&#xff09;完成输入和输出。 二、实验内容 1-1&#xff1a; 在以BUF为首地址的字节存储区存放有若干个无符…

02 RANSAC算法 及 Python 实现

文章目录 02 RANSAC算法 及 Python 实现2.1 简介2.2 算法流程2.3 RANSAC 算法实现直线拟合2.4 利用 RANSAC 算法减少 ORB 特征点误匹配 02 RANSAC算法 及 Python 实现 2.1 简介 RANSAC &#xff08;Random Sample Consensus&#xff0c;随机抽样一致&#xff09;算法的 基本假…

网络通信基础概念介绍

网络通信基础概念介绍 局域网LAN 局域网&#xff0c;即 Local Area Network&#xff0c;简称LAN。 局域网内的主机之间能方便的进行网络通信&#xff0c;又称为内网&#xff1b;局域网和局域网之间在没有连接的情况下&#xff0c;是无法通信的。 局域网是指在一个相对较小的…

Android设计模式--外观模式

弈之为术&#xff0c;在人自悟 一&#xff0c;定义 外观模式要求一个子系统的外部与其内部的通信必须通过一个统一的对象进行。提供一个高层次的接口&#xff0c;使得子系统更易于使用。 外观模式在开发中的使用频率是非常高的&#xff0c;尤其是在第三方的SDK里面&#xff0…

redis运维(二十)redis 的扩展应用 lua(二)

一 redis 的扩展应用 lua redis lua脚本语法 ① 什么是脚本缓存 redis 缓存lua脚本 说明&#xff1a; 重启redis,脚本缓存会丢失 下面讲解 SCRIPT ... 系列 SCRIPT ② LOAD 语法&#xff1a;SCRIPT LOAD lua代码 -->载入一个脚本,只是预加载,不执行思考1&#xff1…

leetcode中“辅助栈”类题目和“单调栈”类题目的异同

1 总结 1 栈中元素的特性 2 单调栈存在一次性连续删除多个栈顶的情况&#xff0c;但是普通的栈&#xff0c;一次只pop掉一个栈顶元素 2 LC1209. 删除字符串中的所有相邻重复项 II - 普通辅助栈 class Solution {public String removeDuplicates(String s, int k) {int ns.l…

OSG粒子系统与阴影-爆炸模拟(3)

爆炸模拟示例 爆炸模拟示例的代码如程序清单11-4 所示&#xff1a; /* 爆炸模拟示例 */ void explosion_11_4() {osg::ref_ptr<osgViewer::Viewer> viewer new osgViewer::Viewer();osg::ref_ptr<osg::GraphicsContext::Traits> traits new osg::GraphicsContex…

Linux:Ubuntu虚拟机安装详解:VMware下的逐步指南

目录 1. centOS系统 2. ubuntu系统 1. 下载Ubuntu映像 step1 step2 step3 2. 新建虚拟机 step1 step2 Step3 step4 step5 step6 内存 内核 映像 显示 网络 3. 网络配置 NAT模式 本机IP获取 ​编辑 bridge模式 4. 开启虚拟机 5. 虚拟机常用配置 语言 …

物联网后端个人第十二周总结

学习工作进度 物联网方面 1.模拟设备通过规则引擎将数据通过mqtt进行转发 在物联网平台上实现模拟设备通过规则引擎将数据通过mqtt进行转发已经全部完成了&#xff0c;所使用的物联网平台在这方面有不少的问题和bug&#xff0c;也可能是没有按照开发者的想法对平台进行使用才导…

文章解读与仿真程序复现思路——电网技术EI\CSCD\北大核心《考虑系统一次频率响应特性的新型电力系统源网荷储协调鲁棒规划》

这篇文章的标题涉及到新型电力系统的源&#xff08;发电源&#xff09;、网&#xff08;电网&#xff09;、荷&#xff08;负荷&#xff0c;即用电需求&#xff09;以及储&#xff08;储能系统&#xff09;的协调鲁棒规划&#xff0c;其中考虑了系统的一次频率响应特性。 让我…

软件设计师:计算机组成与体系结构之计算机基础知识

计算机基础知识 数据的表示 码制及进制转换 原码&#xff1a;将数值转成二进制反码&#xff1a;正数与原码完全相同&#xff1b;负数&#xff0c;除了符号位其他位取反补码&#xff1a;正数与原码完全相同&#xff1b;负数&#xff0c;在补码的基础上加1移码&#xff1a;补码…

CUDA编程二、C++和cuda混合编程的一些基础知识点

目录 一、C运行过程 1、C编译过程 2、代码运行示例 单文件 多文件 a、编译所有cpp文件&#xff0c;但是不链接 b、链接所有的.o文件 c、运行程序 CMake编译 代码 使用方法 编译过程 代码运行 二、C和cuda混合编程 cuda 单文件 cuda和C多文件 手动分步编译 C…

TDA笔记:夏克林老师,南洋理工大学

TDA比传统的统计方法有优势&#xff1a;benchmark中展现了这种优势 laplacian矩阵 多种单纯复形构造方式&#xff0c;可以构造出不同表征 二部图&#xff1a;Dowker complex Tor algebra可以用到多大数据 目前较新