;**************************************************************
;keypad_test
;Tests keypad, writing key pressed to lcd display on 
;Derbot Hand Controller. 
;The lcd controller is S6A0069, whose initialisation is applied.
;TJW rev. 20.7.06							Tested 20.7.06
;***************************************************************
;Clock is 1MHz approx
;Configuration Word: RC oscillator, WDT off, power-up timer on, 
;			   brown-out off, LVP off, all code protect on.
;								
;   Port A      Port B      	Port C
;   ------      ------          ------
;0	lcd bus 2	not used		Display RS
;1	lcd bus 3	keypad col 3	Display E
;2	lcd bus 4	keypad col 2	Interrupt op
;3	lcd bus 5	keypad col 1	SCL
;4	lcd bus 6	keypad row 4	SDA
;5	lcd bus 7	keypad row 3	Display R/W
;6	    -		keypad row 2 	lcd bus 0
;7	    -		keypad row 1   	lcd bus 1

	list 	p=16F873A
	#include p16f873A.inc

;Specify RAM
delcntr1 equ	20	;used in delays
delcntr2 equ	21
temp	 equ	22  ;a temp location, to be used only in consecutive instructions
lcd_op	 equ	23	;holds word to be sent to display
pointer  equ    24  ;points to Character string Tables
kpad_pat equ	25	;holds pattern of keypad
kpad_add equ	26  ;holds kpad address to lu table
kpad_char equ	27	;holds most recent character from keypad
flags	 equ	28	;bit 0 holds RS state during busy_check SR
			
;Specify some port bits
;Port C
lcd_RS	equ	0	;0 for instruction, 1 for character
lcd_E	equ 1
lcd_RW	equ	5
;Port A
lcd_busy equ 5

	org 00
	goto main
	org 04		;interrupt vector
	goto kpad_to_lcd
;Initialise
main bcf	status,rp1
	bsf		status,rp0 		;select memory bank 1
	movlw	B'00000000'	;Port A & C initially all op
	movwf	trisa		
	movwf	trisc		
	movlw	B'00000110'	;All Port A bits digital
	movwf	adcon1
	movlw	B'11110000'	;Port B initially Row bits ip, column op
	movwf	trisb		;(port B not used)	
	bcf		status,rp0 	;select bank 0
;
;initialise display 
		bcf	portb,0		;diagnostic, used in busy_check ***
		call delay200		;wait for power supply to settle
		bcf	portc,lcd_RS	;set control lines
		bcf	portc,lcd_RW
;Relies first on automatic initialisation, as described in data sheet
;now send four command words. Busy Flag not used. 
		movlw B'00001111'	;first command word, Function Set, rotated by 2 bits
		movwf porta
		bsf	portc,lcd_E
		bcf	portc,lcd_E
		call delay1			;wait at least 39us
;
		movlw B'00000011'	;next command, Display ON/OFF
		movwf porta
		bsf	portc,6
		bsf	portc,7
		bsf	portc,lcd_E		
		bcf	portc,lcd_E
		call delay1			;wait at least 39us
;
		movlw B'00000000'	;next command, Display Clear
		movwf porta
		bcf	portc,7
		bsf	portc,6
		bsf	portc,lcd_E		
		bcf	portc,lcd_E
		call delay1		
		call delay1			;wait at least 1.53ms
;
		movlw B'00000001'	;next command, Entry Mode Set
		movwf porta
		bsf	portc,7
		bcf	portc,6
		bsf	portc,lcd_E		
		bcf	portc,lcd_E
		call delay1	
;
;initialise interrupt, enable port change	
		clrf	portb	;initialise keypad value
		bcf		intcon,rbif	
		bsf		intcon,rbie
		bsf		intcon,gie
loop	goto	loop		;await keypad entries

;***********************************************************
;Interrupt Service Routine. 
;***********************************************************
;Keypad press has been detected through Port B Interrupt on Change.Gets keypad pattern, 
;converts to character, stores in kpad_char, sends to lcd, awaits key release, 
kpad_to_lcd call 	kpad_rd
;now convert code to character, forming address used in lu table
		call 	kp_code_conv	
;now send to lcd
		movf	kpad_char,0 ;move character to lcd buffer
		movwf	lcd_op
		bsf		portc,lcd_RS ;set for character op
		call	lcd_write
;test now for keypad release
rel_test call 	kpad_rd		
		movf	kpad_pat,0
		andlw	0fe			;suppress lsb, which is not used
		sublw	0fe			;test if inactive
		btfss	status,z
		goto 	rel_test
		bcf		intcon,rbif ;clear interrupt flag
		retfie
;	
;***********************************************************
;SUBROUTINES
;***********************************************************
;Reads keypad, places pattern into kpad_pat, and resets keypad interface
kpad_rd	movf	portb,w	;read portb value, this will be row pattern
		andlw	B'11110000' ;ensure unwanted bits are suppressed
		movwf	kpad_pat
		bsf		status,rp0 ;set row to op, column to ip
		movlw	B'00001110'
		movwf	trisb
		bcf		status,rp0
		movlw	00
		movwf	portb	;ensure output values still zero
		movf	portb,w	;read portb value, this will be column pattern
		andlw	B'00001110' ;ensure unwanted bits are suppressed
		iorwf	kpad_pat,1	;OR those results into the pattern
