|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Did you know...
|
Serial Wombat Matrix Encoded Keypad Driver
Scanning a keypad with your Serial Wombat is easy! Just follow these simple steps:
Keypresses from the keypad are presented as the first row pin's public data. The public data can be configured to hold the last key pressed, the current key pressed (includes a code for no key pressed) or a bitmap of the first 16 keys of the keypad (1 = pressed, 0 = not pressed). Key numbers start at 0 for first-row-first-column, 1 for first-row-second-column, and so on. For example, in the picture above, the 'C' key would be key number 11. The Serial Wombat is also capable of placing keypress events in a queue. This allows the host to only query occasionally for queued information, without having to worry about missing keypresses. Data can be queued as key numbers (starting at 0x00)when a new key-press event is detected, or as ASCII text (0-9, A-Z) when a new key-press event is detected. If multiple simultaneous keypresses need to be detected, then a 16-bit bitmap can be stored which indicates each change in the first 16 keys. The software supports up to eight columns by eight rows, although only up to four columns and rows have currently been tested, due to unavailablity of larger keypads. If you know of a resonably-priced matrix encoded keypad larger than 4x4, please contact me. I anticipate creating an 8x8 keypad using 4 4x4 pads soon. When configuring the Wombat, the user provides the Wombat pin number of the first column and first row pins. Additional column and row pins are assumed to be the next pins in counting order. For instance, if a 4x4 keypad is connected with row 1 of 4 connected to Wombat pin 7, then the remaining rows are assumed to be attached to Wombat pins 8, 9, and 10. However, certain pins are likely to be used for some special purpose, such as the hardware capture/PWM pins, the SPI capable pins, the RX and TX pins, Oscillator pins, the Boot Mode pin, and, of course, the chip Power Pins. Therefore, the Wombat skips these pins when counting. For example, if pin 13 were the first column pin, then the next three pins would be 14, 15, and 19. (16 and 17 are skipped because they are hardware PWM pins, 18 is skipped because it's the SPI clock line). The following is a list of pins which can be used for the keypad, in ascending order (note that the number of A/D channels may have to be configured to allow pins to be used as digital I/O). Row pins should be directly connected to the Wombat. Column pins require a pull-up resistor to +5V. If pins 33 through 40 are used for the column rows, then pull-up resistors may be omitted, as these pins have internal pull-ups inside the Wombat. Possible Wombat Keypad Pins:2, 3, 4, 5, 7, 8, 9, 10, 15, 19, 20, 21, 22, 27, 28, 29, 30, 33, 34, 35, 36, 37, 38, 39, 40 Message format:Note! Only the first row pin should be configured for Matrix keypad mode. All other pins attached to the keypad will be automatically configured to controlled mode
Example 1 queue keypresses from a 4x4 matrix encoded keypad:
A 4x4 matrix-encoded keypad is to be scanned by the Wombat so that keypresses can be reported back to the host. We will attach the keypad rows to 21, 22, 27 and 28 (these pins are logically "next to each other" in the list above). We'll attach the columns to pins 33, 34, 35 and 36 since these pins have internal pull-up resistors. We'll define a 32 byte queue at user address 0x50, and have the keypad driver put key presses in that queue. The host can then periodically check the queue to see if any new keypresses are in it. Initialization: 0x80 0x0050 0x0020 0x00 0x55 0x55
; Initialize a queue
; At address 0x50 (0x0050)
; 32 bytes long (0x0020)
; Standard queue type (0x00)
200 21 8 33 0x0050 1 0 ; Configure pin 21 to the first row of the keypad
; Pin 33 is the first column of the keypad
; Queue address is 0x0050
; Queue the key number
; Display a 16 key bitmap as public data
201 21 8 4 4 0x55 0x55 0x55; Configure pin 21 to the first row of the keypad
; 4 rows
; 4 Columns
Checking for Keypresses: Sent: 0x82 0x0050 6 0x55 0x55 0x55 0x55
; Get up to 6 bytes from queue at address 0x0050
Response (one possible example): 0x82 2 11 2 0x55 0x55 0x55 0x55
; Two bytes were retreived from the queue
; Button 11 was pressed and released
; Button 2 was pressed and released
Pin Mode Source CodeThe following code is what creates the pin mode for this function inside of the Wombat. You don't need to know this to use the pin mode, but some people find it interesting. See the SDK for some insight into how pin modes work. Hold your mouse over various words and variables for definitions. #ifndef COMPILING_FIRMWARE#include <stdio.h> #endif #include "types.h" #include "utilities.h" #include "global_data.h" #include "kp.h" #pragma code APPLICATION //RX // 0 - CONFIGURE D I/O // 1 - PIN NUMBER // 2 - Mode - Keypad Scan Row 0 // 3 - Pin # for Column 1 // 4 - QUEUE ADDRESS (H) // 5 - (L) // 6 - Scan Delay States // 7 - Mode : 0x0F = 0: queue nothing // 1: queue button push numbers, binary // 2: queue button push numbers, Ascii // 3: queue bitmap changes // 0xF0 // 2: Buffer = last button pressed, held // 1: Buffer = current button number, 255 for none // 0: Buffer = current bitmap, // Registers // This Pin: // R0 - Pin for Column 1 of 4 // R1 - QUEUE ADDRESS (H) // R2 - (L) // R3 - Current Reading Bitmap (High) // R4 - Current Reading Bitmap (Low) // R5 - Previous Reading Bitmap (H) // R6 - Previous Reading Bitmap (L) // R7 - Current State // This pin + 1: // R0 - Currently pressed button 0-15, or 255 for none #define NO_KEY_PRESSED 0xFF // R1 - Previously pressed button // R2 - Scan Delay States // R3 - Mode // R4 - Current Delay #define KEYPAD_STATE_SETUP_ALL_LOW 0 #define KEYPAD_STATE_WAIT_FOR_PRESS 1 #define KEYPAD_STATE_SETUP_ROW_1 2 #define KEYPAD_STATE_SETUP_ROW_1_WAIT 3 #define KEYPAD_STATE_READ_ROW_1 4 #define KEYPAD_STATE_SETUP_ROW_2 5 #define KEYPAD_STATE_SETUP_ROW_2_WAIT 6 #define KEYPAD_STATE_READ_ROW_2 7 #define KEYPAD_STATE_SETUP_ROW_3 8 #define KEYPAD_STATE_SETUP_ROW_3_WAIT 9 #define KEYPAD_STATE_READ_ROW_3 10 #define KEYPAD_STATE_SETUP_ROW_4 11 #define KEYPAD_STATE_SETUP_ROW_4_WAIT 12 #define KEYPAD_STATE_READ_ROW_4 13 #define KEYPAD_STATE_DELAY 14 #define KEYPAD_STATE_IDLE 15 #define KEYPAD_BUFFER_MODE_BINARY 0 #define KEYPAD_BUFFER_MODE_LAST_BUTTON 1 #define KEYPAD_BUFFER_MODE_CURRENT_BUTTON 2 #define KEYPAD_QUEUE_MODE_NONE 0 #define KEYPAD_QUEUE_MODE_BUTTON_DOWN 1 #define KEYPAD_QUEUE_MODE_BUTTON_ASCII 2 #define KEYPAD_QUEUE_MODE_BINARY_CHANGE 3 void keypad_init (void) { local_j = map_pin(map_next_physical_pin[rxbuffer[1]]); get_tp2((local_j); if (rxbuffer[0] == CONFIGURE_CHANNEL_MODE_0) { tp.keypad16.column_pin = rxbuffer[3]; tp.keypad16.queue = RXBUFFER16(4); tp2.keypad16.queue_mode = rxbuffer[6]; tp2.keypad16.buffer_mode = rxbuffer[7]; tp.keypad16.current_reading = 0; tp.keypad16.previous_reading= 0; tp.keypad16.state= KEYPAD_STATE_IDLE; tp2.keypad16.current_button = NO_KEY_PRESSED; tp2.keypad16.previous_button = NO_KEY_PRESSED; } else if (rxbuffer[0] == CONFIGURE_CHANNEL_MODE_1) { tp2.keypad16.rows = rxbuffer[3]; tp2.keypad16.columns = rxbuffer[4]; local_j = rxbuffer[1]; for (local_i = 0; local_i < rxbuffer[3]; ++local_i) { local_k = map_pin(local_j); set_pin(local_k,INPUT); set_mode(local_k, PIN_MODE_CONTROLLED); local_j = map_next_physical_pin[local_j]; } local_j = tp.keypad16.column_pin; for (local_i = 0; local_i < rxbuffer[4]; ++local_i) { local_k = map_pin(local_j); set_pin(local_k,INPUT); set_mode(local_k, PIN_MODE_CONTROLLED); local_j = map_next_physical_pin[local_j]; } tp.keypad16.state = KEYPAD_STATE_SETUP_ALL_LOW; } local_j = map_pin(map_next_physical_pin[rxbuffer[1]]); put_tp2((local_j); } void keypad_process(void) { uint8 temp8_1; uint16 temp1_16; if (tp.keypad16.state == KEYPAD_STATE_IDLE) { return; } local_i = unmap_pin(virtual_pin); local_j = map_pin(map_next_physical_pin[local_i]); get_tp2((local_j); switch (tp.keypad16.state) { case KEYPAD_STATE_SETUP_ROW_1_WAIT: tp.keypad16.state = KEYPAD_STATE_READ_ROW_1; break; case KEYPAD_STATE_SETUP_ALL_LOW: default: { local_j = unmap_pin(virtual_pin); for (local_i = 0; local_i < tp2.keypad16.rows; ++local_i) { set_pin(map_pin(local_j ), LOW); local_j = map_next_physical_pin[local_j]; } tp.keypad16.state = KEYPAD_STATE_WAIT_FOR_PRESS; } break; case KEYPAD_STATE_WAIT_FOR_PRESS: { tp2.keypad16.testing_button = 0; tp.keypad16.current_reading = 0; //Current bitmap = 0 tp2.keypad16.current_button = NO_KEY_PRESSED; //No key pressed tp2.keypad16.current_row = 0; local_j = tp.keypad16.column_pin; for (local_i = 0; local_i < tp2.keypad16.columns; ++ local_i) { if (! read_pin( map_pin(local_j) )) { tp.keypad16.state = KEYPAD_STATE_SETUP_ROW_1; } local_j = map_next_physical_pin[local_j]; } if (tp.keypad16.state != KEYPAD_STATE_SETUP_ROW_1) { tp.keypad16.state = KEYPAD_STATE_DELAY; } } break; case KEYPAD_STATE_SETUP_ROW_1: case KEYPAD_STATE_SETUP_ROW_2: case KEYPAD_STATE_SETUP_ROW_3: case KEYPAD_STATE_SETUP_ROW_4: local_j = unmap_pin(virtual_pin); for (local_i = 0; local_i < tp2.keypad16.rows; ++local_i) { if (local_i == tp2.keypad16.current_row) { set_pin(map_pin(local_j ), LOW); } else { set_pin(map_pin(local_j ), INPUT); } local_j = map_next_physical_pin[local_j]; } ++tp2.keypad16.current_row; tp.keypad16.state = KEYPAD_STATE_SETUP_ROW_1_WAIT; break; case KEYPAD_STATE_READ_ROW_1: case KEYPAD_STATE_READ_ROW_2: case KEYPAD_STATE_READ_ROW_3: case KEYPAD_STATE_READ_ROW_4: local_j = tp.keypad16.column_pin; for (local_k = 0; local_k < tp2.keypad16.columns; ++ local_k) { local_i = map_pin(local_j); if (!read_pin(local_i)) { tp2.keypad16.current_button = tp2.keypad16.testing_button; if (tp2.keypad16.testing_button < 16) { tp.keypad16.current_reading |= uint16_bitfield[tp2.keypad16.testing_button]; } } local_j = map_next_physical_pin[local_j]; ++tp2.keypad16.testing_button; } if (tp2.keypad16.current_row == tp2.keypad16.rows) { tp.keypad16.state = KEYPAD_STATE_DELAY; } else { tp.keypad16.state = KEYPAD_STATE_SETUP_ROW_1; } break; case KEYPAD_STATE_DELAY: queue_address = tp.keypad16.queue; if (tp2.keypad16.buffer_mode == KEYPAD_BUFFER_MODE_CURRENT_BUTTON) { tp.generic.buffer = tp2.keypad16.current_button; } else if (tp2.keypad16.buffer_mode == KEYPAD_BUFFER_MODE_LAST_BUTTON && tp2.keypad16.current_button != NO_KEY_PRESSED) { tp.generic.buffer = tp2.keypad16.current_button; } else if (tp2.keypad16.buffer_mode == KEYPAD_BUFFER_MODE_BINARY) { tp.generic.buffer = tp.keypad16.current_reading; } if (tp2.keypad16.queue_mode == KEYPAD_QUEUE_MODE_BUTTON_DOWN) { if (tp2.keypad16.current_button != NO_KEY_PRESSED && tp2.keypad16.previous_button == NO_KEY_PRESSED) { push_byte(tp2.keypad16.current_button); } } if (tp2.keypad16.queue_mode == 2) { if (tp2.keypad16.current_button != NO_KEY_PRESSED && tp2.keypad16.previous_button == NO_KEY_PRESSED) { push_byte(val_to_ascii(tp2.keypad16.current_button)); } } else if (tp2.keypad16.queue_mode == 3) { if ( tp.keypad16.current_reading != tp.keypad16.previous_reading) { push_word(tp.keypad16.current_reading); } } if (tp.keypad16.current_reading != tp.keypad16.previous_reading) { tp.keypad16.previous_reading = tp.keypad16.current_reading; } if (tp2.keypad16.current_button != tp2.keypad16.previous_button) { tp2.keypad16.previous_button = tp2.keypad16.current_button; } tp.keypad16.state = KEYPAD_STATE_SETUP_ALL_LOW; break; case KEYPAD_STATE_IDLE: break; } // end keypad switch local_i = map_pin(map_next_physical_pin[unmap_pin(virtual_pin) ]); put_tp2((local_i); } #ifdef TEST_KP #define QUEUE_ADDR 0 #define QUEUE_LENGTH 20 #define ROW_PIN_1 33 #define COLUMN_PIN_1 37 void kp_test_update(void); int test_key = 0xff; int main(void) { int i,j; uint8 result; int overall_result = 0; printf ("Keypad test\n"); system_init(); rxbuffer[0] = 128; rxbuffer[1] = (uint8) (QUEUE_ADDR / 256); rxbuffer[2] = (uint8) (QUEUE_ADDR % 256); rxbuffer[3] = (uint8) (QUEUE_LENGTH / 256); rxbuffer[4] = (uint8) (QUEUE_LENGTH % 256); rxbuffer[5] = RAM_QUEUE; process_rxbuffer(); rxbuffer[0] = CONFIGURE_CHANNEL_MODE_0; rxbuffer[1] = ROW_PIN_1; rxbuffer[2] = PIN_MODE_KEYPAD_SCAN_ROW; rxbuffer[3] = COLUMN_PIN_1; rxbuffer[4] = (uint8)(QUEUE_ADDR / 256); rxbuffer[5] = (uint8)(QUEUE_ADDR % 256); rxbuffer[6] = KEYPAD_QUEUE_MODE_BUTTON_DOWN; rxbuffer[7] = KEYPAD_BUFFER_MODE_CURRENT_BUTTON; process_rxbuffer(); rxbuffer[0] = CONFIGURE_CHANNEL_MODE_1; rxbuffer[1] = ROW_PIN_1; rxbuffer[2] = PIN_MODE_KEYPAD_SCAN_ROW; rxbuffer[3] = 4; // 4 rows rxbuffer[4] = 4; // 4 columns rxbuffer[5] = (uint8)(QUEUE_ADDR % 256); rxbuffer[6] = KEYPAD_QUEUE_MODE_BUTTON_DOWN; rxbuffer[7] = KEYPAD_BUFFER_MODE_CURRENT_BUTTON; process_rxbuffer(); register_update_func(kp_test_update); for (j = 0; j < 17; ++ j) { test_key = j; for (i = 0; i < 50; ++i) { process_pins(); } test_key = NO_KEY_PRESSED; for (i = 0; i < 50; ++i) { process_pins(); } } queue_address = QUEUE_ADDR; for (i = 0 ; i < 17; ++i) { if (shift_byte(&result) == QUEUE_BYTE_SHIFTED) { if (i == result) { printf ("KP Test: Success!\n"); } else { printf("Expected %d, got %d\n",i,result); overall_result = 1; } } else { if (i != 16) { printf("KP TEST: Couldn't shift byte\n"); overall_result = 1; } } } return (overall_result); } void kp_test_update(void) { int r; int c; uint8 rp; uint8 cp; for (c = 0; c < 4; ++c) { cp = map_pin(COLUMN_PIN_1 + c); pin_high(cp); } for (r = 0; r < 4; ++r) { rp = map_pin(ROW_PIN_1 + r); for (c = 0; c < 4; ++c) { cp = map_pin(COLUMN_PIN_1 + c); if (((r * 4 + c) == test_key) && read_pin(rp) == LOW) { pin_low(map_pin(cp)); } } } } #endif |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Copyright Wombat Interface Products, 2005-2008. All Rights Reserved.