ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

单片机欠压保护与欠压复位:阈值设定、电路设计及工程实践

单片机欠压保护与欠压复位:阈值设定、电路设计及工程实践 做单片机开发很多人把注意力都放在功能逻辑上——通信协议、显示刷新、电机控制、传感器采集——电源只要一上电能点亮就觉得万事大吉。直到产品在市场里碰上电压跌落设备随机重启、EEPROM数据错乱、继电器乱跳客户当场退货这时候才回头研究电源问题。我在这个坑里栽过跟头所以专门写一篇关于单片机欠压保护、欠压复位的博文把这套机制彻底说透它到底是什么、阈值怎么定、硬件和软件分别怎么实现、实际项目里有哪些防不胜防的坑。欠压保护解决的是“电源电压跌到某个危险值”时系统能否安全退出运行状态的问题欠压复位解决的是“电压已经低到无法维持稳定运行”时系统能否干净地重启的问题。两者是兄弟但做的事情不一样。这篇文章的内容从原理到选型从电路计算到代码实现都按实际工程的逻辑来适合正在做产品设计的工程师也适合做课设、毕设的在校学生。1. 欠压到底是什么掉电瞬间系统里发生的连锁反应欠压或者说电源电压跌落并不只是“电压数字变小”这么简单。它背后是一连串的连锁反应而且每个环节都可能导致产品表现异常。1.1 电压不会“突然没了”而是逐级跌破很多人误以为欠压就是停电或者电池没电系统直接从正常状态跳到关机状态。真实情况远没有那么干净。市电供电的设备电网波动、大功率负载启动都会让整流后的母线电压瞬间拉低电池供电的设备随着电量下降电压是缓慢下滑的即便用DC-DC或LDO做二次稳压当输入侧电压垮掉时输出侧也会跟着垮——只不过是延迟一点、幅度小一点而已。更要命的是同一个系统里有好几种电源轨。比如单片机用3.3V继电器驱动用5V传感器用12V。欠压不是“单片机3.3V先掉”往往是12V或者5V先撑不住。12V掉到9V继电器可能还能勉强吸合但吸合力下降触点抖动5V掉到4.5V逻辑芯片已经开始超出工作范围了。所以做欠压保护一定要站在整个系统的角度去看“谁的电压先顶不住”而不是只盯着单片机那一路。1.2 到达单片机之前欠压先击穿哪些环节我在项目里总结过电压下降的过程里最先出问题的往往不是MCU而是外部电路。第一梯队是继电器、电磁阀、接触器这类电磁驱动器件。它们是纯机械结构吸合力跟线圈电压直接相关。电压偏低时吸合力不足触点接触电阻变大通流能力下降严重的会出现触点反复通断电火花直接烧蚀触点。第二梯队是模拟前端和通信芯片。比如RS485收发器工作电压偏低时输出驱动能力下降总线电平摆幅不够远距离通信就开始误码运放和ADC如果供电不足采集到的电压信号会直接偏掉悬空或接近参考电压时尤其明显。第三梯队才是单片机本身。MCU虽然标称工作电压范围宽比如STM32F103的供电范围是2.0V到3.6V但在电压偏低时Flash读取可能出错、内部RC振荡器频率会漂、GPIO输出高电平不达标、复位电路可能误触发。而且不同寄存器的保持电压并不完全一样有些外设先失效CPU还在跑这种“半脑死亡”状态最难受——程序看着还在运行但逻辑已经全乱了。1.3 项目里哪些场景最容易踩中欠压结合我做过的几个具体项目下面这些场景是欠压问题的高发区电机驱动板电机启动瞬间的堵转电流或启动电流可以达到额定电流的5到7倍如果电源余量不够母线电压会被直接拉下去一大截。电磁炉等强电控制板可控硅调功、IGBT开关瞬间的浪涌电流很大电网电压稍微不稳控制板电源就容易出现跌落。车载电子汽车启动时电瓶电压可能从12V跌到8V甚至更低车载设备如果直接从电瓶取电必须做宽压输入和欠压保护。电池供电设备锂电池放电到截止电压附近时电量曲线会突然掉得很快电量显示往往还没到底设备就罢工了。大电容储能设备断电后靠电容维持供电的电路电压下降速度其实很快如果软件没有在电压尚可时完成关键数据保存就会出现数据丢失或Flash损坏。2. 保护阈值与回差先搞清楚“以谁为准”再定参数很多新手一上来就问“欠压阈值设多少合适”这其实是一个伪命题。阈值不取决于某一颗芯片的标称值而是取决于整个系统里对电压最低可接受的要求。这个要求得从“最脆弱的那个器件”倒推出来。2.1 阈值不能拍脑袋从系统最脆弱器件倒推拿一个典型的5V系统举例单片机最低能跑到2.7V但继电器在3.5V以下吸合力就明显不足RS485收发器低于4.5V就开始误码。那这个系统的欠压保护阈值就不能定在3.0V——那时候单片机还能跑但继电器已经乱跳了通信已经乱套了。阈值应该以RS485收发器的4.5V为基准再留一点余量定在4.0V到4.2V比较合理。设计逻辑是这样一个顺序枚举系统里所有用电模块的最低可靠工作电压。取其中的最大值作为欠压保护阈值的下限基准。加上设计余量通常0.2V到0.5V取决于电源纹波大小。确保正常工作时电源电压的最低点不会下探到这个阈值。举个例子一个5V系统如果知道纹波峰峰值有200mV那阈值不能设成4.9V否则正常工作时也会频繁触发欠压。合理的做法是正常电压范围5.0V±5%最恶劣情况下低到4.75V那欠压阈值设在4.2V到4.5V之间既躲开正常波动又能在真正出问题前报警并停机。2.2 回差是怎么算的以4.2V阈值为例回差也叫滞回宽度是欠压保护里最容易忽略的细节。如果保护阈值和恢复阈值完全一样就会出现临界抖动电压刚低于阈值系统保护停机电压稍微恢复一丁点保护解除又开始工作大负载一启动电压又掉下去……系统就会在保护和不保护之间反复横跳继电器噼里啪啦响接触器疯狂通断非常伤硬件。正确做法是设置两个阈值一个欠压触发阈值一个恢复阈值中间留出滞后区间。比如欠压阈值4.2V恢复阈值4.4V回差就是200mV。这200mV的回差实际含义是“电压跌到4.2V以下触发保护但一定要升到4.4V以上才解除保护”。回差大小的工程经验值一般是系统最大纹波峰峰值的2倍左右。纹波100mV回差就取200mV。太小无法滤掉纹波带来的反复触发太大恢复门槛太高电网上来之后系统迟迟不启动用户体验很差。3. 硬件实现比较器方案、专用芯片和单片机内置检测欠压保护可以用硬件实现也可以用软件实现实际项目里经常是混合着来。硬件的核心优势是响应快、不依赖程序状态哪怕CPU已经跑飞了它也能兜底。这一节把三种主流硬件方案讲清楚包括参数计算和选型细节。3.1 电阻分压比较器方案的设计与计算最经典的电路是用一颗电压比较器比如LM393、LM339加上两个分压电阻和一个基准电压源。我的做法是这样的把被测电源电压比如5V用电阻分压分压点接到比较器的负输入端正输入端接一个基准电压用TL431或稳压二极管产生当分压点电压低于基准电压时比较器翻转输出端从高电平变成低电平。这个低电平接到单片机的外部中断引脚或者直接接到复位引脚上。分压电阻的大小需要算一下。假如要检测阈值4.2V基准电压选2.5VTL431很常用那分压比就是2.5 / 4.2 ≈ 0.595。取R2 100kΩR1 68kΩ实际分压比100 / (100 68) ≈ 0.595正好匹配。电阻一定要选1%精度的普通5%误差的电阻在批量生产时会导致阈值偏差很大——同一批板子有的4.1V就触发有的4.3V才触发非常头疼。这里有几个容易被忽视的细节LM393的输出级是开集电极必须加一个上拉电阻到MCU的IO电压域10kΩ就够。分压点可以加一个10nF的小电容到地滤掉高频毛刺但不要加大电容否则响应速度会变慢。我用68kΩ的源电阻加10nF电容时间常数约0.68ms还可以接受。要让比较器有回差可以在正输入端和输出端之间接一个正反馈电阻让输出电平反过来改变参考电压。算回差宽度时要用正输入端等效电阻和反馈电阻的比例关系来算工程上直接用可调电阻调试更方便。3.2 专用电压检测芯片的选型要点如果不想自己算电阻也不想折腾比较器直接用电压检测芯片最省心。这类芯片内部集成了比较器、基准源和迟滞体积小、精度高、温漂小一颗就能搞定。常见的型号包括TPS3839、MAX809、TCM809、STM809、APX803等。选型时主要看这几个参数参数含义选型建议检测阈值触发电平选择最接近你算出来的系统阈值的型号滞回电压恢复阈值与触发阈值之差一般在20mV~100mV之间满足要求即可输出类型推挽或开漏开漏型号适用于不同电压域的电平转换复位延时电压恢复后延时释放复位的时间常用140ms用于等待电源稳定静态电流芯片自身功耗电池供电产品要特别关注选1μA以下比如TPS3839系列有3.08V、2.93V、2.63V、1.67V等多种阈值可选STM809支持5V、3.3V、3.0V等常见电压。这类芯片另一个好处是输出可以直接接到MCU的NRST引脚实现硬件级的欠压复位接线非常简洁PCBA面积也省。3.3 单片机内置掉电检测的用法STC/STM32很多单片机内部就集成了掉电检测电路不需要额外加芯片。51家族里STC系列的低压检测LVD就很实用比如STC89/STC12/STC8系列通过配置低压检测寄存器可以设置成在电源电压跌到某个阈值时产生中断或复位。STC的低压检测用法大概是初始化时设置LVD阈值有3.7V、4.2V等档位可选使能低压检测中断。电压掉到阈值以下时内部比较器触发程序进入中断服务函数在里面把需要保存的关键数据存到EEPROM然后等待电压进一步跌落或主动关闭负载。这个方案成本为零缺点是阈值档位有限不如外部芯片灵活。STM32系列对应的功能叫PVDProgrammable Voltage Detector可编程电压检测器。PVD的阈值不是随便选的寄存器里有一组固定档位比如2.2V、2.3V、2.4V……一直到2.9V。配置方法是在PWR_CR寄存器里设置PLS再使能EXTI16中断。PVD复位或中断的机制是当VDD电压从高于阈值降到低于阈值时产生事件反过来说从低于阈值升到高于阈值时也会产生事件。所以用PVD还能检测“电压恢复正常”这个状态。STM32 PVD的初始化代码看起来类似这样void pvd_init(void) { // 选择PVD阈值例如2.9V PWR_CR | PWR_CR_PLS_LEV7; // 使能PVD PWR_CR | PWR_CR_PVDE; // PVD连接到EXTI线16使能上升沿和下降沿中断 EXTI_InitTypeDef exti; exti.EXTI_Line EXTI_Line16; exti.EXTI_Mode EXTI_Mode_Interrupt; exti.EXTI_Trigger EXTI_Trigger_Rising_Falling; exti.EXTI_LineCmd ENABLE; EXTI_Init(exti); } void EXTI15_10_IRQHandler(void) { if (EXTI_GetITStatus(EXTI_Line16) ! RESET) { if (PWR_GetFlagStatus(PWR_FLAG_PVDO)) { // VDD低于PVD阈值执行欠压保护动作 SaveCriticalData(); DisableHeavyLoad(); } else { // VDD恢复清理欠压标志准备恢复工作 ClearUnderVoltageFlag(); } EXTI_ClearITPendingBit(EXTI_Line16); } }4. 软件实现ADC采样、状态机与受控复位硬件方案响应快但欠压保护如果全交给硬件会有两个问题一是只知道“电压低了”不知道“低了多少”二是没有机会在断电前完成数据保存和负载关闭。所以实际产品里我一般用“软件为主、硬件兜底”的策略。4.1 什么时候必须用软件方案软件做欠压检测最大的价值是能做到分级保护。比如电池供电设备电压降到3.5V时先降低屏幕亮度、关掉震动电机提醒用户充电降到3.3V时停止大功率操作保存现场数据降到3.1V时彻底进入低功耗模式只留一颗外部唤醒芯片在等待。这种多级管理硬件比较器很难做到。另外有些单片机没有内置LVD也不想额外加电路那就只能用ADC采样电源电压。做课设、毕设的同学经常遇到这种情况比如51单片机自己做个电阻分压把分压点接到ADC引脚程序里判断电压值就够了。4.2 ADC采样与滤波的工程细节用ADC测电压第一步是确定分压电阻和参考电压的关系。以5V系统、单片机ADC参考电压5V为例直接给一个电阻分压比例1:1把VCC分压到ADC输入范围的一半留出测量余量。软件里要做滤波。我常用的方法是中值滤波加滑动平均连续采样5次去掉最大值和最小值剩下的取平均。这个操作能滤掉大部分随机噪声但不适合滤掉“电压真的突然掉了”这种情况——如果电压在快速跌落滤波反而会“平滑”掉真实变化。所以采样周期要短比如每1ms采一次连续判断3到5次再确认欠压。响应慢几毫秒没问题但抗干扰能力会强很多。一个容易被忽略的点是ADC采样时机。电机PWM开关瞬间、继电器吸合瞬间、射频模块发射瞬间电源上都会有大毛刺。如果刚好在这个时刻采样测到的电压可能比真实值低很多。我现在的做法是把欠压检测放到主循环固定位置执行避开中断密集区如果负载开关是可编程的在开关瞬间前后各跳过几个采样周期。4.3 欠压检测状态机消抖逻辑与动作分发不要在主循环里用delay等待电压恢复那样会把整个系统卡死。我建议用状态机来实现欠压检测的消抖和动作分发下面的代码是一个可以参考的框架typedef enum { VOLT_NORMAL, VOLT_CHECK, VOLT_LOW, VOLT_RECOVER } VoltState; #define LOW_CONFIRM_CNT 5 #define HIGH_CONFIRM_CNT 5 #define ADC_LOW_TH 2810 // 对应4.0V #define ADC_RECOVER_TH 2960 // 对应4.25V回差约0.25V static VoltState volt_state VOLT_NORMAL; static uint8_t confirm_cnt 0; void voltage_monitor_task(void) { uint16_t adc_val; adc_val read_avg_adc(); switch (volt_state) { case VOLT_NORMAL: if (adc_val ADC_LOW_TH) { volt_state VOLT_CHECK; confirm_cnt 0; } break; case VOLT_CHECK: if (adc_val ADC_LOW_TH) { confirm_cnt; } else { volt_state VOLT_NORMAL; break; } if (confirm_cnt LOW_CONFIRM_CNT) { // 确定进入欠压状态 save_critical_data(); disable_heavy_load(); volt_state VOLT_LOW; } break; case VOLT_LOW: // 电压必须回升到恢复阈值以上才允许恢复流程 if (adc_val ADC_RECOVER_TH) { volt_state VOLT_RECOVER; confirm_cnt 0; } break; case VOLT_RECOVER: if (adc_val ADC_RECOVER_TH) { confirm_cnt; } else { volt_state VOLT_LOW; break; } if (confirm_cnt HIGH_CONFIRM_CNT) { // 电压稳定恢复执行系统复位 system_reset(); } break; } }这套状态机的设计思想是正常状态不能直接进入欠压处理必须经过一段确认时间欠压状态也不能直接回到正常必须升到更高的恢复阈值并且持续稳定一段时间。这样处理之后那些瞬间的电压毛刺根本不会引发误保护。4.4 欠压后的处理顺序保存、关负载、受控复位确认欠压之后处理顺序是有讲究的。我的推荐顺序是先保存最关键的数据。把系统状态、本次运行参数、错误码写入EEPROM或备份寄存器。注意Flash和EEPROM写入本身需要时间如果电压已经极低可能写一半就断了。这种情况下要评估写入时间必要时用一个大电容储能维持几十毫秒供电保证写入完成。关闭大负载。继电器、电机、加热丝这些能关就关。这样做的目的是降低系统电流消耗让电源电压不要继续快速下跌给后续操作争取时间。设置欠压标志。标志写到RAM和EEPROM各一份。下次上电时程序先读这个标志决定是直接进入正常运行还是先提示用户“上次是异常断电”。受控复位或者进入低功耗。如果电压低到接近MCU的最低工作电压与其让程序在“半脑死亡”状态下乱跑不如主动复位。这就是欠压复位的意义与其等电压低到硬件复位门限不如在电压还没彻底垮掉的时候干净地把系统重置到初始状态。关于软复位51单片机没有统一的软复位指令常见的办法是ISP_CONTR | 0x60针对STC系列或者直接把一个软件看门狗喂不上让它超时复位。STM32直接用NVIC_SystemReset()干净利落。5. 欠压复位和其他“看起来像”的复位有什么区别复位有很多种合在一起很容易晕上电复位、欠压复位、看门狗复位、软件复位……它们触发条件不同、后果不同不要混为一谈。搞清楚区别排查问题时才能少走弯路。5.1 四类复位的触发条件与实现对比复位类型触发条件特点典型实现上电复位POR电源从0开始上升每次上电必触发清除所有寄存器片内上电复位电路欠压复位BOD/LVD电源电压低于MCU最低工作电压电压跌落过程中触发防止MCU乱跑专用电压检测芯片、片内BOD电路看门狗复位程序跑飞未及时喂狗由软件超时触发硬件自动执行独立看门狗或窗口看门狗软件复位程序主动调用复位指令完全受控用于执行重启流程NVIC_SystemReset、软复位指令上电复位和欠压复位的区别在于上电复位发生在电压上升阶段欠压复位发生在电压下降阶段。它们在很多芯片内部用的是同一套检测电路只是触发条件不同。5.2 欠压保护与欠压复位的配合关系这两个概念经常被放在一起说但物理意义不一样。欠压保护是“发现电压危险之后系统采取的保护动作”比如保存数据、关闭负载、进入安全状态欠压复位是“电压实在太低必须重启系统”这个最终处置动作。我用一个比喻来解释欠压保护就像是人在身体出现异常信号时主动停下休息、吃药欠压复位则是已经晕倒被送进急诊室抢救强制重启。一个设计良好的系统欠压保护和欠压复位是配合使用的软件检测到电压接近危险区执行保护动作把现场数据保存好如果电压还在继续掉突破了MCU的复位门限硬件自动复位——此时关键数据已经保存完损失被降到最低电压恢复后MCU重新上电启动读取欠压标志恢复现场或者按安全模式运行。6. 实测踩坑三个案例还原完整排查链路写了这么多原理最后分享几个我实际调过的故障案例。这些案例不是个例几乎每个项目里都能找到类似的影子。6.1 案例1电机启动瞬间系统反复重启现象一块电机驱动板主控是STM32F103电源是24V转5V再转3.3V。电机一启动板子就反复重启但用万用表量5V电压只看到4.8V左右并没有低到离谱。排查过程一开始怀疑是看门狗的问题把看门狗关了现象依旧后来怀疑是软件逻辑问题把主循环里所有任务都屏蔽现象反而更频繁了。用示波器直接看3.3V引脚才发现电机启动瞬间有约300ms的连续毛刺其中若干尖峰已经低于2.9V而我在程序里把PVD阈值设到了2.9V。虽然平均电压看着没事但毛刺已经频繁触发PVD中断中断里我又执行了保存数据和延时导致主循环没法正常运行。修复方案第一步把PVD阈值从2.9V降到2.7V避开毛刺第二步在电机驱动IO上增加了软启动逻辑把PWM占空比从0逐渐升高避免启动瞬间电流尖峰第三步在3.3V输出端加了一个100μF的钽电容把瞬态压降进一步拉低。三层处理之后重启问题彻底消失。这个案例的教训是不要用万用表测电压来判断瞬态问题要用示波器抓毛刺欠压检测的阈值设置必须把瞬态纹波考虑进去。6.2 案例2比较器回差太大导致恢复失败现象一块用硬件比较器做欠压保护的板子电源电压恢复正常后设备不启动必须手动断电再上电才能恢复。排查过程查电路发现当初为了图省事直接在比较器正输入端和输出端之间接了一个100kΩ的正反馈电阻没仔细算回差。实测回差高达800mV保护阈值是4.2V恢复阈值要等到5.0V才能解除——而实际电源电压恢复之后稳定在4.9V刚好卡在恢复阈值下面于是保护一直锁死。修复方案把正反馈电阻换成470kΩ回差缩到约150mV问题解决。这个坑说明回差不是越大越好过了头就会造成欠压恢复困难。6.3 案例3掉电瞬间EEPROM数据丢写现象电池供电的产品客户反馈偶尔出现参数被清零、配置丢失的情况。查日志发现都是发生在电池电压极低、设备即将关机的那一段时间。排查过程掉电瞬间软件检测到电压偏低开始往EEPROM里写当前参数。问题是EEPROM写入需要时间比如常见的AT24C02在5V下页写需要5ms而电压从触发欠压保护到彻底断电可能只有两三毫秒。写入还没完成电压已经没了EEPROM里就留下了一个半写状态的坏数据下次上电读出来校验不过直接被当成无效数据。修复方案硬件上加了一个470μF的储能电容加大掉电后的维持时间软件上调整逻辑在欠压触发后先关闭所有大负载再执行EEPROM写入写入前先读回校验如果写入失败就重试一次再失败就标记为“未完成写入”保证不会用坏数据顶替好数据。6.4 其他容易踩的细节检测点、钳位、上电时序最后再列几个我经常提醒自己的细节检测点位置要选对。不要只检测LDO输出端还要看单片机的实际供电引脚。如果电源路径上有二极管或者MOS管做防反接压降可能不小检测点和MCU实际电压之间会有差异。电机等感性负载关断瞬间会产生反电动势可能导致电源轨出现负压。负压灌进比较器或者ADC引脚轻则读数不对重则烧芯片。解决办法是在电源轨到地之间加一个肖特基二极管做钳位。上电时序也要注意。有些系统的欠压保护阈值设得比较高上电时电源电压从0爬升会先处于“欠压”区间。如果保护电路在上电瞬间被触发并且因为是电平锁存型导致无法自动恢复设备就永远启动不了。设计时必须允许上电过程中有一段“欠压容忍时间”或者在检测到电压恢复后自动解除保护。做欠压保护和欠压复位说到底就是在跟时间赛跑。电压掉落的过程通常只有几毫秒到几十毫秒系统要在这段时间里完成判断、确认、保存、关机这一整套动作。我的经验是先搞清楚系统里谁最怕电压低再把阈值倒推出来硬件负责兜底快速响应软件负责分级处理和现场保护回差和消抖一定要做不然就是给自己埋雷。调完所有逻辑记得拿示波器在阈值附近多抓几次瞬态波形确认没有临界抖动再批量出货。这个功能看着不起眼但真出问题时它往往是让产品在严酷现场活下来的最后一道防线。
返回列表