Ausgewähltes Bild von Andreluiz Cunha von Pixabay.

Das Arduino-Projekt macht mit Batterien schnell schlapp? Wenn man ein Arduino-Projekt mit Batterien betreiben will, muss man sich einige Gedanken zur Minimierung des Stromverbrauchs machen. In diesem Blogbeitrag werden wir uns anschauen, was man tun kann, wenn man eine AVR-MCU einsetzt. Oft kann man den durchschnittlichen Stromverbrauch auf wenige Mikroampere drücken!

Motivation

In einem batteriebetriebenen Kontext sollte die MCU in der Regel nur eine begrenzte Zeit aktiv laufen. Den Rest der Zeit sollte sie schlafen und nur minimalen (oder gar keinen) Strom ziehen. Es gibt zwei typische Anwendungsfälle:

  • periodisches Auslesen von Sensormesswerten (z. B. alle 10 Minuten) und anschließendes Senden oder Protokollieren,
  • kurze Interaktionen, wie z.B. das Durchführen einer Messung und die Anzeige des Ergebnisses nach einer Aktivierung durch einen Benutzer.

Für den letzteren Anwendungsfall könnte man auch an einen Geräteschalter denken, der die MCU physisch von der Batterie trennt. Oft wird jedoch vergessen, das Gerät auszuschalten, und dann ist die Batterie schnell leer. Außerdem ist es viel schöner, wenn sich das Gerät bei Bedarf automatisch ein- und ausschaltet. Wie wir sehen werden, kann man den Stromverbrauch bis zu einem Punkt minimieren, an dem er deutlich niedriger ist als der Selbstentladestrom einer Batterie. Das bedeutet, dass dies dann fast so gut ist, wie das Trennen der MCU von der Batterie.

Minimale Hardware

Die erste Empfehlung für batteriebetriebene Arduino-Projekte ist, auf die Verwendung eines der Standard-Arduino-Boards als Zielboard zu verzichten. Besser ist, man entwirft ein eigenes Board, kauft sich ein minimales wie das RBBB oder das Target–Board von Evil Mad Scientist, oder man modifiziert ein vorhandenes, z.B. ein Pro Mini Board. Der Grund dafür ist, dass wir keine wertvolle Batterieleistung für „Power“-LEDs, ungenutzte Spannungsregler oder ungenutzte USB-Konverter verbraten wollen. Das Uno-Board z.B. verbraucht ca. 40-50 mA. Je nachdem, welche Frequenz und Versorgungsspannung man verwendet, sollte man das auf 3-18 mA reduzieren können, wenn man z.B. das im Bild zu sehende Evil Mad Scientist Board verwendet.

Es ist dabei immer eine gute Idee zu überprüfen, ob man tatsächlich den Stromverbrauch reduziert hat. Für eine grobe Schätzung ist ein billiges Multimeter mit 0,1 μA Auflösung in Ordnung. Ein genaueres Desktop-Multimeter oder der Current Ranger von LowPowerLabs ist natürlich besser. Wichtig ist, dass man alles andere von dem Zielsystem getrennt hat, z. B. den ISP-Stecker oder den FTDI-Konverter, bevor man den Stromverbrauch misst.

Meine Erfahrung mit Low-Power-Projekten ist, dass man alles macht, was in den Spezifikationen aufgeschrieben ist und dann gibt es immer noch eine Kleinigkeit, die man ignoriert hat, was zu einem unerwartet hohen Stromverbrauch führt. Das kann daran liegen, dass der ADC noch aktiv ist oder dass ein Ausgabe-Pin auf 1 geschaltet ist, wobei dieser Pin einen internen Pull-down-Widerstand besitzt, der aber nur dazu geschaltet wird, wenn der Watchdog-Timer deaktiviert ist (ich scherze nicht!).

LowPower-Bibliothek

Der einfachste Weg, Strom zu sparen, ist die Verwendung der LowPower-Bibliothek von rocketscream (wie ich es in einem meiner vorherigen Beiträge beschrieben habe). Man kann diese über den Bibliotheksmanager installieren oder aus dem GitHub-Repository herunterladen.

Die wichtigste Methode ist die powerDown-Methode , die die AVR-MCU in den Power-Down-Modus schickt. In diesem Modus werden die MCU und alle Timer gestoppt. Die einzigen Möglichkeiten, die MCU zu reaktivieren, sind ein externer Interrupt, ein PCI-Interrupt, der Interrupt des Watchdog-Timers oder eine TWI-Adressübereinstimmung (wenn die MCU als TWI-Slave konfiguriert ist). Es gibt andere Schlafmodi, die ebenfalls von der LowPower-Bibliothek abgedeckt werden. Allerdings hatte ich bisher keine Verwendung für sie, außer dem Idle-Modus (siehe unten).

Der folgende Aufruf der powerDown-Methode ist sozusagen das „Hello World“ der Low-Power-Programmierung:

LowPower.powerDown(SLEEP_FOREVER, ADC_OFF, BOD_OFF)

Dieser Aufruf schickt die MCU in den Power-Down-Modus und deaktiviert sowohl den ADC als auch den Brownout-Detektor. Wenn man diesen Aufruf in die Setup- oder Loop-Funktion schreibt, stoppt die MCU die Ausführung und schaltet alles aus. Misst man jetzt Stromverbrauch, so sollte dieser deutlich unter 1 μA liegen.

Man beachte, dass bei einem billigen Multimeter normalerweise eine Genauigkeit von etwa 1 % + 3 angegeben haben, was 1 % Fehler auf dem Messwert und eine maximale Differenz von 3 bei der am wenigsten signifikanten Ziffer bedeutet. Wenn die Auflösung 0,1 μA beträgt, bedeutet dies, dass ein angezeigter Wert von 0,5 μA in Wirklichkeit zwischen 0,2 μA und 0,8 μA liegen könnte!

