当前位置: 首页 > news >正文

Qt学习(开发环境Visual Studio)6.串口助手

本文将使用Qt来做一个串口助手,并实现通信。

首先了解一下串口助手,简单来说,串口助手就是电脑和设备之间的翻译官兼搬运工。

它的工作原理可以浓缩为三步:

物理连接靠“伪装”:现在电脑没有老式串口,全靠USB转串口芯片和驱动,在系统里“伪造”出一个虚拟串口(如COM3)。

收发数据靠“拆装”:发数据时,它把你的文字按字符编码表(如ASCII)转成数字,交给驱动和芯片,拆成一个一个的比特位发出去。收数据时,过程反过来,把收到的比特位拼成字节,再恢复成文字。

核心前提是“约定”:你自己设定的波特率、数据位、停止位这些参数,只是为了告诉驱动和硬件该以什么节奏拆装数据。通信双方的这些设置必须完全一致,否则就会乱码。

目录

新建项目

ui界面设计

属性配置

查看Qt助手

代码部分


新建项目

将项目命名为 SerialPortGui,点击创建

将类的名字改为SerialPort ,选择QWidget

创建完成:

从网上下载一个串口助手的程序:

为了简单起见,我们做的串口就保留核心的功能:端口、波特率、校验位、数据位、停止位(combox);打开串口,发送按钮(botton);发送区,接收区(textEdit)这几个功能即可。

ui界面设计

界面如下,关于控件的命名看右侧栏。

接收区要设置为只读

发送按钮选上checkable,这样点击后变色,用于区分有没有打开。

属性配置

点击项目 右键->属性->Qt Project Settings

勾选串口需要的模块:

查看Qt助手

找到SerialPort类:

还有一个SerialPortInfo

SerialPort类Inherits继承自QIODevice,这是QT中所有输入/输出设备的抽象类,提供了统一的接口来读写各种设备

看一下串口的Public Types:

BaudRate 波特率,类型是枚举:QT里面固定提供了1200、2400、4800等数值,其他类似,DataBits 数据位,Parity 校验位,StopBits 停止位,FlowControl是否使用流控等是我们需要用到的。

这部分是他们的类型

来看函数

这几个函数用来获取信息,比如parity()const 是用来获取当前串口校验位置的查询函数,它的返回类型是QSerialPort::Parity枚举类型,可能的值:

QSerialPort::NoParity // 无校验 (0) QSerialPort::EvenParity // 偶校验 (2) QSerialPort::OddParity // 奇校验 (3) QSerialPort::SpaceParity // 空校验 (4) QSerialPort::MarkParity // 标记校验 (5)

为了实现串口通信,首先要打开串口->获取有效串口Port->设置波特率和校验信息等->编辑发送内容->点击发送

这个函数用来获取有用的串口:

QIODevice类里面有关于读写的部分。

代码部分

这个项目分成两个部分,涉及到界面的一部分,实现串口通信的是一部分。所以要新建项

再新建一个头文件放结构体

#include<Qstring> struct SerialPortParam { QString spp_port_name = "COM5"; //串口名称 qint32 spp_baud_rate = 9600; //波特率 int spp_data_bits = 5; //数据位 int spp_parity = 0; //校验位 int spp_stop_bits = 0; //停止位 };

给serialPort.h添加头文件

#pragma once #include <QtSerialPort/QSerialPort> #include <QTextStream> #include <QObject> #include "Struct.h" /* * @brief 创建结构体,包含串口各个参数 */ class SerialPort : public QSerialPort { Q_OBJECT public: SerialPort(QObject * parent = nullptr); ~SerialPort(); public: /* * @brief 打开/关闭串口 * @param in spp 结构体包含了串口的各个参数 */ bool open_port(const SerialPortParam &spp); /* * @brief 关闭串口 */ void close_port(); /* * @brief 发送数据 * @param data 发送的二进制数据 */ void send_data(const QByteArray& data); /* * @brief 获取可用串口列表 * @param out 串口列表 */ QStringList get_vali_port(); signals: /* * @brief 接收到数据的信号 * @param data 接收的二进制数据 */ void sigDataReceived(const QByteArray& data); /* * @brief 串口状态变化的信号 * @param isOpen 串口是否开启 */ void sigPortStatusChanged(bool isOpen); private slots: /* * @brief 读取串口数据 */ void slotReadData(); private: QSerialPort* m_serialPort = nullptr; };

然后是SerialPort.cpp

