Graphical Display of Keypad on GLCD using PIC microcontroller

Graphical Display of Keypad on GLCD using PIC microcontroller

A graphical LCD is an electronic display unit which can display any type of elements provided by the user. Unlike a character LCD which can only display alphanumeric characters, a graphical LCD can display images, fonts and other structures. This article gives details of graphic LCD interfacing with a PIC microcontroller.

Previously we learned how to interface Keypad and GLCD with PIC microcontrollers. Now let’s integrate both these peripherals to display the keypad and its response on the GLCD graphically. To this end, we need to create functions for keypad input reception and GLCD display of keypad, along with the standard GLCD libraries.

Displaying Keypad on GLCD

To display keypad response on the GLCD, we need to first display the initial state of the keypad and then display an appropriate animation for the key pressed. The display is then reverted back to the initial state ready to display the next keypad response. This could be done with or without bitmaps.

In bitmap method, the display image for initial state and the animation image for each key pressed is stored at different locations in the RAM. After displaying the initial image, whenever a key is pressed the corresponding bitmap is displayed. The display is then reverted back after some delay. The downside of this method is that the code itself occupies a lot of RAM since each bitmap has a size of 8Kb (128*64 bits). Also, the delay in displaying a full bitmap is high. So here we will be using character display method which does not use bitmaps.

Graphical Display of Keypad on GLCD

To display the initial 4*4 matrix keypad we need to display each keys as characters at the required locations. You could also create and print some custom characters to create the frame of the keypad.

Example Firmware:

void keypad_display(void)       //Function for initial keypad display
{
    unsigned int init=32;       //intitial x position
    GLCD_GoTo(init,   1);       //Goto Row 1, Column 1
    GLCD_WriteChar('1');
    GLCD_GoTo(init+20,1);       //Goto Row 1, Column 2
    GLCD_WriteChar('2');
    GLCD_GoTo(init+40,1);       //Goto Row 1, Column 3
    GLCD_WriteChar('3');
    GLCD_GoTo(init+60,1);       //Goto Row 1, Column 4
    GLCD_WriteChar('A');
    
    GLCD_GoTo(init,   3);       //Goto Row 2, Column 1
    GLCD_WriteChar('4');
    GLCD_GoTo(init+20,3);       //Goto Row 2, Column 2
    GLCD_WriteChar('5');
    GLCD_GoTo(init+40,3);       //Goto Row 2, Column 3
    GLCD_WriteChar('6');
    GLCD_GoTo(init+60,3);       //Goto Row 2, Column 4
    GLCD_WriteChar('B');
    
    GLCD_GoTo(init,   5);       //Goto Row 3, Column 1
    GLCD_WriteChar('7');
    GLCD_GoTo(init+20,5);       //Goto Row 3, Column 2
    GLCD_WriteChar('8');
    GLCD_GoTo(init+40,5);       //Goto Row 3, Column 3
    GLCD_WriteChar('9');
    GLCD_GoTo(init+60,5);       //Goto Row 3, Column 4
    GLCD_WriteChar('C');
    
    GLCD_GoTo(init,   7);       //Goto Row 4, Column 1
    GLCD_WriteChar('*');
    GLCD_GoTo(init+20,7);       //Goto Row 4, Column 2
    GLCD_WriteChar('0');
    GLCD_GoTo(init+40,7);       //Goto Row 4, Column 3
    GLCD_WriteChar('#');
    GLCD_GoTo(init+60,7);       //Goto Row 4, Column 4
    GLCD_WriteChar('D');
    
}

Now that the initial keypad display is done, we need to read a key press and then display a corresponding key animation.

Keypad Input Reception

As specified in our previous tutorial on Matrix keypad interfacing with a PIC Microcontroller, the keypad that we use here is a 4*4 matrix keypad with 4 columns and 4 rows. We use the rows as output from the microcontroller to the keypad and the columns as the input to read and determine which key was pressed. This can be done in Pull up mode or Pulldown mode. Here we will be using Pull up mode, where we will be sensing for an input ‘Low’ in the column pins.

In keypad interfacing, we will provide output ‘Low’ to each of the row pins sequentially and check the column pins for input ‘Low’ in each case. The output swipe of ‘Low’ across each row is fast enough to complete at least one full cycle by the time a key is released after the key press. When the key is pressed, the corresponding row and column pins will be shorted, providing us with an input ‘Low’ at the particular column, in the particular stage of the output swipe. Thus the appropriate key press could be determined. This key value is to be returned to the calling function to display it in the GLCD.

Example Firmware:

