Continue to Site

ASM - encoder routine for high PPR optical encoders - ideas please?

augustinetez

Active Member
Rather than load up my timer preload thread with this, I've started this seperately (but it is all related).

For the cheap and nasty mechanical detented encoders, I've got a few routines that work well enough, but are basically hopeless for high (400) ppr optical encoders, even after taking out the divide by 4 bit for the detents - no detents in the encoder I'm using.

Bottom line is that all the stuff I've either found here or elsewhere on the 'net suffer from the inability to keep up with the encoder - they either jitter back and forth between CW and CCW too much, or the most common problem - the faster you spin the encoder, the slower the returned count or they start showing a reversed count ie instead of counting up, they (the code) start counting down and vice versa.

At the moment, I am polling the encoder because everything else in the program does nothing until the encoder moves, so interrupt driven routines aren't needed (yet).

Here are a couple or three bits of code I have been playing with - bottom line, all I need at the end of the routine is to set a direction bit (dir,0 in my program).

Ultimately, the aim is to produce a bit of code that will automatically change the frequency step size of a DDS chip dependent on how fast the encoder is moving.

A modification of Mike K8LH's bit of code - works until you crank up the speed:

Code:
movf    PORTA,W        ; load switch data
andlw    b'00000011'    ; mask encoder B and A switches
xorwf    enc_old,W    ; same as last reading?
btfsc    STATUS,Z    ; yes, branch (no change), else
goto    poll_encoder
xorwf    enc_old,W    ; restore encoder bits in W
rrf    enc_old,f    ; prep for B-old ^ A-new
xorwf    enc_old,f    ; ENCOLD bit 0 = direction
rrf    enc_old,f    ; now Carry bit = direction
movwf   enc_old        ; update ENCOLD (new BA bits)

;****************** For optical encoder *****************************************
;       Prevent encoder slip from giving a false change in direction.

movf    STATUS,w    ; Carry bit = direction
andlw    b'00000001'
movwf    next_dir    ; Save result (in W) as direction
xorwf    dir,w        ; See if direction is same as before
btfsc    STATUS,Z    ; Zero flag set? (i.e, is direction same?)
goto    enc_exit    ; Yes, same direction so no slip; keep going
movf    next_dir,w    ; No Zero-flag, so direction changed
movwf    dir        ; Update the direction indicator
goto    poll_encoder    ; Try again
;********************************************************************************
;
;  set <up> or <dn> switch flag bits based on bit 0 in next_dir
;
enc_exit
bcf    dir,0        ; set <dn> switch flag for Main
btfsc    next_dir,0
bsf    dir,0        ; set <up> switch flag for Main

return

From a DDS program by Curtis W. Preuss - also works if you don't crank up the spin speed too much:

Code:
        movf    PORTB,w        ; Get the current encoder value
andlw    b'00000011'    ; mask encoder B and A switches
movlw    b'00000011'    ; Get encoder mask (to isolate RB0 and RB1)
andwf    enc_read,w    ; Isolated encoder bits into W
movwf    enc_new        ; Save new value
xorwf    enc_old,w    ; Has it changed?
btfsc    STATUS,Z    ; Check zero-flag (zero if no change)
goto    poll_encoder    ; No change, keep looking until it changes
; Else, Zero-flag is not set, so continue on

; It changed. Now determine which direction the encoder turned.

bcf    STATUS,C    ; Clear the carry bit to prepare for rotate
rlf    enc_old,f    ; Rotate old bits left to align "Right-Bit"
movf    enc_new,w    ; Set up new bits in W
xorwf    enc_old,f    ; XOR old (left shifted) with new bits
movf    enc_old,w    ; Put XOR results into W also
andlw    b'00000010'    ; Mask to look at only "Left-Bit" of pair
movwf    next_dir    ; Save result (in W) as direction (bit=UP)
xorwf    last_dir,w    ; See if direction is same as before

;****************** For optical encoder *****************************************
;       Prevent encoder slip from giving a false change in direction.