#include "SerialPort.h" #include <QtSerialPort/QSerialPortInfo> // 构造函数,初始化串口对象 SerialPort::SerialPort(QObject*parent) { m_serialPort = new QSerialPort(this); // 连接串口的readyRead信号到readData槽函数,串口有数据可读,触发slotReadData connect(m_serialPort, &QSerialPort::readyRead, this, &SerialPort::slotReadData); } SerialPort::~SerialPort() { } bool SerialPort::open_port(const SerialPortParam& spp) { // 如果串口已经打开,直接返回true if (m_serialPort->isOpen()) { return true; } // 配置串口参数 m_serialPort->setPortName(spp.spp_port_name);// 设置端口名 m_serialPort->setBaudRate(spp.spp_baud_rate);// 设置波特率 m_serialPort->setDataBits(static_cast<QSerialPort::DataBits>(spp.spp_data_bits));// 设置数据位 m_serialPort->setParity(static_cast<QSerialPort::Parity>(spp.spp_parity)); // 设置校验位 m_serialPort->setStopBits(static_cast<QSerialPort::StopBits>(spp.spp_stop_bits)); // 设置停止位 m_serialPort->setFlowControl(QSerialPort::NoFlowControl);// 禁用流控 bool bRt = m_serialPort->open(QIODevice::ReadWrite); return bRt; } void SerialPort::close_port() { if (m_serialPort->isOpen()) { m_serialPort->close(); // 关闭串口 emit sigPortStatusChanged(false); // 发送状态变化信号 } } void SerialPort::send_data(const QByteArray& data) { // 检查串口是否打开 if (m_serialPort->isOpen()) { m_serialPort->write(data); // 写入数据 emit get_vali_port(); } } //获取可用串口列表 QStringList SerialPort::get_vali_port() { QStringList ports; foreach(const QSerialPortInfo & info, QSerialPortInfo::availablePorts()) { ports << info.portName();///将串口名添加到列表 } return ports; //返回系统可用串口的名称列表 } // 读取串口数据 void SerialPort::slotReadData() { QByteArray data = m_serialPort->readAll(); // 读取所有可用数据 emit sigDataReceived(data); // 发送数据接收信号 }

SerialPortWid.h

#pragma once #include "SerialPort.h" #include "ui_SerialPort.h" class SerialPortWid : public QWidget { Q_OBJECT public: SerialPortWid(QWidget* parent = nullptr); ~SerialPortWid(); private slots: /* * @brief 打开/关闭串口按钮点击槽函数 */ void slotOpen(); /* * @brief 判断发送区内容是否被用户编辑 * @param 发送区的内容 */ void slotTextEdited(const QString& text); /** * @brief 发送数据按钮点击槽函数 */ void slotSend(); /** * @brief 串口状态变化槽函数 * @param isOpen 串口是否打开 */ void slotPortStatusChanged(bool isOpen); /** * @brief 数据接收槽函数 * @param data 接收到的数据 */ void slotReceived(const QByteArray& data); private: /** * @brief 更新串口参数设置 * 从UI控件读取当前参数并保存 */ void updatePortSettings(); private: Ui::SerialPortWid* ui = nullptr; //ui界面指针 SerialPort* m_serialPort = nullptr; //串口操作对象 };

serialPort.cpp

#include "SerialPortWid.h" #include "Struct.h" SerialPortWid::SerialPortWid(QWidget* parent) : QWidget(parent) , ui(new Ui::SerialPortWid) , m_serialPort(new SerialPort(this)) { ui->setupUi(this); //为QComboBox的每个选项设置关联的用户数据 ui->m_cbParity->setItemData(0, QSerialPort::NoParity);//校验位 无校验 ui->m_cbParity->setItemData(1, QSerialPort::EvenParity);//偶校验 ui->m_cbParity->setItemData(2, QSerialPort::OddParity);//奇校验 ui->m_cbStopBits->setItemData(0, QSerialPort::OneStop);//停止位1 ui->m_cbStopBits->setItemData(1, QSerialPort::OneAndHalfStop);//停止位1.5 ui->m_cbStopBits->setItemData(2, QSerialPort::TwoStop);//停止位2 // 连接信号槽 connect(ui->m_pbOpenClose, &QPushButton::clicked, this, &SerialPortWid::slotOpen);//打开串口 connect(m_serialPort, &SerialPort::sigPortStatusChanged, this, &SerialPortWid::slotPortStatusChanged);//更新UI connect(m_serialPort, &SerialPort::sigDataReceived, this, &SerialPortWid::slotReceived);//显示数据 connect(ui->m_teSend, &QLineEdit::textEdited, this, &SerialPortWid::slotTextEdited);//判断文本是否编辑 connect(ui->m_pbSend,&QPushButton::clicked, this, &SerialPortWid::slotSend);//发送数据 // 扫描可用串口 QStringList ports = m_serialPort->get_vali_port(); ui->m_cbPort->clear(); ui->m_cbPort->addItems(ports); if (!ports.isEmpty()) { ui->m_cbPort->setCurrentIndex(0); } } SerialPortWid::~SerialPortWid() { } //打开/关闭串口按钮点击事件 void SerialPortWid::slotOpen() { if (!ui->m_pbOpenClose->isChecked()) { m_serialPort->close_port(); } else { updatePortSettings(); // 从UI更新串口参数 SerialPortParam spp; // 创建参数结构体 // 从UI控件读取参数 spp.spp_port_name = ui->m_cbPort->currentText();//端口名 spp.spp_baud_rate = ui->m_cbBaudRate->currentText().toInt();// 波特率 bool ok; int dataBits = ui->m_cbDataBits->currentText().toInt(&ok); if (ok) { spp.spp_data_bits = static_cast<QSerialPort::DataBits>(dataBits); } spp.spp_data_bits = static_cast<QSerialPort::DataBits>(ui->m_cbDataBits->currentText().toInt()); // 数据位 spp.spp_parity = static_cast<QSerialPort::Parity>(ui->m_cbParity->currentData().toInt());// 校验位 spp.spp_stop_bits = static_cast<QSerialPort::StopBits>(ui->m_cbStopBits->currentData().toInt()); // 停止位 m_serialPort->open_port(spp); // 打开串口 } } void SerialPortWid::slotTextEdited(const QString& text) { ui->m_pbSend->setEnabled(!text.isEmpty()); } void SerialPortWid::slotSend() { QString text = ui->m_teSend->text(); // 获取发送框文本 if (!text.isEmpty()) { m_serialPort->send_data(text.toUtf8()); } } void SerialPortWid::slotPortStatusChanged(bool isOpen) { Q_UNUSED(isOpen); // 避免未使用参数警告 } void SerialPortWid::slotReceived(const QByteArray& data) { ui->m_teRecevie->append(QString::fromUtf8(data)); } void SerialPortWid::updatePortSettings() { SerialPortParam param; // 设置端口名称 param.spp_port_name = ui->m_cbPort->currentText(); // 设置波特率 QString baudRateStr = ui->m_cbBaudRate->currentText(); param.spp_baud_rate = baudRateStr.toInt(); // 设置数据位(使用itemData获取关联的枚举值) param.spp_data_bits = static_cast<QSerialPort::DataBits>( ui->m_cbDataBits->currentData().toInt()); // 设置校验位(使用itemData获取关联的枚举值) param.spp_parity = static_cast<QSerialPort::Parity>( ui->m_cbParity->currentData().toInt()); // 设置停止位(使用itemData获取关联的枚举值) param.spp_stop_bits = static_cast<QSerialPort::StopBits>( ui->m_cbStopBits->currentData().toInt()); }

