Stromsparen mit dem ATtiny

In einem vorherigen Beitrag wurde die Programmierung und Verwendung des ATtiny schon grundlegend beschrieben. Hier soll nun auf das Stromersparnis-Potential eingegangen werden, welches sich vor allem bei batteriebetriebenen Projekten bezahlt machen kann.
Da beim ATtiny keine externen Komponenten Strom verbrauchen können -wie beim Arduino-Board- wird hier nur auf interne Sparmöglichkeiten eingegangen.

Verwendete Bauteile

Grundsätzliche Überlegungen

Generell ist es nur sinnvoll, sich über Einsparmaßnahmen bzgl. des Stromverbrauchs beim ATtiny Gedanken zu machen, wenn der Mikrocontroller nicht die gesamte Betriebszeit etwas zu tun hat oder wenn die peripheren Komponenten deutlich weniger Strom als der Mikrocontroller selbst verbrauchen.

Betriebsspannung & Taktfrequenz

Der Stromverbrauch kann schon sehr leicht durch Reduzierung der Betriebsspannung vermindert werden. Dies kann (abhängig vom Typ des ATtiny) jedoch mit einer Reduzierung der Taktfrequenz einhergehen, doch kann diese in vielen Anwendungen durchaus ohne Probleme vermindert werden, um eine deutlich längere Batterielaufzeit zu ermöglichen.
Der folgende Sketch wird als Basis für die nächsten Versuche verwendet:

#define PIN_LED PCINT0

void setup()
{
    pinMode(PIN_LED, OUTPUT);
}

void loop()
{
    digitalWrite(PIN_LED, HIGH);
    delay(3000);
    digitalWrite(PIN_LED, LOW);
    delay(3000);
}

Messungen

In der folgende Messtabelle kann man leicht erkennen, dass eine Reduktion von Betriebsspannung und Taktfrequenz einen immensen Unterschied in der Stromaufnahme des ATtiny ausmacht.

Typ Taktfrequenz Spannung Stromaufnahme (LED an) Stromaufnahme (LED aus)
ATtiny85 1MHz 5V 5mA 2,5mA
ATtiny85 1MHz 3V 2mA 1mA
ATtiny85 8MHz 5V 10,81mA 7,70mA
ATtiny85 8MHz 3V 5,50mA 4,21mA
ATtiny85 16MHz 5V 15,85mA 13,0mA
ATtiny85 16MHz 3V 8,66mA 7,47mA
ATtiny13A 1,2MHz 5V 3,6mA 1,17mA
ATtiny13A 1,2MHz 3V 1,78mA 0,62mA
ATtiny13A 4,8MHz 5V 4,58mA 2,22mA
ATtiny13A 4,8MHz 3V 2,42mA 1,34mA
ATtiny13A 9,6MHz 5V 7,15mA 4,22mA
ATtiny13A 9,6MHz 3V 3,73mA 2,38mA

Zustände der Ausgänge (GPIO)

Je nachdem, wie die GPIOs (Ein-/Ausgänge) der Mikrocontroller geschaltet sind, verbrauchen sie unterschiedlich viel Strom. Für die folgenden Messungen wurde als Sketch eine leere loop()-Funktion verwendet. Im setup() wurden die Pins entsprechend gesetzt.

Typ Taktfrequenz Spannung GPIO-Zustand Stromaufnahme
ATtiny85 1MHz 5V nicht zugewiesen 2,482mA
ATtiny85 1MHz 5V INPUT 2,533mA
ATtiny85 1MHz 5V OUTPUT (LOW) 1,569mA
ATtiny85 1MHz 5V OUTPUT (HIGH) 1,572mA
ATtiny85 1MHz 3V nicht zugewiesen 0,969mA
ATtiny85 1MHz 3V INPUT 0,969mA
ATtiny85 1MHz 3V OUTPUT (LOW) 0,964mA
ATtiny85 1MHz 3V OUTPUT (HIGH) 0,975mA
ATtiny13A 1,2MHz 5V nicht zugewiesen 1,241mA
ATtiny13A 1,2MHz 5V INPUT 1,290mA
ATtiny13A 1,2MHz 5V OUTPUT (LOW) 0,929mA
ATtiny13A 1,2MHz 5V OUTPUT (HIGH) 0,931mA
ATtiny13A 1,2MHz 3V nicht zugewiesen 0,559mA
ATtiny13A 1,2MHz 3V INPUT 0,560mA
ATtiny13A 1,2MHz 3V OUTPUT (LOW) 0,555mA
ATtiny13A 1,2MHz 3V OUTPUT (HIGH) 0,561mA

ADC abschalten

In Anwendungsfällen, in denen der ADC (= Analog-Digital-Konverter) nicht benötigt wird, so kann dieser auch abgeschaltet werden. Für die folgenden Messungen wurde als Sketch eine leere loop()-Funktion verwendet. Im setup() wurde der ADC abgeschaltet mit:
ADCSRA = 0;

