V seriálu článků o desce TinyLab si postupně představujeme jednotlivé komponenty, které na desce nalezneme. V dnešním článku si představíme rotační enkodér.
Rotační enkodér
Rotační enkodér najdeme na TinyLab uprostřed na spodní straně desky. Na první pohled vypadá trochu jako potenciometr, ale jeho pricip je jiný. Na rozdíl od potenciometru, který má pevně daný rozsah, ve kterém ho můžeme otáčet, má enkodér rozsah neomezený. Jak je to možné? Podívejte se na obrázek níže.
Na osu enkodéru je připevněno „ozubené kolo“. Toto kolo je neustále připojeno na pin enkodéru, který je označený 1. Obvod mezi pinem 1 a 2 a mezi 1 a 3 se uzavírá a otevírá podle toho, jak osou enkodéru otáčíme. Všimněme se navíc, že pacičky pinů 2 a 3 mají různou délku a tedy k jejich doteku s ozubeným kolem dochází v různý čas. Pokud enkodérem otáčíme po směru hodinových ručiček, dotkne se pacička 2 kola dříve, než pacička pinu 3. Pokud budeme otáčet proti směru hodin, bude pořadí opačné. Díky pulzům na pinech 2 a 3, vzniklých v důsledku spojování a rozpojování obvodu, jsme schopni určit směr a rychlost otáčení. Pokud bychom si výstup enkodéru při otáčení jeho osy vynesli na graf, dostali bychom přibližně následující průběh.
Řízení enkodéru
Úplně nejjednodušším způsobem, jak z enkodéru získat nějaká data, je přistupovat k jeho pinům jako k tlačítku. Na desce TinyLab je enkodér připojen na piny 6 a 7. (Po řadě odpovídají pinům 2 a 3 na obrázku výše). Kromě toho enkodér na desce obsahuje ještě tlačítko, které stiskneme stlačením osy enkodéru dolů. Toto tlačítko je připojeno na pin A5. Dnes ho používat nebudeme, ale pokud vás zajímá, jak ho použít, podívejte se na článek o tlačítkách. Začneme tedy tím, že budeme zkoumat stav pinů 6 a 7 a budeme si ho vypisovat na sériovou linku. Nesmíme zapomenout na přepnutí přepínačů u pinů 6 a 7 do správné polohy.
byte enc2 = 6; byte enc3 = 7; void setup() { pinMode(enc2, INPUT_PULLUP); pinMode(enc3, INPUT_PULLUP); Serial.begin(9600); } void loop() { byte s2 = digitalRead(enc2); byte s3 = digitalRead(enc3); Serial.print(s2); Serial.print(' '); Serial.println(s3); delay(50); }
Když si nyní v Arduino IDE spustíme sériový plotter, získáme podobný graf:
Nejdříve jsem enkodérem otáčel po směru hodinových ručiček, poté proti směru. Je vidět, že při otáčení po směru hodinových ručiček předbíhá hodnota na jednom pinu, poté na druhém. Co s tím? Zkusíme detekovat změnu.
Počítání otočení enkodéru
Při zkoumání otočení nám stačí sledovat, když se hodnota jednoho pinu změní z 0 na 1 (sledujeme tedy tzv. náběžnou hranu). Když tuto náběžnou hranu detekujeme, zjistíme hodnotu na druhém pinu a podle toho zjistíme, jakým směrem se zrovna enkodér otáčí.
byte enc2 = 6; byte enc3 = 7; byte lastState3; long counter = 0; void setup() { pinMode(enc2, INPUT_PULLUP); pinMode(enc3, INPUT_PULLUP); lastState3 = digitalRead(enc3); Serial.begin(9600); } void loop() { byte state3 = digitalRead(enc3); if(state3 != lastState3) { byte state2 = digitalRead(enc2); lastState3 = state3; if(state3 == HIGH) { if(state2 == LOW) { counter--; } else { counter++; } Serial.println(counter); } } }
Kód výše nám po sériové lince vypíše aktuální hodnotu čítače, který používáme k určování pootočení enkodéru od počátečního stavu.
Kód výše má ale jednu chybu. Přijdete na to jakou?
Co když bude zrovna Arduino zaměstnané nějakým složitým výpočtem a nevšimne si, že došlo ke změně hodnoty na pinu?
Jak se s tím vypořádat? Pomůže přerušení.
Přerušení
Přerušení je způsob, kterým můžeme procesor, na kterém běží náš program, na chvilku vyrušit (jak název napovídá) a donutit ho dělat něco jiného. Procesor si tedy počítá hlavní program, když v tom najednou přijde přerušení, on si na chvíli odskočí, vykoná určenou funkci, a poté zase pokračuje ve vykonávání programu.
Zdroje přerušení můžou být různé. Mohou být vyvolaná například dokončením měření analogové hodnoty, nebo také změnou na pinu. Dopodrobna se přerušením nebudeme zabývat, ale ukážeme si, jak ho využít při zjišťování změny stavu tlačítka. Důležité je si uvědomit, že ne všechny piny mohou přerušení při změně hodnoty vyvolat. Když se podíváme do dokumentace, zjistíme, že Arduino Leonardo, na kterém je TinyLab založen, umožňuje vyvolat přerušení změnou na pinech 0, 1, 2, 3, 7. Nás tedy pro naše účely bude zajímat pin 7. Program se stejnou funkčností, jako výše, ale s využitím přerušení, může vypadat následovně.
byte enc2 = 6; byte enc3 = 7; volatile long counter = 0; volatile bool changed = false; void setup() { pinMode(enc2, INPUT_PULLUP); pinMode(enc3, INPUT_PULLUP); Serial.begin(9600); attachInterrupt(digitalPinToInterrupt(enc3), encoderChange, RISING); } void loop() { if(changed) { Serial.println(counter); changed = false; } } void encoderChange() { detachInterrupt(digitalPinToInterrupt(enc3)); byte state2 = digitalRead(enc2); if(state2 == LOW) { counter--; } else { counter++; } changed = true; attachInterrupt(digitalPinToInterrupt(enc3), encoderChange, RISING); }
Rozeberme si představený kód. Proměnné counter a changed slouží k počítání pozice enkodéru a k informování hlavního programu, že došlo ke změne. Všimněte si klíčového slova volatile před deklarací proměnných. To musíme přidat u všech proměnných, které chceme ve funkci obsluhující přerušení měnit.
Ve funkci setup nejdříve nastavíme piny 6 a 7 jako vstupní, inicializujeme sériovou linku a následně nastavíme obsluhu přerušení. Funkce attachInterrupt jako první parametr bere číslo přerušení. Pokud chceme zjistit číslo přerušení, které náleží zvolenému pinu, použijeme k tomu funkci digitalPinToInterrupt. Druhým parametrem je funkce, která se stará o obsluhu přerušení a třetím je informace o tom, na jakou změnu čekáme. U nás je to náběžná hrana (rising edge). Existují i další možnosti – viz dokumentace.
Ve funkci loop sledujeme proměnnou change a pokud je nastavena na true, vypíšeme hodnotu počitadla na sériovou linku a proměnnou změníme na false, aby při dalším volání funkce loop nedošlo k vypsání.
Poslední funkcí je encoderChange, která slouží k obsluze přerušení. V ní nejdříve zrušíme přerušení deaktivujeme (aby se obsluha přerušení nepřerušila v půlce dalším přerušením), poté zjistíme stav druhého pinu a podle toho aktualizujeme počitadlo. Nakonec obsluhu přerušení opět aktivujeme.
K enkodéru by se toho dalo napsat ještě více (například něco o ošetření zákmitů), stejně tak o přerušení. To si ale nechejme zase na jindy.