Moin,
wie kann man beim Cortex-M3/4 (STM32) in einem Schritt die Interrupts aktivieren und den Sleep-Modus (WFI) betreten, ohne dass Interrupts dazwischen funken?
Das ist relevant bei solchen Konstrukten, wenn man den Sleep-Mode zur Reduktion der Leistungsaufnahme einsetzen möchte:
1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
9 |
|
10 |
|
11 |
|
12 |
|
13 |
|
14 |
|
15 |
|
16 |
|
17 |
|
18 |
|
19 |
|
20 |
|
21 |
|
22 |
|
23 |
|
24 |
|
25 |
|
Der Interrupt signalisiert über die Variable, dass etwas zu tun ist. Die Funktion doSomething kann nicht direkt im Interrupt ausgeführt werden, weil sie langsam ist, Delays nutzt, Teil einer proprietären Bibliothek ist welche nicht geändert werden kann o.ä. Die Interruptsperre ist aber nötig um counter konsistent modifizieren zu können.
Die Tücke ist: Wenn genau zwischen dem __enable_irq() und dem WFI der Interrupt kommt und den counter inkrementiert, betritt der Controller trotzdem den Sleep-Modus und wacht ggf. nie wieder/zu spät wieder auf.
Man muss irgendwie sicherstellen dass zwischen Abfragen des counters und Betreten des Sleep-Mode kein Interrupt auftreten kann - oder die ISR muss irgendwie das Betreten des Sleepmode verhindern. Ersetzt man die Interrupt-Sperre durch Atomics, hat man übrigens das gleiche Problem, aber eben zwischen Abfrage des Atomic-Werts und WFI.
Gibt es dafür eine smarte Lösung?
Eine Möglichkeit wäre es WFE statt WFI zu nutzen und SEV im Interrupt-Handler bzw. SEVONPEND. Aber eigentlich ist dieser Mechanismus für Inter-Core-Kommunikation intendiert, in Multicore-Systemen würde man damit unnötig andere Kerne wecken.
Das Problem tritt sehr ähnlich auf wenn man Task-Queues für "normale" Multithreading-Systeme implementieren möchte, dort können Condition Variables helfen. So etwas bräuchte man hier auch, aber eben ohne Multithreading.
Mir ist eine relativ komplizierte Lösung mit SVC und SLEEPONEXIT eingefallen. Kennt jemand eine elegante kompakte Lösung? Wie macht man das bei Controllern welche kein WFE+SEV oder SLEEPONEXIT haben, wie z.B. AVR?