Die MCU ausgeschaltet zu haben, ist schon mal ein guter Anfang. Aber man möchte sie natürlich auch wieder aufwecken. Wie oben erwähnt, kann dies mit einem externen Interrupt oder mit einem Pin-Change-Interrupt erfolgen, wobei das letztere vom Arduino-Kern nicht unterstützt wird. Man kann jedoch in der MCU-Spezifikation nachschlagen oder eine vorgefertigte Bibliothek verwenden.

Wenn man die MCU regelmäßig aufwecken möchte, muss man für den ersten Parameter etwas anderes angeben. Mögliche Argumente sind SLEEP_8S, SLEEP_4S, SLEEP_2S, SLEEP_1S, SLEEP_500MS, SLEEP_250MS, SLEEP_120MS, SLEEP_60MS, SLEEP_30MS, SLEEP_15MS. Wenn man beim Aufruf von powerDown eines dieser Argumente verwendet, wird der Watchdog-Timer (WDT) die MCU nach der angegebenen Zeit aufwecken.

Wie viel Energie braucht man beim Schlafen?

Die Datenblätter gängiger ATmega- und ATtiny-Chips geben an, dass der Power-Down-Strombedarf zwischen 0,1 μA und 0,2 μA liegt, wenn alles ausgeschaltet ist und die Temperatur bei etwa 25 °C liegt. Wird dies aber auch in der Praxis erreicht? Und macht es einen Unterschied, ob die Pins als Ein- oder Ausgänge konfiguriert sind? Wie viel Energie verbraucht der BOD, der ADC und der WDT? Um eine Antwort zu erhalten, habe ich einige Experimente auf einem ATmega328P und auf den ATtinys 84, 85, 1634 und 167 durchgeführt. Das folgende Programm spezifiziert alle verschiedenen Einstellungen, die ich gemessen habe.

#include <LowPower.h>

#define MODE ALLOFF_INP

#define ALLOFF_INP 0
#define ALLOFF_OUTH 1
#define ALLOFF_OUTL 2
#define WDT_INP 3
#define WDT_OUTH 4
#define WDT_OUTL 5
#define BODON 6
#define ADCON 7

void setup() {
#if (MODE == ALLOFF_OUTH || MODUS == ALLOFF_OUTL || \
     MODUS == WDT_OUTH || MODUS == WDT_OUTL)
  for (byte d=0; d < NUM_DIGITAL_PINS; d++) pinMode(d, OUTPUT);
#endif
#if (MODE == ALLOFF_OUTH || MODUS == WDT_OUTH)
  für (byte d=0; d < NUM_DIGITAL_PINS; d++) digitalWrite(d, HIGH);
#endif
}

void loop() {
#if (MODE == ALLOFF_INP) || (MODE == ALLOFF_OUTH) || (MODUS == ALLOFF_OUTL)
  LowPower.powerDown(SLEEP_FOREVER, ADC_OFF, BOD_OFF);
#elif (MODE == WDT_INP) || (MODE == WDT_OUTH) || (MODUS == WDT_OUTL)
  LowPower.powerDown(SLEEP_8S, ADC_OFF, BOD_OFF);
#elif (MODUS == BODON) 
  LowPower.powerDown(SLEEP_FOREVER, ADC_OFF, BOD_ON);
#elif (MODUS == ADCON) 
  LowPower.powerDown(SLEEP_FOREVER, ADC_ON, BOD_OFF);
#endif
}

Man beachte, dass die ATtiny-MCUs von der LowPower-Bibliothek nicht unterstützt werden. Man muss meinen Fork dieser Bibliothek herunterladen, wenn man die Bibliothek auf ATtinys einsetzen will.

Nachfolgend sieht man die Ergebnisse dieser Experimente für die Versorgungsspannungen von 3,3 V und 5 V (man sehe mir bitte die Benutzung des Dezimalpunktes statt des Dezimalkommas in der folgenden Tabelle nach). Der MCU-Takt ist irrelevant, da alle internen Timer angehalten sind. Ich muss hinzufügen, dass ich, um BOD auf dem ATtiny85 und ATtiny1634 zu deaktivieren, die entsprechenden Fuse-Bits programmieren musste. Ich habe nur ATtiny85s mit Silizium-Revision B, die die BOD-Deaktivierung durch Software nicht unterstützen. Und der ATtiny1634 unterstützt die BOD-Deaktivierung durch Software generell nicht.

ModeMega
328P
3.3 V
Mega
328P
5.0 V
Tiny
84
3.3 V
Tiny
84
5.0 V
Tiny
85
3.3 V
Tiny
85
5.0 V
Tiny
1634
3.3 V
Tiny
1634
5.0 V
Tiny
167
3.3 V
Tiny
167
5.0 V
All off,
pins input
0.110.140.090.120.180.460.090.100.090.11
All off,
pins output high
0.110.140.090.120.180.464167110.100.11
All off,
pins output low
0.110.140.090.120.180.460.090.110.100.11
WDT on,
pins input
4.186.144.516.264.817.032.094.064.476.35
WDT on,
pins output high
4.186.144.516.264.817.032.094.064.486.35
WDT on,
pins output low
4.186.144.516.264.817.032.084.074.476.35
BOD on18.120.416.919.018.521.118.320.618.220.6
ADC on106135221299246327225300238317
Power-down supply current (in µA)

