为什么电池设备必须谈功耗

一颗纽扣电池的容量通常只有 200 到 300 毫安时,如果设备在工作模式下吃电 20 毫安,理论上十几个小时就会耗尽。但真实的电池类产品——智能门锁、温湿度传感器、可穿戴设备——往往要求一颗电池撑半年甚至一年。这中间的差距不是靠选更大的电池解决的,而是靠让芯片 99% 的时间都在睡觉。

低功耗设计的核心思路是:让 CPU 尽量少醒、醒了尽量快干完活再睡回去。平均电流决定电池寿命,而不是工作电流。
嵌入式低功耗配图:中央芯片与电池、睡眠波形和电路走线
把芯片的平均电流压到微安级,电池寿命才能以年为单位计算

很多新手做低功耗的第一步就走错了:他们盯着 datasheet 里的 Run 模式电流,觉得 10 毫安已经很优秀。实际上一个合理的低功耗方案,平均电流往往在 10 微安以下——比 Run 模式小一千倍。真正决定寿命的不是干活时耗多少电,而是闲着的时候耗多少电。

三种睡眠模式:Stop、Standby 与 Shutdown

以常见的 Cortex-M 芯片为例,低功耗模式大致分三档:Sleep 只是停 CPU,外设照样跑;Stop 把内核时钟停掉但保留 SRAM,唤醒很快;Standby 几乎切断所有电源,只有备份域和唤醒引脚活着,电流可以低到 1 微安以下,但唤醒后相当于复位。

/* 进入 Stop 模式:CPU 停,但 SRAM 内容保留,中断可唤醒 */
static void enter_stop(void)
{
    HAL_SuspendTick();               /* 停掉 SysTick,避免它立刻唤醒 */
    __HAL_RCC_PWR_CLK_ENABLE();
    HAL_PWR_EnterSTOPMode(PWR_LOWPOWERREGULATOR_ON, PWR_STOPENTRY_WFI);
    /* 被唤醒后从这里继续执行,需要重新配置时钟 */
    SystemClock_Config();
    HAL_ResumeTick();
}

/* Standby:最省电,唤醒后等同于复位 */
static void enter_standby(void)
{
    HAL_PWR_DisableWakeUpPin(PWR_WAKEUP_PIN1);
    __HAL_PWR_CLEAR_FLAG(PWR_FLAG_WU);
    HAL_PWR_EnableWakeUpPin(PWR_WAKEUP_PIN1);
    HAL_PWR_EnterSTANDBYMode();
}

选哪个模式有个简单的判断:唤醒后需要保留现场(变量、栈)就用 Stop;唤醒后从头启动也无所谓就用 Standby。Standby 电流最低,但它把 RAM 里的状态全丢了,下次启动要重新初始化——对传感器采集这种“醒了就干活”的场景反而最合适。

外设时钟门控:不用就关掉

很多开发者不知道的是,芯片内部每个外设都有独立的时钟使能位。UART、SPI、ADC 这些模块即使你不去用它,只要时钟开着,就会白白消耗几百微安。低功耗设计的一条铁律是:哪个外设不用,就在进入低功耗前把它的时钟关掉。

/* 进入 Stop 前关掉所有暂时不用的外设时钟 */
static void periph_clock_gate(void)
{
    __HAL_RCC_GPIOA_CLK_DISABLE();
    __HAL_RCC_GPIOB_CLK_DISABLE();
    __HAL_RCC_USART1_CLK_DISABLE();
    __HAL_RCC_SPI1_CLK_DISABLE();
    __HAL_RCC_ADC1_CLK_DISABLE();
    /* 只保留要用来唤醒的引脚对应的 GPIO 时钟 */
    __HAL_RCC_GPIOC_CLK_ENABLE();    /* PC13 接了按键唤醒 */
}

/* 唤醒后再按需重新打开 */
static void periph_clock_ungate(void)
{
    __HAL_RCC_GPIOA_CLK_ENABLE();
    __HAL_RCC_USART1_CLK_ENABLE();
    MX_SPI1_Init();
    MX_ADC1_Init();
}
时钟门控的三个细节
  • 引脚设为模拟输入未使用的 GPIO 设为模拟输入可避免施密特触发器耗电,比悬空更省电。
  • 调试口别忘了关SWD 在默认状态下会持续耗电,量产固件里要在初始化末尾关闭 JTAG/SWD。
  • LSE 低速晶振按需开用 RTC 唤醒才开 LSE,不用就停掉,几十微安也是肉。

功耗测量:用电流表抓准每一微安

不测量就谈不上优化。低功耗测量最大的坑是:普通万用表的电流档分辨率通常是毫安级,根本测不到微安级的待机电流。你需要一台支持微安甚至纳安档的精密电流表,或者用一个大功率采样电阻加示波器来看电流曲线。

/* 用 RTC 周期性唤醒,测量平均电流的典型代码结构 */
static void rtc_wakeup_setup(void)
{
    RTC_HandleTypeDef hrtc = {0};
    hrtc.Instance = RTC;
    HAL_RTCEx_SetWakeUpTimer_IT(&hrtc, 6000, RTC_WAKEUPCLOCK_CK_SPRE_16BITS);
    /* 每 6 秒醒一次:采集传感器、上报、再睡回去 */
}

void RTC_IRQHandler(void)
{
    HAL_RTCEx_WakeUpTimerIRQHandler(&hrtc);
    if (collect_and_report() == true) {
        /* 上报成功,再进 Stop */
    }
    enter_stop();
}

测量时重点看三个值:睡眠电流(目标几微安)、唤醒工作电流(看峰值和持续时间)、以及两者加权后的平均电流。一颗 220mAh 的纽扣电池,按平均 10 微安算,理论寿命是 22000 小时,大约两年半——扣掉自放电和保守系数,做到一年以上是合理目标。