Qt学习(开发环境Visual Studio)6.串口助手 📅 发布时间:2026/7/14 18:45:08 👁️ 浏览次数: 本文将使用Qt来做一个串口助手并实现通信。首先了解一下串口助手简单来说串口助手就是电脑和设备之间的翻译官兼搬运工。它的工作原理可以浓缩为三步物理连接靠“伪装”现在电脑没有老式串口全靠USB转串口芯片和驱动在系统里“伪造”出一个虚拟串口如COM3。收发数据靠“拆装”发数据时它把你的文字按字符编码表如ASCII转成数字交给驱动和芯片拆成一个一个的比特位发出去。收数据时过程反过来把收到的比特位拼成字节再恢复成文字。核心前提是“约定”你自己设定的波特率、数据位、停止位这些参数只是为了告诉驱动和硬件该以什么节奏拆装数据。通信双方的这些设置必须完全一致否则就会乱码。目录新建项目ui界面设计属性配置查看Qt助手代码部分新建项目将项目命名为 SerialPortGui点击创建将类的名字改为SerialPort 选择QWidget创建完成从网上下载一个串口助手的程序为了简单起见我们做的串口就保留核心的功能端口、波特率、校验位、数据位、停止位combox打开串口发送按钮(botton)发送区接收区textEdit这几个功能即可。ui界面设计界面如下关于控件的命名看右侧栏。接收区要设置为只读发送按钮选上checkable这样点击后变色用于区分有没有打开。属性配置点击项目 右键-属性-Qt Project Settings勾选串口需要的模块查看Qt助手找到SerialPort类还有一个SerialPortInfoSerialPort类Inherits继承自QIODevice这是QT中所有输入/输出设备的抽象类提供了统一的接口来读写各种设备看一下串口的Public TypesBaudRate 波特率类型是枚举QT里面固定提供了1200、2400、4800等数值其他类似DataBits 数据位Parity 校验位StopBits 停止位FlowControl是否使用流控等是我们需要用到的。这部分是他们的类型来看函数这几个函数用来获取信息比如parityconst 是用来获取当前串口校验位置的查询函数它的返回类型是QSerialPort::Parity枚举类型可能的值QSerialPort::NoParity // 无校验 (0) QSerialPort::EvenParity // 偶校验 (2) QSerialPort::OddParity // 奇校验 (3) QSerialPort::SpaceParity // 空校验 (4) QSerialPort::MarkParity // 标记校验 (5)为了实现串口通信首先要打开串口-获取有效串口Port-设置波特率和校验信息等-编辑发送内容-点击发送这个函数用来获取有用的串口QIODevice类里面有关于读写的部分。代码部分这个项目分成两个部分涉及到界面的一部分实现串口通信的是一部分。所以要新建项再新建一个头文件放结构体#includeQstring 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_castQSerialPort::DataBits(spp.spp_data_bits));// 设置数据位 m_serialPort-setParity(static_castQSerialPort::Parity(spp.spp_parity)); // 设置校验位 m_serialPort-setStopBits(static_castQSerialPort::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_castQSerialPort::DataBits(dataBits); } spp.spp_data_bits static_castQSerialPort::DataBits(ui-m_cbDataBits-currentText().toInt()); // 数据位 spp.spp_parity static_castQSerialPort::Parity(ui-m_cbParity-currentData().toInt());// 校验位 spp.spp_stop_bits static_castQSerialPort::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_castQSerialPort::DataBits( ui-m_cbDataBits-currentData().toInt()); // 设置校验位使用itemData获取关联的枚举值 param.spp_parity static_castQSerialPort::Parity( ui-m_cbParity-currentData().toInt()); // 设置停止位使用itemData获取关联的枚举值 param.spp_stop_bits static_castQSerialPort::StopBits( ui-m_cbStopBits-currentData().toInt()); }可以正常通信了~
从核心点到噪声点:DBSCAN密度聚类算法实战解析 1. 初识DBSCAN:当数据像星空一样分布第一次接触DBSCAN时,我被它的思维方式惊艳到了——它不像K-Means那样要求数据必须乖乖围成球形,而是像观察星空一样,寻找那些星星密集的星座区域。想象你站在郊外仰望夜空,有些区域… 2026/7/14 18:43:08
AI生成论文检测与降重实战指南 1. 论文AI率检测与降重的核心挑战学术写作中AI生成内容检测已成为学术界的新焦点。去年我在投稿核心期刊时,论文被系统检测出92%的AI生成率,经过两个月摸索最终降至16%。这个过程中发现三大核心痛点:第一代检测工具主要依赖表层特征分析&… 2026/7/14 18:43:08
【进阶指南】解锁ChatGPT图文创作:用Markdown与Unsplash API打造专属配图助手 1. 为什么需要ChatGPT生成带配图的内容? 在日常内容创作中,图文结合的形式往往比纯文本更具吸引力。根据研究,带图片的内容能提升80%的用户参与度。但传统工作流需要先写文案,再手动搜索配图,最后排版,整个… 2026/7/14 18:43:08
Windows弹窗广告清理实战:从系统工具到第三方方案 这类工具最值得先看的不是它能清理多少种广告,而是能不能在普通 Windows 环境下稳定运行、不误删系统文件、清理后不反弹。很多号称“一键清理”的工具,要么清理不彻底,要么自己就是弹窗来源。我更建议把第一次使用拆成三步:先确认… 2026/7/14 20:43:42
2026年AI论文助手横评:5款主流工具,谁才是性价比之王? 一句话答案:综合生成、降重、去AI痕迹、排版全链路来看,毕业之家ai(www.biye.com) 在性价比上一骑绝尘,是学生党的首选 AI论文助手。一、为什么需要一款好用的AI论文助手论文写作环节繁多——选题、开题、综述、正文、降重、排版、… 2026/7/14 20:41:42
OpenAI Codex代码审查实战:AI自动PR审查提升开发效率 你是否有过这样的经历:深夜提交完 PR 代码后,第二天早上收到同事的审查意见,发现因为一个简单的语法错误或逻辑漏洞,导致整个功能无法正常测试?或者在小团队中,每个人都忙于自己的开发任务,PR 审… 2026/7/14 20:41:42
Moravec与Forstner算子实战:点特征提取及相关系数匹配的Python实现与调优 1. 点特征提取基础与算法原理在计算机视觉和摄影测量领域,点特征提取是图像匹配、三维重建等任务的基础环节。简单来说,它就像在一张照片中寻找"容易辨认的标记点"——比如棋盘格的交叉点、建筑物的墙角等。这些特征点需要满足两个核心条件&am… 2026/7/14 20:39:41
计算机毕业设计之jsp网吧管理系统 随着我国经济的高速发展与人们生活水平的日益提高,人们对生活质量的追求也多种多样。尤其在人们生活节奏不断加快的当下,人们更趋向于足不出户解决各种问题,网吧管理展现了其蓬勃生命力和广阔的前景。与此同时,为解决用户需求&… 2026/7/14 20:37:41
YOLOv8模型从训练到ONNX部署全流程实战指南 在工业创新竞赛项目中,数据采集与模型部署是构建完整AI应用的关键环节。很多团队在数据采集后需要将模型部署到不同平台,但往往卡在环境配置和格式转换环节。本文将分享一套完整的数据采集软件使用流程,结合远程服务器训练YOLOv8模型… 2026/7/14 20:37:41
XUnity.AutoTranslator 游戏实时翻译插件:从原理到实战的完整指南 1. 项目概述:当游戏语言成为一堵墙作为一名玩了十几年日系、欧美独立游戏的“老油条”,我太懂那种面对一款心仪已久、画风玩法都戳中G点的游戏,却因为语言不通而望而却步的痛了。尤其是那些基于Unity引擎开发的、体量不大但内容精良的作品&am… 2026/7/14 0:05:14
2026普通文员学数据分析的价值 一、2026年普通文员学习数据分析的必要性随着数字化转型加速,数据分析技能正逐渐成为职场基础能力。普通文员学习数据分析可以提升工作效率、增强竞争力,并为职业转型提供更多可能性。二、数据分析对文员的价值自动化办公:通过数据分析工具&a… 2026/7/14 0:05:14
2026从计划员到主管,生产管理者学数据分析有用吗? 一、生产管理领域的职业发展路径 从计划员到主管的角色转变,是生产管理者职业发展的典型路径。计划员主要负责生产排程、库存管理和资源协调等基础工作,而主管则需要承担团队管理、决策支持和效率优化等更高级别的职责。这种转变不仅仅是职位的提升&… 2026/7/14 0:05:14
Git reset 与 revert 深度对比:5个关键差异与 3 种典型应用场景 Git Reset 与 Revert 深度对比:5个关键差异与3种典型应用场景在团队协作开发中,代码版本管理如同行走钢丝——一步失误可能导致整个项目陷入混乱。作为Git进阶用户,你是否曾在深夜面对错误的提交束手无策?是否在强制推送后收到同事… 2026/7/13 8:31:55
GitHub 学生包申请避坑:5个常见失败原因与开发者工具调试方案 GitHub 学生包申请技术排障指南:5个高频失败场景与开发者工具实战方案第一次尝试申请GitHub学生包时,我盯着屏幕上那个不断转圈的加载动画整整15分钟,最终只等来了一行冰冷的错误提示。这可能是许多开发者共同的经历——明明按照教程操作&… 2026/7/14 18:25:04
冒烟测试用例设计规范:5%-10%覆盖率下的3类核心场景与执行标准 冒烟测试用例设计的黄金法则:5%-10%覆盖率下的精准筛选策略在快节奏的敏捷开发环境中,冒烟测试作为质量保障的第一道防线,其重要性不言而喻。当测试资源有限而时间紧迫时,如何从海量测试用例中精准筛选出那关键的5%-10%࿰… 2026/7/14 5:09:41