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? goto isrP_NoRBIF ;Om inte: hoppa till nästa kollrutin ;Läs in nya tillståndet movf PORTB,w ; *** Ingen annanstans får PORTB läsas dierkt! *** c INTCON,RBIF ;Kvittera att vi har läst movwf B_PORTBIN ;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 rlf B_2phState,f cc ifs B_PORTBIN,4 sc rlf B_2phState,f movf B_2phState,w andlw b'1111' addwf PCL,F ;Before Now don´t care this below goto isrBchange_NO ; 0 0 0 0 00 00 goto isrBchange_UP ; 0 0 0 1 01 10 goto isrBchange_DN ; 0 0 1 0 10 01 goto isrBchange_ERR ; 0 0 1 1 11 11 goto isrBchange_DN ; 0 1 0 0 01 01 goto isrBchange_NO ; 0 1 0 1 00 11 goto isrBchange_ERR ; 0 1 1 0 11 00 goto isrBchange_UP ; 0 1 1 1 10 10 goto isrBchange_UP ; 1 0 0 0 10 10 goto isrBchange_ERR ; 1 0 0 1 11 00 goto isrBchange_NO ; 1 0 1 0 00 11 goto isrBchange_DN ; 1 0 1 1 01 01 goto isrBchange_ERR ; 1 1 0 0 11 11 goto isrBchange_DN ; 1 1 0 1 10 01 goto isrBchange_UP ; 1 1 1 0 01 10 goto 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 goto isrP_2phEnd isrBchange_UP INC2 W_2phcnt ifzc goto isrP_2phEnd s F_2phRange ;Indikera "utanför skala" comf W_2phcnt,f ;Ändra 0000h till FFFFh comf W_2phcnt+1,f ; DEBUGIND_const 7 ; TEST goto isrP_2phEnd isrBchange_DN FDEC2 W_2phcnt comf W_2phcnt+1,W ;Är högbyten annat än FF så är det OK ifzc ;Alltså: är ettkomplementet annat än 0? goto isrP_2phEnd ;I så fall OK! comf W_2phcnt,W ;Annars kolla lågbyten likadant... ifzc goto isrP_2phEnd ;Om wrappade till FFFF: s F_2phRange ;Indikera "utanför skala" clrf W_2phcnt ;nollställ räknaren.. clrf 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) clrf auxint ; Clear aux var btfsc encx ; and get encoder inputs bsf auxint,0 ; in bit 0... btfsc ency bsf auxint,1 ; ...and 1 ; ; Here starts the hard work movf auxint,W ; encod <- (actual state) xor (previous one) xorwf encod,F ; (only bits 0 and 1 affected) ; rrf auxint,F ; XOR results, reordered, get rlf encod,F ; into encod, in bits 0 and 1. rrf auxint,F ; This bits are the motion indicators rlf encod,F ; 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: ; btfsc encod,2 ; Direction 'X' motion incf posenc,F btfsc encod,3 ; ...or 'Y' decf posenc,F ; 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)