;reset keypad interface
		bsf		status,rp0 ;set row to ip, column to op
		movlw	B'11110000'
		movwf	trisb
		bcf		status,rp0
		clrf	portb	;ensure output values still zero
		return

;Converts keypad pattern held in kpad_pat to ASCII character, first forming 
;address (in kpad_add) that is used in lu table. Returns with character held in kpad_char
kp_code_conv bcf status,c
		rrf		kpad_pat,1	;discard bit 0 which is not used
		clrf	kpad_add
;deduce row
		btfsc	kpad_pat,6
		goto	kp1
		goto	col_find	;here if row 1, kpad_add stays as is
kp1		btfsc	kpad_pat,5
		goto	kp2
		movlw	B'00000100'	;here if row 2
		iorwf	kpad_add,1	;form table address
		goto	col_find
kp2		btfsc	kpad_pat,4
		goto	kp3
		movlw	B'00001000'			;here if row 3
		iorwf	kpad_add,1	;form table address
		goto	col_find
kp3		btfsc	kpad_pat,3
		goto	kp4
		movlw	B'00001100'			;here if row 3
		iorwf	kpad_add,1	;form table address
		goto	col_find
kp4		movlw	D'16'			;no row detected, return "E" via Table
		goto keypad_op
;now deduce column
col_find btfsc	kpad_pat,2
		goto	cf1
		goto    keypad_op	;here if column 1, kpad_add stays as is
cf1		btfsc	kpad_pat,1
		goto	cf2
		movlw	B'00000001'			;here if column 2
		iorwf	kpad_add,1	;form table address	
		goto   keypad_op
;assume now column 3
cf2		movlw	B'00000010'			
		iorwf	kpad_add,1	;form table address	
keypad_op movf	kpad_add,0
		call	kp_table
		movwf	kpad_char	;save the character
		return
;
;Table called to convert pattern recd from keypad to actual character. Note that
;ASCII codes will be returned, as each digit is in format 'D'.
kp_table addwf pcl,1
		retlw	'1'		;row 1
		retlw	'2'
		retlw	'3'
		retlw	'A'		;Error code
		retlw	'4'		;row 2
		retlw	'5'
		retlw	'6'
		retlw	'B'		;Error code
		retlw	'7'		;row 3
		retlw	'8'
		retlw	'9'
		retlw	'C'		;Error code
		retlw	'*'		;row 4
		retlw	'0'
		retlw	'#'
		retlw	'D'		;Error code
		retlw	'E'		;Error code
;
;Waits until busy clear, and writes word held in lcd_op to display.
;RS must be preset to required value, this status is preserved. 
lcd_write call busy_check
		bcf	portc,lcd_rw
		bcf	status,c
		rrf	lcd_op,1	;form output bits, op word sits across ports a & c
		bcf	portc,6		;set value of bit 0 of bus
		btfsc status,c
		bsf	portc,6
		bcf	status,c
		rrf	lcd_op,1
		bcf	portc,7		;set value of bit 1 of bus
		btfsc status,c
		bsf	portc,7
		movf lcd_op,0
		movwf porta
		bsf	portc,lcd_E
		bcf	portc,lcd_E
		return
;Test Busy Flag, and wait till cleared
busy_check bsf status,rp0 	;select memory bank 1
		movlw B'00111111'	;set port A all ip
		movwf trisa	
		bcf	status,rp0
 		bcf	flags,0			
		btfsc portc,lcd_RS	;save RS bit in flags, 0
		bsf	flags,0		
		bcf	portc,lcd_RS 	;access instruction register
		bsf	portc,lcd_RW	;set to read
busy_loop bcf portc,lcd_E	
		bsf	portc,lcd_E
		btfsc porta,lcd_busy ;test the busy flag, loop if still busy 
		goto busy_loop		
		bcf	portc,lcd_E	
		bsf	status,rp0 		;select memory bank 1
		movlw B'00000000'	;set port A all op, 
		movwf trisa	
		bcf	status,rp0
		bcf	portc,lcd_RS	
		btfsc flags,0		;reinstate RS bit
		bsf	portc,lcd_RS	
		return
;
;introduces delay of 100us approx
delay100u  movlw 	D'6' ;6 cycles called, each taking 16us
        movwf  	delcntr1
del1001    nop	;4 inst cycles in this loop, ie 16us
        decfsz 	delcntr1,1
        goto 	del1001
        return

;introduces delay of 1ms approx
delay1  movlw 	D'62'	;62 cycles called, each taking 16us
        movwf  	delcntr1
del1    nop			;4 inst cycles in this loop, ie 16us
        decfsz 	delcntr1,1
        goto 	del1
        return
;
;introduces delay of 5ms approx
delay5  movlw 	D'250'	;250 cycles called 
        movwf  	delcntr1
del51   nop				;5 inst cycles in this loop, ie 20us
 		nop
        decfsz 	delcntr1,1
        goto 	del51
        return
;
;introduces delay of 200ms approx, by 200 calls to delay1
delay200  movlw D'200'
        movwf  	delcntr2
del2    call 	delay1
        decfsz  delcntr2,1
        goto 	del2
        return  
;
	end
