Continue to Site

Welcome to our site!

Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

  • Welcome to our site! Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

4x4 Keypad, Dual Action keys, debounced.

Status
Not open for further replies.

Mosaic

Well-Known Member
Hi all:
I modified the code in my other post to handle a 4x4 keypad in a single port. Debounced (both on/off) and with Shortpress and Longpress detection for any key. These 2 bytes are polled in your code to detect which key/type is pressed.
The Shortpress/Longpress bytes are refreshed every iteration; they are transition monitored, so no 'repeating' keypresses are triggered. U have to release and repress to get 'repeats'.

I include the full code incl. ISR setup etc. for the 16f886 (my workhorse PIC). There is also a useful bit of code doing a clearing of all GPRs in that PIC.

No ADCs or WPUs are used...just Digital PORT I/O so this should work for any PIC.

Code:
;                                                                     *
;    Files Required: P16F886.INC                                      *
;                                                                     *
;**********************************************************************

;**********************************************************************
	errorlevel -302 ; suppress message 302 from list file

	list		p=16f886		; list directive to define processor
	#include	"P16F886.inc"		; processor specific variable definitions
	__IDLOCS   0x0003  ;Your Firmware Version if you care.
 
	;radix D
 		
	__CONFIG    _CONFIG1,  _IESO_OFF & _FCMEN_OFF & _PWRTE_ON  & _WDT_OFF & _INTOSCIO & _BOR_ON & _LVP_OFF & _CP_ON&_CPD_ON& _MCLRE_OFF &_INTRC_OSC_NOCLKOUT ;program protected
	__CONFIG    _CONFIG2,	_WRT_OFF & _BOR40V
	
	

	;****************GPR declarations****************		; note Bank0 last 16bytes,0x70 to 0x7f, visible in all banks as local last 16.
	TmpBnk0		EQU  	0x20 	; Bank 0 tmp variable
	STATmp			EQU	0x21	; isr status tmp
	temp_pclath		EQU	0x22 	; isr paging temp
	LastKeystate		EQU	0x23
	Dbouncecount	EQU	0x24	; Counts every 4 ms in ISR
	DbKeystate		EQU	0x25	; debounced keys
	KeyTrans		EQU	0x26	; debounce transitioning (on/off)
	HoldKeystate		EQU	0x27	; carries Hold state for  switches,
	HoldTrans		EQU	0x28	; carries hold transition state for switches
	Shortpress		EQU	0x29	; byte to track which switch , row/col pressed & released. Monitor this within your prg to act on a keypress (short)
	Longpress		EQU	0x2a	; byte to track which switch , row/col pressed & held. . Monitor this within your prg to act on a keypress (long)
	
	
	;all GPR banks variables for 16F886, 70 - 7f.
	WregTmp		EQU	0x7f	;isr  wreg save - allbanks
	

	;******** Prog Start ********;
	Org	0x000	; processor reset vector
 	goto	Main	; go to beginning of program
	Org	0x04	; interrupt vector
;Preserve key registers first!

	movwf WregTmp; save wreg in an allbanks GPR
	movf STATUS,w
	clrf STATUS; sets bank0
	movwf STATmp; save status
	movf  PCLATH,w     ; save PCLATH as well
	movwf temp_pclath
	clrf STATUS; BANK 0
   	clrf PCLATH       ; now on code page 0 for sure too.

;*********timer code**********
	incf Dbouncecount,f ;counts 4ms intervals for debouncing switch.
	movf Dbouncecount,w
	sublw .250 ; 250 x 4ms = 1 sec
	skpnz
	clrf Dbouncecount
	;skpnz
	;incf secondcount,f;  count 1 per sec. Can be useful for system clocks etc.
	
	bcf PIR1,2 ; clear isr tmr1 compare flag
Regrestore; Restore registers
	
	movf temp_pclath,w ;get PCLATH
	clrf STATUS; bank0
  	movwf PCLATH  ;Move W into PCLATH
	movf STATmp,w
	movwf STATUS ; restore status
	swapf WregTmp,f ; swap nybbles
	swapf WregTmp,w ; swap again and leave in wreg.
	retfie ;return & reactivate GIE.

