拓冰建站拓冰建站
首页 / 资讯中心 / 正文

用Qt从零开发串口助手:通信、FFT频谱与exe打包实战

简介这款Qt串口助手以可执行程序形式发布专为嵌入式开发者、硬件工程师和电子爱好者打造满足日常串口调试、参数配置、数据收发与通信测试需求也适合有一定编程基础的读者直接使用或二次扩展。压缩包内共五十一个文件整体大小约二十三点五三兆其中包含二十四个动态链接库、二十二个界面翻译文件、两个可执行程序以及配套的源码与编译中间文件。动态链接库与主程序配套完整解压后即可运行翻译文件覆盖多语言界面源码与目标文件则有助于了解Qt资源加载与编译过程。目前已有一百三十人次学习下载既可作为即用型调试助手也可以当作学习Qt串口通信和程序发布的参考案例。通过该程序读者能快速上手串口通信模块的调用方式理清依赖库的组织结构与发布目录的常见分工对后续构建自己的串口工具或入门学习都有帮助。 做串口调试这一行的朋友电脑里应该都躺过好几个串口调试助手。但每次遇到新设备、新协议或者要复现一个偶发的通信bug总会觉得现成工具差那么一点意思——界面固定死板、没法定制收发协议更别说把收到的数据直接画成频谱图了。所以我花了两个晚上用Qt从零写了一个串口助手exe程序把串口通信、数据解析、时域波形显示甚至FFT频域分析都塞了进去再用windeployqt打包成绿色版的exe发给同事彻底告别“借别人的工具还得将就”的日子。这篇文章就是把我实际开发、打包、踩坑的过程完整记录下来。我不会把每个函数都贴出来那是给生成器干的事但核心设计思路、QSerialPort的配置细节、QCustomPlot怎么做时域转频域、打包exe时遇到的问题排查方法这些都会展开讲。如果你是刚接触Qt串口编程的嵌入式工程师或者想把自己的小工具打包分发出去又老碰壁这篇应该能帮你省下不少时间。1. 为什么要自己写串口助手工具选型与整体设计1.1 现成工具够用为什么还要自己开发市面上现成的串口助手其实不少SSCOM、XCOM、SecureCRT这些我都用过日常收发个数据、调个AT指令完全没问题。但真到自己搞设备调试或者做产测工具的时候痛点就很明显了协议定制功能太弱比如要按Modbus RTU格式组帧、要自动附加CRC校验很多工具要么不支持要么脚本功能极其难用数据处理和可视化能力约等于零收到的波形数据想直接看曲线、看频谱还得拷贝到别的软件里界面和交互逻辑是人家定死的没法根据自己习惯调整也没法集成到自动化测试流程里。更重要的是大部分现成工具都不是绿色免安装、可配置的部署到产线或客户现场时很麻烦。所以自己开发一个串口助手exe图的不是“能用”而是“顺手”。1.2 功能需求梳理与技术路线做之前先盘了一下需求。核心功能必须有串口参数配置波特率、数据位、停止位、校验位、收发数据显示ASCII/HEX切换、定时发送、文件发送、清空、统计收发字节数。这些是串口助手的标配一个都不能少。扩展功能我加了两块一是接收数据的时域波形滚动显示二是对采集到的波形做FFT变换直接切到频域看频谱。这个在调试传感器、音频模块、电机驱动器时特别好用。技术路线用的是Qt Widgets加QSerialPort模块编译环境是Qt 5.15.2加MSVC2019 64位绘图用的是QCustomPlot配合KissFFT做时域转频域。这里有个非常重要的选型考量编译器尽量选MSVC而不是MinGW后面打包分发会省很多事具体原因我在第4章详细说。2. 串口通信核心功能实现协议、收发与界面2.1 QSerialPort使用要点从枚举到参数配置QSerialPort这个类封装得相当干净但有几个细节新手容易踩坑。第一个是串口枚举直接用QSerialPortInfo::availablePorts()遍历就行但要注意它返回的是QListQSerialPortInfo每个元素里有portName()COM口号、description()设备描述、manufacturer()厂商和serialNumber()。生产环境里最好把description和manufacturer也一起显示出来不要只显示COM号。我遇到过这种情况设备管理器里能看到CH340或FTDI的USB转串口但Qt程序枚举不到。这通常不是Qt的问题而是驱动没装好或者设备被其他程序占用。所以我做下拉框时除了刷新按钮还在状态栏里把枚举结果和setPortName()后的错误信息都打出来了。打开串口的参数配置直接上代码serialPort-setPortName(ui-comCom-currentText()); serialPort-setBaudRate(ui-comBaud-currentText().toInt()); serialPort-setDataBits(QSerialPort::Data8); serialPort-setStopBits(QSerialPort::OneStop); serialPort-setParity(QSerialPort::NoParity); serialPort-setFlowControl(QSerialPort::NoFlowControl); if (!serialPort-open(QIODevice::ReadWrite)) { QMessageBox::critical(this, 错误, 串口打开失败 serialPort-errorString()); return; }需要重点提醒的是关闭串口前务必确认读写操作已经结束。我之前就遇到过一个用户反馈程序退出后串口在系统里还被占用了几秒钟别的工具打不开。后来定位到是析构函数里没有显式调用close()而是依赖QSerialPort析构自动关闭这在极端情况下会延迟释放。现在我的代码里无论是退出还是切换串口都会先disconnect()信号再close()。2.2 收发数据完整性的处理粘包、半包和缓冲用QSerialPort接收数据时最直观的方式是监听readyRead()信号然后调用readAll()。但直接这样做会有一个经典问题粘包和半包。串口底层是按字节流到达的驱动和操作系统会做缓冲应用层每次readyRead()拿到的并不一定是一帧完整的数据可能是半帧也可能攒了好几帧。具体的处理思路要分情况。如果只是做一个简单的调试助手数据直接追加显示没问题但要按帧处理就得引入帧同步机制。我在工程里加了一个接收缓冲QByteArray m_rxBuffer每次readyRead后先append进去然后再从缓冲里按帧格式解析。帧格式一般是固定的比如Modbus RTU的帧头0x3A、地址码、功能码这些解析完一帧就从缓冲里移除对应的字节。对于高速串口还有个容易忽略的坑UI卡顿。如果波特率是921600一秒能收将近90KB数据如果每收到一次readyRead都去更新一次TextEdit界面绝对卡死。我这里的方案是定时刷新用QTimer每隔100毫秒把缓冲中的数据统一追加到显示区同时把“接收字节数”的统计值在状态栏更新。这样即便数据量大界面也不会明显掉帧。发送端同样有讲究。比如定时发送我用的是一个独立的QTimer最小间隔10毫秒不能低于这个值否则Windows系统定时器精度不够时间误差会很大。发送HEX格式时需要把“AA BB CC”这种带空格的字符串转成字节数组转换要严格校验非法字符防止用户输了个“GG”导致程序解析崩溃。2.3 界面设计里那些容易被忽略的细节界面布局不用太花哨但要符合调试习惯。左上角是串口参数区波特率下拉框加一个“自定义”选项方便输入非标波特率比如7.3728M这种少见值。接收区支持HEX和ASCII切换这个用QPlainTextEdit的只读模式就够了。接收显示支持暂停滚动用户正在翻历史数据时可以勾选暂停否则源源不断的输出会让人抓狂。状态栏放收发字节计数器和最近一条错误信息。这个“最近一条错误信息”很重要很多人调串口时遇到Resource error这种提示一脸懵其实在代码里加上connect(serialPort, QSerialPort::errorOccurred, ...)并在状态栏显示什么问题都一目了然。同时建议给波特率、数据位、停止位这些参数加上配置持久化也就是程序退出前写到QSettings里下次启动自动加载。这个功能看似不起眼实际工作中真的能省不少操作时间。3. 数据可视化扩展用QCustomPlot把时域信号转成频域波形3.1 时域波形窗口的实时滚动显示只显示串口收来的ASCII码和HEX终究不够直观。调试音频模块、振动传感器这类设备时时域波形非常关键。我引入了QCustomPlot做波形显示把串口收到的数据按“每一帧取一个数值点”的方式追加到一个环形缓冲区中然后实时重绘。QCustomPlot的集成很直接把qcustomplot.h和qcustomplot.cpp两个文件拷到工程里pro文件里加一行SOURCES qcustomplot.cpp。然后初始化一个QCustomPlot对象设置x轴为时间或点数y轴为幅值。实时刷新时不要直接清空全部数据再重绘那样CPU占用会飙升。正确的做法是用graph(0)-setData()传入最新的整段数据然后调用replot()。这里我踩过一个坑如果每秒刷新三十次以上replot()太重了CPU占用接近满核。后来改成只在采集到新数据时才触发重绘且重绘间隔限制在30fps以内CPU占用瞬间降到10%以下。3.2 FFT与KissFFT的集成从时域到频域的关键转换频域分析是另一个让我觉得“这工具没白写”的功能。嵌入式行业的大部分数据都是时域采集的比如振动波形、电流波形但很多故障特征比如50Hz工频干扰、某个频率的谐振点在时域里几乎看不出来必须在频域里才明显。Qt本身没有现成的FFT接口网上有人推FFTW功能强大但体积太大了打包出来的exe会多出好几兆。我最后选的是KissFFT它非常轻量只由几个.c文件组成直接加到工程里编译就行。核心调用流程如下初始化kiss_fft_alloc(nfft, 0, NULL, NULL)第一个参数是FFT点数一般取采样点数的2次幂比如1024、2048。填充输入把时域数据按实部、虚部排列成kiss_fft_cpx结构体数组虚部填0。执行变换kiss_fft(cfg, fin, fout)得到频域复数结果。计算幅度谱对每个频率点的实部和虚部求模即sqrt(re*re im*im)。把频率轴映射出来频率分辨率 采样率 / FFT点数。比如采样率是2000Hz做了1024点FFT那频率分辨率就是2000/1024≈1.95Hz也就是说频谱图上相邻每个点代表的频率间隔约1.95Hz。这里必须提醒一个重要的前提采样率必须满足奈奎斯特定理也就是采样率至少是信号最高频率的两倍否则频谱图会出现混叠。串口助手里没有硬件做带限滤波所以只能靠用户自己理解当前信号的频率成分否则看到的高频分量可能是假的。3.3 QCustomPlot在Release模式下崩溃的排查用QCustomPlot做FFT频谱图时我遇到一个极其诡异的问题Debug模式下一切正常一编译成Release版点击“频域显示”按钮程序直接崩溃甚至没有报错信息。排查了很久最后锁定在两个地方一个是KissFFT的缓冲区分配。我原来在堆上new了一个kiss_fft_cpx数组但Release模式下优化开了之后可能因为某些未初始化内存的问题导致读取越界。改成用std::vector管理内存后问题消失。另一个是QCustomPlot的刷新线程冲突。我在一个后台线程里调用了customPlot-graph(0)-setData()而Qt的UI操作是线程不安全的。Debug运气好没炸Release一开优化就露馅。把所有QCustomPlot的重绘都挪回主线程就稳定了。4. exe打包发布与部署windeployqt、驱动与图标4.1 用windeployqt把依赖项一网打尽写好了串口助手发给自己用很简单但发给同事、客户就得打包成干净利落的exe。Qt的发布最有名的一个坑就是在自己的开发机上双击运行好好的exe拷到同是Windows的别的电脑上双击毫无反应或者弹窗报错“无法定位程序输入点”再或者就是那句大名鼎鼎的“no qt platform plugin could be initialized”。其实原因就是依赖的动态链接库没带全。Qt项目编译完的exe只会在开发机上借助系统环境变量找到Qt的DLL别人电脑上可没有这些环境变量。解决办法是使用官方自带的windeployqt工具。我通常是这样操作的cd build-YourProject-Desktop_Qt_5_15_2_MSVC2019_64bit-Release windeployqt --release --no-system-d3d-compiler YourProject.exe跑完之后exe所在的目录会多出一堆东西包括platforms文件夹里面最重要的是qwindows.dll、styles文件夹、一堆Qt5*.dll还有一个translations文件夹。这些缺一不可。特别是platforms/qwindows.dll如果你只拷exe和Qt5Core.dll、Qt5Gui.dll少了qwindows.dll程序启动的时候就会报“could not be initialized”这个错。很多新手图省事想手动一股脑把Qt安装目录的bin文件夹全拷过去这绝对不可取。正确的做法是只用windeployqt生成的最小文件集然后整个目录一起分发。这里顺便说说为什么前面强调用MSVC而不是MinGW。如果你用MinGW编译windeployqt也能用但打包出来的目录里会带着libgcc_s_seh-1.dll、libstdc-6.dll、libwinpthread-1.dll这一堆MinGW运行时库目标机器没装这些库也跑不起来。MSVC方案就好在Windows几乎所有机器都自带VC运行库或通过系统更新很常见分发时的运行环境复杂度和潜在故障率都会小很多。4.2 “no qt platform plugin could be initialized”问题解析这个报错我应该已经收到过不下十次了“我打包好的exe发给我同事他打开直接弹窗说Windows no qt platform plugin could be initialized reinstalling the application。”产生这个问题的原因基本排除了代码逻辑就是运行时找不到平台插件。具体情况有两种一种是你用windeployqt部署后不小心把platforms文件夹删了另一种是程序在加载插件时因为有其他Qt版本的环境变量干扰加载了不兼容的插件。解决办法除了确认platforms/qwindows.dll存在外还有个高级技巧在程序入口的main函数里把QLibraryInfo::location(QLibraryInfo::PluginsPath)输出的路径打印出来对比一下和exe目录是否一致。如果不一致可以在main函数最开头调用QCoreApplication::addLibraryPath(QCoreApplication::applicationDirPath() /plugins)把插件路径强制指定到本地。对于“exe改了图标但显示还是默认图标”的问题方法有两种一种是在代码里调用setWindowIcon这只改了运行时的窗口图标另外生成的exe文件图标可以用RC文件的方式在pro文件里加入或借助资源文件进行编译。我更喜欢RC文件方式简单直接。4.3 串口驱动的分发细节CH340、CH341、FTDI自己开发串口助手exe的用户多半是在和USB转串口设备打交道。市面上最常用的USB转串口芯片是CH340、CH341和FTDI系列。如果目标客户电脑没装对应驱动打开串口一定会失败。我处理的方案是在程序点击“打开串口”失败时会先检查枚举出来的串口设备描述如果是CH340芯片则提示“检测到CH340设备但可能缺少驱动请先安装CH340驱动”并把官方驱动下载地址写到界面上。不要试图在安装包里捆绑驱动并静默安装驱动安装涉及系统底层经常被杀毒软件拦截把官方下载指引做进界面是风险最小、成功率最高的方式。另外一提如果用到的是USB转串口设备拔插USB后COM口号往往变了这在自动化工装测试里特别头疼。程序侧可以记录当前USB设备的VID/PID拔插后重新枚举自动匹配设备描述进入对应的新COM口号。这个功能不复杂但对产测人员来说非常省心。5. 实际排查经验与实用技巧5.1 串口关闭失败、数据丢失、端口占用开发和使用过程中遇到过几个典型问题我直接整理成一个速查表方便你遇到对应情况能快速定位现象原因排查方向与解决打开串口提示“Access denied”或“占用”串口被其他程序占用用任务管理器关闭上一次异常退出的调试助手或查一下是否有监控软件占用串口串口能打开但收不到数据波特率/校验位配错或接线问题先用串口助手自发自收短接TX/RX排除外部硬件问题再用示波器量TXD波形接收数据出现乱码波特率不匹配或USB转串口芯片质量问题检查设备管理器串口参数更换高品质USB转串口线FTDI芯片的可靠性显著高于某些寨版CH340高波特率下丢数接收缓冲区溢出或UI刷新不及时用定时刷新UI、加大QSerialPort缓冲、必要时使用线程接收exe在其他电脑打不开/闪退缺失Qt运行库或VC运行库用windeployqt重新部署MSVC编译时注意VC运行库如为静态编译则考虑版权与体积问题Linux下串口权限不足用户无串口设备读写权限将用户加入dialout组或使用udev规则分配权限5.2 防丢数的底层机制接收线程与事件循环丢数这个问题值得多写几句。之前接手过一个项目客户反馈用串口助手接收蓝牙模块发来的大量数据丢包率接近30%。刚开始我以为是波特率太高驱动扛不住查了半天才发现问题出在代码在主线程里用readyRead直接处理数据而UI刷新占用了大量事件循环时间缓冲区里的数据被新数据覆盖。解决办法是标准的“生产者-消费者”模式用一个QThread专门接收串口数据readyRead信号连接到一个槽函数里面只把数据append进线程安全队列不做任何UI操作。主线程用QTimer定时从队列取数据去刷新显示区。队列本身可以用QMutex保护住或者直接上QQueue加锁。这样实测921600波特率、数据量最大时丢包率直接归零。如果你只是自己调试用不搞这么复杂也行但只要是要做产测、做长时间可靠性测试这个线程模型一定要尽早设计进去。还有一个容易忽略的点串口关闭的时序。程序退出时如果还有数据在飞close()后被挂起的数据会丢失。我习惯在关闭串口前主动waitForBytesWritten并延时一小段时间比如10ms给底层驱动一点时间把数据送完。5.3 串口助手还能怎么扩展当然写到这里肯定有朋友会问这篇文章里提到的核心技术是不是就只能用在串口助手其实不是。QSerialPort加QCustomPlot的组合完全可以用于各类数据采集上位机比如CAN转串口调试、GPS/NMEA协议解析显示轨迹、传感器标定工具等。FFT频谱分析功能也完全可以复用在音频采集、振动分析、电网谐波分析等场景中。以我个人的实际体会来说自己动手做一个Qt串口助手exe的过程收获最大的其实不是那几百行代码而是对整个工具链的理解——从串口通信的时序到底层驱动再到UI刷新与线程模型最后到exe打包与部署。这一整套链条下来你再回去用现成的串口调试工具哪怕人家界面再简陋你也能猜到它背后大概经历了哪些步骤、哪些地方可能出问题。最后再分享一个小技巧发布exe前记得在开发机上用打包出的exe目录整体拷贝一份到虚拟机Win10/Win11都装一个里做一次“洁净环境测试”。因为开发机上Qt环境变量、VC运行库都齐很难暴露问题虚拟机里啥都没有一跑就能看出哪些DLL漏了、哪些驱动缺了。用这个办法前后帮我堵住了至少一半的发布问题强烈推荐。本文还有配套的精品资源点击获取
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门