news 2026/3/11 15:49:29

零基础实现串口通信:QSerialPort从零开始教程

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
零基础实现串口通信:QSerialPort从零开始教程

从零开始搞串口通信:手把手带你用 QSerialPort 写出第一个上位机程序

你有没有遇到过这种情况——手头有个STM32开发板,接上传感器后想看看数据输出,结果发现电脑根本“收不到”?或者写了个小工具发指令给Arduino,可对方毫无反应?

别急。这背后大概率不是硬件坏了,而是串口没配对

在嵌入式、工业控制和物联网的世界里,串口通信(Serial Communication)就像一条看不见的“电话线”,连接着你的PC和各种设备。它不炫酷,但极其可靠;它速度慢,却足够稳定。更重要的是——只要你懂一点Qt,就能轻松驾驭它。

今天我们要聊的就是 Qt 中那个让你秒变“软硬通吃”的利器:QSerialPort
不用怕没基础,这篇文章就是为完全零经验者准备的。我们不堆术语,不讲理论套话,只干一件事:从新建项目开始,一步步做出一个能收能发的串口助手


为什么是 QSerialPort?

你说现在都2025年了,Wi-Fi、蓝牙、USB Type-C满天飞,还谈什么串口?

问得好。

但现实是:你在调试任何一块单片机时,第一件事永远是打开串口打印日志。无论是 Arduino 的Serial.println(),还是 STM32 HAL 库里的printf重定向,背后走的都是 UART 协议。

而作为 PC 端开发者,你要做的就是——听懂这台设备在说什么

这时候,QSerialPort就登场了。

它是 Qt 官方提供的串口模块,封装了 Windows 的CreateFile/ReadFile、Linux 的/dev/tty*操作,让你用同一套代码,在三个主流操作系统上都能正常工作。

而且它基于 Qt 的信号槽机制,天然适合 GUI 编程。也就是说,你不需要开线程去轮询数据,只要一有新消息进来,系统自动通知你:“嘿,有数据来了!”

这才是现代 C++ 开发该有的样子。


第一步:让工程认识 QSerialPort

新建一个 Qt Widgets Application 项目后,首先要告诉编译器:“我要用串口功能”。

打开.pro文件,加上这一行:

QT += serialport

就这么简单。接下来在需要的地方引入头文件:

#include <QSerialPort> #include <QSerialPortInfo>

前者负责读写操作,后者用来扫描当前有哪些串口可用。

⚠️ 注意:如果提示找不到QSerialPort,说明你安装的 Qt 版本没带这个模块。建议使用在线安装器勾选 “Qt Serial Port” 组件。


第二步:找出你的设备插在哪个口上

想象一下:你把 Arduino 插到电脑 USB 口,系统分配了一个 COM3 或/dev/ttyACM0。但你怎么知道是哪一个?难道让用户凭空猜?

当然不行。

所以第一步应该是——枚举所有可用串口,让用户自己选。

#include <QSerialPortInfo> for (const QSerialPortInfo &info : QSerialPortInfo::availablePorts()) { qDebug() << "端口名称:" << info.portName(); qDebug() << "设备描述:" << info.description(); qDebug() << "制造商:" << info.manufacturer(); }

运行这段代码,你会看到类似这样的输出:

端口名称: "COM3" 设备描述: "USB Serial Device" 制造商: "Arduino LLC"

是不是立刻就知道该连哪个了?

如果你做图形界面,可以把这些信息填充进一个下拉框(QComboBox),用户点一下就能选定目标设备。


第三步:建立连接——参数必须严丝合缝

串口通信不像网络那样智能协商,它的规则非常原始:双方必须事先约定好所有参数,否则就会“鸡同鸭讲”。

这些参数包括:

参数常见值
波特率9600, 115200
数据位8
停止位1
校验位无(NoParity)
流控无(NoFlowControl)

其中最重要的是波特率。比如你设成 115200,而单片机那边是 9600,那收到的数据就是一堆乱码。

下面我们来配置并打开串口:

QSerialPort serial; // 设置要连接的端口名(根据用户选择) serial.setPortName("COM3"); // Windows 示例 // serial.setPortName("/dev/ttyUSB0"); // Linux/Mac 示例 // 配置通信参数 serial.setBaudRate(QSerialPort::Baud115200); serial.setDataBits(QSerialPort::Data8); serial.setParity(QSerialPort::NoParity); serial.setStopBits(QSerialPort::OneStop); serial.setFlowControl(QSerialPort::NoFlowControl); // 尝试打开 if (serial.open(QIODevice::ReadWrite)) { qDebug() << "✅ 串口已成功打开"; } else { qWarning() << "❌ 打开失败:" << serial.errorString(); }

