Sleep Modes und Power Management

Über den Beitrag

Im letzten Beitrag hatte ich gezeigt, wie man den ATmega328P standalone, also außerhalb seiner Arduino UNO Peripherie betreibt. Das spart Kosten, Platz und Strom. Betreibt ihr ein Projekt mit so einem „nackten“ Atmega328P, ist ein Durchschnittsakku von sagen wir 2000 mAh trotzdem nach wenigen Tagen leer, selbst wenn ihr nichts anderes dran hängen habt. Um den Strombedarf zu senken, haben die AVR Microcontroller verschiedene Sleep Modes. Wenn ihr zum Beispiel eine Wetterstation baut, in der ein Microcontroller auf der Sensorseite nur einmal pro Minute eine Messung durchführt, kann man ihn den Rest der Zeit getrost in den Schlaf schicken. 

Stromverbrauch einiger Boards und MCUs (Bare Minimum Sketch / 5V)
Tabelle 1: Stromverbrauch einiger Boards und MCUs (Bare Minimum Sketch / 5V)

Ich konzentriere mich in diesem Beitrag auf den ATmega328P, da er denjenigen, die mit dem Arduino UNO arbeiten, am geläufigsten ist. Für viele Projekte ist er jedoch überdimensioniert. Deswegen gehe ich zum Schluss des Beitrages noch einmal kurz auf die Sleep Modes der weniger stromhungrigen ATtinys 85/45/25 am Beispiel des ATtiny85 ein. Auch die ATtinys 85/45/25 sind über die Arduino IDE programmierbar. Das hatte ich hier beschrieben. 

Inhaltsangabe

Vorab noch….

Hochladen der Sketche

Ich werde in dem Beitrag nicht oder nur sehr wenig darauf eingehen, wie die Sketche auf ATmega328P bzw. den ATtiny85 kommen. Das habe ich ausführlich in den oben angegebenen Beiträgen beschrieben. Es würde den Rahmen sprengen, das hier noch einmal im Detail zu wiederholen. 

Pinout des ATmega328P

Zur Orientierung noch das Pinout Schema des ATmega328P:

Pinout des ATmega328P

Die Sleep Modes des ATmega328P

Sleep Modes behandele ich am Beispiel des ATmega328P

Der Atmega328P kennt sechs verschiedene Sleep Modes. Je nach Modus werden mehr oder weniger seiner Funktionen schlafen geschickt. Entsprechend unterschiedlich ist auch der verbleibende Stromverbrauch.

Wenn ihr den ATmega328P schlafen schickt, müsst ihr ihn natürlich auch wieder wecken können. Jeder Modus hat dabei eine eigene Auswahl möglicher „Weckrufe“. Darüber hinaus ist zu beachten, dass der ATmega328P je nach „Schlaftiefe“ unterschiedlich lange zum Aufwachen braucht. 

Die folgende Tabelle gibt einen Überblick, in welchen Sleep Modes welche Funktionen noch aktiviert sind und wie man den ATmega328P wieder wecken kann. Die gute Nachricht ist, dass die Tabelle für eine ganze Serie von ATmegas gültig ist (siehe Tabellenunterschrift). 

Tabelle 2: Sleep Modes des ATmega328P (und 48A/48PA/88A/88PA/168A/168PA/328)

Hier eine Kurzbeschreibung der Sleep Modes. Mehr Informationen gibt es im Datenblatt.

  • Idle: der Leichtschlaf. Unter Umständen müsst ihr weitere Komponenten per PRR (kommt später) direkt abschalten, damit der ATmega328P nicht ungewollt aufwacht.
  • ADC Noise Reduction: Auch in diesem Modus bleibt vieles angeschaltet. Der ADC Noise Reduction Mode wird, wie der Name schon andeutet, auch verwendet, um bei Analog Digital-Wandlungen Rauschen zu reduzieren. Dadurch kann eine höhere Auflösung erreicht werden. 
  • Power-down: der stromsparendste Tiefschlaf. Lediglich externe Interrupts, TWI (Two Wire Interface -> I2C) oder der Watchdog Interrupt können den ATmega328P aufwecken. 
  • Power-save: ist ähnlich wie der Power-down Modus, allerdings ist der Timer2 noch wach und könnte über einen externen Taktgeber betrieben werden.
  • Stand-by: hier läuft noch der Systemoszillator. Diesen Modus wählt man typischerweise, wenn schnelles Aufwachen nötig ist. Lediglich sechs Zyklen werden benötigt. Man muss dazu wissen, dass ein externer Quarzoszillator Einschwingzeiten im Millisekundenbereich hat. 
  • Extended Standby: Gegenüber dem Stand-by Modus ist hier der Timer2 aktiv.

