2.9 Obsluha digitálneho vstupu cez prerušenie
Je veľmi nepraktické, aby hlavný program popri svojej činnosti každú chvíľu zisťoval, či nedošlo k stlačeniu tlačidla alebo inej vstupnej interakcii - a to nutne často, aby ju vôbec zachytil. Takéto neustále dopytovanie sa označuje ako polling. Situáciu vieme riešiť elegantnejšie - cez udalosť, ktorá vyvolá prerušenie vykonávania hlavného programu.
Mali by sme sa snažiť minimalizovať trvanie spracovania udalosti. V ideálnom prípade by malo ísť len o jednoduché nastavenie nejakej globálnej stavovej premennej. Dôležité je uvedomiť si, že počas vykonávania obsluhy udalosti je prerušené vykonávanie hlavného programu a že ak sa vyskytne druhá udalosť počas obsluhy prvej, zaradí sa do fronty.
V dokumentácii MicroPython nájdete pokročilé odporúčania pre spracovanie prerušenia.
IRQ v MicroPython
V prípade ESP32 je možné prerušenie zo vstupného portu definovať funkciou irq() už dobre známeho objektu Pin pre GPIO (v režime vstupu, teda Pin.IN). Názov IRQ znamená interrupt request a vyjadruje teda požiadavku na prerušenie programu z dôvodu udalosti. Funkcii irq() musíme uviesť parameter handler - odkaz na funkciu obsluhy udalosti, ktorá sa zavolá pri vzniku udalosti:
from machine import Pin
tlacidlo = Pin({číslo portu}, Pin.IN)
tlacidlo.irq(handler = Tlacidlo)
Tejto obslužnej funkcii bude odovzdaný vstupný parameter typu Pin - inštancia toho objektu, ktorý vyvolal udalosť:
def Tlacidlo(pin):
print(f"Tlačidlo na {pin} zaznamenalo udalosť!")
Udalosť sa vyvolá aj pri zmene stavu z 0 na 1 a aj pri zmene z 1 na 0. Toto môžeme zmeniť bitovým parametrom trigger - špecifikuje, kedy má vzniknúť udalosť, na výber máme konštanty:
- Pin.IRQ_FALLING - vyjadruje zmenu stavu z 1 na 0, teda typicky stlačenie tlačidla pri použití pull-up;
- Pin.IRQ_RISING - vyjadruje zmenu stavu z 0 na 1, teda typicky stlačenie tlačidla pri použití pull-down.
Ukážka pre zachytenie stlačenia tlačidla s pull-up:
tlacidlo.irq(handler = Tlačidlo, trigger = Pin.IRQ_FALLING)
Ďalšie využiteľné pokročilé parametre nájdete v dokumentácii k funkcii irq()
Deaktivácia IRQ
Po aktivácii prerušenia bude jeho obslužná funkcia zachytávať udalosti aj po skončení programu. Pre zrušenie spracovania prerušenia je potrebné zavolať funkciu irq() s hodnotou None, teda:
tlacidlo.irq(None)
Zákmity tlačidla
Reálny fyzický svet nie je vždy totožný s jeho virtuálnym modelom v počítači. Hoci sa tlačidlo zdá byť veľmi spoľahlivým a jednoduchým prvkom, ktorý poskytuje jednoznačný vstup, nie je to tak…
V reálnom nasadení sa tlačidlo nespráva ideálne - udalosť tlačidla nie je náhly prechod zo stavu 0 do 1, či naopak. V skutočnosti dochádza pri zmene stavu k dočasnému náhodnému striedaniu hodnôt 0 a 1, kým sa hodnota neustáli. Takéto striedanie nazývame zákmity (bouncing). Ide o zásadný problém, ktorý je nutné riešiť (debounce), inak je tlačidlo z praktického hľadiska len obtiažne použiteľné. Dobré je hardvérové riešenie, ktorého podstatou je zadržanie krátkych zmien pomocou kondenzátora.
Tlačidlá na vývojových doskách obvykle majú zákmity riešené hardvérovo. Ak nie je k dispozícii hardvérové riešenie, pomôcť môže jednoduchý softvérový trik (ten je možné využiť aj ako doplnok hardvérového riešenia) - po stlačení tlačidla počkáme približne 10 ms a zistíme, či je tlačidlo ešte stále stlačené:
from time import sleep_ms
def Tlacidlo(pin):
sleep_ms(10) # počkáme chvíľku ...
if pin(): return # ... a uistíme sa, že je tlačidlo ešte stlačené
print(f"Tlačidlo na {pin} bolo stlačené!")
Vyššie uvedené riešenie je síce veľmi jednoduché, ale nie veľmi profesionálne - predovšetkým porušuje zásadu čo najrýchlejšieho spracovania udalosti. Hoci je 10 ms z hľadiska človeka veľmi krátky čas, z hľadiska MCU sa jedná o pomerne dlhé zdržanie, počas ktorého je nielen prerušený hlavný program, ale zdržané sú aj prípadné ďalšie prerušenia (napríklad pre bezdrôtovú komunikáciu).

Lepšie riešenie zákmitov (debouncing)
Namiesto čakania by sme mali používať časovú značku pre zapamätanie posledného stlačenia. Nové stlačenie tlačidla budeme pokladať za platné len v prípade, že od predošlého stlačenia uplynul nejaký minimálny čas (napríklad 100 ms):
from time import ticks_ms, ticks_diff
tlacidlo_cas = 0 # globálna premenná pre uloženie času posledného stlačenia
def Tlacidlo(pin):
global tlacidlo_cas
if ticks_diff(ticks_ms(), tlacidlo_cas) < 100: return # ak neubehlo aspoň 100 ms, neakceptujeme
print(f"Tlačidlo na {pin} bolo stlačené!")
tlacidlo_cas = ticks_ms()