可以正常通信了~

http://www.jsqmd.com/news/1188877/

相关文章:

  • lingjing外部靶机 targetedPractice:burp-labs
  • 数据库多语言存储:从加列到翻译表的五种方案深度解析
  • 2026年中国高端门窗行业十大品牌盘点,附行业现状
  • 积家中国官方售后服务中心|服务热线及具体地址权威信息声明(2026年7月最新) - 积家官方售后服务中心
  • 【JAVA毕设源码分享】基于springboot寻味中华网美食推荐网站的设计与实现(程序+文档+代码讲解+一条龙定制)
  • 新快消新零售——解读数字战略与新零售【附全文阅读】
  • 海口香奈儿上门回收哪家靠谱?奢二网无服务费当场全款转账,正规高价回收包包店铺推荐 - 每日生活报
  • 基于Torch7的CIFAR-10图像分类实战:从数据预处理到Kaggle提交
  • 什么是 AI Agent?我天天派一群 AI 自己跑完活回来交差,这篇讲清它和聊天机器人差在哪
  • FineReport(帆软报表)V11.05安装使用教程
  • 企业群组自动化管理:智能监控与响应方案
  • Agentic AI Home Energy Management System: A Large Language Model Framework for Residential Load S...
  • Linux顺序锁(seqlock)原理与应用详解
  • C++类模板实战:从零构建可配置的SmartArray容器
  • 构建高可用双因素认证系统:融合TOTP与FIDO2协议的设计与实践
  • 专科生论文写作神器:8款AI工具实战指南
  • Wand-Enhancer完全指南:如何为WeMod客户端解锁高级功能并构建远程控制面板
  • 2026年7月最新厦门帝舵官方售后维修服务网点地址与客服电话 - 帝舵中国官方服务中心
  • Unity UI -- (7) 世界空间UI:从Canvas渲染模式到3D场景交互设计
  • 重磅发布!深圳钻石回收机构权威排名发布,收的顶凭专业鉴定,资质齐全领跑六大机构附地址 - 奢侈品回收测评
  • 系统规划与管理师-IT 治理核心知识点全解析
  • C++在医疗影像领域的12项系统级优化与性能优势解析
  • 无限创意画布AI工具怎么选?五大热门工具实力排行与场景推荐
  • 惠普暗影精灵终极性能解锁:OmenSuperHub完整使用指南
  • 创建账户和组
  • Red Hat Enterprise Linux 7 / 8 / 9 配置国内镜像源与本地源实战指南
  • 从核心点到噪声点:DBSCAN密度聚类算法实战解析
  • 2026避坑指南|西安首饰回收交易全攻略,正规机构筛选标准一目了然 - 全国二奢机构参考
  • 2026年7月最新宁波萧邦官方售后维修服务网点地址与客服电话 - 萧邦中国官方服务中心
  • 工业负载控制方案:TPD2017FN与PIC18F46K22实战应用