Display Custom Characters on 16x2 LCD using PIC Microcontroller

In our previous tutorial, we have learnt How to Interface a 16*2 LCD with PIC Microcontroller. We recommend you to go through it before going any further, if you are beginner in PIC microcontroller. Previously we have also learnt the basics of PIC using LED blinking Program and  Timers in PIC Microcontroller. You can check here all the tutorials on Learning PIC Microcontrollers using MPLABX and XC8 compiler.

In this tutorial, let us make it more interesting by creating our own custom characters and displaying them on our LCD screen using PIC16F877A PIC Microcontroller. Also there are some pre-defined mostly used custom characters given by the HD44780A IC itself, we will also see how we can use them. As explained in our previous tutorial our LCD has a Hitachi HD44780 controller embedded on it which helps us to display characters. Each character that we display is already pre-defined inside the ROM of the HD44780 IC. We will learn about LCD controller IC HD44780, before displaying character on LCD.

 

16x2 Dot Matrix LCD Controller IC HD44780:

In order to display a custom character, we have to somehow tell the IC that how the custom character will look like. To do that we should know about the Three types of Memories present inside the HD44780 LCD controller IC:

Character Generator ROM (CGROM):  It is the read only memory which, as said earlier, contains all the patterns of the characters pre-defined inside it. This ROM will vary from each type of Interface IC, and some might have some pre-defined custom character with them.  

Display Data RAM (DDRAM): This is a random access memory. Each time we display a character its pattern will be fetched from the CGROM and transferred to the DDRAM and then will be placed on the screen.  To put it simple, DDRAM will have the patterns of all characters that are currently being displayed on the LCD Screen. This way for each cycle the IC need not fetch data from CGROM, and helps in getting a short update frequency

Character generator RAM (CGRAM): This is also a Random access memory, so we can write and read data from it. As the name implies this memory will be the one which can used to generate the custom character. We have to form a pattern for the character and write it in the CGRAM, this pattern can be read and displayed on the Screen when required.

 

Now, since we got a basic understanding of the types of Memory present in the HD44780 interface IC. Let us take a look into its datasheet to understand a bit more.

datasheet-of-lcd-controller-IC-HD44780

As, the datasheet implies, the HD44780 IC has provided as 8 Locations to store our custom patterns in CGRAM, also on the right we can see that there are some pre-defined characters which can also be displayed on our LCD Screen. Let us see how we can do it.

 

Displaying a Custom Character on 16x2 LCD:

To display a custom character we must first generate a pattern for it and then save it to the CGRAM. Since we have the library functions with us already, it should be easy to do this with some simple commands. Here is the Library for LCD functions, but here we have copy-pasted all the Library functions in the program itself, so no need to include this header file in our program. Also check this article for Basic LCD working and its Pinouts.

The first step is to generate a pattern or the custom character. As we know each character is a combination of 5*8 dots. We have to select which dot (pixel) should go high and which should stay low. Simply draw a box like below and shade the regions based on your character. My character here is a stick man (hope it looks like one). Once shaded, simple write the equivalent binary value of each byte as shown below.

binary-code-for-custom-character

Simply put a ‘1’ on the shaded region and a ‘0’ on the un-shaded region for each byte, and that is it our custom pattern is ready. Similarly I have made 8 custom pattern codes for our 8 memory spaces present it the CGROM. They are listed in the table below.

S.NO:

Custom Character

Pattern Code

1

creating-binary-code-for-custom-character-1

0b01110,
0b01110,
0b00100,
0b01110,
0b10101,
0b00100,
0b01010,
0b01010

2

creating-binary-code-for-custom-character-2

0b00000,
0b00000,
0b01010,
0b00100,
0b00100,
0b10001,
0b01110,
0b00000

3

creating-binary-code-for-custom-character-3

0b00100,
0b01110,
0b11111,
0b11111,
0b01110,
0b01110,
0b01010,
0b01010

4

creating-binary-code-for-custom-character-4

0b01110,
0b10001,
0b10001,
0b11111,
0b11011,
0b11011,
0b11111,
0b00000

