Avtor: mag. Vladimir Mitrović
E-pošta: vmitrovic12@gmail.com
2020_286_32
Z rotacijskim dajalnikom impulzov (nem. Drehimpulsgeber), oziroma z rotacijskim enkoderjem (ang. Rotation Encoder) sem se prvič srečal pred več kot petnajstimi leti. Ta zanimiv elektromehanski element, ki ima videz “običajnega” potenciometra, na svojih digitalnih izhodih daje informacijo o kotu zasuka in smeri vrtenja njegove osi, poleg tega pa še o dolžini pritiska na njegovo os v smeri navzdol. Vse to je zelo uporabno, če želite upravljati neko napravo le z enim samim gumbom. Kasneje sem jih srečeval v različnih napravah, kjer so delovali bolj ali manj natančno, morda zaradi mehanske obrabe ali slabo napisanega programa, tega pač nisem analiziral.
Primerek takšnega rotacijskega enkoderja je nameščen tudi na modulu KY-040 iz 37-in-1 kompleta in bo v tem članku glavni predmet pozornosti. Spoznali bomo, kako deluje in napisali ustrezni program, ki bo uspešno “dešifriral” impulze na njegovih izhodih.
KY-040
Rotacijski enkoder (KY-040 Rotation Encoder Module, slika 108)
Poleg rotacijskega enkoderja, sta na KY-040 modulu še dva pull-up upora R2 in R3; predviden je tudi prostor za tretji upor z oznako R1, vendar ta ni nameščen, vsaj ne na tistem modulu, ki sem ga imel na razpolago.
Ta vsebina je samo za naročnike
- Smer vrtenja je enaka smeri vrtenja urinih kazalcev, če je na padajočem robu CLK signala DT = “0” in na naraščajočem robu DT = “1”;
- Smer vrtenja je v nasprotni smeri vrtenja urinih kazalcev, če je na padajočem robu CLK signala DT = “1” in na naraščajočem robu DT = “0”.
Po vsaki spremembi CLK signala, ima os položaj v katerem lahko obstane. Ti položaji so na diagramih prikazani z navpičnimi črtkanimi črtami. Rotacijski enkoder, ki je vgrajen na KY-040 modulu, ima 30 takšnih položajev enakomerno porazdeljenih znotraj polnega kroga, ali če povemo z drugimi besedami, os lahko obstane na položajih, ki so po obodu krožnice razporejeni na vsakih 12 stopinj.
“SW” izhod je povezan na stikalo S, ki ga aktivira navpični pritisk na os. Če na KY-040 modulu ni nameščen upor R1, mora mikrokontroler poskrbeti za ustrezno pull-up napetost (lahko pa se na modulu naknadno namesti SMD upor z upornostjo okrog 10 kΩ).
Samo branje stanja SW izhoda ne predstavlja kakšno posebno programsko spretnost . Na drugi strani pa pravilno določanje smeri vrtenja osi in še posebno natančno šteje premikov med hitrim vrtenjem osi ni čisto enostavno. Sestavili bomo testno vezje po shemi s slike 109, s katerim se CLK signal nadzira prek dveh vhodov na mikrokontrolerju, PD2 in PD3 (Arduino priključka “2” in “3”). S tem pristopom bomo iz podprograma lažje spremljali spremembo stanja CLK signala in učinkovito reagirali na njegov naraščajoči in padajoči rob. Arduino UNO modul se napaja prek USB vodila računalnika, po istem vodilu pa pošilja tudi sporočila o stanju rotacijskega enkoderja na emulator terminala.
Program 37in1_prog_15a.bas
Program 37in1_prog_15a.bas je napisan za preverjanje delovanja modula KY-040 v vezju po shemi na sliki 109 z uporabo ukaza Debounce. Na začetku najprej preberemo datoteko s definicijami Arduino priključkov
$include "Arduino_pins.sub"
in definiramo priključke, ki so povezani z rotacijskim enkoderjem:
Config Arduino_pin#2 = Input Config Arduino_pin#3 = Input Config Arduino_pin#4 = Input Config Arduino_pin#5 = Input Arduino_port#5 = 1
Najbrž ste opazili, da smo samo vhodnemu priključku Arduino_pin#5 morali aktivirati pull-up upor, vsi ostali vhodi pa imajo svoje pull-up upore že nameščene na modulu KY-040. Če želimo spremembe logičnih stanj spremljati z Debounce ukazom, moramo upoštevati dejstvo, da ta ukaz izvaja preverjanje stanja dvakrat v razmaku 25 ms. Če os obračamo počasi, ne bomo imeli težav, če pa jo zavrtimo hitreje, Debounce ukazu ne bo več uspelo pravilno zaznavati vseh sprememb. Zato moramo delovanje ukaza Debounce nekoliko pospešiti tako, da skrajšamo čas preverjanja. Vendar moramo biti pri tem previdni:
- večji parameter sicer prinaša bolj zanesljivo (kvalitetnejše) filtriranje pojava odskakovanja kontaktov, vendar prihaja pri hitrem vrtenju osi do „preskakovanja” posameznih impulzov ali napačnega branja smeri vrtenja;
- manjši parameter omogoča natančno štetje impulzov, vendar le pod pogojem, da so stikala kvalitetna, kar pomeni, da ne povzročajo teh motečih pojavov med odpiranjem in zapiranjem kontaktov ali pa so ti pojavi res kratki.
Eksperimentalno sem za primeren čas zakasnitve med dvema zaporednima branjema izbral 2 ms, zato je v skladu s tem nastavljen tudi Debounce ukaz:
Config Debounce = 2
Program v glavni programski zanki preverja, če se je zgodila sprememba logičnega stanja na katerem izmed vhodov 2, 3 ali 5 in v primeru spremembe skoči na izvajanje ustreznega podprograma:
Do Debounce , Arduino_pin#2 , 0 , Clk_dn , Sub Debounce , Arduino_pin#3 , 1 , Clk_up , Sub Debounce , Arduino_pin#5 , 0 , Sw , Sub
Če je na primer CLK signal pravkar prešel v stanje “0”, se bo izvedel podprogram Clk_dn:, v katerem je potrebno le preveriti trenutno stanje DT priključka 4 in povečati enega od števcev:
Clk_dn: If Arduino_pin#4 = 0 Then Incr Ccw_cnt Else Incr Cw_cnt EndIf Return
Kot pove že njuno ime, spremenljivka Cw_cnt šteje premike v smeri vrtenja urinih kazalcev, spremenljivka Ccw_cnt pa v obratni smeri. Izvajanje podprograma Clk_up kličemo v trenutku, ko CLK signal preide v stanje “1” in je zelo podoben prejšnjemu; prav tako preverja trenutno stanje DT priključka in poveča vrednost ene od navedenih spremenljivk v skladu z diagrami na sliki 108.
Podprogram Sw, s katerim štejemo pritiske na os, je programsko še najmanj zahteven, zato smo mu dali še eno dodano vrednost – poleg štetja bomo merili tudi dolžino, torej trajanje pritiska na os:
Sw: Incr Sw_cnt sw_t = 2 While Arduino_pin#5 = 0 Incr Sw_t If Sw_t> 2000 Then Exit While EndIf Waitms 1 Wend Return
Trajanje pritiska v milisekundah je zapisano v spremenljivki Sw_t. Merjenje je omejeno na dve sekundi; ko se ta čas izteče, se bo izvajanje programa nadaljevalo, tudi če je os še vedno pritisnjena, vrednost spremenljivke Sw_t pa bo 2001.
Za ukazom Debounce program v glavni programski zanki preverja, če se je spremenila vrednost katere od spremenljivk cw_cnt, ccw_cnt ali sw_cnt in v tem primeru pošlje ustrezno sporočilo na emulator terminala. Če se je na primer spremenila vrednost spremenljivke Cw_cnt, bi bilo poslano sporočilo takšno:
Do ... If Cw_cnt<>Cw_cnt_old Then Print "CW_cnt = " ; Cw_cnt cw_cnt_old = cw_cnt EndIf ... Loop
Kako so to in ostala sporočila videti na emulatorju terminala, prikazuje slika 110.
V prikazu na sliki 110 ni bilo napačnih odčitkov, med daljšim raziskovanjem pa se je izkazalo, da so napake res redke: k tem sta predvsem prispevala ustrezna konfiguracija Debounce ukaza in kvaliteta samega enkoderja, ki je vgrajen na modulu KY-040. Vendar obstaja še en faktor, ki neposredno vpliva na zanesljivost branja in to je skupna dolžina trajanja Do-Loop zanke. Če je trajanje vseh ukazov v zanki daljše od nekaj milisekund, se lahko pojavi enak problem, kot pri napačno izbranem Debounce parametru: morda kakšen premik ne bo zaznan ali pa bo napačno določena smer vrtenja.
Če želimo imeti natančno odčitavanje tudi pri bolj zahtevnih programih, pa branje stanj stikal ne bomo smeli izvajati z Debounce ukazi, ampak bomo morali uporabiti rešitev s prekinitvami. V ta namen je najboljša izbira pri Arduinu prav uporaba priključkov “2” in “3”, saj sta povezana s priključkoma mikrokontrolerja PD2 in PD3, ki jima lahko določimo funkcijo zaznavanja zunanjih prekinitev INT0 in INT1. Kako lahko to naredimo, si bomo ogledali v naslednjem programskem primeru.
Program 37in1_prog_15b.bas
Program 37in1_prog_15b.bas je napisan za preverjanje delovanja modula KY-040 v vezju, ki ga vidimo na sliki 109 z uporabo zunanjih prekinitev INT0 in INT1. “Zahteven” program z Do-Loop zanko, katere izvajanje traja zelo dolgo, smo simulirali z ukazom Wait 1:
Do Wait 1
Da bi proces izvajanja Do-Loop zanke postal viden, smo na priključek “13” povezali LEDico (ta ni prikazana na shemi, ki je na sliki 108; uporabimo lahko RGB diodo po shemi s slike 107 iz prejšnjega članka ali na podoben način povezati kakšno drugo LEDico). V glavi programski zanki bomo to LEDico vsako sekundo izmenično prižigali in ugašali:
Arduino_port#13 = Not Arduino_port#13 ... Loop
Ukazi, ki so označeni kot “…” so ukazi, s katerimi emulatorju terminala pošiljamo sporočila ob spremembi vrednost katere izmed spremenljivk, cw_cnt, ccw_cnt ali sw_cnt ins o popolnoma enaki kot v programu prejšnjega primera.
V programu pa zdaj ni Debounce ukazov, s katerimi bi spremljali spremembe CLK signala na priključkih “2” in “3”, ampak smo v ta namen nastavili funkciji prekinitev INT0 in INT1, ter jima določili pripadajoči prekinitveni rutini Int0_sub in Int1_sub:
Config Int0 = Falling On Int0 Int0_sub Config Int1 = Rising On Int1 Int1_sub
Preden vstopimo v glavno programsko zanko, bomo izbrisali morebitne predčasne zahteve za prekinitev (dokler se obdeluje obstoječa prekinitvena rutina) obeh
Set Eifr.intf0 Set Eifr.intf1
in potem prekinitve tudi omogočili:
Enable Int0 Enable Int1 Enable Interrupts
Prekinitvene rutine morajo nadomestiti preverjanje stabilnosti vhodnega signala, kar je del funkcionalnosti Debounce ukaza. Zato bomo v prekinitveni rutini Int0_sub po preteku 2 ms preverili, če je stanje vhodnega priključka še vedno “0”, šele nato bomo poklicali podprogram Clk_dn:
Int0_sub: Waitms 2 If Arduino_pin#2 = 0 Then Gosub Clk_dn EndIf
Clk_dn je popolnoma enak kot v prejšnjem programu: v odvisnosti od stanja na DT priključku se poveča števec pomikov v eni ali drugi smeri. Če preverjanje stanja DT priključka pokaže, da je bila prekinitev izzvana zaradi pojavov na stikalu med preklapljanjem, se podprogram Clk_dn ne bo izvedel. V primeru, da se to zgodi, se bo postavil interrupt flag v obeh prekinitvenih rutinah, zato je treba te zastavice pred povratkom v glavno programsko zanko izbrisati:
Set Eifr.intf0 Set Eifr.intf1 Return
Prekinitvena rutina Int1_sub, ki se proži ob naraščajočem robu CLK signala, je zelo podobna opisani in je ne bomo znova analizirali. Pozornost bomo raje posvetili obdelavi prekinitve, ki jo povzroči pritisk na os enkoderja. Logično stanje na SW vhodu modula KY-040 spremlja Arduino priključek “5”, oziroma priključek PD5 mikrokontrolerja. Za razliko od priključkov PD2 (=INT0) in PD3 (=INT1) imajo vsi ostali priključki mikrokontrolerja drugačen mehanizem obdelave prekinitev: možnosti samega mikrokontrolerja so bolj skromne, poleg tega pa so tudi same prekinitve v Bascomu nekoliko slabše „pokrite“. Zaradi naštetih razlogov se bomo morali zato z obdelavo te prekinitve malo bolj potruditi.
Priključek PD5 je mogoče določiti kot vir prekinitve z oznako PCINT21, kateri bomo določili tudi prekinitveno rutino Pcint2_sub in prekinitve nato omogočili na naslednji način:
On Pcint2 Pcint2_sub Set Pcmsk2.pcint21 Set Pcifr.pcif2 Enable Pcint2
V prekinitveni rutini bomo najprej preverili, če je šlo za padajoči rob signala (os je pritisnjena)
Pcint2_sub: If Arduino_pin#5 = 0 Then
potem pa po 2 ms to ponovno preverili. Le v primeru, ko v obeh preverjanjih dobimo pozitivni rezultat, se izvede klic podprograma Sw:
Waitms 2 If Arduino_pin#5 = 0 Then Gosub Sw EndIf EndIf Return
Ker je podprogram Sw sedaj del prekinitvene rutine, smo tu izločili preverjanje dolžine trajanja pritiska na os:
Sw: Incr Sw_cnt Sw_t = 2 Return
Vsako nepotrebno zadrževanje v prekinitveni rutini je nezaželeno, saj neposredno vpliva na odvijanje glavnega programa (v našem primeru bi z daljšim pritiskom na os enkodera upočasnili utripanje LEDice). Pravzaprav smo podprogram Sw obdržali le zaradi podobnosti s predhodnim programom, saj bi ukaz Incrsw_cnt lahko čisto preprosto ugnezdili v samo prekinitveno rutino.
Sporočila, ki jih program pošilja na emulator terminala, so enaka tistim, ki jih vidimo na sliki 110.
Opombe: V samem članku so programi predstavljeni bolj površno, zato je potrebno za popolno razumevanje dobiti na vpogled in preučiti celotne programe. Datoteko Arduino_pins.sub z definicijami priključkov Arduino UNO in oba programa, 37in1_prog_15a.bas in 37in1_prog_15b.bas , lahko brezplačno dobite v uredništvu revije Svet elektronike.
Barduino-25