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.
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.
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);
}
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 |
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 |
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 |
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.
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
}
Ä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
}
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.