5

creating-binary-code-for-custom-character-5

0b01110,
0b10000,
0b10000,
0b11111,
0b11011,
0b11011,
0b11111,
0b00000

6

creating-binary-code-for-custom-character-6

0b00000,
0b10001,
0b01010,
0b10001,
0b00100,
0b01110,
0b10001,
0b00000

7

creating-binary-code-for-custom-character-7

0b00000,
0b00000,
0b01010,
0b10101,
0b10001,
0b01110,
0b00100,
0b00000

8

creating-binary-code-for-custom-character-8

0b11111,
0b11111,
0b10101,
0b11011,
0b11011,
0b11111,
0b10001,
0b11111

Note: It is not mandatory to load all the 8 spaces provided in the CGRAM.

 

Programming and Working Explanation:

Now our pattern codes are ready, we just have to load them to the CGRAM of LCD and display them using PIC microcontroller. To load them in to the CGRAM we can form a 5*8 array of elements and load each byte by using a ‘for loop’. The array of pattern code is shown below:

const unsigned short Custom_Char5x8[] = {
  0b01110,0b01110,0b00100,0b01110,0b10101,0b00100,0b01010,0b01010, // Code for CGRAM memory space 1
  0b00000,0b00000,0b01010,0b00100,0b00100,0b10001,0b01110,0b00000, // Code for CGRAM memory space 2
  0b00100,0b01110,0b11111,0b11111,0b01110,0b01110,0b01010,0b01010, // Code for CGRAM memory space 3
  0b01110,0b10001,0b10001,0b11111,0b11011,0b11011,0b11111,0b00000, // Code for CGRAM memory space 4
  0b01110,0b10000,0b10000,0b11111,0b11011,0b11011,0b11111,0b00000, // Code for CGRAM memory space 5
  0b00000,0b10001,0b01010,0b10001,0b00100,0b01110,0b10001,0b00000, // Code for CGRAM memory space 6
  0b00000,0b00000,0b01010,0b10101,0b10001,0b01110,0b00100,0b00000, // Code for CGRAM memory space 7
  0b11111,0b11111,0b10101,0b11011,0b11011,0b11111,0b10001,0b11111  // Code for CGRAM memory space 8
};

 

Each memory space is loaded with its respected character pattern. To load this pattern into the HD44780 IC, the data-sheet of HD44780 has to be referred, but it is just lines of command that can be used to set the address of the CGRAM

//*** Load custom char into the CGROM***//////
  Lcd_Cmd(0x04);   // Set CGRAM Address
  Lcd_Cmd(0x00);   // .. set CGRAM Address
  for (i = 0; i <= 63 ; i++)
    Lcd_Print_Char(Custom_Char5x8[i]);
  Lcd_Cmd(0);      // Return to Home
  Lcd_Cmd(2);      // .. return to Home
  //*** Loading custom char complete***//////

 

Here, inside the ‘for loop’ each binary value is loaded into the CGROM. Once the pattern is loaded, the custom characters can be made to display by simply calling the location of the pattern using the void Lcd_Print_Char(char data) function as shown below.

    Lcd_Print_Char(0); // Display Custom Character 0
    Lcd_Print_Char(1); // Display Custom Character 1
    Lcd_Print_Char(2); // Display Custom Character 2
    Lcd_Print_Char(3); // Display Custom Character 3
    Lcd_Print_Char(4); // Display Custom Character 4
    Lcd_Print_Char(5); // Display Custom Character 5
    Lcd_Print_Char(6); // Display Custom Character 6
    Lcd_Print_Char(7); // Display Custom Character 7

 

Print Predefined Special Character:

The HD44780 IC has some predefined special characters stored in the DDROM. These characters can be directly printed on to the screen by referring to its binary value in the datasheet.

For example: The binary value of the character "ALPHA" is 0b11100000. How to obtain this can be understood from the figure below, likewise you can get value for any special character which is pre-defined in the IC.

binary-code-for-predefined-character-in-HD44780

 