btfsc    STATUS,Z    ; Zero flag set? (i.e, is direction same?)
goto    enc_continue    ; Yes, same direction so no slip; keep going
movf    next_dir,w    ; No Zero-flag, so direction changed
movwf    last_dir    ; Update the direction indicator
movf    enc_new,w    ; Save the current encoder bits (now in W)
movwf    enc_old        ; for next time
goto    poll_encoder    ; Try again
;********************************************************************************
;
enc_continue
clrf    last_dir    ; Clear last_dir (default is DN)
bcf    dir,0
btfsc    enc_old,1    ; Are we going UP?
goto    enc_up        ; Yes, go process it.
; Else, we are goiong down
goto    enc_movement    ; Indicate that the encoder has moved

enc_up
movlw    b'00000010'    ; Get UP value
movwf    last_dir    ; and set in last_dir
bsf    dir,0

enc_movement            ; Arrive here when encoder is being turned
movf    enc_new,w    ; Get the current encoder bits
movwf    enc_old        ; Save them in ren_old for the next time
bsf    flags,2        ; Set encoder changed flag

return            ; Return to the caller

And one from Leon - which has an error in it - this is extraneous as in doesn't do anything -> MOVWF Q_NOW
This one misbehaves the most:

Code:
;
;
; A quadrature encoder traverse a couple of states
; when it is rotating these are:
;       00      |  Counter
;       10      |  Clockwise
;       11      |     ^
;       01      V     |
;       00  Clockwise |
;
;
BCF     STATUS,C        ; Force Carry to be zero
MOVF    PORTB,W         ; Read the encoder
ANDLW   0x03            ; And it with 0011
MOVWF   Q_1             ; Store it
IORWF   Q_1,W           ; Or in the current value
MOVWF   QUAD_ACT        ; Store at as next action
MOVF    Q_1,W           ; Get last time
MOVWF   Q_NOW           ; And store it.
;
; Computed jump based on Quadrature pin state.
;
MOVWF   PCLATH
MOVF    QUAD_ACT,W      ; Get button state
RETURN                  ; 00 -> 00
GOTO    DEC_COUNT       ; 00 -> 01 -1
GOTO    INC_COUNT       ; 00 -> 10 +1
RETURN                  ; 00 -> 11
GOTO    INC_COUNT       ; 01 -> 00 +1
RETURN                  ; 01 -> 01
RETURN                  ; 01 -> 10
GOTO    DEC_COUNT       ; 01 -> 11 -1
GOTO    DEC_COUNT       ; 10 -> 00 -1
RETURN                  ; 10 -> 01
RETURN                  ; 10 -> 10
GOTO    INC_COUNT       ; 10 -> 11 +1
RETURN                  ; 11 -> 00
GOTO    INC_COUNT       ; 11 -> 01 +1
GOTO    DEC_COUNT       ; 11 -> 10 -1
RETURN                  ; 11 -> 11
INC_COUNT:
INCF    COUNT,F
MOVLW   D'201'
SUBWF   COUNT,W
BTFSS   STATUS,Z
RETURN
DECF    COUNT,F
RETURN
DEC_COUNT
DECF    COUNT,F
MOVLW   H'FF'
SUBWF   COUNT,W
BTFSS   STATUS,Z
RETURN
INCF    COUNT,F
RETURN

end

Pommie

Well-Known Member
Note, due to the method used to read the encoder, only 1 count per click is counted rather than 4.

Mike.

BobW

Active Member
Just saw this topic now. (I've been away from this forum for quite a while because of the annoying popup ads.) Since there've been no posts for 6 weeks, I'm wondering if the problem has been resolved.

As others have suggested, I think that the encoder routine will need to be interrupt driven for best operation, but it also depends on how much stuff is going on in the main program loop.

A few years ago, I wrote an encoder routine for a 4096 ppr encoder to read machine position for an industrial project. The algorithm is simple and fast on a low end 4MHz 16F630. More about it in this post:

More recently I've used it to adjust frequency on an AD9850/51 DDS frequency generator chip, though I used a much lower resolution encoder for this. Tuning is very smooth and it can handle a fast spin of the dial without any glitches.

augustinetez

Active Member
Hi Bob.

Testing of the code I ended up using has proven to do the job - forgot what I ended up using without looking it up - the mechanical aspect of the project means it can't be spun all that fast anyway.

But I will keep a note of the code in your post linked above to play with when I get my current project sorted.
Thanks.

Replies
4
Views
674
Replies
0
Views
2K
Replies
5
Views
556
Replies
37
Views
2K
Replies
3
Views
2K