单片机控制板异常排查六步法:上电无反应与运行死机
前几天一个做设备维护的朋友打电话过来说现场一台控制器又“抽风”了上电没反应指示灯不亮偶尔上电能亮跑一个多小时就死机断电重启又能跑一阵。他怀疑主控芯片坏了换了一片还是老样子。其实干这行久了都明白这类问题七八成不在芯片本身而在供电、时钟、复位、程序加载这些最基础的环节。这篇就把我这几年代码和烙铁里泡出来的排查经验整理成一套六步法专门对付“上电没反应、运行中死机、现场间歇性抽风”这三类高频单片机控制板异常给和我一样在项目现场和Lab里两头跑的工程师做个参考。1. 第一步电源系统——上电没反应先量电压再换芯片1.1 用万用表测到的“正常”电压可能是假象很多新手拿到一块不上电的板子第一反应就是拿万用表量一下3.3V或者5V有没有量到数值就认为供电没问题转头开始怀疑芯片。这个思路坑过不少人。有一次我接手一块板子万用表量3.3V完全正常但就是不开机后来用示波器一看波形上叠了一大堆纹波峰峰值超过300mV。对3.3V供电的STM32来说这个纹波足以让内部复位电路反复触发芯片基本上处于“一直在启动”的状态表现出来就是完全没有反应。万用表测到的是直流平均值它把纹波给“平滑”掉了所以看起来是正常的3.3V。真正要看的是纹波和瞬态跌落一般3.3V数字电源的纹波最好控制在50mV以内5V系统控制在100mV以内而且满载时的跌落不能超过额定电压的5%。这个瓶颈通常出在滤波电容上。陶瓷电容用久了会容量衰减电解电容则会ESR升高两者都会让电源动态响应变差。我惯用的排查顺序是先目测滤波电容有没有鼓包、漏液、脱焊再拿示波器探头靠在芯片电源引脚旁的电容两端要测纹波必须用近端接地弹簧探头不能用普通鳄鱼夹地线否则测出来的全是空间耦合噪声。如果纹波超标先把主滤波电容并联一个10uF到100uF的低ESR电解电容再把芯片电源引脚旁的0.1uF高频去耦电容换成全新的大部分问题能当场解决。1.2 看电流上电瞬间的功耗变化说明很多问题除了量电压更要看电流。我调试单板时习惯用一个带电流显示的稳压电源先把电压设到板子额定值限流调小一点比如预限流100mA再上电。这样即使板上有短路也不会烧到后面的器件。正常的上电电流曲线是电源刚接通时有一个充电浪涌然后跌落下来再随着主时钟启动和程序初始化爬升到一个稳定值。如果上电后电流直接顶到限流值基本可以判断是电源短路。这时候别急着换芯片先把所有能拆的跳线帽拆掉用万用表电阻档测电源对地的阻值。我一般在板上找一个电源引脚的测试点量对地电阻如果小于10欧姆那肯定有硬短路重点检查钽电容和焊接时造成的锡连。如果电流为零说明电源链路根本不通优先怀疑保险丝、自恢复保险、电源开关、电源插座虚焊以及稳压芯片的输入输出引脚是否真的焊上了。还有一种比较隐蔽的情况电流很小比如十几毫安但单片机还是没有工作。这很可能是单片机进入了复位状态或者晶振没起振但很多人会误认为是芯片坏了。所以我常说电流是比电压更诚实的指标它能告诉你电路到底有没有在真正运行。1.3 电源时序与上电斜率被忽略的隐性复位危机现在不少控制板都是多路电源供电比如5V进来先经过一级降压到3.3V给IO和存储再经过一颗LDO降到1.2V给内核。芯片的数据手册里一般都会给上电时序要求有的要求内核电源先于IO电源有的要求两者相差不能超过多少毫秒。如果时序反了芯片里的逻辑电路可能进入一个闩锁状态虽然电压都量得出来但芯片就是不工作。还有一种常见情况是上电斜率太慢。电源软启动电容选得太大或者前级有缓慢爬升的缓启动电路导致VDD在几百毫秒内才从0V爬到3.3V。芯片内部的PORPower-On Reset电路在这种情况下可能一直不触发或者触发不完全于是程序就不会从正确的起始位置执行。解决方法是外加一颗专用的电源监控芯片比如MAX809/TCM809或者用RC复位电路把复位脉冲延长到几十毫秒以上保证在电源稳定后芯片才被释放出来运行。我踩过的一个坑是这样在一个产品上用了一颗带软启动的升压芯片给单片机供电为了减小开机浪涌把软启动电容从1nF增大到22nF结果上电斜率慢到了十几毫秒每伏几台样机出现偶发不开机。后来给单片机复位脚加了一个RC延时上电电路问题立刻消失了。所以说电源不只是“有没有电”的问题还得关心它是怎么“送进来”的。2. 第二步时钟和复位——芯片的心跳都停了别指望程序能跑2.1 判断晶振是否起振的正确姿势如果供电正常电流也合理但单片机还是没反应下一步就是查时钟。很多单片机没有内部振荡器或者出厂时内部RC配置不对外部晶振不起振芯片就完全“罢工”。判断晶振是否起振示波器探头直接接到晶振引脚上。无源晶振正常工作时两个引脚上应该能看到一个正弦波频率等于标称值幅度一般在VDD的1/3到2/3之间。注意不要用10x探头以外的普通探头去点探头电容会影响振荡条件本来就快不起振的晶振被你一测直接停振。如果示波器测不到波形先查两个匹配电容就是晶振引脚对地的两个负载电容。常见值在12pF到22pF如果电容焊错、漏焊或者虚焊晶振可能不起振。再查晶振本体有没有破损、两端是否都与焊盘可靠接触。如果是STM32这种支持外部时钟的芯片还要检查软件里RCC配置是不是把HSE使能了有些复制来的初始化代码根本没开外部时钟全靠内部RC跑着而内部RC精度不够串口波特率就偏了表现出来就是“程序在跑但外设不正常”。2.2 复位引脚时序不够上电等于白上复位电路是另一个高频坑。芯片的复位脚在正常情况下应该处于电平释放状态比如STM32的NRST是低电平复位平时应该被电阻拉到3.3V上电瞬间被电容拉低一段时间然后电容充电到高电平芯片才开始运行。如果复位电容太小比如只有100nF那复位脉冲宽度可能只有几百微秒。对供电斜率很慢的板子来说可能电源还没稳定复位就已经结束了芯片在一个混乱的电压下开始执行指令跑飞概率极高。我一般把复位电容放在1uF到10uF配合10K到100K的上拉电阻RC时间常数在几十毫秒量级确保电源稳定后芯片再启动。还有一个容易忽略的点复位脚上的外部干扰。如果复位信号线走线过长或者和继电器、电机驱动线挨得太近现场干扰会直接把复位脚拉低造成芯片莫名重启。表现出来就是“运行中突然死机然后自己恢复”。排查时可以拿示波器长时间捕捉复位脚波形看看有没有毛刺。有的话在复位脚上对地加一个小电容滤高频或者加强复位芯片会好很多。2.3 用复位原因寄存器定位“假死机”很多单片机都有复位原因寄存器就是用来记录上一次复位是谁触发的。比如STM32的RCC_CSR寄存器里可以读到上电复位、外部复位、看门狗复位、软件复位等标志。死机后重新上电程序先读这个寄存器就能判断这次“死机”到底是上电复位还是看门狗复位。我习惯在系统初始化最开头加一段代码把复位标志保存到备份寄存器里然后等程序正常运行后再通过串口或者日志打出来。现场人员看到日志里写着“上次复位看门狗”那就说明软件大概率跑飞了如果写着“上次复位上电”那可能就是电源掉电或者接触不良。这一步排查成本极低却能把死机问题的范围缩小一大半。如果芯片不支持复位原因寄存器也可以通过一个“软复位计数器”来近似判断在备份RAM里存一个自增变量每次启动加一如果发现短时间内启动次数暴增那一定是系统在被反复复位。这个技巧在51、AVR等老平台上同样适用只要有一个掉电不丢失的RAM或者EEPROM区域就行。3. 第三步程序加载与配置字——代码没进去板子当然不动3.1 下载失败不一定是芯片锁死先查这几项上电没反应还有一种非常低级的原因芯片里根本没有程序或者程序是旧的。很多人用的单片机是自己焊接的第一次烧录时却失败然后就以为芯片坏了。实际上下载失败大部分是通信时序和启动条件的问题。拿STC系列来说它需要冷启动下载先点下载按钮再给目标板上电进入ISP引导区。如果你用的是复用串口引脚的下载器还要确认目标板供电电压和下载器电平兼容。STM32则要检查BOOT0引脚从串口下载需要BOOT0拉高下载完再拉低复位运行。很多板子把BOOT0直接接了个按键就是要配合下载用的结果现场人员不知道以为死机了反复上电也没用。如果下载器能识别到芯片但擦除或写入时报错优先怀疑目标板供电不足。USB转串口模块的3.3V输出电流一般就几十到几百毫安带不动整块板。正确做法是目标板单独供电下载器只连着TX、RX和GND。3.2 配置字/熔丝位设错程序可能直接跑飞51、AVR、PIC这类有配置字或熔丝位的芯片问题就更隐蔽。熔丝位设错了芯片可能启用了错误的时钟源或者把复位引脚配置成了IO口导致调试器连不上、程序不跑、不能复位。我处理过一台设备AVR单片机可以下载程序但上电就是不运行。最后用编程器读熔丝位才发现时钟源被配置成了外部晶振而板上根本没有焊晶振用的是内部RC。改回内部RC后一切正常。所以拿到一块“没反应”的板子一定要先用编程器把配置字读出来看一眼特别是时钟源、看门狗使能、BOD掉电检测这几个选项。这里也提醒一下STC的ISP下载软件里有很多选项比如“上电复位使用延时参数”“在程序区结束处添加重要测试参数”“下载时顺便修改硬件选项”。如果不懂默认设置就好不要乱勾尤其是“允许低压复位”和“看门狗”相关的选项配置错了会让芯片在低电压或程序跑飞时反复复位看起来就像死机。3.3 “能编译但能跑”背后的C语言陷阱程序能下载成功也不代表代码就没问题。如果芯片一上电就死机或者运行一会就死机软件侧的C语言陷阱也逃不掉。最常见的就是变量类型和volatile关键字。中断和主循环共享的变量如果不用volatile修饰编译器在开优化后可能把变量读到寄存器里缓存导致主循环永远看不到中断里更新的值。比如用标志位判断事件中断里置1主循环里while(!flag)一直死等没有volatile的话可能永远等不到看起来就是死机。还有位域操作。很多人喜欢用结构体位域来表示寄存器状态但位域操作不是原子的。如果主循环在读写一个位中断也在改同一个字节的其他位就可能出现数据丢失。这个问题的经典表现是程序偶尔“抽风”状态不对但重新初始化又能跑。解决方法是把整个字节读出来改完再写回去或者用临界区保护。还有一个容易被忽略的是堆栈。后面第四步专门展开讲但这里先提醒一句如果你的板子在加功能、加中断之后开始偶尔死机大概率是堆栈不够或者中断嵌套把栈冲爆了。这跟芯片本身没关系是软件的问题。4. 第四步运行中死机的软件侧——中断、堆栈、看门狗的相爱相杀4.1 中断优先级配置不当堆栈说爆就爆运行中死机而且没有规律软件原因里排第一的就是中断和堆栈问题。单片机的中断嵌套是有深度限制的。如果所有中断优先级都一样那么中断处理中再来一个中断就只能排队等这本身不会爆栈。但如果我们配置了抢占优先级比如高优先级中断可以打断低优先级中断而低优先级中断的处理函数里又开了耗时很长的操作比如软件延时、I2C时序那就可能有多个中断嵌套在一起每嵌套一层就要压入一整套寄存器堆栈空间很快耗尽。我见过一个案例低优先级的中断里调用了printf通过串口打印调试信息突然来了一个高优先级定时器中断然后又嵌套了串口中断三层嵌套直接冲爆了默认的1K栈空间程序直接跑飞。从那以后我给自己定了一条规矩中断里只做置标志、清标志、读数据寄存器这类短操作所有耗时的事情全部放到主循环里处理。如果确实需要中断嵌套务必要估算好最大嵌套层数然后在链接脚本或启动文件里把堆栈空间加大至少预留2KB以上。用STM32的话在startup文件里修改Stack_Size不要把栈顶只压在RAM的末尾要给栈留出至少20%的余量。4.2 堆栈溢出怎么提前发现而不是等死机堆栈溢出是那种“没死之前完全正常死了之后又复现不了”的恶心问题。要提前发现可以在程序初始化的时候把整个栈空间填充成一个特殊值比如0xA5A5A5A5然后跑一段时间或跑完一轮功能逻辑再用调试器查看栈尾部的这个特殊值还剩多少。如果被覆盖了大量区域说明栈底已经顶到很危险的位置了。这是我在实际项目里用的一个小技巧定义一个大数组作为栈然后把栈指针指到数组末尾每跑一次主循环就检查数组前几个字节是不是还是0xA5A5A5A5。不是的话就拉响一个警告把当前栈指针值和最近几次调用的函数地址存进日志。这个“软件水位计”帮我抓到了好几次隐藏很深的堆栈溢出。另外很多编译器的链接脚本默认把栈放在RAM末尾如果RAM总共只有4KB而代码里的全局变量很大再加上堆栈和堆就可能在中间撞车。解决方法是明确声明栈的起始位置或者在编译器设置里缩小堆大小把空间让给栈。反正对嵌入式单片机来说malloc几乎用不到堆设成最小甚至0都可以。4.3 看门狗喂不对程序再稳也扛不住看门狗是双刃剑。它本来是为了防止程序跑飞但如果喂狗逻辑写得不合理反而会频繁复位系统看起来就像“死机自动重启”。很多人图省事在主循环里喂狗结果某个分支因为等待外设超时花了比看门狗周期更长的时间虽然系统并没有死机只是卡在一个操作上看门狗已经超时复位了。这种情况下程序会反复复位现场就是“上电能跑跑一会自己重启”。正确的做法是看门狗只用来检测“系统完全失控”所以在主循环里喂狗还不够最好在任务调度器的空闲任务里喂或者甚至在更底层的定时中断里喂。不过也要防止这种极端情况中断一直在跑但主循环已经死透了这样喂狗也停不下来。比较实用的做法是“两级看门狗”定时器中断里清一个软件计数器主循环里再清另一个只有两个都被清掉硬件看门狗才不会被触发。这样既能检测主循环卡死又不会被单任务超时误伤。喂狗时间窗口也要算好。假设看门狗超时300ms那么喂狗间隔建议在100ms到200ms之间还要保证在最差情况下最长的中断禁用段最长的循环分支也能在超时前喂到。不能把一个刚能跑通的喂狗间隔直接上量产你要考虑外设通信的最差延时比如I2C挂久了EEPROM写一页最坏可能要几十毫秒。4.4 volatile和共享数据保护看不见的数据竞争中断问题里还有一个经常让人挠头的现象主循环里轮询一个非volatile标志结果明明中断已经置位了主循环就是看不到。这类问题常常只在开优化之后出现不开优化怎么跑都正常。一个典型例子是我之前做车载控制器时速度采样中断里更新一个计数变量主循环里用这个变量去计算速度。开了-O2优化后主循环第一次读取被编译器放进了寄存器之后一直用寄存器里的旧值不管中断怎么改内存主循环都用不上导致速度显示卡死偶发归零。后来给变量加上volatile问题立刻消失。但也要注意volatile只能保证“每次都从内存重新读取”不能保证“多字节读写是原子的”。如果主循环里要读取一个32位的共享变量而它在中断里会被修改主循环读它时要防止读到“改了半个字节”的中问态。最简单的方法是加临界区保护或者在读取前暂时关中断读完再打开。别小看这个细节很多设备在现场偶发数据错乱最后都是栽在这种数据竞争上。5. 第五步现场“抽风”的硬件诱因——EMI、接触不良、热漂移5.1 干扰的三条主要入侵路径如果供电、时钟、程序都排查过板子在实验室里连续跑48小时都没问题一到现场就间歇性抽风那就要往电磁干扰和接触可靠性上想。现场和实验室最大的区别就是有电机、继电器、变频器和长线缆干扰几乎是无处不在的。干扰主要有三条入侵路径一是电源线电网里的浪涌和共模干扰沿着电源线直接灌进板子二是信号线连接传感器、开关、通信线缆的线如果靠近动力线耦合进来的是脉冲群和静电三是地线也就是地弹。如果板子通过较长的接地线连到机柜而机柜里又有大电流设备不同接地点之间就有电位差这个电位差会造成信号电平和主控地不一致轻则通信错码重则复位死机。排查时最简单的方法是用示波器加长时间触发在死机发生前后抓取关键节点的波形。重点看电源芯片输出口、主控复位脚、信号输入口。如果抓到复位脚有异常毛刺那就是干扰从复位电路进来了处理方式是加RC滤波。如果电源输出在设备动作瞬间有一个很大的跌落那就需要加大输入储能电容或者在电源入口加共模电感和TVS。5.2 振动接触不良最容易被现场“忽悠”的一种现场另一大类“抽风”是物理层面的接插件接触不良、焊点冷焊、线束端子氧化。这种问题最大的特点是“现场拍打一下可能就正常了”所以很容易被误判成干扰或者芯片问题。我处理过一个机械臂控制板的案例现象是设备运行到某个角度时偶尔死机但换一块板子还是会在那个位置出问题。后来发现出问题的瞬间机械臂动作会让一块信号排线发生机械应力排线座接触不良导致复位信号被拉低。换了镀金排线座、把线束固定好之后故障彻底消失。在这方面我的经验是所有排线、插簧端子能用螺纹端子的别用快插能打胶固定的别让它悬着。调试阶段多用酒精或专用清洁剂擦一下接插件的接触面并检查是否有氧化变色的。对长期振动的环境尽量把大电解电容用热熔胶或者卡子固定住避免引脚长期受力后产生疲劳断连。5.3 温度漂移和器件老化冷机正常热机必死还有一种非常典型的“抽风”是温度相关的。设备在车间里刚开机时一切正常跑了一段时间温度升上来就开始死机然后停机冷却后又能开机。遇到这种情况优先怀疑晶振。晶振的频率温漂和起振条件受温度影响很大如果选用的晶振负载电容偏大或偏小温度升高后负阻变小可能直接停振。另外电解电容在高温下ESR变大造成电源纹波增大也会导致复位。排查时可以用电吹风对着IC局部加热或者用冷喷剂对可疑器件降温。如果加热后故障复现冷却后恢复基本就能锁定是哪颗器件的问题。但也要注意不要一上来就怀疑芯片。之前遇到一块板子客户说温度高了就死机我用电吹风挨个吹吹到电源芯片旁边的储能电容时故障立刻出现——电容老化后ESR特别高在高温下直接失效导致电源跌落。换了一颗低ESR的固态电容问题解决。5.4 看门狗的现场兜底策略即便上述硬件都排完现场还是有些偶发干扰无法根除。那就要靠系统设计兜底。我的做法是量产软件里默认开启硬件看门狗而且复位后尽快恢复不卡在复杂的自检流程上。同时如果复位发生在上一次系统没有正常关机的情况下要把关键参数保存到EEPROM或Flash里开机后恢复现场比如电机角度、工艺参数、执行步骤。这个“复位后自恢复”逻辑能让现场偶发故障的影响从“死机停产”变成“自动重启一下继续跑”。虽然不是根治但绝对是提高设备可用率最实在的手段。要注意的是如果系统里带执行机构复位后的恢复动作必须不能造成安全事故先回到安全位置再恢复工艺数据这一点比不停机更重要。6. 第六步让偶发故障开口说话——复现、日志和长期监测6.1 提高偶发故障复现率的四个方向前面五步能解决大部分问题但最难处理的是那种“三天才死一次每次都是不同原因”的偶发故障。这时候硬猜已经没用了必须想办法让它复现复现不了就加监测设备。提高复现率的方法主要有四个方向一是电压拉偏把供电电压从额定值往下调到下限再往上调到上限看故障是否在某个电压点出现二是温度循环用高低温箱或者简单点的电吹风冷喷剂做温度扫描三是振动测试把板子固定在振动台上或者拿一个小型振动电机绑在板上模拟现场振动四是加外部干扰源比如在电源线上用ESD枪打脉冲群模拟现场继电器和电机产生的干扰。我自己的习惯是做一个“拷机录音”把故障出现前后十分钟内串口输出的调试日志、复位标志、关键外设状态全部打出来存成一个文件然后让设备一直跑跑出故障就分析日志跑不出就继续跑。这不是笨办法而是最诚实的办法。偶发故障如果没有日志你连猜的边界都没有。6.2 黑匣子用日志记录复位现场很多人不愿意加日志觉得浪费Flash和代码量。但等设备在客户现场死机又复产不了的时候你就会明白一个能告诉你“死机前最后一次关键变量是什么”的黑匣子比什么都值钱。设计上可以定义一个环形缓冲区比如在Flash里开辟20KB每次往日志里写一个包含时间戳、复位标志、当前状态机状态、最近操作命令、几个关键变量的结构体。在程序正常运行时循环覆盖写当检测到异常前兆时比如某步超时、电压跌落、中断异常把当前缓冲区内容冻结到另一块区域下次上电后可以通过串口读出来。如果是非常低成本的芯片也可以用备份寄存器如STM32的BKP存几个关键变量在掉电瞬间由VBAT供电保存最近一次故障标志。我接过一个客户的案子设备偶尔在早上第一次开机时报故障但一到现场就正常。最后就是靠这个黑匣子日志发现早上上电那一刻某个传感器上电稳定时间不够程序在传感器没准备好时就发起了读取命令导致初始化失败。如果没有日志这种问题几乎不可能在远程排查出来。6.3 老化与边界测试把问题消灭在出厂前最后一条也是我一直向做产品的朋友反复强调的出厂前一定要做老化和边界测试不要以为实验室“能跑”就万事大吉。至少做这几项一是高低温存储和运行特别是超出标称温度范围10到15度二是电源波动测试包括电压跌落和瞬间断电三是对外通信接口的长时间数据压力测试四是重复上电测试用继电器或者定时插座让板子每隔30秒掉电再上电连续跑几千次很多“偶发不上电”的问题会在这个测试里现出原形。有一种常见的老化陷阱是“只测好板子”。如果要找出批次性问题应该从每批产品里抽一两块做破坏性测试比如加大电源电压到极限、长时间满负荷运行、故意让看门狗超时看它复位后是否能稳定恢复。只有把这些边界都摸透了设备在现场的“抽风”率才能降下来。我以前做过一个批量测试连续上电5000次发现有3块板子在某次上电后没有正常启动查下来是一颗复位芯片的阈值电压批次离散性太大在低温时释放时序不对。如果没有这个老化测试这3%的不良品流到现场就是三个投诉案例。踩过这些坑之后我现在排查任何单片机控制板问题都会先按照这六步走一遍不跳步、不瞎猜。很多时候问题的答案就在最不起眼的供电纹波、复位时序、堆栈水位或者一个没加volatile的变量里。先让故障站出来说话再去动烙铁和代码排查效率能高出一倍不止。
上一篇/下一篇内容由系统自动关联
返回资讯列表 →