☰
QT串口通讯上位机入门教程:从零实现串口收发与避坑指南
2026/10/19 10:55:19 网站建设 项目流程

简介:面向Qt初学者的串口通讯入门例程,以QSerialPort库串联全流程,结合虚拟串口与串口调试助手,帮助嵌入式、物联网及上位机开发方向的学习者零基础掌握串口参数配置、数据收发、错误处理等核心操作。压缩包共11个文件,整体约5.51MB,包含完整Qt工程源码(.cpp、.h、.pro、.ui)、编译好的可执行程序、XCOM V2.0串口调试助手以及vspd虚拟串口工具,无需物理串口即可模拟联调,特别适合缺乏硬件的学习环境。源码演示了使用open()打开串口、setBaudRate()设置波特率、write()发送数据、readAll()接收数据等关键API用法,注释清晰、逻辑简洁,便于对照调试助手实时观察收发明细、快速定位问题。已有380人学习下载,适合零基础开发者作为第一个串口通讯练手项目,在掌握基础后通过扩展多线程读写与数据解析,可进一步衔接实际项目开发。

1. 先搞清楚这件事:QT串口通讯到底是什么

很多刚接触硬件的朋友都有过这样的经历:手里拿着一块开发板或者一个传感器,数据已经通过串口往外发了,但电脑这边就是不知道怎么把数据接住、显示出来。这时候你就需要一个“上位机”——一个跑在电脑上的程序,负责和硬件设备对话。串口通讯就是这场对话里最传统、也最常用的一条通道,几乎所有入门级的开发板和工控设备,第一步要打通的就是它。

QT正是用来造这种上位机的得力工具。你可以把它理解成一套帮你搭窗口、管按钮、处理数据的巨型工具箱,通讯部分的活交给它现成的串口类,界面部分的活交给它的拖拽式设计器。这篇教程面向完全零基础的你,不讲底层原理,只讲清楚QT串口通讯到底是怎么回事,然后带你装好环境、写出第一个真正能收发数据的程序。学完之后你会发现,这件事远没有想象中那么难。

2. 先搞懂一个关键问题:为什么上位机入门首选QT

很多新手一看到“QT串口通讯”这六个字,先被两个问题吓住:QT到底是个什么东西?串口通讯又是什么?其实这两个问题都可以用很通俗的方式讲清楚。先说结论:QT是目前做上位机最顺手的选择之一,它帮你把最麻烦的底层细节都包了起来,让你能专心写业务逻辑,而不是去面对一堆晦涩的系统API。

2.1 先澄清一个误会:QT不是一门开发语言

我先纠正一个非常常见的错误认知。你在搜索框里输入“qt 开发语言”,好像QT是一门编程语言,但其实不是。QT的真名是Qt,通常指的是一套用C++写出来的开发框架,你可以把它理解成“造房子用的成套模板”,而C++才是真正的“砖头”。你用C++写核心逻辑,QT在旁边提供窗口、按钮、输入框这些现成的零件,以及一种叫“信号槽”的机制,让组件之间能自动通信。

既然QT不是语言,为什么大家都说“用QT开发”?因为QT把界面设计和底层调用都封装好了,你只要按模板把控件拖到窗口上,再写少量的C++代码把它们串起来,一个带图形界面的程序就能跑起来。我第一次接触QT时最大的感受是:这不是什么高深玄学,而是一个给你装满了工具的工具箱,你不需要自己造锤子,只需要学会怎么用锤子。

零基础能不能学?能。如果你会一点C语言,学起来会轻松不少;如果完全没写过代码,也先别退缩——串口通讯里真正需要你写的代码量很少,更多的时间是花在理解“程序怎么和设备对上话”这件事上。等你跑通第一个例子,自然就有了继续往下学的基础。

2.2 QT做串口上位机,到底赢在哪几个点

你可能会问:上位机难道不能用别的语言写吗?当然可以,Python、C#都能写,但QT在串口通讯这个场景下有四个很实际的优势。

第一,QT自带了现成的串口类QSerialPort。你不需要学习Windows底层那套复杂的串口API,也不需要去翻一堆系统文档。打开串口、配置波特率、收发数据,这些操作在QT里都对应着简洁的类方法,几行代码就能完成。第二,QT的“信号槽”机制特别适合串口这种被动接收数据的场景。串口不像网络那样有明确的服务端和客户端,数据随时可能从设备那边传过来,信号槽可以做到“数据一到,你的处理函数自动被触发”,完全不用自己写死循环轮询。

