1. 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.
    Dismiss Notice

4x4 Keypad, Dual Action keys, debounced.

Discussion in 'Code Repository' started by Mosaic, Apr 8, 2013.

  1. Mosaic

    Mosaic Well-Known Member

    Jun 3, 2010
    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 (text):

    ;                                                                     *
    ;    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    _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
        clrf Dbouncecount
        ;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.
        goto  Evaldebounce; skip col. scans when a keypress is being handled.
        ;scan columns for a keypress.
        rlf PORTB,f; shift col.
        movf PORTB,w; read rows and cols
        andlw b'11110000'; isolate columns
        bsf PORTB,4; reset col1 roll.
        movwf PORTB; write col. roll to Port
        movf PORTB,w; read rows and cols to detect keypress.

        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.
        goto Testkeyhold
        movf PORTB,w
        andlw b'11110000'; select active column.
        iorwf Shortpress; complete shortpress row & column switch identifier.
        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.  
        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
        clrf INDF; clr ram cell
        incf FSR,f; inc pointer
        goto Memfill

    Attached Files:

    Last edited: Apr 13, 2013

Share This Page