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.
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
Last edited: