Morgans Reglerteknik, mrt at iname.com says:
To decode Quadrature Encoders: Generally,
- Get the two phase bits
- Together with the two bits from the last read form a 4-bit word
- Use this for a 16 entry goto table
- The table will contain 4 each of gotos for count up, count down, no change (glitch) and error (both phase changed, probably overspeed).
An easy way of getting the bits right is to left rotate them into a register, then mask using andlw 1111b before addwf PCL. Save the register until next call to this routine.
Interrupt on change is excellent for getting an interrupt on any change. But the routine can also be called anytime as it will handle no change too. I did this on a F877, using interrupt on change. It did reach 13kHz+ count and at the same time 4kHz timer 0 int driving some software timers etc, using 4,096 MHz xtal and 2-level interrupt structure. (Interrupt get what happened / Interrupt post service with interrupt re-enabled (soft timers, safety checks, buffers, etc) / Main prog)
ifc INTCON,RBIF ;Har porttillståndet ändrats? jmp isrP_NoRBIF ;Om inte: hoppa till nästa kollrutin ;Läs in nya tillståndet mov W, RB ; *** Ingen annanstans får PORTB läsas dierkt! *** c INTCON,RBIF ;Kvittera att vi har läst mov B_PORTBIN, W ;lagra för denna samt andra rutiner på port B ;Bearbeta tvåfassignal genast! ;Hämta fasbitarna från B_PORTBIN till B_2phState 1:0, de förra i 3:2... cc ;Rotate the new bits in... ifs B_PORTBIN,5 sc rl B_2phState cc ifs B_PORTBIN,4 sc rl B_2phState mov W, B_2phState and W, #%1111 add PC, W ;Before Now don´t care this below jmp isrBchange_NO ; 0 0 0 0 00 00 jmp isrBchange_UP ; 0 0 0 1 01 10 jmp isrBchange_DN ; 0 0 1 0 10 01 jmp isrBchange_ERR ; 0 0 1 1 11 11 jmp isrBchange_DN ; 0 1 0 0 01 01 jmp isrBchange_NO ; 0 1 0 1 00 11 jmp isrBchange_ERR ; 0 1 1 0 11 00 jmp isrBchange_UP ; 0 1 1 1 10 10 jmp isrBchange_UP ; 1 0 0 0 10 10 jmp isrBchange_ERR ; 1 0 0 1 11 00 jmp isrBchange_NO ; 1 0 1 0 00 11 jmp isrBchange_DN ; 1 0 1 1 01 01 jmp isrBchange_ERR ; 1 1 0 0 11 11 jmp isrBchange_DN ; 1 1 0 1 10 01 jmp isrBchange_UP ; 1 1 1 0 01 10 jmp isrBchange_NO ; 1 1 1 1 00 00 isrBchange_NO ; DEBUGIND_const 4 ; TEST s F_2phGlitch ;Indikera "glitch" GLITCH? ;ev godkänna glitch? Näe; lös ut = prioritera säkerhet isrBchange_ERR DEBUGIND_const 5 ; TEST s F_2phSpeed ;Indikera "överfart" OVERSPEED jmp isrP_2phEnd isrBchange_UP INC2 W_2phcnt ifzc jmp isrP_2phEnd s F_2phRange ;Indikera "utanför skala" not W_2phcnt ;Ändra 0000h till FFFFh not W_2phcnt+1 ; DEBUGIND_const 7 ; TEST jmp isrP_2phEnd isrBchange_DN FDEC2 W_2phcnt mov W, /W_2phcnt+1 ;Är högbyten annat än FF så är det OK ifzc ;Alltså: är ettkomplementet annat än 0? jmp isrP_2phEnd ;I så fall OK! mov W, /W_2phcnt ;Annars kolla lågbyten likadant... ifzc jmp isrP_2phEnd ;Om wrappade till FFFF: s F_2phRange ;Indikera "utanför skala" clr W_2phcnt ;nollställ räknaren.. clr W_2phcnt+1 ; ; DEBUGIND_const 6 ; TEST isrP_2phEnd ; DEBUGIND_low4 W_2phcnt ; TEST isrP_NoRBIF DEBUGIND_const 4 ;Int on change klar
Matthew Ballinger [MattBeck at AOL.COM] says:
Or a much more efficient routine would be (in parallax mnemonics):ChkEnc mov new,Enc ;get new input and new,#%00000011 ;get rid of all but 2 enc bits mov temp,new ;make copy xor temp,old ;is old = new jz ChkEnc ;then no change, go back cje temp,#3,err ;goto "error" if both bits changed clc ;get ready for right shift rl Old ;align bits xor old,new ;check for direction jb old.1,up ;if 1 then up direction, if 0 then down direction
Alvaro Deibe Diaz [adeibe at CDF.UDC.ES] says:
; Encoder read #define encx PORTA,1 ; Encoder inputs #define ency PORTA,2 ; ; ; Inic (usually needless) clr auxint ; Clear aux var snb encx ; and get encoder inputs setb auxint.0 ; in bit 0... snb ency setb auxint.1 ; ...and 1 ; ; Here starts the hard work mov W, auxint ; encod <- (actual state) xor (previous one) xor encod, W ; (only bits 0 and 1 affected) ; rr auxint ; XOR results, reordered, get rl encod ; into encod, in bits 0 and 1. rr auxint ; This bits are the motion indicators rl encod ; At this point you have a new read of the encoder. Bits 2 and 3 of 'encod' have the information about the direction of movement of the encoder. If you have to translate this information into a variable, then you can do something like this: ; snb encod.2 ; Direction 'X' motion inc posenc snb encod.3 ; ...or 'Y' dec posenc ; Bits 4,5 and 6,7 of 'encod' have the previous reads.
Lance Allen says:
The easiest way (in my opinion) is to feed one of the two outputs (I assume its a quadrature type) to B0 and the other one to any other input (probably B1). Set the B0 input to ext interrupt enabled . Whenever an interrupt is detected by an edge presenting itself (rising or falling .. its all programable) a movement of (pulses-per-rev divided by 360) deg has been made and if you read B1and there is a 1 there the encoder turned one way, if a 0 then it turned the other.If no interrupts are available (B0 is the only ext one) then you will need to poll one input and read the other on a change.... instead.
Morgan Olsson says:
Encoders use to have between 32 to several thousands cycles per revolution.If you want perfect synchornization to a 0° sync signal, set the two gotos that you wish will count into the zero position to point to specialized count up/down routines that also checks the sync signal.
This will give the best possible zero position synchronization, with a resolution of four steps per cycle, making the most out of the encoder.
Using interrupt on change for the two lines is the best.
If you need to poll for changes instead, we we might want to add a few lines of code before my example that just check if any line has changed, thus minimizing the cycles needed when no change. (but will add total cycles when a change has occurred)