ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

用VC打造自己的串口调试工具ComTest:从底层通信到协议解析

用VC打造自己的串口调试工具ComTest:从底层通信到协议解析 简介ComTest串口调试工具是基于VC编写的完整串口通信工程面向硬件开发、嵌入式系统调试及物联网设备测试等场景解决RS-232标准下串口通信中数据接收、发送与稳定性检查的实际问题。压缩包为RAR格式共18个文件以h头文件、cpp源文件为主辅以rc资源脚本、ico图标、工程配置文件等包体仅27KB结构紧凑便于直接阅读和二次编译。已有539人学习/下载适合正在学习Windows串口编程或需要快速搭建调试助手的开发者参考。通过该工程读者可以掌握CreateFile打开串口、SetCommState配置波特率与数据位校验位、ReadFile/WriteFile收发数据、ClearCommError状态检查以及CloseHandle释放资源等关键API的调用方式工程还演示了异步事件驱动与错误处理在实际串口调试工具中的落地方式并提供一个可以发送命令、实时查看响应的简易调试界面对理解串口通信协议和VC基础项目组织均有帮助。 手头这块STM32板子串口又吐乱码了我一边在串口助手里翻十六进制报文一边在心里第N次叹气为什么就没有一个工具能让我一边看数据一边动态改波特率还能顺手把当前帧截下来回放后来我实在忍不了干脆自己动手用VC写了一个就是这篇要聊的ComTest串口调试工具。它不是那种追求大而全的商业软件而是从一个嵌入式开发者的真实使用习惯出发把日常调板子最刚需的几个功能做到了顺手。如果你也经常跟串口打交道或者正琢磨着自己封装一个串口通信组件这篇应该能给你一些参考。1. 为什么开发ComTest现成工具总差那么一两个功能1.1 我遇到的串口调试痛点调试嵌入式设备时串口工具基本是每天都要开的。市面上现成的工具我用过很多功能上其实都够用但总有几个点让我特别难受。比如有的工具想改波特率必须重新打开串口有的定时发送只能选几档固定间隔有的接收区在数据量大时明显卡顿还有的对超过9的COM口号支持不友好。单个看都是小问题但调试时反复来回切换就非常消耗耐心。最让我头疼的是协议调试。很多串口工具只会把收到的字节原样显示但对于一帧带帧头、地址、长度、校验的数据我经常要在心里手动比对或者复制到计算器里算校验和。稍微复杂一点的调试场景比如测试一个需要先发命令再等应答的下位机工具不支持脚本联动就只能在发送框里手工敲一次一次试效率很低。1.2 从“凑合用”到“自己写”的决策过程我本来是打算找一个更合适的现成工具后来发现与其去适应工具的脾气不如按照自己的调试习惯写一个。我选VC开发原因很直接底层可以直接调用Win32 API操作串口就是CreateFile、ReadFile那套干净利落不用套一大堆框架MFC的界面开发效率也足够做个调试工具完全够用。项目代号就叫ComTest定位清晰串口通信调试测试工具不追求花哨只求准确、稳定、顺手。ComTest的核心目标我列了四条一是打开串口后所有参数都能动态调整不需要反复开关二是接收数据要稳定不丢、显示不卡三是要能按时间或帧间隔自动拆分数据方便看协议交互过程四是发送要灵活支持文本、Hex、定时、脚本式发送。后面所有功能设计都是围绕这四条展开的。2. ComTest的核心串口通信底层架构与关键实现2.1 串口打开与参数配置注意COM口号大于9的坑串口操作在Windows上本质就是把串口当成文件来读写。核心API是CreateFile但这里有一个特别容易踩坑的细节COM1到COM9可以直接用COM1这种写法打开而COM10及以上的端口必须写成\\.\COM10的格式。很多工具在端口大于9时不显示或者打不开基本都是这个原因。我在ComTest里做了一套端口枚举和打开逻辑用QueryDosDevice遍历系统中真实存在的串口同时兼容两种命名格式这样无论是USB转串口还是PCI扩展卡只要是系统认出来的设备都能在列表里找到。实际调用的代码大概是这样的HANDLE hCom CreateFile( \\\\.\\COM3, // 设备路径 GENERIC_READ | GENERIC_WRITE, // 读写权限 0, // 独占方式打开 NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, // 使用异步IO防止界面卡死 NULL );打开之后就是配置串口参数。DCB结构体里存着波特率、数据位、校验位、停止位这些关键参数。我专门做了一个立即生效按钮修改参数后先GetCommState取出当前配置改完再SetCommState写回去不需要关闭再打开串口。这个功能在调试中特别实用比如先以9600波特率收到一部分数据再切换到115200看另一部分的反应。实测下来切换动作很干净不会丢数据。2.2 超时策略决定上位机能否跟上下位机节奏串口通信里超时设置是个容易被忽视但影响很大的参数。IOCTL_SetCommTimeouts对应的COMMTIMEOUTS结构体里有几个关键字段ReadIntervalTimeout表示两个字节之间的最大间隔时间ReadTotalTimeoutMultiplier和ReadTotalTimeoutConstant配合决定单次读取的总超时WriteTotalTimeoutConstant控制写入超时。我调试时最常用的是间隔超时策略。比如我用50毫秒作为字节间隔阈值如果50毫秒内没有新数据进来就认为一帧数据接收完了。这个值既要大于下位机的字节间间隔又不能让用户等待太久。早期的版本我把间隔设为100毫秒结果在9600波特率下帧间隔和数据间隔差异不明显经常出现拆帧错乱后来根据实际波形调整为50毫秒才稳定。如果说下位机的发送逻辑你改不了那么这个参数就是你唯一能妥协的旋钮值得反复实验。2.3 接收线程与事件驱动不卡界面、不丢数据的关键接收数据如果直接用主界面线程的循环去读一旦数据量大或者界面操作频繁就会互相拖累。我用了一个专门的工作线程做接收配合WaitCommEvent事件通知机制只有在串口有数据到达时才触发读取。读取使用重叠IO超时由系统管理不会block线程。工作线程每读到数据就通过PostMessage把数据指针传给主窗口主界面在消息响应函数里做显示和解析。这里有一个我踩过的坑PostMessage传的是指针如果接收线程在消息还没被处理时就释放了那块内存主界面拿到的是野指针。解决方案是维护一个环形缓冲队列接收线程把数据拷贝到队列里主界面从队列取数据两个线程用临界区保护数据量小的时候丢帧率可以做到几乎为零。实测在2M波特率满负荷接收时界面还能流畅刷新。3. 接收数据展示从字节流到可读报文3.1 字节数组转十六进制字符串一个函数解决显示问题接收区显示是串口工具的门面。原始数据是字节数组展示时一般有Hex和ASCII两种模式。Hex模式的核心就是一个字节转两字符的工具函数。我在ComTest里写得很简单CString ByteToHexString(BYTE data) { static const char HEX[] 0123456789ABCDEF; CString str; str.Format(%c%c , HEX[data 4], HEX[data 0x0F]); return str; }把收到的每一字节转成两位十六进制加一个空格拼接显示就成了常见的Hex视图。这个转换看起来简单但我发现不同实现效率差距很大。如果用CString的Format逐个处理数据一多会明显卡顿。我优化后才意识到串口工具吞数据本质上是上位机处理速度跟不上。我的做法是开一块1MB的缓冲区先统一转成char数组再一次性赋给CString插入显示控件刷新频率控制在每秒20次左右显示性能和实时性平衡得比较好。3.2 帧拆分与时间戳让协议交互一目了然常用串口工具的一个痛点是分不清哪段数据属于同一帧。串口本身是字节流没有帧边界帧的划分完全依赖协议规则。ComTest做了两件提升体验的事一是按时间间隔自动拆分也就是前面提到的间隔超时二是给每帧数据打上时间戳并编号显示。在调试Modbus、自定义协议这种一问一答的场景里这个功能可以直接看出命令和应答的对应关系不用再靠眼睛数字节。另外我加了一个自动解析区域可以通过配置文件自定义帧格式比如第几字节是帧头、哪几位是长度、校验是什么算法。配置好之后收到的每一帧会自动解析出关键字段并高亮显示CRC错误、长度不匹配时直接标红。这个功能我自己用得很爽相当于把一个通用工具变成了私人的协议分析器。3.3 接收日志与保存数据不能只活在屏幕上调试过程中经常需要把串口数据保存下来给同事分析或留档。ComTest的日志功能包含两部分一部分是单向保存把所有接收到的原始数据按时间写入文件支持Hex和二进制两种格式另一部分是带时间戳的交互日志把发送和接收的每条数据都记录下来。日志保存当时有个没想到的问题文件写入如果频繁打开关闭在高速接收下会丢失部分数据。我的解决方案是写日志线程和接收线程分离接收线程只往缓冲区塞数据日志线程每隔几百毫秒批量写入一次文件。在断电或者程序崩溃时最多丢几百毫秒的数据可接受。这也让我意识到串口工具里的所有慢操作都应该和接收主链路解耦这是保证不丢数据的第一原则。4. 发送功能的工程化定时、脚本、自动应答4.1 文本与Hex发送的切换逻辑发送功能的细节决定了调试效率。ComTest支持文本模式和Hex模式切换文本模式下输入的字符按ASCII码直接发送Hex模式下输入的每一对十六进制数转换为一个字节。输入框里做了实时校验Hex模式下不允许输入非十六进制字符防止发出去的数据和预期不一致。这一块的设计有个容易忽略的点文本模式和Hex模式的换行符处理。很多下位机指令以回车换行结尾文本模式下我加了一个发送时追加回车换行的选项而Hex模式下就需要注意手动输入0D 0A。用过一段时间后我索性在Hex模式下也增加了常用控制码的快捷按钮比如发0D 0A、发00、发FF点一下就插入对应字节省了很多手敲的功夫。4.2 定时发送与脚本流程测试重复交互场景的利器定时发送用于压力测试和心跳模拟。ComTest的定时发送间隔可以自由填最小到10毫秒不像有些工具只给几档固定选项。定时间隔我建议不要低于20毫秒因为Windows定时器精度有限设太小的间隔实际达不到反而让用户误以为工具不准确。更进阶的是脚本流程发送。我在ComTest里内置了一个简单的脚本引擎用文本描述指令序列先发一帧握手命令等待3秒再发一帧查询命令收到指定帧头数据后发送下一帧否则重试。脚本本身是个文本文件解析也不复杂就是逐行执行和跳转。这个功能帮我在测试一个OTA升级流程时省了不少时间整个升级流程的交互日志自动跑完异常场景也能通过调整脚本复现。4.3 自动应答与校验计算工具主动帮你想一步很多调试场景里上位机需要模拟一个从设备的应答角色。ComTest支持预设应答规则当收到的数据匹配某一帧特征时自动发送预设的应答帧。这个功能在调试某些只能当主机的设备时特别有用相当于把ComTest变成了一个可编程的从设备模拟器。另外较新版本里我加了CRC16、CRC32、累加和、异或和等几种常用校验的自动计算。在脚本编辑和自动应答配置里都可以用占位符让工具自动计算并填入校验位。比如 会被替换成当前待发送帧的CRC16值。这个设计思路有一个很大的好处不要让用户手动去算校验把计算让给程序既减少出错也提升了调试速度。后来我把帧结构配置、校验计算和自动应答整合到一起就变成了一个轻量级的串口协议仿真器这是ComTest最让我满意的部分。5. 界面与工程化细节老VC项目的现代化打磨5.1 控件布局与字体缩放高分屏下的顽固问题VC6时代的程序在高分屏下经常出现字体模糊、控件错位的问题。ComTest主体是在VC6环境下写的我最初只考虑了普通分辨率的显示后来在Win10的2K屏上使用界面惨不忍睹。解决思路是手动处理DPI感知。程序启动时调用SetProcessDPIAware让系统按实际像素坐标运行而不是缩放后的虚拟坐标界面就清晰了。控件布局方面用OnSize消息动态调整各区域大小实现了窗口大小变化时接收区、发送区、状态栏的比例自适应。处理这种老工程的界面问题我的经验是不指望MFC自动适配窗口固定高度600宽度根据DPI动态计算保证所有控件在主流分辨率下都完整可见。5.2 数据显示区的自绘与高亮接收区不再是一团黑字接收区原本用的是MFC的CEdit控件数据量大了之后渲染性能不够。后来我改成了自绘列表控件每一帧数据占一行不同状态用不同背景色正常接收白色背景、发送的数据浅绿色背景、校验错误红色背景、解析成功的关键字段加粗显示。这样一来满屏数据不再是黑压压一片异常信息一眼就能抓住。自绘控件要用到WM_CTLCOLOR和OnDrawItem这两个消息的处理这正好和VC6下一个经典的需求相关位图局部放大显示。调试图像传输时把收到的图像字节流存成位图文件然后双击数据行就能在预览窗口里看到图像。我专门做了一个局部放大功能鼠标框选图像区域后放大显示专门用来排查图像传输时是否出现花屏、错位、颜色异常等问题。这套功能陪我抓出过好几回摄像头模组的配置错误属于比较实用的附加功能。5.3 消息泵与窗口刷新拖拽时不卡顿的秘密Windows程序在拖拽窗口、弹出菜单时消息循环会进入模态状态这期间主界面不再处理普通消息。如果接收线程还在以很高的频率PostMessage界面消息队列会被塞满等拖拽结束后相当于积压了大量待显示的数据处理起来会突然卡一下。解决方法是限制PostMessage的频率即使接收线程每秒产生了大量消息也只在界面可处理的范围内投递比如每50毫秒最多投递一条“新数据到达”消息。接收数据先存在缓冲区里界面上限刷新频率。经过这个处理窗口拖拽结束的瞬间界面只是补刷最近的数据而不是把积压的数据全部追赶一遍。这种设计上的克制是我在多次实测卡顿后总结出来的比单纯加硬件性能更有效。6. 发布与部署VC运行库、兼容性与“杀软误报”6.1 微软VC运行库为什么你的工具在别人电脑上打不开项目标题里特意提到了“VC”这背后有一个绕不开的问题VC6编译出的程序依赖于MSVCR60.dll这套运行库但新版Windows系统默认不一定带这组库。很多人在给别人发工具后发现对方双击没反应或者弹窗提示“找不到MSVCR60.dll”根源就在这里。我的做法是直接采用静态链接在VC6工程设置里把运行时库改成“Multithreaded (/MT)”这样程序就不再依赖外部的VC运行库。代价是编译产物体积会大一点但换来了拷贝到任何机器都能直接运行。如果你用VS2015以上的环境开发同理应尽量选择静态链接或者使用微软官方的Visual C Redistributable安装包。静态链接的ComTest现在可以放到U盘里在Win7到Win11的64位和32位系统上直接跑不需要装任何环境这个“绿色属性”对调试工具来说非常重要。6.2 杀毒软件误报与自签名自研工具分发的一个现实问题自己写的程序发布到别人电脑上另一个常见问题是杀毒软件报毒。尤其是没有代码签名证书的程序很多安全软件会给出“未知程序”的警告甚至直接拦截。ComTest早期发给同事时就遇到过在几台机器上被直接删除的情况。应对方案有两个方向。一是购买代码签名证书对exe进行签名这是正规商业软件的方案成本不低。二是把工具的校验值和源码仓库地址写在使用说明文档里让使用者自行核对再把可执行文件做成压缩包分发压缩包比单文件被拦截的概率低一些。对于纯个人项目这两个办法结合使用基本能解决问题。另外我在ComTest里提供了关闭自动更新和网络请求的功能程序完全不碰网络从行为上也更容易说服杀毒软件“我是清白的”。6.3 多系统兼容性实测记录我在一系列目标机器上做了验证Win7 32位虚拟机、Win10 64位实机、Win11 64位实机还有一台跑着更老版本Windows的老工控机。实测中主要出现过两个问题一是在Win7下控件字体显示偏旧后来改用系统默认字体加粗处理二是Win11对未经签名驱动的串口设备限制导致部分USB转串口无法识别这个是系统层面的限制和工具本身无关我只能在文档里说明让用户换驱动或调整设备安装设置。这类面向老系统兼容的程序我的经验是在开发环境中就尽量用系统自带的公共控件不要依赖第三方界面库也不要过度使用新的Windows API否则兼容性问题会雪上加霜。ComTest在这几台机器上跑了完整的收发测试稳定性是过关的这也证实了老技术栈加上谨慎的工程决策依然能做出好用的工具。7. 从ComTest延伸出去DLL插件化与自动化协作的思考7.1 用DLL实现协议解析插件主程序不用为每个协议改代码ComTest后期我逐渐发现每接一个新设备都要在源码里加协议解析逻辑太折腾了。于是我把协议解析抽成了独立的DLL插件机制主程序定义好接口每个DLL实现一个对应的解析器用户在界面上选择用哪个DLL来解析当前数据流。接口定义极其简洁导出三个函数就够了一个是初始化传入协议配置参数一个是解析函数输入一帧原始数据输出解析结果字符串还有一个是释放资源。VC6下动态加载DLL就是LoadLibrary和GetProcAddress的常规操作完全不需要复杂的框架。我把Modbus RTU、自定协议、NMEA 0183这几类解析都做成了独立的DLL主程序加一个DLL就能支持一种新协议不用重新编译。这个设计让ComTest从“我自己的测试工具”变成了“能适配多种设备的通用平台”。7.2 把数据交给Excel处理和上报串口与办公软件的一次协作调试过程中还有一类需求是记录和分析数据比如把一段串口报文整理成表格或者生成测试报告。我尝试过直接用VC读写Excel文件方式是通过Excel的COM接口创建Workbook和Sheet然后把数据逐行写入单元格。这条路能走通但处理不当会有遗留的Excel进程占用问题需要注意调用Release和Quit的顺序。更轻量的方案是让ComTest直接导出CSV文件再用Excel打开完全不依赖OLE自动化也不怕系统里没装Office。压力测试时产生的几十万条日志用CSV格式导出后在Excel里做筛选排序和画曲线都很流畅。这个过程中我体会到工具和办公软件之间的协作很多时候用通用中间格式比直接调用API更稳定、更通用。7.3 网络透传把串口数据伸到局域网的另一个角落硬件开发经常遇到一个场景设备在实验室这边而你在办公室那边不想来回跑。我给ComTest加了一个透传模式把串口数据转发到指定的TCP服务器或UDP端口反过来也能从网络上接收数据再下发到串口。这样通过局域网我就能远程操作连接在实验室电脑上的串口设备调试日志也能同步传到办公电脑上查看。这个功能实现起来不算复杂本质上就是开一个socket线程在串口线程和网络线程之间做数据搬运。但我必须提醒的是透传模式存在数据格式和安全问题如果设备协议里没有足够的校验不建议跨公网使用。我自己也只在可控的局域网内用主要图一个方便不至于把调试环境暴露到不可信的网络上。8. 持续打磨ComTest后续想做的方向写到这里ComTest的基本面貌已经清楚了。从我个人的开发和使用经历来看一个调试工具的生命力在于它是否贴合真实的使用习惯。很多通用工具做得大而全但恰恰缺少了专业场景下的那种“刚刚好”。ComTest之所以我每天都在用就是因为它的每个功能几乎都来自我调板子时实实在在的烦恼。后续我计划做几件事一是把脚本引擎强化成支持条件判断和循环的完整状态机这样能模拟更复杂的交互流程二是增加一个数据曲线绘制面板把指定字段的数值实时画成波形调试传感器输出时会很有用三是考虑把核心串口通信层抽出来封装成独立的类库方便在其他工具里复用。如果你也在折腾类似的串口工具或者正被某个现成工具的不顺手折磨不妨试试按自己的思路写一个。不用一开始就想做得多完善先把收发通起来再在一次次实战中持续加功能。这个由自己掌控的调试环境用起来是真的舒服。本文还有配套的精品资源点击获取
返回列表