Modo de suspensión STM32: se ejecuta la interrupción pero la CPU permanece en WFI

Soy bastante nuevo en la arquitectura ARM y estoy trabajando con una placa que contiene un microcontrolador STM32F0 y un módem RF, que envía una interrupción cada vez que recibe un mensaje. Estoy tratando de implementar un modo de suspensión en un microcontrolador para ahorrar energía, pero no entiendo muy bien por qué se comporta de esa manera.

Empecé con un programa simple que pone la CPU en modo de suspensión al comienzo de la ejecución. Luego espera a que la interrupción externa lo despierte, ejecuta la interrupción y continúa con el programa principal. En el ISR, cambio un LED en el pin PA0 cada vez que se llama a la rutina, pero luego uso el pin PA1 en main() que enciende y apaga el LED con un breve retraso, lo que indica que el microcontrolador salió del modo de suspensión.

Mi código básicamente se ve así:

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

    HAL_PWR_EnterSLEEPMode(0,1);

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

Y el ISR para la EXTI

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

    // Some other stuff

    HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_1);
}

El Manual de referencia de la arquitectura ARMv6 establece lo siguiente con respecto a la espera de interrupción en la página B1-243:

Cuando un procesador emite una instrucción WFI, puede suspender la ejecución y entrar en un estado de bajo consumo. Puede permanecer en ese estado hasta que el procesador detecte uno de los siguientes eventos de activación de WFI:

  • Se establecen.

  • Una excepción asíncrona con una prioridad que, si PRIMASK.PM se estableciera en 0, se adelantaría a cualquier excepción actualmente activa.

  • Si la depuración está habilitada, un evento de depuración.

  • Un evento de activación WFI DEFINIDO POR LA IMPLEMENTACIÓN.

Si mi entendimiento es correcto, la segunda línea se aplica en este caso. Lo que significa que llamar a cualquier interrupción independientemente de su prioridad despertaría el procesador, ya que el WFI se llamó desde la red principal. Para información, todas las prioridades de interrupción implementadas se establecen en 0 excepto EXTI, que se establece en -1.

Ahora bien, esto es lo que realmente sucede. El LED en PA0 alterna apropiadamente, lo que indica que el ISR sí se ejecuta. Pero el LED en PA1 permanece apagado, lo que significa que al completar el ISR, la CPU permanece en WFI. Esto se confirma incluso durante la depuración, mientras insertaba un punto de interrupción en la rutina del servicio de interrupción. Después de dejar que el código se ejecute, se detiene con éxito en el punto de interrupción. Si luego continúo con la depuración con una función paso a paso, el programa salta a la instrucción WFI (sobre la cual continúa a la principal, ya que la depuración puede provocar la finalización de la WFI).

Mi pregunta es, ¿por qué la CPU regresa al WFI cuando claramente se llama a la interrupción? Según tengo entendido, si la CPU está en WFI, debería continuar con su operación donde la dejó tan pronto como se llame a cualquier interrupción.

¿Qué hace "Delay ()" sin argumento? ¿Quizás estás atrapado en eso?
Todas las funciones de inicio y retraso y demás son solo un reemplazo simplificado del código real. Estoy usando un bucle for en lugar de Delay() y probé el programa comentando HAL_PWR_EnterSLEEPMode(0,1) y el LED parpadea muy bien. E incluso si el uC estuviera atascado en el retraso, primero ejecutaría la instrucción de alternar, que encendería el LED pero el LED permanece apagado.
Bueno, el paso 1 debería ser DESHACERSE DEL CÓDIGO REAL. Reduzca su código al mínimo necesario para replicar su problema. Para cuando termine de hacer esto, es probable que haya resuelto su propio problema.
Nota:void HAL_Delay(__IO uint32_t Delay)
Supongo que este código es una simplificación de su objetivo final; en general, es mejor asumir que un WFI por sí solo podría no hacer que el procesador entre en estado de suspensión (debido a la cláusula de activación de impdef). En algún lugar, deberá retroceder y volver a intentar el WFI (suponiendo que no quede trabajo).

Respuestas (1)

Compruebe si el SLEEPONEXITbit de entrada SCB->SCRestá establecido. Establecer eso en 1 causaría exactamente lo que ha descrito:

Bit 1 DORMIR SALIR

Configura la suspensión al salir cuando se regresa del modo Controlador al modo Subproceso. Establecer este bit en 1 permite que una aplicación controlada por interrupciones evite regresar a una aplicación principal vacía.

0: No dormir al volver al modo Thread.

1: Entrar en suspensión, o suspensión profunda, al regresar de una rutina de servicio de interrupción.