Die Sleep Modes aktivieren

Sleep Modes über das SMCR steuern

Die Sleep Modes werden durch entsprechende Einträge in das Sleep Mode Control Register SMCR gesteuert. Die Bits SM0…SM2 legen den Modus fest, das SE Bit (sleep enable) startet den Schlafmodus. 

Das Sleep Mode Control Register SMCR
Einstellung der Sleep Modes über das SMCR
Tabelle 3: Einstellung der Sleep Modes über das SMCR

Sleep Modes mit den sleep.h Funktionen

Die Kurzschreibweise

Nicht jeder mag es, Register mit Anweisungen wie SMCR |= (1<<SM0); zu beschreiben. Wenn ihr die Headerdatei sleep.h einbindet, dann könnt ihr besser lesbare Funktionen verwenden. Wie das geht, zeige ich im folgenden Sketch.

Zum Aufwecken verwende ich den Watchdog Timer, den ich auf 8 Sekunden eingestellt habe. Details zum Watchdog Timer findet ihr hier. Andere Weckmethoden bespreche ich später.

Der Sketch macht folgendes:

  • zunächst werden wdt.h (für den Watchdog Timer) und sleep.h eingebunden
  • PD7 wird auf OUTPUT gesetzt 
  • der Watchdog wird eingerichtet
  • Die LED an PD7 geht 3.5 Sekunden lang an
    • das dient lediglich als „Ich-bin-wach-Zeichen“
    • 3.5 Sekunden habe ich gewählt, damit mein träges Multimeter den Stromverbrauch vernünftig messen konnte
  • nach weiteren 3.5 Sekunden wird der Watchdog Timer zurückgesetzt
  • der Sleep Mode wird mit set_sleep_mode(...) ausgewählt, in diesem Fall Power-down
  • sleep_mode() startet den Schlafmodus
  • nach 8 Sekunden weckt der Watchdog; die ISR (Interrupt Service Routine) ist leer, sie muss aber trotzdem in den Sketch aufgenommen werden
  • Der Sketch nimmt seine Arbeit direkt hinter sleep_mode() wieder auf
#include <avr/wdt.h>
#include <avr/sleep.h>

void setup(){
  DDRD = (1<<PD7); // equals (roughly) pinMode(7, OUTPUT);
  watchdogSetup();
}

void loop(){
  PORTD |= (1<<PD7);  // equals (roughly) digitalWrite(7, LOW);
  delay(3500);
  PORTD &= ~(1<<PD7); // equals (roughly) digitalWrite(7, LOW);
  delay(3500);
  wdt_reset();

  set_sleep_mode(SLEEP_MODE_PWR_DOWN); // choose power down mode
//  set_sleep_mode(SLEEP_MODE_PWR_SAVE); // choose power save mode
//  set_sleep_mode(SLEEP_MODE_STANDBY); // choose external standby power mode
//  set_sleep_mode(SLEEP_MODE_EXT_STANDBY); // choose external standby power mode
//  set_sleep_mode(SLEEP_MODE_IDLE); // did not work like this!
//  set_sleep_mode(SLEEP_MODE_ADC); // choose ADC noise reduction mode
//  sleep_bod_disable();  // optional brown-out detection switch off  

  sleep_mode(); // sleep now!
}

void watchdogSetup(void){
  cli();
  wdt_reset();
  WDTCSR |= (1<<WDCE) | (1<<WDE);
  WDTCSR = (1<<WDIE) | (0<<WDE) | (1<<WDP3) | (1<<WDP0);  // 8s / interrupt, no system reset
  sei();
}

