Při psaní složitých programů se snadno dostanete do situace, kdy obvod nereaguje okamžitě, například při stisknutí tlačítka. V takovém případě je nejlepší použít přerušení, která jsou popsána v tomto článku, aby každý pochopil myšlenku jejich použití.
Nejprve však několik slov o senzorech známých z poplašné techniky: jazýčkových spínačích a detektorech pohybu.
Objednejte si sadu prvků a začněte se učit v praxi! Kliknutím sem přejdete do obchodu >>
Senzory alarmu: Jazýčkové spínače
Prvním, velmi jednoduchým alarmovým čidlem je jazýčkový spínač. Používá se na dveře a okna. Skládá se ze dvou částí: vhodně tvarované destičky (ve skleněné trubičce) a magnetu, který se připevní na dveře/okno.
Když se magnet přiblíží k trubici, vnitřní desky se dotknou a proud teče z jedné linie senzoru do druhé. Z hlediska Arduino je tedy situace analogická použití běžných tlačítek.
V praxi se jazýčkové spínače pro alarmové aplikace prodávají v krytech, které umožňují snadnou montáž – jeden takový snímač je součástí sad společnosti Forbot. Ty jsou namontovány následujícím způsobem (magnet na okně a jazýčkový spínač na rámu). I sebemenší pohyb okna přeruší obvod a spustí alarm:
Jazýčkový spínač v praxi
Nyní je čas otestovat chování senzoru v praxi. Za tímto účelem zapojíme jednoduchý testovací obvod. Jako indikátor stavu alarmu slouží komerčně dostupná dioda RGB. Pokud je okno/dveře zavřené, dioda by měla svítit zeleně, jinak červeně.
Hotové sady pro kurzy Forbot
Komponenty pro cvičení z kurzu Arduino (úroveň 2) jsou k dispozici jako hotové sady! Patří sem programovatelné diody, analogové a digitální teploměry, 7-segmentové displeje a senzor pohybu (PIR).
Seznam připojení:
- LED_R přes rezistor 1k na pinu 10,
- LED_G přes 1k rezistor na pinu 11,
- LED_B přes rezistor 1k na pinu 12,
- společna katoda LED k zemi,
- jeden vývod jazýčkového spínače na pin 0,
- druhý pin jazýčkového spínače na zem.
Jazýčkový spínač není polární prvek, na tom nezáleží,
který z pinů připojíme k zemi a který k Arduino!
Zapojení na desce plošných spojů může vypadat jako na obrázku níže. Pro zjednodušení jsem jazýčkový spínač přišrouboval k překližce pomocí šroubu M3 ze sady a hotového otvoru v základně.
Mimořádně jednoduchý princip funkce senzoru umožňuje stejně jednoduchý program. Kromě absolutního minima (konfigurace vývodů) kontrolujeme také stav senzoru:
#define LED_R 10
#define LED_G 11
#define LED_B 12
#define JAZÝČKOVÝ SPÍNAČ 0
void setup() {
pinMode(LED_R, OUTPUT); //Jednotlivé řídicí piny diod jako výstupy
pinMode(LED_G, OUTPUT);
pinMode(LED_B, OUTPUT);
pinMode(JAZÝČKOVÝ SPÍNAČ, INPUT_PULLUP); //Jazýčkový spínač jako vstup
digitalWrite(LED_R, LOW); //Dioda vypnuta
digitalWrite(LED_G, LOW);
digitalWrite(LED_B, LOW);
}
void loop() {
if (digitalRead(JAZÝČKOVÝ SPÍNAČ) == LOW) { //Pokud je snímač zkratovaný
digitalWrite(LED_R, LOW); //Stav OK - zelená dioda
digitalWrite(LED_G, HIGH);
} else {
digitalWrite(LED_R, HIGH); //Stav ALARM - červená dioda
digitalWrite(LED_G, LOW);
}
}
Výsledkem je jednoduchý alarm, který reaguje na stav jazýčkového spínače:
Jak je vidět na obrázku výše, obě části jazýčkového spínače nemusí ležet přesně vedle sebe. Je povolena určitá volnost, což výrazně usnadňuje montáž na okna/dveře.
Domácí úkol 4.1
Použijte bzučák bez generátoru (jako v případě v předchozí části kurzu) a napište program, který spustí akustický alarm, jakmile je aktivován jazýčkový spínač. Jeho opětovný kontakt (např. zavřením dveří) by neměl deaktivovat akustický signál.
Alarmové senzory: Detekce pohybu, PIR
Většina z nás si detektory pohybu PIR (Passive Infra Red) spojuje s poplašnými systémy. Z pohledu osoby používající komerčně dostupný senzor se jedná o jednoduché zařízení, které při detekci pohybu odpovídajícím způsobem změní stav svého výstupu.
Nebudeme se zde zabývat přesným principem jejich fungování. Je však důležité si uvědomit, že nejjednodušší z těchto senzorů jsou založeny na měření infračerveného záření (tj. teploty) prostředí. Proto je například teplý průvan také považován za pohyb. O něco „chytřejší“ řešení stojí odpovídajícím způsobem více.
Zajímavé je také podívat se, jak vypadá samotný snímač, který je obvykle umístěn za speciální čočkou (uvnitř krytu snímače):
HC-SR501 - oblíbený PIR senzor
Pro hobby projekty vývojáři nejspíše použijí velmi levný modul PIR senzoru s označením HC-SR501. Nyní se na to podíváme. Nejdůležitější informace o tomto modulu:
- Přípustné napájecí napětí: 5-20 V,
- Spotřeba energie: od 40 uA v pohotovostním režimu do 150 uA,
- Výstupní signál: 0 / 3,3 V,
- Nastavitelný rozsah: 3-7 m,
- Detekční úhel: 120º.
Jak vidíte, můžeme tento senzor napájet přímo z Arduino, protože již správně pracuje s napětím 5 V. Navíc odebírá relativně malý proud, takže nenarušuje činnost zbytku obvodu (jako tomu bylo například při použití serva). Lze jej bez problémů použít i v konstrukcích napájených z baterie.
Aby se snímač při montáži lépe používal, dodává jej výrobce bez krytu. Sada se samozřejmě dodává s odpovídajícím plastovým objektivem, bez kterého by snímač nemohl fungovat.
Ze spodní strany jsou vidět 3 zajímavé součásti:
- Potenciometr (Tx) pro nastavení doby trvání vysokého stavu při detekci objektu.
- Potenciometr (Sx) pro nastavení citlivosti senzoru.
- Připojení – napájení a výstupní signál.
Modul HC-SR501 je připraven k použití téměř okamžitě po vybalení. Ve výchozím nastavení pracuje celé zařízení v režimu retriggeringu. Tento režim znamená, že při detekci pohybu se na výstupu objeví vysoký stav, který se udržuje po určitou dobu (Tx), a při každé další detekci pohybu se čas (Tx) opět odpočítává od nuly.
Zjednodušeně řečeno: Vysoký stav je na výstupu tak dlouho,
dokud senzor detekuje pohyb + čas nastavený na potenciometru Tx.
V režimu bez opětovného spuštění se výstup dostane do vysokého stavu pouze jednou, poté výstup senzoru klesne – bez ohledu na to, zda jsou detekovány další probíhající pohyby.
Režimy se mění pomocí propojek, které jsou na desce snímače
označeny jako L a H. V tomto článku používáme standardní režim (H).
Funkčnost senzoru v režimu, který jsme zvolili (opětovné spuštění), je podobná funkčnosti jazýčkového spínače popsané výše. Vysoký stav na vstupu Arduino znamená, že byl detekován pohyb. Potenciometrem Tx lze také nastavit, jak dlouho má trvat vysoký stav po jedné detekci pohybu.
Tuto hodnotu můžeme nastavit v rozmezí přibližně 5 sekund až 3 minuty.
Rozsah nastavení času a rozsah snímače se u jednotlivých zařízení mírně liší. Pro jistotu doporučujeme otestovat vlastní modul.
HC-SR501 v praxi
Nejprve použijeme snímač pohybu podobně jako jazýčkový spínač. Zapojení diody RGB zůstává beze změny. Senzor PIR jsem jednoduše přidal tak, že jsem ho připojil následujícím způsobem:
- Vcc → 5V na kontaktní desce,
- OUT → pin č. 2 na Arduino,
- GND → GND na kontaktní desce.
Přes pájené spoje (gold piny) není senzor zapojen přímo do kontaktní desky. To lze řešit různými způsoby. Použil jsem kombinaci konektorů samec-samec a samice-samice – jak je znázorněno na obrázku níže. Tímto způsobem jsem zapojil jeden konektor samice do modulu a druhý (samec) přímo do kontaktní desky. Oba typy kabelů jsou součástí sad Forbot pro kurz Arduino úroveň II.
Celý okruh nakonec vypadal takto:
Zaprvé se nemusíte obtěžovat s potenciometry pro nastavení doby a rozsahu pulzů. Stačí je otočit do krajní polohy, jak je znázorněno na obrázku níže. Pak je nejjednodušší pozorovat účinek obvodu.
Pokud nastavíte potenciometry jinak, může být zpočátku mnohem obtížnější pochopit, jak obvod funguje (zejména pokud nastavíte příliš vysokou hodnotu pro Tx).
Program funguje podobně jako předchozí program:
#define LED_R 10
#define LED_G 11
#define LED_B 12
#define JAZÝČKOVÝ SPÍNAČ 0
#define PIR 2
void setup() {
pinMode(LED_R, OUTPUT); //Jednotlivé řídicí piny diod jako výstupy
pinMode(LED_G, OUTPUT);
pinMode(LED_B, OUTPUT);
pinMode(JAZÝČKOVÝ SPÍNAČ, INPUT_PULLUP); //Jazýčkový spínač jako vstup
pinMode(PIR, INPUT); //PIR jako vstup
digitalWrite(LED_R, LOW); //Dioda vypnuta
digitalWrite(LED_G, LOW);
digitalWrite(LED_B, LOW);
}
void loop() {
if (digitalRead(PIR) == LOW) { //Pokud je snímač zkratovaný
digitalWrite(LED_R, LOW); //Stav OK - zelená dioda
digitalWrite(LED_G, HIGH);
} else {
digitalWrite(LED_R, HIGH); //Stav ALARM - červená dioda
digitalWrite(LED_G, LOW);
}
}
Při detekci pohybu se dioda přepne z polohy zelenéna červenou a po chvíli se přepne zpět na zelenou. Doporučuji vyzkoušet dosah senzoru, protože je poměrně dlouhý!
Je důležité si uvědomit, že senzory PIR svým dosahem pokrývají velkou oblast. Jejich rozlišovací schopnost jim neumožňuje rozpoznat například „mávající prst v rohu místnosti“. Měly by se používat spíše v situacích, kdy je třeba reagovat na odchod osoby apod.
Jednotlivé senzory PIR neumožňují lokalizovat místo, kde je pohyb detekován.
Domácí úkol 4.2
Napište program, který při detekci pohybu přehraje krátkou melodii. Takové zařízení může být například připevněno ke dveřím a hlásit vstup nové osoby do místnosti.
Přerušení na Arduino
Samotné senzory alarmu nevyžadují žádné nové znalosti programování. Lze je velmi snadno použít ve vašich projektech. Protože je hardwarová část tak jednoduchá, můžeme se věnovat přerušení na Arduino, což je zcela nové téma.
Nejprve poznámka pro ty, kteří jsou s tématem obeznámeni.. Jsem si vědom toho, že přerušení jsou mnohem složitějším problémem. V následujícím popisu jsem se však pokusil některé informace zobecnit a uvést pouze ty, které se týkají Arduino. Nechci čtenáře zahlcovat teorií, kterou stejně na začátku nepoužijí.
Co jsou vnější přerušení?
Přerušení je mechanismus, který umožňuje pod vlivem vnějšího signálu okamžitě přerušit právě prováděnou část programu a přepnout na určitou funkci. Například alarm musí být zaregistrován, jakmile dojde ke spuštění jazýčkového spínače, bez ohledu na to, co program právě dělá.
Tento úkol bychom označili za časově kritický pro celé zařízení.
Zdá se, že stačí jednoduchá smyčka, která kontroluje stav vstupu. Nemůžeme si však dovolit, aby byl program vždy zaměstnán pouze kontrolou senzoru. Někdy je nutná komunikace s počítačem (UART) nebo ovládání dalších částí systému, což zabírá drahocenný čas, během kterého bychom mohli zmeškat signál z poplachového senzoru.
Přerušení každodenního života
Pro ilustraci problému bych rád uvedl příklad ze života. Očekáváte důležitou zásilku, tak důležitou, že záměrně zůstáváte doma a čekáte na kurýra. Jak už to v takových situacích bývá, interkom právě selhal a kurýr nemá vaše číslo. Zároveň byste doma rádi dělali něco jiného – například vařili polévku.
Verze 1 (bez přerušení). Během dne musíte pravidelně chodit k okénku, abyste zjistili, zda kurýr dorazil(?). Nakrájíte mrkev, přidáte ji do polévky a podíváte se z okna. Nakrájíš další mrkev a vrátíš se k oknu. Nejenže musíte přerušit obě činnosti, ale co hůř, kurýr může přijet právě ve chvíli, kdy krájíte zeleninu.
Kurýr nemá čas, a tak po chvíli odjede,
a ani si neuvědomíte, že už jste jeho příchod propásli!
Verze 2 (s přerušením). Tentokrát nemáte v úmyslu jít čas od času k oknu. Proto se usmíváte na svoji starší sousedku, která se celý den dívá z okna (asi každý má takovou) …..
Požádáte ji, aby zaklepala na zeď, jakmile uvidí kurýra. Od této chvíle se věnujete výhradně krájení mrkve. Celý den v klidu a pohodě, aniž by šla k oknu. Najednou uslyšíte klepání na zeď – to je pro vás velmi důležitý signál – je tu „něco“, co je třeba udělat právě teď. Kurýr přijel a čeká, tento úkol je časově kritický.
Proto bez váhání přerušíte aktuální úkol
(i uprostřed řezání) a vyzvednete zásilku.
Když ji vyzvednete a vrátíte se do bytu, můžete pokračovat ve vaření polévky tam, kde jste přestali. Díky pomoci souseda, který vás vyrušil z práce, jste se mohli věnovat jiným úkolům a hlavně jste nezmeškali příjezd kurýra.
Převratné změny ve světě mikrokontrolérů
Výše uvedený popis je záměrně oddělen od programování, ale všimněte si, že:
- Vaření polévky → běžná obsluha programu (např. hlášení o stavu, komunikace s počítačem, ovládání komponent).
- Příjezd kurýra → časově kritická úloha, např. detekce poplachu PIR senzorem nebo stisknutí tlačítka uživatelem. Vše, co vyžaduje reakci „tady a teď“. Nikdo nechce, aby zařízení rozpoznalo stisknutí tlačítka až po 5 sekundách.
- Sousedka → Mechanismus zpracování přerušení.
- Klepání na zeď → Zpráva o přerušení.
- Vyzvednutí zásilky → Zpracování přerušení.
Nyní jsme vyřešili mechanismus přerušení!
Doufám, že je nyní jasné, v jakých situacích je to užitečné a jaké výhody to přináší. Výhody přerušení:
- Na pozadí máme „pomocníka“, který monitoruje vstup (senzor).
- Tento pomocný program pracuje paralelně s mikrokontrolérem, který provádí program.
- Nemusíme „ztrácet“ čas častou kontrolou senzoru.
- Použití přerušení zajistí, že událost bude zpracována přesně v okamžiku, kdy je to potřeba. A to i v případě, že je nutné přerušit právě prováděnou část programu.
Kdy je přerušení v praxi užitečné?
Zkusme si nyní v praxi na Arduino ukázat, kdy může být přerušení nutné. Vraťme se k předchozímu příkladu, kde barva LED diody závisela na stavu jazýčkového spínače:
#define LED_R 10
#define LED_G 11
#define LED_B 12
#define JAZÝČKOVÝ SPÍNAČ 0
void setup() {
pinMode(LED_R, OUTPUT); //Jednotlivé řídicí piny diod jako výstupy
pinMode(LED_G, OUTPUT);
pinMode(LED_B, OUTPUT);
pinMode(JAZÝČKOVÝ SPÍNAČ, INPUT_PULLUP); //Jazýčkový spínač jako vstup
digitalWrite(LED_R, LOW); //Dioda vypnuta
digitalWrite(LED_G, LOW);
digitalWrite(LED_B, LOW);
}
void loop() {
if (digitalRead(JAZÝČKOVÝ SPÍNAČ) == LOW) { //Pokud je senzor zkratovaný
digitalWrite(LED_R, LOW); //Stav OK - zelená dioda
digitalWrite(LED_G, HIGH);
} else {
digitalWrite(LED_R, HIGH); //Stav ALARM - červená dioda
digitalWrite(LED_G, LOW);
}
}
Hlavní smyčka programu je krátká a jejím jediným úkolem je kontrola stavu jazýčkového spínače. Není možné, aby takový program nezjistil otevření jazýčkového spínače. Stačí však jednoduchá změna programu a situace vypadá úplně jinak…
Rozšiřme program tak, aby smyčka trvala relativně dlouho. Takového efektu můžeme dosáhnout přidáním ovládacího prvku – blikání diody zabudované v Arduino (pin 13) by mělo signalizovat činnost „alarmu“:
#define LED_R 10
#define LED_G 11
#define LED_B 12
#define LED_SYG 13
#define JAZÝČKOVÝ SPÍNAČ 0
void setup() {
pinMode(LED_R, OUTPUT); //Jednotlivé řídicí piny diod jako výstupy
pinMode(LED_G, OUTPUT);
pinMode(LED_B, OUTPUT);
pinMode(LED_SYG, OUTPUT);
pinMode(JAZÝČKOVÝ SPÍNAČ, INPUT_PULLUP); //Jazýčkový spínač jako vstup
digitalWrite(LED_R, LOW); //Dioda vypnuta
digitalWrite(LED_G, LOW);
digitalWrite(LED_B, LOW);
digitalWrite(LED_SYG , LOW);
}
void loop() {
if (digitalRead(JAZÝČKOVÝ SPÍNAČ) == LOW) { //
Pokud je senzor zkratovaný
digitalWrite(LED_R, LOW); //Stav OK - zelená dioda
digitalWrite(LED_G, HIGH);
} else {
digitalWrite(LED_R, HIGH); //Stav ALARM - červená dioda
digitalWrite(LED_G, LOW);
}
digitalWrite(LED_SYG, HIGH); //Blikání diody
delay(2000);
digitalWrite(LED_SYG, LOW);
delay(2000);
}
Podívejme se, jak se program chová nyní! Alarm již nefunguje dobře. Celá věc reaguje na změny senzorů různě – někdy okamžitě, někdy až po 4 sekundách a někdy vůbec.
To se u poplašného systému nesmí stát. Spuštění alarmu po několika sekundách je sice ještě přijatelné, ale naprostá absence reakce takové řešení diskvalifikuje. Kdyby zloděj rychle prošel oknem, náš systém by si toho ani nestačil všimnout.
Nemůžeme si dovolit, aby kritický úkol
(„alarmová zpráva“) nebyl dokončen.
Je čas konečně zavést přestávku a zlepšit program!
Přerušení v Arduinu
Nejprve je třeba ve funkci setup() deklarovat přerušení. Použijeme poměrně dlouhý příkaz attachInterrupt, jehož syntaxe je následující:
attachInterrupt(digitalPinToInterrupt(PIN), FUNKCE, REAKCE_NA);
Toto téma je poměrně složité, proto jsem se každému argumentu věnoval podrobněji v samostatné podkapitole.
Pin přerušení - digitalPinToInterrupt(PIN)
Prvním argumentem funkce, která deklaruje přerušení, je specifikace pinu, který má být použit jako externí přerušení. Přerušení je mechanismus zabudovaný v mikrokontrolérech – není to jen programovací proces.
Z tohoto důvodu můžeme používat pouze určité piny.
To platí například i pro UART, který je vždy připojen na piny 0 a 1.
V případě UNO můžeme zpracovávat pouze přerušení na pinech 2 a 3. Jiné desky řeší přerušení v jiných množstvích na jiných pinech. Proč zadáváme číslo pinu v argumentech funkce attachInterrupt prostřednictvím jiné funkce digitalPinToInterrupt?
Jedna věc jsou piny a jejich číslování, druhá věc jsou následná přerušení a jejich číslování. Podle mého názoru není v této fázi podrobná znalost tohoto tématu nutná. Stačí se podívat na tabulku:
Jak vidíte, přerušení označené int.0 na desce Arduino UNO je na pinu 2, ale v případě Arduino Leonardo je int.0 na pinu 3. Nebudu rozebírat, proč tomu tak je.
Zde přichází ke slovu funkce digitalPinToInterrupt, která, abychom si nemuseli pamatovat čísla přerušení, přiřadí číslo automaticky na základě zadaného pinu a typu desky Arduino vybraného v kompilátoru.
Tím je zajištěno, že kód napsaný na UNO bude fungovat i při rekompilaci na Leonardo atd. Bez této funkce by program běžel nesprávně (nebo vůbec).
V případě Arduino UNO máme dvě možnosti:
attachInterrupt(digitalPinToInterrupt(2), FUNKCE, REAKCE_NA); //Přerušení na pinu 2
attachInterrupt(digitalPinToInterrupt(3), FUNKCE, REAKCE_NA); //Přerušení na pinu 3
Funkce vyvolaná při přerušení
Když mikrokontrolér obdrží informaci o vnějším přerušení, přeruší aktuální úlohu a provede kód obsažený v námi napsané funkci. Jeho název je druhým argumentem funkce attachInterrupt().
Jedná se o běžnou funkci, jak jsme často psali v kurzu Arduino I. úrovně. Nemůže však přijímat ani vracet argumenty. Pokud by například přerušení vývodu 2 nastavilo proměnnou Alarm na hodnotu 1, stalo by se to následovně:
attachInterrupt(digitalPinToInterrupt(2), nastavAlarm, REAKCE_NA); //Přerušení na pinu 2
[...]
void nastavAlarm {
alarm = 1;
}
Měli byste si uvědomit, že dobrá funkce, která je volána při přerušení,
je funkce, jejíž provedení trvá jen krátkou dobu!
Žádné čekání, žádné složité výpočty, žádné podmínky při čekání na další signály atd. Více se o tom nyní rozepisovat nebudu, protože v praxi to bude mnohem srozumitelnější.
Kdy je třeba hlásit přerušení (REAKCE_NA)?
Již víme, který pin má fungovat jako externí přerušení a který kód se má spustit. Jako poslední parametr funkce attachInterrupt musíme zadat, „na co“ má mechanismus přerušení reagovat. Můžeme si vybrat mezi:
- LOW – Vyvolá přerušení, pokud je na vstupu nízký stav.
- CHANGE – volání při změně hodnoty na pinu (z vysoké na nízkou a naopak).
- RISING – volání při změně hodnoty z nízkého do vysokého stavu.
- FALLING – volání při změně hodnoty z vysoké na nízkou.
Možnost vyvolat přerušení, když se objeví stoupající nebo klesající svah, usnadňuje psaní programů v mnoha situacích!
Nebojte se, pokud to pro vás v tuto chvíli není přirozené!
Nejlepší je pochopit to v praxi.
Přerušení Arduino v praxi
Je čas na první program, který bude využívat přerušení. Začneme kontrolou stavu čidla PIR, protože je již připojeno k vývodu, který zpracovává přerušení.
První program, který využívá přerušení, by mohl vypadat takto:
#define LED_R 10
#define LED_G 11
#define LED_B 12
#define JAZÝČKOVÝ SPÍNAČ 0
#define PIR 2
void setup() {
pinMode(LED_R, OUTPUT); //Jednotlivé řídicí piny diod jako výstupy
pinMode(LED_G, OUTPUT);
pinMode(LED_B, OUTPUT);
pinMode(JAZÝČKOVÝ SPÍNAČ, INPUT_PULLUP); //Jazýčkový spínač jako vstup
pinMode(PIR, INPUT); //PIR jako vstup
digitalWrite(LED_R, LOW);
digitalWrite(LED_G, HIGH);
digitalWrite(LED_B, LOW);
attachInterrupt(digitalPinToInterrupt(PIR), alarm, RISING); // Přerušení reagující na stoupající svah
}
void loop() {
//Nic se neděje
}
void alarm() {
digitalWrite(LED_R, HIGH); //Stav ALARM - červená dioda
digitalWrite(LED_G, LOW);
}
Na první pohled by tento kód neměl dělat nic, protože v hlavní smyčce loop() není žádný příkaz. Stačí si program načíst, abyste se přesvědčili o opaku. Pokud je detekována změna logického stavu výstupu senzoru (z nízkého na vysoký), rozsvítí se červená dioda.
Použili jsme přerušení vyvolané vzestupným kopcem (RISING), protože nás zajímá situace, kdy výstup senzoru změní svůj stav z nízkých 0 V na vysoké 3,3 V, tj. vzroste.
To je zajímavé, protože stav senzoru nikde „přímo“ v softwaru nekontrolujeme! To za nás provádí hardwarový mechanismus odpovědný za přerušení, který reaguje na změnu stavu senzoru.
Je třeba poznamenat, že přerušení je signalizováno pouze jednou, když je detekován pohyb – konkrétně když senzor detekuje pohyb a signál na jeho výstupu se změní z nízké na vysokou hodnotu.
Kromě toho je funkce tohoto mechanismu necitlivá na věci, které jsou umístěny v hlavní smyčce. Můžeme tam například přidat blikající diodu (připojenou na pin 13), která by za normálních okolností způsobila zpožděnou odezvu na signál ze senzoru:
#define LED_R 10
#define LED_G 11
#define LED_B 12
#define LED_SYG 13
#define JAZÝČKOVÝ SPÍNAČ 0
#define PIR 2
void setup() {
pinMode(LED_R, OUTPUT); //Jednotlivé řídicí piny diod jako výstupy
pinMode(LED_G, OUTPUT);
pinMode(LED_B, OUTPUT);
pinMode(LED_SYG, OUTPUT);
pinMode(JAZÝČKOVÝ SPÍNAČ, INPUT_PULLUP); //Jazýčkový spínač jako vstup
pinMode(PIR, INPUT); //PIR jako vstup
digitalWrite(LED_R, LOW);
digitalWrite(LED_G, HIGH);
digitalWrite(LED_B, LOW);
attachInterrupt(digitalPinToInterrupt(PIR), Alarm, RISING); // Přerušení reagující na stoupající svah
}
void loop() {
digitalWrite(LED_SYG, HIGH);
delay(2000);
digitalWrite(LED_SYG, LOW);
delay(2000);
}
void alarm() {
digitalWrite(LED_R, HIGH); //Stav ALARM - červená dioda
digitalWrite(LED_G, LOW);
}
Domácí úkol 4.3
Napište program, který pomocí přerušení spustí alarm (rozsvítí se červená LED dioda), ale ne ihned, jakmile senzor zaznamená pohyb, ale až když se pohyb zastaví.
Přenos informací "z" funkce přerušení
Jak jsem již zmínil, funkce odpovědná za obsluhu přerušení nemůže vrátit žádnou hodnotu pomocí standardního mechanismu return. To lze samozřejmě „obejít“ například použitím globálních proměnných. Musíte však zajistit, aby všechny globální proměnné použité v rámci funkce přerušení byly deklarovány se speciální předponou – volatile.
Použití předpony „volatile“ zajišťuje, že se překladač nepokusí tuto proměnnou optimalizovat, čímž se vyhne mnoha obtížně odhalitelným chybám. Pokud by byla optimalizace aktivována, program by si nemusel „všimnout“, že se hodnota proměnné během přerušení změnila.
Zkusme vytvořit program, který bude počítat, jak často senzor PIR zaznamenal pohyb, a v závislosti na tom nastaví barvu LED RGB. K tomu stačí zvýšit hodnotu proměnné, která bude počítat počet vzestupných hran v příslušném přerušení.
Program v praxi funguje podle očekávání. Se zvyšujícím se číslem se mění barva kontrolky LED, což znamená, že senzor PIR zaznamenal pohyb.
Domácí úkol 4.4
Napište program, který pomocí přerušení změní stav LED diody na opačný stav pokaždé, když PIR senzor detekuje pohyb. To znamená: první detekce se rozsvítí, druhá detekce se nerozsvítí atd.
#define LED_R 10
#define LED_G 11
#define LED_B 12
#define LED_13 13
#define JAZÝČKOVÝ SPÍNAČ 0
#define PIR 2
volatile int kolikrát = 0;
void setup() {
pinMode(LED_R, OUTPUT); //Jednotlivé řídicí piny diod jako výstupy
pinMode(LED_G, OUTPUT);
pinMode(LED_B, OUTPUT);
pinMode(JAZÝČKOVÝ SPÍNAČ, INPUT_PULLUP); //Jazýčkový spínač jako vstup
pinMode(PIR, INPUT); //PIR jako vstup
digitalWrite(LED_R, LOW);
digitalWrite(LED_G, HIGH);
digitalWrite(LED_B, LOW);
attachInterrupt(digitalPinToInterrupt(PIR), alarm, RISING); // Přerušení reagující na stoupající svah
}
void loop() {
if (kolikrát < 3) { // Akceptovatelně - zelená barva
digitalWrite(LED_R, LOW);
digitalWrite(LED_G, HIGH);
digitalWrite(LED_B, LOW);
} else if (kolikrát < 6) { //Začíná být nebezpečně - modrá barva
digitalWrite(LED_R, LOW);
digitalWrite(LED_G, LOW);
digitalWrite(LED_B, HIGH);
} else { //Alarm - pohyb detekován více než 6krát - červená barva
digitalWrite(LED_R, HIGH);
digitalWrite(LED_G, LOW);
digitalWrite(LED_B, LOW);
}
}
void alarm() { //Přerušení
kolikrát++; //Byl zjištěn další alarm
}
Co nesmí funkce z přerušení obsahovat?
Používání přerušení nám může usnadnit život v mnoha projektech. Stejně snadno se však můžete chytit do pasti. Nesprávné použití přerušení může způsobit, že se program bude chovat velmi podivně a bude obtížné zjistit příčinu.
Při psaní funkcí, které jsou volány přerušením, je proto třeba vzít v úvahu následující skutečnosti:
- V žádném případě se do nich nesmí zabudovat zpoždění (delay, delayMicroseconds atd.).
- Neprovádějte v nich žádné operace, které trvají dlouho.
- Operace prováděné v rámci přerušení by měly být extrémně krátké. Stejně jako výše – inkrementujte proměnnou a je konec. Už se nemusíme starat ani o zapnutí příslušné LED diody. Můžeme to udělat normálně ve smyčce – jediným časově kritickým aspektem byla skutečnost, že PIR senzor zaregistroval pohyb.
Naštěstí pro Arduino funkce delay() nefunguje ani tehdy, když ji zabudujeme do funkce přerušení. Funkce delayMicroseconds() však bude fungovat dobře, ale neměli bychom ji používat k umělému vyvolávání delších prodlev v programu.
Pokud funkce prováděná během přerušení trvá velmi dlouho, ovlivní to chod celého programu. To může mimo jiné vést k uměle prodlouženým zpožděním, která jsou generována funkcí delay().
Proč je to důležité? Pokud by provedení funkce z přerušení trvalo např. 5 sekund (což je příliš dlouho), každý výskyt přerušení vede k takovému zpoždění celého programu.
Nezapomeňte, že celá výhoda daného mechanismu spočívá v tom, že mikrokontrolér přeruší probíhající operaci a provede funkci přerušení. Pokud bychom v hlavní smyčce měnili stav diody každou sekundu, každé volání přerušení by diodu zmrazilo na dalších 5 sekund.
To znamená, že následující kód by byl viditelný ve smyčce a LED by blikala různě – někdy by se její stav měnil každou 1 sekundu, jindy každých 6 sekund (delay(1000) + doba přerušení):
void loop() {
digitalWrite(13, HIGH);
delay(1000);
digitalWrite(13, LOW);
delay(1000);
}
Při psaní delších a složitějších programů by bylo velmi, velmi obtížné najít příčinu takové chyby!
Od výše uvedených pravidel se samozřejmě můžete odchýlit, ale pouze pokud jste s tématem přerušení obeznámeni a přesně víte, co se po takových změnách stane. Pokud však s programováním teprve začínáte a jste přesvědčeni, že je třeba zavést zpoždění při přerušení, v 99,99 % případů se mýlíte – problém lze jistě vyřešit jinak.
Kdy byste měli používat přerušení?
- Pokud je reakce na vnější signál časově kritická (např. detekce alarmu).
- Pokud je program velmi složitý (mnoho funkcí, smyček, zpoždění) a musíme také neustále reagovat, např. na tlačítka/senzory.
Kdy byste neměli používat přerušení?
- Pokud je program relativně krátký nebo nemá zpoždění, abychom mohli průběžně kontrolovat všechny záznamy.
- Pokud získávání informací ze vstupů/senzorů není časově kritické.
Všechny poznámky o „šetření přerušení“ mají samozřejmě smysl, když píšeme rozsáhlý kód. Pokud je úkol, který chceme splnit, jednoduchý a použití přerušení nám může usnadnit práci, vyplatí se ho použít.
Nejdůležitějším bodem je, abyste se do všeho příliš nehrnuli s přestávkami, protože pak si můžete snadno způsobit spoustu problémů.
Shrnutí
Použití přerušení v Arduino není obtížné, je to jen několik řádků kódu navíc. Je však obtížné dělat to správně a vědomě. Doporučuji hrát si s přerušeními a experimentovat. Zvláště důležité je pochopit, jak se spouštění přerušení podle kopců (vzestupné/sestupné) liší od spouštění podle stavů. V dalších článcích se pokusím poskytnout více informací o přerušeních v praxi.
Objednejte si sadu prvků a začněte se učit v praxi! Kliknutím sem přejdete do obchodu >>