第2章:STM32时钟系统与功耗关系

时钟,是MCU的心脏。心跳多快,功耗就有多高。这个道理很简单,但实际项目中,很多人栽在时钟配置上。

我记得刚做低功耗产品那会儿,一块电池供电的传感器板,怎么调都多出2mA电流。查了三天,最后发现是HSI没关,外设时钟还在跑。嗯,从那以后,我每次看原理图第一件事就是先理清时钟树。

2.1 时钟树架构详解

STM32的时钟树,说白了就是一套时钟源 + 分频器 + 开关的网络。你想想看,CPU、外设、总线、RTC,它们各自需要不同频率的时钟。如果全都用同一个高频时钟,那功耗肯定爆炸。

下面这张图,是我画的一个简化版时钟树结构。它帮你快速理解时钟是怎么分发的。

STM32时钟树简化结构图 HSE (8MHz) HSI (16MHz) LSE (32.768kHz) LSI (32kHz) PLL SYSCLK AHB (HCLK) APB1 (≤36MHz) APB2 (≤72MHz) RTC / 独立看门狗 说明: HSE/HSI 经 PLL 倍频后产生 SYSCLK,再分频给 AHB、APB1、APB2 总线。 LSE/LSI 专供 RTC 和独立看门狗,功耗极低,适合休眠时保持计时。

从图上你能看到,时钟源有四个:HSE(外部高速)、HSI(内部高速)、LSE(外部低速)、LSI(内部低速)。它们经过PLL倍频后,生成SYSCLK,再分发给AHB、APB1、APB2总线。

这里有个关键点:APB1最高只能跑到36MHz(F1系列),而APB2可以到72MHz。如果你把APB1的外设时钟配高了,不仅外设可能跑飞,功耗也会白白增加。

2.2 HSE/LSE/HSI/LSI的选择策略

选哪个时钟源,直接决定了你的功耗基线。我列个表,你一看就明白。

时钟源 频率 典型功耗 适用场景
HSE 4-16MHz(常用8MHz) 高(需外部晶振+起振电流) 需要高精度主频,如通信、数据处理
HSI 8/16MHz(取决于型号) 中(内部RC,无需外部元件) 快速启动、低成本设计
LSE 32.768kHz 极低(μA级) RTC日历、低功耗休眠唤醒
LSI 约32kHz(精度差) 极低(μA级) 独立看门狗、低精度RTC

我的经验:如果产品需要长时间休眠,我建议用LSE做RTC时钟源。LSI虽然省电,但温度漂移大,一天能差几分钟。曾经有个客户反馈设备时间越走越偏,最后发现是LSI惹的祸。

选型时,你可以这样判断:

  • 追求低功耗 + 不需要高精度主频 → 直接用HSI,省掉外部晶振,省2个引脚,省PCB面积。
  • 需要高精度通信(如CAN、USB) → 必须用HSE,内部RC精度不够,通信会丢包。
  • 休眠时需要保持时间 → 用LSE,功耗只有几个μA。
  • 只是喂狗,不需要精确时间 → LSI就够了,省一个晶振。

2.3 系统时钟与总线时钟的配置对功耗的影响

时钟频率和功耗的关系,不是线性的,而是近似正比。频率翻倍,动态功耗几乎翻倍。但静态功耗(漏电流)基本不变。

所以,调优的核心思路是:够用就好,别多给。

2.3.1 系统时钟(SYSCLK)配置

SYSCLK是CPU的运行频率。跑得越快,处理能力越强,但功耗也越高。我见过有人把STM32F103超到128MHz,结果芯片发烫,电池半天就没电。

实际项目中,我一般这样配:

  • 待机/低负载:SYSCLK降到8-16MHz,用HSI直接输出,不经过PLL。
  • 中等负载:SYSCLK 48-64MHz,用HSE+PLL,平衡性能和功耗。
  • 高负载:SYSCLK 72MHz(F1上限),只在需要大量计算时短暂使用。

关键公式:动态功耗 ≈ C × V² × f。其中f是时钟频率,V是工作电压。频率降一半,功耗降一半。电压降一点,功耗平方级下降。

2.3.2 总线时钟(AHB/APB)配置

总线时钟控制着外设的访问速度。很多人只关注SYSCLK,忽略了总线时钟。其实,总线时钟每降低1MHz,都能省下几十μA。

我举个例子:

  • APB1上挂的是低速外设(UART、I2C、SPI、TIM),这些外设通常不需要高频时钟。APB1最高36MHz,但如果你只跑115200波特率的UART,APB1给8MHz就绰绰有余。
  • APB2上挂的是高速外设(ADC、SPI1、TIM1),如果不用这些外设,可以把APB2时钟降到最低。

注意:降低总线时钟时,要确保外设的时钟源满足其最低工作频率。比如,SPI通信速率取决于APB时钟,配低了会导致通信失败。

2.3.3 外设时钟门控

这是最容易被忽略的一点。STM32每个外设都有独立的时钟使能位。即使SYSCLK和总线时钟都配好了,如果某个外设的时钟没关,它依然在耗电。

我曾经在一个项目中,发现UART1明明没用,但功耗多了0.5mA。查了半天,原来是初始化代码里开了RCC_APB2Periph_USART1,但后面忘了关。

正确的做法是:

// 开启外设时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
// 使用外设...
// 使用完毕后,关闭外设时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, DISABLE);

嗯,这个习惯一定要养成。每次配完外设,顺手把不用的时钟关掉。

2.4 实战配置建议

下面是我个人习惯的一套低功耗时钟配置流程,你可以参考:

  1. 确定应用场景:是持续运行还是间歇工作?需要高精度吗?
  2. 选择主时钟源:低功耗选HSI,高精度选HSE。
  3. 配置PLL:如果不需要高频,直接旁路PLL,省掉PLL的功耗(约几十μA)。
  4. 分频总线时钟:AHB、APB1、APB2按需分频,能低则低。
  5. 关闭未用外设时钟:在初始化末尾,遍历所有外设,关掉不用的。
  6. 休眠前降频:进入Stop模式前,把SYSCLK降到最低,再切到HSI。

一个小技巧:在调试阶段,可以用RCC_GetClocksFreq()函数打印出当前所有时钟频率,确认配置是否符合预期。我每次调完时钟都会跑一遍这个函数,心里踏实。

好了,时钟系统这部分就讲到这里。记住一句话:时钟是功耗的源头,管好时钟,就管好了一半的功耗。