Lekce 6 - Arduino - Pokročilá práce s tlačítky
V minulé lekci, Arduino - Projekty s LED diodami a potenciometrem, jsem si ukázali další příklady práce s LED diodami, které již umíme ovládat i pomocí potenciometru.
Dnes se v Arduino HW tutoriálu budeme dále zabývat prací s tlačítkem.
Tlačítko je asi nejjednodušší a zároveň
nejproblematičtější součástka, se kterou může Arduino
pracovat. Kromě tlačítka totiž musíme zapojit na daný pin přes rezistor i
nějakou výchozí hodnotu, aby nedocházelo k rušení ve
chvíli, kdy není stisknuté a na pinu by nebyla přivedena ani 0
ani 1
.
V předchozích příkladech jste si možná všimli, že tlačítko nereaguje vždy stoprocentně. Není totiž úplně snadné zařídit, aby jedním stisknutím tlačítka došlo k zapnutí a opětovným stisknutím toho samého tlačítka k vypnutí obvodu. Vše si dnes vysvětlíme. V dalších příkladech také budeme chtít, aby držení tlačítka zvyšovalo hodnotu rychleji, než opakované mačkání tlačítka.
Konstrukce tlačítka
Tlačítko, neboli mikrospínač, je elektromechanická součástka. Slouží k vytvoření krátkodobých ovládacích impulzů tak, že sepne nebo přeruší elektrický obvod:
Nejčastěji se tlačítka ve slaboproudé elektronice skládají z plastového hmatníku, kovového plíšku a kontaktů. Impulz pak vzniká stlačováním hmatníku, čímž se prohýbá plíšek, který vodivě spojuje kontakty. Po uvolnění tlačítka se plíšek narovná do původní polohy a funguje tedy i jako vratná pružina:
Zapojení tlačítka
V programování se při práci s tlačítky používá jednoduché čtení stavů na digitálním nebo analogovém pinu. V prvním případě rozlišujeme pouhé dva stavy, jsou to:
- logická jednička (
1
,HIGH
,true
) a - logická nula (
0
,LOW
,false
).
Jako logickou 1
bere digitální vstup hodnotu
napětí vyšší než 3,0 V. Pro desky s napětím 3,3 V je to hodnota 2 V.
Logickou 0
vrací napětí menší než 1,5 V. Pro desky s
napětím 1 V je to 3,3 V. Oblast mezi 1,5 V - 3,0 V je tzv. "zakázaná oblast"
a procesor na ni nijak nereaguje.
Nestačí ovšem přivést na pin hodnotu jen při stisknutí tlačítka,
potřebujeme, aby tam neustále byla jasně definovaná hodnota 1
nebo 0
, jinak by došlo k rušení. Výchozí stav
se tedy řeší zapojením rezistoru a permanentním
přivedením buď stavu 0
, nebo 1
na pin, na který je
tlačítko připojeno. Těmto dvěma režimům se říká INPUT
(pull down) a INPUT_PULLUP
(pull up) a každý
má své výhody i nevýhody. První z nich vrací defaultně 0
,
zatímco druhý vrací 1
.
Režim INPUT
(pull down)
Tento způsob vyžaduje externě připojený "pull-down" rezistor
mezi vybraný pin a GND, s hodnotou řádově v kΩ. Nepsaným
pravidlem je použití hodnoty 10 kΩ. Takto je pin
permanentně připojen k zemi přes velký odpor a je na něm tak logická
0
, tj. hodnota LOW
. Reagovat pak bude na logickou
1
.
Tlačítko se poté připojí mezi napájecí 5V napětí a vybraný pin, na
který pak stisknutím přivádí hodnotu HIGH
. Osobně mohu tento
způsob doporučit, jelikož částečně potlačuje vlivy elektromagnetických
rušení, a zvyšuje tak stabilitu projektu.
Pozor! Pokud bychom v této verzi nastavení vstupu zvolili hodnotu rezistoru příliš nízkou, nebo zkratovali pin s GND, může dojít ke zničení procesoru Arduina!
Režim INPUT_PULLUP
Druhá varianta je jednodušší, tlačítko se připojí mezi pin a
GND. Uvnitř procesoru Arduina se připojí k pinu interní "pull-up"
rezistor, který na pin přivádí přes velký odpor
logickou 1
. Tento způsob vyhodnocuje logickou
0
, čili LOW
.
Je vhodný pro ladění na nepájivém kontaktním poli (breadboard), kdy u náročnějších projektů odpadá nutnost externích rezistorů a tedy šetří místo na desce. Avšak v hotovém projektu neodstraňuje vlivy možných rušení.
Rozdíl mezi oběma způsoby je schematicky znázorněn na obrázku:
Příklad programu
Jdeme na praktickou ukázku programu. Využijeme vestavěnou LED diodu na pinu 13, proto postačí pouze nepájivé pole, pár tlačítek a propojky:
void setup() { pinMode(3, INPUT_PULLUP); pinMode(4, INPUT); pinMode(13, OUTPUT); } void loop() { if (!digitalRead(3)) digitalWrite(13, 1); // můžeme také použít výraz (digitalRead(3) == LOW) if (digitalRead(4)) digitalWrite(13, 0); }
Ve smyčce setup()
nastavíme piny 3 a 4 jako vstupy. Jeden je
typu INPUT
a druhý typu INPUT_PULLUP
. A samozřejmě
nastavíme i výstup na pinu 13. Stisknutím tlačítka na pinu 3 se LED
rozsvítí, stisknutím tlačítka na pinu 4 zhasne. Vcelku jednoduché a
spolehlivé.
Ovládání výstupu jedním tlačítkem
Mít jedno tlačítko na vypnutí a druhé na zapnutí může být někdy na obtíž, protože tlačítko na zapnutí a vypnutí často stačí jen jedno společné. Stisknutím zapneme, dalším stisknutím vypneme atd.
Spolehlivé ovládání jediným tlačítkem se může jevit jako snadný úkol, ale nastává tu řada problémů. Stisk tlačítka totiž nikdy není úplně bez závad.
Problém se zákmity
Během jediného stisku může být impulz nestálý, jelikož může mít tzv. "zákmity". To vede ke špatnému vyhodnocení signálu. Program si bude myslet, že impulzů bylo během doby jediného stisknutí několik, a podle toho také provede případné operace (inkrementace, dekrementace, změna stavu, vypnutí atd...) spojené s některou proměnnou.
Demonstrativní ukázkou je program níže. Zde je pro jednoduchost použito tlačítko pinu 3 z předchozí ukázky:
bool svitit = false; void setup() { pinMode(3, INPUT_PULLUP); pinMode(13, OUTPUT); } void loop() { if (!digitalRead(3)) { svitit = !svitit; } digitalWrite(13, svitit); }
Program obsahuje proměnnou typu bool
. Každé stisknutí
tlačítka na pinu 3 změní její stav z true
na
false
a naopak. Podobný kód obsluhy jsme měli i v předchozích
příkladech. Schválně si však zkusme tlačítko několikrát stisknout.
Možná teď vidíte, že se LED dioda rozsvěcí a zhasíná ne zcela
pravidelně. Každý cyklus programu totiž čte stav tlačítka, a při
kmitočtu 16 MHz je to opravdu hodně čtení za jedinou sekundu.
Řešení pomocí pauzy
Problém lze sice ošetřit pauzou prostřednictvím funkce
delay()
uvnitř těla podmínky, avšak nejedná se o šťastné
řešení:
if (!digitalRead(3)) { svitit = !svitit; delay(200); // doplněná pauza }
Co je na tomto řešení špatného? Zkusme tlačítko držet stisknuté delší dobu. Uvidíme, že nám LED dioda bliká. To může v některých případech způsobit problém. Pokud obsluha nechtěně přidrží tlačítko, může se pak výsledné zařízení chovat jinak, než je v daný moment žádoucí.
Řešení s cyklem
Možné řešení je pomocí cyklu while
. Není zvlášť
složité, a jeho výsledek jistě uspokojí nespočet projektů.
V programu je použita funkce millis()
, která nezávisle na
běhu programu počítá čas od spuštění Arduina v milisekundách. Jedná se
o velice užitečnou funkci, která na rozdíl od funkce delay()
nezasekne běh programu. Daní je ale o trošku složitější práce s ní:
unsigned long casomira; int dobaStisku, limit = 25; bool svitit; void setup() { pinMode(3, INPUT_PULLUP); pinMode(13, OUTPUT); } void loop() { casomira = millis(); digitalWrite(13, svitit); while (!digitalRead(3)) { dobaStisku = millis() - casomira; } if (dobaStisku >= limit) { svitit = !svitit; dobaStisku = 0; } }
Program obsahuje proměnnou typu unsigned long casomira
, která
slouží k měření času. Jakmile dojde ke stisknutí tlačítka, spustí se
cyklus, který se provádí tak dlouho, dokud je tlačítko stisknuté. Uvnitř
cyklu se měří doba stisku, kdy se od proměnné casomira
odečítá hodnota millis()
. Tento rozdíl se ukládá do
proměnné dobaStisku
.
Po uvolnění tlačítka měření ustává a následně se testuje, zda je
doba stisku větší než požadovaný limit (v ukázce 25
ms).
Pokud je tato podmínka splněna, změní se stav proměnné
svitit
, což vede k rozsvícení/zhasnutí LED diody. Nakonec
vynulujeme proměnnou pro dobu stisku. Právě limit 25
ms v
drtivé většině případů spolehlivě eliminuje případné zákmity
tlačítka i rušivé vlivy.
Pokud by nedošlo k vynulování doby stisku, byl by program stejně nestabilní, jako v předchozích ukázkách!
Takto máme tedy jistotu, že každý stisk tlačítka bude zaznamenán jako
jeden. Tento způsob nás samozřejmě neomezuje pouze na změnu stavů
true
/false
, ale lze ho použit i jinde, například
při výběru v menu možností, zvyšování/snižování požadované teploty,
času, úrovně intenzity světla atd.
Dlouhý stisk tlačítka
Pojďme na poslední příklad. V něm budeme chtít změnit postupnými
stisky například hodnotu od 0
do 255
pro PWM
regulaci. V uvedených příkladech bychom museli desítky vteřin zběsile
mačkat tlačítko, než se jas zvýší na maximum. Nešlo by to nějak
pořešit? Samozřejmě, že šlo!
Po lehké úpravě můžeme elegantně tlačítkem měnit jas externí LED diody buď mačkáním, nebo dlouhým stiskem. Zapojímee obvod podle přiloženého schématu:
Obslužný kód bude nyní následující, hned si jej vysvětlíme:
unsigned long casomira; int dobaStisku, limit = 25; byte pwm = 0; void setup() { pinMode(3, INPUT_PULLUP); pinMode(5, OUTPUT); } void loop() { casomira = millis(); analogWrite(5, pwm); while(!digitalRead(3)) { dobaStisku = millis() - casomira; if (dobaStisku > 150) { break; } } if (dobaStisku >= limit && pwm < 255) { pwm += 5; dobaStisku = 0; } }
Podmínka uvnitř cyklu říká programu, ať přeruší cyklus pokud
dobaStisku
překročí hodnotu 150
ms. Tato hodnota
byla zvolena záměrně takto nízká. Tím je samozřejmě splněna i
základní limitní podmínka 25
ms, tedy dojde k navýšení
hodnoty PWM o 5
a vynulování doby stisku. V základní podmínce
je ještě zarážka pro jas pwm < 255
. Bez ní by po
překročení hodnoty LED dioda nepříjemně blikla.
Určitě bychom při programování více než jednoho tlačítka použili pro tlačítko třídu, nebo si rovnou vytvořili vlastní Knihovnu pro přehlednější práci.
Do projektu si můžete vyzkoušet přidat i část pro snižování jasu nebo jinak lehce experimentovat.
V další lekci, Arduino - Membránová klávesnice a její programování, si ukážeme a popíšeme membránovou klávesnici a vytvoříme nové projekty, ve kterých se ji naučíme používat.
Měl jsi s čímkoli problém? Stáhni si vzorovou aplikaci níže a porovnej ji se svým projektem, chybu tak snadno najdeš.
Stáhnout
Stažením následujícího souboru souhlasíš s licenčními podmínkami
Staženo 295x (1.53 kB)
Aplikace je včetně zdrojových kódů