char keyrec(void)                   //Function to record key
{
    while(1)
    {
        write = 0xF7;               //Write output 'low' to R1
        __delay_ms(10);
        switch (read)
        {
            case 0x07:              //Check if C1 is 'low' 
            {
                wait(0x08);         //Wait till key is released
                return('1');        //Return appropriate key as detected
            }
            case 0x0B:              //Check if C2 is 'low' 
            {
                wait(0x04);
                return('2');
            }
            case 0x0D:              //Check if C3 is 'low' 
            {
                wait(0x02);
                return('3');
            }
            case 0x0E:              //Check if C4 is 'low' 
            {
                wait(0x01);
                return('A');
            }
        }
        
        write = 0xFB;               //Write output 'low' to R2
        __delay_ms(10);
        switch (read)
        {
            case 0x07:
            {
                wait(0x08);
                return('4');
            }
            case 0x0B:
            {
                wait(0x04);
                return('5');
            }
            case 0x0D:
            {
                wait(0x02);
                return('6');
            }
            case 0x0E:
            {
                wait(0x01);
                return('B');
            }
        }
        
        write = 0xFD;               //Write output 'low' to R3
        __delay_ms(10);
        switch (read)
        {
            case 0x07:
            {
                wait(0x08);
                return('7');
            }
            case 0x0B:
            {
                wait(0x04);
                return('8');
            }
            case 0x0D:
            {
                wait(0x02);
                return('9');
            }
            case 0x0E:
            {
                wait(0x01);
                return('C');
            }
        }
        
        write = 0xFE;               //Write output 'low' to R4
        __delay_ms(10);
        switch (read)
        {
            case 0x07:
            {
                wait(0x08);
                return('*');
            }
            case 0x0B:
            {
                wait(0x04);
                return('0');
            }
            case 0x0D:
            {
                wait(0x02);
                return('#');
            }
            case 0x0E:
            {
                wait(0x01);
                return('D');
            }
        }          
    }
}

Here ‘read’ is a user defined macro to read the 4 bits of columns. The ‘wait’ function is used for debouncing the input, where we will wait till the key is released to ensure that the key is detected only once after a single press.

Example Firmware:

#define read ((PORTB & 0xF0)>>4)

void wait(int a)                    //Function to wait for debouncing
{
    while(!(read & a))              //Wait till specified key is released
        __delay_ms(100);
}

Note: Here we have used PORTB for the keypad. Hence the program can only run independently from the microcontroller and not in debug mode since the ICD3 uses PORT B for programming and debugging. Also, caution should be exercised while placing the jumpers in programming mode while uploading the program and in PORT mode while running the program.

Animation display for key press

After sensing a key press, we should first go to the location of the particular character. This could be done with simple Switch-case statements. An offset is also provided to specify the column offset from the character position.

Example Firmware:

void keygoto_pos(char a,int offset)             //Function to position of required character
{
    unsigned int init=32;
    init+=offset;                               //Applying required offset
    switch (a)
    {
        case '1':GLCD_GoTo(init   , 1); break;
        case '2':GLCD_GoTo(init+20, 1); break;
        case '3':GLCD_GoTo(init+40, 1); break;
        case 'A':GLCD_GoTo(init+60, 1); break;
        case '4':GLCD_GoTo(init   , 3); break;
        case '5':GLCD_GoTo(init+20, 3); break;
        case '6':GLCD_GoTo(init+40, 3); break;
        case 'B':GLCD_GoTo(init+60, 3); break;
        case '7':GLCD_GoTo(init   , 5); break;
        case '8':GLCD_GoTo(init+20, 5); break;
        case '9':GLCD_GoTo(init+40, 5); break;
        case 'C':GLCD_GoTo(init+60, 5); break;
        case '*':GLCD_GoTo(init   , 7); break;
        case '0':GLCD_GoTo(init+20, 7); break;
        case '#':GLCD_GoTo(init+40, 7); break;
        case 'D':GLCD_GoTo(init+60, 7); break;
    }
}

Next, we will create a custom function, similar to the character display library function, to display the negative image of the character as the key press animation. We will also turn ON the column before and after the character for better visualization of the negative image.

Example Firmware:

void GLCD_WriteChar_Inv(char charToWrite)       //Function to get negative of character
{
int i,a;
charToWrite -= 32; 
GLCD_WriteData(0xFF);                           //Turn ON column left to character
for(i = 0; i < 5; i++) 
{
    a=(int)font5x8[(5 * charToWrite) + i];
    a=~a;                                       //Taking bitwise not on the data to get negative
    GLCD_WriteData(a);
}
GLCD_WriteData(0xFF);                           //Turn ON column right to character
}

After displaying the animation we need to revert the display back to its initial state. This could be done by writing a ‘space’ to the left of the character position, to turn OFF the column left of the negative image. The actual character image is then displayed over the negative image, providing us with the initial keypad display on the GLCD.

Example Firmware:

void keypress_display(char a)               //Function to display animation of key pressed
{
    GLCD_GoTo(1,0);
    GLCD_WriteChar(a);                      //Display recently pressed character at top left corner
    
    keygoto_pos(a,-1);                      //Offset of -1 to turn ON column before character
    GLCD_WriteChar_Inv(a);                  //Displaying negative character
    __delay_ms(500);
    
    keygoto_pos(a,-6);                      //Offset of -6 to display 'space'(1 character = 6 columns)
    GLCD_WriteChar(' ');                    //To turn OFF column to the right of key
    GLCD_WriteChar(a);                      //Reverting back to initial state
}

Here we have also displayed the recently pressed key on the top left corner of the display.

After reverting back to initial display state, we can now check for subsequent keys pressed and display its animation. Hence we have the keypad response display on GLCD. This methodology could be applied to touch screen keypads to obtain keypad response.

Graphical Display of Keypad on GLCD demo