第三,界面设计是可视化的。QT提供了一套拖拽式的界面设计器,你像画图一样把按钮、输入框、文本区摆到窗口上,程序运行时它们就会原样显示出来。新手最大的障碍往往不是代码,而是不知道界面从哪下手,QT把这个门槛降得很低。第四,QT是跨平台的。你在Windows上写好的串口程序,以后需要搬到Linux或者别的环境下运行时,只需要重新编译一遍,代码几乎不用改。对于可能会接触多种系统的学习者来说,这一点能省下不少事。

串口通讯本身也没有听上去那么神秘。它本质上就是两台设备之间约定好用同一种节奏一个字节一个字节地传数据,而这个“节奏”由四个参数决定:波特率、数据位、停止位、校验位。在QT里,配置这四个参数不过就是setBaudRate、setDataBits、setStopBits、setParity四行代码的事。把思路理成这样,后面的学习就会顺畅很多。

3. 动手第一步:装好环境,让第一个窗口跑起来

理解了QT是什么之后,接下来要做的事非常明确:把开发环境装好,跑起来一个最简单的窗口。这一步完全不用考虑串口,先把地基打好。很多新手上来就想直接写串口代码,结果窗口都没弹出来过,处处碰壁。我建议先安下心把环境走通,这会省掉后面很多自我怀疑的时间。

3.1 下载QT,零基础该选哪个版本

去QT的官网下载页面,选择开源版本即可。“开源版本”对应的英文是Open Source,选它不需要付费,注册一个邮箱账号就能下载。这里提醒一句:下载过程可能会让你选择登录,这是正常流程,填个邮箱注册一下就行,不是什么复杂的操作。

安装的时候新手最容易犯的错是“不知道勾选什么”。组件选择界面会列出好几个大项,不要贪多全选,你只需要关注一个带“MinGW”字样的编译器组件,以及Qt Creator这个开发环境,加起来几GB左右,装完就够用了。如果你看到其他看不懂的组件,暂时可以不管,它们大多是扩展支持,以后用到再说。安装路径尽量不要带中文和空格,这一步看着不起眼,但能帮你避开一些莫名其妙的编译问题。

安装完成后启动Qt Creator,你会看到一个欢迎界面。这时候不着急新建工程,先确认自己能看到左侧有“新建项目”之类的入口即可。环境的安装第一次往往比较慢,等它彻底跑完再开始下一步,不要在安装到一半时强行关闭,否则后面会碰到一堆残缺文件带来的怪问题。

3.2 新建一个串口工程,先不碰代码

打开Qt Creator之后,选择“New Project”,然后在模板列表里选择“Application”分类下的“Qt Widgets Application”。这个模板会生成一个带窗口界面的标准桌面程序,正好是我们需要的。

接下来会有几个选项:项目名称、保存路径、构建系统等。项目名称可以叫SerialDemo,路径选个自己容易找到的位置。构建系统新手不用纠结,留在默认即可。类名方面,类名可以叫MainWindow,基类选择QMainWindow,这样生成的窗口自带菜单栏和状态栏的位置,后续扩展方便。之所以不选QDialog,是因为对话框的交互模式不太适合做设备通讯这种长时间运行的程序。

创建完成后,Qt Creator会自动生成几个文件,其中最关键的是三个:main.cpp是程序入口,mainwindow.cpp和mainwindow.h是主窗口的逻辑代码,mainwindow.ui是界面设计文件。你暂时不需要读懂每一行,只要知道它们各自的分工就行。这时直接点击左下角的绿色运行按钮,如果一切正常,屏幕上会弹出一个空白窗口,标题是MainWindow。看到这个窗口,你的环境就算正式跑通了。

3.3 如果窗口没弹出来,先检查这三样

窗口跑不起来时,大多数问题集中在三个地方。第一,编译器组件没装好。你可以到“选项”里的构建套件页面看,确认已识别的编译器里出现MinGW相关的条目。找不到的话,说明安装时组件没勾全,需要重新运行安装器补上。第二,编译报错提示找不到某个库,很大概率是安装时组件少了,不要试图手动复制文件,直接补装组件最省事。第三,项目路径或用户名包含中文,导致编译过程出错。解决办法就是换个纯英文目录重新建一次工程。

