背景

一块纽扣电池供电的 BLE 温湿度节点,目标是 一年寿命。CR2032 标称 220 mAh,折算平均电流要压到 < 25 µA
广播占空比已经做得很低,瓶颈就落在 Stop2 模式的静态电流上。

第一次测量:20 µA。目标:< 2 µA。

工具

  • 万用表:只能看稳态,µA 下噪声大,别指望看瞬态。
  • Joulescope / Power Profiler Kit:必备,能看 µs 级电流尖峰。
  • 示波器 + 电流环路:土办法但够用。

排查清单

1. GPIO 浮空 → 省了 8 µA

未用的 GPIO 不能悬空。MX_GPIO_Init 里确认所有未用引脚:

1
2
GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;   // 或者 INPUT + PULL_DOWN
GPIO_InitStruct.Pull = GPIO_NOPULL;

Analog 模式是最省的,因为施密特触发器关闭,没有翻转漏电。

2. 调试接口没关 → 省了 1.2 µA

SWD 的 PA13/PA14 在 Stop 模式下仍消耗电流。生产固件里显式关掉:

1
2
3
HAL_DBGMCU_DisableDBGSleepMode();
HAL_DBGMCU_DisableDBGStopMode();
HAL_DBGMCU_DisableDBGStandbyMode();

注意:关了之后板子就烧不进去了,所以用条件编译:

1
2
3
#ifndef DEBUG
HAL_DBGMCU_DisableDBGStopMode();
#endif

3. LSE 启振电流 → 省了 0.3 µA

默认 LSE driver 强度太大,对于 6 pF 晶振用 Medium low 就够:

1
__HAL_RCC_LSEDRIVE_CONFIG(RCC_LSEDRIVE_MEDIUMLOW);

4. VREFBUF 和 ADC → 省了 3 µA

ADC 测完就要关:

1
2
3
HAL_ADC_Stop(&hadc1);
__HAL_RCC_ADC_CLK_DISABLE();
HAL_PWREx_DisableVREFBUF(); // 内部基准电压缓冲

5. 外设时钟 & DMA → 省了 4 µA

进入 Stop2 之前,显式关闭所有不需要在低功耗下运行的外设时钟。
这条很容易忘:HAL 把外设 init 做得很完整,但没人给你把时钟收尾。

6. 传感器 I²C 上拉 → 省了 1.8 µA

SHT40 工作时不耗电(已进入 sleep),但 I²C 线上 10 kΩ 上拉到 3 V 的漏电逃不掉。
把上拉电阻改到 100 kΩ,并让 MCU 在不通信时把 SCL/SDA 拉低(或者断开上拉 VCC)。

7. RTC 已经是必须的,就剩它了

最终 Stop2 + RTC 运行时的静态电流稳定在 1.5 µA,达标。

总账

优化项 节省 累计电流
初始 - 20.0 µA
GPIO 模拟模式 8 µA 12.0 µA
关 SWD 1.2 µA 10.8 µA
LSE 驱动档 0.3 µA 10.5 µA
ADC/VREFBUF 3 µA 7.5 µA
外设时钟 4 µA 3.5 µA
I²C 上拉 1.8 µA 1.7 µA
杂项收尾 0.2 µA 1.5 µA

一点经验

  • 一次只改一个地方,每次都用 Joulescope 测,否则不知道是哪条起了作用。
  • 测 48 h 平均电流比瞬时峰值有意义,BLE 广播的瞬间峰会冲到 10 mA。
  • 最终评估用实际电池跑,LDO / DCDC 的静态电流不在 MCU 侧。