Once the binary value is known, the corresponding character can be printed to the screen by simply using the void Lcd_Print_Char(char data) function as shown below,

Lcd_Print_Char(0b11100000); //binary value of alpha from data-sheet

 

The complete code of this project is given below in Code section, also check the detail Video explanation at the end of this tutorial.

 

Circuit Connections and Testing:

This project does not have any additional hardware requirement, we have simply used the same connections from the previous LCD interfacing tutorial and used the same board which we have created in LED blinking Tutorial. As always let’s simulate the program using Proteus to verify our output.

display-custom-characters-on-16x2-LCD-using-pic-microcontroller-circuit

Once we have are simulation running as expected, lets directly burn the code into our Hardware set-up. The output of the program should be something like this:

display-custom-characters-on-16x2-LCD-using-pic-microcontroller-with-xc8

So that’s how you can Display any Custom Character on 16x2 LCD using PIC Microcontroller with MPLABX and XC8 compiler. Also check our complete PIC Microcontroller Learning Series here.

Code: 

#define _XTAL_FREQ 20000000

#define RS RD2
#define EN RD3
#define D4 RD4
#define D5 RD5
#define D6 RD6
#define D7 RD7

#include <xc.h>

#pragma config FOSC = HS        // Oscillator Selection bits (HS oscillator)
#pragma config WDTE = OFF       // Watchdog Timer Enable bit (WDT disabled)
#pragma config PWRTE = ON       // Power-up Timer Enable bit (PWRT enabled)
#pragma config BOREN = ON       // Brown-out Reset Enable bit (BOR enabled)
#pragma config LVP = OFF        // Low-Voltage (Single-Supply) In-Circuit Serial Programming Enable bit (RB3 is digital I/O, HV on MCLR must be used for programming)
#pragma config CPD = OFF        // Data EEPROM Memory Code Protection bit (Data EEPROM code protection off)
#pragma config WRT = OFF        // Flash Program Memory Write Enable bits (Write protection off; all program memory may be written to by EECON control)
#pragma config CP = OFF         // Flash Program Memory Code Protection bit (Code protection off)

//LCD Functions Developed by Circuit Digest.

/*******START OF LCD FUNCTIONS*********/
void Lcd_SetBit(char data_bit) //Based on the Hex value Set the Bits of the Data Lines
{
    if(data_bit& 1) 
        D4 = 1;
    else
        D4 = 0;

    if(data_bit& 2)
        D5 = 1;
    else
        D5 = 0;

    if(data_bit& 4)
        D6 = 1;
    else
        D6 = 0;

    if(data_bit& 8) 
        D7 = 1;
    else
        D7 = 0;
}

void Lcd_Cmd(char a)
{
    RS = 0;           
    Lcd_SetBit(a); //Incoming Hex value
    EN  = 1;         
        __delay_ms(4);
        EN  = 0;         
}

Lcd_Clear()
{
    Lcd_Cmd(0); //Clear the LCD
    Lcd_Cmd(1); //Move the curser to first position
}

void Lcd_Set_Cursor(char a, char b)
{
    char temp,z,y;
    if(a== 1)
    {
      temp = 0x80 + b - 1; //80H is used to move the curser
        z = temp>>4; //Lower 8-bits
        y = temp & 0x0F; //Upper 8-bits
        Lcd_Cmd(z); //Set Row
        Lcd_Cmd(y); //Set Column
    }
    else if(a== 2)
    {
        temp = 0xC0 + b - 1;
        z = temp>>4; //Lower 8-bits
        y = temp & 0x0F; //Upper 8-bits
        Lcd_Cmd(z); //Set Row
        Lcd_Cmd(y); //Set Column
    }
}

