4、时钟与外设管理:待机状态下的“断舍离”

好,咱们接着聊。前面几章我们把待机模式的进入和唤醒讲得差不多了。这一章,我重点说说时钟和外设的管理。

你想想看,一个系统在待机时,CPU都停了,你还开着PLL、HSE这些大功率时钟源干嘛?那不是白白浪费电嘛。我个人的习惯是,进入待机前,必须做一次彻底的“断舍离”——把不必要的时钟源统统关掉,把没用的外设时钟也禁掉。

4.1 为什么要关时钟源?

说白了,时钟源就是功耗的“大头”。

  • PLL(锁相环):这东西倍频上去,功耗可不小。我测过,一个8MHz的HSE经过PLL倍频到72MHz,PLL本身就能吃掉几百微安。待机时你根本用不着高频,关了它。
  • HSE(高速外部晶振):外部晶振起振后,振荡电路一直在工作。待机时如果不需要精确的RTC时钟,完全可以关掉。
  • HSI(高速内部振荡器):这个相对省电一些,但待机时如果连CPU都不跑了,留着它也没意义。

嗯,这里要注意:关时钟源不是随便关的。你得确保系统在唤醒后能重新初始化时钟树。我见过有人把HSE关了,结果唤醒后忘了重新配置PLL,系统直接跑在HSI上,时序全乱套了。

4.2 禁用未使用的外设时钟

这个其实是个基本功。很多工程师在初始化时把外设时钟都打开了,但待机时忘了关。你想想看,一个USART模块,即使不发送数据,只要时钟还开着,它就在消耗电流。

我建议的做法是:

  1. 在进入待机前,遍历所有外设,把那些不需要的时钟位清零。
  2. 保留必要的外设,比如RTC、LPTIM(低功耗定时器)、某些唤醒引脚对应的GPIO。
  3. 注意DMA和中断控制器,它们的时钟有时会被忽略。

我曾经在一个项目中,待机电流怎么也降不下来。查了半天,发现是DMA的时钟没关。那个DMA虽然没在传输数据,但时钟一开,寄存器还在工作,白白多耗了50微安。你说冤不冤?

4.3 配置RTC作为定时唤醒源

这是待机方案的核心之一。RTC(实时时钟)在待机模式下依然可以运行,靠的是低速外部晶振(LSE)或者低速内部振荡器(LSI)。

我个人习惯用LSE,32.768kHz那种。精度高,功耗也低。LSI虽然省电,但温漂大,做定时唤醒容易不准。

配置RTC唤醒的步骤大致如下:

// 1. 使能RTC时钟(如果还没使能)
RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE);

// 2. 允许访问备份域
PWR_BackupAccessCmd(ENABLE);

// 3. 配置RTC时钟源为LSE
RCC_LSEConfig(RCC_LSE_ON);
while (RCC_GetFlagStatus(RCC_FLAG_LSERDY) == RESET); // 等待LSE稳定

RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE);

// 4. 使能RTC
RCC_RTCCLKCmd(ENABLE);

// 5. 等待RTC寄存器同步
RTC_WaitForSynchro();

// 6. 配置唤醒定时器
RTC_SetWakeUpCounter(0x7FFF); // 设置唤醒计数值
RTC_WakeUpCmd(ENABLE);        // 使能唤醒功能

// 7. 使能RTC唤醒中断(在NVIC中也要使能)
RTC_ITConfig(RTC_IT_WUT, ENABLE);

这里有个坑:RTC的唤醒中断在待机模式下是有效的,但前提是你得在进入待机前把中断使能配置好。我刚开始做的时候,忘了使能NVIC中的RTC中断线,结果待机后怎么都唤不醒。查了两天,才发现是中断没配通。

注意:RTC唤醒中断的优先级要设置好。如果优先级太低,可能会被其他中断抢占,导致唤醒失败。我一般设成最高优先级(0)。

4.4 时钟切换的“平滑过渡”

进入待机前,你可能需要从PLL切换到HSI或者直接关闭系统时钟。这个切换过程要小心。

我建议的流程是:

  1. 先把系统时钟切换到HSI(或者直接关闭,如果支持的话)。
  2. 然后关闭PLL、HSE。
  3. 最后再进入待机模式。

为什么要先切换?因为如果你直接关PLL,系统时钟可能瞬间丢失,导致CPU跑飞。我遇到过这种情况,代码直接卡死在某个中断里,再也出不来了。

小技巧:在切换时钟源时,可以用一个临时变量保存当前时钟状态。唤醒后根据这个状态恢复时钟树,避免重复初始化。

4.5 实测数据:关与不关的差别

我拿STM32F103做过一个测试,大家看看数据:

状态 时钟源 外设时钟 待机电流
未优化 PLL+HSE+HSI全开 所有外设时钟使能 约 850 µA
部分优化 仅保留HSI 关闭大部分外设 约 120 µA
完全优化 仅保留LSE(给RTC) 仅保留RTC和唤醒GPIO 约 3.5 µA

看到了吧?从850微安降到3.5微安,差了200多倍。这就是时钟和外设管理的威力。

4.6 避坑指南

最后,我总结几个容易踩的坑:

  • 坑一:关掉HSE后,RTC如果依赖HSE分频,会直接停摆。所以RTC一定要用LSE或LSI。
  • 坑二:有些外设的时钟是“链式”的。比如USART的时钟关了,但它的GPIO复用功能可能还开着。要一起关。
  • 坑三:唤醒后,时钟树恢复的顺序很重要。先开HSE,再开PLL,最后切换系统时钟。顺序错了,系统可能直接死机。

嗯,这一章的内容就这些。时钟和外设管理,说白了就是“该关的关,该留的留”。你只要把每个模块的功耗都抠出来,待机电流自然就降下去了。下一章,我们聊聊GPIO和唤醒源的具体配置,那个更细,也更考验耐心。