
做低功耗设备的朋友应该都有这种体会一块小电池要撑几个月甚至一年CPU能睡就睡外设能关就关可偏偏电压检测这个功能怎么都绕不开。锂电池有没有过放铅酸电池还剩下多少电设备要不要主动报警全靠它。然而最简单的电阻分压采样一不留神就会在待机时悄悄漏掉几十微安对几百毫安时的电池来说这点电流看着不大时间一长就把待机时间吃掉了。今天分享的这个电路思路不复杂就是用一颗PMOS管给分压电阻供电按需开启、采完即关再加上一颗NMOS做电平转换实现用3.3V单片机的GPIO去控制12V电池侧的检测通路。这个结构很适合用在电池供电的物联网传感器、电瓶车电池监测、太阳能控制器等场景既能保留电阻分压的简单和精度又不用为了省电去上专用电压监测芯片。1. 项目到底要解决什么问题1.1 低功耗电压检测的痛点大家最常见的电压检测电路其实就是两个电阻串联分压中间抽头接ADC像这样Vbat经过R1到采样点采样点经过R2到GND。这个电路简单到不需要思考但问题恰恰出在“常开”上。我算过一笔账如果系统检测12V电池R1取100kΩR2取22kΩ那么只要电池接着分压网络就一直有电流流过。电流大约是12V / 122kΩ ≈ 98µA。这个数值单独看确实不多但用一年就是 98µA × 24小时 × 365天 ≈ 858mAh相当于一颗纽扣电池或者小容量锂电池的标称容量直接没了。如果是用大电池做后备的仪表这一笔待机电量会直接把整机的续航拖崩。有人会说用大阻值电阻啊比如R1取10MΩR2取2.2MΩ分压网络电流会降到1µA左右。但阻值一上去后面ADC的压力就来了。很多单片机的ADC输入阻抗并不高源阻抗几十千欧还能勉强接受几兆欧的话采样保持电容根本充不满读出来的电压会明显偏低。就算你加了很大的外部电容充放电时间也变长检测一次要等几百毫秒甚至几秒而且高阻抗节点对PCB漏电和环境噪声特别敏感稍微受潮读数就漂。专用电压检测芯片比如一些复位IC或者电压比较器待机电流确实很低但一来芯片本身有成本二来不少电压监测IC只能输出一个“高/低”电平想量化具体电压还是要额外接ADC通道或者选带I2C接口的专用电量计成本和复杂度又上去了。所以最实用的思路就是电路还是用电阻分压但别让它一直通电。让一颗MOS管当闸门只有在需要采样的几十毫秒内把分压网络接到电池上采样完立刻断开。这样既保留了分压采样的精度又把平均功耗压到了微安以下。1.2 电阻分压 MOS管控制的设计思路这个项目里MOS管不是用来做线性稳压也不是用来驱动大负载而是做一个“电源路径开关”。具体分工是这样电阻分压网络负责把电池电压按比例映射到MCU的ADC量程内。P-MOS管Q1做高边开关控制分压网络与电池正极之间的通断。N-MOS管Q2做低边电平转换让3.3V的GPIO能去控制12V电源域里的P-MOS。为什么高边用P-MOS而不是像很多人想的那样把N-MOS放在分压网络下面做低边开关我一开始也试过低边方案电池正极接R1R1另一端接VsenVsen再接R2R2下端串N-MOS到GND。看起来没问题但N-MOS关断的时候R2下端悬空Vsen这个节点会通过R1被拉到接近电池电压。如果电池是12VMCU供电是3.3VADC引脚直接吃12V这就不安全了。虽然可以加钳位二极管但钳位二极管的漏电流又会破坏待机功耗。所以绕了一圈还是回到高边P-MOS方案关断时Vsen通过R2稳定地拉到GND不会出现高压悬空节点。2. 核心电路拆解分压电阻计算与控制管选型2.1 电阻分压网络阻值、精度、功耗怎么权衡分压电阻的阻值选择本质是在“电源功耗”和“采样精度”之间找平衡。很多人第一反应是越大越省电但实际设计时要看系统和ADC的具体情况。我这套设计针对12V电池系统MCU的ADC参考电压是3.3V同时预留一定的余量最高要检测到16V。如果满量程3.3V对应16V那理论分压比就是k 3.3 / 16 0.206实际选电阻时不会刚好卡到理论值我取了R1 100kΩR2 22kΩ分压比k R2 / (R1 R2) 22 / 122 0.180312V电池满电电压大约12.6V采样点电压 Vsen 12.6 × 0.1803 ≈ 2.27V落在3.3V量程内到了16V时 Vsen 2.88V仍然不会超过ADC上限。分压比留了一点余量给电阻精度和参考电压漂移腾出空间。分压电阻阻值取100k和22k而不是直接上10M主要原因是ADC源阻抗。从取样点看进去等效阻抗是R1和R2并联Rth R1 // R2 100k // 22k ≈ 18kΩ。这个阻抗在很多MCU的ADC手册里属于“建议用低源阻抗但通过增加采样时间可以接受”的范围。如果后面再接一个10nF的滤波电容那么给电容充电的时间常数大约是18k × 10nF 180µs实际留2ms左右足够稳定不会影响周期性检测。电阻精度方面普通1%贴片电阻对于12位ADC来说温度变化时会产生百分之几的误差。如果只做粗略的电量指示1%够用如果要求电压精度在±1%以内建议换成0.1%精度或低温漂的薄膜电阻或者在产线上做两点校准在代码里把gain和offset补偿掉。我在量产项目里一般用1%电阻然后通过可调电源标定两个点把计算系数写死在程序里这样综合误差可以做到1%以内。2.2 MOS管选型不要只看导通电阻这颗P-MOS管Q1是这个电路里最容易选错的地方。很多人一看到“MOS管”就只看导通电阻Rds(on)其实在这个低频、小电流的检测场景里Rds(on)根本不重要。真正要关心的几个参数是Vds耐压Q1的漏源电压至少要是系统最高电压的1.2~1.5倍。12V系统建议选30V以上16V也安全。AO3401A的Vds是-30V完全够用。Vgs耐压这个特别容易踩坑。P-MOS管导通时栅极会被Q2拉到GND而源极接电池正极12V所以Vgs -12V。选管子时必须确认Vgs的绝对最大值大于系统最高电压。AO3401A的Vgs最大±20V检测12V没问题如果电池电压超过18V就要小心了。更高压的系统栅极驱动还要加稳压管限压不能直接这么接。门极电荷Qg和等效输入电容Ciss这个决定了开关速度。我们这里是低频开关每次开关间隔几毫秒以上所以不太敏感。但如果后续想用PWM方式控制使能那Q1的Qg就要选小的否则会严重发热。漏电流IDSS待机功耗的直接来源。AO3401A标称漏电流很小室温下通常在µA级以下实际万用表测出来待机电流基本就是它贡献的。如果买了劣质物料这一项会超得离谱。N-MOS管Q2负责把3.3V GPIO信号转换成12V域的开关信号。2N7002是我常用的型号Vgs(th)约为1V~2.5V3.3V驱动没有问题开启后的导通电阻只有几欧姆完全够用来拉低P-MOS的栅极。注意不能用那种需要5V以上驱动的高门槛NMOS否则GPIO高电平不够Q2导通不彻底P-MOS栅极拉不下去整个检测通道就处于半开状态。2.3 电平转换驱动3.3V单片机控制12V电源路径直接用GPIO去控制P-MOS是不行的。P-MOS的导通条件是以源极为参考的Vgs为负压也就是说Vgs要低于阈值电压才能导通。12V电池系统里P-MOS源极在12V如果GPIO输出3.3VVgs 3.3 - 12 -8.7V这会让P-MOS一直导通如果GPIO输出0VVgs -12V还是导通。所以直接用3.3V GPIO根本关不断这颗P-MOS。正确的做法就是加一颗N-MOS做开漏式的电平转换。GPIO输出高电平时Q2导通把P-MOS的栅极拉到GNDP-MOS的Vgs变成-12VP-MOS导通GPIO输出低电平时Q2截止P-MOS栅极通过R3上拉到源极Vgs变成0VP-MOS关断。这样就实现了“3.3V控制12V”的转换。需要注意Q1栅极和源极之间的R3不能省。这颗电阻有两个作用第一在MCU还没初始化、GPIO处于高阻状态时把P-MOS栅极可靠地拉到源极保证默认关断第二给栅极积累的电荷提供泄放通路防止静电或干扰导致误触发。但R3也不能选太小因为Q2导通后R3两端电压是电池电压会有电流一直从Vbat经R3到Q2到地。我取2.2MΩ12V下只有5.5µA左右的电流对导通窗口来说可以接受同时也能保证关断速度。3. 完整电路与参数计算3.1 电路连接关系完整的电路连接关系可以这样描述Vbat电池正极 │ ├── Q1.SPMOS Source │ Q1.DPMOS Drain── R1 ── Vsen节点 │ ├── R2 ── GND │ ├── C1 ── GND │ └── ADC引脚 Q1.G ── R3(2.2MΩ) ── Q1.S Q1.G ── Q2.D Q2.S ── GND Q2.G ── R4(1kΩ) ── GPIO_EN Q2.G ── R5(1MΩ) ── GND当GPIO_EN输出高电平时Q2导通Q1栅极被拉到GNDQ1导通电池电压经过Q1、R1、R2分压后在Vsen节点建立采样电压。当GPIO_EN输出低电平时Q2截止Q1栅极通过R3上拉到电池正极Q1关断分压网络与电池断开Vsen节点通过R2放到GND。3.2 元件清单为了方便大家直接抄作业我整理了一份BOM清单编号元件关键规格用途R1贴片电阻100kΩ ±1%0603分压上臂R2贴片电阻22kΩ ±1%0603分压下臂R3贴片电阻2.2MΩ ±5%0603PMOS栅源上拉R4贴片电阻1kΩ ±5%0603NMOS栅极串联电阻R5贴片电阻1MΩ ±5%0603NMOS栅极下拉C1贴片电容10nFX7R0603采样点滤波储能Q1PMOSAO3401ASOT-23高边电源开关Q2NMOS2N7002ASOT-23电平转换R4这颗串在Q2栅极的电阻主要作用是抑制GPIO走线带来的振铃以及限制上电瞬间的栅极充电电流。它在低频下对功能没有影响但建议保留特别是GPIO线比较长或者走线经过继电器、电机这类干扰源时能减少误触发。3.3 静态功耗与平均功耗分析下面算一下这套电路在12V电池下的功耗表现。先算导通时的电流。Q1导通、Q2导通的状态下有几路电流分压网络的电流、R3的电流、R5的电流。分压网络电流I_div 12V / (100kΩ 22kΩ) 12 / 122k ≈ 98.4µAR3电流I_R3 12V / 2.2MΩ ≈ 5.5µAR5电流I_R5 3.3V / 1MΩ 3.3µA所以导通时总电流大约是I_on 98.4 5.5 3.3 ≈ 107µA如果按每秒检测一次每次导通5ms计算平均电流就是I_avg 107µA × 0.005 / 1 0.535µA这个数字已经比常开方案低了接近两个数量级。如果再把检测周期拉长到10秒一次平均电流就降到约54nA。对于大多数电池供电设备来说这个消耗完全可以忽略。待机状态下Q1关断R3两端电压差为0没有电流Q2截止R5两端电压差为0也没有电流分压网络与电池断开只剩Q1本身的漏电流和PCB漏电。实测做好板子清洗后待机电流一般在1µA以下。如果测出来十几微安那基本是哪里没做对可以直接去第5章排查。4. 软件配合检测时序与ADC处理4.1 使能时序开多久、关多久硬件搭好了软件时序如果没配合好照样会吃大亏。这里最典型的错误就是GPIO一拉高立刻去读ADC。因为Vsen节点上并联了C1需要时间充电到稳定值。按前面的参数Vsen节点的等效源阻抗是R1//R2 ≈ 18kΩC1是10nF时间常数τ 18kΩ × 10nF ≈ 180µs稳定到0.1%以内大约需要5τ也就是900µs。实际程序里我会留2ms以上确保采样点电压已经完全建立。如果C1换成100nF那稳定时间会变成1.8ms延时至少留5ms才稳妥。关闭检测时同样要留一点时间让Vsen通过R2放电到GND。这个放电时间常数为R2 × C1 22kΩ × 10nF ≈ 220µs。所以检测完成后拉低GPIO再等1ms再进休眠ADC引脚就不会带着残压也不会通过内部保护二极管漏电。4.2 ADC读取与电压换算ADC部分假设系统用12位ADC参考电压3.3V。采样点电压和ADC值的关系是Vsen ADC值 / 4096 × 3.3V电池电压则是Vbat Vsen / k其中k 22 / 122 ≈ 0.1803。比如ADC读到2800那么Vsen 2800 / 4096 × 3.3 ≈ 2.255VVbat 2.255 / 0.1803 ≈ 12.51V实际代码里不要用浮点数做除法效率低而且不同MCU的浮点库可能带来不确定误差。我一般先把ADC平均值算成mV再用整数乘除法或者预先把分压比转成缩放系数。4.3 代码实现这里给一份参考代码以HAL库风格的伪代码为例#define ADC_REF_MV 3300 #define ADC_FULL_SCALE 4096 #define DIVIDER_RATIO 0.1803f uint32_t read_battery_mv(void) { uint32_t sum 0; uint32_t adc_raw; // 使能分压检测通路 HAL_GPIO_WritePin(EN_GPIO_Port, EN_Pin, GPIO_PIN_SET); delay_ms(2); // 等待Vsen稳定 // 多次采样取平均 for (int i 0; i 8; i) { adc_raw hal_adc_read_battery_channel(); sum adc_raw; delay_us(100); // 让ADC输入电容充分恢复 } // 关闭分压检测通路进入低功耗 HAL_GPIO_WritePin(EN_GPIO_Port, EN_Pin, GPIO_PIN_RESET); delay_ms(1); // 等待Vsen放电到0 uint32_t adc_avg sum / 8; uint32_t vsen_mv adc_avg * ADC_REF_MV / ADC_FULL_SCALE; uint32_t vbat_mv (uint32_t)((float)vsen_mv / DIVIDER_RATIO); return vbat_mv; }这段代码里使能后的2ms延时和采样后的1ms放电延时都很关键。如果MCU的ADC采样速度很快连续采样8次会在极短时间内完成所以每次采样之间隔100µs是为了让内部的采样保持电容有足够时间跟住Vsen节点的电压避免测到前一次转换后的残留电荷。5. 实测与常见问题排查5.1 待机电流偏大待机电流偏大是这套电路最常见的问题而且多数不是原理错误而是细节没做到位。第一检查R3和R5的阻值。R3在导通窗口内会通过电流如果焊接时把2.2MΩ焊成了22kΩ导通电流会从5µA级别直接跳到几百微安待机虽然不受影响但整体功耗会爆表。R5如果被焊成10kΩGPIO高电平时的电流就是3.3V/10k330µA同样很离谱。第二检查PCB清洁度。分压网络和栅极这些高阻节点最怕助焊剂残留和潮湿。我在样板调试时遇到过待机电流从0.5µA变成12µA的情况用酒精清洗后重新烘干电流就恢复正常了。如果是湿度大的环境板子表面最好涂三防漆。第三确认MCU在休眠时GPIO状态。很多MCU的GPIO从外部看是高阻但内部可能有微弱上拉或下拉如果EN引脚实际被拉到高电平Q2会导通P-MOS也会跟着开。解决办法是在软件休眠前明确把EN引脚配置为输出低电平同时保持R5下拉电阻在板上。5.2 采样读数不准、跳动读数不准要先排除建立时间不够。如果你发现电池电压是12V程序读出来只有11V甚至更低大概率是GPIO拉高后没有等C1充满就去采样了。前面算过2ms是底线保守一点放到5ms不会有副作用毕竟平均功耗依然很低。排除建立时间后再看分压电阻精度。1%电阻的温度系数一般是±100ppm/°C如果工作环境温差大电阻值会漂。要求高的话R1和R2都选±50ppm或者更低的低温漂型号。ADC读数跳动则一般是参考电压波动或采样时序问题。如果电池直接给MCU供电而系统里又有无线模块、继电器这类大电流负载ADC参考电压会跟着波动。这种情况建议采样时错开负载工作时段或者使用MCU内部的带隙基准作为ADC参考而不是用VDD直接供电。在Vsen节点加10nF C1后读数跳动通常已经会好很多。如果仍然有跳变可以用软件滤波比如连续采8次去掉最大最小再取平均或者做一个简单的滑动平均。5.3 高边PMOS发热看到P-MOS发热很多人的第一反应是Rds(on)太大。但在本电路里采样电流只有100µA左右就算Rds(on)有100mΩ压降也才10µV不可能发热。真正会发热的原因只有两个Q1没有完全导通或者开关频率太高。Q1没有完全导通本质上是因为Q2没有把Q1栅极拉到足够低。测量Q1栅极对地电压如果GPIO为高时栅极电压还有好几伏那就要检查GPIO驱动电平是否足够。3.3V驱动的2N7002没问题但如果你用的是1.8V GPIO2N7002的阈值可能不够要换更低阈值的NMOS。至于开关频率如果业务上把EN信号改成PWM来进一步占空比控制那就要重新考虑驱动了。P-MOS在开关过程中会经过线性区米勒效应会拖长这个过程开关损耗会急剧增加。本设计是低频点动一次只导通几毫秒所以不需要担心。5.4 上电误开启与关断延迟有人会问MCU还没初始化的时候GPIO是高阻这个期间Q1会不会误开启正常不会因为R5把Q2栅极固定在地Q2截止R3把Q1栅极拉到源极Q1也截止。但如果大楼里电磁干扰比较强或者板子周围有动力线高阻节点还是有可能被耦合出电压。所以启动代码一进去就要尽快把EN引脚配置为输出低不要让它长时间悬空。关断延迟的问题主要出在R3阻值选得太大。R3同时也是Q1栅极电荷的泄放通路如果选10MΩ甚至更大Q2关断后Q1栅极要经过很长时间才能被拉回源极电位在这个过程里分压网络一直处于半导通状态。我实测用2.2MΩ时关断延迟在1ms以内对低功耗检测来说完全够用。如果你打算进一步降低导通功耗把R3换成10MΩ建议同时用示波器确认关断波形别让半导通时间吃掉你的平均功耗预算。5.5 问题速查表现象可能原因处理方法待机电流达到几十µAR3/R5焊错、PCB漏电、Q1漏电清洗板子核对阻值更换MOS管采样电压明显偏低建立时间不够、分压比不准增加延时校准电阻系数ADC读数跳变参考电压不稳、受到干扰加滤波电容避开负载切换时段PMOS发热Q2驱动不足、开关频率过高检查GPIO电平重新设计驱动上电瞬间误接通GPIO高阻、干扰耦合使用R5下拉启动时尽快拉低EN关断后仍有漏电流R3太大导致关断慢减小R3或增加栅极泄放路径最后再分享一个实际操作中的小细节如果被测电压特别高比如48V系统这套电路的基本结构还能用但P-MOS的Vgs耐压必须重新考虑。多数小信号PMOS的Vgs最大也就±20V直接用3.3V GPIO经过NMOS去控制会产生过压风险。这时候需要在Q1的栅极回路里串一颗稳压管把Vgs钳制在安全范围内。调试这类电路时我习惯先不接电池用可调电源慢慢升压一边升压一边观察Vsen节点的电压和ADC读数确认分压比和关断状态都正常后再上整机。这个习惯能帮你避开不少隐蔽的硬件坑。