Die Ergebnisse scheinen mit den Angaben in den Datenblättern übereinzustimmen. Es gibt jedoch eine Überraschung in der ATtiny1364-Spalte. 416 μA (3,3 V) oder 711 μA (5 V) im ausgeschalteten Zustand scheint übermäßig hoch zu sein und tritt nur auf, wenn die Pins als Ausgänge geschaltet sind und eine 1 ausgegeben wird und wenn der WDT nicht läuft. Die Erklärung für dieses merkwürdige Verhalten findet sich im Errata-Abschnitt des Datenblatts:

Port Pin Should Not Be Used As Input When ULP Oscillator Is Disabled
Port pin PB3 is not guaranteed to perform as a reliable input when the Ultra Low Power (ULP) oscillator is not running. In addition, the pin is pulled down internally when ULP oscillator is disabled.
Problem Fix / Workaround
The ULP oscillator is automatically activated when required. To use PB3 as an input, activate the watchdog timer. The watchdog timer automatically enables the ULP oscillator.

Ich brauchte den größeren Teil eines Abends, um die Verbindung zwischen diesem Text und meinen Messungen herzustellen. Zuerst muss man verstehen, dass es auch ein Problem gibt, wenn PB3 als Ausgabe konfiguriert ist (weil dann der Pull-down-Widerstand Strom zieht). Zweitens muss man die „Problembehebung“ ignorieren und den Pin im Ruhezustand auf den niedrigen Zustand setzen (oder ihn zu einem Eingang machen).

Abgesehen davon macht sich der ATtiny 1634 jedoch ziemlich gut, wenn der WDT läuft. Es sind nur 2 μA (bei 3,3 V) im Vergleich zu 4-5 μA (bei 3,3 V) für alle anderen MCUs! Darüber hinaus verfügt die MCU über 16 kB Flash-Speicher, 1 kB SRAM, 17 nutzbare GPIOs, 2 USARTs (!), ADC und den ganzen Rest. Aus diesem Grund ist es mittlerweile mein Lieblings-ATtiny. Das einzige Problem ist, dass man lernen muss, SMD-Teile zu löten.

Vergleicht man die Tabelleneinträge mit den Messergebnissen, die Nick Gammon für den ATmega328P in seinem sehr gut lesbaren Low-Power-Tutorial angibt, fällt auf, dass in Nicks Fall der Power-Down-Strom für den „All Off“-Modus 0,35 μA beträgt, wenn die Pins als Eingänge konfiguriert sind, und 1,85 μA, wenn die Pins als Ausgänge konfiguriert sind und sich in einem hohen Zustand befinden. Es stellt sich heraus, dass Nick anscheinend vergessen hat, AVcc und AGND mit Vcc bzw. GND zu verbinden (siehe das Foto des Evil Scientist Boards in seinem Beitrag). In der Tat, wenn ich diese Pins trenne, messe ich die gleichen Werte wie Nick. In dem Zusammenhang möchte ich Regel 2 aus einem meiner vorherigen Beiträge wiederholen: Sind die AVcc- und AGND-Pins (die analogen Versorgungsspannungspins) mit Vcc bzw. GND verbunden?

Regelmäßiges Aufwachen

Wenn man die MCU periodisch aufwecken möchte, sagen wir alle 10 Minuten, muss man die Anzahl der 8-Sekunden-Weckaktionen zählen, d.h. man muss bis 600/8 = 75 zählen. Die interessante Frage ist, wie viel zusätzliche Leistung diese Zählung benötigt. Um eine Vorstellung davon zu bekommen, wie hoch der resultierende durchschnittliche Versorgungsstrom ist, nimmt man den Durchschnitt des Ruhestroms I_{sleep} und des aktiven Versorgungsstroms I_{active}, gewichtet nach der Zeit des Schlafens t_{sleep} und der aktiven Zeit t_{active} :

I_{avg} =\frac{t_{sleep} \times I_{sleep} + t_{active} \times I_{active}}{t_{sleep} + t_{active}}

Die aktive Zeit besteht aus der Aufwachzeit und der Rechenzeit. Die Aufwachzeit hängt davon ab, welche Art von MCU-Oszillator man verwendet. Dem Datenblatt entnimmt man, dass der interne Oszillator 6 Taktzyklen, ein Keramikresonator 1000 Zyklen und ein Quarzoszillator 16000 Zyklen benötigt, bevor er stabil ist. Man muss also die Fuse-Bits entsprechend anpassen, um zu vermeiden, dass sie mit einem instabilen Oszillator laufen. Wenn man BOD per Software deaktiviert hat, beträgt die minimale Reaktivierungszeit 60 μs, wie im Datenblatt angegeben: When the BOD has been disabled, the wake-up time from sleep mode will be approximately 60 μs to ensure that the BOD is working correctly before the MCU continues executing code.

Das Inkrementieren einer ganzzahligen Variablen, das Vergleichen mit einer Konstante und das erneute Einschlafen ist mit etwa 100 Anweisungen, d. h. mit weniger als 300 Zyklen, möglich. Unter der Annahme eines 16-MHz-Taktes würden wir für 8.000.000 μs schlafen und für (300 + x) / 16 μs aktiv sein, wobei x die Anzahl der Weckzyklen ist. Unter der Annahme von 6 μA Schlafstrom, 16 mA aktivem Versorgungsstrom und 16.000 Zyklen bis zum Aufwachen erhalten wir einen durchschnittlichen Versorgungsstrom von

\frac{8000000 \mu{}s \times 6 \mu{}A + 1019 \mu{}s \times 16000 \mu{}A}{8001019 \mu{}s} = 8,03 µA.

Mit anderen Worten, der zusätzliche Aufwand liegt bei etwa 2 μA. Mit der viel kürzeren Aufwachzeit eines Resonators wären es nur 6,16 μA und mit dem internen Oszillator nur 6,03 μA.

