Ich bin gerade dabei, batteriebetriebene Software mit den EFM Gekko-Controllern (http://energymicro.com/) zu entwickeln, und möchte, dass der Controller schläft, wenn es nichts Sinnvolles dafür gibt. Zu diesem Zweck wird der WFI-Befehl (Wait For Interrupt) verwendet. Dadurch wird der Prozessor in den Ruhezustand versetzt, bis ein Interrupt auftritt.
Wenn der Schlaf durch Speichern von etwas in Anspruch genommen würde, könnte man Lade- / Speicherexklusivoperationen verwenden, um etwas zu tun wie:
// dont_sleep wird immer dann mit 2 geladen, wenn etwas passiert // sollte die Hauptschleife zwingen, mindestens einmal zu zyklieren. Wenn ein Interrupt // tritt auf, wodurch es während der folgenden Anweisung auf 2 zurückgesetzt wird, // Verhalten wird so sein, als ob der Interrupt danach passiert wäre. store_exclusive (load_exclusive (dont_sleep) >> 1); while (! nicht schlafen) { // Wenn zwischen der nächsten Anweisung und store_exclusive ein Interrupt auftritt, schlafe nicht load_exclusive (SLEEP_TRIGGER); if (! dont_sleep) store_exclusive (SLEEP_TRIGGER); }
Wenn ein Interrupt zwischen den Operationen load_exclusive und store_exclusive auftritt, wird store_exclusive übersprungen, wodurch das System die Schleife erneut durchläuft (um festzustellen, ob der Interrupt dont_sleep gesetzt hat). Leider verwendet der Gekko einen WFI-Befehl anstelle einer Schreibadresse, um den Schlafmodus auszulösen. Schreiben von Code wie
if (! dont_sleep) WFI ();
Es besteht die Gefahr, dass ein Interrupt zwischen 'if' und 'wfi' auftritt und 'dont_sleep' gesetzt wird, aber die wfi wird trotzdem ausgeführt. Was ist das beste Muster, um das zu verhindern? Setzen Sie PRIMASK auf 1, um zu verhindern, dass Interrupts den Prozessor kurz vor der Ausführung der WFI unterbrechen, und löschen Sie sie sofort nach? Oder gibt es einen besseren Trick?
BEARBEITEN
Ich wundere mich über das Ereignis. Nach der allgemeinen Beschreibung wäre es für die Unterstützung mehrerer Prozessoren gedacht, aber ich habe mich gefragt, ob etwas wie das Folgende funktionieren könnte:
if (dont_sleep) SEV (); / * Löscht das Ereignis-Flag der folgenden WFE, ohne jedoch zu schlafen * / WFE ();
Jeder Interrupt, der "don't_sleep" setzt, sollte auch einen SEV-Befehl ausführen. Wenn der Interrupt also nach dem "if" -Test auftritt, löscht die WFE das Ereignis-Flag, geht aber nicht in den Ruhezustand. Klingt das nach einem guten Paradigma?
Antworten:
Ich habe die
dont_sleep
Sache nicht vollständig verstanden , aber Sie könnten versuchen, die "Hauptarbeit" im PendSV-Handler zu erledigen, der auf die niedrigste Priorität eingestellt ist. Dann planen Sie einfach jedes Mal einen PendSV von anderen Handlern ein, wenn Sie etwas erledigen müssen. Sehen Sie hier, wie es geht (es ist für M1, aber M3 ist nicht zu unterschiedlich).Eine weitere Möglichkeit, die Sie (möglicherweise zusammen mit dem vorherigen Ansatz) nutzen können, ist die Funktion "Sleep-on-exit". Wenn Sie dies aktivieren, wird der Prozessor nach dem Beenden des letzten ISR-Handlers in den Ruhezustand versetzt, ohne dass Sie WFI anrufen müssen. Sehen Sie einige Beispiele hier .
quelle
Legen Sie es in einen kritischen Bereich. ISRs werden nicht ausgeführt, sodass Sie nicht das Risiko eingehen, dass sich dont_sleep vor WFI ändert. Sie wecken jedoch den Prozessor und die ISRs werden ausgeführt, sobald der kritische Abschnitt endet.
Ihre Entwicklungsumgebung verfügt wahrscheinlich über wichtige Abschnittsfunktionen, die jedoch ungefähr so aussehen:
EnterCriticalSection ist:
ExitCriticalSection ist:
quelle
Ihre Idee ist in Ordnung, genau das implementiert Linux. Sehen Sie hier .
Nützliches Zitat aus dem oben erwähnten Diskussionsthread, um zu verdeutlichen, warum WFI auch bei deaktivierten Interrupts funktioniert:
quelle
Vorausgesetzt, dass:
Dann besteht die Lösung darin, PRIMASK zu verwenden, um Interrupts zwischen der Flagvalidierung und WFI zu blockieren:
quelle
Was ist mit Sleep on Exit-Modus? Dieser geht automatisch in den Ruhezustand, wenn ein IRQ-Handler beendet wird, sodass nach der Konfiguration kein "normaler Modus" mehr ausgeführt wird. Ein IRQ passiert, er wacht auf und führt den Handler aus und geht wieder in den Schlaf. Kein WFI benötigt.
quelle