void Lcd_Start()
{
  Lcd_SetBit(0x00);
  for(int i=1065244; i<=0; i--)  NOP();  
  Lcd_Cmd(0x03);
    __delay_ms(5);
  Lcd_Cmd(0x03);
    __delay_ms(11);
  Lcd_Cmd(0x03); 
  Lcd_Cmd(0x02); //02H is used for Return home -> Clears the RAM and initializes the LCD
  Lcd_Cmd(0x02); //02H is used for Return home -> Clears the RAM and initializes the LCD
  Lcd_Cmd(0x08); //Select Row 1
  Lcd_Cmd(0x00); //Clear Row 1 Display
  Lcd_Cmd(0x0C); //Select Row 2
  Lcd_Cmd(0x00); //Clear Row 2 Display
  Lcd_Cmd(0x06);
}

void Lcd_Print_Char(char data)  //Send 8-bits through 4-bit mode
{
   char Lower_Nibble,Upper_Nibble;
   Lower_Nibble = data&0x0F;
   Upper_Nibble = data&0xF0;
   RS = 1;             // => RS = 1
   Lcd_SetBit(Upper_Nibble>>4);             //Send upper half by shifting by 4
   EN = 1;
   for(int i=2130483; i<=0; i--)  NOP(); 
   EN = 0;
   Lcd_SetBit(Lower_Nibble); //Send Lower half
   EN = 1;
   for(int i=2130483; i<=0; i--)  NOP();
   EN = 0;
}

void Lcd_Print_String(char *a)
{
    int i;
    for(i=0;a[i]!='\0';i++)
       Lcd_Print_Char(a[i]);  //Split the string using pointers and call the Char function 
}

/*******END OF LCD FUNCTIONS*********/
const unsigned short Custom_Char5x8[] = {
  0b01110,0b01110,0b00100,0b01110,0b10101,0b00100,0b01010,0b01010, // Code for CGRAM memory space 1
  0b00000,0b00000,0b01010,0b00100,0b00100,0b10001,0b01110,0b00000, // Code for CGRAM memory space 2
  0b00100,0b01110,0b11111,0b11111,0b01110,0b01110,0b01010,0b01010, // Code for CGRAM memory space 3
  0b01110,0b10001,0b10001,0b11111,0b11011,0b11011,0b11111,0b00000, // Code for CGRAM memory space 4
  0b01110,0b10000,0b10000,0b11111,0b11011,0b11011,0b11111,0b00000, // Code for CGRAM memory space 5
  0b00000,0b10001,0b01010,0b10001,0b00100,0b01110,0b10001,0b00000, // Code for CGRAM memory space 6
  0b00000,0b00000,0b01010,0b10101,0b10001,0b01110,0b00100,0b00000, // Code for CGRAM memory space 7
  0b11111,0b11111,0b10101,0b11011,0b11011,0b11111,0b10001,0b11111  // Code for CGRAM memory space 8
};

int main()
{
    unsigned int a;char i;
    TRISD = 0x00;
    Lcd_Start();
   
    //*** Load custom char into the CGROM***//////
  Lcd_Cmd(0x04);   // Set CGRAM Address
  Lcd_Cmd(0x00);   // .. set CGRAM Address
  for (i = 0; i <= 63 ; i++)
    Lcd_Print_Char(Custom_Char5x8[i]);
  Lcd_Cmd(0);      // Return to Home
  Lcd_Cmd(2);      // .. return to Home
  //*** Loading custom char complete***//////
  
    while(1)
    {
        Lcd_Clear();
        //Print all Custom characters// 
    Lcd_Set_Cursor(1,1);
    Lcd_Print_Char(0); // Display Custom Character 0
    Lcd_Print_Char(1); // Display Custom Character 1
    Lcd_Print_Char(2); // Display Custom Character 2
    Lcd_Print_Char(3); // Display Custom Character 3
    Lcd_Print_Char(4); // Display Custom Character 4
    Lcd_Print_Char(5); // Display Custom Character 5
    Lcd_Print_Char(6); // Display Custom Character 6
    Lcd_Print_Char(7); // Display Custom Character 7
    
    //Print predefined special character//
    Lcd_Set_Cursor(2,1);
    Lcd_Print_Char(0b11100000); //binary value of alpha from data-sheet
    __delay_ms(1000);
    }
    return 0;
}

Video: 

Leave a comment