这三个问题占了新手环境报错的大头,排查的时候按顺序来,基本都能定位。环境跑通后,你已经成功走完了“从零到有窗口”的阶段,这比记任何概念都重要。

4. 写第一个串口例程:从界面到收发一条龙

环境就绪后,我们开始正题:做一个能打开串口、发送数据、接收数据的完整小程序。这一章的目标非常具体,就是跑通“选择端口→打开串口→发送内容→收到回应”的整条链路。代码量不大,但每一步都值得亲手敲一遍。

4.1 先搭界面:三个控件搞定基础框架

双击mainwindow.ui打开设计器,你会看到左边是控件面板,中间是画布。我们要往窗口上放的控件其实只需要这几个:两个下拉框、一个输入框、两个按钮、一个文本区。别嫌简陋,这套组合已经能覆盖串口通讯最核心的操作。

控件类型objectName 建议用途
QComboBoxcomboBoxPort选择端口号
QComboBoxcomboBoxBaud选择波特率
QLineEditlineEditSend输入要发送的内容
QPushButtonbtnOpen打开或关闭串口
QPushButtonbtnSend发送数据
QTextEdittextEditReceive显示收到的数据

在右侧属性面板里,把textEditReceive的readOnly属性勾选为true,这样收到的内容就不会被误编辑。波特率下拉框可以预设几个常用值:9600、115200、38400、57600,默认选9600。很多入门设备出厂默认就是9600,后面如果你的设备说明书上写着115200,在程序里也能随时切换。

接着在按钮上右键,选择“转到槽”,会弹出一些信号选项,选clicked()。这个操作会自动在代码里生成一个以on_btnOpen_clicked命名的槽函数,我们之后就把对应逻辑写在这些函数里。这就是QT的便利之处——界面和代码的绑定几乎是自动完成的。

4.2 核心代码:找端口、开串口、收数据

界面搭好之后开始写代码。第一步,在mainwindow.cpp文件的最前面,引入三个头文件,并在构造函数里把系统当前可用的串口号填进下拉框。

#include <QSerialPort> #include <QSerialPortInfo> #include <QMessageBox> // 构造函数里,在 ui->setupUi(this) 之后加下面这段: const auto ports = QSerialPortInfo::availablePorts(); // 遍历系统所有可用串口 for (const QSerialPortInfo &info : ports) { ui->comboBoxPort->addItem(info.portName()); // 把每个串口号加进下拉框 }

这里做的事情很简单:availablePorts()会返回一个列表,里面保存着当前电脑上所有能被系统识别到的串口设备。portName()就是类似“COM3”这样的名字。你不需要手动填串口号,程序启动时它会自己去查。如果这个列表是空的,说明驱动或设备没就绪,后面第五章会专门讲这种坑。

第二步,在刚才生成的on_btnOpen_clicked槽函数里,填写打开串口的核心逻辑。这是整个程序最关键的一段代码。

