#include <xc.h>

#define _XTAL_FREQ 20000000UL

#define LCD_RS LATAbits.LATA0
#define LCD_E  LATAbits.LATA1

const char keymap[4][4] =
{
    {'1','2','3','A'},
    {'4','5','6','B'},
    {'7','8','9','C'},
    {'*','0','#','D'}
};

void LCD_Command(unsigned char cmd)
{
    LATB = cmd;
    LCD_RS = 0;
    LCD_E = 1;
    __delay_ms(2);
    LCD_E = 0;
    __delay_ms(2);
}

void LCD_Data(unsigned char data)
{
    LATB = data;
    LCD_RS = 1;
    LCD_E = 1;
    __delay_ms(2);
    LCD_E = 0;
    __delay_ms(2);
}

char Keypad_GetKey(void)
{
    unsigned char row, col;

    for(row = 0; row < 4; row++)
    {
        LATD = 0x0F;                    // RD0-RD3 all HIGH
        LATD = LATD & ~(1 << row);      // make one line LOW
        __delay_us(100);

        for(col = 0; col < 4; col++)
        {
            if((PORTD & (0x10 << col)) == 0)        // RD4..RD7 LOW?
            {
                __delay_ms(20);                     // debounce
                if((PORTD & (0x10 << col)) == 0)
                {
                    while((PORTD & (0x10 << col)) == 0);   // wait release
                    return keymap[row][col];
                }
            }
        }
    }
    return 0;                           // no key pressed
}

void main(void)
{
    char keys[4];               // stores 4 key presses
    unsigned char i;

    ADCON1 = 0x0F;              // make pins digital

    TRISB = 0x00;               // LCD data
    TRISAbits.TRISA0 = 0;       // RS
    TRISAbits.TRISA1 = 0;       // E

    TRISD = 0xF0;               // RD0-RD3 output, RD4-RD7 input
    LATD  = 0x0F;

    __delay_ms(20);
    LCD_Command(0x38);          // 8-bit, 2 lines
    LCD_Command(0x0C);          // display ON
    LCD_Command(0x01);          // clear
    __delay_ms(5);
    LCD_Command(0x06);          // auto-increment

    while(1)
    {
        LCD_Command(0x01);      // clear screen
        __delay_ms(5);
        LCD_Command(0x80);      // line 1
        LCD_Data('E');
        LCD_Data('N');
        LCD_Data('T');
        LCD_Data('E');
        LCD_Data('R');
        LCD_Data(' ');
        LCD_Data('4');
        LCD_Data(' ');
        LCD_Data('K');
        LCD_Data('E');
        LCD_Data('Y');
        LCD_Data('S');

        // Read 4 successive key presses
        for(i = 0; i < 4; i++)
        {
            do
            {
                keys[i] = Keypad_GetKey();
            } while(keys[i] == 0);      // wait until a key is pressed
        }

        // Display the 4 entered characters
        LCD_Command(0xC0);      // line 2
        for(i = 0; i < 4; i++)
        {
            LCD_Data(keys[i]);
        }

        __delay_ms(3000);       // show for 3 s, then repeat
    }
}
