多彩编程 多彩编程MZPH · CODE BLOG
ARTICLE DETAIL

文章详情

深耕前端与后端开发技术的一线实战笔记与踩坑复盘。

嵌入式偶发Bug排查实战:串口假死、蓝牙断连与烧录失败的定位思路

嵌入式偶发Bug排查实战:串口假死、蓝牙断连与烧录失败的定位思路 深夜的实验室里你最怕听到的一句话是什么不是“板子烧了”而是“哎刚才还好好的我啥也没动它就不行了”。在嵌入式开发里我们管这叫偶发 Bug。它不像必现问题那样可以拿个逻辑分析仪一挂、复现、抓波形、定位、修复一条龙走完。偶发问题更像鬼打墙——它确实存在但你想请它出来聊聊的时候它偏偏不出现。串口偶尔收发失败、蓝牙莫名断开、烧录十次失败两次这类问题几乎每个搞硬件的工程师都遇到过。这篇博文我想借着三个我实际排查过的典型案例聊聊处理偶发 Bug 的思路。一个是串口“假故障”的换机排除一个是蓝牙断开的录屏取证一个是“新旧批次对照”的烧录排查。这三个案例串起来正好对应偶发问题排查里最重要的三件事隔离变量、保留证据、对照差异。如果你也正在被某个“偶尔出现、复现不了、看日志又一切正常”的问题折磨希望这篇内容能帮你理清头绪。1. 偶发问题的本质它一定不是无缘无故的先说个扎心的事实在软硬件系统里不存在真正的“无缘无故”。任何偶发现象背后都有一个确定的诱因只是这个诱因和现象之间的因果关系被某些隐藏的变量拉长了、打乱了。比如串口偶发乱码可能和温度漂移有关蓝牙偶发断连可能和某个特定场景下的内存越界有关烧录偶发失败可能和 USB 供电的瞬间跌落有关。现象是偶发的但原因一定是确定的只是我们还没有把它隔离出来。所以偶发问题的排查逻辑本质上就是“降维”把不确定的东西一步步变成确定的。要做到这一点需要三样工具隔离、取证、对照。隔离是换机排除的核心思路取证是录屏、抓包、日志这些手段的核心价值对照是新旧批次、新旧版本、新旧环境之间的差异分析。这三个工作合在一起就能把偶发问题从“鬼故事”变成“工程问题”。还有一个心态问题要先摆正别急着怀疑是玄学。我见过很多工程师排查两天无果之后开始怀疑是不是芯片体质差、是不是天线布局不行、是不是供应商偷工减料。这些怀疑不是不能有但它们的优先级应该在确认完所有软件逻辑、电气环境和操作流程之后。实际上我排查过的偶发问题里最终原因大部分都是很土的东西——配置寄存器时的时序竞争、接触不良、供电纹波、甚至是一根劣质杜邦线。真正“芯片本身有问题”的案例占比非常低。2. 串口假故障换机排除要换对“机”才行2.1 先别急着换芯片串口也有“假死”有段时间我在调一块 STM32 的板子UART1 用来和上位机通信现象很诡异板子跑着跑着串口就不输出了上位机发的命令也完全没反应。最开始我以为是程序跑飞挂了调试器看程序还在正常运行标志位、状态机全部正常就是串口外设像被人点了暂停键。复位板子之后一切恢复正常但跑几分钟到几个小时不等又会复现。我当时的第一反应是查代码检查了串口中断服务函数、检查了 DMA 的配置、检查了是否有地方误关了串口时钟。查了两天代码看下来完全没有问题。后来我怀疑是不是芯片的串口外设本身有问题于是换了一片全新的 STM32重新烧录固件结果问题依旧。直到这里我才想起来做一件很基础的事情换机排除。不过这里的“机”不是指芯片而是指上位机——也就是那台跑着串口调试助手的电脑。我找了一台笔记本电脑装好 USB 转串口驱动把板子的 TX、RX、GND 三根线接过去跑了整整一个下午串口通信一次都没断。这时候我基本断定问题出在原来的上位机环境而不是板子本身。2.2 串口假故障的完整排查步骤那次经历之后我总结了一套串口偶发问题的排查顺序核心原则就是先软后硬、先易后难、先上位机后下位机。第一步确认供电和接线。很多串口偶发问题不是串口本身的问题而是共地没做好。串口通信的 RX、TX 电平都是对地而言的如果板子和上位机之间没有可靠的共地地电位漂移就会造成偶发乱码甚至通信中断。用万用表量一下板子的 GND 和 USB 转串口模块的 GND 之间电压如果超过 0.1V 就要注意了。第二步换一个 USB 口。听起来像废话但“USB 口供电不足”导致的串口假死在现实里出现的频率超乎想象。有些电脑前面的 USB 口经过延长线和 Hub供电和信号质量都不如主板原生 USB 口。把 USB 转串口模块换到机箱背面、或者换一个直连主板的 USB 口问题可能直接就消失了。第三步换一台电脑或者换一个 USB 转串口模块。这一步其实就是标题里说的“换机排除”的实操版本。如果你的板子上用的是 CH340 或者 FTDI 芯片的方案建议换一台电脑试试的同时也换一个不同芯片方案的 USB 转串口模块。因为不同芯片在驱动实现、缓冲区管理上是有差异的有些国产低成本方案的模块在高波特率、大数据量下会有隐性丢数据的问题。第四步关注串口软件本身。串口调试助手这类上位机工具看着功能简单但底层实现差别很大。有些工具在接收缓冲区溢出时表现是直接丢包有些是界面卡死有些则会导致 USB 转串口芯片的接收缓冲区卡住整个串口“假死”。我排查那个问题时最后找到的原因就是原来的电脑上装的串口助手版本太老大流量数据下接收缓冲区会溢出溢出之后驱动层就罢工了。换了一个支持更大缓冲区的串口调试工具问题彻底消失。2.3 换机排除的常见误区“换机”这个手段本身很简单但在实际操作里很多人会换错方向。我把常见的误区列一下。第一个误区是只换设备不换环境。把板子带到另一张工位上测试换了一台电脑但用的还是同一个 USB 转串口模块、同一根 USB 线、同一个串口调试助手。这样的“换机”换得很不彻底因为真正的问题可能就藏在那个模块或者那根线里。要换就成组地换电脑、USB 线、串口模块、串口调试助手全部换掉保险起见连串口波特率都换一个档位测试。第二个误区是忽视时间维度。偶发问题需要“挂机测试”才能暴露。我就见过有人换了一台电脑测了五分钟觉得没问题就宣布排除了。对于偶发问题5 分钟的测试基本没有说服力最好挂机跑至少 2 个小时如果条件允许跑一个通宵更稳妥。我在实际工作中会把挂机测试做成标准动作设置好自动发送、自动记录日志跑一晚第二天看日志有没有断点。第三个误区是不记录现场。换机排查的时候每换一个变量就该记录一次当前状态。哪台电脑、哪个 USB 口、哪个串口工具、什么波特率、是否设置流控全部要有个记录。否则排查了三天最后你可能根本说不清是换了什么之后问题才消失的。这里还要特别提醒一句串口的流控CTS/RTS 硬件流控是很多偶发问题的隐形元凶。如果你的串口配置里打开了硬件流控但实际连接没有接那两根流控线那串口就可能出现“偶尔卡住、复位才能恢复”的现象。我的建议是在排查串口偶发问题时先把流控关掉用最简的最小化连接TX、RX、GND 三根线来跑测试。3. 蓝牙断开的录屏取证让偶发问题“现形”3.1 蓝牙断连为什么难排查蓝牙类产品有个特点无线链路的稳定性受环境干扰影响非常大。2.4GHz 频段里有 WiFi、蓝牙、无线鼠标、微波炉甚至 USB 3.0 接口的电磁辐射都能干扰它。这就导致蓝牙偶发断连问题出现时所有人第一反应都是“是不是信号被干扰了”。但根据我的经验真正因为干扰导致偶发断连的情况在室内短距离场景下并没有想象中那么多。更多的蓝牙断连问题反而出在协议栈逻辑、电源管理和 Android 系统权限这些“软件层面”的东西上。这也是为什么标题里强调“录屏取证”——因为蓝牙的连接状态、断开瞬间、断开前后的用户操作这些信息是推断断连原因的关键证据而录屏是最好的记录方式之一。3.2 录屏取证到底要录什么我做蓝牙相关调试时遇到偶发断连问题第一件事就是让测试人员或者自己拿着手机打开录屏功能完整记录从连接成功到断开出现的整个过程。这一步看着简单但录屏取证有四个细节要注意。第一个细节必须在录屏里同步显示时间。断连问题的排查需要把时间轴和日志对应起来。手机屏幕上方一般都有时间显示录屏时要把状态栏露出来不要全屏沉浸式录屏把时间遮住。如果手机状态栏不显示秒级时间可以考虑在屏幕上额外挂一个悬浮时钟确保录屏素材里能看到精确到秒的时间点。第二个细节录屏要覆盖断开前后的操作。蓝牙断连往往有触发条件比如用户从桌面回到 App、手机息屏再亮屏、切换 WiFi、播放视频这些操作都可能导致蓝牙栈状态变化。录屏的目的就是记录这些“上下文”所以在整个测试期间不要只盯着蓝牙设置页面看要把手机的正常使用操作也录进去说不定断开的触发点就藏在这些操作里。第三个细节记录蓝牙信号强度RSSI。在蓝牙设置页面或者借助厂商调试工具把 RSSI 数值显示在屏幕上录屏的时候一并录进去。如果断开瞬间 RSSI 已经掉到 -90dBm 以下那大概率是距离或环境问题如果 RSSI 一直很好却突然断开那就更有理由怀疑软件逻辑。第四个细节同时记录下位机的日志。手机在录屏开发板这边同步用串口或者 J-Link RTT 输出日志把两边的记录按时间轴对齐。这样断开时就能看清上位机手机和固件下位机各自的状态。有一次我排查一个 HC-05 蓝牙模块的断连问题就是靠这种对齐方式发现手机端显示断开的时间点比模块串口输出的“断开连接”事件早了 30 毫秒。这个毫秒级的差异直接推翻了我最初“模块主动断开”的假设转而怀疑是手机蓝牙协议栈主动发起了断开请求。3.3 一次真实的蓝牙断连定位过程那次排查的产品是一个基于 ESP32 的蓝牙设备手机 App 通过 BLE 连接控制。用户反馈设备连接后偶尔会在几分钟内断开重新连接又能用一段时间。既然是偶发问题我先让现场的测试工程师用录屏记录了一次完整的复现过程。回看录屏时发现一个规律断开几乎都发生在手机息屏之后的一小段时间里。于是我去查固件逻辑发现我们的 ESP32 为了省电在连接空闲时会进入modem sleep模式。这个模式下WiFi 和蓝牙共存的时序会被压缩如果此时手机正好发来一个长数据包ESP32 因为睡眠模式唤醒不及时就会丢包进而触发连接超时断开。这就解释了为什么录屏取证是决定性的如果没有录屏我根本不知道断开和息屏之间的时间关联最多只能在固件里加一堆日志盲猜。有了录屏这个时间锚点再结合 ESP32 的功耗日志、蓝牙日志很快就把问题缩小到了功耗策略上。修复方案也简单在 BLE 连接保持期间关闭modem sleep或者把睡眠唤醒时间调得更激进一些。录屏这个手段本质上是给偶发问题加了一个“目击证人”。有了这个证人你就不用单靠日志去脑补现场了排查效率和准确率都会提升一大截。我这里再强调一下不是所有断连都是干扰。做蓝牙调试手里有证据再下结论永远比靠猜更可靠。4. “新旧批次对照”的烧录排查用对照组说话4.1 烧录偶发失败不是稀有事件第三类典型案例是烧录偶发失败。这个问题在量产产线上最要命因为产线烧录讲究节拍偶尔失败一次就得停下来人工处理。在研发阶段烧录偶发失败也很烦特别是你用 Keil、STM32CubeProgrammer 或 esptool 烧录时“Verify 失败”“连接超时”“芯片读保护”这些报错随机出现最折磨人。我遇到的一个典型情况是这样的一块 ESP32 开发板用 esptool 烧录固件十次里有那么一两次会在擦除 Flash 阶段报错报错信息大致是A fatal error occurred: Timed out waiting for packet header。当时我的第一反应是检查烧录环境USB 线换了一根、USB 口换了一个、波特率从 921600 降到 115200问题还是偶发存在。后来我做了件很关键的事把手里另一块同型号但不同时间采购的 ESP32 开发板拿出来用完全相同的烧录流程去烧。结果发现新采购的那块板子连续烧录二十次都是成功的而旧批次板子就会偶发失败。这个“新旧批次对照”的结果一下子就把问题的焦点从“烧录流程”拉到了“板子本身的硬件差异”上。4.2 批次对照的具体操作方法所谓“新旧批次对照”说白了就是给排查对象找一个对照组用对比的方式把“可疑变量”拆出来。但这个方法要做出效果有三条操作细则必须遵守。第一对照组必须足够“干净”。你要找一块确定没有问题的板子做对照这块板子的同型号版本、同批次、同方案最好是刚从包装里拆出来、没有经过反复焊接和频繁烧录的。如果你手里只有一块可疑板子、一块同样可疑的板子那两者之间的对比结果也说明不了太多。第二要确保烧录流程、烧录工具、烧录环境完全一致。换板子不换工具烧录软件版本保持一致USB 口保持一致电脑保持一致。这样你才能认定成功与失败的差异是板子带来的而不是别的变量引入的。第三对照不是烧一次两次就完事。偶发问题嘛一次烧录看不出什么至少要每个批次各烧录 20 次以上把成功/失败的结果做个统计。如果旧批次失败 3 次、新批次 0 次这个对比就有说服力了。我还习惯在同一个条件下做交叉验证把旧批次的芯片拆下来换到新批次的板子上烧录如果失败跟着芯片走说明是芯片差异如果失败跟着板子走说明是板子外围电路问题。那次 ESP32 的案例后来我用热风枪把旧批次的 ESP32 模块拆下来换到新批次板子上烧录失败问题也跟着芯片走了。再进一步用显微镜观察发现旧批次的 Flash 芯片和 ESP32 模组之间有一处虚焊焊点边缘有细微裂纹。烧录时大电流通过虚焊点接触电阻增大导致 Flash 供电跌落擦除操作就超时了。补焊之后旧批次板子的烧录失败问题也消失了。所以批次对照帮我定位到的最终原因是一个很隐蔽的硬件工艺问题而不是芯片本身“体质差”。4.3 烧录排查不可忽视的几个软性变量在批次对照之外烧录偶发失败还有几个“软性变量”值得记录在案。一是供电烧录器或开发板的 USB 供电在擦写 Flash 时需要比较大的瞬间电流如果供电能力不足烧录必然偶发失败。我实战中会用稳压电源给板子单独供 5V 电同时把 USB 只用于数据通信这种方式能排除大半的烧录供电问题。二是目标芯片的复位时序尤其是 STM32烧录时 BOOT0 引脚和 NRST 引脚的电平时序很敏感如果调试器在连接的时候复位时序不对就会随机出现 “Cannot access target” 之类的报错。三是烧录工具的驱动缓存老版本的驱动和新版本的操作系统之间可能因为缓冲机制不一致导致偶发通信失败。这里还要多说一种情况烧录文件本身的不确定。比如你每次编译出的固件大小有微小变化或者编译选项里开了不同的优化等级烧录行为也会不一样。我在排查烧录偶发问题时会把每次烧录的固件做个 MD5确认同一个版本的固件是否一致。如果 MD5 都对不上那就别谈后面的批次对照了先把构建流程搞稳定再说。5. 偶发 Bug 排查的通用方法论5.1 固化变量是第一原则三个案例讲完了你会发现背后其实是一套通用的排查哲学偶发问题不可怕可怕的是排查时变量一直在变。你在改代码的同时换了电脑、又换了供电、还顺手调了波特率就算问题解决了你也永远不知道是哪个动作解决了它。所以我的做法是在动手排查之前先花十分钟把当前现场“凝固”下来。记录当前的固件版本、硬件版本、工具版本、操作系统、连接方式、复现频率。然后再开始逐项调整每调整一个变量就重新观察一段时间。这就像做科学实验你不可能同时改变两个条件还指望得出可靠的结论。5.2 取证手段优先级针对不同的偶发问题类型取证的优先级也不一样。我平时会按这个顺序来首先是软件日志。无论是串口、蓝牙还是烧录只要系统能吐日志先加日志。日志的粒度要细最好能把关键状态机的跳转、外设事件的进出都打出来。加了足够的日志之后再去复现往往问题自己就露马脚了。其次是录屏/录波形。凡是有 UI 交互、有状态显示的场景录屏是最直观的证据。对于硬件信号层面的问题则是用示波器/逻辑分析仪抓波形。蓝牙断开抓 HCI 日志串口异常抓 UART 波形烧录失败抓复位时序和供电波形。最后才是换硬件、换芯片、换批次。硬件替换是“重武器”效果好但失败成本高——换了芯片之后如果问题还在你可能还得花大力气把原芯片焊回去。所以我建议不到万不得已不要先动硬件。先用日志和录屏把现场变成看得见摸得着的东西。5.3 一个实用的排查顺序表我把上面三类的排查思路整理成一个可以照做的顺序表放在项目组内部用了很久也分享在这里。场景首先检查其次排查最后对照串口偶发异常共地、供电、USB口、串口工具缓冲区流控开关、驱动版本、波特率误差换电脑、换USB转串口模块、换板子蓝牙偶发断开录屏记录整体过程、功耗模式信号强度RSSI、连接参数、协议栈日志换手机对照、换模块批次、抓HCI包烧录偶发失败固件MD5、USB线/口、供电稳定性复位时序、BOOT引脚电平、驱动版本新旧批次板卡/芯片对照交叉测试这个表的核心还是“从软到硬、从证据到替换”。先收集证据再做有依据的替换。这几个案例带给我的一些体会做嵌入式这些年我越来越觉得排查偶发问题的能力才是区分初级工程师和资深工程师的一道分水岭。初级工程师遇到偶发问题第一反应是“改代码试试”改几次没效果就想换硬件。而有了足够经验的工程师会先耐下心来做取证、做隔离、做对照。串口的换机排除、蓝牙的录屏取证、烧录的批次对照单看都是很朴素的手段但组合起来就是一套能应对大部分偶发问题的方法论。当然偶发问题里永远有我们没见过的花样。我也依然会遇到排查几天没头绪的情况但现在的我不再会觉得那是“玄学”而是会反问自己是不是我还没有找到那个被忽略的变量这种心态可能比任何具体的技术手段都更重要。下次遇到偶发 Bug 的时候不妨先泡杯茶把现场录下来把板子换个环境跑一跑把新旧批次放在一起对照一下你会发现那些看起来神出鬼没的问题其实都藏在一个合情合理的角落。
返回列表