ISR(WDT_vect){//put in additional code here
}

 

Für andere Sleep Modes entkommentiert einfach die entsprechenden Zeilen. Probiert auch mal den Idle Modus. Ihr werdet sehen, dass er nicht funktioniert und die LED mit einer Frequenz von 3.5 s leuchtet. Idle ist sozusagen ein leichter Schlaf und aus einem solchen wacht man eben auch leicht auf. Wir kommen noch dazu wie ihr das verhindert. 

Und nun vertauscht die Zeilen 10 und 12. Die LED leuchtet nun auch während der ATmega328P schläft, denn alle Registerinhalte bleiben während der Schlafphase erhalten, einschließlich der Portregister.  

Die ausführlichere Schreibweise

Die Funktion sleep_mode(); entspricht der Befehlsfolge:

sleep_enable(); 
sleep_cpu(); 
sleep_disable();

Das ist übrigens in sleep.h so definiert. Ihr findet die Datei unter: „Program Files (x86)\Arduino\hardware\tools\avr\avr\include\avr\sleep.h“. Schadet nicht, da mal reinzuschauen. 

Es ist sicherer, wenn ihr die ausführlichere Befehlsfolge verwendet und vor sleep_enable() alle Interrupts durch cli() abschaltet. Vor sleep_cpu() schaltet ihr die Interrupts über sei() wieder ein. Ohne das zwischenzeitliche Abschalten kann es zur „Kollision“ von Interrupts mit unvorhersehbaren Folgen kommen. Das spielt vor allem dann eine Rolle, wenn zwischen sleep_enable() und sleep_cpu() weitere Anweisungen eingefügt werden und genau das werden wir bald tun. 

Aber zunächst einmal sieht der Sketch jetzt so aus:

#include <avr/wdt.h>
#include <avr/sleep.h>

void setup(){
  DDRD = (1<<PD7); // equals (roughly) pinMode(7, OUTPUT);
  watchdogSetup();
}

void loop(){
  PORTD |= (1<<PD7);  // equals (roughly) digitalWrite(7, LOW);
  delay(3500);
  PORTD &= ~(1<<PD7); // equals (roughly) digitalWrite(7, LOW);
  delay(3500);
  wdt_reset();

    set_sleep_mode(SLEEP_MODE_PWR_DOWN); // choose power down mode
//  set_sleep_mode(SLEEP_MODE_PWR_SAVE); // choose power save mode
//  set_sleep_mode(SLEEP_MODE_STANDBY); // choose external standby power mode
//  set_sleep_mode(SLEEP_MODE_EXT_STANDBY); // choose external standby power mode
//  set_sleep_mode(SLEEP_MODE_IDLE); // did not work!
//  set_sleep_mode(SLEEP_MODE_ADC); // choose ADC noise reduction mode

  cli(); // deactivate interrupts
  sleep_enable(); // sets the SE (sleep enable) bit
  sei(); // 
  sleep_cpu(); // sleep now!!
  sleep_disable(); // deletes the SE bit
}

void watchdogSetup(void){
  cli();
  wdt_reset();
  WDTCSR |= (1<<WDCE) | (1<<WDE);
  WDTCSR = (1<<WDIE) | (0<<WDE) | (1<<WDP3) | (1<<WDP0);  // 8s / interrupt, no system reset
  sei();
}

ISR(WDT_vect){//put in additional code here
}

 

Liebhaber der Registerschreibweise können Zeile 23 bis 27 ersetzen durch:

SMCR |= (1<<SM1); // power down sleep mode
cli();
SMCR |= (1<<SE); // sleep_enable();
sei();
asm("SLEEP"); // sleep_cpu();
SMCR &= ~(1<<SE); // sleep_disable();

Ich finde das hat in seiner Kürze auch was. 

Was bringen die Sleep Modes – einige Messungen