void MainWindow::on_btnOpen_clicked() { if (serial->isOpen()) { // 如果串口已经打开,就关掉它 serial->close(); ui->btnOpen->setText("打开串口"); return; } serial->setPortName(ui->comboBoxPort->currentText()); // 从下拉框取串口号 serial->setBaudRate(ui->comboBoxBaud->currentText().toInt()); // 波特率 serial->setDataBits(QSerialPort::Data8); // 数据位 8 serial->setStopBits(QSerialPort::OneStop); // 停止位 1 serial->setParity(QSerialPort::NoParity); // 无校验 if (serial->open(QIODevice::ReadWrite)) { // 尝试打开 ui->btnOpen->setText("关闭串口"); connect(serial, &QSerialPort::readyRead, this, &MainWindow::readData); } else { QMessageBox::warning(this, "提示", "串口打开失败,请检查是否被占用"); } }

这段代码里,配置串口参数的四行是全篇的重点。你的设备说明书上怎么写,这些参数就怎么设,两边的数值必须完全一致,否则数据就会变成乱码。QIODevice::ReadWrite表示以“既能读又能写”的方式打开端口,这样同一个串口连接同时支持收发。打开成功后,connect这一行把“串口收到新数据”这个事件和你即将写的接收函数绑在一起,只要设备一发数据,readData就会被自动调用。

第三步是发送和接收。在on_btnSend_clicked里写发送逻辑,另外新建一个readData成员函数处理接收。

void MainWindow::on_btnSend_clicked() { if (!serial->isOpen()) { // 保护判断:没打开串口时点了发送会崩 QMessageBox::warning(this, "提示", "请先打开串口"); return; } QByteArray data = ui->lineEditSend->text().toUtf8(); // 把输入框文字转成字节 serial->write(data); // 发送出去 } void MainWindow::readData() { QByteArray data = serial->readAll(); // 一次性读走所有已到达的数据 if (!data.isEmpty()) { ui->textEditReceive->append(QString::fromUtf8(data)); // 显示到接收区 } }

注意write并不会立刻把数据物理发送出去,它只是交给系统缓冲区,数据量少时表现几乎和实时发送一样,但当我们后面接触大流量数据时,就会明白这里还有缓冲区的讲究,现在先不用深究。接收端用的是readyRead信号加readAll的搭配:信号负责通知你“有数据到了”,readAll负责把数据全部取出来再显示。

到这里,你的第一个QT串口例程已经完整了。运行程序,选好端口,点“打开串口”,然后在发送框里输入内容点击发送,数据就会通过串口发出去。如果另一端有人用另一个工具连接着并能返回数据,接收区就会不断显示内容。对新手来说,这套流程已经足够满足绝大多数调试场景了。

5. 新手避坑指南:我在QT串口上踩过的五个大坑

跑通第一版程序只是起点,真正折磨人的永远是各种“看起来没问题但就是不工作”的时刻。这一章我把自己在QT串口开发里翻车最多的五个坑整理出来,每条按现象、原因、解决办法的顺序写,希望能帮你省掉几天的排查时间。

5.1 端口下拉列表是空的,一个设备都看不到

现象:程序启动后,端口下拉框里什么都没有。这不是代码的问题,因为availablePorts()返回的就是系统当前识别的串口列表,列表为空只能说明系统层面就没发现任何串口设备。原因一般是两种:一是驱动没装好,USB转串口线插上后,系统不知道该把它识别成什么设备;二是设备供电不足或者线材质量有问题,导致设备没正常枚举。

解决办法也很直接:把设备拔下来重新插一次,或者换一个USB口试试。接着打开设备管理器,展开“端口(COM和LPT)”这一项,如果能看到类似“USB-SERIAL CH340”这样的条目,说明设备已被识别;如果显示黄色感叹号,那就是驱动问题,去设备详情里手动更新驱动即可。设备管理器里能看到串口号,但程序里还是空的,那多半是权限问题,换用管理员身份运行Qt Creator再试。

5.2 串口明明存在,但open()返回失败

现象:代码里serial->open返回了false,弹出了“串口打开失败”的提示框。新手最容易忽略的原因是:串口已经被别的软件占用了。比如你在测试时开着一个串口调试助手没有关闭,或者上一次运行的程序没有正常退出,占用了同一个COM口。另一个常见原因是在设备管理器里把同一个端口配置成了重复的COM号,导致系统冲突。

解决办法分两步。第一,检查并关闭所有占用串口的程序,包括你之前打开的调试助手、其他终端软件,甚至是另一个还没关掉的程序实例。第二,在设备管理器里确认没有重复的COM号。如果都查过了还是打不开,再考虑是否是设备本身有问题,可以换一个端口测试。把“每次打开失败都弹提示框”写成习惯,能让你很快知道问题出在哪个环节,而不是默默失败。

5.3 能打开串口,但收不到任何数据

现象:程序正常打开串口,另一端确实在发送数据,但接收区安安静静。这个问题必须按顺序排查,按照我的经验,两个原因占了绝大多数。第一个是串口参数不一致。波特率、数据位、停止位、校验位,四个参数任何一个不匹配,收到的数据都会错乱甚至收不到。第二个是收发线接反了。串口的TXD要接对方的RXD,很多新手把两根线对着接,结果数据根本没进入对方的接收引脚。

交叉验证的办法是最快的:用一根导线把串口模块的TXD和RXD直接短接,然后在自己的程序里发一条数据。如果接收区能显示自己发出去的内容,说明通讯链路本身没问题,问题出在外接设备那一端。如果连自发自收都不行,那就要往驱动和硬件方向找原因了。

5.4 发送中文数据,另一边收到乱码

现象:发英文字符一切正常,一输入中文,接收方显示的内容就变成了一堆不可识别的符号。原因是编码不一致。QT默认使用UTF-8编码,而很多单片机设备或者调试工具默认使用GBK,两边编码对不上,中文自然就变成了乱码。

解决办法是统一编码。如果你的设备说明书里写的是“字符集GBK”,发送时就需要先把中文从UTF-8转成GBK,QT里可以用QTextCodec来完成转换;如果设备支持UTF-8,那就保持默认。另外要注意,发送数据时我用的是toUtf8(),不是toLatin1(),这两者对英文字符没差别,但处理中文字符时天差地别。遇到乱码别慌,先确认两端的字符集再调。

5.5 数据稍微多一点,界面就卡死

现象:程序运行没问题,但一旦另一端连着发很多数据,窗口就变得很卡,甚至直接未响应。原因是接收数据的操作写得太笨重。如果你在readData里对每条数据都做复杂的控件刷新操作,而数据来的速度又很快,UI线程就会被不断占用。

解决办法有三个方向:一是不要在readyRead槽函数里做耗时操作,比如频繁更新大文本、写数据库,尽量只做简单显示。二是需要处理大量数据时,把数据先放进缓冲区,在固定时间间隔里统一刷新界面,而不是每条数据都触发一次重绘。三是对数据量要求极高的场景,把串口收发放到独立线程里,工作线程负责读写,UI线程只负责显示。新手阶段前两条够用了,第三条等你对QT有了更深理解再碰也不迟。

6. 用虚拟串口顺手练起来,再谈下一步学什么

没有真实硬件能不能继续练?能。虚拟串口工具可以帮你在电脑上凭空造出一对互相连通的串口,比如COM3和COM4。程序打开COM3,调试助手打开COM4,两边就能像真实设备一样互相收发数据。这样你不需要任何硬件也能把串口程序完整地调通,整个逻辑和真实硬件几乎完全一样。

6.1 没有硬件也能练:虚拟串口成对造

安装一款虚拟串口工具后,打开它的窗口,添加一对端口,比如添加“COM3”和“COM4”,工具会自动在系统里虚拟出这两个COM口,并且让它们背对背连通。这时打开你的QT程序,选择COM3并打开串口,再打开一个串口调试助手,连接COM4。接下来你在调试助手那边发送的数据会出现在QT程序的接收区里,在QT这边发送的内容同样会在调试助手那边显示出来,原理等同于两台设备之间的真实通讯。

串口调试助手在这个过程里扮演的是“对方设备”的角色。它能让你清楚地看到程序发出的每一字节原始数据,还能切换到十六进制显示,验证数据格式是否正确。很多新手在调试时习惯只盯着程序看,其实调试助手才是发现问题最快的窗口。

调试助手与你的QT程序千万不要同时打开同一个COM口。两个程序同时占用一个端口,必定有一个打开失败,这是新手最容易踩到的操作误区。记住:程序用COM3,调试助手就用COM4,各占一端,互不干扰。

6.2 下一步学什么:从定时发送到协议编程

跑通收发已经算是正式入门,接下来的路可以分三条走。第一条是学会用QTimer定时发送数据,这能帮你模拟周期性上报数据的设备场景。第二条是尝试把接收到的数据显示成曲线,这将涉及QT里图表组件的使用,也会让你对什么叫做“UI刷新”有更具体的体会。第三条是接触Modbus这类工控领域最常见的串口通信协议,学习如何在QSerialPort之上封装请求和响应的报文格式。协议是串口通讯走向实用化的关键一步,但它需要你先把基础收发吃透再学。

写到这里想分享一个个人习惯:我后来每次拿到新的串口设备,都强制自己先打开设备管理器确认当前COM口号,再用调试助手做一次自发自收的测试,确认通道没问题之后才动QT代码。这个小习惯帮我挡下了大量的低级错误,也让我排查问题时有清晰的顺序。希望这个习惯也能帮到你,祝你在QT串口这条路上越走越顺。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询