;
;*******************************************************************
;
;	Experimental quadrature decoder for 12F508/12F509 
;	Input: Quadrature signals on GP0, GP1. "Reverse" control on GP3
;	Output: Low going pulse on on GP5, Direction indicator on GP4
;	This expands on previous versions by adding direction-sensing debounce.
;	The principal drawback of a preset loop-counting approach (eg Quad1)  
;	is the tradeoff between keeping-up with high speed input 
;	(spinning the dial) and discriminating adequately between direction- 
;	reversal and contact-bounce at low rotation speeds. 
;	This new approach makes some assumptions about the general nature
;	of encoders.
;
;	1/	Contact bounce or jitter only occurs on the line that has 
;		changed most recently and therefore resembles a change in the
;		direction of rotation.
;	2/	Due to the inertia of the mechanism, reversal of direction 
;		can occur only at low rotation speeds.
;
;	For example:
;	00 to 01 to 00, at high rotation speed is likely to be bounce
;	00 to 01 to 11, at high rotation speed is not likely to be bounce
;	00 to 11 or 11 to 00 or 10 to 01 or 01 to 10 should never occur
;	The principle here is to set a longer debounce threshold for transitions 
;	that indicate a reversal of the current direction of rotation
;
;	Consider a 250 vane wheel spinning at 10 rotations per second.
;	each edge of the vane generates a transition and there are two sensors
;	This yields a transition rate of 250 x 4 x 10 = 10000 transitions per sec.
;	ie 100uS per transition.
;	In practice, quadrature 'skew' will reduce this time but 100uS per
;	transition should be fast enough for all practical purposes.
;	Our input capture loop time is approx 20uS and so the maximum (8-bit)
;	255 Consecutive identical readings would take 5.1mS corresponding to a
; 	rotation rate of approx. 0.2 rotations per second: 72 degrees per sec.    
;	We require instantaneous rotation speed to be lower than this to
;	accept that the direction of rotation has changed.
;
;	Connect low going quadrature pair to pins 6 and 7
;	15uS low going pulse output on pin 2, direction output on pin3
;	connect pin4 to 0V and reboot to reverse direction
;	The "non debounced" direction indicator is brought out on pin 5
;	for diagnostic purposes. 
;
;	V1.2	CMS 26/03/2008
;
;*******************************************************************
;
#include <p12F509.inc>
	__config (_MCLRE_OFF & _CP_OFF & _WDT_OFF & _IntRC_OSC)
;
#define	CLKBIT	GPIO,5
#define	DIRBIT	GPIO,4
#define	REVBIT	GPIO,3
#define	DIRFLG	GPIO,2
;
MSKBYT	equ	3				; quadrature input bit mask
;
higspd	equ	D'004'			; hi speed debounce value
lowspd	equ	D'255'			; low speed reversal credibility threshold
;
;	reserve some space for variables
;
    cblock 0x08
;
temp1
temp2
count
speed
;
    endc
;
    cblock 0x10
;
;	reserve 16 bytes for a jump table
;
none1		;0	;0000
cw1			;1	;0001
ccw1		;2	;0010
inv1		;3	;0011
ccw2		;4	;0100
none2		;5	;0101
inv2		;6	;0110
cw2			;7	;0111
cw3			;8	;1000
inv3		;9	;1001
none3		;a	;1010
ccw3		;b	;1011
inv4		;c	;1100
ccw4		;d	;1101
cw4			;e	;1110
none4		;f	;1111
;
    endc
;
	org 0     					; osccal value in w on power up
	movwf	OSCCAL				;
;
	movlw	B'10000111'			; no wake on pin change, weak pull-ups on
	option						; internal timer clock, prescale 256 
;
	movlw   B'00001011'			; bit0, bit1 and bit3 are inputs
    tris	GPIO				;
;
	clrf	GPIO				; all outputs off, rotation clockwise
	bsf		CLKBIT				; clock pulse active lo
;
;	load up the jump-table and curse Harvard
;
	movlw	none				; same as last scan
;
	movwf	none1				; into all "no change"
	movwf	none2				; locations
	movwf	none3				; 	
	movwf	none4				; 	
;
	movlw	grab				; start address of grab routine
;
	movwf	inv1				; into all "non valid"
	movwf	inv2				; locations
	movwf	inv3				; 	
	movwf	inv4				; 	
;
	movlw	cw					; start address of cw routine
;
	btfss	REVBIT				; normal or reverse output required?
	movlw	ccw					; start address of ccw routine
;
	movwf	cw1					; into all "clockwise" locations	
	movwf	cw2					; 	
	movwf	cw3					; 	
	movwf	cw4					; 	
;
	movlw	ccw					; start address of ccw routine
;
	btfss	REVBIT				; normal or reverse output required?
	movlw	cw					; start address of cw routine
;
	movwf	ccw1				; into all "anticlockwise" locations
	movwf	ccw2				; 	
	movwf	ccw3				; 	
	movwf	ccw4				; 	
;
;	main loop
;
grab equ	$
;
	movf	temp1,w				; save previous
	movwf	temp2				; input
;
	movf	GPIO,w				; grab data
	andlw	MSKBYT				; mask
	movwf	temp1				; save
;
	bcf		STATUS,C			; not necessary but safer
	rlf		temp2,f				; rotate previous data left twice
	rlf		temp2,w				; 
;
	iorwf	temp1,w				; form four-bit transition code
;
	iorlw	0x10				; form table offset address 		
;
	movwf	FSR					; get jump vector
	movf	INDF,w				;
;
	bsf		CLKBIT				; clock signal to inactive (hi)
;
	movwf	PCL 				; and go
;
cw	equ		$
;
	movlw	higspd				; assume no direction change
	btfsc	DIRFLG				; test previous direction
	movlw	lowspd				; revese direction indicated
	movwf	count				; refresh counter
	bcf		DIRFLG				; flag clockwise rotation
	goto	grab				;
;
ccw	equ		$
;
	movlw	higspd				; assume no direction change
	btfss	DIRFLG				; test previous direction
	movlw	lowspd				; revese direction indicated
	movwf	count				; refresh target
	bsf		DIRFLG				; flag anti-clockwise rotation
	goto	grab				;
;
none equ	$
;
	movf	count,f				; test counter zero flag
	btfss	STATUS,Z			; pass new code once only 
	decfsz	count,f				; unchanged data so
	goto	grab				; decrement debounce counter
;
;	we have a consistent code
;
	btfsc	DIRFLG				; copy flag to output
	bsf		DIRBIT				; 
	btfss	DIRFLG				;
	bcf		DIRBIT				;
;
	bcf		CLKBIT				; clock signal to active (lo)
	goto 	grab				;
;
	end
