Serial Wombat
a general-purpose digital interface device for hobbyists, engineers and students

 


Home
Overview
Protocol
Documentation
Channel Modes
Direct Control
Controlled Pin
Raw A/D
A/D Averaging
A/D 1st Order Filtering
Matrix Keypad
Servo Control
Analog Direct
Analog Follow
Rotary Encoder
Debouncing
Counter
Hysteresis
Morse Code
Pulse
Unipolar Stepper
LCD Driver 1
LCD Driver 2
HW Pulse Meas.
2D Lookup
SPI Master
HD44780 Generic
Remote Control
DataLogger
Min-Max
Public Data
Timed IO
Getting Started
Connectivity
Pin Mode SDK Beta
Sample Projects
Downloads
Contact Us
Purchase
Forum

Did you know...

 

Serial Wombat Matrix Encoded Keypad Driver

The Serial Wombat can scan matrix encoded keypads of many different sizes. Support for 2x2, 2x3, 2x4, 3x3,3x4 and 4x4 matrix encoded keypads has been tested. Support for bigger pads, such as 5x5, 6x6, 7x7, 8x8, 9x9 and 10x10 should work as well. However, I have not been able to locate pads this large to test with, so I am currently unable to guarantee this functionality.

Scanning a keypad with your Serial Wombat is easy! Just follow these simple steps:

  • Connect the column pins on the keypad to pins on the Wombat
  • Place pull-up resistors on the column pins if the chosen column pins don't have internal pull-ups
  • Connect the row pins on the keypad to pins on the Wombat
  • Send Two Commands to the Wombat which include the Wombat pins of the 1st column and row pins on the keypad, and the format in which keypresses should be reported or recorded.

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

Data Sent to the Wombat: 200 Pin # 8 Column Pin Queue Address MSB Queue Address LSB Queue Mode Public Data Mode
Meaning: Configure Pin First message Wombat Pin Attached to First keypad row Matrix Keypad Mode The Wombat pin # which is connected to the first column The 16-bit address of the queue into which keypress information is pressed (fill with 0xFFFF if no queue is used) Data to be queued:

0: None
1: Key #
2: Key Ascii
3: Bitmap

Data to be displayed:

0: Bitmap of first 16 keys
1: Last button pressed:
2: Current button pressed( 0xFF = NONE)

This message is echoed back by the Wombat.

 

Data Sent to the Wombat: 201 Pin # 8 Number of Rows Number of Columns 0x55 0x55 0x55
Meaning: Configure Pin Second message Wombat Pin Attached to First keypad row Matrix Keypad Mode Valid values are 2 through 10 Valid values are 2 through 10 Unused Unused Unused
This message is echoed back by the Wombat.

 

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:
Every time a key is released, it will be added to the queue. Data can be read from a queue using the Get Queue Data command. We'll ask for the maximum of 6 bytes per request, although there may be less than that currently in the queue. The second byte of the message tells how many bytes were retreived.

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 Code

The 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.