WFI(Wait For Interrupt)指令,常用于让 MCU 进入低功耗状态。

但很多工程师不知道的是:执行 WFI 并不一定保证 MCU 会进入低功耗模式,在某些情况下它会被直接当做 NOP 执行。

如果在执行 WFI 的那一刻,NVIC 里已经存在一个"使能且优先级足够"的 pending 中断,那么WFI就会被当做NOP执行。
听起来有点抽象,看下面几个例子就明白了:
1)示例1:pending + 使能 → WFI 被当 NOP
SCB->SCR |= (1<<2);
NVIC_EnableIRQ(TIM3_IRQn);
NVIC_SetPendingIRQ(TIM3_IRQn);
__WFI();

2)示例 2:pending 但未使能 → 正常进入 DeepSleep

现象:MCU 正常进入 DeepSleep,电流降至
μA 级。

原因: 虽然 TIM3 的 pending 位被置位,但中断未使能,NVIC 不会向处理器提交该异常,WFI 条件检查通过,正常休眠。3)示例 3:__disable_irq() + pending → 仍然不进 DeepSleep
SCB->SCR |= (1<<2);
NVIC_EnableIRQ(TIM3_IRQn);
NVIC_SetPendingIRQ(TIM3_IRQn);
__disable_irq();
__WFI();__disable_irq() 设置 PRIMASK = 1,屏蔽所有可配置中断。此时执行 WFI,处理器仍然检测到 pending 异常(TIM3),WFI 被当做 NOP 执行。void EXTI2_3_IRQHandler(void)
{
if (std_exti_get_pending_status(EXTI_LINE_GPIO_PIN3))
{
std_exti_clear_pending(EXTI_LINE_GPIO_PIN3);
}
SCB->SCR |= (1<<2);
NVIC_EnableIRQ(TIM3_IRQn);
NVIC_SetPriority(TIM3_IRQn, NVIC_PRIO_0);
NVIC_SetPendingIRQ(TIM3_IRQn);
__WFI();
}现象: WFI 没有进入 DeepSleep。
原因: TIM3 优先级(0)高于当前正在执行的 EXTI 中断优先级(1)。Cortex-M 规则是:只有 pending 中断的优先级严格高于当前异常优先级时,WFI 才会被唤醒。这里 TIM3 优先级更高,处理器判定它可以在退出当前 ISR 后立即抢占,因此 WFI 不生效。
如果将 TIM3 优先级改为 1 或 2(≤ 当前优先级),则 TIM3 无法抢占,WFI 正常进入 DeepSleep。
WFI 的"检查 pending 中断"机制意味着:任何在 WFI 执行瞬间已经 pending 且使能的中断,都会阻止 MCU 进入低功耗模式。即使某个外设中断不是你配置的 DeepSleep 唤醒源(比如 TIM3 在 DeepSleep 下时钟被关),它的 NVIC pending 位仍然可能在进入 WFI 前被意外置位,从而阻止休眠。所以如果你不希望出现上述情况,在进入DeepSleep前要调用NVIC_ClearPendingIRQ()清除掉相关标志位,或者清除标志位且从源头上关掉这个中断,唤醒后需要的话再重新开启。
------------ END ------------