ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

嵌入式偶发Bug排查实战:串口假故障、蓝牙断连与烧录对比

嵌入式偶发Bug排查实战:串口假故障、蓝牙断连与烧录对比 做嵌入式开发最怕听到的词就是“偶发”。不是完全复现不了也不是一点没规律而是那种“你盯着它的时候它就好好的你一转身它就犯错”的bug。串口连着连着丢一帧蓝牙配对好好的莫名断开同一套固件烧进上一批板子一切正常、烧进新批次就起不来。这类问题最折磨人因为它不给你一个稳定的出发点你甚至没法判断是自己代码的问题、工具链的问题还是硬件本身的问题。这篇内容我结合自己在调试室里跟这类“偶发故障”死磕的经历重点聊三件事串口假故障怎么用换机排除法锁定真凶蓝牙偶发断连怎么用录屏取证建立可追溯的证据链以及“新旧批次对照”这种烧录排查思路在实际操作中怎么落地。如果你正在被某个时好时坏的板子折磨希望这篇能帮你少走几条弯路。1. 偶发 bug 的底层逻辑证据优先直觉靠边先说个我自己的感受。刚工作那两年遇到这种偶发问题第一反应往往是怀疑自己代码写错了于是翻来覆去查中断、查时序、查缓冲区。查了几天没结果开始怀疑芯片有问题然后换芯片、换板子最后发现是调试器接触不良——这种“查到最后发现是最不可能的地方出问题”的经历估计不少同行都有过。之所以偶发 bug 难查核心在于它违背了人的直觉。我们习惯把故障当作“非黑即白”的确定性事件但偶发问题本质上是概率事件背后通常存在一个或多个月经不调的触发条件。这个条件可能是时序上的、电压上的、温度上的也可能是工具链版本差异带来的甚至可能是你换了一根 USB 线导致的。所以在展开三个具体案例之前我想先把排查方法论立起来。总结下来就是三句话先拿到证据再谈定位。能录屏就录屏能抓日志就抓日志别靠记忆去猜。每次只改变一个变量。换机器、换芯片、换固件、换线材一次只动一个否则你永远不知道是谁起作用。建立“对照组”。同一套代码烧进不同板子同一块板子烧不同固件对比结果能帮你快速划定问题边界。这三个原则贯穿本文的三个案例。你对照着看会发现换机排除、录屏取证、新旧批次对照本质上都是为了让“偶发”变成“可观测的、可复现的差异”。2. 串口假故障的换机排除2.1 什么是“串口假故障”所谓假故障就是表面上看是串口坏了实际上坏的不是串口本身。我遇到过几种典型表现串口调试助手打开端口报“打开失败”或“被占用”但设备管理器里明明枚举正常。偶尔能收到数据但数据乱码或者丢字节丢得厉害。程序跑得好好的串口突然一整段没输出过几秒又自己恢复了。板子换到同事电脑上一切正常回到自己电脑上就不行。这些现象很容易让人怀疑是 MCU 的 UART 外设出了问题或者代码里串口初始化有缺陷。但实际上这里面相当一部分是“串口链路”的问题跟芯片本身没关系。最常见的就是 USB 转串口适配器接触不良、驱动冲突、DMA 配置和空闲中断打架、电气电平不匹配。2.2 换机排除法的操作步骤遇到这种问题我向来不急着改代码先做一轮物理层和链路层的排除。换机排除法听起来笨但极其有效尤其适合“手上有多台电脑、多个适配器”的调试环境。第一步先把当前这套“板子 USB 线 适配器 电脑”的组合整体换掉。拿一台确认健好的电脑、一根确认没有问题的 USB 线、一个全新的 USB 转串口模块重新连接板子看问题是否复现。如果问题消失了说明问题就出在原先的链路组合里而不是板子本身。第二步只换电脑不换适配器再只换适配器不换电脑。这样交叉两次基本能定位是电脑端的问题还是适配器的问题。如果换电脑后问题消失多半是驱动或 USB 控制器兼容性问题如果换适配器后问题消失那就是适配器芯片老化、虚焊或者劣质线材导致信号质量差。第三步把板子上的 TX、RX 对地电阻和连接方式检查一遍。很多假故障是电压摆幅不够造成的比如两个设备一个用 3.3V 电平、一个用 5V 电平直接连就会偶尔正常偶尔异常。这种情况换成板子供电统一或者加电平转换模块问题立刻就没了。2.3 假故障背后的“真凶”清单按我踩过的坑和帮别人排查过的经验串口假故障最常见的几种根因排个序USB 转串口芯片驱动冲突尤其是 CH340 和 CP210x 同时存在、或老版本驱动和新芯片不匹配。劣质 USB 线线芯太细导致供电不足或信号衰减表现就是“动一下线就断开不动就正常”。串口助手软件的端口缓存机制比如某些助手在缓冲区满了之后会直接丢数据而不是暂停接收。嵌入式端的 DMA 配置问题。如果使用了 DMA 接收但空闲中断处理不当高波特率下容易丢包看起来就像偶发故障。电气干扰。电机、继电器这类强干扰源一启动串口就开始乱码干扰源一停就恢复。我印象最深的一次是帮朋友查一块 GD32F470 板子串口每隔几分钟就掉线一次。代码检查了好几遍找不到问题最后发现是适配器插在电脑前面板的 USB 口上供电不稳导致芯片偶尔复位。换到后面板原生 USB 口问题消失。这种坑你不做换机排除光看代码永远找不到原因。提示换机排除不是“瞎换”而是通过逐渐缩小链路范围来确定故障边界。每次只换一个环节不要同时换一堆东西否则你无法判断是谁生效。3. 蓝牙断开的录屏取证3.1 蓝牙“偶发断连”为什么说不清蓝牙的偶发断连比串口更让人头疼。串口至少还有线连着你还能拿示波器去量波形蓝牙是无线链路断开之后一点痕迹都不留。客户报障说“蓝牙键盘用着用着一会儿就断开”你问“多久断一次”“断的时候在干什么”“手机还是电脑端”对方也说不清楚。这种时候单纯靠人去盯现场是不现实的指望靠记忆去复述更不现实。我自己的做法是“录屏取证”。核心思路很简单让现场环境自己留下证据。在电脑上架一个录屏工具连续录制整个操作过程等到问题复现后通过录屏回放去定位断连的时间点、当时的界面状态、蓝牙指示灯状态等等。这个方法听上去简单但真正做起来有几个细节需要注意。3.2 录屏工具的选择与配置录屏工具不需要多花哨能长时间稳定录制就行。我平时主要用两种一个是 OCam一个是 ShareX。OCam 的好处是界面简单、编码效率高适合长时间录制ShareX 的好处是开源免费、支持自定义区域录制而且能跟截图、文件上传联动。两者都能输出 mp4 格式体积可控。配置上有几个关键参数直接关系到取证效果码率不要设得太低。视频码率建议 8Mbps 以上否则蓝牙断开瞬间屏幕上可能出现的细微提示文字会糊掉。帧率至少 15fps建议 30fps。蓝牙断开、重连这个过程往往只有几秒钟帧率低了看不到中间状态。关闭麦克风录音。录屏取证要的是画面和系统声音环境噪音反而干扰后期分析。录制时长和磁盘空间要做预估。一个 1080p 30fps 的视频每小时大约 1~2GB。建议录制前清空磁盘或者录制完及时转存。3.3 录屏 日志联动抓取光有录屏还不够我一般在录屏的同时把系统蓝牙日志也打开。Windows 上可以用事件查看器里的 Bluetooth 日志Android 上可以开开发者选项里的蓝牙 HCI 日志这样录屏负责记录“表面现象”日志负责记录“底层事件”两者一对应很多问题就清楚了。实际操作流程是这样的启动录屏软件选择录制区域开始录制。同时开启蓝牙 HCI 日志或系统日志抓取。让测试人员或自己正常使用设备尽量复现偶发断连。过程中不要手动去开关蓝牙让它自然断。断连发生后停止录屏保存日志。回放录屏找到断连的时间点再回到日志里查看同一时间段的蓝牙事件。我在一次排查 HC05 蓝牙模块连接不上的问题时就用了这个方法。同事反馈模块连上一会儿就断每次断开的时间点毫无规律。通过录屏回放发现断开前的瞬间屏幕右下角都会弹出一个 USB 设备断开连接的提示才意识到 HC05 模块是用 USB 转 TTL 供电的而那个 USB 转 TTL 芯片因为供电不足会偶发性掉电蓝牙自然跟着断。这种问题单靠蓝牙日志是查不出来的必须录屏才能发现那个一闪而过的 USB 提示。3.4 从录屏证据中能读到什么录屏取证最大的价值除了帮你定位问题还能帮你“证明”问题不是出在你的设备上。很多时候客户报障你的第一反应是对方操作姿势不对但你不能直接这么说。录屏回放能客观还原整个过程的每一个细节是用户主动切换了蓝牙设备导致的断开还是系统的省电策略把蓝牙休眠了还是设备本身确实异常重启了。有了视频证据内部讨论、跟客户沟通都会轻松很多。另外录屏取证还有一个隐藏好处它能帮你发现那些平时没注意到的“环境变量”。比如断开瞬间是不是有某个软件恰好弹窗抢占焦点是不是系统刚好在后台运行了某个更新任务这些在做完录屏回放之前你是完全无感的。提示录屏取证的关键不是“录到画面”而是“记录问题发生的上下文”。断连前 10 秒的画面往往比断开瞬间更重要。4. “新旧批次对照”的烧录排查4.1 同一固件、两批板子结果不一样第三种典型场景是烧录相关。明明代码没改、编译环境没动、烧录步骤也一样上一批板子烧进去跑得好好的新一批板子烧进去要么起不来要么运行过程中随机死机。这种问题最容易让人抓狂因为你找不到任何逻辑上的“改动点”所有变量看起来都是一样的。这里就要用到“新旧批次对照”的排查思路。说白了就是找两块板子一块是以前跑得好好的老批次一块是现在出问题的新批次。然后拿同一个固件、同一台烧录器、同一个 IDE 工程分别烧进去对比现象。这步看似简单但实际操作中有几个变量是容易被忽略的。4.2 烧录环节的差异可能比你想象的更隐蔽烧录看似是个“一键完成”的操作实际上涉及很多环节烧录器的型号和固件版本、烧录软件(比如 Keil、J-Flash、IAR)的版本、烧录方式(比如 SWD、JTAG、串口 ISP)、烧录地址、时钟配置、Flash 算法甚至烧录时 USB 线的供电质量。我遇到过一次印象很深的案例。用 JLink 给一批 STM32 板子烧录固件老批次一切正常新批次烧录完成后偶尔出现程序不启动的现象。反复排查后发现新批次板子上的复位电路电容值变了导致 JLink 烧录完成后复位时序不匹配程序没有被正确复位启动。这个问题不去对比新旧批次板子的硬件细节光看烧录日志是发现不了的。换了烧录器复位方式、延迟复位时间问题解决。还有一次情况类似但根因完全不同。一个客户用 ESP32 模块做产品新批次模块烧录 ESPHome 或自定义固件后Wi-Fi/蓝牙功能时好时坏。用新旧批次对照法把老批次模块和新批次模块放一起烧录同一个固件发现新批次模块烧录时需要不同的 Flash 频率或模式比如 DIO 换成 QIO否则启动后射频部分不稳定。问题根源是 Flash 芯片型号变化、烧录算法不一致并不是双方谁的责任单纯属于批次差异带来的调试适配需求。4.3 对照实验的正确姿势一次只改一个变量做新旧批次对照最大的忌讳是“同时对比太多东西”。如果新批次板子不仅改了 Flash 型号还换了晶振还改了 PCB 布局你会发现做对比实验根本没法定位问题。正确的做法是第一步老批次板子 旧固件确认能正常运行作为基准。第二步老批次板子 新固件确认固件本身没有退化。第三步新批次板子 旧固件确认是不是硬件批次差异导致的问题。如果第三步有问题说明问题出在硬件批次差异如果第三步没问题、第四步新批次板子加新固件出问题那就要怀疑固件跟新硬件的兼容性。这套流程里核心是三个字——控制变量。每一轮只改动一个环节然后明确记录现象是否变化。我把这些现象记录在一个表格里每一轮对照都追加一行几个小时后基本上就能把问题定位到某一个具体变量上。另外还有一个容易被忽略的点烧录器本身的兼容性。比如 J-Link 的驱动版本、Keil 的 pack 包版本甚至不同批次的芯片内部 Flash 算法略有差异都可能导致烧录行为不同。当你做对照实验的时候尽量固定同一台烧录器、同一个软件版本否则你对比出来的“差异”可能只是工具差异。注意烧录排查时不要只盯着烧录成功与否。烧录成功的日志只代表 Flash 写入完成不代表程序能正常启动。用“烧录成功”作为合格标准会漏掉很多启动层面的偶发问题。4.4 烧录失败类问题同样适用对照法除了启动异常烧录本身失败的问题也很常见。Keil5 报烧录失败、JLink 连接不稳定、串口 ISP 烧录到一半卡死这些看着像工具问题其实也可以用新旧批次对照法来排查。比如拿老批次板子试同一个烧录指令老批次能成功、新批次失败那问题大概率在硬件两个批次都失败则优先检查工具链配置。我见过一个很典型的案例用串口给 Arduino Uno 烧录引导程序烧录到一半总是失败。老批次板子没事新批次板子必现。最后发现是新批次板子的自动复位电路上电容容值和复位时间不匹配导致烧录过程中 DTR 信号控制复位时序不对。这个案例里如果你只盯着烧录软件配置哪怕调一百遍参数也解决不了问题必须通过“新旧比对”把视线引到硬件差异上才能真正找到出口。5. 把三招串起来一个完整的偶发 bug 排查流程换机排除、录屏取证、新旧批次对照这三招不是孤立的。现实中的问题往往不按单一类型出现很可能串口、蓝牙、烧录的问题交织在一起。这时候你需要一个通用的排查步骤。5.1 排查前先做“环境快照”无论问题多诡异开始排查之前先把当前的软硬件版本信息记录下来电脑型号、操作系统版本、IDE 版本、编译器版本、烧录器型号和固件版本、芯片批次批号、外围模块型号。这步看似繁琐但能帮你避免查了半天之后发现“原来是系统自动更新改了驱动”这种乌龙。环境快照的记录形式我建议用简单的 Markdown 或纯文本文档按时间点逐条记录。后面每次改动一个变量就在文档里追加一条。这个习惯帮我节省了大量重复劳动。5.2 先复现再定位后修复不管面对的是串口问题、蓝牙问题还是烧录问题顺序永远是先想办法复现复现不了就上录屏、上日志尽量获取现场证据拿到证据后缩小怀疑范围锁定了范围后用控制变量法做验证验证通过后再修复修复后还要回归测试几轮确认没有引入新问题。这个过程没有捷径。偶发 bug 排查本来就是个体力活但它也有它的浪漫当你最终找到原因的那一刻你会觉得之前熬的夜都值得。我也经常提醒自己不要因为某个问题看起来很“偶发”就草率地用“运气不好”来收场绝大多数偶发问题背后都有一个可以被定位的必然因素只是我们还没有找到那个触发条件罢了。5.3 建立自己的故障排查工具箱最后说点务实的。调试偶发 bug工具和习惯同样重要。我自己的桌子上会常备以下几样东西遇到问题顺手就能用上至少两台电脑一台主用一台备用方便做换机对比。两三个不同品牌的 USB 转串口模块备着交叉验证。一根质量靠得住、供电充足的 USB 线并标记好日期避免拿到劣质线浪费时间。至少一款录屏软件OCam 或 ShareX保证随时能打开录制。一个专门记录调试日志的文档模板包括日期、环境、现象、改动项、结果。每块板子贴好批次标签这个习惯在“新旧批次对照”时能省掉很多麻烦。5.4 回归测试不能省找到原因、修好之后千万别急着收工。偶发问题的根因往往是概率性的修复方案是否真正有效需要经过一定时长的回归验证才能确认。比如串口假故障修复后建议连续跑 24 小时以上的压力通信测试蓝牙断连修复后建议连续开关设备、频繁连接断开至少几十轮烧录问题修复后建议连续烧录多片板子确认稳定。回归测试的结果也要记录在排查文档里作为后续问题回溯的依据。这不仅是工程素养的问题更是对自己工作时间的一种保护。今天不记录三个月后同样的偶发问题再出现你又要从零开始查一遍。6. 我的个人总结与建议做了这么多年开发和调试我最大的体会是偶发 bug 不是靠“聪明”解决的而是靠“方法”解决的。聪明可能会让你更快地想到某些可能性但真正让你从一团乱麻中走出来的是老老实实的证据采集、变量控制和对比验证。我也越来越习惯用“最笨”的方式来处理疑难问题先录屏、先记录、先做对照实验而不是先改代码。有很多次我改了十几行代码觉得终于修好了结果第二天问题照旧反而是耐下心来做了一轮换机对比才发现在某个不起眼的环节上有一个硬件差异在作祟。如果你手上正好有一个让你头疼的偶发问题我建议你照着这篇文章里的方法试一轮。先做环境快照再搭好录屏和日志工具然后按控制变量的思路逐步排查。不要指望一口气解决但只要你每一步都留下了可靠的证据答案通常会在你预想不到的地方等着你。
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进