news 2026/4/23 17:02:08

手把手教程:使用QSerialPort实现简单串口收发

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
手把手教程:使用QSerialPort实现简单串口收发

手把手教你用 QSerialPort 写一个能跑的串口助手

你有没有遇到过这样的场景:手头一块刚焊好的开发板,连上电脑死活没反应;或者调试 GPS 模块时,串口工具一发指令就卡住?别急,今天我们就从零开始,用Qt 的 QSerialPort搭一个真正可用的串口收发程序。不是那种“能编译就行”的玩具项目,而是一个你可以直接拿去改造成自己上位机的小工具。

重点是——全程不讲废话,只说人话,代码能跑,坑我都替你踩过了。


为什么选 QSerialPort?别再用 C 风格 API 了!

你说串口通信不就是打开、读写、关闭吗?Linux 下open()read()write()不香吗?

香是香,但前提是你的程序不需要界面、不用跨平台、也不怕不同系统之间行为不一致。

一旦你要做个带按钮、文本框、能自动刷新端口列表的 GUI 工具,你会发现:

  • Windows 上 COM 口要走 Win32 API
  • Linux 要配 termios 结构体,权限还可能不够
  • macOS 又是另一套设备节点命名规则

这时候你就明白,统一抽象有多重要

而 Qt 的QSerialPort就干了这件事。它把底层这些破事全封装了,你只需要关心:

“我要连哪个口?”、“波特率设多少?”、“数据来了怎么处理?”

剩下的交给 Qt。一套代码,Windows、Linux、macOS 全都能跑。

而且它是标准 Qt 类,支持信号槽、自动内存管理、IDE 断点调试……简直是现代 C++ 开发者的福音。


第一步:先把环境搭起来

先别急着写逻辑,第一步永远是让工程能编译通过。

.pro文件里加上这一行:

QT += serialport

就这么一句。没了。

如果你用的是 Qt Creator,记得重新运行 qmake 或构建一下项目,不然会报undefined reference

头文件也简单:

#include <QSerialPort> #include <QSerialPortInfo>

前者用来操作串口,后者可以帮你枚举当前系统有哪些串口设备可用。

比如你想让用户从下拉菜单选端口,就可以这么做:

foreach (const QSerialPortInfo &info, QSerialPortInfo::availablePorts()) { qDebug() << "发现串口:" << info.portName() << "(" << info.description() << ")"; }

输出可能是:

发现串口: "COM3" ("USB Serial Port")

这样你就知道该连哪个口了。


第二步:参数配不对,通信全白费

串口通信就像两个人打电话,必须约定好“语速”和“暗号”,否则听到的全是乱码。

最常见的配置是这组黄金组合:

115200-8-N-1

翻译成人话就是:

  • 波特率:115200(每秒传这么多比特)
  • 数据位:8 位(一个字节)
  • 校验位:无(现在设备都挺稳,基本不用校验)
  • 停止位:1 位(表示一个字符结束)

代码怎么写?

QSerialPort serial; serial.setPortName("COM3"); // 端口号 serial.setBaudRate(QSerialPort::Baud115200); // 波特率 serial.setDataBits(QSerialPort::Data8); // 8位数据 serial.setParity(QSerialPort::NoParity); // 无校验 serial.setStopBits(QSerialPort::OneStop); // 1位停止 serial.setFlowControl(QSerialPort::NoFlowControl); // 不用流控

注意!所有参数都有对应的枚举值,不要自己填数字。比如Baud115200是 Qt 定义的常量,比你写115200更安全,编译器还能帮你检查拼写错误。


第三步:打开串口,别忘了信号槽

串口打开了就能收发了吗?错。关键在于——你怎么知道有数据来了?

传统做法是开个线程不停read(),累不累?

Qt 的做法优雅得多:事件驱动 + 信号槽

只要调用了open(),串口就会在后台监听数据。一旦收到新内容,立刻触发readyRead()信号。

所以我们这么写:

if (serial.open(QIODevice::ReadWrite)) { qDebug() << "✅ 串口已打开"; connect(&serial, &QSerialPort::readyRead, this, &MainWindow::onDataReceived); connect(&serial, &QSerialPort::errorOccurred, this, &MainWindow::onSerialError); } else { qWarning() << "❌ 打不开串口:" << serial.errorString(); }