Ich habe den letzten Sketch auf dem Arduino UNO, dem standalone ATmega328P und dem ATtiny85 bei verschiedenen Taktfrequenzen laufen lassen und den Stromverbrauch in der Schlafphase gemessen. Zum Teil habe ich den Sketch über die Arduino IDE hochgeladen (wie im letzten Beitrag beschrieben), zum Teil über Atmel Studio. 

Stromverbrauch in verschiedenen Sleep Modi
Tabelle 4: Stromverbrauch in versch. Sleep Modi für den UNO, ATmega328P und ATtiny85

Teilweise waren die Ergebnisse wie erwartet, zum Teil auch nicht:

  • Das Arduino UNO Board schluckt ordentlich Strom, da viel an Peripherie unterhalten werden muss (z.B. die Power LED).
  • Der Idle-Mode verbraucht im standalone Betrieb am meisten Strom (ich komme noch dazu, wie man ihn zum Laufen bekommt).
  • Am wenigsten Strom verbraucht der Power-down Sleep Mode
  • Es macht für den Power-down Modus einen erstaunlich großen Unterschied, ob der Sketch per Arduino IDE oder per Atmel Studio hochgeladen wird. Woran das genau liegt, kann ich nicht sagen. Die Schaltungen waren absolut identisch (nach Trennung von den Programmern). 
  • Im Power-down Modus gibt es keinen Unterschied bei unterschiedlichen Taktfrequenzen – der Systemoszillator ist ja auch aus.
  • Die Verwendung eines ATtiny85 bringt noch einmal eine Riesenersparnis.

Weitere Energiesparpotentiale

Energiesparen mit dem Power Reduction Register

Je nach Sleep Mode und gewünschter Weckmethode könnt ihr den Stromverbrauch noch weiter senken, indem ihr einige Komponenten manuell abschaltet. Gesteuert wird das über das Power Reduction Register PRR:

Das Power Reduction Register PRR

Gesetzte Bits schalten die entsprechende Komponente ab:

  • PRTWI: schaltet das Two Wire Interface (I2C) ab. Nach dem Aufwecken muss es neu initialisiert werden.
  • PRTIM2: schaltet den Timer2 im synchronen Modus ab.
  • PRTIM0/PRTIM1:  diese Bits sind für das Abschalten der Timer0 und Timer1 zuständig. 
    • Die Timer0/1/2 setzen ihre Arbeit nach dem Aufwecken fort, ohne dass weitere Maßnahmen getroffen werden müssen
  • PRSPI: schaltet die SPI Schnittstelle aus. SPI muss nach dem Aufwecken neu initialisiert werden.
  • PRUSART0: schaltet die serielle Schnittstelle aus, also RX/TX (Universal Synchronous and Asynchronous serial Receiver and Transmitter). Die USART Schnittstelle muss nach dem Aufwachen neu intialisiert werden. 
  • PRADC: schaltet den AD-Wandler aus

Ihr müsst keine Binäroperationen anwenden, um die Bits eurer Wahl zu setzen (obwohl das ja nicht wirklich schwer ist). Dafür gibt es Funktionen, die in der Headerdatei power.h definiert sind. Im folgenden Sketch findet ihr die (selbsterklärenden) Funktionen in Zeile 26 bis 32.

#include <avr/wdt.h>
#include <avr/sleep.h>
#include <avr/power.h>

void setup(){
  DDRD = (1<<PD7); // equals (roughly) pinMode(7, OUTPUT);
  watchdogSetup();
}

void loop(){
  PORTD |= (1<<PD7);  // equals (roughly) digitalWrite(7, LOW);
  delay(3500);
  PORTD &= ~(1<<PD7); // equals (roughly) digitalWrite(7, LOW);
  delay(3500);
  wdt_reset();

//  set_sleep_mode(SLEEP_MODE_PWR_DOWN); // choose power down mode
//  set_sleep_mode(SLEEP_MODE_PWR_SAVE); // choose power save mode
//  set_sleep_mode(SLEEP_MODE_STANDBY); // choose external standby power mode
//  set_sleep_mode(SLEEP_MODE_EXT_STANDBY); // choose external standby power mode
  set_sleep_mode(SLEEP_MODE_IDLE); 
//  set_sleep_mode(SLEEP_MODE_ADC); // choose ADC noise reduction mode

  cli();
  sleep_enable();
  power_adc_disable(); 
  power_usart0_disable();
  power_spi_disable(); 
  power_timer0_disable();
  power_timer1_disable(); 
  power_timer2_disable(); 
  power_twi_disable();
  //sleep_bod_disable(); // disable brown-out detector
  sei();
  sleep_cpu();
  sleep_disable();
  power_all_enable();

}

