Interface keypad with PIC16F84a / PIC16F88

Discussion in 'Embedded Systems and Microcontrollers' started by Pinnacle187, Jun 1, 2016.

  1. Pinnacle187

    Thread Starter New Member

    Apr 28, 2016
    13
    0
    Hello, everyone I need a little help with making a Keypad interface with a LCD (16x2) and a PIC16F88 or PIC16F84 (they have same layout). I want to implement a keypad but with a phone keypad function (see image below), also instead of # and * buttons, shift and DELETE. By phone keypad function I mean is that if "2 button" is pressed once a "2" is displayed; BUT when SHIFT is pressed and then press 2-button once an "a" is displayed and twice "b", three times a "c"...
    I have most of the code finished, but i don't know how to add a SHIFT button (along with the function described) and the deleted button.
    I have put the code and the simulation in a winrar file, along with a capture of the circuit.

    MAIN
    Code (Text):
    1. ///THIS IS MAIN CODE
    2.  
    3. /*PIC16F88A interface to 3x4 Keypad and 16x2 LCD
    4.  
    5. */
    6.  
    7. #include <16f88.h>
    8. #fuses XT,NOLVP,NOWDT,NOPROTECT
    9. #use delay(clock=4000000)
    10. #include "flex_lcd.c"
    11. #include <kbd.c>
    12.  
    13. void main()
    14. {
    15.    char k=0;                     // El caracter k se guarda Character k to store keypad input
    16.    delay_ms(1);
    17.    /* Peripherals Configurations */
    18.    lcd_init();                   // Turn LCD ON, along with other initialization commands
    19.    kbd_init();                   // Initialize Keypad
    20.  
    21.    lcd_gotoxy(1,1);              // point LCD cursor to col1 row1
    22.   // lcd_putc(" ");  // print on LCD
    23.    lcd_gotoxy(1,2);              // point LCD cursor to col1 row2
    24.    //lcd_putc(" ");
    25.    while(1) // infinite loop
    26.    {
    27.       k = kbd_getc();            // read keypad input (if exists)
    28.       if(k!=0)                   // if key captured
    29.       {
    30.          lcd_putc(k);            // print captured key to lcd
    31.          k=0;                    // reset k for another loop round
    32.          delay_ms(250);          // delay between key presses
    33.       }
    34.       delay_ms(1);               // delay_between read trials
    35.    }
    36. }
    37.  

    LCD CODE
    Code (Text):
    1. // flex_lcd.c
    2.  
    3. // Para los pines del LCD
    4.  
    5. #define LCD_DB4   PIN_A2
    6. #define LCD_DB5   PIN_A3
    7. #define LCD_DB6   PIN_A4
    8. #define LCD_DB7   PIN_B0
    9.  
    10. #define LCD_E     PIN_A1
    11. #define LCD_RS    PIN_A0
    12. #define LCD_RW    PIN_A1
    13.  
    14. // olny 6-pin interface on LCD, so[ATTACH]107114[/ATTACH] [ATTACH]107114[/ATTACH] [ATTACH]107116[/ATTACH] [ATTACH]107114[/ATTACH] [ATTACH]107116[/ATTACH]
    15. // connect the R/W pin on the LCD to ground, and commented
    16. // out the following line.
    17.  
    18. //#define USE_LCD_RW   1  
    19.  
    20. //========================================
    21.  
    22. #define lcd_type 2        // 0=5x7, 1=5x10, 2=2 lines
    23. #define lcd_line_two 0x40 // LCD RAM address for the 2nd line
    24.  
    25.  
    26. int8 const LCD_INIT_STRING[4] =
    27. {
    28. 0x20 | (lcd_type << 2), // Func set: 4-bit, 2 lines, 5x8 dots
    29. 0xc,                    // Display on
    30. 1,                      // Clear display
    31. 6                       // Increment cursor
    32. };
    33.                            
    34.  
    35. //-------------------------------------
    36. void lcd_send_nibble(int8 nibble)
    37. {
    38. // Note:  !! converts an integer expression
    39. // to a boolean (1 or 0).
    40. output_bit(LCD_DB4, !!(nibble & 1));
    41. output_bit(LCD_DB5, !!(nibble & 2));
    42. output_bit(LCD_DB6, !!(nibble & 4));
    43. output_bit(LCD_DB7, !!(nibble & 8));
    44.  
    45. delay_cycles(1);
    46. output_high(LCD_E);
    47. delay_us(2);
    48. output_low(LCD_E);
    49. }
    50.  
    51. //-----------------------------------
    52. // This sub-routine is only called by lcd_read_byte().
    53. // It's not a stand-alone routine.  For example, the
    54. // R/W signal is set high by lcd_read_byte() before
    55. // this routine is called.  
    56.  
    57. #ifdef USE_LCD_RW
    58. int8 lcd_read_nibble(void)
    59. {
    60. int8 retval;
    61. // Create bit variables so that we can easily set
    62. // individual bits in the retval variable.
    63. #bit retval_0 = retval.0
    64. #bit retval_1 = retval.1
    65. #bit retval_2 = retval.2
    66. #bit retval_3 = retval.3
    67.  
    68. retval = 0;
    69.  
    70. output_high(LCD_E);
    71. delay_cycles(1);
    72.  
    73. retval_0 = input(LCD_DB4);
    74. retval_1 = input(LCD_DB5);
    75. retval_2 = input(LCD_DB6);
    76. retval_3 = input(LCD_DB7);
    77.  
    78. output_low(LCD_E);
    79.  
    80. return(retval);
    81. }
    82. #endif
    83.  
    84. //---------------------------------------
    85. // Read a byte from the LCD and return it.
    86.  
    87. #ifdef USE_LCD_RW
    88. int8 lcd_read_byte(void)
    89. {
    90. int8 low;
    91. int8 high;
    92.  
    93. output_high(LCD_RW);
    94. delay_cycles(1);
    95.  
    96. high = lcd_read_nibble();
    97.  
    98. low = lcd_read_nibble();
    99.  
    100. return( (high<<4) | low);
    101. }
    102. #endif
    103.  
    104. //----------------------------------------
    105. // Send a byte to the LCD.
    106. void lcd_send_byte(int8 address, int8 n)
    107. {
    108. output_low(LCD_RS);
    109.  
    110. #ifdef USE_LCD_RW
    111. while(bit_test(lcd_read_byte(),7)) ;
    112. #else
    113. delay_us(60);
    114. #endif
    115.  
    116. if(address)
    117.    output_high(LCD_RS);
    118. else
    119.    output_low(LCD_RS);
    120.    
    121. delay_cycles(1);
    122.  
    123. #ifdef USE_LCD_RW
    124. output_low(LCD_RW);
    125. delay_cycles(1);
    126. #endif
    127.  
    128. output_low(LCD_E);
    129.  
    130. lcd_send_nibble(n >> 4);
    131. lcd_send_nibble(n & 0xf);
    132. }
    133.  
    134. //----------------------------
    135. void lcd_init(void)
    136. {
    137. int8 i;
    138.  
    139. output_low(LCD_RS);
    140.  
    141. #ifdef USE_LCD_RW
    142. output_low(LCD_RW);
    143. #endif
    144.  
    145. output_low(LCD_E);
    146.  
    147. delay_ms(15);
    148.  
    149. for(i=0 ;i < 3; i++)
    150.    {
    151.     lcd_send_nibble(0x03);
    152.     delay_ms(5);
    153.    }
    154.  
    155. lcd_send_nibble(0x02);
    156.  
    157. for(i=0; i < sizeof(LCD_INIT_STRING); i++)
    158.    {
    159.     lcd_send_byte(0, LCD_INIT_STRING[i]);
    160.  
    161.     // If the R/W signal is not used, then
    162.     // the busy bit can't be polled.  One of
    163.     // the init commands takes longer than
    164.     // the hard-coded delay of 60 us, so in
    165.     // that case, lets just do a 5 ms delay
    166.     // after all four of them.
    167.     #ifndef USE_LCD_RW
    168.     delay_ms(5);
    169.     #endif
    170.    }
    171.  
    172. }
    173.  
    174. //----------------------------
    175.  
    176. void lcd_gotoxy(int8 x, int8 y)
    177. {
    178. int8 address;
    179.  
    180. if(y != 1)
    181.    address = lcd_line_two;
    182. else
    183.    address=0;
    184.  
    185. address += x-1;
    186. lcd_send_byte(0, 0x80 | address);
    187. }
    188.  
    189. //-----------------------------
    190. void lcd_putc(char c)
    191. {
    192. switch(c)
    193.    {
    194.     case '\f':
    195.       lcd_send_byte(0,1);
    196.       delay_ms(2);
    197.       break;
    198.  
    199.     case '\n':
    200.        lcd_gotoxy(1,2);
    201.        break;
    202.  
    203.     case '\b':
    204.        lcd_send_byte(0,0x10);
    205.        break;
    206.  
    207.     default:
    208.        lcd_send_byte(1,c);
    209.        break;
    210.    }
    211. }
    212.  
    213. //------------------------------
    214. #ifdef USE_LCD_RW
    215. char lcd_getc(int8 x, int8 y)
    216. {
    217. char value;
    218.  
    219. lcd_gotoxy(x,y);
    220.  
    221. // Wait until busy flag is low.
    222. while(bit_test(lcd_read_byte(),7));
    223.  
    224. output_high(LCD_RS);
    225. value = lcd_read_byte();
    226. output_low(lcd_RS);
    227.  
    228. return(value);
    229. }
    230. #endif
    231.  

    KEYPAD CODE

    Code (Text):
    1. // Un-commented the following define to use port B
    2. #define use_portb_kbd TRUE
    3.  
    4. // Maked sure the port used has pull-up resistors (or the LCD) on
    5. // the column pins
    6.  
    7. #if defined use_portb_kbd
    8.    #byte kbd = getenv("SFR:PORTB")
    9. #else
    10.    #byte kbd = getenv("SFR:PORTD")
    11. #endif
    12.  
    13. #if defined use_portb_kbd
    14.    #define set_tris_kbd(x) set_tris_b(x)
    15. #else
    16.    #define set_tris_kbd(x) set_tris_d(x)
    17. #endif
    18.  
    19. //Keypad connection:   (for example column 0 is B2)
    20. //                Bx:
    21.  
    22. #ifdef blue_keypad  ///////////////////////////////////// For the blue keypad
    23. #define COL0 (1 << 2)
    24. #define COL1 (1 << 3)
    25. #define COL2 (1 << 6)
    26.  
    27. #define ROW0 (1 << 4)
    28. #define ROW1 (1 << 7)
    29. #define ROW2 (1 << 1)
    30. #define ROW3 (1 << 5)
    31.  
    32. #else ////////////////////////////////////////////////// For the black keypad
    33. #define COL0 (1 << 5)
    34. #define COL1 (1 << 6)
    35. #define COL2 (1 << 7)
    36.  
    37. #define ROW0 (1 << 1)
    38. #define ROW1 (1 << 2)
    39. #define ROW2 (1 << 3)
    40. #define ROW3 (1 << 4)
    41.  
    42. #endif
    43.  
    44. #define ALL_ROWS (ROW0|ROW1|ROW2|ROW3)
    45. #define ALL_PINS (ALL_ROWS|COL0|COL1|COL2)
    46.  
    47. // Keypad layout:
    48. char const KEYS[4][3] = {{'1','2','3'},
    49.                          {'4','5','6'},
    50.                          {'7','8','9'},
    51.                          {'*','0','#'}};
    52.  
    53. #define KBD_DEBOUNCE_FACTOR 33    // Set this number to apx n/333 where
    54.                                   // n is the number of times you expect
    55.                                   // to call kbd_getc each second
    56.  
    57.  
    58. void kbd_init() {
    59. }
    60.  
    61. char kbd_getc( ) {
    62.    static BYTE kbd_call_count;
    63.    static int1 kbd_down;
    64.    static char last_key;
    65.    static BYTE col;
    66.  
    67.    BYTE kchar;
    68.    BYTE row;
    69.  
    70.    kchar='\0';
    71.    if(++kbd_call_count>KBD_DEBOUNCE_FACTOR) {
    72.        switch (col) {
    73.          case 0   : set_tris_kbd(ALL_PINS&~COL0);
    74.                     kbd=~COL0&ALL_PINS;
    75.                     break;
    76.          case 1   : set_tris_kbd(ALL_PINS&~COL1);
    77.                     kbd=~COL1&ALL_PINS;
    78.                     break;
    79.          case 2   : set_tris_kbd(ALL_PINS&~COL2);
    80.                     kbd=~COL2&ALL_PINS;
    81.                     break;
    82.        }
    83.  
    84.        if(kbd_down) {
    85.          if((kbd & (ALL_ROWS))==(ALL_ROWS)) {
    86.            kbd_down=FALSE;
    87.            kchar=last_key;
    88.            last_key='\0';
    89.          }
    90.        } else {
    91.           if((kbd & (ALL_ROWS))!=(ALL_ROWS)) {
    92.              if((kbd & ROW0)==0)
    93.                row=0;
    94.              else if((kbd & ROW1)==0)
    95.                row=1;
    96.              else if((kbd & ROW2)==0)
    97.                row=2;
    98.              else if((kbd & ROW3)==0)
    99.                row=3;
    100.              last_key =KEYS[row][col];
    101.              kbd_down = TRUE;
    102.           } else {
    103.              ++col;
    104.              if(col==3)
    105.                col=0;
    106.           }
    107.        }
    108.       kbd_call_count=0;
    109.    }
    110.   set_tris_kbd(ALL_PINS);
    111.   return(kchar);
    112. }
     
  2. GopherT

    AAC Fanatic!

    Nov 23, 2012
    6,067
    3,837
    We can't decipher your code without a circuit and an explanation (block diagram) of how you hope to implement your circuit/software.

    Is the keypad multiplexed? Is it the type of tuch-tone key pad (different audio frequency for each row/column pair) - post the datasheet of the keypad to be used.
     
  3. Pinnacle187

    Thread Starter New Member

    Apr 28, 2016
    13
    0
    The circuit is in the rar file
     
  4. GopherT

    AAC Fanatic!

    Nov 23, 2012
    6,067
    3,837
    Sorry, I don't open those. My iPad doesn't have an app for that.
     
  5. Pinnacle187

    Thread Starter New Member

    Apr 28, 2016
    13
    0
    Sorry here it is.
     
    Last edited: Jun 3, 2016
  6. DatLe

    New Member

    Jun 1, 2016
    1
    0
    Hi Pinnacle187,
    How about the function of "*" and "#" button.
    Think about hold action? You can using hold action to use as SHIFT and DEL function.
    In that case, you can use apply function of "*" and "#" button when they're released.
     
  7. joeyd999

    AAC Fanatic!

    Jun 6, 2011
    2,692
    2,756
    This is quite easy, I've done it a number of times (I think the first time was around 12 y.o. or so), and I don't require a schematic.

    1. Your actual keypad routine should send back a code for each key based upon row, column position. You've got a 4x3 keypad, so I'd send the following: Row 1: 0-2, Row 2: 3-5, Row 3: 6-8, Row 4: 9-11. Each code only requires a nibble, so I'd also send back the shift status as an additional bit, say bit 7 (MSB).

    2. Make 4 map strings. These strings map key codes to actual values. For instance:

    keymap0 = "123456789*0#"
    keymap1 = "1ADGJMPTW*0#"
    keymap2 = "1BEHKNRUY*0#"
    keymap3 = "1CFILOSVZ*0#"

    3. Have another routine, a state machine, that keeps track of the history of button presses (and the state of the shift key), reads the keycode, and returns the proper character from the appropriate keymap, depending upon state.

    The end.
     
  8. GopherT

    AAC Fanatic!

    Nov 23, 2012
    6,067
    3,837
    I'm sure he'll have no more questions after that explanation.
     
  9. dannyf

    Well-Known Member

    Sep 13, 2015
    1,828
    365
    It is fairly simple within the framework of your code. You showed fairly good sophistication in your code. The construct of yoyr kbd-getc() is far morr advaanced than manny code pieces shown here so I'm pretty sure you can get it done all by yourself.

    Good luck.
     
  10. MaxHeadRoom

    Expert

    Jul 18, 2013
    10,565
    2,379
Loading...