Electronic – STM32 Sleep Mode: Interrupt gets executed but the CPU stays in WFI

armcmicrocontrollersleepstm32

I'm fairly new to the ARM architecture and I'm working with a board that contains STM32F0 microcontroller and an RF modem, which sends an interrupt every time it receives a message. I'm trying to implement a sleep mode in a microcontroller to save power but I don't quite understand why it behaves the way it does.

I started with a simple program that puts the CPU into sleep mode at the beginning of the execution. Then it waits for the external interrupt to wake it up, execute the interrupt and continue with the main program. In the ISR I toggle an LED on pin PA0 every time the routine is called, but then use pin PA1 in the main() that toggles and LED on and off with a short delay, indicating that the microcontroller exited the sleep mode.

My code basically looks like this:

int main()
{
    periph_init();
    modem_init();

    HAL_PWR_EnterSLEEPMode(0,1);

    while(1)
    {
        HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_1);
        Delay();
    }
}

And the ISR for the EXTI

void EXTI0_1_IRQHandler(void)
{
    HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_0);

    // Some other stuff

    HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_1);
}

The ARMv6 Architecture Reference Manual states the following regarding the Wait For Interrupt on the page B1-243:

When a processor issues a WFI instruction it can suspend execution and enter a low-power state. It can remain
in that state until the processor detects one of the following WFI wake-up events:

  • A reset.

  • An asynchronous exception at a priority that, if PRIMASK.PM was set to 0, would preempt any currently active exceptions.

  • If debug is enabled, a debug event.

  • An IMPLEMENTATION DEFINED WFI wakeup event.

If my understanding is correct, the second line applies in this case. Which means, that calling any interrupt regardless of its priority would wake up the processor, since the WFI was called from the main. For information, all the implemented interrupt priorities are set to 0 except for the EXTI, which is set to -1.

Now this is what actually happens. The LED on PA0 toggles appropriately, indicating that the ISR does indeed execute. But the LED on PA1 stays turned off, which means that upon completing the ISR, the CPU remains in a WFI. This is confirmed even during the debugging, while I inserted a breakpoint in the interrupt service routine. After letting the code run, it succesfully stops at the breakpoint. If I then continue debugging with a step-by-step function, the program jumps to the WFI instruction (upon which it continues to the main, since debugging can cause to terminate the WFI).

My question is, why does the CPU return to the WFI when the interrupt clearly gets called? Given my understanding, if the CPU is in WFI, it should continue with it's operation wherever it left off as soon as any interrupt get called.

Best Answer

Check if the SLEEPONEXIT bit in SCB->SCR is set. Setting that to 1 would cause exactly what you've described:

Bit 1 SLEEPONEXIT

Configures sleep-on-exit when returning from Handler mode to Thread mode. Setting this bit to 1 enables an interrupt-driven application to avoid returning to an empty main application.

0: Do not sleep when returning to Thread mode.

1: Enter sleep, or deep sleep, on return from an interrupt service routine.