Typ Taktfrequenz Spannung ADC Stromaufnahme
ATtiny85 1MHz 5V aktiv 2,603mA
ATtiny85 1MHz 5V aus 2,333mA
ATtiny85 1MHz 3V aktiv 0,978mA
ATtiny85 1MHz 3V aus 0,752mA
ATtiny13A 1,2MHz 5V aktiv 1,253mA
ATtiny13A 1,2MHz 5V aus 1,267mA (?)
ATtiny13A 1,2MHz 3V aktiv 0,558mA
ATtiny13A 1,2MHz 3V aus 0,556mA

Sleep-Modus

Aufbau

Um den Sleep-Modus zu demonstrieren, wir ein simpler Aufbau verwendet. Nur eine LED wird mit einem Vorwiderstand an einen Pin (z.B. PB3) geschaltet, um den Betriebszustand zu visualisieren. Dazu wird noch ein Mikrotaster an einem weiteren Pin (z.B. PB2) angebracht, um einen externen Trigger zum Aufwecken des ATtiny zu ermöglichen.

Schaltplan für den Aufbau mit dem ATtiny85

Aufwecken durch Pin Change Interrupt (PCI)

In dem nachfolgenden Sketch wird im loop() eine LED für 1 Sekunde zum Leuchten gebracht. Dann erlischt sie und der ATtiny wird in den Sleep-Modus versetzt. Nun wartet der ATtiny darauf, dass ein sog. Pin Change Interrupt (PCI) an einem vorher gewählten Pin durchgeführt wird, d.h. der Mikrotaster wird gedrückt. Ist dies der Fall, so wird der ATtiny geweckt und das Programm läuft weiter.
Der folgende Sketch ist auf einem ATtiny85 mit interner Clock von 1MHz kompiliert und getestet worden. Bei anderen Taktfrequenzen wird delay() nicht mehr richtig funktionieren und man sollte Verzögerungen ohne delay() implementieren.

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

#define PIN_LED    PCINT0
#define PIN_BUTTON PCINT2

ISR (PCINT0_vect) {}

void setup()
{
    pinMode(PIN_LED, OUTPUT);
    pinMode(PIN_BUTTON, INPUT_PULLUP);

    // pin change interrupt
    PCMSK |= bit (PIN_BUTTON);
    GIFR  |= bit (PCIF); // clear any outstanding interrupts
    GIMSK |= bit (PCIE); // enable pin change interrupts
}

void loop()
{
    digitalWrite (PIN_LED, HIGH);
    delay (5000);
    digitalWrite (PIN_LED, LOW);
    goToSleep();
}

void goToSleep()
{
    set_sleep_mode(SLEEP_MODE_PWR_DOWN);
    ADCSRA = 0;            // turn off ADC
    power_all_disable();  // power off ADC, Timer 0 and 1, serial interface
    sleep_enable();
    sleep_cpu();
    sleep_disable();
    power_all_enable();    // power everything back on
}

Aufwecken durch den Watchdog Timer (WDT)

Ähnlich zu dem Beispiel mit dem WDT und dem Arduino wacht hier der ATtiny durch den internen Watchdog auf. Der Mikrotaster aus dem obigen Schaltplan wird aus der Schaltung entfernt.

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

#define PIN_LED    PCINT0

volatile bool toggle = true;

// Watchdog imer Interrupt Service Routine
ISR(WDT_vect)
{
    toggle = true;
}

void setup()
{
    pinMode(PIN_LED, OUTPUT);

    // setup of the WDT
    cli();
    wdt_reset(); // reset watchdog timer
    MCUSR &= ~(1 << WDRF); // remove reset flag
    WDTCR = (1 << WDCE); // set WDCE, access prescaler
    WDTCR = 1 << WDP0 | 1 << WDP1 | 1 << WDP2; // set prescaler bits to to 2s
    WDTCR |= (1 << WDIE); // access WDT interrupt
    sei();
}

void loop()
{
    if (toggle) {
        toggle = false;
        digitalWrite(PIN_LED, !digitalRead(PIN_LED));
        enterSleepMode();
    }
}

void enterSleepMode()
{
    byte adcsra;

    adcsra = ADCSRA; // save ADC control and status register A
    ADCSRA &= ~(1 << ADEN); // disable ADC

    MCUCR |= (1 << SM1) & ~(1 << SM0); // Sleep-Modus = Power Down
    MCUCR |= (1 << SE); // set sleep enable
    sleep_cpu(); // sleep
    MCUCR &= ~(1 << SE); // reset sleep enable

    ADCSRA = adcsra; // restore ADC control and status register A
}

Stromverbrauch

In dem obigen Setup wurde eine Spannungsversorgung über einen 18640-LiPo-Akku mit 3V angelegt. Die angeschlossene LED ist mit einem 1kΩ-Vorwiderstand geschaltet. Dabei verbraucht dieses Setup im Betriebsmodus -d.h. während die LED leuchtet und der ATtiny arbeitet- ~1,8mA. Geht der ATtiny in den Sleep-Modus, so konnte ich nur noch ~20µA messen, was jedoch vermutlich durch mein (nicht allzu hochwertiges) Multimeter recht ungenau ist. Jedoch ist das Stromsparpotential beachtlich.

Weiterführende Links

zurück