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

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

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

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

一 redis 的扩展应用 lua redis加载lua脚本文件 ① 调试lua脚本 redis-cli 通过管道 --pipe 快速导入数据到redis中 ② 预加载方式 1、错误方式 2、正确方式 "案例讲解" ③ 一次性加载 执行命令&#xff1a; redis-cli -a 密码 --eval Lua脚本路径 key …

从程序员到解决方案工程师:一次跨界的职场冒险

在科技行业里&#xff0c;程序员和解决方案工程师是两个非常常见的职业。虽然这两个职业都需要一定的行业理解和问题解决能力&#xff0c;但它们的工作内容和职责却有很大的不同。 那么&#xff0c;如果一名程序员决定转行做一名解决方案工程师&#xff0c;他会经历怎样的体验…

03 _ 系统设计目标(一):如何提升系统性能?

提到互联网系统设计&#xff0c;可能听到最多的词就是“三高”&#xff0c;也就是“高并发”“高性能”“高可用”&#xff0c;它们是互联网系统架构设计永恒的主题。这里将整体探讨下高并发系统设计的目标&#xff0c;然后在此基础上&#xff0c;探讨下&#xff1a;如何提升系…

如何使用 WordPress搭建一个博客?详细搭建教程

域名服务器环境WordPress程序个人博客或企业官网等 前言&#xff1a;域名服务器是需要一些费用域名是一年服务器需要租赁3个月以上的才有备案码推荐购买一年,WordPress主题和插件有免费和付费的看自己需求 一、环境已经安装好了已经在运营项目咋就跳过从建站开始 二、进入根目录…

曲线拟合:走进数据建模中的艺术与科学

在现代科学和工程领域&#xff0c;曲线拟合是一项重要的数据分析技术&#xff0c;它可以通过数学模型来近似描述实际数据中的复杂关系。本文将详细介绍曲线拟合的基本概念、方法和应用领域&#xff0c;并探究其在数据建模中的艺术与科学。 第一节&#xff1a;曲线拟合的基本概…

IBM X3650M4安装ESXI6.5卡在/lsl_mr3.v00

环境&#xff1a;IBM X3650M4服务器双盘配置raid1&#xff0c;通过rufus制作启动U盘&#xff0c;安装VMware Vsphere 5.5系统 问题&#xff1a;卡在/lsi_mr3.v00界面无法往下运行&#xff08;两台配置一样的机器遇到同样的问题&#xff09; 解决方案&#xff1a; 直接在U盘根…

远程安全访问JumpServer:使用cpolar内网穿透搭建固定公网地址

文章目录 前言1. 安装Jump server2. 本地访问jump server3. 安装 cpolar内网穿透软件4. 配置Jump server公网访问地址5. 公网远程访问Jump server6. 固定Jump server公网地址 前言 JumpServer 是广受欢迎的开源堡垒机&#xff0c;是符合 4A 规范的专业运维安全审计系统。JumpS…

pop链反序列化 [MRCTF2020]Ezpop1

打开题目 网站源码为 Welcome to index.php <?php //flag is in flag.php //WTF IS THIS? //Learn From https://ctf.ieki.xyz/library/php.html#%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E9%AD%94%E6%9C%AF%E6%96%B9%E6%B3%95 //And Crack It! class Modifier {protected …

【问题思考总结】多维随机变量函数的分布的两种情况的计算方法【离连/连连】

问题 今天做李六第一套的时候发现&#xff0c;有的时候&#xff0c;面对这种第二问的题&#xff0c;很自然地就想到了Fz&#xff08;z&#xff09;&#xff0c;然后进行化简&#xff0c;但是有的时候&#xff0c;像这道题&#xff0c;就突然发现P{XY<z}是一个非常复杂的形式…

【java】-D参数使用

