4. 定时器唤醒:RTC定时唤醒、LPTIM的使用与唤醒模式选择

各位同学,咱们接着聊低功耗唤醒。前面讲了外部中断唤醒,那是靠“外力”叫醒系统。但很多时候,我们需要系统自己“定个闹钟”,到点了就起来干活。这就是定时器唤醒的核心思想。

我个人习惯把定时器唤醒比作“嵌入式系统的闹钟”。你设定好时间,到点它就响。只不过这个“闹钟”的功耗要低得多,而且精度和灵活性各有千秋。今天咱们重点看两种:RTC(实时时钟)和LPTIM(低功耗定时器)。

4.1 RTC定时唤醒:最常用的“万年历闹钟”

RTC,说白了就是一个独立于主系统运行的时钟。即使主芯片进入深度睡眠,RTC模块依然在跑,靠一个独立的32.768kHz晶振或者内部低速RC振荡器维持计时。

为什么选32.768kHz? 因为2的15次方正好是32768,分频后很容易得到1秒的精准计时。这个频率的晶振功耗也极低,非常适合电池供电场景。

我在项目中遇到过一个问题:某款智能水表,要求每小时上报一次数据。如果用主芯片的定时器,每唤醒一次要消耗几十微安的电流。后来改用RTC唤醒,待机电流直接降到了1.5微安以下。嗯,这就是RTC的价值。

核心要点: RTC唤醒通常支持两种模式——绝对时间唤醒(比如每天上午8点)和相对时间唤醒(比如30分钟后)。实际项目中,相对时间唤醒用得更多。

来看一个典型的RTC配置代码片段(以STM32为例):

// 配置RTC唤醒定时器,每30秒唤醒一次
void RTC_WakeUp_Config(void)
{
    // 使能RTC时钟
    RTC_ClockCmd(RTC_CLOCK_ENABLE);
    
    // 设置唤醒计数器,假设RTC时钟为1Hz
    // 唤醒周期 = (计数器值 + 1) 秒
    RTC_SetWakeUpCounter(29);  // 30秒唤醒一次
    
    // 使能唤醒中断
    RTC_WakeUpCmd(ENABLE);
    RTC_ITConfig(RTC_IT_WUT, ENABLE);
}

// 唤醒中断服务函数
void RTC_WKUP_IRQHandler(void)
{
    if(RTC_GetITStatus(RTC_IT_WUT) != RESET)
    {
        // 清除中断标志
        RTC_ClearITPendingBit(RTC_IT_WUT);
        
        // 这里放唤醒后的处理代码
        System_WakeUp_Handler();
    }
}

个人经验: 配置RTC唤醒时,记得先检查RTC是否已经初始化。我曾经在调试时忘了这个步骤,结果芯片死活不唤醒,查了半天才发现RTC根本没跑起来。你想想看,闹钟都没电,它怎么会响呢?

4.2 LPTIM:更灵活的低功耗定时器

LPTIM(低功耗定时器)是近年来MCU上越来越常见的模块。它比RTC更灵活,但功耗略高一点点。LPTIM可以在多种低功耗模式下运行,包括Stop模式甚至Standby模式(取决于具体芯片)。

LPTIM的优势在于:

  • 时钟源多样:可以使用内部低速RC、外部晶振,甚至某些IO口的脉冲
  • 分辨率更高:通常支持微秒级的定时,而RTC一般是秒级
  • 支持多种触发模式:除了定时溢出,还能做输入捕获、PWM输出等

说白了,如果你需要“短时间、高精度”的定时唤醒,LPTIM比RTC更合适。比如一个传感器需要每100毫秒采集一次数据,用RTC就太粗糙了,LPTIM正好。

我记得有一次做可穿戴设备,需要每50毫秒检测一次心率。用RTC根本做不到,用主定时器又太耗电。最后选了LPTIM,在Stop模式下跑,功耗只有2微安左右,完美解决问题。

