时钟系统与功耗:HSE、HSI、LSE、LSI 时钟源的选择,PLL 配置对功耗的影响,时钟门控技术
时钟系统,说白了就是 STM32 的心脏。心跳多快,功耗就有多高。很多朋友做低功耗项目,一上来就盯着睡眠模式、停止模式调,结果发现电流还是降不下去。我告诉你,问题八成出在时钟源的选择上。
这一章,咱们就把时钟源和功耗的关系彻底捋清楚。我会结合自己踩过的坑,告诉你什么时候用 HSE,什么时候切 HSI,PLL 到底该不该开。
1. 四大时钟源,各有各的脾气
STM32 内部有四个时钟源,每个都有自己的功耗特性。我整理了一张表,方便你对比:
| 时钟源 | 全称 | 典型频率 | 启动时间 | 典型功耗 | 精度 |
|---|---|---|---|---|---|
| HSE | 高速外部晶振 | 4-16 MHz | ~2 ms | 高(~500 μA) | 极高(±10 ppm) |
| HSI | 高速内部 RC | 8 MHz(可微调) | ~10 μs | 中(~100 μA) | 一般(±1%) |
| LSE | 低速外部晶振 | 32.768 kHz | ~1 s | 低(~0.5 μA) | 极高(±5 ppm) |
| LSI | 低速内部 RC | ~32 kHz(实际有偏差) | ~50 μs | 极低(~0.2 μA) | 差(±5%~±10%) |
看到没?HSE 启动要 2 毫秒,功耗 500 微安。LSI 启动只要 50 微秒,功耗 0.2 微安。这差距,你想想看。
核心原则:不需要高精度时,别开 HSE。不需要高频率时,别开 PLL。这是低功耗设计的第一条铁律。
2. 我的选择策略:按场景切源
我个人习惯,把项目场景分成三类:
- 全速运行场景:比如采集传感器数据、做 FFT 运算。这时候用 HSE + PLL,跑到 72 MHz 甚至更高。功耗高,但没办法,性能要保证。
- 待机监听场景:比如等待按键、等待 RTC 闹钟。这时候切到 HSI,关掉 PLL。频率降到 8 MHz,功耗能降 60% 以上。
- 深度休眠场景:比如电池供电的温湿度采集,一小时醒一次。这时候只用 LSI 驱动 RTC 和独立看门狗。系统电流能压到 2 μA 以下。
我在一个水表项目里遇到过一个问题:设备用电池供电,要求 5 年寿命。一开始用 HSE + PLL 跑 72 MHz,然后进 Stop 模式。结果发现电池半年就挂了。后来改成:唤醒后切 HSI 跑 8 MHz,采集完立刻进 Standby 模式,RTC 用 LSI 驱动。电流从 15 μA 降到了 1.8 μA。嗯,这才是正确的做法。
3. PLL 配置对功耗的影响
PLL 这东西,是个功耗大户。为什么?因为它内部有压控振荡器(VCO),还有反馈分频器。频率越高,VCO 的电流越大。
我实测过一组数据,用的是 STM32F103:
| 配置 | 系统频率 | 运行电流(典型) |
|---|---|---|
| HSI 直接输出 | 8 MHz | ~8 mA |
| HSE + PLL (x9) | 72 MHz | ~50 mA |
| HSE + PLL (x6) | 48 MHz | ~35 mA |
| HSE 直接输出 | 8 MHz | ~10 mA |
看到了吗?从 8 MHz 到 72 MHz,频率翻了 9 倍,电流翻了 6 倍。而且 PLL 本身就有基础功耗,即使输出频率很低,只要 PLL 使能,它就在耗电。
注意:PLL 的 VCO 频率范围通常是 64-128 MHz(不同系列有差异)。如果你只需要 24 MHz,不要用 PLL 从 8 MHz 倍频到 24 MHz。直接切 HSI 或 HSE 分频,关掉 PLL,功耗更低。
4. 时钟门控技术:白送的功耗优化
时钟门控,英文叫 Clock Gating。说白了就是:不用某个外设的时候,把它的时钟关掉。
STM32 的 RCC 寄存器里,有一堆 xxxEN 位。比如 RCC_APB2ENR_USART1EN、RCC_AHB1ENR_GPIOAEN。这些位就是时钟门控的开关。
我见过很多工程师,初始化的时候把所有外设时钟都打开了,然后就不管了。结果芯片在睡眠模式下,电流还是居高不下。为什么?因为外设时钟虽然没在工作,但时钟树还在给它们送时钟信号,触发器还在翻转,动态功耗一直在消耗。
正确的做法是:
// 错误示范:只开不管
RCC->APB2ENR |= RCC_APB2ENR_USART1EN | RCC_APB2ENR_SPI1EN | RCC_APB2ENR_TIM1EN;
// 正确做法:用前开,用完关
RCC->APB2ENR |= RCC_APB2ENR_USART1EN; // 开 USART1 时钟
// ... 配置 USART1,发送数据 ...
RCC->APB2ENR &= ~RCC_APB2ENR_USART1EN; // 关 USART1 时钟
小技巧:进入低功耗模式前,遍历一遍所有外设的时钟使能寄存器,把没用的都关了。我习惯写一个 DisableUnusedPeriphClocks() 函数,在进入 Sleep/Stop 模式前调用。这个习惯帮我省了不少电。
5. 知识结构图:时钟与功耗的关系
下面这张图,把时钟源、PLL、时钟门控和功耗的关系画清楚了。你可以把它当作本章的思维导图:
6. 避坑指南:我踩过的三个坑
做低功耗调试这些年,我在时钟系统上栽过不少跟头。挑三个典型的跟你说说:
- 坑一:RTC 用 LSI,时间越跑越偏。 我曾经在一个数据记录仪项目里,RTC 用 LSI 驱动。结果一天下来,时间误差超过 5 分钟。后来用频率计一测,LSI 实际输出只有 28 kHz,偏差 12.5%。解决方案:对时间精度有要求时,必须用 LSE。
- 坑二:退出 Stop 模式后,HSE 起振失败。 我遇到过芯片从 Stop 模式唤醒后,HSE 起振超时,系统卡死在 HSI 上。后来查手册才发现,HSE 起振需要时间,如果唤醒后立刻切到 HSE,可能还没稳定。解决方案:唤醒后先切 HSI,等 HSE 稳定后再切换。
- 坑三:时钟门控没关干净,休眠电流多出 10 μA。 有一次我怎么都找不到漏电点,最后用示波器量时钟输出脚,发现 SPI1 的时钟还在翻转。原来初始化 SPI 时开了时钟,但进休眠前忘了关。从那以后,我写了个专门的函数,进休眠前把所有外设时钟扫一遍。
调试小工具:用 RCC->AHBENR、RCC->APB1ENR、RCC->APB2ENR 这三个寄存器的值,可以快速判断哪些外设时钟还开着。我习惯在调试时把这三个值通过串口打印出来,一目了然。