引言
在嵌入式低功耗设计中,STOP 模式是功耗与唤醒速度的折中首选。STM32F4 的 STOP 模式电流可低至 µA 级,但唤醒延迟(通常 10-100µs)受时钟源、电压调节器状态等因素影响,导致时序敏感任务(如精确采样、无线时隙同步)出现抖动。RTC 闹钟可周期性唤醒,但精度受限于 LSE 32.768kHz;LPTIM 支持低功耗运行,却无法在 STOP 下独立计时(需外部时钟)。本文组合两者:RTC 负责粗粒度唤醒(毫秒级),LPTIM 在唤醒后立即进行微秒级校准,从而对齐外部事件。
原理剖析
1. STOP 模式唤醒路径
- 进入 STOP:
PWR_EnterSTOPMode(PWR_Regulator_LowPower, PWR_STOPEntry_WFI),内核时钟停止,但 RTC、LPTIM(若配置为外部时钟)可继续运行。 - 唤醒源:RTC 闹钟(WKUP 引脚或 EXTI 线)、LPTIM 比较事件(需连接至 EXTI)、外部中断等。
- 唤醒延迟:从事件发生到 CPU 执行第一条指令,包含时钟稳定时间(HSI 启动约 8µs)、电压调节器恢复时间(低功耗模式需 30-50µs)。
2. RTC 闹钟特性
- 使用 LSE(32.768kHz)或 LSI 时钟,闹钟比较寄存器支持秒/分钟/小时/日期,精度最低 1 秒。
- 唤醒周期可配置为 1Hz 至 1 天,但无法产生亚秒级中断(除非使用 RTC 亚秒寄存器,但 F4 系列不支持)。
3. LPTIM 特性
- LPTIM 可在 STOP 模式下运行,但必须选择外部时钟源(如 LSE 或外部引脚时钟)。
- 支持 16 位计数器,可产生比较匹配中断,并映射到 EXTI 唤醒。
- 由于 LSE 频率固定,LPTIM 可实现 1/32768 秒(约 30.5µs)的定时精度,但无法覆盖长时间(最大 2 秒)。
4. 组合策略
- 设计目标:在唤醒时刻 T_wake 对齐到微秒级。
- 方案:RTC 设置闹钟在 T_wake 前 5ms 唤醒 CPU(粗唤醒),CPU 唤醒后立即启动 LPTIM 计时,LPTIM 定时至剩余时间(5ms - 实际已过时间),然后触发精确事件(如 GPIO 翻转或 DMA)。
- 关键:利用 RTC 唤醒后的固定延迟(可测量)进行补偿,LPTIM 负责微调。
配置步骤
1. 时钟与电源配置
void SystemClock_Config(void) {
// 使能 PWR 时钟
__HAL_RCC_PWR_CLK_ENABLE();
// 配置 LSE 作为 RTC 和 LPTIM 时钟源
__HAL_RCC_LSE_CONFIG(RCC_LSE_ON);
while (__HAL_RCC_GET_FLAG(RCC_FLAG_LSERDY) == RESET);
// 选择 LSE 作为 RTC 时钟
__HAL_RCC_RTC_CONFIG(RCC_RTCCLKSOURCE_LSE);
__HAL_RCC_RTC_ENABLE();
// 使能 LPTIM1 时钟(挂在 APB1,但时钟源为 LSE)
__HAL_RCC_LPTIM1_CLK_ENABLE();
}
2. RTC 闹钟配置
void RTC_Alarm_Config(void) {
RTC_HandleTypeDef hrtc;
hrtc.Instance = RTC;
hrtc.Init.HourFormat = RTC_HOURFORMAT_24;
hrtc.Init.AsynchPrediv = 127; // 异步分频,产生 256Hz
hrtc.Init.SynchPrediv = 255; // 同步分频,产生 1Hz
HAL_RTC_Init(&hrtc);
// 设置闹钟在 5 秒后(示例)
RTC_AlarmTypeDef sAlarm = {0};
sAlarm.AlarmTime.Hours = 0;
sAlarm.AlarmTime.Minutes = 0;
sAlarm.AlarmTime.Seconds = 5;
sAlarm.AlarmSubSecondMask = RTC_ALARMSUBSECONDMASK_ALL;
sAlarm.AlarmMask = RTC_ALARMMASK_DATE_WEEKDAY; // 仅匹配时分秒
sAlarm.Alarm = RTC_ALARM_A;
HAL_RTC_SetAlarm_IT(&hrtc, &sAlarm, RTC_FORMAT_BIN);
// 使能 EXTI 线 17(RTC 闹钟)
HAL_NVIC_SetPriority(RTC_Alarm_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(RTC_Alarm_IRQn);
}
3. LPTIM 配置
void LPTIM_Config(void) {
LPTIM_HandleTypeDef hlptim;
hlptim.Instance = LPTIM1;
hlptim.Init.Clock.Source = LPTIM_CLOCKSOURCE_APBCLOCK_LPOSC; // 使用 LSE
hlptim.Init.Clock.Prescaler = LPTIM_PRESCALER_DIV1;
hlptim.Init.Trigger.Source = LPTIM_TRIGSOURCE_SOFTWARE;
hlptim.Init.OutputPolarity = LPTIM_OUTPUTPOLARITY_HIGH;
hlptim.Init.UpdateMode = LPTIM_UPDATE_IMMEDIATE;
hlptim.Init.CounterSource = LPTIM_COUNTERSOURCE_INTERNAL;
HAL_LPTIM_Init(&hlptim);
// 设置比较值,例如 100 个 LSE 周期(约 3.05ms)
HAL_LPTIM_SetOnce_IT(&hlptim, 100);
// 使能 EXTI 线 23(LPTIM1 比较事件)
HAL_NVIC_SetPriority(LPTIM1_IRQn, 1, 0);
HAL_NVIC_EnableIRQ(LPTIM1_IRQn);
}
4. 进入 STOP 与唤醒处理
volatile uint32_t wake_time_us;
void EnterStopWithRTC(void) {
// 设置 RTC 闹钟为 5ms 后(通过修改秒和亚秒?F4 不支持亚秒,故用秒,但可设置秒+1,然后测量延迟)
// 为简化,此处使用秒级,实际可结合 RTC 预分频值产生 1Hz 中断,但精度不足,故用 LPTIM 校准。
// 进入 STOP
HAL_PWR_EnterSTOPMode(PWR_LOWPOWERREGULATOR_ON, PWR_STOPENTRY_WFI);
// 唤醒后重新配置系统时钟(因为 STOP 会关闭 HSI)
SystemClock_Config();
// 记录唤醒时刻(通过 DWT 计数器)
wake_time_us = DWT_GetMicrosecond();
}
void RTC_Alarm_IRQHandler(void) {
HAL_RTC_AlarmIRQHandler(&hrtc);
}
void HAL_RTC_AlarmAEventCallback(RTC_HandleTypeDef *hrtc) {
// RTC 唤醒,启动 LPTIM 进行精确定时
HAL_LPTIM_SetOnce_IT(&hlptim, 150); // 例如 150*30.5us ≈ 4.575ms
HAL_LPTIM_Start_IT(&hlptim);
}
void LPTIM1_IRQHandler(void) {
HAL_LPTIM_IRQHandler(&hlptim);
}
void HAL_LPTIM_CompareMatchCallback(LPTIM_HandleTypeDef *hlptim) {
// 精确时刻到达,执行任务(如 GPIO 翻转)
HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_0);
}
完整代码示例(整合)
// main.c 核心部分
int main(void) {
HAL_Init();
SystemClock_Config();
GPIO_Init(); // 配置 PA0 为输出
DWT_Init(); // 初始化 DWT 计数器用于微秒计时
RTC_Alarm_Config();
LPTIM_Config();
while (1) {
// 设置 RTC 闹钟为 1 秒后(示例)
Set_RTC_Alarm_Seconds(1);
// 进入 STOP
HAL_PWR_EnterSTOPMode(PWR_LOWPOWERREGULATOR_ON, PWR_STOPENTRY_WFI);
// 唤醒后,系统时钟已重新配置,但 LPTIM 和 RTC 仍运行
// 此时可进行任务
// 注意:LPTIM 在 STOP 唤醒后可能仍在运行,但需重新初始化?
}
}
// 精确延时函数(基于 DWT)
void DWT_Delay_us(uint32_t us) {
// 实现略
}
注意事项
- 时钟源选择:LPTIM 必须使用 LSE 或外部时钟,否则在 STOP 下无法运行。确保 LSE 稳定后再进入 STOP。
- 唤醒延迟补偿:每次唤醒延迟可能不同(受温度、电压影响),建议在运行时测量实际延迟(如通过 GPIO 翻转和逻辑分析仪),并动态调整 LPTIM 比较值。
- RTC 闹钟精度:F4 的 RTC 闹钟仅支持秒级,若需毫秒级,可考虑使用 RTC 的唤醒定时器(WUT)但精度仍有限。本方案中 RTC 只负责粗唤醒,实际时序由 LPTIM 保证。
- LPTIM 溢出:16 位计数器在 32.768kHz 下最大约 2 秒,若需更长,可级联或使用外部分频。
- 中断优先级:RTC 和 LPTIM 中断优先级需合理设置,避免在唤醒处理中被打断。
- 系统时钟恢复:从 STOP 唤醒后,需重新配置系统时钟(如 HSI),否则外设时钟可能异常。
- 调试建议:使用逻辑分析仪测量 GPIO 翻转时刻,对比预期时间,调整 LPTIM 比较值。
总结
通过 RTC 闹钟的粗粒度唤醒与 LPTIM 的微调,STM32F4 在 STOP 模式下可实现微秒级的时序对齐。该方法兼顾低功耗与实时性,适用于周期性精确采样、无线通信同步等场景。开发者需注意时钟源配置和唤醒延迟补偿,通过实际测量优化参数,即可获得稳定可靠的时序性能。