STM32 Sleep Mode: Interrupt wird ausgeführt, aber die CPU bleibt im WFI

Ich bin ziemlich neu in der ARM-Architektur und arbeite mit einem Board, das einen STM32F0-Mikrocontroller und ein HF-Modem enthält, das jedes Mal, wenn es eine Nachricht empfängt, einen Interrupt sendet. Ich versuche, einen Schlafmodus in einem Mikrocontroller zu implementieren, um Strom zu sparen, aber ich verstehe nicht ganz, warum er sich so verhält, wie er es tut.

Begonnen habe ich mit einem einfachen Programm, das die CPU zu Beginn der Ausführung in den Schlafmodus versetzt. Dann wartet er auf den externen Interrupt, um ihn aufzuwecken, den Interrupt auszuführen und mit dem Hauptprogramm fortzufahren. In der ISR schalte ich jedes Mal, wenn die Routine aufgerufen wird, eine LED an Pin PA0 um, verwende dann aber Pin PA1 in der Hauptfunktion (), die mit einer kurzen Verzögerung ein- und ausschaltet und anzeigt, dass der Mikrocontroller den Schlafmodus verlassen hat.

Mein Code sieht im Grunde so aus:

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

    HAL_PWR_EnterSLEEPMode(0,1);

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

Und der ISR für das EXTI

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

    // Some other stuff

    HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_1);
}

Das ARMv6-Architektur-Referenzhandbuch gibt Folgendes in Bezug auf Wait For Interrupt auf Seite B1-243 an:

Wenn ein Prozessor einen WFI-Befehl ausgibt, kann er die Ausführung aussetzen und in einen Niedrigleistungszustand eintreten. Es kann in diesem Zustand verbleiben, bis der Prozessor eines der folgenden WFI-Weckereignisse erkennt:

  • Eingestellt sind.

  • Eine asynchrone Ausnahme mit einer Priorität, die, wenn PRIMASK.PM auf 0 gesetzt wäre, alle derzeit aktiven Ausnahmen verdrängen würde.

  • Wenn Debug aktiviert ist, ein Debug-Ereignis.

  • Ein IMPLEMENTIERUNGSDEFINIERTES WFI-Weckereignis.

Wenn ich richtig verstehe, gilt in diesem Fall die zweite Zeile. Das bedeutet, dass der Aufruf eines beliebigen Interrupts unabhängig von seiner Priorität den Prozessor aufwecken würde, da der WFI von der Hauptleitung aus aufgerufen wurde. Zur Information werden alle implementierten Unterbrechungsprioritäten auf 0 gesetzt, mit Ausnahme von EXTI, das auf -1 gesetzt wird.

Das passiert nun tatsächlich. Die LED auf PA0 schaltet entsprechend um und zeigt an, dass die ISR tatsächlich ausgeführt wird. Aber die LED auf PA1 bleibt ausgeschaltet, was bedeutet, dass die CPU nach Abschluss der ISR in einem WFI bleibt. Dies wird sogar während des Debuggens bestätigt, indem ich einen Haltepunkt in die Interrupt-Service-Routine eingefügt habe. Nachdem der Code ausgeführt wurde, stoppt er erfolgreich am Haltepunkt. Wenn ich dann das Debuggen mit einer Schritt-für-Schritt-Funktion fortsetze, springt das Programm zum WFI-Befehl (wobei es weiter zum Hauptbildschirm geht, da das Debuggen dazu führen kann, dass der WFI beendet wird).

Meine Frage ist, warum kehrt die CPU zum WFI zurück, wenn der Interrupt eindeutig aufgerufen wird? Wenn sich die CPU in WFI befindet, sollte sie nach meinem Verständnis mit ihrem Betrieb dort fortfahren, wo sie aufgehört hat, sobald ein Interrupt aufgerufen wird.

Was macht "Delay()" ohne Argument? Vielleicht steckst du darin fest?
Alle Init-Funktionen und Verzögerungen und dergleichen sind nur ein vereinfachter Ersatz für den eigentlichen Code. Ich verwende eine for-Schleife anstelle von Delay() und habe das Programm getestet, indem ich HAL_PWR_EnterSLEEPMode(0,1) auskommentiert habe und die LED schön blinkt. Und selbst wenn der uC bei der Verzögerung stecken geblieben wäre, würde er zuerst den Toggle-Befehl ausführen, der die LED einschalten würde, aber die LED bleibt aus.
Nun, Schritt 1 sollte sein, DEN TATSÄCHLICHEN CODE ZU LÖSCHEN. Reduzieren Sie Ihren Code auf das zum Replizieren Ihres Problems erforderliche Minimum. Wenn Sie damit fertig sind, haben Sie wahrscheinlich Ihr eigenes Problem gelöst.
Notiz:void HAL_Delay(__IO uint32_t Delay)
Ich gehe davon aus, dass dieser Code eine Vereinfachung Ihres Endziels darstellt - im Allgemeinen ist es am besten anzunehmen, dass ein WFI allein den Prozessor möglicherweise nicht in den Ruhezustand versetzt (aufgrund der impdef Wakeup-Klausel). Irgendwo müssen Sie zurückschleifen und das WFI erneut versuchen (vorausgesetzt, es bleibt keine Arbeit übrig).

Antworten (1)

Überprüfen Sie, ob das SLEEPONEXITBit in SCB->SCRgesetzt ist. Das Setzen auf 1 würde genau das bewirken, was Sie beschrieben haben:

Bit 1 SCHLAFAUSGANG

Konfiguriert Sleep-on-Exit bei der Rückkehr vom Handler-Modus in den Thread-Modus. Das Setzen dieses Bits auf 1 ermöglicht es einer Interrupt-gesteuerten Anwendung, die Rückkehr zu einer leeren Hauptanwendung zu vermeiden.

0: Nicht schlafen, wenn in den Thread-Modus zurückgekehrt wird.

1: Eintreten in Schlaf oder Tiefschlaf bei der Rückkehr von einer Interrupt-Service-Routine.