Ich hatte eine Diskussion mit jemandem über die Verwendung einer Softwareverzögerung () und wie sie nur NOP-Befehle für eine bestimmte Anzahl von Taktzyklen gemäß Ihrer Verzögerung ausführt und daher ständig läuft und Strom verbraucht. Die Alternative nach meiner Lektüre ist die Verwendung von Systemtimern. Aber sobald diese Timer aktiviert sind, werden sie auch um die gleiche Anzahl von Taktperioden erhöht, sodass ich denke, dass der Stromverbrauch ähnlich wäre.
Ich wollte wissen, ob es einen Unterschied im Stromverbrauch zwischen den beiden gibt, abgesehen von der Tatsache, dass delay() Ihr Programm und Ihre Sachen blockiert. Gibt es alternative Verzögerungsmethoden, die mir ebenfalls nicht bekannt sind und zu einem besseren Stromverbrauch führen?
Da es zu viele Variablen gibt, um sie richtig zu beantworten, werde ich versuchen, ein Beispiel zu geben. Die Beispielaufgabe besteht darin, die Anzahl der verstrichenen Sekunden auf einer 7-Segment-LED-Anzeige kontinuierlich anzuzeigen und auf 0 zurückzugehen, sobald 60 Sekunden erreicht sind. Ich denke, das Ziel wäre es, die Effizienz einer 1-Sekunden-Verzögerung bei der Verwendung einer Funktion mit SysTick IRQ zu vergleichen
Da ich selbst neugierig bin, habe ich einige einfache Messungen an einem 32L152CDISCOVERY- Board durchgeführt. Mein Testprogramm besteht aus 4 Tests, die jeweils die verstrichenen Sekunden auf dem LCD anzeigen. Der Unterschied zwischen den Tests besteht darin, was sie tun, bis eine Sekunde verstreicht.
Der erste Test macht nichts innerhalb der Schleife. Es ist eine reine "Besetztschleife", die sich dreht, bis sich eine Variable ändert.
Die zweite Schleife enthält eine einzelne NOP
Anweisung.
Die dritte Schleife führt 1000 NOP
Anweisungen hintereinander aus.
Die vierte Schleife führt eine WFI
Anweisung aus und schläft, bis ein Interrupt auftritt.
Durch Drücken der blauen Taste gelangen Sie zum nächsten Test.
Es gibt zwei Kompilierzeitparameter.
SLOWTICK
ändert die SysTick
Interruptfrequenz von 1 kHz auf 1 Hz.ALIGN_OFF
ein ein und ändert ihre Ausrichtung.NOP
Der Code:
#define STM32L152xC
#include "stm32l1xx.h"
#include "lcd.h"
//#define SLOWTICK
//#define ALIGN_OFF
volatile int tick_s;
void SysTick_Handler() {
#ifdef SLOWTICK
tick_s += 1;
#else
static int tick_ms;
tick_ms += 1;
if(tick_ms == 1000) {
tick_ms = 0;
tick_s += 1;
}
#endif
}
void hw_init() {
// use the 16MHz internal HSI as clock source, no PLL
RCC->CR |= RCC_CR_HSION;
while(!(RCC->CR & RCC_CR_HSIRDY))
;
RCC->CFGR |= RCC_CFGR_SW_HSI;
while((RCC->CFGR & RCC_CFGR_SWS) != RCC_CFGR_SWS_HSI)
;
SystemCoreClockUpdate();
// enable GPIOs for the LCD and the pushbutton
RCC->AHBENR = RCC_AHBENR_GPIOAEN | RCC_AHBENR_GPIOBEN | RCC_AHBENR_GPIOCEN;
__ISB(); // wait a bit, see STM32L1 errata on RCC
lcd_init();
#ifdef SLOWTICK
SysTick_Config(SystemCoreClock);
#else
SysTick_Config(SystemCoreClock / 1000);
#endif
}
int last_s = -1;
void display_s() {
int d1 = (last_s / 10) % 10;
int d2 = last_s % 10;
lcd_displaychar(d1 + '0', 0, 0, 5);
lcd_displaychar(d2 + '0', 0, 0, 6);
lcd_update();
}
// repeats an instruction 10 times
#define TEN(x) ({ ({ x; }); ({ x; }); ({ x; }); ({ x; }); ({ x; }); \
({ x; }); ({ x; }); ({ x; }); ({ x; }); ({ x; }); })
int button_pressed() {
return GPIOA->IDR & 1;
}
int main() {
hw_init();
int temp_s;
while(1) {
lcd_displaytext("0NOP");
asm volatile(".align 4");
#ifdef ALIGN_OFF
asm volatile("nop");
#endif
while(1) {
while(1) {
temp_s = tick_s;
if(temp_s != last_s)
break;
}
last_s = temp_s;
display_s();
if(button_pressed())
break;
}
while(button_pressed())
;
lcd_displaytext("1NOP");
asm volatile(".align 4");
#ifdef ALIGN_OFF
asm volatile("nop");
#endif
while(1) {
while(1) {
temp_s = tick_s;
if(temp_s != last_s)
break;
asm volatile("nop");
}
last_s = temp_s;
display_s();
if(button_pressed())
break;
}
while(button_pressed())
;
lcd_displaytext("xNOP");
asm volatile(".align 4");
#ifdef ALIGN_OFF
asm volatile("nop");
#endif
while(1) {
while(1) {
temp_s = tick_s;
if(temp_s != last_s)
break;
// triple nesting repeats 10*10*10 times
TEN(TEN(TEN(asm volatile("nop"))));
}
last_s = temp_s;
display_s();
if(button_pressed())
break;
}
while(button_pressed())
;
lcd_displaytext("WFI");
asm volatile(".align 4");
#ifdef ALIGN_OFF
asm volatile("nop");
#endif
while(1) {
while(1) {
temp_s = tick_s;
if(temp_s != last_s)
break;
asm volatile("wfi");
}
last_s = temp_s;
display_s();
if(button_pressed())
break;
}
while(button_pressed())
;
}
}
Der Stromverbrauch der MCU wurde gemessen, indem ein Amperemeter zwischen Pin 1 und 2 von JP1 angeschlossen wurde. Der Debugger wurde getrennt, indem die Jumperkappen von CN3 entfernt wurden.
+--------+--------+--------+--------+
| Test 1 | Test 2 | Test 3 | Test 4 |
| 0NOP | 1NOP | xNOP | WFI |
+---------------------+--------+--------+--------+--------+
| //#define SLOWTICK | | | | |
| //#define ALIGN_OFF | 4.3 mA | 4.7 mA | 2.8 mA | 1.4 mA |
+---------------------+--------+--------+--------+--------+
| //#define SLOWTICK | | | | |
| #define ALIGN_OFF | 5.0 mA | 4.8 mA | 2.8 mA | 1.4 mA |
+---------------------+--------+--------+--------+--------+
| #define SLOWTICK | | | | |
| //#define ALIGN_OFF | 4.3 mA | 4.7 mA | 2.8 mA | 1.4 mA |
+---------------------+--------+--------+--------+--------+
| #define SLOWTICK | | | | |
| #define ALIGN_OFF | 4.9 mA | 4.8 mA | 2.8 mA | 1.4 mA |
+---------------------+--------+--------+--------+--------+
Schlussfolgerungen:
NOP
Anweisungen reduziert den Verbrauch, aber es kann schwierig sein, die genaue Anzahl von Anweisungen zu generieren, die für eine bestimmte Verzögerung benötigt werden. Vom Speicherbedarf ganz zu schweigen.Eine Belegtschleife (wie die, die delay()
Sie beschreiben, wahrscheinlich nicht die, die delay
Sie tatsächlich verwenden) hält Ihren CPU-Kern auf voller Geschwindigkeit.
Wenn Sie den CPU-Kern einfach im Leerlauf halten und auf einen Interrupt warten, läuft nur ein viel, viel einfacherer Hardwarezähler. "Viel einfacher" bedeutet, dass eine Taktung, die viel weniger Transistoren schaltet, und der Stromverbrauch in digitalen Schaltungen normalerweise die Energie ist, die beim Schalten eines Transistors verloren geht.
Den Taktgenerator des CPU-Kerns kann man wirklich lange abschalten, das spart noch mehr Strom.
Diese Frage wird von so vielen Variablen beeinflusst, dass sie im Wesentlichen unbeantwortbar wird. Hier sind einige Gründe dafür:
Es gibt auch andere Überlegungen.
Ich würde vermuten, dass es bei Verwendung einer modernen MCU mit integriertem Programmspeicher und Datenspeicher schwierig sein wird, bei kurzen Verzögerungen einen großen Leistungsunterschied zu erkennen. In diesem Szenario können messbare Energieeinsparungen für lange Verzögerungen realisiert werden, wenn der SLEEP-Modus der MCU verwendet wird.
jsotola
jsotola
Chris Stratton
akivjh
Chris Stratton
sleep(1)
sowohl blockierend als auch potenziell effizient ist.