Kicsit lassan haladok az önképzéssel, nehezen gyűjtöm össze a megfelelő mennyiségű szorgalmat, hogy a hétköznapi hajtás után egy-egy hétvégén pihenés helyett elővegyem a hardvereket a fiókból. Ezen a hétvégén kivételesen sikerült. Hogy pontos legyek, kezembe került egy inkrementális enkóder, ami (akinek nem ismerős a fogalom) arra jó, hogy forgó mozgás irányát, mennyiségét és esetleg sebességét meghatározzuk.
Az abszolút enkóderekkel ellentétben az inkrementális enkóderek nem képesek megadni a forgás pillanatnyi helyét, segítségükkel csak azt lehet meghatározni, hogy történt-e forgás valamelyik irányba. Viszont jelentősen olcsóbbak, és kevesebb lábat foglalnak el a mikrokontrolleren. Ez sem utolsó szempont, tekintve hogy pár óra kínlódás után rájöttem, hogy a kontrolleremen két láb halott..
Általában véve a forgási enkóderek működését a Wikipedia vonatkozó cikke jobban elmagyarázza, mint én tudnám, így erre nem térek ki részletesen. A lényeg csupán annyi, hogy egy egységnyi forgás alatt két kapcsoló zár az enkóderben egymás után, a forgás irányától függően. Ennek tipikus bekötése active-low jelleggel rendkívűl egyszerű, az enkóder C csatlakozója ráköthető a földre, az A és B lábak számára pedig kiválasztunk két portot a kontrolleren, az én esetemben ez RB6 és RB7 lett. Ezen kívül semmilyen más alkatrészre nincs szükség, hiszen a Pic16F690-es processzor integráltan tartalmaz felhúzó áramkört, ami képes magas szinten tartani az adott portot, és leföldelés esetén biztonságos szinten tartja az áramot.
A feladat tehát a következő: detektálni kell az enkóder kapcsolóinak a zárását, figyelembe véve a sorrendjüket, és ennek megfelelően kell egy változó értékét növelni, vagy csökkenteni. Ha az A kapcsoló zár előbb, és később a B, akkor növelni, ha fordítva akkor csökkenteni kell eggyel a változót. Az általam használt EC12E típusú enkóder 24 diszkrét egységre bont egy kört, azaz 15 fokonként képes érzékelni a forgást, tehát a változó értékét 15-el megszorozva megkapjuk a pontos forgást. Az enkóder mechanikus jellege miatt a használat közben számítani kell pergés előfordulására, aminek a kezeléséről gondoskodni kell.
A mikroprogram gyors időközönként leolvassa a kapcsolók állását, és ez alapján kell eldöntenie, hogy merre forgatjuk az enkóder karját. Mivel figyelembe kell venni a kapcsolók korábbi állapotát is, triviálisan adja magát egy állapotgép-alapú megoldás, ami a pergést úgy küszöböli ki, hogy megengedi az állapotok közötti oda-vissza ugrálást a pergést követve, a pergés stabilizálódásával pedig az állapot is a megfelelő értéken rögzül.
Az állapotgép bemenete a két kapcsoló állása és minden leolvasáskor végrehajt egy lépést:

Az állapotgép megfelelő élei biztosítják, hogy amíg valamelyik kapcsoló pereg, addig a megfelelő két állapot között lépkedjen. Az ábrán pirossal és kékkel jelöltem azokat a lépéseket, mikor is növelem vagy csökkentem a forgást mérő változó értékét.
A programot a gyakorlatban is kipróbáltam, assembly-ben írtam a Pic16F690-hez a kódot, és a pickit2 próbapanelen lévő 4 led segítségével jelzem a változó aktuális értékét:
#include <p16F690.inc>
__config (_INTRC_OSC_NOCLKOUT & _WDT_OFF & _PWRTE_OFF & _MCLRE_OFF & _CP_OFF & _BOR_OFF & _IESO_OFF & _FCMEN_OFF)
cblock 0x20
State ;state machine variable
Display ;data to be displayed
endc
org 0
Start:
clrf PORTB
bsf STATUS,RP0 ; select Register Page 1
bcf OPTION_REG,7
movlw 0xFF
movwf TRISA ; Make PortA all input
movwf TRISB
clrf TRISC ; Make PortC all output
clrf ANSEL ; all pins digital
bcf STATUS,RP0 ; back to Register Page 0
bsf STATUS,RP1 ; Reg page 2
movlw 0xFF
movwf WPUB ; enable weak pull-up for Port B
bcf STATUS, RP1 ; Reg page 0
clrf Display
clrf State
MainLoop:
;state table:
; input | 000 | 010 | 100 | 011 | 101 |
; 00 | ->000 | == always ->000
; 01 | ->010 | ->000 | ->010 |->000- | == if state,0 ->000 (dec if 101), else ->010
; 10 | ->100 | ->000 | ->100 |->000+ | ->100 | == if state,1 ->000 (inc if 011), else ->100
; 11 | ->000 | ->011 | ->101 | ->011 | ->101 | == if state,0 ->101, else if state,1 ->011, else ->000
;decide current input: 00, 01, 10, or 11
;note that all input are active low
btfss PORTB,6
goto Input0
goto Input1
Input0:
btfss PORTB,7
goto Input00
goto Input01
Input1:
btfss PORTB,7
goto Input10
goto Input11
Input11: ;00
clrf State ;state ->000
goto Output
Input10: ;01
btfss State,0
goto I01B ;if 0xx
goto I01A ;if 1xx
I01A:
btfsc State,2 ;if xx1
decf Display,1 ; decrement data
clrf State ;->000
goto Output
I01B:
clrf State ;->010
bsf State,1
goto Output
Input01: ;10
btfss State,1
goto I10B ;if x0x
goto I10A ;if x1x
I10A:
btfsc State,2 ;if xx1
incf Display,1 ; increment data
clrf State ;->000
goto Output
I10B:
clrf State ;->100
bsf State,0
goto Output
Input00: ;11
btfsc State,0 ;if 1xx
goto IA
btfsc State,1 ;or x1x
goto IA
goto Output
IA:
bsf State,2 ;->uu1
goto Output
Output:
movfw Display ; Copy the display to the LEDs
movwf PORTC
goto MainLoop
end
A dolgot összeállítva minden várakozásomat felülmúlva működött, a forgást pontosan és gyorsan méri, bár csak kézzel forgattam, így nem tudtam letesztelni, hogy mekkora szögsebességet képes még mérni. Mindenestre akár felhasználónak nyújtott bemeneti eszközként is remekül szerepelhet, mindenképpen kifinomultabb megoldás mint pl. egy potméter A/D átalakítóval. Digitális hifiken rendszeresen találkozhatunk ezzel a megoldással hangerő-szabályozás formájában.