Wenn man noch mehr Strom sparen möchte und/oder man die Anforderung hat, zu genau festgelegten Zeiten aufzuwachen, dann könnte man auch daran denken, eine Echtzeituhr hinzuzufügen, die das Aufwachen der MCU auslöst. Mit einem solchen Regime kommt man während des Schlafens auf weniger als 1 μA Versorgungsstrom. Es gibt RTCs wie DS3231, die eine Genauigkeit von 2ppm haben, was einen Timing-Fehler von einer Minute pro Jahr impliziert. Tatsächlich ist es möglich, die Uhr zu kalibrieren, um den Timing-Fehler zu minimieren, was zu einer noch höheren Genauigkeit führt, wie von Pete berichtet. Mit einem solchen Setup kann man einen autonomen Sensorknoten jahrelang mit einer Batterie mit einem minimalen Timing-Fehler von wenigen Sekunden pro Jahr betreiben.

Energiesparen im Wachzustand

Bisher haben wir nur über das Schlafen gesprochen. Wahrscheinlich möchte man jedoch auch im Wachzustand Strom sparen. Grundsätzlich gibt es fünf Möglichkeiten, dies zu tun:

  • Ändern der MCU-Takterzeugung
  • Reduktion der Taktfrequenz
  • Reduktion der Versorgungsspannung
  • Deaktivieren nicht verwendeter MCU-Module
  • Energiesparmodi verwenden
Ändern der MCU-Takterzeugung

Bevor wir auf die Details eingehen, wollen wir zunächst einen Referenzwert festlegen. Dieser Referenzwert ist ein ATmega328P ohne irgendwelche Peripherie, der mit 5 Volt auf 16 MHz unter Verwendung eines Quarzoszillator läuft, entweder auf einer speziellen Zielplatine oder einem getuneten Pro Mini. Im Datenblatt ATmega328P findet man folgende Abbildung:

Aktive Versorgungsstromkurve kopiert aus dem ATmega328 Datenblatt von Microchip

Daraus könnte man folgern, dass unsere Referenz-MCU etwa 9,5 mA verbrauchen sollte. Mit einem Programm, das nur eine leere Setup- und eine leere Loop-Funktion enthält, habe ich zwischen 15,0 und 18,5 mA für verschiedene Chips gemessen. Ich habe auch andere Messungen mit bis zu 24 mA gesehen. Es ist mir nicht klar, was die Ursache für diese Art von hoher Variabilität ist.

Betrachten wir ein bestimmtes Beispiel, bei dem 16,9 mA in der Standardkonfiguration verbraucht werden. Man kann den Strombedarf ändern, indem man die Fuse-Bits für die Taktquelle ändert. Indem man die Taktquelle von einem Quarzoszillator mit geringer Leistung auf einen Vollschwingoszillator umschaltet, kann man die Unempfindlichkeit gegenüber elektromagnetischen Störungen verbessern, aber man erhöht damit auch den Stromverbrauch um etwa 1 mA. Auf der anderen Seite, wenn man den Quarz gegen einen Keramikresonator austauscht, verliert man an Genauigkeit im Timing (von 30ppm auf 1 %), aber man reduziert den Strom im Bereich von 1 mA. Außerdem wird die Aufwachzeit erheblich reduziert (siehe oben). Ebenso spart man etwas Energie, wenn man von einem externen Quarzoszillator zum internen Oszillator wechselt. Man beachte, dass der interne Oszillator werkseitig bis zu einer Genauigkeit von 10 % kalibriert ist. Dies kann durch Benutzerkalibrierung auf 1 % erhöht werden, z.B. mit meinem Kalibrierungsprogramm.

Reduktion der Taktfrequenz

Viel höhere Einsparungen sind möglich, wenn die Taktfrequenz reduziert wird, wie aus der obigen Grafik ersichtlich sein sollte. Durch die Reduzierung der Taktfrequenz benötigt man jedoch mehr Zeit, um etwas zu berechnen. Insbesondere bei Anwendungen, bei denen die MCU nur für kurze Zeit wach ist, um einen Sensor auszulesen, ist es möglicherweise nicht sinnvoll, die Geschwindigkeit zu reduzieren. Wenn die MCU jedoch länger wach ist und ein Großteil dieser Zeit mit aktivem Warten verbracht wird, ist eine Reduzierung der Geschwindigkeit eine Option. Auch hier ergibt die obige Zahl für 5 Volt sehr optimistische Werte, die man in der Praxis nicht zu erreichen scheint.

Reduktion der Versorgungsspannung

Der interessante Punkt bei der Reduktion der Taktfrequenz ist, dass man auch die Versorgungsspannung reduzieren kann. Bei 8 MHz reichen 3,3 Volt. Das ist eine gute Nachricht, denn man möchte oft eine Schnittstelle zu 3,3-Volt-Sensoren herstellen. Darüber hinaus spart man durch die Reduktion der Versorgungsspannung Strom. Für 8 MHz bei 3,3 Volt sollte der Versorgungsstrom laut obiger Abbildung 3 mA betragen. Und man bekommt tatsächlich Zahlen um 3,5 mA. Im Vergleich zu unserem Referenzwert ist dies eine Reduktion um mehr als den Faktor 4!

Tatsächlich führe ich die meisten meiner Projekte auf diese Weise durch. Einer der Hauptgründe ist, dass ich dann Li-SOCl2-Batterien verwenden kann, die eine Nennspannung von 3,6 Volt haben. Ein weiterer Grund ist, dass ich keinen externen Quarzoszillator benötige. Und schließlich reichten 8 MHz für fast alle meine bisherigen Projekte.

Deaktivieren nicht verwendeter Module

