4、ADC与电池电量检测:ADC采样原理、分压电路设计、电池电压曲线、低电量中断唤醒
各位同学,欢迎来到第四讲。
上一章我们把PWM和电机驱动搞定了,牙刷能转了。但有个问题——它转着转着突然没电了,你正刷着牙呢,它停了,这体验可太糟糕了。所以这一章,我们来聊聊怎么让电动牙刷知道自己还剩多少电,以及快没电时怎么优雅地通知你。
4.1 ADC采样原理——芯片的“眼睛”怎么看电压?
ADC,全称模数转换器。说白了,就是把连续的模拟电压,变成芯片能理解的数字。
我打个比方:你拿一把尺子量水杯高度。尺子上的刻度是离散的,水杯高度是连续的。ADC干的就是这个活——它把0~3.3V的电压范围,分成4096份(如果是12位ADC),每一份对应一个数字。
关键参数:
- 分辨率:12位ADC,就是2^12=4096个台阶。每个台阶代表3.3V/4096 ≈ 0.8mV。嗯,精度够用了。
- 采样率:每秒能采多少次。测电池电压不需要很快,几十Hz就够了。
- 参考电压:这个很重要。ADC的满量程对应的是参考电压。我们一般直接用VDD(3.3V)做参考。
避坑指南:我曾经在一个项目里直接用芯片内部参考电压,结果发现电池电压和ADC读数对不上。查了半天,原来是内部参考电压有±5%的误差。从那以后,我习惯用外部精密参考,或者至少做一次软件校准。
4.2 分压电路设计——电池电压太高,芯片扛不住
锂电池满电4.2V,芯片只能承受3.3V。直接连?芯片会冒烟的。
所以我们需要分压电阻。原理很简单:两个电阻串联,中间抽头接ADC引脚。
计算公式:
V_adc = V_bat * R2 / (R1 + R2)
我一般选R1=100kΩ,R2=200kΩ。这样分压比是2/3。4.2V电池对应ADC输入2.8V,安全。
设计要点:
- 电阻值不能太小,否则电池漏电快。我见过有人用10kΩ,结果待机电流多了几百微安。
- 电阻值也不能太大,否则ADC输入阻抗会拉低电压。一般100k~1MΩ比较合适。
- 加一个0.1μF电容到地,滤掉噪声。你想想看,电机转动时产生的干扰可不小。
我的习惯:在分压电阻和ADC引脚之间串一个1kΩ电阻,起到限流保护作用。万一ADC引脚配置错了,也不至于烧芯片。
4.3 电池电压曲线——锂电池的“脾气”你得摸清
锂电池的放电曲线不是线性的。你不能简单地说“3.7V就是一半电”。
典型锂电池放电曲线(1C放电):
| 电量百分比 | 开路电压(V) | 带载电压(V) |
|---|---|---|
| 100% | 4.20 | 4.10 |
| 80% | 3.95 | 3.85 |
| 60% | 3.85 | 3.75 |
| 40% | 3.75 | 3.65 |
| 20% | 3.60 | 3.50 |
| 5% | 3.30 | 3.20 |
| 0% | 3.00 | 2.90 |
注意看,带载电压比开路电压低。因为电机一转,电池内阻会分压。所以测电量时,最好在电机停止时测,或者做软件补偿。
软件补偿思路:
// 伪代码示例
uint16_t get_battery_level(void)
{
uint32_t adc_value = read_adc();
uint32_t voltage_mv = adc_value * 3300 / 4096; // 原始ADC值转mV
voltage_mv = voltage_mv * 3 / 2; // 补偿分压比
// 查表法,比公式计算更准
if (voltage_mv > 4100) return 100;
else if (voltage_mv > 3850) return 80;
else if (voltage_mv > 3750) return 60;
else if (voltage_mv > 3650) return 40;
else if (voltage_mv > 3500) return 20;
else if (voltage_mv > 3200) return 5;
else return 0;
}
注意:不同品牌的电池曲线有差异。我建议你拿实际电池测一遍,把数据点记录下来,做成自己的查表。别完全信数据手册,那都是理想情况。
4.4 低电量中断唤醒——让牙刷在“临终”前喊一声
电动牙刷最尴尬的场景是什么?刷到一半没电了。所以我们要做低电量检测,提前通知用户。
实现方案:
- 主循环轮询:每隔几秒测一次电压。简单,但费电。
- ADC中断:电压低于阈值时触发中断。省电,但配置复杂。
- 低电压比较器:有些芯片内置比较器,硬件触发。最省电。
我个人推荐用第二种——ADC中断唤醒。因为大多数MCU都支持,而且功耗控制得好。
配置步骤:
// 以STM32为例的伪配置
void adc_low_battery_init(void)
{
// 1. 配置ADC为连续转换模式
// 2. 设置模拟看门狗(AWD)阈值
// 低阈值设为:2.8V * 4096 / 3.3 ≈ 3475
// 对应电池电压:3.2V(分压后2.13V)
HAL_ADC_AnalogWDGConfig(&hadc, 3475, 4095, ADC_ANALOGWATCHDOG_SINGLE_REG);
// 3. 使能ADC中断
HAL_ADC_Start_IT(&hadc);
// 4. 进入低功耗模式
HAL_PWR_EnterSTOPMode(PWR_LOWPOWERREGULATOR_ON, PWR_STOPENTRY_WFI);
}
// 中断回调
void HAL_ADC_LevelOutOfWindowCallback(ADC_HandleTypeDef* hadc)
{
// 电压低于阈值,唤醒MCU
// 闪烁LED,发出蜂鸣声
// 保存当前状态到Flash
// 然后进入深度睡眠,直到用户充电
}
避坑指南:我曾经遇到过一个问题——牙刷在电机启动瞬间,电压会瞬间跌落,触发了低电量中断。后来我加了软件滤波:连续3次采样都低于阈值,才认为是真低电量。这叫“去抖”,和按键消抖一个道理。
低电量后的行为设计:
- 第一次检测到低电量:闪烁绿灯,每次启动时提示。
- 第二次检测到低电量:闪烁红灯,强制停止电机。
- 电压低于3.0V:进入深度睡眠,所有功能关闭,只保留充电检测。
为什么要分等级?因为锂电池过度放电会损坏。你想想看,一块电池几十块钱,因为软件没处理好报废了,多冤。
小结
这一章我们聊了ADC怎么工作,分压电路怎么设计,电池曲线怎么看,以及低电量怎么处理。说白了,就是让牙刷知道自己还剩多少电,并且在快没电时做出正确的反应。
下一章,我们会讲充电管理——怎么安全地把电池充满,以及充电时的状态指示。嗯,到时候见。
公众号:蓝海资料掘金营,微信deep3321