void watchdogSetup(void){
  cli();
  wdt_reset();
  WDTCSR |= (1<<WDCE) | (1<<WDE);
  WDTCSR = (1<<WDIE) | (0<<WDE) | (1<<WDP3) | (1<<WDP0);  // 8s / interrupt, no system reset
  sei();
}

ISR(WDT_vect){//put in additional code here
}

 

Wenn ihr den Sketch ausprobiert, werdet ihr sehen, dass jetzt auch der Idle Sleep Mode funktioniert. Alle Störenfriede, die den leichten Schlaf unterbrechen könnten, sind abgeschaltet. 

Ihr müsst natürlich aufpassen, dass ihr keine Komponenten ausschaltet, die ihr zum Aufwecken benutzen wollt! 

Ausschalten des Brown-Out Detektors (BOD)

Was ist ein BOD?

Bei einem plötzlichen, kompletten Stromausfall spricht man von einem Black-Out. Diesen Ausdruck kennt wohl jeder. Von einem Brown-Out hingegen spricht man, wenn die Versorgungsspannung des Microcontrollers unter ein Mindestlevel rutscht. Typischerweise passiert das, wenn der Akku, der euer Projekt betreibt, sich entleert. Ein Microcontroller, der zu wenig Spannung bekommt, macht völlig unvorhersehbare Dinge. Der Microcontroller selbst nimmt dabei nicht unbedingt Schaden. Aber wenn die Dinge, die er steuert „austicken“, kann das unter Umständen kritisch sein. Deshalb haben die meisten Microcontroller einen Brown-Out Detektor (BOD). Dieser vergleicht die Betriebsspannung mit einem festen Trigger-Level. Bei Unterschreitung dieses Levels wird ein Reset ausgelöst, um das System zu schützen. Bei manchen Anwendungen mag ein Brown-Out jedoch unproblematisch sein. 

Wie schalte ich den BOD aus?

Bei den picoPower Varianten der AVR Microntroller kann der Brown-Out Detektor ausgeschaltet werden. Ihr erkennt die picoPower Modelle am „P“ im Namen, z.B. beim ATmega328P. Die Steuerung erfolgt über das MCU Control Register MCUCR:

Das Microcontroller Control Register MCUCR

Relevant sind dabei die folgenden Bits:

  • BODS: BOD Sleep
  • BODSE: BOD Sleep Enable

Beide Bits müssen in einer bestimmten Abfolge gesetzt werden, um den BOD auszuschalten. Einfacher ist es mit der in sleep.h definierten Funktion sleep_bod_disable(). Im letzten Sketch (idle_mode_enable.ino) hatte ich die Funktion schon in Zeile 33 eingefügt, aber noch nicht erklärt und auskommentiert. 

Alternativ könnt ihr, sofern ihr Programme wie Atmel Studio benutzt, den BOD auch über die zuständigen Fuse Bits (BODLEVEL) steuern. Es stehen die Einstellungen 4.3 V, 2.7 V, 1.8 V oder „disabled“ zur Verfügung. 

BOD Einstellung über die Fuses
BOD Einstellung über die Fuses

Nach meiner Messung bringt das Ausschalten des BODs im Sleep Mode Power-down weitere 8 µA Stromersparnis. Klingt nicht viel, aber in einem Jahr sind das immerhin ungefähr 70 mAh.

Senken der Taktfrequenz