💡坑点提醒

  • 如果提示“权限被拒绝”(Permission denied),在 Linux/macOS 上可能需要将用户加入dialoutuucp组。
  • 如果提示“设备正在使用”,检查是否已有其他软件(如串口助手、Arduino IDE)占用了该端口。

第四步:异步接收数据——千万别卡主线程!

很多人初学时喜欢这样写接收逻辑:

while (true) { if (serial.hasBytesAvailable()) { auto data = serial.readAll(); process(data); } }

大错特错!

这种轮询方式会阻塞主线程,导致界面直接“卡死”。正确的做法是利用 Qt 的事件驱动模型,通过信号来响应数据到达。

关键信号是:readyRead

connect(&serial, &QSerialPort::readyRead, [&]() { QByteArray data = serial.readAll(); qDebug() << "📩 收到数据:" << data; // 转成字符串显示(支持中文) QString text = QString::fromUtf8(data); ui->textBrowser->append(text); // 显示在文本框中 });

这个readyRead()信号会在操作系统内核通知“有新数据到达”时自动触发。你只需要快速把数据取出来处理即可。

✅ 最佳实践:

  • 不要在槽函数里做耗时计算(如解析大文件),否则会影响后续数据接收;
  • 使用readAll()一次性读完缓冲区,避免遗漏;
  • 若需处理帧协议(如以\n结尾),可用readLine(),但记得设置超时防止阻塞。

第五步:发送数据——你可以发文本也能发十六进制

发送比接收更简单,调用write()就行。

发送普通文本

QString msg = "开启LED\n"; serial.write(msg.toUtf8());

注意要用toUtf8(),否则中文会乱码。

发送十六进制命令

有些协议要求特定字节流,比如 Modbus 或自定义二进制协议:

QByteArray cmd; cmd.append(0x01); // 地址 cmd.append(0x03); // 功能码 cmd.append(0x00); // 起始地址高 cmd.append(0x00); // 起始地址低 cmd.append(0x00); // 寄存器数量高 cmd.append(0x01); // 寄存器数量低 cmd.append(0x84); // CRC校验高 cmd.append(0x0A); // CRC校验低 serial.write(cmd);

🔍 提示:若需确认数据是否真正发出,可以监听bytesWritten(qint64 bytes)信号。


第六步:别忘了善后——错误处理与资源释放

你以为打开就完事了?错。真正的健壮程序必须考虑异常情况。

最常见的问题是:用户拔掉了USB转串口线

此时如果不处理,再调用write()就会导致崩溃。

解决方案是绑定errorOccurred信号:

connect(&serial, &QSerialPort::errorOccurred, [&](QSerialPort::SerialPortError error) { if (error == QSerialPort::ResourceError) { // 通常是设备断开 qCritical() << "🔴 设备已断开:" << serial.errorString(); serial.close(); // 自动关闭,防止后续操作 QMessageBox::warning(this, "警告", "设备已断开,请重新连接"); } });

另外,在程序退出或切换端口前,记得手动关闭:

if (serial.isOpen()) { serial.close(); }

否则下次可能无法再次打开同一个端口。


实战技巧:那些没人告诉你但必须知道的事

🛑 问题1:明明发了数据,对方却没反应?

先自查以下几点:

  • TX 和 RX 是否接反?记住:你的TX要连对方的RX
  • GND有没有共地?没有地线,信号就没参考电平
  • 波特率是否一致?建议两端都固定为 115200
  • 单片机有没有启用串口外设和中断?

可以用串口助手工具反向测试:PC 发 → 单片机收 → 单片机回显 → PC 接收验证


💬 问题2:中文显示乱码?

这是编码问题的经典表现。

解决方法是在接收时明确指定 UTF-8 解码:

QString str = QString::fromUtf8(data);

而不是默认的QString(data),后者按 Latin-1 处理,中文必乱。


💥 问题3:程序崩溃或无法重连?

常见原因是对象生命周期管理不当。

比如你在一个函数里声明了QSerialPort serial;,然后绑定了信号槽。但函数结束时栈对象析构,串口也被关闭,但信号还在试图调用已销毁的对象——直接段错误。

✅ 正确做法:

  • QSerialPort成员变量放在类中(heap or stack 不重要,作用域要够长)
  • 或使用new QSerialPort(this)让父对象自动管理

架构设计建议:做一个专业级串口助手

如果你想把这个小demo升级成实用工具,这里有几个值得加的功能:

功能实现思路
ASCII / Hex 显示切换接收时判断模式,用data.toHex(' ')转换
自动换行刷新定时合并碎片数据,避免每字节刷一次UI
发送历史记录QCompleter+QSettings保存最近输入
日志保存将收发内容写入.log文件
自动重连检测断开后启动 QTimer 延时尝试 reconnect

甚至可以进一步集成 Modbus RTU 解析器,变成工控级 HMI 工具。


总结:你已经掌握了通往硬件世界的钥匙

看到这里,你应该已经明白:

  • 如何发现可用串口
  • 如何正确配置参数并打开连接
  • 如何异步接收数据而不卡界面
  • 如何发送文本和二进制指令
  • 如何处理断开、权限等常见异常

这些技能组合起来,足以让你写出第一个真正有用的上位机程序。

无论是用来调试自己的开发板,还是帮同事读取仪器数据,这套方案都经得起实战考验。

更重要的是,你现在已经站在了一个交叉点上:
前端界面 + 后端通信 + 硬件交互

而这正是现代嵌入式软件工程师的核心竞争力。


下一步你想做什么?

  • 加个定时发送按钮?
  • 把收到的数据画成曲线图?
  • 实现 CRC 校验自动计算?
  • 支持 Modbus 协议解析?

都可以。因为现在,你已经有了最坚实的基础。

如果你正在尝试实现某个具体功能,欢迎留言交流。我们一起把想法变成现实。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/3/10 10:47:57

iwck输入防护工具终极指南:高效屏蔽键盘鼠标的完整解决方案

iwck输入防护工具终极指南&#xff1a;高效屏蔽键盘鼠标的完整解决方案 【免费下载链接】I-wanna-clean-keyboard Block the keyboard input while you were eating instant noodles on your laptop keyboard. 项目地址: https://gitcode.com/gh_mirrors/iw/I-wanna-clean-ke…

作者头像 李华
网站建设 2026/3/11 18:46:12

MusicFree歌单迁移:跨平台音乐收藏转移的终极解决方案

MusicFree歌单迁移&#xff1a;跨平台音乐收藏转移的终极解决方案 【免费下载链接】MusicFree 插件化、定制化、无广告的免费音乐播放器 项目地址: https://gitcode.com/GitHub_Trending/mu/MusicFree 你是否曾经因为音乐平台版权变更而被迫放弃精心收藏的歌单&#xff…

作者头像 李华
网站建设 2026/3/5 19:11:31

CheatEngine-DMA内存分析工具完整使用指南

CheatEngine-DMA内存分析工具完整使用指南 【免费下载链接】CheatEngine-DMA Cheat Engine Plugin for DMA users 项目地址: https://gitcode.com/gh_mirrors/ch/CheatEngine-DMA CheatEngine-DMA是一款专为直接内存访问技术设计的强大Cheat Engine插件&#xff0c;让您…

作者头像 李华
网站建设 2026/3/9 9:41:40

Inter字体破解数字阅读困局:3大核心技术革新用户体验

Inter字体破解数字阅读困局&#xff1a;3大核心技术革新用户体验 【免费下载链接】inter The Inter font family 项目地址: https://gitcode.com/gh_mirrors/in/inter 在信息爆炸的数字时代&#xff0c;我们每天都要面对海量的屏幕阅读任务。然而&#xff0c;传统的字体…

作者头像 李华
网站建设 2026/3/3 23:44:19

Qwen3-VL性能优化:推理速度提升5倍方案

Qwen3-VL性能优化&#xff1a;推理速度提升5倍方案 1. 背景与挑战&#xff1a;Qwen3-VL-WEBUI的部署瓶颈 随着多模态大模型在视觉理解、图文生成和代理交互等场景中的广泛应用&#xff0c;Qwen3-VL作为阿里云最新推出的视觉-语言模型&#xff0c;在功能上实现了全面跃迁。其支…

作者头像 李华
网站建设 2026/3/4 7:40:14

RevokeMsgPatcher:你的聊天消息保险箱,让撤回功能彻底失效

RevokeMsgPatcher&#xff1a;你的聊天消息保险箱&#xff0c;让撤回功能彻底失效 【免费下载链接】RevokeMsgPatcher :trollface: A hex editor for WeChat/QQ/TIM - PC版微信/QQ/TIM防撤回补丁&#xff08;我已经看到了&#xff0c;撤回也没用了&#xff09; 项目地址: htt…

作者头像 李华