AVR-MCUs enthalten eine Reihe nützlicher Module wie ADCs, UARTs, SPI- und TWI-Schnittstellen sowie Timer. Höchstwahrscheinlich verwendet man nicht alle gleichzeitig innerhalb eines Projekts. In diesem Fall kann man die nicht verwendeten Module ausschalten. Man kann entweder direkt das Leistungsreduzierungsregister PRR ändern oder man muss <avr/power.h> einbinden und kann dann Makros verwenden, um Module zu deaktivieren. Für den ATmega328P gibt es folgende Makros:

  • power_usart0_disable()
  • power_twi_disable()
  • power_timer2_disable()
  • power_tiner1_disable()
  • power_timer0_disable()
  • power_spi_disable()
  • power_adc_disable()
  • power_all_disable()

Man beachte, dass vor dem Deaktivieren des ADC der ADC gestoppt werden sollte, indem man ADCSRA auf 0 setzt.

Es gibt auch Makros, um jedes Gerät wieder zu aktivieren (mit _enable). Allerdings muss man beachten, dass einige Geräte erneut initialisiert werden müssen. Also würde ich nur Geräte deaktivieren, die nie benutzt werden.

Um eine Vorstellung davon zu bekommen, wie viel Strom eingespart werden kann, betrachte man die folgende Tabelle.

Stromverbrauch des I/O-Moduls kopiert aus dem Datenblatt ATmega328 von Microchip
Energiesparmodi verwenden

Selbst wenn man im Prinzip wach ist, kann man u.U. ein kurzes Nickerchen machen. Betrachtet man die verschiedenen Energiesparmodi der AVR-MCUs, so sieht vor allem der Idle-Modus vielversprechend aus. Alle Geräte und Timer laufen weiter, nur der MCU-Takt und der Flash-Speichertakt werden angehalten. Das bedeutet, dass alle externen und internen Interrupt-Quellen sofort bedient werden. Insbesondere die millis()-Zählung wird nicht beeinflusst. Auf der anderen Seite beendet der millis-Interrupt jeden idle-Aufruf nach höchstens einer Millisekunde (oder zwei Millisekunden bei Betrieb auf 8 MHz).

Ich habe den Idle-Modus in meiner eigenen delay-Funktion verwendet, die wie folgt aussieht:

void idledelay(unsigned long msecs)
{
  unsigned long start = millis();

  while (millis() - start &lt; msecs) 
    LowPower.idle(SLEEP_FOREVER);
}

Nutzt man diese idledelay-Funktion in der loop-Funktion als einzige Aktion auf einem ATmega328P mit 3,3 V und 8 MHz Takt, dann reduziert man den Stromverbrauch deutlich. Unter der Annahme eines aktiven Versorgungsstroms von 3,5 mA, eines Leerlaufversorgungsstroms von 1 mA und einer aktiven Zeit von etwa 200 Anweisungen zur Bedienung des millis-Interrupts, d.h. weniger als 600 Zyklen, kommt man auf einen durchschnittlichen Versorgungsstrom von 1,09 mA. D.h. man spart tatsächlich ganz erheblich, insbesondere in Fällen, in denen man viele Aufrufe der idledelay-Funktion verwendet.

Was ist mit den anderen Energiesparmodi? Nun, sie alle stoppen Timer0 und verwirren aus diesem Grund Arduinos Zeitmessung. Ich habe eigentlich nie einen guten Anwendungsfall dafür gefunden.

Ausschalten externer Geräte

Unser Fokus lag bisher auf dem Stromverbrauch der MCU. In der Regel gibt es jedoch Sensoren, Displays, LEDs, Servomotoren und andere Peripheriegeräte, die von der MCU gesteuert werden. Und auch sie verbrauchen natürlich Energie, auch wenn sie nicht genutzt werden, oft viel mehr als die MCU im aktiven Modus.

Wie bei der MCU besteht der offensichtliche Weg, um Strom zu sparen, darin, externe Geräte so oft und so lange wie möglich auszuschalten. Wenn man also z.B. eine Lebenszeichen-LED benötigt, genügt es, anstatt eine LED dauerhaft eingeschaltet zu haben, diese alle 30 Sekunden für 200 ms zu blinken. Als weiteres Beispiel habe ich in einem meiner Projekte einen RFID-Empfänger, der etwa 20 mA benötigt. Anstatt ihn die ganze Zeit eingeschaltet zu haben, schalte ich ihn nur ein, wenn das Gerät aufrecht gehalten wird, denn nur in dieser Position soll es einen RFID-Tag lesen. Natürlich braucht man einen zusätzlichen Sensor, um die Orientierung zu erfassen. Allerdings benötigt ein Sensor wie der BMA250 im Low-Power-Modus nur 16 μA, sofern man den Spannungsregler auf dem Breakout-Board entfernt.

Eine Möglichkeit, externe Geräte zu schalten ist, sie über Ausgangspins der MCU mit Strom zu versorgen. Ein ATmega328P, ein ATtiny84 oder ein ATtiny85 können bis zu 20 mA pro Ausgangspin liefern, bei einem ATtiny1634 sind es nur 10 mA. Man beachte jedoch, dass je mehr Strom gezogen wird, desto niedriger ist die Ausgangsspannung. Wenn man beispielsweise 10 mA über einen Ausgangspin an einem ATtiny1634 bei Vcc = 3 V liefert, beträgt die Ausgangsspannung nur noch 2,5 V. Normalerweise schalte ich alles mit einem Versorgungsstrom von 5 bis 10 mA direkt über einen Ausgangspin, was bisher einwandfrei funktioniert hat. Wer wirklich helle LEDs oder 7-Segment-Displays haben möchte, muss die superhellen Typen verwenden, die in der Regel etwas teurer sind, aber auch bei Versorgungsströmen von nur 3-4 mA noch sichtbar sind. Wichtig ist es außerdem zu beachten, dass es auch eine Obergrenze für den Gesamtstrom gibt, der durch den Vcc- und GND-Pin der MCU fließen darf, der 200 mA beträgt. Bisher bin ich nie auch nur annähernd an diese Grenze herangekommen.

