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 低电量中断唤醒——让牙刷在“临终”前喊一声

电动牙刷最尴尬的场景是什么?刷到一半没电了。所以我们要做低电量检测,提前通知用户。

实现方案:

  1. 主循环轮询:每隔几秒测一次电压。简单,但费电。
  2. ADC中断:电压低于阈值时触发中断。省电,但配置复杂。
  3. 低电压比较器:有些芯片内置比较器,硬件触发。最省电。

我个人推荐用第二种——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