Main ; configure ports & ISR.
	banksel	ANSELH
	clrf ANSELH; all outs
	Banksel TRISB
	movlw b'00001111' ; rb0-3 are row inputs, rb4-7 are column outputs.
	movwf TRISB
	movlw b'01001100'
	movwf OSCCON ; set 1Mhz, Osc setup based on config fuses . (1Mhz for pwr saving, 4uS per instruction)
	bsf PIE1,2 ; enable ccp1 interrupt setup below.
	clrf STATUS
	movlw 0x03
	movwf CCPR1H ; high byte of Eccp1
	movlw 0xe8
	movwf CCPR1L ; low byte of Eccp1, to total 1000 before ccp1 interrupt at 250000Hz = 250Hz = 4 msec period. 250 x  4ms ticks => 1 seconds.
	movlw b'00001011'
	movwf CCP1CON;	Configure Tmr1 COMPARE to CCPRx to setup CCP1 interrupt freq.
	bsf T1CON,0; start 16 bit tmr1, no scaling. When TMR1x equal to  CCPRx value above, CCP1 interupt happens.
	bsf INTCON,7 ;  general interrupts enable
	bsf INTCON,6; Peripheral Int enable - start Tmr1/ccp1.




 GPRinit;clears all gpr banks to zero.
	clrf STATUS
	movlw 0x21; bank 0
	movwf FSR	
	movlw .94; # of bytes to fill.
	movwf TmpBnk0; this is used as a temp on startup.
	call Memfill

	movlw 0xA0; bank1
	movwf FSR	
	movlw .80
	movwf TmpBnk0
	call Memfill

	bsf STATUS,IRP; bank sel for FSR to bank 2/3
	movlw LOW (0x110); bank2
	movwf FSR	
	movlw .96
	movwf TmpBnk0
	call Memfill

	movlw LOW (0x190); bank3
	movwf FSR	
	movlw .96
	movwf TmpBnk0
	call Memfill
	
	clrf STATUS
	clrf PORTA ; 
	clrf PORTC
	clrf PORTB
	clrf PORTE



MainLoop; iterative
	call Keypad

	goto MainLoop
	
	
		

Keypad; Evaluate keypresses for click, hold, open with debounce & transition states. Works for a 4x4 keypad. Port pins 0-3 are rows, 4-7 are columns. Keys have a common col(0-3)and bridge to each row (0-3)
	;Change the port if you wish. 
;*****The 'Shortpress' GPR and 'Longpress' GPR Values indicate the active key press as follows:  Col 0, Row0 = 17, R1=18, R2=20, R3=24; Col 1, Row 0 = 33, R1=34,R2=36,R3=40;; Col2, Row0=65,R1=66,R2=68;R3=72;;Col3 Row0 = 129; R1=130; R2=132;R3=136.
; Further details below. Not required to use the code though.

;Requires a 4 ms tick (usually done in an ISR) for 'Dbouncecount'. Can use a different ISR ,eg. a 1mSec and fix the debounce delay using a bit 4 compare rather than bit 2  used below in the line with ;*****************

;Each of the 8 Transition bits in the bytes (Keytrans and HoldTrans) allow for debounced edge triggering events. eg. If the DbKeystate,0  is hi and the Keytrans,0 is high then the debounced key0 press leading edge just happened.  
; The Transition bytes are set  if a new  debounced keystate just happened . These are cleared every iteration.
;If DbKeystate is Hi and Keytrans,0 is lo, then the debounced Keypress is mature.
;If the DbKeystate,0 is lo and the KeyTrans,0 is hi then the debounced key was just released - trailing edge.
; A similar structure applies to HoldTrans and HoldKeystate bytes as applies to a 'long key hold/click' operation - holding down a key for a 1/2 second.
; Thus a single key can deliver up to 4 states, leading and trailing edge for both short clicks and long clicks. Detect the col & row bits set in the Shortpress/Longpress gprs to use this detail.
; The use of the leading edge transitions is manifest when preventing 'repeating' a routine that responds to the key condition.eg. only respond to a keypress if the transition bit is also set, like make a beep.
;The scanning code stops when a key is pressed. It continues when the key is released and the release is debounced.
;

