Putting Your Device to Sleep
To, čo sa aktuálne podarilo použitím prerušení, je odstrániť kód z hlavnej slučky programu. Mohlo by sa zdať, že tým, že je táto slučka prázdna, je možné ušetriť energiu. Toto zdanie je však mylné. Mikrokontrolér je totiž stále aktívny a stále v ňom dochádza k volaniu funkcie loop()
, aj keď je jej telo prázdne. Môžete si vyskúšať, čo dosiahnete na svojom počítači, ak v interpretéri príkazového riadku napíšete takýto skript:
while true; do
# nothing to do in here
:
done
Ak sa následne pozriete na vyťaženie procesora uvidíte, že je (v prípade viacproserového systému je jedno jadro) vyťažené na 100%. A to aj napriek tomu, že uvedený fragment kódu nič nerobí.
Ako teda usporiť energiu v prípade, ak je obsah funkcie loop()
prázdny a mikrokontrolér "nič" nevykonáva? Odpoveďou je uspanie mikrokontroléra.
What is Sleep Mode?
Režim spánku je špeciálny režim mikrokontroléra, do ktorého je možné mikrokontrolér prepnúť v čase jeho neaktivity. V tomto režime sa mikrokontrolér prepne do režimu nízkej spotreby, čím je možné elektrickú energiu ušetriť. Jej ušetrené množstvo závisí od toho, aké všetky komponenty zostanú napájané aj po prechode do režimu spánku. Stav mikrokontroléra sa v režime spánku nestratí, pretože zostáva v jeho pamäti.
Princíp fungovania je podobný, ako keď váš domáci spotrebič, ako je napr. televízor, DVD prehrávač alebo set-top box, vypnete pomocou diaľkového ovládača. Zariadenie sa nevypne úplne, pretože čaká na signál z diaľkového ovladača, ktorý ho opätovne zobudí (zapne) a uvedie do plnej prevádzky. Počas režimu spánku je však jeho odber elektrickej energie minimálny v porovnaní s množstvom elektrickej energie, ktorú odoberá pri plnej prevádzke. Mnohé zariadenia tento režim reprezentujú pomocou červenej LED diódy.
Režim spánku nefunguje bez použitia prerušení. Ak sa raz mikrokontrolér uspí, okrem prerušenia ho vie zobudiť len reset (ktorý je vlastne tiež prerušením). Prerušenie v tomto prípade funguje ako budík - zobudí mikrokontrolér, ktorý prerušenie automaticky obslúži pomocou príslušnej IRS funkcie. Následne môže mikrokontrolér opäť uspať alebo sa začne vykonávať hlavný program.
Putting Arduino to Sleep
Množstvo ušetrenej energie bude závisiť od toho, akú prototypovaciu dosku Arduino použijete. Rozličné verzie Arduín obsahujú rozličné súčasti, ktoré tiež spotrebujú nejakú energiu. Ak napríklad použijete Arduino Uno, v režime spánku bude mať spotrebu 19 mA, zatiaľ čo v bežnej prevádzke bude mať spotrebu v rozmedzí 30 až 40 mA. Ak ale použijete Arduino Pro Mini, jeho spotreba počas spánku bude len 0.57 mA a v bežnej prevádzke 25 mA. Rozdiel je teda značný.
To, aké režimy spánku podporuje konkrétny mikrokontrolér, je potrebné vždy overiť v jeho dokumentácii. V prípade mikrokontroléra ATmega328P, ktorý je srdcom Arduino Uno sa jedná o 6 režimov, z ktorých je len 5 dostupných v hlavičkovom súbore avr/sleep.h
:
- Idle (
SLEEP_MODE_IDLE
) - ADC Noise Reduction (
SLEEP_MODE_ADC
) - Power-save (
SLEEP_MODE_PWR_SAVE
) - Standby (
SLEEP_MODE_STANDBY
) - Power-down (
SLEEP_MODE_PWR_DOWN
)
Najmenej úsporným režimom je režim Idle. Je to taktiež predvolený režim, takže ak počas behu programu nebude explicitne zvolený iný režim, pri uvedení mikrokontroléra do režimu spánku sa použije režim Idle. Z tohto režimu je možné mikrokontrolér zobudiť takmer ľubovoľným spôsobom.
Najviac úsporným režimom je režim Power-down. V tomto režime je zakázaných najviac súčastí mikrokontroléra a zo spánku ho je možné prebudiť len pomocou externých prerušení.
Jednotlivé režimy sa od seba navzájom líšia tým, aké všetky súčasti mikrokontroléra budú vypnuté, ako aj tým, akým spôsobom bude zasa mikrokontrolér prebudený. V tabuľke XXX sa nachádza prehľad možností, ktorými je možné mikrokontrolér ATmega328P zobudiť. Pre konkrétny mikrokontrolér je vždy dobré overiť jeho možnosti v dokumentácii.
Wake-up Sources | Idle | ADC Noise Reduction | Power-save | Standby | Power-down |
---|---|---|---|---|---|
INT1, INT0 and Pin Change | X | X | X | X | X |
TWI Address Match | X | X | X | X | X |
Timer2 | X | X | X | ||
SPM/EEPROM Ready | X | X | |||
A/D Converter | X | X | |||
Watchdog Timer | X | X | X | X | X |
Other I/O | X |
Netreba zabudnúť na to, že mikrokontrolér sa dá z každej úrovne spánku vždy úspešne prebudiť pomocou RESET-u.
Pre prácu s režimom spánku je potrebné do programu načítať hlavičkový súbor avr/sleep.h
. Ten obsahuje všetky potrebné makrá a funkcie na prácu s režimom spánku. Vo všeobecnosti bude stačiť použiť tieto z nich:
set_sleep_mode()
- funkcia na nastavenie režimu spánku, pričom parametrom môže byť lenSLEEP_MODE_IDLE
,SLEEP_MODE_ADC
,SLEEP_MODE_PWR_SAVE
,SLEEP_MODE_STANDBY
aleboSLEEP_MODE_PWR_DOWN
sleep_mode()
- makro na prechod do režimu spánku spolu s nastavením bituSE
(Sleep Enable) pred prechodom do spnánku a aj jeho vyčistením po zobudení
[!NOTE]
V rozličných zdrojoch sa dá stretnúť s postupnosťou volaní týchto makier:
sleep_enable(); sleep_cpu(); sleep_disable();
Volaním makra
sleep_mode()
je možné nahradiť tieto tri makrá naraz. Makrosleep_mode()
totiž najprv bitSE
nastaví (volaniesleep_enable()
), následne mikrokontrolér uspí (volaniesleep_cpu()
) a po zobudení zasa bitSE
vyčistí (volaniesleep_disable()
).
Jednoduchá ilustrácia uvedenia mikrokontroléra do režimu spánku sa nachádza v nasledujúcom fragmente kódu, ktorý predstavuje modifikáciu štandardného príkladu Blink. Na 500 ms sa rozsvieti vstavaná LED dióda, na čo sa mikrokontrolér uvedie do najtvrdšieho spánku (Power-down). Nakoľko však pred spánkom nebol zadefinovaný žiadny spôsob opätovného prebudenia, mikrokontrolér sa už nezobudí a LED dióda už nezhasne. To znamená, že úroveň HIGH
zostane na pin-e nezmenená aj po uspatí. Jediný spôsob, ako ho opätovne prebudiť, je stlačiť tlačidlo RESET
.
#include <Arduino.h>
#include <avr/sleep.h>
void setup(){
pinMode(LED_BUILTIN, OUTPUT);
set_sleep_mode(SLEEP_MODE_PWR_DOWN);
}
void loop(){
// blink
digitalWrite(LED_BUILTIN, HIGH);
delay(500);
// sleep
sleep_mode();
// unreachable code
digitalWrite(LED_BUILTIN, LOW);
delay(500);
}
[!WARNING]
Tu však pozor! Ak sa mikrokontrolér uspí v režime Power-down bez toho, aby bol definovaný spôsob jeho zobudenia pomocou prerušenia, mikrokontrolér sa už nezobudí. Jediným spôsobom, ako ho zobudiť je privedením signálu na pin
RESET
. Dôsledkom tejto situácie bude, že pokus o preprogramovanie mikrokontroléra v stave spánku bude neúspešný. Nástroj pre nahratie kódu do mikrokontroléra sa bude v prípade neúspechu pokúšať nahrať kód opakovane. Stačí v tomto momente mikrokontrolér resetnúť. Tým sa mikrokontrolér zobudí a počas jeho štartovania sa kód do mikrokontroléra nahrá.
Waking the Microcontroller with External Interrupts
Upravíme prípad použitia zariadenia na rozpoznávanie pohybu. Tentokrát sa zariadenie po spustení uspí. Akonáhle PIR senzor detekuje pohyb, zariadenie sa zobudí a začne blikať externou diódou. Blikať bude dovtedy, kým sa nestlačí tlačidlo, ktoré zariadenie zresetuje, vypne LED diódu a zariadenie uspí.
Stavový diagram zariadenia sa veľmi nebude líšiť od predchádzajúceho. Zariadenie sa bude môcť opät nachádzať v jednom z troch stavov. Zmení sa však spôsob prechodov medzi jednotlivými savtmi.
Rovnako tak sa nezmení ani schéma zapojenia - bude identická, ako je na obrázku XXX.
Zdrojový kód riešenia sa nachádza vo výpise XXX. Za najväčšiu odlišnosť oproti predchádzajúcemu kódu je možné považovať prechod do režimu spánku.
#include <Arduino.h>
#include <TimerOne.h>
#include <avr/sleep.h>
#define PIN_LED 8
#define PIN_PIR 2
#define PIN_BTN 3
volatile bool isMovement;
volatile byte countdown;
void idle();
void watch();
void alarm();
void idle(){
isMovement = false;
digitalWrite(PIN_LED, LOW);
digitalWrite(LED_BUILTIN, LOW);
// reatach interrupts
detachInterrupt(digitalPinToInterrupt(PIN_BTN));
attachInterrupt(digitalPinToInterrupt(PIN_PIR), watch, RISING);
Timer1.detachInterrupt();
// go to sleep
set_sleep_mode(SLEEP_MODE_PWR_DOWN);
interrupts();
sleep_mode();
}
void tick(){
countdown--;
if(countdown == 0){
alarm();
}
// go to sleep
interrupts();
sleep_mode();
}
void watch(){
// update state
digitalWrite(PIN_LED, HIGH);
// reatach interrupts
set_sleep_mode(SLEEP_MODE_ADC);
attachInterrupt(digitalPinToInterrupt(PIN_PIR), idle, FALLING);
countdown = 10;
Timer1.attachInterrupt(tick);
Timer1.restart();
// go to sleep
interrupts();
sleep_mode();
}
void alarm(){
// update state
digitalWrite(PIN_LED, HIGH);
isMovement = true;
// reatach interrupts
Timer1.detachInterrupt();
detachInterrupt(digitalPinToInterrupt(PIN_PIR));
attachInterrupt(digitalPinToInterrupt(PIN_BTN), idle, LOW);
}
void setup(){
// set pin modes
pinMode(PIN_LED, OUTPUT);
pinMode(PIN_PIR, INPUT);
pinMode(PIN_BTN, INPUT_PULLUP);
// setup timer and sleep mode
Timer1.initialize(1 * 1000000);
// enter idle state
idle();
}
void loop(){
if (isMovement == true){
digitalWrite(PIN_LED, HIGH);
delay(1000);
digitalWrite(PIN_LED, LOW);
delay(1000);
}
}
V tomto prevedení je možné vidieť ošetrenie externých prerušení. A to konkrétne level interrupt, ktorý reprezentuje tlačidlo a pin change interrupt, ktorý reprezentuje PIR senzor.
Low-Power Library
Mikrokontrolér často obsahuje aj súčasti, ktoré potrebujú osobitné nastavenia, ak chceme znížiť ich spotrebu energie. Jedná sa napríklad o:
- A/D prevodník
- analógový komparátor
- Brown-out detect
- 3 časovače
- Watch-dog timer
Tieto súčasti mikrokontroléra je možné vypínať a zapínať selektívne. Je to možné dosiahnuť pomocou registrov mikrokontroléra alebo pomocou volaní makier nachádzajúcich sa v knižnici avr/power.h
. Napríklad pre vypnutie A/D prevodníka je možné zavolať makro power_adc_disable()
a pre jeho opätovné zapnutie zasa makro power_adc_enable()
.
Existuje však knižnica s názvom Low-Power, ktorá zjednodušuje prácu s režimom spánku a umožňuje pred prechodom do spánku vypínať požadované komponenty s cieľom dosiahnutia vyššej úspory energie. Knižnica má tieto vlastnosti:
- podporuje všetky režimy spánku mikrokontroléra ATmega328P,
- umožňuje mikrokontrolér uspať na 15 ms, 30 ms, 60 ms, 120 ms, 250 ms, 500 ms, 1 s, 2 s, 4 s, 8 s, a na stálo,
- umožňuje vypnúť A/D prevodník,
- umožňuje vypnúť modul Brownout Detector
- vo vybraných režimoch spánku umožňuje vypnúť všetky časovače, USART0, TWI, a modul SPI
Knižnica sa používa prostredníctvom triedy LowPower
, ktorá má niekoľko metód: idle()
, adcNoiseReduction()
, powerDown()
, powerSave()
, powerStandby()
a powerExtStandby()
. Každá z nich reprezentuje niektorý z režimov spánku mikrokontroléra. Pomocou parametrov týchto metód je možné nastaviť dĺžku strvania spánku ako aj zoznam súčastí, ktoré majú byť zapnuté alebo vypnuté.
Príklad použitia ilustruje nasledujúci riadok kódu. Pomocou neho sa mikrokontrolér uspí na 8 sekúnd v režime Power Down, pričom moduly ADC a BOD budú vypnuté.
LowPower.powerDown(SLEEP_8S, ADC_OFF, BOD_OFF);
https://github.com/lowpowerlab/lowpower (vraj lepsia verzia?)
http://www.rocketscream.com/blog/2011/07/04/lightweight-low-power-arduino-library/