Im Dauer-Wachbetrieb bringt das Senken der Taktfrequenz eine erhebliche Einsparung (siehe Tabelle 1). Ihr habt aber auch gesehen, dass der Stromverbrauch des Microcontrollers zumindest im Power-down Modus frequenzunabhängig ist. Wenn ihr den Microcontroller schlafen schickt und nur zwischendurch kurz aufweckt um ein paar Dinge zu, dann kann es günstiger sein, eine hohe Taktfrequenz zu wählen. Der Grund dafür ist, dass die Dinge bei hoher Taktfrequenz schneller erledigt sind. Die Schnelligkeit überkompensiert den höheren Stromverbrauch. Verwendet ihr delays, sieht die Sache allerdings schon wieder anders aus. Zu dem Thema Energieverbrauch vs. Taktfrequenz gibt es auch eine interessante Diskussion auf mikrocontroller.net

Senken der Betriebsspannung

Ein Microcontroller wirkt wie ein Kondensator bzw. wie viele kleine Kondensatoren und deren Ladung hängt von der Betriebsspannung ab. Man spricht von parasitären Kapazitäten. Hinzu kommen Leckströme, z.B. von Leiterbahn zu Leiterbahn.

Im Power-down Modus konnte ich den Stromverbrauch des ATmega328P von 0.15 mA (über Arduino IDE programmiert) auf immerhin 0.12 mA senken. Weitere Informationen und Vergleichswerte findet ihr hier.

Andere Weckmethoden

In den bisherigen Beispielen war der Watchdog unser Wecker für den ATmega328P. Ich möchte nun noch auf zwei andere Methoden eingehen. 

Aufwecken per externem Interrupt

Mit Arduino Funktionen

Im folgenden Sketch wird der ATmega328P per externem Interrupt geweckt. Dazu habe ich einen Taster an INT0 (PD2, Pin4 = Arduino Pin 2) gehängt, der beim Betätigen ein HIGH Signal erzeugt. Mit jedem Tasterdruck wacht der ATmega328P auf, die LED leuchtet für 1 Sekunde, dann legt sich der ATmega328P wieder hin. 

Mit den Arduino Funktionen lässt sich der Interrupt einfach einrichten. Es ist ratsam, den Interrupt nach dem Aufwachen zu deaktivieren, damit er z.B. beim Tasterprellen nicht mehrfach ausgelöst wird. Ich habe hier übrigens wieder die kurze sleep_mode() Funktion verwendet, um das Augenmerk auf das Wesentliche zu lenken. 

#include <avr/sleep.h>

void setup(){
  DDRD = (1<<PD7); 
}

void loop(){
  PORTD |= (1<<PD7); 
  delay(1000);
  PORTD &= ~(1<<PD7); 
  attachInterrupt(digitalPinToInterrupt(2), intRoutine, RISING);
  
  set_sleep_mode(SLEEP_MODE_PWR_DOWN); // choose power down mode
  sleep_mode(); // sleep now!
}

void intRoutine(){ 
  detachInterrupt(2); // external interrupt disable (INT0)
}

 

Programmierung in C

Ihr könnt die zuständigen Register auch direkt ansprechen, was nicht weiter schwer ist. Im EICRA Register (External Interrupt Control Register A) stellt ihr die Bedingungen ein, unter denen der Interrupt ausgelöst wird.

Das External Interrupt Control Register A EICRA
Einstellung des Interrupts an INTO

Im EIMSK Register (External Interrupt Mask Register) wird der Interrupt aktiviert:

Das External Interrupt Mask Register EIMSK

Und so sieht der Sketch dann aus: 

#include <avr/sleep.h>

void setup(){
  DDRD = (1<<PD7); 
  EICRA |= (1<<ISC01)|(1<<ISC00); // interrupt on rising edge of INT0
}

void loop(){
  PORTD |= (1<<PD7); 
  delay(1000);
  PORTD &= ~(1<<PD7); 
  EIMSK |= (1<<INT0); // external interrupt enable on INT0
  
  set_sleep_mode(SLEEP_MODE_PWR_DOWN); // choose power down mode
  sleep_mode(); // sleep now!
}

// INT0 interrupt service routine
ISR(INT0_vect){ 
  EIMSK &= ~(1<<INT0); // external interrupt disable (INT0)
}

 

Aufwecken mit Timer2

