|
Getting an input from a user may seem trivial, but in real word it will lead to resolve some problems like switch bounce, input correction, input validation, polling function in each major loop of the program, and so on...
Here is a very convenient way to solve them : this interrupt-based keypad library will do it for you. |
Page index :
Why another PIC Keypad LIBRARY ?
There are already tons of keypad PIC libraries on the web, but I think this one will be the most universal and versatile, featuring :
- keypad up to 8x8 or buttons up to 8
- fully configurable I/O :
- user defined ports
- in keypad mode row port and column port may be different
- user defined bits in port
- user defined rows and columns number
- user defined character lookup table
- user defined erase and enter keys
- works on interrupt : no polling necesary, no key lost
- auto debounce, delay is user defined
- switchable typematic (auto-repeat) mode at run time, rate is user defined
- switchable linear/circular buffer mode at run time
- input is available at any time in a string buffer
- maximum input size is user defined
- other functions :
- last key entered
- keypad status...
Learn more below :
Library Functions
PROTOTYPE |
void kp_init() |
PARAMETERS |
none |
RETURNS |
nothing |
DESCRIPTION |
prepare the PIC keypad and button library. this function must be called before enabling timer interrupt. |
REQUIRES |
the file keypad_cfg.h must be set accordingly to user's hardware (see below) |
EXAMPLE |
kp_init() ; |
PROTOTYPE |
unsigned char kp_full() |
PARAMETERS |
none |
RETURNS |
return > 0 if the buffer is full, 0 otherwise |
DESCRIPTION |
check for buffer full |
REQUIRES |
kp_init() must have been called and timer interrupt must be enabled |
EXAMPLE |
if(kp_full()) return ; // return if buffer full |
PROTOTYPE |
unsigned char kp_hit() |
PARAMETERS |
none |
RETURNS |
return > 0 if a key is being pressed, 0 otherwise (pseudo function) |
DESCRIPTION |
check for a key hit |
REQUIRES |
kp_init() must have been called and timer interrupt must be enabled |
EXAMPLE |
while(kp_hit()) ; // wait for key to be released |
PROTOTYPE |
unsigned char kp_enter() |
PARAMETERS |
none |
RETURNS |
return > 0 if enter key has been pressed, 0 otherwise (pseudo function) |
DESCRIPTION |
check for enter key |
REQUIRES |
kp_init() must have been called and timer interrupt must be enabled |
EXAMPLE |
if(kp_enter()) { ... } // do input treatment on enter key |
PROTOTYPE |
unsigned char kp_erase() |
PARAMETERS |
none |
RETURNS |
return > 0 if last key was erase , 0 otherwise (pseudo function) |
DESCRIPTION |
check for erase key |
REQUIRES |
kp_init() must have been called and timer interrupt must be enabled |
EXAMPLE |
if(kp_erase()) { ... } // do input treatment on erase key |
PROTOTYPE |
unsigned char kp_last() |
PARAMETERS |
none |
RETURNS |
return last key pressed |
DESCRIPTION |
get last key |
REQUIRES |
kp_init() must have been called and timer interrupt must be enabled |
EXAMPLE |
switch(kp_last()) { ... } // process keys |
PROTOTYPE |
void kp_setCircular() |
PARAMETERS |
none |
RETURNS |
nothing |
DESCRIPTION |
switch on circular mode on input buffer : when buffer is full, new entry will cause the buffer to be left shifted (character at first position will be lost) new entry is then added to the end of the buffer. |
REQUIRES |
kp_init() must have been called and timer interrupt must be enabled |
EXAMPLE |
kp_setCircular() ; |
PROTOTYPE |
void kp_setLinear() |
PARAMETERS |
none |
RETURNS |
nothing |
DESCRIPTION |
switch on linear mode on input buffer : when buffer is full, new entry will be ignored. |
REQUIRES |
kp_init() must have been called and timer interrupt must be enabled |
EXAMPLE |
kp_setLinear() ; |
PROTOTYPE |
unsigned char kp_circular() |
PARAMETERS |
none |
RETURNS |
return > 0 if input buffer is in circular mode, 0 otherwise. |
DESCRIPTION |
check for buffer circular mode, default is linear mode. |
REQUIRES |
kp_init() must have been called and timer interrupt must be enabled |
EXAMPLE |
if(kp_circular()) kp_setLinear() ; |
PROTOTYPE |
void kp_setTypematic() |
PARAMETERS |
none |
RETURNS |
nothing |
DESCRIPTION |
switch on typematic (auto-repeat) mode |
REQUIRES |
kp_init() must have been called and timer interrupt must be enabled |
EXAMPLE |
kp_setTypematic() ; |
PROTOTYPE |
void kp_unsetTypematic() |
PARAMETERS |
none |
RETURNS |
nothing |
DESCRIPTION |
switch off typematic (auto-repeat) mode |
REQUIRES |
kp_init() must have been called and timer interrupt must be enabled |
EXAMPLE |
kp_unsetTypematic() ; |
PROTOTYPE |
unsigned char kp_typematic() |
PARAMETERS |
none |
RETURNS |
return > 0 if typematic (auto-repeat) in enable, 0 otherwise. |
DESCRIPTION |
return typematic state, default is not enabled. |
REQUIRES |
kp_init() must have been called and timer interrupt must be enabled |
EXAMPLE |
if(kp_typematic()) kp_unsetTypematic() ; |
PROTOTYPE |
void kp_flush() |
PARAMETERS |
none |
RETURNS |
nothing |
DESCRIPTION |
flush input, buffer is cleared |
REQUIRES |
kp_init() must have been called and timer interrupt must be enabled |
EXAMPLE |
kp_flush() ; |
PROTOTYPE |
void kp_isr() |
PARAMETERS |
none |
RETURNS |
nothing |
DESCRIPTION |
this function must not be called directly by user, but must be placed in the timer interrupt() function.
the input is stored in buffer kp_buf, defined as a pointer to char. |
REQUIRES |
kp_init() must have been called |
EXAMPLE |
void interrupt(void) { if(INTCON.T0IF) // timer 0 overflow ? { kp_isr() ; // call keypad service routine
INTCON.T0IF = 0 ; // done } } |
Configuration
Keypad configuration is defined in file keypad_cfg.h, you can change it depending on your hardware and preferences :
SYMBOL |
USAGE |
EXAMPLE |
KEYPAD_MODE |
define this symbol if you are using a keypad (rows and colums) do not define this symbol if you are using buttons (colums only) |
#define KEYPAD_MODE
|
KP_ROW_PORT |
PORT where rows are connected to (only if KEYPAD_MODE is defined) |
#define KP_ROW_PORT PORTB
|
KP_ROW_TRIS |
TRIS direction register corresponding to KP_ROW_PORT (only if KEYPAD_MODE is defined) |
#define KP_ROW_TRIS TRISB
|
KP_ROW_BITNUM |
bit number of the first row in the PORT, other bits must be next and contiguous (only if KEYPAD_MODE is defined) |
#define KP_ROW_BITNUM 0
|
KP_ROW_NUMBER |
number of rows (only if KEYPAD_MODE is defined) |
#define KP_ROW_NUMBER 3 |
KP_COL_PORT |
PORT where columns are connected to. Each column input must be pulled down with a 10 K resistor. |
#define KP_COL_PORT PORTB
|
KP_COL_TRIS |
TRIS direction register corresponding to KP_COL_PORT |
#define KP_COL_TRIS TRISB |
KP_COL_BITNUM |
bit number of the first row in the PORT, other bits must be next and contiguous |
#define KP_COL_BITNUM 3 |
KP_COL_NUMBER |
number of columns |
#define KP_COL_NUMBER 4 |
KP_SCAN_CODES |
scan codes lookup table keypad must be considered this way :
the top left key is the first one, the bottom right key is the last one. you have to read from top to bottom and from left to right : * 7 4 1 0 8 5 2 # 9 6 3 |
#define KP_SCAN_CODES "*7410852#963" |
KP_ERASE |
erase key, must be one in KP_SCAN_CODES string don't define it if you don't need an erase key |
#define KP_ERASE '*' |
KP_ENTER |
enter key, must be one in KP_SCAN_CODES string don't define it if you don't need an enter key |
#define KP_ENTER '#' |
KP_MAX_LENGTH |
maximum input length |
#define KP_MAX_LENGTH 16 |
KP_TMR_REPEAT |
typematic rate, must be set depending on timer overflow period and user's preferences. you can use this formula : KP_TMR_REPEAT = (Fosc / 4 / TMR_PRESCALER / (2 ^ TMR_BITS)) * DELAY_REPEAT where : Fosc is the frequency of the oscillator in Herz. For example if you are using a 8 Mhz crystal with HS clock mode, then Fosc = 8000000 TMR_PRESCALER is the prescaler (or postscaler if any) value of your timer. For example if you are using a 1:1 prescaler then TMR_PRESCALER = 1 TMR_BITS is the number of bits of your timer, it is either 8 or 16 DELAY_REPEAT is the delay in second before the next auto repeat.
when using a PIC16F877A @ 8 Mhz with 8 bit TMR0 and no prescaler, if you want an auto repeat rate of 300 ms, use : KP_TMR_REPEAT = (8000000 / 4 / 1 / (2 ^ 8)) * .3 = 2343 |
#define KP_TMR_REPEAT 2343 |
KP_TMR_DEBOUNCE |
debounce time, must be set depending on timer overflow period and user's preferences. you can use this formula : KP_TMR_DEBOUNCE = (Fosc / 4 / TMR_PRESCALER / (2 ^ TMR_BITS)) * DEBOUNCE_DELAY where : Fosc is the frequency of the oscillator in Herz. For example if you are using a 8 Mhz crystal with HS clock mode, then Fosc = 8000000 TMR_PRESCALER is the prescaler (or postscaler if any) value of your timer. For example if you are using a 1:1 prescaler then TMR_PRESCALER = 1 TMR_BITS is the number of bits of your timer, it is either 8 or 16 DEBOUNCE_DELAY is the delay in second before the keyboard to be read again.
when using a PIC16F877A @ 8 Mhz with 8 bit TMR0 and no prescaler, if you want a debounce time of 30 ms, use : KP_TMR_DEBOUNCE = (8000000 / 4 / 1 / (2 ^ 8)) * .03 = 234 |
#define KP_TMR_DEBOUNCE 234 |
KP_MAX_LENGTH |
maximum input length |
#define KP_MAX_LENGTH 16 |
CIRCUIT EXAMPLE
Click on the picture to enlarge |
Columns must be pulled down, use resistors from 4K7 to 22K.
Don't forget that columns are from bottom to top, and rows for left to right. This arrangement is due to code simplification, because library can work with buttons too.
If you use buttons only instead of keypad, they must also be pulled down. In this case, there is no row but only columns.
|
MIKROC SOURCE CODE EXAMPLE
Here is the mikroC source code of the program example. You have to use the zipped mikroC project to build it.
#include "keypad_lib.h" const char erase_char[] = {2,6,14,31,14,6,2,0} ; const char enter_char[] = {1,1,5,13,31,12,4,0} ; const char full_char[] = {0,10,21,10,21,10,0,0} ; const char hit_char[] = {0,4,4,4,31,14,4,0} ; const char circ_char[] = {16,24,28,25,19,7,3,1} ; const char type_char[] = {31,21,21,4,4,4,4,14} ; void CustomChar(const char *def, unsigned char n, char pos_row, char pos_char)
{
char i ;
LCD_Cmd(64 + n * 8) ;
for(i = 0 ; i <= 7 ; i++)
{
LCD_Chr_Cp(def[i]) ;
}
LCD_Cmd(LCD_RETURN_HOME) ;
LCD_Chr(pos_row, pos_char, n) ;
}
void interrupt(void)
{
if(INTCON.T0IF) {
kp_isr() ; INTCON.T0IF = 0 ; }
}
void main()
{
LCD_Init(&PORTD) ;
LCD_Cmd(LCD_CLEAR) ;
LCD_Out(1, 1, "KeypadLib") ;
LCD_Cmd(LCD_SECOND_ROW) ;
kp_init() ;
OPTION_REG = 0x80 ; INTCON = 0xA0 ; for(;;) {
if(kp_hit()) {
LCD_Cmd(LCD_CURSOR_OFF) ; CustomChar(hit_char, 1, 1, 12) ; if(kp_enter()) {
CustomChar(enter_char, 2, 1, 13) ;
if(strcmp(kp_buf, "123") == 0) {
if(kp_circular()) {
kp_setLinear() ;
}
else
{
kp_setCircular() ;
}
}
else if(strcmp(kp_buf, "321") == 0) {
if(kp_typematic()) {
kp_unsetTypematic() ;
}
else
{
kp_setTypematic() ;
}
}
kp_flush() ; }
else
{
LCD_Chr(1, 13, ' ') ;
}
if(kp_full())
{
CustomChar(full_char, 0, 1, 11) ;
}
else
{
LCD_Chr(1, 11, ' ') ;
}
if(kp_erase())
{
CustomChar(erase_char, 3, 1, 14) ;
}
else
{
LCD_Chr(1, 14, ' ') ;
}
if(kp_circular())
{
CustomChar(circ_char, 4, 1, 15) ;
}
else
{
LCD_Chr(1, 15, ' ') ;
}
if(kp_typematic())
{
CustomChar(type_char, 5, 1, 10) ;
}
else
{
LCD_Chr(1, 10, ' ') ;
}
LCD_Chr(1, 16, kp_last()) ; LCD_Chr(1, 12, ' ') ; LCD_Out(2, 1, " ") ; LCD_Out(2, 1, kp_buf) ; LCD_Cmd(LCD_BLINK_CURSOR_ON) ; }
}
}
|
PROJECT DOWNLOAD
You can use this software as you wish, if you accept to do it at your own risks.
Download PIC Keypad Library C source code with demo example for mikroC : zipped file, 6 Ko
content of the archive :
- keypad_lib.c, 10 Ko : library C source code
- keypad_lib.h, 3 Ko : library C definitions
- keypad_cfg.h, 2 Ko : user's configuration
- keypad.ppc, 1 Ko : mikroC project file
- keypad.c, 8 Ko : demo example for PIC16F877A
You can get mikroC from here : http://www.mikroe.com/en/compilers/mikroc/pic/
Please report any bug, comment or suggestion in my forums. Thanks !
|