Wenn man ein Gerät schalten möchte, das mehr als 10 mA benötigt, muss man einen elektronischen Schalter verwenden. Es gibt zwei Arten solcher Schalter, High-Side- und Low-Side-Schalter. Die erste Art von Schaltern befindet sich zwischen der Last und der Versorgungsspannung (auf der oberen Seite der Last), die letztere Art befindet sich zwischen der Last und der Masse. Der Baldengineer hat dazu ein sehr schönes Tutorial.

Anstatt Transistoren (BJT oder MOSFET) zu verwenden, gibt es auch sogenannte Lastschalter, die MOSFETs und einiges mehr enthalten, um die Stromversorgung für Geräte zu schalten. Sie verfügen in der Regel auch über einen Überlastschutz und reduzieren die Anstiegszeit, sodass Stromspitzen vermieden werden. Ein Beispiel ist der TPS2041, der bis zu 500 mA mit maximal 5,5 V schalten kann. Es gibt andere solcher Schalter, die noch höhere Spannungen schalten können, aber interessanterweise scheinen die meisten von ihnen heutzutage (im Jahr 2021) ausverkauft zu sein.

Batterielebensdauer

Da es in diesem Beitrag darum geht, wie man ein Arduino-Projekt mit Batterien realisiert, müssen wir über die verschiedenen Arten von Batterien sprechen, die es gibt. Die wichtigsten Parameter sind Nennspannung, Kapazität und Selbstentladungsrate. Ich habe ein paar typische Werte aus dem Web für die Übersichtstabelle unten gesammelt. Kapazitätswerte sind für typische Exemplare, und die Kapazität im wirklichen Leben hängt natürlich von einer Reihe von Dingen wie Temperatur, Entladestrom, Nutzungsmuster und wahrscheinlich mehr ab. Die Selbstentladungsrate ist eine grobe Annäherung, die auf Zahlen basiert, die ich im Internet gefunden habe. Man sollte jedoch eine gute Vorstellung davon bekommen, was man von einem Batterietyp erwarten kann. Man kann diese Daten dann als Näherung verwenden, um den äquivalenten durchschnittlichen Selbstentladungsstrom zu berechnen (ein Konzept, das in Nicks oben erwähntem Tutorial eingeführt wurde). Unter der konservativen Annahme, dass jeden Monat die gleiche Menge an Batteriekapazität entladen wird, die durch den Prozentsatz der voll geladenen Batterie angegeben wird, kann man die in einer Stunde entladene Kapazität leicht berechnen, indem man die monatlich entladende Kapazität durch 24\times 30 dividiert. Wenn x mAh in einer Stunde entladen werden, bedeutet dies, dass eine Stunde lang ein Strom von x mA fließt, d.h. x ist der äquivalente durchschnittliche Selbstentladungsstrom.

TypeNominal
voltage
Capacity
mAh
Self-discharge
rate per month
Equivalent
self-discharge
current in µA
Rechargable
AAA
LSD-NiMH
1.28002%22Y
AAA
NiMH
1.2100025%347Y
AA
LSD-NiMH

1.219002%53Y
AA
NiMH
1.2250025%868Y
AAA
Alkaline
1.510000.5%7N
AAA
LI-FeS2
1.512000.1%1.6N
AA
Alkaline
1.530000.5%21N
AA
Li-FeS2
1.530000.1%4.1N
CR2032
Li-MnO2
3.02300.1%0.3N
AA
Li-SOCI2
3.626000.1%3.6N
9V battery
NiMH
8.420025%69Y
9V battery
Alkaline
9.05000.5%3.5N
9V battery
Li-FeS2
9.010000.1%1.4N

Mit dieser Zahl kann man nun die Akkulaufzeit für einen Sensorknoten oder ein interaktives Gadget abschätzen. Für einen Sensorknoten berechnet man den durchschnittlichen Versorgungsstrom (wie im Abschnitt über periodische Reaktivierungen), indem man den Selbstentladungsstrom addiert. Man kann dann die Kapazität durch den durchschnittlichen Versorgungsstrom teilen und das Ergebnis ist die Batterielaufzeit.

Angenommen, man hat einen Sensorknoten, der alle 30 Minuten aufwacht, eine Messung durchführt und dann die Messung mit einem HF-Modul, z. B. dem RFM12B, sendet. Das Senden eines Telegramms benötigt 10 ms und zieht 25 mA, das Warten auf eine ACK-Nachricht kann weitere 10 ms bei 15 mA dauern. Darüber hinaus haben wir die 3 mA für den Betrieb der MCU. Approximieren wir das mit 30 mA für 20 ms an (was bereits die Weckzeit beinhalten sollte). Für den Power-Down-Modus gehen wir von 5 μA aus und nehmen eine Li-SOCI2-Batterie mit einem Selbstentladestrom von 3,6 μA an, was sich auf 8,6 μA summiert. Dann liegt der durchschnittliche Versorgungsstrom bei

\frac{8.6 \mu{}A \times 1799980 ms + 30000 \mu A \times 20 ms}{1800000 ms} = 8,9 \mu{}A.