;rolling column pointer. PortB4-7 =>cols 0 - 3
	clrf KeyTrans ;limit all transitions to one complete program loop.
	clrf HoldTrans; " "
	movf PORTB,w
	andlw b'00001111'; test if any keypresses detected.
	iorwf  DbKeystate,w; or debounced keypress active.
	skpz
	goto  Evaldebounce; skip col. scans when a keypress is being handled.
	;scan columns for a keypress.
	clrc
	rlf PORTB,f; shift col.
	movf PORTB,w; read rows and cols
	andlw b'11110000'; isolate columns
	skpnz
	bsf PORTB,4; reset col1 roll.
	skpz
	movwf PORTB; write col. roll to Port
	movf PORTB,w; read rows and cols to detect keypress.

Evaldebounce	
	movf PORTB,w ; get newstate					
	andlw b'00001111'; isolate rows.
	xorwf LastKeystate,w ; state changes					
	xorwf LastKeystate,f ; make Laststate =Newstate.					
	andlw b'11111111'; test wreg if any bits changed
	btfss STATUS,Z; skip zero set, else (statechange)
	clrf Dbouncecount; reset counter (counts every 4 msec in ISR)
	btfss Dbouncecount,2 ; skip if compared bit 2 set=> 16ms passed, else ;*******************
	goto Testkeyhold;
	movf LastKeystate,w;													
	xorwf DbKeystate,w; get row changes bet. current state and debounced state.		
	movwf KeyTrans; save in transition byte.											
	xorwf DbKeystate,f; apply them to DB state	
	movf DbKeystate,w	
	xorwf HoldKeystate,w ; ensure Hold not active as well.
	xorlw .255; setup test for a key release (open)
	andwf KeyTrans,w; verify transition of key release.
	movwf Shortpress; update shortpress row status, based on click/release.	
	skpnz
	goto Testkeyhold
	movf PORTB,w
	andlw b'11110000'; select active column.
	iorwf Shortpress; complete shortpress row & column switch identifier.
	
Testkeyhold
	movf  DbKeystate,w	;														
	andwf HoldKeystate,w ;zero Hold bits if matching key is released - wreg.				
	xorwf HoldKeystate,w ; get Transition from Hold to release							
	movwf HoldTrans; update HoldTransition byte.													
	xorwf KeyTrans,f ; toggle any matching release from clicks off, release from hold priority.
	xorwf HoldKeystate,f ; now immediately switch off Hold if a key is released - file.		
	xorwf DbKeystate,w ; get newly pressed keys to test for hold.(state change)						
	btfss STATUS,Z; skip if  no new key pressed, else
	
	
Testforholdcount ; test if timer expired
	btfss Dbouncecount,7 ; 128*4ms = 512ms
	goto Keydone
	movf DbKeystate,w;							
	xorwf HoldKeystate,w ; get statechanges			
	iorwf HoldTrans,f; update hold transition byte.			
	;using what's still in wreg to apply changes		
	xorwf HoldKeystate,f;apply changes		
	movf HoldKeystate,w
	andwf  HoldTrans,w; verify transition of key 
	movwf Longpress; update Longpress status.	
	skpnz
	goto Keydone
	movf PORTB,w
	andlw b'11110000'; select active column.
	iorwf Longpress; complete Longpress row & column switch identifier.
Keydone	return
				
 Memfill; init GPR blocks with zero.
	decfsz TmpBnk0,f; skip when TmpBnk0 has counted down to zero.
	goto Erase
	return
Erase
	clrf INDF; clr ram cell
	incf FSR,f; inc pointer
	goto Memfill
	
	END
 

Attachments

  • Screenshot_1.png
    Screenshot_1.png
    80.4 KB · Views: 574
Last edited:
Status
Not open for further replies.

Latest threads

New Articles From Microcontroller Tips

Back
Top