Keypad Module
This module is responsible for detecting and reporting valid keypresses. The interrupt does most of the hard stuff and all the program needs to do is to call KeyGet when it needs to know what key was pressed, if any.
The keys are detected in KeyChk, by scanning each row individually via KeyRow. This tallies up the number of detected keys, and if exactly one was pressed, the scan code of that key is returned. If none, or more than one, is pressed then the routine indicates that nothing has been pressed. The interrupt continually checks for keys every milisecond, and if one is held down for longer than 10ms then it is saved in the buffer upon being released.
Download:
; /////////////////////////////////////////////////////////////
; // Keypad Module II
; // (c) 1999 Geoff Knagge, Andrew Carr, Mark Lynn
; // last modified : 22/10/1999
; //
; // Assumes Key I/O port has been previously initialised for
; // output on bits 0-3, and input on bits 4-7. Only
; // KeyInterrupt and KeyGet should be called from outside this
; // module.
; //
; // KeyInterrupt should be called regularly (e.g. every 1 ms)
; //
; // Valid keypresses result in a scan code being copied to
; // KeyStore. KeyGet returns the code in A and clears KeyStore
; /////////////////////////////////////////////////////////////
; Scan codes are: 00 = "1" 04 = "2" 08 = "3"
; 01 = "4" 05 = "5" 09 = "6"
; 02 = "7" 06 = "8" 0A = "9"
; 03 = "*" 07 = "0" 0B = "#"
; // Data
lastkey: .byte KS_none
timeDown: .byte 0
KSetWhenReleased: .byte 0
KeyStore: .byte KS_none
; // Hardware Addresses used...
KeyPort = 42 ; port 2C
; // Constants
KeyDebounce = 0A ; milliseconds
; //Scan Code Constants :
KS_Invalid = 0FF
KS_None = 0FF
KS_1 = 00
KS_2 = 04
KS_3 = 08
KS_4 = 01
KS_5 = 05
KS_6 = 09
KS_7 = 02
KS_8 = 06
KS_9 = 0A
KS_0 = 07
KS_Star = 03
KS_Hash = 0B
KDelay: push AF
mvi A,20
KDRL: dcr A
cpi 0
jnz KDRL
pop AF
ret
;//////////////////////////////////////
KeyRow: ; reads the current row.
; in: A =output data, b = count,
; c = base scan code, e = old scan code
; out : b = new count, e = scan code
out keyport
call KDelay
in keyport ; read the column bits
ani 70 ; mask off column bits
cpi 0
jz kr4
cpi 10 ; check column 1
jnz kr2
inr b ; col 1 pressed
mov e,c ; e = scan code
jmp kr4
kr2: inr c
inr c
inr c
inr c
cpi 20
jnz kr3
inr b ; col 2 pressed
mov e,c
jmp kr4
kr3: inr c ; check if col 3 pressed
inr c
inr c
inr c
cpi 40 ; test that col 3 only is down
jnz kr5 ; if not, error
inr b ; otherwise increase counter
mov e,c ; save the scan code
jmp kr4 ; then exit
kr5: inr b ; more than one pressed
inr b ; so increase counter past invalid count
kr4:
ret
KeyChk: ; output: A=scan code
push hl
push de
push bc
mvi B,0 ; B = counter of keys
mvi c,0
mvi a,11111110# ; row 1
call keyrow
mvi c,1
mvi a,11111101# ; row 2
call keyrow
mvi c,2
mvi a,11111011# ; row 3
call keyrow
mvi c,3
mvi a,11110111# ; row 4
call keyrow
mov a,b
cpi 1 ; was one key only pressed?
jz kc2 ; yes, no error
cpi 0
jz kc3
mvi e,ks_invalid
jmp kc2
kc3: mvi e,ks_none
kc2: mov a,e
pop bc
pop de
pop hl
ret
;/////////////////////////////////////
;//////////////////// Key Handler... returns nothing, but destroys
;//////////////////// the contents of the A, H and L registers.
KeyInterrupt:
lhld times2
inx hl
shld times2
lxi hl,timedown
mov a,m
inr a
mov m,a
call KeyChk ; get the key scan code in A
lxi hl,lastkey ; point hl to the last scan code read
cpi KS_None ; was any key pressed this time?
jz KI_nokey
cpi KS_invalid ; was a valid key pressed?
jnz KI_valid
; mov m,A ; store the invalid code in lastkey
jmp KI_end
KI_valid:
cmp m ; is this key the same as last check?
jnz KI_newkey ; no, go somewhere else...
lxi hl,timeDown
mov a,m
cpi KeyDebounce ; has it been down long enough?
jnz KI_end ; no, nothing more to do
mvi a,1 ; yes, set the SetWhenReleased flag
lxi hl,KsetWhenReleased
mov m,A
jmp KI_end ; nothing more to do
KI_newkey: ; a new key press has occurred
mov m,A ; store that code in lastkey
xra a ; A = 0
lxi hl,KsetWhenReleased; clear setWhenreleased (though it
; should be clear anyway... best to be safe)
mov m,A;
lxi hl,timeDown ; set timeDown to zero
mov m,A
lxi hl,0
shld times2
jmp KI_end ; nothing more to do
KI_nokey: ; no key is pressed
xra A ; A = 0
lxi hl,0
shld times2
lxi hl,timeDown
mov m,a ; reset time down counter
lxi hl,KsetWhenReleased
cmp m ; check SetWhenReleased flag
jz KI_nopress ; if clear, no valid key press detected
mov m,A ; otherwise, clear the flag
lxi hl,lastkey
mov a,m ; get the scan code
lxi hl,KeyStore
mov m,a ; and store it in the buffer
KI_nopress:
mvi a,KS_None ; set lastkey to none
lxi hl,lastkey
mov m,a;
KI_end: ret
;///////////////////////
KeyGet: push bc
push hl
lxi hl, keystore
mov a,m ; retrieve the scan code, if any
mvi c,KS_None
mov m,c ; clear the key from memory
pop hl
pop bc
ret
|