Das heißt, unsere Batterie sollte für 2600000 \mu{}Ah/8,9 \mu{}A = 292134 h = 12172 d = 33 y halten. Mit anderen Worten, wir können diesen Sensorknoten einsetzen und dann vergessen! Wem dies zu viel manuelle Berechnung ist, kann stattdessen den Batterielaufzeitrechner verwenden. Man beachte, dass dieser Rechner 27 Jahre anstelle von 33 Jahren als Ergebnis ergibt. Der Grund dafür ist, dass dieser Rechner eine reduzierte Batteriekapazität von 85 % der Nennkapazität verwendet, aber die Selbstentladung ignoriert. Man kann jetzt leicht einige Berechnungen anstellen und feststellen, dass das Aufwachen des Sensors alle 10 Minuten die Akkulaufzeit um nur 10 % auf 30 Jahre verkürzt. Die Verwendung billigerer Batterien wie Alkaline AA-Batterien (hier bräuchten wir zwei) gibt uns 10 Jahre. Ich bin mir jedoch nicht sicher, ob Alkaline-Batterien wirklich 10 Jahre halten.

Die Schätzung der Batterielaufzeit für ein interaktives Gadget ist viel schwieriger, da es schwierig ist, das Nutzungsmuster vorherzusagen. Nehmen wir als Beispiel eine Gepäckwaage an, die eine CR2032-Batterie verwendet. Hier können wir wohl von einem Power-Down-Versorgungsstrom von 0,5 μA (oder weniger) ausgehen. Die Selbstentladung beträgt 0,3 μA und addiert sich zu 0,8 μA. Im aktiven Modus kann die MCU 3 mA, das Display wahrscheinlich weitere 4 mA und der Wägesensor (ein HX711) 1,5 mA ziehen, insgesamt also 8,5 mA. Nehmen wir an, dass eine Messung normalerweise 30 Sekunden dauert. Je nachdem, wie viel man reist, verwendet man die Waage wahrscheinlich weniger als 120 Mal pro Jahr. So verbraucht man jedes Jahr 8 mAh für den Power-Down-Modus und Selbstentladestrom und 8,5 mAh für den aktiven Einsatz, d.h. insgesamt 16,5 mAh pro Jahr. Das bedeutet, dass die Batterie bis zu 14 Jahre alt werden kann. Leider sind nicht alle diese Gepäckwaagen so konzipiert. Ich besitze eine billige chinesische Gepäckwaage, bei der die Batterie in etwa einem halben Jahr leer ist, während die Waage überhaupt nicht benutzt wird. Also muss ich die Batterie immer entfernen, nachdem ich sie benutzt habe.

Der Begriff Batteriekapazität wurde bisher in einer Weise verwendet, die darauf hindeutet, dass die Batterie nach Verbrauch der Kapazität leer ist. Es gibt jedoch keinen solchen Punkt, an dem die Batterie vollständig leer ist. Tatsächlich wird die Kapazität in der Regel mit der Qualifizierung einer Abschaltspannung angegeben. Zum Beispiel wird dieser Schwellenwert für Alkaline-Batterien normalerweise mit 0,9 Volt angegeben. NiCd- und NiMH-Akkus werden in der Regel nur bis 1,0 Volt eingesetzt, um Schäden an den Zellen zu vermeiden. Für Li-SOCI2-Batterien gibt die Firma SAFT an, dass die Nennkapazität in Bezug auf eine Abschaltspannung von 2,0 V angegeben wird. Aufgrund der Anforderungen externer Geräte wie Displays oder Sensoren und wegen der Taktfrequenz von 8 MHz sollte die Versorgungsspannung jedoch nicht unter 2,5 V liegen, was bedeutet, dass man nicht die volle Kapazität der Batterie nutzen kann. Glücklicherweise ist die Entladekurve der Li-SOCI2-Akkus jedoch sehr flach, sodass es keinen großen Unterschied macht, ob wir bei dieser Art von Batterien bei 2,5 oder 2,0 Volt (siehe unten) abschalten.

Entladungsdiagramm kopiert aus dem LS14500 Datenblatt von SAFT

Spannungsregler

Oben haben wir angenommen, dass die Batterie direkt mit der MCU und möglicherweise mit externen Geräten verbunden ist. In der Tat ist dies die bevorzugte Konfiguration, da keine Energie verschwendet wird. Manchmal kann es jedoch notwendig sein, Spannungsregler zu verwenden. Ein Szenario ist, wenn man manchmal mehr als nur ein paar Milliampere benötigt, sagen wir 60 mA. Dann haben die oben genannten Li-SOCI2 ein Problem. Die Spannung sinkt auf 3,1 oder sogar auf 2,6 V (wenn es sehr kalt ist).

Wenn ich eine Batterie benötige, die auch höhere Ströme liefern kann, benutze ich lieber 4 AA(A)-Batterien oder eine 9-Volt-Batterie und verwende einen Linearregler, der die ungenutzte Spannung in Wärme umwandelt. Der Standardregler dafür ist der LM7805. Dies ist jedoch kein Regler, den man in einem batteriebetriebenen Gerät verwenden möchten. Erstens hat er eine Dropout-Spannung von 2 V, d.h. er benötigt mindestens 2 Volt mehr als am Ausgang geliefert wird. Zweitens hat er einen Ruhestrom von 8 mA, d.h. er verbrennt 8 mA, auch wenn keine Last vorhanden ist.

Stattdessen möchte man einen LDO-Regler (Low Dropout) mit minimalem Ruhestrom verwenden. Beispiele dafür sind MCP 1702 und HT75XX-1. Sie haben sowohl eine sehr niedrige Dropout-Spannung als auch einen Ruhestrom von etwa 2 μA, was für ein Low-Power-Design akzeptabel ist. Während der MCP1702 über einen höheren Ausgangsstrom (250 mA) liefern kann, hat der HT75XX-1 (100 mA) den Vorteil, dass er auch dann sinnvoll arbeitet, wenn die Eingangsspannung unterhalb der Nennausgangsspannung liegt. Der MCP1702 entleert in diesem Fall die Batterie, während der HT75XX-1 die niedrigere Spannung weiter reicht, ohne verrückt zu spielen.

