做低功耗设备的朋友应该都有这种体会:一块小电池要撑几个月甚至一年,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放在分压网络下面做低边开关?我一开始也试过低边方案:电池正极接R1,R1另一端接Vsen,Vsen再接R2,R2下端串N-MOS到GND。看起来没问题,但N-MOS关断的时候,R2下端悬空,Vsen这个节点会通过R1被拉到接近电池电压。如果电池是12V,MCU供电是3.3V,ADC引脚直接吃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.1803
12V电池满电电压大约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.5V,3.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.3V,Vgs = 3.3 - 12 = -8.7V,这会让P-MOS一直导通;如果GPIO输出0V,Vgs = -12V,还是导通。所以直接用3.3V GPIO根本关不断这颗P-MOS。
正确的做法就是加一颗N-MOS做开漏式的电平转换。GPIO输出高电平时,Q2导通,把P-MOS的栅极拉到GND,P-MOS的Vgs变成-12V,P-MOS导通;GPIO输出低电平时,Q2截止,P-MOS栅极通过R3上拉到源极,Vgs变成0V,P-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.S(PMOS Source) │ Q1.D(PMOS 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栅极被拉到GND,Q1导通,电池电压经过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%,0603 | PMOS栅源上拉 |
| R4 | 贴片电阻 | 1kΩ ±5%,0603 | NMOS栅极串联电阻 |
| R5 | 贴片电阻 | 1MΩ ±5%,0603 | NMOS栅极下拉 |
| C1 | 贴片电容 | 10nF,X7R,0603 | 采样点滤波储能 |
| Q1 | PMOS | AO3401A,SOT-23 | 高边电源开关 |
| Q2 | NMOS | 2N7002A,SOT-23 | 电平转换 |
R4这颗串在Q2栅极的电阻,主要作用是抑制GPIO走线带来的振铃,以及限制上电瞬间的栅极充电电流。它在低频下对功能没有影响,但建议保留,特别是GPIO线比较长或者走线经过继电器、电机这类干扰源时,能减少误触发。
3.3 静态功耗与平均功耗分析
下面算一下这套电路在12V电池下的功耗表现。
先算导通时的电流。Q1导通、Q2导通的状态下,有几路电流:分压网络的电流、R3的电流、R5的电流。
分压网络电流:
I_div = 12V / (100kΩ + 22kΩ) = 12 / 122k ≈ 98.4µA
R3电流:
I_R3 = 12V / 2.2MΩ ≈ 5.5µA
R5电流:
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.255V
Vbat = 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/10k=330µ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 GPIO,2N7002的阈值可能不够,要换更低阈值的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 问题速查表
| 现象 | 可能原因 | 处理方法 |
|---|---|---|
| 待机电流达到几十µA | R3/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读数,确认分压比和关断状态都正常后再上整机。这个习惯能帮你避开不少隐蔽的硬件坑。