Das ADC-Modul des STM32 wird in meiner Anwendung verwendet, die sehr empfindlich auf den Stromverbrauch reagiert.
In dieser Anwendung muss der ADC nur mit 20 Abtastungen pro Sekunde arbeiten. Die Verwendung des DMA verbraucht mehr Strom als ich erwartet hatte. Ich entschied mich dafür, dass es im Single-Sample-Modus funktioniert, indem ich eine Aufgabe (FreeRTOS) eine Konvertierung auslösen und alle 50 ms darauf warten lasse, dass die Konvertierung durchgeführt wird.
Hier ist mein Code:
u16 i;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
ADC_Cmd(ADC1, ENABLE);
for(i=0;i<sizeof(ADC_Channel_Table)/sizeof(u8);i++)
{
ADC_RegularChannelConfig(ADC1, ADC_Channel_Table[i], 1, ADC_SampleTime_71Cycles5);
ADC_SoftwareStartConvCmd(ADC1, ENABLE);
while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC))
{
}
ADCConvertedValue[i] = ADC_GetConversionValue(ADC1);
}
ADC_Cmd(ADC1, DISABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, DISABLE);
Wobei "sizeof(ADC_Channel_Table)" 5 ist, weil 5 Kanäle abgetastet werden.
Die Abtastzeit ist ADC_SampleTime_239Cycles5 (eigentlich 256 Zyklen, wobei die Konvertierungszeit enthalten ist). 5 Kanäle benötigen also etwa 1500 Zyklen. Der ADC-Takt beträgt 12 MHz und 1500 Zyklen sind etwa 120 µs.
Und schau dir den Code an:
while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC))
{
}
Das bedeutet, dass die CPU damit beschäftigt ist, auf die Konvertierung zu warten, und die Wartezeit insgesamt 120 µs beträgt.
120 µs sind groß, da die CPU so lange warten muss und dadurch Energie verschwendet wird, aber diese Zeit ist zu klein für das RTOS. Das RTOS kann eine so geringe Zeit nicht verbrauchen.
Ich möchte also einige "Energiespar"-Anweisungen in die Warteschleife einfügen.
Zum Beispiel:
while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC))
{
__ASM("NOP");__ASM("NOP");__ASM("NOP");__ASM("NOP");
}
Aber NOP verbraucht genauso viel Strom wie jeder andere Befehl, den ich ausprobiert habe.
Welche Anweisung kann ich in meine While-Schleife einfügen, die am wenigsten Strom verbraucht?
Wenn Sie Strom sparen möchten, versetzen Sie die MCU in den Ruhezustand. Die relevanten Anweisungen sind WFI und WFE: Wait-for-Interrupt bzw. Wait-for-Event.
WFI ist irgendwie selbsterklärend: Es wacht auf, wenn Sie einen Interrupt erhalten. (Der Interrupt muss jedoch aktiviert sein!)
WFE könnte etwas mehr Erklärung verdienen. Um es zu verwenden, reicht es wahrscheinlich aus zu wissen, dass, wenn Sie SEVONPEND setzen, ein Interrupt, der im NVIC nicht aktiviert ist, als Ereignis qualifiziert wird. Wenn Sie also den ADC-Interrupt im Peripheriegerät, aber nicht im NVIC aktivieren, können Sie warten, bis er mit WFE abgeschlossen ist. Verwenden Sie weiterhin die Schleife mit der Flag-Prüfung, da möglicherweise andere Ereignisse die MCU aufwecken. (Ersetzen Sie einfach die NOPs durch ein WFE) Weitere Informationen finden Sie wie üblich im Referenzhandbuch.
Wahrscheinlich möchten Sie auch auswählen, in welchen Tiefschlaf Sie wechseln möchten, aber wie das geht, hängt möglicherweise von der spezifischen STM32-MCU ab, die Sie verwenden. Zumindest bei einem Modell waren die relevanten Flags PDDS, LPDS und SLEEPDEEP. Lesen Sie auf jeden Fall den entsprechenden Abschnitt des Referenzhandbuchs.
Versuchen wir, die entgegengesetzte Frage zu beantworten, um zu erklären, warum NOP
keine Energie gespart wird. Der Cortex-M3 ist ein Prozessor, der klein und relativ einfach konzipiert ist – er verfügt nicht über viele der Schaltkreise, die für die Ausführung dedizierter Aufgaben ausgelegt sind (Caches, Fließkomma, Verzweigungsvorhersage), oder wo dies der Fall ist, sind sie auf Implementierungen beschränkt Holen Sie sich den größten Erfolg für die niedrigsten Kosten.
Die meisten Anweisungen durchlaufen die 3-Stufen-Pipe und erledigen ähnliche Arbeitsmengen. Befehlsabruf, Dekodierung und eine ALU-Operation. Um die Leistung zu erhöhen, könnten wir vielleicht eine Datenübertragung hinzufügen (die mehr Logik außerhalb des Kerns beleuchtet, aber dann höchstwahrscheinlich die gesamte Pipeline anhalten könnte, da der Kern in Ordnung ist). Vielleicht könnten wir die Ein-Zyklus-Teilung direkt nach einer Datenübertragung verwenden - dort könnten Sie möglicherweise zwei zusätzliche Teile der Aktivität gleichzeitig ausführen. Möglicherweise verringert dies jedoch die Aktivität der Registerdatei, sodass nicht alles dazu beiträgt, die Spitzenleistung zu erreichen.
Obwohl es einige Variationen von Anweisungen zu Anweisungen gibt (und eine Menge Designaufwand, um dies zu optimieren), hängt der Großteil der Wirkleistung nicht zu sehr von den Anweisungen ab. Sicher, wenn Sie die Pipe mit mehreren füllen, NOP
wird jede Toggle-Aktivität gestoppt, aber die Pipe bewegt sich immer noch weiter. Das Anhalten der Pipe (für eine langsame Datenübertragung) wird viel mehr bewirken, aber der einzige klar definierte Zustand, auf den Sie sich verlassen können, um optimal zu sein, sind WFI
und die verschiedenen Schlafzustände.
Größere Kerne haben viel mehr Spitze-zu-Mittelwert, da ein größerer Anteil ihres Siliziums in einer vorab abgerufenen und vorhergesagten Leerlaufschleife inaktiv ist (und wahrscheinlich mehr andere aktive Logik auf dem Chip, die oft auch takt- / leistungsgesteuert sein kann). )
Wenn Sie gerne alle 62,5 ms oder 31,25 ms (im Gegensatz zu 50 ms) Samples erhalten möchten, können Sie die RTC-Alarmfunktion verwenden, um den STM32 aufzuwecken und zwischen den Konvertierungen in den Standby-Modus zu versetzen. Dies spart massiv Strom, insbesondere wenn Sie den ADC so konfigurieren, dass er nach der Konvertierung in den Energiesparmodus wechselt (dies kann von der STM32-Serie abhängig sein). Wenn Sie eine Konvertierung starten, gehen Sie in den Standby/Stopp, sobald Sie aufwachen, ist die Konvertierung sowieso abgeschlossen. Dies fügt eine Latenz von einem Sample hinzu und erfordert, dass Sie das Sample verarbeiten, bevor Sie ein neues Sample starten, aber abhängig von Ihrer Anwendung kann dies machbar sein.
Fluss:
Starten Sie die ADC-Wandlung und aktivieren Sie den RTC-Alarm
Wechseln Sie in den Stoppmodus/Standby-Modus
Sobald RTC den Mikrocontroller aufgeweckt hat, holen Sie sich den ADC-Umwandlungswert
Probe verarbeiten (lagern/filtern etc.)
Vorgang erneut starten!
Stopp- und Standby-Modi verbrauchen sehr wenig Strom, daher kann dies je nach Ihrer Anwendung eine geeignete Methode sein.
Ich würde versuchen, einen Timer zu verwenden, um die Konvertierung zu starten, und einen Interrupt, um das Ergebnis zu sammeln.
Der CPU-Kern kann dann schlafen, bis Daten verfügbar sind.
Als Alternative zum Schlafen können Sie den Taktgenerator möglicherweise so konfigurieren, dass er dem Kern nur einen von 2, 4, 8 usw. Takten gibt, die in den Chip gehen. Wenn die CPU viele Operationen ausführen muss, die Code im Wert von sechs Zyklen erfordern, aber aufgrund externer Hardwareeinschränkungen nur einmal alle 64 Zyklen ausgelöst werden können, kann eine Verlangsamung der CPU auf 1/8 Geschwindigkeit genau das Richtige sein. Während die Verwendung eines Leerlaufmodus mit Interrupt-Aufwecken besser sein kann, als die CPU auf voller Geschwindigkeit zu belassen, muss die CPU möglicherweise viele Zyklen für jedes Ereignis aufwenden, um die Leerlauf-/Aufwach-Übergänge zu konfigurieren. Einige Controller haben Optionen, um die Geschwindigkeiten für die Hauptleitung und die Interrupt-Handler separat einzustellen, sodass selbst wenn die Hauptleitung die CPU auf 1/8-Geschwindigkeit verlangsamt, die Interrupt-Handler mit normaler Geschwindigkeit ausgeführt werden. Ich denke jedoch nicht, dass dies ein besonders häufiges Merkmal ist. Sonst bist du
Oder wenn ein leichtes Zittern der Uhr kein Problem darstellt.
Um die MCU ansonsten in den Ruhezustand zu versetzen, setzen Sie einfach asm ("WFI") in die FreeRTOS-Leerlaufschleife.
Ich habe eine ähnliche Situation, aber ich habe gerade alles auf den 1-ms-Tick-Timer von FreeRTOS ausgerichtet.
Ignacio Vazquez-Abrams
efox29
efox29
Ale..chenski