* keypad.asm - Code for working with the 16-key keypad *------------------------- * Keypad-related equates. *------------------------- TxD EQU PD1 ; Bit 1 of port D drives the SCI transmit pin ROW1 EQU PD2 ; Bit 2 of port D drives row #1 of keypad ROW2 EQU PD3 ; Bit 3 of port D drives row #2 of keypad ROW3 EQU PD4 ; Bit 4 of port D drives row #3 of keypad ROW4 EQU PD5 ; Bit 5 of port D drives row #4 of keypad COL1 EQU PE0 ; Bit 0 of port E picks up column #1 of keypad COL2 EQU PE1 ; Bit 1 of port E picks up column #2 of keypad COL3 EQU PE2 ; Bit 2 of port E picks up column #3 of keypad COL4 EQU PE3 ; Bit 3 of port E picks up column #4 of keypad *------------------------------ * Keypad-related constant data *------------------------------ * We use this table to look up the ASCII code for any given keypad key. table: FCB '1,'2,'3,'A ; The first row of keys is: [1][2][3][A] (Select with port D, bit 2) FCB '4,'5,'6,'B ; The second row of keys is: [4][5][6][B] (Select with port D, bit 3) FCB '7,'8,'9,'C ; The third row of keys is: [7][8][9][C] (Select with port D, bit 4) FCB '*,'0,'#,'D ; The fourth row of keys is: [*][0][#][D] (Select with port D, bit 5) * Columns visible in port E, bits: 0 1 2 3 *------------------------------------------------------------------------------ * Initialize keypad. Sets up keypad rows 1-4 (port D, bits 2-5) as output pins * from the HC11 so we can send row-select signals to them, while leaving TxD * (also in port D) also still an output, so we can still use the SCI port at * the same time. *------------------------------------------------------------------------------ init_keypad: LDAA #ROW1|ROW2|ROW3|ROW4|TxD ; Select TxD and all keypad rows. STAA DDRD ; Set them as outputs in the port D data direction register. RTS ; That was easy. *------------------------------------------------------------------------------- * Keypad input subroutine. Idles until a key is pressed, then returns the ASCII * code for the key that was pressed in accA. *------------------------------------------------------------------------------- get_key: JSR any_key ; Is any key pressed? BEQ get_key ; If not, wait till one is. JSR key_scan ; A key was pressed; figure out which one. TSTA ; Whoa, was no key pressed after all? BEQ get_key ; If so, then it's a false alarm; just wait again. PSHA ; Preserve key value. JSR delay ; Wait for a little while, to avoid key bounce. JSR key_scan ; Scan keys again. PULB ; Get previous key into accB. CBA ; Is the same key still pressed? BNE get_key ; If not, it's a glitch; ignore it & wait again. RTS ; A valid keypress occurred, return the code in A. *------------------------------------------------------------ * See if any key is pressed at all. Doesn't determine which * key was pressed. If a key is pressed, A is nonzero (and * the bits in A denote which columns had keys pressed in them). *------------------------------------------------------------ any_key: LDX #PORTD ; Let X be a pointer to port D LDAA #ROW1|ROW2|ROW3|ROW4 ; Select all keypad rows at once. * No RTS here because we just want to fall through into the next subroutine. *-------------------------------------------------------------------- * Check the keypad rows selected in accA to see which columns have * keys pressed in those rows. Columns with pressed keys are returned * in accA. When called, X should contain a pointer to PORTD. *-------------------------------------------------------------------- key_check: STAA 0,X ; Turn on all of the selected rows. NOP ; Delay 2 cycles. (Wait for RC/buffer delay?) NOP ; Delay 2 more cycles to be sure. LDAA PORTE ; See which keypad columns in PORTE have keys pressed in them. ANDA #COL1|COL2|COL3|COL4 ; We're only interested in these bits of PORTE. RTS ; Return accA value to caller. *------------------------------------------------------------------ * Figure out which key is being pressed. This is accomplished by * activating the rows one at a time, and seeing which column(s) in * the given row have keys pressed in them. If multiple keys are * depressed at the same time, this just returns the first one. *------------------------------------------------------------------ key_scan: CLRB ; accB = key index in table, initially 0. LDAA #ROW1 ; Select row #1 JSR key_check ; See which keys are being pressed in that row. BNE key_row ; If any, now figure out the column. ADDB #4 ; Else, move B down to row #2 in key table. LDAA #ROW2 ; Select row #2 JSR key_check ; See which keys are being pressed in that row. BNE key_row ; If any, now figure out the column. ADDB #4 ; Else, move B down to row #3 in key table. LDAA #ROW3 ; Select row #3 JSR key_check ; See which keys are being pressed in that row. BNE key_row ; If any, now figure out the column. ADDB #4 ; Else, move B down to row #4 in key table. LDAA #ROW4 ; Select row #4 JSR key_check ; See which keys are being pressed in that row. BEQ no_key ; If no key was found, return a null result. * At this point, we have converged on the correct row, and now we just need * to determine the column number. We simply shift the column bits out to the * right until we come across one that's a 1; then we know we're in the right column. key_row: LSRA ; Shifts column 1 bit out to the carry bit. BCS key_done ; If carry is set, we're in the correct column! INCB ; Else, move table index over to column 2. LSRA ; Shifts column 2 bit out to the carry bit. BCS key_done ; If carry is set, we're in the correct column! INCB ; Else, move table index over to column 3. LSRA ; Shifts column 3 bit out to the carry bit. BCS key_done ; If carry is set, we're in the correct column! INCB ; Else, move table index over to column 4. LSRA ; Shifts column 4 bit out to the carry bit. BCC no_key ; If carry bit is clear, no key was pressed! (This should never happen.) * At this point, accB has zeroed in on the correct index into the keypad table. * We just need to look up the key code and return it. key_done: PSHX ; Preserve X register so we don't stomp it. CLRA ; Clear MSB of D; lets D = table index. ADDD #TABLE ; Add the base address of the lookup table into D. XGDX ; Transfer the table pointer into the X register. LDAA 0,X ; Fetch the character code from the selected table entry. PULX ; Rescue old X value from oblivion. RTS ; Return with key's ASCII code in accA. * If we get here, then no keys were pressed at all; return ASCII code 0 (null) to indicate that. no_key: CLRA ; No key was pressed; clear accA to a null code. RTS ; Return that to our caller. *--------------------------------------------------------------------------- * Delay for approximately 60 ms; this is used after receiving key presses * to make sure the key is really solidly pressed rather than just bouncing. *--------------------------------------------------------------------------- delay: PSHX ; Be polite to caller; preserve his X register value. LDX #20000 ; 20,000 iterations, 6 cycles each, 0.5us/cycle = 60 ms JSR dloop ; Call our all-purpose delay loop. PULX ; Restore X index register (now 0) to its former glory. RTS ; Return to caller.