Wenn ihr den ATmega328P über einen Timer wecken wollt, dann geht das mit dem Timer2 in allen Sleep Modes, außer im Power-down und Power-save Modus. Wer meine Beiträge über die 8-Bit Timer0 und Timer2 und den 16-Bit Timer1 gelesen hat, der weiß, dass der Timer2 in relativ kurzer Zeit überläuft, selbst bei Verwendung des maximalen Prescalers. Auf die Details der Timerprogrammierung gehe ich hier nicht noch einmal ein. 

Im folgenden Beispielsketch ist der Timer2 mit dem maximalen Prescaler eingestellt. Bei einer Taktfrequenz von 16 MHz werden pro Sekunde 16 Mio/1024/256 = ~61 Interrupts ausgelöst. Um wieder auf ca. 8 Sekunden Ruhephase zu kommen, habe ich einen Counter eingefügt, der bei jedem Aufwachen inkrementiert wird. Erst beim 500sten Aufwachen leuchtet die LED. Das entspricht 500 / 61 = ~8.2 Sekunden Pause. Man sollte meinen, dass das häufige Aufwachen zu erheblichem Mehrverbrauch an Strom führt. Erstaunlicherweise habe ich mit dieser Methode in der Schlafphase sogar etwas weniger Stromverbrauch festgestellt als mit der Watchdog Dauerschlafmethode (2.92 mA gegenüber 2.97 mA).

#include <avr/sleep.h>
#include <avr/power.h>
int counter = 0;

void setup(){
  DDRD = (1<<PD7); 
  TCCR2A = 0x00; // Wave Form Generation Mode 0: Normal Mode, OC2A disconnected
  TCCR2B = (1<<CS22)|(1<<CS21)|(1<<CS20); // prescaler = 1024
  TIMSK2 = (1<<TOIE2);
}

void loop(){
  if(counter==500){
    TIMSK2 &= ~(1<<TOIE2);
    PORTD |= (1<<PD7); 
    delay(1000);
    PORTD &= ~(1<<PD7); 
    counter = 0;
  }
  TIMSK2 = (1<<TOIE2); // interrupt when TCNT2 is overflowed
  TCNT2 = 0;
  set_sleep_mode(SLEEP_MODE_PWR_SAVE); // choose power down mode
  //power_timer2_disable(); // if you uncomment the MCU will sleep forever 
  sleep_mode(); // sleep now!
  counter++;
}

// TIMER2 interrupt service routine
ISR(TIMER2_OVF_vect){ 
}

 

Und dann könnt ihr ja nochmal die Zeile 23 entkommentieren. Der ATmega328P verfällt daraufhin in einen Dornröschenschlaf – nur ohne Prinz, der ihn wach küsst. Erst ein Reset weckt ihn wieder. 

Sleep Modes auf andere AVR MCUs anwenden

Wie schon eingangs erwähnt ist der ATmega328P nicht unbedingt die erste Wahl für ein stromsparendes Projekt. Mit dem hier erworbenen Wissen sollte es aber kein Problem sein, auch andere AVRs in den Schlaf zu schicken. Schaut ins Datenblatt und sucht nach Sleep Mode. So sieht beispielsweise die Sleep Mode Tabelle für den ATtiny85 / 45 / 25 aus:

Sleep Modes ATtiny85, ATtiny45, ATtiny25

Es stehen drei Sleep Modes zur Verfügung, die über die entsprechenden Funktionen aus sleep.h aktiviert werden können. Wecken über den Timer ist nicht möglich. Wenn ihr die Register direkt ansprecht, müsst ihr allerdings darauf achten, dass diese z.T. unterschiedliche Bezeichnungen haben oder dass z.B. der Watchdog andere Prescaler hat. 

Danksagungen

Diesmal habe ich eine ganze Menge Einzelbilder von Pixabay verwendet. Im einzelnen Danke ich den fleißigen Fotografen:

One thought on “Sleep Modes und Power Management

  1. Sehr interessanter Beitrag. Damit hast du mir echt einiges erleichtert. Ich hatte einige Probleme mit der Programmierung…

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.