一、偶发bug的排查,从来不是靠运气
做嵌入式开发和硬件调试的朋友,大概率都遇到过这种让人抓狂的场景:设备在产线上测试了一整天,一切正常;刚交付给客户,第二天就反馈"串口连不上""蓝牙自己断开""板子烧录失败"。等你火急火燎赶到现场,拿着一模一样的操作步骤复现,它又完全正常了。你走了,问题又冒出来。反复几次之后,团队里就会开始出现"玄学""灵异事件"之类的说法,甚至有人怀疑是客户操作姿势不对。
我在这行干了十来年,对这种"偶发bug"其实已经有了一种条件反射:不是问题真的偶发,而是触发条件里藏着我们还没看到的变量。它可能是USB线材的压降、可能是蓝牙连接参数的时序竞争、可能是新批次物料某个电容的批次差异。这些东西单看都不起眼,组合在一起,就成了一个只在特定时间窗内出现的致命组合。
本篇要聊的,就是三个我在实际项目中反复用到的排查手段:串口假故障的换机排除法、蓝牙断开的录屏取证法、烧录异常的"新旧批次对照"法。这三个方法都不复杂,甚至可以说相当朴素,但恰恰是这种朴素的手段,在定位偶发问题时比任何高端仪器都管用。如果你也在和"时好时坏"的bug缠斗,这篇文章应该能帮你把排查思路彻底捋顺。
二、串口假故障:先换线、换口、换电脑,再谈代码
2.1 什么是"假故障",为什么容易骗到人
嵌入式调试离不开串口。不管是STM32、ESP32还是其他MCU,一上来第一件事几乎都是先把串口打通。但恰恰是这个最基础的工具,最容易出现"假故障"——也就是问题根本不在你的代码或电路,而在传输链路的某个环节。
最常见的表现有几类:
- 串口调试助手打开后,发数据下去没任何回应,但设备明明在跑。
- 收数据时断时续,偶尔乱码,看不出任何规律。
- 换个USB口、换台电脑,就好一段时间,之后又犯。
- 用逻辑分析仪抓波形,一切正常;直接用串口工具连,就是不通。
这类问题之所以"假",是因为它伪装成软件bug或固件bug出现在你面前,等你花半天时间把代码翻了个底朝天,最后发现罪魁祸首可能只是一根劣质USB线。我见过太多同事在代码上反复打日志、加延时、改中断优先级,折腾一整天之后,换了一根线整个世界清净了。
2.2 换机排除法的实操顺序
遇到串口异常,我的习惯是不碰代码,先花五分钟排除链路,而且顺序固定,像体检一样一项一项过:
- 换线。这是最优先的一步。USB转串口线(比如CH340、CP2102、FTDI方案的线)是消耗品,线芯折断、屏蔽层损坏、USB头接触不良,都可能导致传输层不稳定的问题。不要看线外表完好就跳过这步,我遇到过一根看起来崭新的线,内部已经有断路,只有在特定角度弯折时才连通。有条件的话直接换一根全新的短线(越短越好)测试。
- 换USB口。台式机前置USB口供电往往不稳,机箱前面板的线材质量也参差不齐。串口芯片虽然功耗不高,但劣质前置口可能出现供电跌落,导致芯片工作异常。换到主板后置USB口,最好是直连的,往往就好了。
- 换串口工具。换个串口调试助手(比如从某厂配置工具换到开源串口工具),排除上位机设置问题。检查波特率、停止位、校验位是不是匹配,这个看起来基础,但真有人把115200和921600弄混过。
- 换电脑。换一台电脑,装最新的官方驱动,排除驱动兼容问题。这一步在Windows 11升级之后尤其重要,老版本CH340驱动在Win11下会出现设备识别但通信异常的情况。
- 换设备。如果有第二块同型号板子,直接换上。如果换了设备好了,至少说明链路没问题,问题回到板子本身——这时候再考虑是不是串口芯片虚焊、RX/TX接反、电平不匹配。
2.3 换机到底换掉了什么变量
很多新手不明白:"我一换线就好了,但原来的线明明能用啊?"这里要说的核心逻辑是:偶发问题的根因往往在临界状态上,而不是完全故障。一根线可能大部分时候是好的,但在某个温度、某个弯折角度、某个拖拽状态下,线间电容或接触电阻发生变化,恰好让信号电平跌出阈值之外。你把它拔下来换掉,等于消除了这个"临界变量"。
同理,换电脑、换USB口,本质上都是在消除"供电质量""驱动版本""接地参考"这些可能处在临界状态的变量。这个逻辑是整个"换机排除法"的精髓:不是机器不行,而是机器的某个特征参数不稳定,你换机,就是把它从临界状态一脚踢回正常区间。
换句话说,换机排除法并不只是"碰运气",它是有意识地切断一组变量,观察现象是否消失。如果换了线就好了,说明变量在"线"这段;如果换线没用、换电脑才好,说明变量在"电脑端驱动/供电"这段。这套逻辑走一遍,能让排查范围缩小非常多。
2.4 串口假故障的真凶清单
| 现象特征 | 首要怀疑 | 验证手段 | 解决办法 |
|---|---|---|---|
| 完全不通,偶尔通一下 | USB线内部断芯、USB头氧化 | 换线 + 弯折测试 | 换高质量短线 |
| 时通时断,伴有乱码 | 供电不稳、接地不良 | 示波器看串口芯片VCC纹波,换USB口 | 换直连后置USB口,检查地线 |
| 换电脑会好,原电脑不行 | 驱动版本冲突、系统级电源管理 | 查看设备管理器,更新官方驱动 | 卸载旧驱动,重装最新版 |
| 新板子批量出现串口异常 | 物料批次差异、焊接问题 | 万用表测RX/TX通断,对比正常板 | 检查焊点,联系贴片厂做切片分析 |
| 波特率对不上逻辑分析仪却正常 | 工具配置错误 | 核对参数字节比对 | 统一9600/115200,校验位N/8/1 |
这个表格里的每一项我都踩过。尤其是"供电不稳"这一项,在连接长USB延长线时几乎百发百中:线长超过一米,压降就很明显了,CH340这类芯片虽然标称低功耗,但供电降到3.3V以下时,它的内部LDO会进入不稳定状态,表现就是时好时坏。
2.5 一个真实的"换机排雷"记录
去年做一个工业控制项目,客户反馈上位机周期性地收不到下位机的数据。我们远程调试了三天,加了心跳包、改了串口中断、甚至怀疑是DMA配置问题。最后我让现场工程师做了一件事:把现场那条USB转串口线换成一米以内的短线,然后……就没有然后了。问题彻底消失,一个月没复发。
复盘时发现,原生产线为了布线方便,用了三米长的USB延长线,线材还是低价采购批次,屏蔽层极薄。在现场的电磁环境下,这个长度和屏蔽量刚好踩在误码临界点上。固件和上位机的改动,确实能在一定程度上缓解误码,但只要链路本身的临界状态还在,问题就无法根除。换线之后,那个"偶发"变量被直接移除了。
这就是串口假故障的特点:它经常不是代码的锅,但如果不先排除链路,你就永远在代码里做无用功。我的经验是,遇到串口任何奇怪问题,先花五分钟换线换口换电脑,把这套逻辑走完再碰代码,能省下至少半天的排查时间。
三、蓝牙断开的录屏取证:把"说不清的现象"变成"看得见的证据"
3.1 为什么蓝牙偶发断开必须取证
蓝牙的问题和串口还不太一样。串口的异常至少能通过逻辑分析仪抓电平、通过串口工具复现,是个"有物理痕迹"的问题。蓝牙就不一样了——特别是手机App通过BLE控制设备的场景,断开是发生在空中的,没有线缆可抓,没有波形可见,你手里只有一句"它自己断了"。
这种时候,口头描述几乎没有任何诊断价值。客户说"断开了",你不可能知道是手机系统杀的、是距离远了、是信道冲突了、还是固件主动断的。你也没法复现,因为你在现场时可能就是不断。所以我坚决执行一个原则:凡是蓝牙偶发问题,不管什么渠道反馈,第一件事先让反馈方录屏。
这个"录屏取证"不是随便拿手机拍两下屏幕,而是要按照标准流程来做,确保录下来的内容里有充分的诊断信息。我用的是下面这套方法,已经实践了三年多,定位过好几次真实蓝牙bug。
3.2 标准取证流程
- 让用户/测试人员开启手机自带的录屏功能,iOS和Android都自带,不需要额外装App。关键点是:录屏要保持到问题复现并继续录30秒,不能一看断开了就马上关掉,因为断开之后的自动重连行为、系统弹窗、信号栏状态都是判断根因的重要线索。
- 打开开发者选项里的蓝牙日志(Bluetooth HCI snoop log)。这一点初学者很容易忽略。Android在开发者选项里开启"HCI日志记录"后,系统会把整个蓝牙协议栈的空中数据包存成btsnoop文件;iOS可以用Xcode的Tools里的CoreBluetooth日志。这个文件拉出来用Wireshark打开,能看到断连前最后的几个空中包——是手机主动发的Disconnect请求,还是对端无响应超时,一目了然。
- 在设备端开启AT日志或协议栈日志。如果你用的是HC-05/HC-06这类经典蓝牙模块,就是串口AT日志;如果用CSR、杰理这类芯片方案,就要在SDK里打开协议栈的调试输出。重点是记录设备端看到断开事件的时间点和原因码(Reason Code)。
- 录屏过程中注意操作动作和手机上关键信息的同屏。比如距离、是否有其他App在后台占用、手机是否自动熄屏、是否插了USB(Android上插USB且开启USB调试时,部分系统会短暂关闭蓝牙扫描)等。
得到这些素材后,把录屏时间轴、btsnoop时间轴、设备端日志时间轴对齐,问题往往立刻现形。举个典型例子:录屏里能看到"手机屏幕熄灭的瞬间,App收到断连触发退出"——这就很可能不是蓝牙链路的物理问题,而是App在后台被系统挂起,蓝牙回调没能及时处理,超时后系统主动断链。这在Android上极其常见。
3.3 录屏里具体看什么
录屏不是录了就完事,我一般让测试人员按下面的清单自查一遍,把有效信息标记出来:
- 断连前最后10秒手机屏幕上的操作:是否在滑动、是否切了App、是否有电话进来、是否弹了系统通知。
- 断连瞬间的信号格数:这个能区分"距离+遮挡"导致的问题和"链路空闲超时"导致的主动断开。
- 断开后App界面是"一直转圈"还是直接显示已断开:转圈说明App还在等回调,可能是固件侧没正常响应;直接断开说明底层链路enty已经断了。
- 是否有自动重连,重连是否成功:如果重连成功,说明设备端的广播/扫描参数没问题,问题集中在连接保持阶段的参数配置上。
这些信息组合起来,基本能派生出几个典型的排查方向——如果是"信号弱先断开",就要做链路预算,检查天线匹配、发射功率;如果是"空闲一段时间后断开",就要查**连接间隔(Connection Interval)、从机延迟(Slave Latency)、超时时间(Supervision Timeout)**这几个BLE协议参数。我处理过一个案例,就是某个模块厂商的SDK默认把Supervision Timeout配成了400ms,而链路事件间隔又偏长,导致只要有一次射频冲突没收到包,主从双方便直接判定超时断链。表现就是"放着不动一会儿就断,一靠近就恢复",用户描述为"蓝牙不稳定"。这类问题如果不录屏取证,几乎没法定位,因为你在现场拿着设备来回走动时它就是不犯。
3.4 实测案例:一段录屏揪出重连参数设计缺陷
做某款BLE心率设备时,用户反馈"手机连着连着就断了,有时候自己又连回来"。第一轮反馈只是文字描述,我们猜测了很多方向:天线匹配、固件内存泄漏、主从设备兼容性。后来我要求用户按上面流程录了一段时间的屏,btsnoop文件一拉出来,真相非常清晰:
断开前两秒,手机端发起了Connection Parameter Update Request,把连接间隔从30ms改成了100ms——这是iOS在App进入后台时为了省电自动做的。设备端对这个请求的响应是直接拒绝,然后手机端立即发了Disconnect。也就是说,断连的根本原因是设备固件里没有正确响应参数更新请求,导致主设备(手机)主动放弃连接。
这个bug在实验室里极难复现,因为实验室测试时App都是在前台运行,手机不会发参数更新请求。但用户的日常使用场景里,App切后台是家常便饭。一段录屏 + 一个btsnoop文件,比十份测试报告都有用。从那以后,我把"录屏 + HCI日志"列进了所有蓝牙项目的验收标准,任何反馈蓝牙"偶发断连"的一律先取证再排查。
3.5 录制素材之外还需要留意的点
录屏取证虽然厉害,但有几点容易踩坑:
- 别让手机系统省电优化把录屏App杀掉。一些Android手机上录屏工具在后台会被系统回收,导致录到一半断了。解决方法是尽量用系统自带的录屏(通常带有前台服务优先级),或者把录屏工具加入电池优化白名单。
- btsnoop日志要记得保存时间点。开启日志后会持续记录所有蓝牙数据,文件可能很大。复现问题后尽早把文件拷出来并标注时间区间,免得日志被新的数据覆盖。
- 不要只看手机端。如果设备端用的是经典蓝牙(SPP),就不存在btsnoop这个概念,这时候要看的是设备端日志里最后几个AT应答。HC-05模块在断开时会有返回状态码,比如
+DISCONNECT: 13,对应原因码0x13(远端用户终止连接)。这种细节在固件文档里都有,只是平时没人会去看。
四、"新旧批次对照"的烧录排查:为什么同样固件,新板子就是烧不进去
4.1 烧录失败可能和"批次"有关
第三个场景是烧录。做量产的人都有这种经历:同一个hex文件、同一个烧录工具、同一个操作员,旧批次板子怎么烧怎么成,新批次板子开始出现偶发失败。现象可能是:
- J-Flash连不上芯片,报"Cannot connect to target"。
- 能连上但擦除到一半失败,报错在特定地址。
- 校验失败,烧进去运行起来也不正常。
- 十块板子大概一两块失败,换个人去烧又成功了。
这看起来又是典型的"偶发bug",但如果把视角放到批次的维度上,它就是可复现问题了。我处理过太多"新批次烧录困难"的案例,核心排查思路就是标题里提到的"新旧批次对照"——把新批次板和旧批次板放在同一个测试环境下,逐项比对差异,直到找出真正导致差异的那个点。
4.2 对照排查的具体操作
我先说标准操作流程,这套流程不限芯片型号,STM32、ESP32、普通8位MCU都适用:
- 锁定对照组。拿至少三块旧批次正常板、三块新批次故障板(能从十块里挑出明显失败的更好)。把它们的生产批次号、贴片日期、固件版本号都记录清楚。
- 统一烧录环境。同一个烧录器、同一条烧录线、同一个上位机软件版本,依次去烧新旧板子各三次,记录每次的成功/失败时间点和报错码。注意顺序要打乱,避免烧录器发热导致的系统性偏差。
- 交换中间变量。把新板子放到旧板子原来放的位置、用旧板子的线材、烧录器去烧;反过来也做一遍。如果新板子不管换什么工具都失败,而旧板子怎么都能成功,那问题基本就在板端,而不是工具和环境。
- 逐项硬件比对。把新旧板子的原理图、BOM、PCB版本拉出来,重点看这几处:电源滤波电容的容值/封装、晶振负载电容、BOOT引脚上拉/下拉电阻、复位电路RC参数、芯片的丝印批次期。有改版就逐项核对改动项。
- 波形测试。示波器同时测烧录瞬间新旧板子的VDD跌落、复位引脚毛刺、时钟波形,看有没有明显不同。这一步经常能直接暴露问题。
- 破坏性对照。把新板子芯片焊到旧板子上、旧板子芯片焊到新板子上,交叉验证。如果"新板子+旧芯片"烧录正常,那问题就在新芯片的物料本身;如果"旧板子+新芯片"也正常,问题就在新板子的外围电路差异。
4.3 我在实际项目里定位到的那颗"电容"
拆解一个真实案例:某款基于STM32F103的控制板,新批次贴片后开始出现大概10%的烧录失败,报错集中在连接阶段。做了上面的对照法之后,发现换芯片交叉测试时,芯片没有问题,板子也没问题——同样的芯片装在旧板上能烧,装在几块新板上失败。最终波形测试发现,新板子在烧录器拉低位启动引脚的那个瞬间,VDD有大约300ms的跌落,幅度接近复位阈值。
查BOM后发现,新批次为了成本优化,把主控附近的100uF钽电容换成了一颗47uF的普通电解电容,而且封装尺寸不同导致布局位置偏移,去耦路径长了一大截。平时运行完全没影响,但烧录器在连接阶段会有一个比较高的瞬态电流需求,电源跌落刚好踩到了芯片内部复位门槛上。故障表现就是"烧录器连不上,但偶尔换根线又成功"——因为不同烧录线的阻抗不一样,瞬态电流的电压跌落程度不同,就产生了"偶发"的观感。
这个问题如果不用新旧批次对照,几乎不可能找到。因为它在正常运行时没有任何症状,而且新批次板的原理图和旧批次在逻辑上完全等价,只有对照法才会逼着你去逐一看BOM差异和布局差异。
4.4 烧录对照中的"伪变量"和"真变量"
做对照实验时,最大的坑是混淆了真正起作用的变量和无关变量。我给大家一个经验列表:
| 常见"伪变量" | 真实影响 | 排查时的正确做法 |
|---|---|---|
| 烧录器品牌不同 | 不同工具对电平时序容忍度不同,但这不是批次根因 | 统一品牌型号再对比 |
| 主板USB供电不同 | 影响烧录器供电,但不会只挑新板子出问题 | 用独立电源给烧录器供电,排除干扰 |
| 办公室温度变化 | 影响芯片时钟精度,尤其低温晶振起振慢 | 在恒温环境复测 |
| 操作员手法差异 | 夹子接触压力不同,会造成随机失败 | 换固定夹具,标准化压接压力 |
| 芯片丝印批号不同 | 有时芯片厂商内部改版(die revision)会影响烧录时序 | 记录丝印批次,联系原厂确认 |
如果你的对照实验里同时混入多个变量,结论就会失真。比如"新板子 + 新烧录器 + 新线"全换了才成功,你可能以为是板子的锅,实际上只是烧录器线的问题。所以我一再强调:一次只动一个变量,这是对照法唯一的铁律。
4.5 从烧录排查延伸出去的"批次意识"
"新旧批次对照"这个方法,往大了说是一种批次管理思维。做硬件的都知道,BOM、PCB版本、物料批次一变,整条线的行为和预期都要重新验证。烧录只是第一道关卡,后面可能还会有"新批次板子低功耗电流偏大""新批次板子蓝牙连接距离变短"之类的问题冒出来。
我的建议是:从试产开始,就给每一批板子建档。记录PCB版本、主要芯片批次、关键物料变更点、烧录良率、首件测试数据。将来一旦出现"只有某批板子有问题"的反馈,这个档案能让你把排查范围从全系统迅速缩小到具体的批次差异上。没有档案,遇到这种问题就只能靠拆板、看丝印、回忆采购记录,效率会差非常多。
五、三种排查方法背后,其实是同一条逻辑
5.1 偶发问题 = 临界状态 + 触发窗口
串口假故障、蓝牙偶发断开、烧录随机失败,表面上是三个完全不同的场景,但它们的本质结构是一致的:
系统里某个参数处在"正常偏下"的临界状态,平时不会暴露,但只要出现某个短暂的触发窗口(一次电流波动、一次射频冲突、一次瞬态负载),问题就会被点燃。
换机排除法,是在横向移除临界参数——我把可能处在临界状态的线、口、电脑全部换掉,观察问题是否消失。
录屏取证法,是在纵向固定触发窗口——我用录屏和日志把"断连前最后几秒"的完整时序抓下来,让触发窗口从不可见变成可见。
新旧批次对照法,是在利用自然实验锁定根因——新批次和旧批次之间的BOM、PCB、芯片版本差异,就是系统里被植入的一个现成变量,对照它就能快速找到分歧点。
5.2 一套可以复用的排查清单
把这三招合起来,我总结了一份适用于绝大多数偶发硬件问题的排查清单。遇到任何"时好时坏"的bug,建议按这个顺序走一遍:
- 先取证,再动手。能录屏的录屏,能抓包的抓包,能打日志的打日志。第一时间把"偶发现象"变成"可回放的数据"。这步做不好,后面全是瞎碰。
- 先链路,再设备。线材、接口、供电、地线,这些最基础的链路先排除掉。不要一上来就怀疑固件或硬件设计。串口的案例里,一根线的成本只有几块钱,但能坑掉整周的排查时间。
- 先对照,再假设。如果有新旧批次、有多台设备、有多个环境,先把它们放在一起做变量对照,而不是急着在原理图上猜。对照试验的结果会直接指向嫌疑区域。
- 先记录,再修复。找到嫌疑方向后,不要马上改代码或改板子,先做适量的重复实验把误判率压到可以接受的范围。偶发问题的"偶发"属性,决定了你没有足够样本数就不能下结论。比如烧录失败,至少复现五次以上再总结规律。
- 修复后要回归验证。修改之后,用旧的故障环境、旧的触发操作步骤完整回归一遍,确认问题确实消失。同时跑一次长时间连续测试,确认没有引入新的临界状态。
5.3 最后再分享一点个人体会
有人会问:"这些方法看起来都很基础,为什么不是每个人都这么做?"我的体会是,偶发bug最消耗人的不是技术难度,而是心态。它让你觉得问题不在你的掌控之中,于是容易病急乱投医。但恰恰是这种时候,最需要我们把排查动作变慢、变标准、变朴素。换线、录屏、对照,这些事情一点都不酷,但它们在无数个深夜救过我。
我现在的习惯是,凡是经手的产品,都会在文档里留一个"疑难问题排查记录"分区,把每次偶发问题的取证素材、排查链路、根因和修复方案都写进去。下次再遇到类似问题,先翻文档、再跑流程,效率会高很多。做硬件这行,经验不是凭空来的,全是这些不起眼的"笨办法"堆出来的。希望这篇梳理能给你一些当抓手的东西,下次再碰上"偶发"两个字,心里先有个章程。