在开发过程中我们使用开源工具经常会用到在启动命令时候加入一个 -Dxxx 类型的参数。到底-Dxxx是干什么用的了。 官方文档 地址&#xff1a;文档地址 java命令使用 下面是来源于官方文档&#xff1a; java [options] classname [args] java [options] -jar filename [args…

VUE留言板

效果预览图 完整代码 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>作业</title><styl…

Element-UI Upload 手动上传文件的实现与优化

文章目录 引言第一部分&#xff1a;Element-UI Upload 基本用法1.1 安装 Element-UI1.2 使用 <el-upload> 组件 第二部分&#xff1a;手动上传文件2.1 手动触发上传2.2 手动上传时的文件处理 第三部分&#xff1a;性能优化3.1 并发上传3.2 文件上传限制 结语 &#x1f38…

【Linux】常见指令及周边知识(一)

【Linux】常见指令及周边知识&#xff08;一&#xff09; 一、初始Linux操作系统1.Linux背景2.如何使用Linux 二、学习Linux之前的预备周边知识&#xff08;重点&#xff09;&#xff1a;1.什么叫做文件&#xff1f;2. Linux下的路径分隔符3.在Linux中为什么会存在路径&#xf…

了解抽象思维的应用与实践

目录 一、快速了解抽象思维 &#xff08;一&#xff09;抽象思维的本质理解 &#xff08;二&#xff09;系统架构中的重要性 &#xff08;三&#xff09;软件开发中抽象的基本过程思考 意识和手段 抽象的方式 抽象层次的权衡 二、业务中的应用实践 &#xff08;一&…

百度AI布局:从财报看百度的核心竞争力和未来发展方向

百度是中国最大的搜索引擎&#xff0c;也是全球领先的人工智能&#xff08;AI&#xff09;公司。百度在2023年第三季度业绩中&#xff0c;展示了其在AI领域的强劲表现和广阔前景。 百度财报透露了关于AI业务的哪些重要信息&#xff1f; 百度在2023年第三季度的财报中&#xf…

Eclipse安装EvoSuite插件

Eclipse安装EvoSuite插件 EvoSuite自动为Java类生成JUnit测试套件,针对分支覆盖率等代码覆盖率标准。为了提高可读性,生成的单元测试被最小化,并且捕获被测试类的当前行为的回归断言被添加到测试中。 EvoSuite的GitHub地址:https://github.com/EvoSuite/evosuite 📕Ecl…

MobileNets发展与总结

写在前面&#xff1a;本博客仅作记录学习之用&#xff0c;部分图片来自网络&#xff0c;如需引用请注明出处&#xff0c;同时如有侵犯您的权益&#xff0c;请联系删除&#xff01; 文章目录 引言MobileNetsMobileNet - V1思想代码实现 MobileNet - V2思想代码实现 MobileNet - …

flask依据现有的库表快速生成flask实体类

flask依据现有的库表快速生成flask实体类 在实际开发过程中&#xff0c;flask的sqlalchemy对应的model类写起来重复性较强&#xff0c;如果表比较多会比较繁琐&#xff0c;这个时候可以使用 flask-sqlacodegen 来快速的生成model程序或者py文件&#xff0c;以下是简单的示例&a…

人力资源管理后台 === 基础环境+登陆

目录 1.人力资源项目介绍 1.1 项目架构和解决方案 1.2 课程安排 1.3 课程具备能力 1.4 课程地址 2. 拉取项目基础代码 3.项目目录和入口文件介绍 4.App.vue根组件解析 5.基础设置settings.js和导航守卫permission.js 6.Vuex的结构 7.使用模板中的Icon图标 8.扩展…

【Python】(自定义类)计算语句执行时间

一个玩具&#xff0c;写着来玩的。 用的time模块&#xff0c;代码很简单(所以才说是个玩具) 代码&#xff1a; import time class TimeStamp:__timestampNone__keyNonedef __init__(self,tipsNone,keyNone):self.__timestamp{}self.NewStamp(tips,key)def NewStamp(self,tips,…