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:
From a DDS program by Curtis W. Preuss - also works if you don't crank up the spin speed too much:
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:
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
movwf enc_read ; Save it
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:
;
; QUAD State
;
; A quadrature encoder traverse a couple of states
; when it is rotating these are:
; 00 | Counter
; 10 | Clockwise
; 11 | ^
; 01 V |
; 00 Clockwise |
;
;
QUAD_STATE:
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.
;
MOVLW high QUAD_STATE
MOVWF PCLATH
MOVF QUAD_ACT,W ; Get button state
ADDWF PCL,F ; Indirect jump
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