Anstelle von Linearreglern kann man auch an Schaltregler denken, die Niederspannung in höhere Spannung umwandeln, sogenannte Boost-Wandler. Ich habe noch keine davon in meinen Projekten verwendet, da sie in der Regel einen recht hohen Ruhestrom von mehr als 0,5 mA haben, z.B. der U1V10F3 von Pololu. Das ist in Ordnung, wenn man sowieso einen hohen Selbstentladungsstrom hat, sieht aber nach Energieverschwendung aus, wenn man z.B. Alkaline Zellen verwendet.

Das MCP 1640 Booster-IC sieht beim Studium des Datenblattes sehr vielversprechend aus. Es hat einen Ruhestrom von weniger als 20 μA, kann bis zu 100 mA liefern und beginnt um 0,8 V zu arbeiten. Bei 0,8 V Eingangsspannung soll es immer noch einen Wirkungsgrad von 80 % haben, wenn der Ausgangsstrom 10 mA beträgt. Die Breakout-Boards, die ich habe, liefern jedoch nicht. Das Board, das 100 mA bei 3,3 V liefern sollte, ging in den Overdrive, als die Last nur 10 mA und die Eingangsspannung 1,2 Volt betrug. Es hatte dann einen Eingangsstrom von 120 mA!

Der LTC3525 ist ein weiterer Booster-IC, das in die Low-Power-Kategorie passt. Es hat einen Ruhestrom von 7 bis 30 μA je nach Eingangsspannung und kann bei einer Festspannung von 3, 3,3 oder 5V bis zu 60-150 mA Ausgangsstrom liefern. Es hat einen Wirkungsgrad von mehr als 90% für eine durchschnittliche Last. Jeelabs verkauft das sogenannte AA-Board mit einem davon. Ich werde es auf jeden Fall mal testen!

Es gibt auch Schaltregler, die eine niedrigere Ausgangsspannung als die Eingangsspannung haben, sogenannte Abwärtswandler, und es gibt Regler, die in beide Richtungen gehen. Ich habe noch nie einen gesehen, der einen niedrigen Ruhestrom hat.

Überwachung der Versorgungsspannung

Wie oben erwähnt, nimmt die Versorgungsspannung mit der Zeit ab, wenn man mit Batterien arbeitet. Dies bedeutet, dass man einen Punkt festlegen sollte, an dem man das System ausschaltet. Wenn man eine AVR-MCU mit einer zu niedrigen Spannung betreibt, kann der EEPROM-Speicher und der Flash-Speicher beschädigt werden. Wenn man ein Display verwendet, hört dies irgendwann auf zu funktionieren.

Man könnte jetzt denken, dass dies ein idealer Anwendungsfall für das Brownout-Erkennungssystem wäre. In den meisten Fällen muss man jedoch den Betrieb des Systems einstellen, bevor ein für die MCU kritisches Spannungsniveau erreicht wird. Daher deaktiviere ich den BOD in der Regel komplett, um Energie und Weckzeit zu sparen. Stattdessen messe ich Versorgungsspannung beim Aufwachen. Man kann zu diesem Zweck den ADC mit der Versorgungsspannung als Referenzspannung verwenden und die intern erzeugte Referenzspannung von 1,1 V messen. Tatsächlich liegt der reale Wert je nach Exemplar (Temperatur und Versorgungsspannung) zwischen 1,0 und 1,2 V.

Um die Variabilität des Exemplars zu eliminieren, habe ich ein Kalibrierungsprogramm geschrieben, das man verwenden kann, auch wenn nur Zugriff auf die ISP-Pins besteht. Dieses Programm schreibt den Kalibrierungswert in das EEPROM, wenn man dies wünscht. Die Vcc-Bibliothek kann dann verwendet werden, um recht genaue Messungen der Versorgungsspannung durchzuführen. Auf dieser Grundlage kann die MCU eine Warnung ausgeben, wenn die Batteriespannung zu niedrig ist.

Man sollte meinen, dass diese Methode nicht funktioniert, wenn man einen Spannungsregler verwendet, da man in diesem Fall die geregelte Spannung misst. Für die Linearregler funktioniert es jedoch, wenn sie im Pass-Through-Modus arbeiten, sobald die Spannung unter der Eingangsschwelle liegt. Für Schaltregler funktioniert es wirklich nicht. Jean-Claude Wippler von Jeelabs hat dafür jedoch eine Lösung.

Zusammenfassung

Zusammenfassend lässt sich sagen, dass das Design von Batterie-basierten Arduinoprojekten nicht sonderlich kompliziert ist. Man sollte:

  • unnötige Hardware wie Power-LEDs, ungenutzte USB-Wandler oder ungenutzte Spannungsregler sollte man vermeiden,
  • so viel wie möglich schlafen (und ADC und BOD vorher ausschalten),
  • auf 8 MHz mit 3,3 Volt (oder weniger) laufen,
  • verwenden einer eignen delay-Funktion, die den Idle-Sleep-Modus verwendet,
  • externe Geräte so oft wie möglich ausschalten,
  • Spannungsregler nach Möglichkeit zu vermeiden,
  • wenn man Spannungsregler verwendet, sollte man solche mit niedrigem Ruhestrom verwenden, und schließlich sollte man
  • die Batteriespannung überwachen.

Der wichtigste Punkt bei Low-Power-Projekten ist, das System auf die Low-Power-Eigenschaft zu testen, bevor man es in die Wildnis entlässt. Wie ich eingangs erwähnt habe, gibt es oft sehr subtile Dinge, die zu viel höheren Versorgungsströmen führen können als erwartet. Und diese Probleme werden nur entdeckt, wenn man das System unter realistischen Bedingungen testet.

Views: 247