Week 04

interrupts, timers, sleep mode

Annoucements

  • (slide) homepage

    • Jekyll + GitLab pages + CI
    • je to lepšie a lepšie
  • (slide) mattermost

  • (slide) hack košice

    • iný typ hackathonu ako tie, ktoré ste mali možnosť zažiť
    • medzinárodná (študentská) účasť
    • priestor pre to, aby ste sa naozaj niečo nové naučili
  • (slide) Namakaný deň 2019

Handling the Internal Interrupts with Arduino

(slide) Do kategórie interných prerušení patria časovače. Pomocou nich je možné napríklad presne načasovať spúšťanie časti programu, čo sa častokrát používa aj na vytvorenie dojmu “paralelizácie” vykonávaných úloh.

(slide) Mikrokontrolér ATmega328P obsahuje 3 časovače označené ako TIMER0, TIMER1 a TIMER2. TIMER0 a TIMER28 bitové, zatiaľ čo TIMER2 je 16 bitový časovač. Ich funkcionalita však závisií od frekvencie mikrokontroléra. V prípade Arduina je použitý externý oscilátor s frekvenciou 16 MHz.

(slide) Kažý časovač môže generovať jeden alebo viac prerušení. Tieto sú:

  • Compare Match Interrupt - Tento typ prerušenia vznikne v situácii, keď je potrebné, aby sa časovač zastavil pri dosiahnutí konkrétnej hodnoty. Jeho aktuálna hodnota sa porovnáva s požadovanou a keď dôjde k zhode, dôjde k vyvolaniu tohto prerušenia.

  • Overflow Interrupt - Každý časovač používa vnútorné počítadlo, ktoré má svoj rozsah v závislosti od veľkosti použitého registra. Hodnota počítadla sa postupne zvyšuje, a keď je dosiahnutá maximálna hodnota a tá sa znova zvýši o jednotku, dôjde k pretečeniu a vyvolaniu tohto typu prerušenia.

  • Input Capture Interrupt - Tento typ prerušenia sa používa na zachytenie vzniknutej udalosti na pine. K prerušeniu dôjde vtedy, keď je na pine zaznamenaná konkrétna hrana signálu. Tá môže byť vzostupná, zostupná alebo akákoľvek. Časovač dokáže zaznamenať čas vzniku tejto udalosti.

S použitím týchto časovačov sa je možné stretnúť pri programovaní Arduina bežne. Časovač TIMER0 sa používa pre funkcie pracujúce s časom, ako sú delay(), millis() a micros(). TIMER1 je zasa možné nájsť v knižnici Servo.h a TIMER2 sa používa pri generovaní zvuku pomocou funkcie tone(). Funkcia analogWrite() využíva každý časovač, ale každý pre rozličné piny.

(slide) Prehľad vlastností dostupných časovačov na mikrokontroléri ATmega328P sa nachádza v tabuľke XXX.

Timer Size Range Possible Interrupts
TIMER0 8 bits 0-255 Compare Match, Overflow
TIMER1 16 bits 0-65535 Compare Match, Overflow, Input Capture
TIMER2 8 bits 0-255 Compare Match, Overflow

(slide) Osobitným časovačom je Watchdog. Ak sa zariadenie dostane do chybového stavu, je jeho úlohou po uplynutí časového intervalu mikrokontrolér reštartovať. Pre prácu s Watchdog časovačom je k dispozícii knižnica avr/wdt.h.

TimerOne Library

(slide) S časovačmi je možné pracovať dvoma spôsobmi: pomocou štandardných AVR knižníc alebo pomocou knižníc tretích strán. A práve knižnica TimerOne ponúka veľmi jednoduché API, ktoré zakryje nízkoúrovňový prístup ponúkaný knižnicami AVR.

Táto knižnica poskytuje kolekciu funkcií na nastavenie 16 bitového časovača označovaného ako TIMER1 (odtiaľ názov knižnice). Pôvodným zámerom bolo vytvoriť jednoduchý a rýchly spôsob na nastavenie periódy PWM. Nakoniec však bolo do knižnice zahrnuté aj prerušenie timer overflow a iné vlastnosti.

(slide) Na prácu s knižnicou stačí poznať len tieto funkcie:

  • Timer1.initialize(microseconds) - Táto funkcia inicializuje prácu s časovačom a musí byť zavolaná ako prvá. Parameter microseconds definuje periódu časovača.
  • Timer1.attachInterrupt(function) - Vždy po uplynutí periódy sa spustí ISR funkcia, ktorej názvoj je uvedený v parametri.
  • Timer1.detachInterrupt() - Zakáže prerušenie, takže ISR funkcia sa prestane spúšťať.

Pomocou knižnice je však možné ovládať aj už spustený časovač pomocou týchto funkcií:

  • Timer1.start() - Spustí časovač a začne novú periódu.
  • Timer1.stop() - Zastaví časovač.
  • Timer1.restart() - Reštartuje časovač od začiatku novej periódy.

Putting Your Device to Sleep

(slide) 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?

(slide) 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.

(slide) 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.

(slide) 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

(slide) 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í 3040 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ť len SLEEP_MODE_IDLE, SLEEP_MODE_ADC, SLEEP_MODE_PWR_SAVE, SLEEP_MODE_STANDBY alebo SLEEP_MODE_PWR_DOWN
  • sleep_mode() - makro na prechod do režimu spánku spolu s nastavením bitu SE (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. Makro sleep_mode() totiž najprv bit SE nastaví (volanie sleep_enable()), následne mikrokontrolér uspí (volanie sleep_cpu()) a po zobudení zasa bit SE vyčistí (volanie sleep_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);
}

Additional Sources