
1. 偶发 bug 的复盘纪律不急着改代码先锁住变量调试这行干久了最怕听到的就是一句话“这个 bug 是偶发的刚才还在现在又不出来了。”这句话一出来就意味着前面几个小时的排查动作大概率全是白做的。偶发问题真正的难度不在于“修”而在于“复现”。你连现场都抓不住改什么代码都是在蒙着眼睛拆弹改完也没法验证到底是不是这个原因。我自己踩过的坑太多了。有一回是产品端的串口偶发收不到数据客户那边隔几天报一次每次都是重启一下就好换个人测试就死活复现不了。后来折腾了大半个月最后发现不是代码逻辑问题而是 USB 转串口线在特定电脑上供电不足导致电平不稳。你看方向偏了时间全浪费在“猜”上。所以我现在处理偶发 bug第一反应不是开调试器而是先做三件事建时间线、锁环境差、留证据快照。1.1 偶发问题最难的不是修而是复现复现偶发 bug 有一个基本共识它不是完全随机一定有个触发条件只是这个条件平时很难碰到。这个条件可能是某个温度区间、某路供电电压的波动、某个外设中断来得太密集、某条指令执行到了特定时序甚至只是某根线接触不良的抖动。我一般会让反馈问题的人回答四个问题第一次出现是什么时候最近一次出现又是什么时候中间隔了多久出现时是在做什么操作待机、通信、刷机、还是批量数据处理最近有没有动过硬件换过模块、改过线序、更新过工具现场能不能留下点东西截图、录屏、日志、串口输出什么都有用。这四个问题卡住任何一个后面的排查就很容易走弯路。尤其是“最近有没有动过硬件”这条很多人默认排除但实际上一大半偶发问题的根子就在这。1.2 现场信息的三件套时间线、环境差异、证据快照我在项目里会强制自己养成一套“现场信息三件套”的习惯每次都按这个顺序来收集不跳步信息类型具体内容为什么重要时间线首次出现、频次、持续时长、恢复方式判断问题是瞬态还是稳态是否与定时任务相关环境差异当时用的电脑、电源、线材、固件版本、周边设备锁定变量排除“换了一个条件就变了”的情况证据快照录屏、串口日志、寄存器读数、拍照记录接线事后复盘有据可依不靠记忆这套方法最直接的价值就是逼着你把“偶发”变成“可描述”把“模糊”变成“可查”。等到信息收集全了你往往已经能圈出两到三个可疑变量剩下的就是逐一排除。1.3 一个值得养成的习惯改动前先归档这里我必须多说一句每一次准备动手修改之前先把当前能正常工作的版本完整归档。这不是废话而是很多人到了紧急时刻根本想不起来做。有一次我调 ESP32 的蓝牙连接为了测试一个猜想改了一行初始化参数结果改完更糟想回去改原来的配置发现早忘了原值是什么。那一行参数找了半天才从 Git 记录里翻出来白白浪费了时间。所以我现在的做法是任何 firmware 改动之前先在本地做一次完整的状态备份包括编译产物、配置文件、烧录工具版本。这样不管改出了什么结果随时能退回去对照。尤其是后面要讲到的“新旧批次对照”排查法没有干净的归档对照你根本没资格谈“对比”两个字。2. 串口假故障的换机排除换电脑有效不等于电脑坏了串口调试应该是最常见的嵌入式开发场景了但串口问题也是“假故障”重灾区。所谓假故障就是现象上看着是设备坏了、芯片通信异常实际上问题出在链路某个不起眼的环节甚至出在调试工具和电脑的配合上。我先说一个几乎每个人都遇到过的情形串口调试助手打开发指令设备没反应数据收不到。换一台电脑插上测试一切正常。第一反应往往是“原来那台电脑有问题”。这个结论不完整——换电脑有效不等于那台电脑坏了更不代表项目里的设备没问题它只是帮你把问题范围切掉了一块。2.1 为什么“换台电脑”常常能治好串口假故障我在多个项目里验证过换电脑之后串口恢复正常的背后往往藏着下面这些真实原因原因具体表现为什么换电脑就“好”了USB 主机控制器差异老台式机前置 USB 口供电不足设备枚举失败或丢数据换台电脑供电充足枚举稳定转串口芯片驱动版本不同CH340、CP2102 在不同版本驱动下缓冲机制有差异新电脑驱动版本匹配行为完全不同地线电位差设备地、电脑地之间存在压差导致电平判断错误另一台电脑接地更干净压差被掩盖USB 转串口线材老化线缆内部断股、屏蔽差传输不稳定换电脑时顺手换了线但没意识到串口被占用残留上次程序异常退出驱动没释放串口句柄新电脑没有历史残留信息这个表看懂之后你就能明白换电脑其实是一个“整体替换变量”的操作——你同时换了 USB 控制器、驱动、供电环境、接地环境、历史状态。它非常适合用来做排查但绝不能当成最终结论。正确的做法是确认换电脑能解决问题之后再回头用排除法找出具体是哪个变量导致的问题。2.2 换机只是起点串口链路排查清单串口链路看似简单就是 TX、RX、GND 三根线但实际排查起来有固定套路。我一贯的顺序如下先测电平。拿万用表量 TX、RX 对 GND 的电压正常空闲状态应该是接近供电电压3.3V 或 5V如果量出来只有零点几伏那大概率是芯片没正常工作或者引脚被复用。再查接线。TX 要接到对端的 RXRX 接对端的 TXGND 必须共地。这个简单到不能再简单的错误我见过新手犯也见过老手在跳线堆里翻车。接着排除流控。很多 USB 转串口工具默认开启了 RTS/CTS 流控但目标板硬件上根本没接这两个脚。这时候表现就是数据发出去收不到回包或者收回包时断断续续。直接把流控关闭问题可能当场消失。然后看设备枚举。在电脑的设备管理器里确认串口号、驱动是否正常。如果枚举出来是未知设备先别怀疑芯片把 USB 线换一根短的再试。最后看波特率误差。串口通信是有容错范围的一般要求在正负百分之二以内。如果你的 MCU 用了内部 RC 时钟和上位机设置波特率偏差稍大一点正常温度下没问题温度一高就可能偶发乱码和丢包。有一次排查一个 GD32F470VET6 开发板的串口偶发收不到数据折腾了三天最后发现是开发板上的 USB 转串口芯片 VCC 脚被一根杜邦线短路到了 GND电压被拉低电平逻辑混乱。这种问题换几台电脑都没用必须回到链路本身去量。2.3 MCU 侧容易漏掉的点DMA 与中断优先级链路全部检查完之后问题如果还在那就要往 MCU 内部看了。串口收发偶发异常常见内因有两个DMA 配置不当和中断优先级冲突。用 DMA 接收串口数据时通常配合空闲中断来判断一帧数据的结束。很多人只在初始化里开了 DMA但忘了配置 DMA 通道的优先级导致在 CAN、定时器中断密集时串口数据迟迟搬不走环形缓冲被覆盖。表现出来就是偶发丢帧频率不高但每次一来就是一批数据里丢几个字节。我给一个排查思路第一确认 DMA 接收通道确实处于开启状态很多人初始化了 DMA 但忘了调用HAL_UART_Receive_DMA这类启动函数第二检查串口全局中断的抢占优先级不要让它在中断风暴里被饿死第三如果用过LL库和HAL库明确函数内部的处理差异不要把两套机制混用。另一类常见情况是中断服务函数里做了耗时操作比如打印、延时、写 Flash导致本应很快完成的数据接收被拖慢。串口在高速率下跑起来中断稍微慢一点硬件接收缓冲区就溢出了。你把中断服务函数精简到只做读数据和置标志位问题往往就消失了。总结一句话串口假故障的排查顺序必须是“链路 → 工具 → 芯片内部”永远别倒过来。3. 蓝牙断开的录屏取证把“偶尔掉线”变成一条可复盘的时间轴蓝牙问题比串口问题更让人头疼因为它的现场往往不在你的工位上而是在客户手里、用户手里、路上跑的手持设备里。蓝牙断开的偶发性非常高而且很多人反馈的时候只会说“连不上”“总掉线”给不出任何依据。这时候录屏就是最好用的取证手段。别觉得录屏是“甩锅”或“留后路”实际上录屏能把模糊问题变成一条有时间轴、有操作路径、有现象界面的完整记录后面分析起来效率能翻几倍。3.1 录屏录什么状态栏、信号强度、操作路径三要素很多人一说录屏就是拿手机从头录到尾录完了发现全是垃圾信息。真正有效的蓝牙故障录屏至少要包含三个要素要素说明作用状态栏全程记录蓝牙图标是否消失、信号格数变化直接反映 RF 链路是否中断信号强度开启开发者选项里显示 dBm 数值判断是信号弱导致掉线还是信号正常但协议层异常操作路径从打开蓝牙、配对、连接、使用到掉线的完整操作复现触发条件确认掉线和某个动作是否相关以 Android 为例在开发者选项里可以勾选“显示蓝牙设备信号强度”和“蓝牙 HCI 信息采集器”。前者能在状态栏显示实时的 RSSI后者会把蓝牙芯片收发的 HCI 数据包记录下来。录屏的同时把这两项打开掉线前后到底发生了什么基本就藏不住了。我要求客户录屏的时候会说清楚第一不要中途暂停第二掉线后继续录三十秒方便我看到重连过程第三如果能再截一张系统“关于蓝牙”或“蓝牙日志”的图更好。3.2 系统日志与 HCI 抓包怎么配合录屏是给人看的日志是给机器挖的。单独用录屏只能看到“掉了”这个现象要定位“为什么掉”必须配合日志和协议层抓包。常用的组合是这三样Android 的 “蓝牙 HCI 信息采集器”。开启后系统会自动生成 btsnoop 文件导出后可以用 Wireshark 打开直接看到 LMP、LL、ACL 层的帧。掉线原因在协议层几乎是明摆着的是对方发了断连请求reason code还是本机超时判定链路丢失都会在 HCI 包里有体现。dumpsys bluetooth_manager或厂商自带的蓝牙日志。能查到当前连接状态、休眠策略、是否被系统回收。应用侧的代码日志。能确认掉线时应用还在做什么操作比如正在发送音频数据还是处于后台休眠。实际配合方法是先将录屏的时间轴和系统日志的时间戳对齐在录屏画面里故意做一次“看时间”的动作比如点开系统时钟这样后期可以准确对齐到秒级。然后在日志里从掉线时间点往前推两秒看有没有异常状态变更。3.3 三个典型场景的取证重点我这几年遇到的蓝牙断开问题翻来覆去无非三种典型情况每种取证重点都不同。第一种串口透传模块掉线比如 HC-05。这种模块出问题多半和配置有关主从模式设错、波特率不匹配、连接间隔过大导致对端超时。取证时重点录清楚模块上的 LED 状态快闪表示可配对慢闪表示已连接。掉线时如果是快闪说明连接真的断了如果模块侧慢闪但手机侧显示断开那多半是手机系统把蓝牙“静默回收”了。第二种蓝牙音频 A2DP/SCO 切换掉线。典型表现是听歌正常一来电话就断。这时候要看 HCI 日志里是否存在 A2DP 断开后尝试切 SCO 失败的过程。很多国产蓝牙芯片在 A2DP 和 SCO 之间切换需要重新配置链路参数参数不对就掉线。取证时要明确记录掉线发生是在电话接通的瞬间还是在挂断瞬间。第三种BLE HID 设备比如蓝牙键盘、自拍杆。这类设备为省电会进入休眠必然导致连接暂时断开这是正常现象。但如果每次唤醒都要十几秒才能重连那就是从机广播参数或主机扫描策略有问题。取证重点是把“按键唤醒后到重新输入生效”的秒数录下来连续测三次基本能确认问题程度。蓝牙问题里还有一个容易被忽视的因素信道干扰。在办公区、地铁站这种 2.4GHz 极其拥挤的环境BLE 掉线概率会明显上升。如果用户反馈“在公司经常掉在家里不掉”那就先在录屏里记下环境和时间点然后再考虑是不是需要进行跳频或调整广播信道设置。4. “新旧批次对照”查烧录同一份固件新批次进不了程序烧录问题也是嵌入式开发里的高频场景尤其是量产阶段。最常见的一句话是“这固件在上一批板子上随便烧新到的板子死活连不上烧录器J-Link 都识别不到。”出现这种情况很多人的第一反应是怀疑芯片坏了。但“整批芯片都坏了”在概率上非常低更合理的解释是这批板子和上一批之间发生了什么你没注意到的硬件差异。这就是“新旧批次对照”排查法的核心思路用新旧板卡互相交叉验证把问题定位到具体差异上。4.1 “新旧批次对照”到底对照什么对照不是把新旧板子摆在一起看外观而是对照这几个维度对照维度具体操作目的MCU 型号与封装确认新旧批次物料是否完全一致包括丝印、批次号查清是否换了同系列但不同型号的芯片引脚与原理图对比新版原理图改动、网络表差异排除板级设计变化带来的影响烧录工具与配置确认 J-Link 固件版本、Keil 配置、烧录速率是否一致排除工具侧变量核心供电与时钟实测新板 VCC、复位脚、晶振起振情况排查硬件工作条件差异芯片内部状态读取 ID Code、Option Byte、读保护级别排查芯片被锁或配置异常这四个维度对照下来绝大多数烧录异常都能圈出范围。注意新旧批次对照排查时最忌讳的是一上来就怀疑某一方“绝对没问题”要保持两边条件完全可互换结论才可信。4.2 四组交叉实验的经典剧本把新旧变量分开最标准的做法是四组交叉实验旧板 旧烧录工具确认基线可用。旧板 新烧录工具判断烧录工具是否被换坏。新板 旧烧录工具验证是不是新板子本身的问题。新板 新烧录工具复现客户的完整现场。如果只有第 3、4 组失败那问题几乎可以锁定在新板子硬件上。这时再针对 MCU 的供电、复位、时钟、烧录引脚逐项测量。如果第 4 组失败但第 3 组成功那问题在烧录工具或软件配置上优先排查驱动版本和烧录器固件。有一次我们遇到新批次板子 J-Link 能识别内核但一烧录就报错用旧板测什么事都没有。交叉实验做完后发现新旧板子的供电设计完全一样但新板子的一颗去耦电容换了个容值更小的规格导致烧录时内核瞬间电流拉低电压烧录器直接掉线。这种问题不通过对照实验单纯盯代码绝对查不出来。4.3 新批次里最容易变的五个隐藏项交叉实验确定是新板子问题之后再把范围从 Board 收窄到具体原因。我做这类排查时会优先检查下面五个点优先级从上到下芯片读保护RDP级别。很多芯片出厂自带的 RDP 级联不一样或者上一批贴片前被烧录过工厂测试代码并开启了读保护新批没有正确解除。表现是J-Link 能连上但读取 ID 码或擦除 Flash 时被拒绝。解决路径是执行整片擦除或切换到烧录器的“unsecure”模式。Option Byte 配置。新批次的 Option Byte 可能和旧批次不同比如把 BOOT0 引脚配成了别的功能、看门狗进了硬启动状态。表现复杂多样但读一次目标芯片内部寄存器就能对比出来。SWD 引脚被复用。如果新版代码上电后立刻将 SWDIO/SWCLK 配成普通 GPIO烧录器就再也连不上了。这个情况在“前一版能烧新固件烧不了”的问题里特别常见。处理办法是烧录前让 MCU 先进入复位状态再使用“Connect under Reset”模式连接。供电能力不足。新批次板子如果带了更多外设或者 DC-DC 输出电流余量不够烧录器通过调试接口向芯片索取电流时电压就会塌陷。看现象是烧录到一半报错断开重试偶尔成功。晶振参数变化。更换了晶振品牌或负载电容导致内部时钟不稳定进而影响烧录器与芯片的同步时序。这种问题可以通过降低烧录器速率临时验证如果降低速率后烧录稳定就重点查时钟回路。4.4 一次 J-Link 连不上新批次的完整复盘分享一个我做过的实际案例。当时项目用 Keil5 J-Link 给一批新板烧录 GD32 固件现象是旧板秒连新板报“Cannot connect to target”。我按交叉实验跑了一遍锁定是板子差异。然后量了供电3.3V 正常复位脚电压稳定SWDIO/SWCLK 对应 MCU 引脚的波形在空闲时正常。最后用 J-Link Commander 尝试连接发现设备能枚举但读取 ID 码失败。这时我不再反复连接而是把两块板并排放在桌上用万用表直接量新旧板 SWDIO 到 MCU 引脚的导通性。结果发现新板上 MCU 的 SWDIO 引脚虚焊探针点在测试点上正常但内部根本没和芯片脚连通。补焊之后一次通过。那个批次其实只有这一块板子虚焊但排查路径是完整的。虚焊问题在烧录故障里通常最隐蔽因为它不像供电异常有波形可看也不像读保护有寄存器可查只能靠导通性测试去验证。所以每次测烧录问题我强烈建议备一根万用表笔把烧录引脚逐个量一遍这个动作花不了两分钟但能省下半天时间。5. 我在现场养成的几个工作习惯分享给你说回最初的问题偶发的 bug 怎么办我的答案其实一直都没变——不要急着修先让问题变得能复现、能描述、能对比。串口假故障用换机排除蓝牙断开用录屏取证烧录失败用新旧批次对照本质上都在做同一件事把模糊的偶发问题改造成清晰的、有对照组的、可验证的问题。这几条习惯是我这几年踩坑踩出来的分享给同样被偶发问题折磨过的人排查记录永远写在纸面上或文档里不要写在脑子里。复盘的时候你唯一可信的参考就是当时的记录。工具链版本一定要记录清楚。驱动版本、烧录器固件、IDE 版本任何一个升级都可能改变行为。同一项目里同时出现多个问题时一次只处理一个。很多人把偶发问题越查越乱的原因就是同时动了多根线。怀疑“玄学”之前先把地线、供电、接线这种最基础的东西重新检查一遍。我见过太多所谓玄学问题最终都死在这三件事上。不要害怕向别人求助时给对方“完整背景”。很多人报 bug 时只说现象不说自己改过什么。学会把改动历史交代清楚排查效率能提升一大截。排查偶发问题本质上就是一场和不确定性的拉锯战。你能做的不是消除所有未知而是把未知范围一步步压缩到可控区域。希望这篇记录能帮你下次遇到类似问题时少走几段弯路。