// LPTIM配置示例:每100ms唤醒一次
void LPTIM_Config(void)
{
    // 使能LPTIM时钟
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_LPTIM, ENABLE);
    
    // 配置LPTIM参数
    LPTIM_InitTypeDef lptim_init;
    lptim_init.LPTIM_Clock = LPTIM_CLOCK_SOURCE_APB1_LPOSC;  // 使用低速时钟
    lptim_init.LPTIM_Prescaler = LPTIM_PRESCALER_DIV16;      // 分频
    lptim_init.LPTIM_AutoReload = 999;                       // 自动重装载值
    LPTIM_Init(LPTIM, &lptim_init);
    
    // 使能比较匹配中断
    LPTIM_ITConfig(LPTIM_IT_ARRM, ENABLE);
    
    // 启动定时器
    LPTIM_Cmd(LPTIM, ENABLE);
}

避坑指南: 我曾经在LPTIM的时钟源选择上栽过跟头。有些芯片的LPTIM在低功耗模式下只能使用特定的时钟源,比如LSE或LSI。如果你选了APB时钟,进入Stop模式后时钟就停了,定时器自然也就罢工了。所以配置前一定要看芯片手册的时钟树,确认低功耗模式下哪些时钟还活着。

4.3 周期性唤醒 vs 一次性唤醒

这两个概念其实很好理解。但很多新手容易混淆,我简单说说。

特性 周期性唤醒 一次性唤醒
工作方式 定时器自动重装载,持续产生唤醒事件 定时器计数到目标值后停止,只唤醒一次
典型应用 传感器周期性采集、心跳包上报 延时启动、单次任务触发
功耗对比 相对较高(需要持续维护定时器状态) 更低(唤醒后定时器可关闭)
配置复杂度 需要设置自动重装载值 只需设置比较值,无需重装载

周期性唤醒:就像你每天早上7点的闹钟,天天响。定时器溢出后自动重新加载计数值,继续下一轮计时。适合需要持续、规律执行任务的场景。

一次性唤醒:就像你设了个“30分钟后提醒我”的倒计时,响完就没了。定时器只跑一次,到达目标值后停止。适合那些只需要执行一次的任务,比如“开机后5分钟自动进入深度睡眠”。

你想想看,如果某个任务只需要执行一次,你却用了周期性唤醒,那定时器就会一直跑,白白浪费电流。我见过有人用RTC做一次性唤醒,结果忘了关闭自动重装载功能,系统每隔几秒就醒一次,电池两天就耗光了。嗯,这就是没搞清楚两种模式区别的后果。

我的建议: 在项目初期就明确需求——这个唤醒是“一次性的”还是“周期性的”?如果是周期性的,周期是固定的还是可变的?固定周期用RTC或LPTIM的自动重装载模式,可变周期则需要在唤醒后重新配置定时器。

4.4 实际项目中的选择策略

说了这么多,到底什么时候用RTC,什么时候用LPTIM?我总结了几条经验:

  1. 唤醒间隔大于1秒:优先选RTC。功耗最低,而且有独立的日历功能,适合做时间戳。
  2. 唤醒间隔在毫秒级:选LPTIM。RTC的秒级精度不够用。
  3. 需要绝对时间唤醒:比如“每天凌晨3点上报数据”,必须用RTC。
  4. 需要高精度短定时:比如“每10毫秒检测一次按键”,LPTIM更合适。
  5. 芯片支持情况:有些低端MCU没有LPTIM,只能用RTC或者主定时器的低功耗模式。

最后提醒一句:无论用哪种定时器唤醒,都要注意唤醒后的系统恢复时间。从低功耗模式唤醒到CPU开始执行代码,通常需要几十到几百微秒。如果你的任务对实时性要求很高,这个时间必须算进去。

好了,定时器唤醒这部分就讲到这里。下一节咱们聊聊更高级的唤醒方式——传感器触发唤醒,看看怎么让外部环境变化来叫醒系统。