这个问题涉及到STM32低功耗模式的中断唤醒机制。
答案是:先执行RTC的中断服务函数(RTC_IRQHandler),然后再返回到 HAL_PWR_EnterSTOPMode
函数之后的代码(即 SystemClock_Config()
)继续执行。
下面是详细的执行流程和原理分析:
唤醒过程详细流程
-
进入Stop模式:
- 当代码执行
HAL_PWR_EnterSTOPMode()
中的 WFI
(Wait For Interrupt) 指令后,内核停止执行,芯片进入低功耗的Stop模式。
-
RTC Alarm事件发生:
- RTC计时到达设定的闹钟时间,触发了Alarm事件。
-
产生中断请求:
- RTC Alarm事件会置位相应的中断标志位,并向NVIC(嵌套向量中断控制器)发出一个中断请求。
-
芯片唤醒:
- 这个中断请求会将芯片从Stop模式中唤醒。内核退出睡眠状态,时钟恢复。
-
执行中断服务程序:
- 唤醒后,内核的第一要务是处理中断。NVIC会检查中断源,并跳转到对应的中断向量表,找到
RTC_IRQHandler
函数的地址并开始执行。
- 在
RTC_IRQHandler
内部,通常会调用 HAL_RTC_AlarmIRQHandler
这个函数来处理Alarm中断,它会清除中断标志位并调用您事先设置好的回调函数(例如 HAL_RTC_AlarmAEventCallback
)。
-
退出中断服务程序:
- 中断服务程序执行完毕后,使用
BX LR
指令返回。这个返回地址,正是当初执行 WFI
指令后,程序计数器(PC)保存的下一条指令的地址。
-
继续执行主程序:
- 从中断返回后,程序流程就回到了
HAL_PWR_EnterSTOPMode
函数内部(紧跟在 WFI
指令之后)。该函数会执行一些必要的清理工作后也返回。
- 最终,代码继续执行
Enter_Stop_Mode
函数中 HAL_PWR_EnterSTOPMode
之后的代码,即重新配置系统时钟 SystemClock_Config()
和恢复SysTick HAL_ResumeTick()
。
为什么需要这样设计?
这种“先中断,后主流程”的设计是中断系统的标准行为,非常合理:
- 实时性:中断的核心目的是为了立即响应一个紧急事件。确保中断服务程序最先执行,可以最快地对事件做出处理(例如,记录唤醒时间、设置一个标志位、执行一个关键操作)。
- 安全性:
HAL_PWR_EnterSTOPMode
之后的代码(尤其是 SystemClock_Config()
)通常涉及对时钟树的重要配置。确保在配置时钟之前,所有可能依赖旧时钟的中断都已经处理完毕,可以避免潜在的错误。
代码编写建议
在您的代码中,通常会在RTC Alarm中断服务程序(或其回调函数)中做一些“轻量级”的操作,例如设置一个全局的标志变量 rtc_wakeup_flag = 1;
。
而在主循环或 Enter_Stop_Mode
函数之后,你再检查这个标志位,来决定后续要执行哪些具体的任务(例如读取传感器数据并发送)。
// 定义一个全局变量
volatile uint8_t rtc_wakeup_flag = 0;
// RTC Alarm回调函数(在中断上下文被调用)
void HAL_RTC_AlarmAEventCallback(RTC_HandleTypeDef *hrtc)
{
rtc_wakeup_flag = 1; // 仅仅设置标志位,不执行耗时操作
}
void Enter_Stop_Mode(void)
{
// ... 进入Stop模式前的配置 ...
HAL_PWR_EnterSTOPMode(PWR_LOWPOWERREGULATOR_ON, PWR_STOPENTRY_WFI);
// 从这里开始,是唤醒后执行的地方
SystemClock_Config();
HAL_ResumeTick();
// 检查唤醒标志,执行后续应用逻辑
if(rtc_wakeup_flag == 1) {
rtc_wakeup_flag = 0;
// 执行你的主要任务,例如:
Read_Sensor();
Send_Data();
}
// 任务执行完后,可以再次进入Stop模式
// Enter_Stop_Mode();
// 注意:在实际应用中,这可能会是一个循环,或者由主循环控制。
}
总结
顺序 |
执行内容 |
上下文 |
1 |
HAL_PWR_EnterSTOPMode(PWR_LOWPOWERREGULATOR_ON, PWR_STOPENTRY_WFI); |
主程序 |
2 |
(芯片进入Stop模式) |
- |
3 |
RTC Alarm事件发生,芯片唤醒 |
硬件事件 |
4 |
RTC_IRQHandler / HAL_RTC_AlarmIRQHandler / HAL_RTC_AlarmAEventCallback |
中断服务程序 |
5 |
SystemClock_Config(); |
主程序(Enter_Stop_Mode 函数) |
6 |
HAL_ResumeTick(); |
主程序(Enter_Stop_Mode 函数) |