这里有两个重点:

  1. readyRead信号会在有数据可读时自动触发,不会阻塞主线程
  2. errorOccurred能捕获断开连接、权限问题等异常,让你及时响应。

⚠️ 坑点提醒:有些新手喜欢在while(1)里循环read(),结果 UI 直接卡死。记住,GUI 程序永远不要做阻塞操作!


第四步:发数据很简单,但有个细节要注意

发送数据用write(),返回值是写入缓冲区的字节数:

void MainWindow::sendData(const QString &text) { QByteArray data = text.toUtf8(); // 推荐 UTF-8 编码 qint64 result = serial.write(data); if (result == -1) { qWarning() << "❌ 发送失败:" << serial.errorString(); } else { qDebug() << "📤 已发送" << result << "字节"; } serial.flush(); // 强制立即发送(可选) }

关于flush()的说明:

  • 大多数情况下不用调,操作系统会自动调度。
  • 如果你对实时性要求极高(比如控制机械臂),可以加一句强制刷出。

另外,如果是发送 HEX 数据(比如0x02 0x03),那就别用QString,直接构造QByteArray

QByteArray hexData; hexData << 0x02 << 0x03 << 0xFF; serial.write(hexData);

第五步:收数据最难的地方——别以为 readAll() 就完事了

很多人以为readAll()一调,数据就完整了。错得很离谱。

真实世界中,串口数据是“流式到达”的。比如你发了一个 JSON 包:

{"temp":25,"hum":60}

可能第一次readyRead收到{\"temp\":25,
第二次才收到"hum\":60}

这就叫粘包/拆包

所以你在onDataReceived里不能直接解析,得缓存起来,等一整帧收完再说:

private slots: void onDataReceived() { QByteArray chunk = serial.readAll(); receiveBuffer.append(chunk); // 累积到缓冲区 // 示例:以换行符为分隔符判断是否接收完成 while (receiveBuffer.contains('\n')) { int index = receiveBuffer.indexOf('\n'); QByteArray frame = receiveBuffer.left(index + 1); receiveBuffer.remove(0, index + 1); processFrame(frame); // 解析这一帧 } }

当然,实际协议可能是:

  • 固定长度帧
  • 带帧头帧尾(如0x7E ... 0x7E
  • 包含长度字段

那你就要根据具体协议来判断什么时候“收完了”。

✅ 秘籍:宁可在readyRead里多调几次readAll(),也不要试图在里面 sleep 或做耗时计算,否则会影响后续数据接收。


第六步:那些没人告诉你但必须知道的事

1. 权限问题(Linux/macOS 常见)

插上 USB 转串口模块,发现打不开/dev/ttyUSB0

大概率是权限不够。

解决方法有两个:

  • 临时方案:sudo chmod 666 /dev/ttyUSB0
  • 永久方案:写 udev 规则,把用户加入 dialout 组

推荐后者:

sudo usermod -aG dialout $USER

然后重启生效。


2. 串口被占用怎么办?

另一个程序(比如串口助手、Arduino IDE)开着,你就打不开。

解决办法:

  • 提示用户先关闭其他程序
  • 在打开前尝试探测是否已被占用

可以用QSerialPort::open()返回值判断。


3. 如何实现自动重连?

设备突然拔掉 USB,下次插回来希望自动连上?

思路是监听errorOccurred中的DeviceNotFoundError,然后启动一个定时器轮询:

void MainWindow::onSerialError(QSerialPort::SerialPortError error) { if (error == QSerialPort::DeviceNotFoundError) { QTimer::singleShot(2000, this, &MainWindow::tryReconnect); } }

当然,别无限重试,加个计数上限更稳妥。


4. 显示模式切换:ASCII 还是 HEX?

调试时经常需要看原始字节流。建议你的界面提供两种显示方式:

  • ASCII:适合看日志、JSON、AT 指令
  • HEX:适合看二进制协议、Modbus、加密数据

转换也很简单:

QString hexStr = receiveData.toHex(' ').toUpper(); // → "02 03 FF"

最后一点建议:别把简单事搞复杂

我见过太多“高级”串口工具,功能一堆,结果连基本收发都不稳定。

刚开始学,记住几个原则:

  1. 先让它通起来:哪怕只有一个输入框、一个输出框、两个按钮(打开/发送)
  2. 参数可配置:波特率、端口名能改
  3. 日志要清晰:打印时间戳、方向(TX/RX)、数据内容
  4. 别迷信多线程QSerialPort本身是非阻塞的,除非特殊需求,根本不需要另开线程

等你把这个最简版本跑通了,再慢慢加功能:

  • 自动识别设备
  • 协议解析面板
  • 数据绘图
  • 日志保存

写在最后

到现在为止,你应该已经掌握了如何用QSerialPort实现一个真正可用的串口通信程序。

它不只是“技术demo”,而是你能拿去实战的基础框架。

无论你是要做温湿度监控上位机、PLC 调试工具,还是无人机地面站,底层通信逻辑都逃不开这几个步骤:

枚举 → 配置 → 打开 → 发送 ← 接收 → 解析

QSerialPort把这套流程变得异常简洁。

下次当你面对一块沉默的开发板时,不要再靠别人的串口工具碰运气了。

自己动手,写一个属于你的通信桥梁。

如果你实现了基础功能,欢迎留言交流你遇到的问题,我可以帮你一起优化架构。

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

Vim代码格式化终极指南:让代码整洁如新的简单方法

Vim代码格式化终极指南&#xff1a;让代码整洁如新的简单方法 【免费下载链接】vim-autoformat 项目地址: https://gitcode.com/gh_mirrors/vim/vim-autoformat 在编程的世界中&#xff0c;整洁的代码不仅美观&#xff0c;更是高效协作的基础。Vim-Autoformat作为一款强…

作者头像 李华
网站建设 2026/4/23 14:35:23

TOAST UI Editor表格合并功能终极指南:从入门到精通

TOAST UI Editor表格合并功能终极指南&#xff1a;从入门到精通 【免费下载链接】tui.editor &#x1f35e;&#x1f4dd; Markdown WYSIWYG Editor. GFM Standard Chart & UML Extensible. 项目地址: https://gitcode.com/gh_mirrors/tu/tui.editor 还在为Markdow…

作者头像 李华
网站建设 2026/4/23 11:34:36

Open-AutoGLM开源争议全解析(20年专家深度解读)

第一章&#xff1a;Open-AutoGLM 开源吗 Open-AutoGLM 是一个备受关注的自动化代码生成与语言理解工具&#xff0c;其是否开源直接影响开发者社区的参与度和技术生态的扩展性。目前&#xff0c;该项目已在主流代码托管平台公开其核心仓库&#xff0c;采用宽松的 MIT 许可证&…

作者头像 李华
网站建设 2026/4/23 11:31:48

智谱Open-AutoGLM如何颠覆AI开发?:5大核心技术亮点全面解读

第一章&#xff1a;智谱Open-AutoGLM方法 智谱AI推出的Open-AutoGLM是一种面向自动化自然语言处理任务的开源框架&#xff0c;旨在通过大语言模型驱动的智能体实现从数据预处理、模型选择到超参数优化的全流程自动化。该方法融合了提示工程、任务分解与自我反馈机制&#xff0c…

作者头像 李华
网站建设 2026/4/23 13:20:19

如何快速上手MediaPipe:5分钟构建智能应用的完整指南

如何快速上手MediaPipe&#xff1a;5分钟构建智能应用的完整指南 【免费下载链接】mediapipe-samples 项目地址: https://gitcode.com/GitHub_Trending/me/mediapipe-samples MediaPipe是谷歌推出的开源机器学习框架&#xff0c;专门为移动设备和边缘计算场景设计&…

作者头像 李华
网站建设 2026/4/23 11:33:10

IP-Adapter-FaceID PlusV2技术解析:解锁AI人脸生成新境界

IP-Adapter-FaceID PlusV2技术解析&#xff1a;解锁AI人脸生成新境界 【免费下载链接】IP-Adapter-FaceID 项目地址: https://ai.gitcode.com/hf_mirrors/h94/IP-Adapter-FaceID 还在为AI生成的人脸"形似神不似"而烦恼吗&#xff1f;IP-Adapter-FaceID PlusV…

作者头像 李华