Display Custom Characters on 16X2 LCD using PIC Microcontroller

Display Custom Characters on LCD using PIC Microcontroller

In this blog post, we will learn how to interface 16×2 Lcd with PIC Microcontroller (PIC16F877A). We will also learn how to display custom characters on 16X2 LCD using PIC microcontroller.

16X2 LCD has a controller HD44780, which provides the 8 locations (CGRAM ) to store the created LCD custom characters. We can use these custom characters as per our requirements. The custom characters are self-made characters which we design by our self.

In my previous blog post “How to create a custom character on 16×2 LCD”, I have described important registers and memory of HD44780.  I have also explained how to interface alphanumeric LCD with 8051 microcontrollers, You can check this article to click the below link,

So let us see code that explains the interfacing of 16×2 LCD  with PIC and how to display custom characters on 16X2 LCD using PIC microcontroller. In this blog post, I have written two codes one to display beetle and Arrow and second to display charging a battery. I have used MPLAB v8.85 with the HI-TECH C v9.83 compiler to creating this project “Display Custom Characters on LCD using PIC Microcontroller”.

 

1) Display beetle and Arrow on 16×2 alphanumeric LCD:

 

/* Name     : main.c
*  Purpose  : Main file for displaying custom characters on LCD with PIC16F628A.
*  Author   : Amlendra Kumar
*  Website  : https://aticleworld.com
*/
#include<htc.h>

// Configuration word for PIC16F877A
__CONFIG( FOSC_HS & WDTE_OFF & PWRTE_ON & CP_OFF & BOREN_ON
          & LVP_OFF & CPD_OFF & WRT_OFF & DEBUG_OFF);

// Define CPU Frequency
// This must be defined, if __delay_ms() or
// __delay_us() functions are used in the code
#define _XTAL_FREQ   20000000



// Define Pins for LCD
#define LCD_E    		RA1   // Enable pin for LCD
#define LCD_RS	 		RA0   // RS pin for LCD
#define LCD_Data_Bus 	PORTB // Data bus for LCD

// Define Pins direction registers
#define LCD_E_Dir     		TRISA1
#define LCD_RS_Dir   	 	TRISA0
#define LCD_Data_Bus_Dir 	TRISB

// -----------------------   Define Custom Characters ----------------------- //
/*Hex value to create the pattern (Arrow)*/
unsigned char ArrowObject[] = {0x00,0x04,0x08,0x1f,0x08,0x04,0x00,0x00};
/*Hex value to create the pattern (betel leaf)*/
unsigned char BetelObject[]= {0x00,0x1b,0x15,0x11,0x0a,0x04,0x00,0x00};
/*---------------------------------------------------*/

// Constants for delay
#define E_Delay       500


/*---------Start Function Declarations for LCD-------------*/
void WriteCommandToLCD(unsigned char);
void WriteDataToLCD(char);
void InitLCD(void);
void WriteStringToLCD(const char*);
void ClearLCDScreen(void);
void LCDBuildChar(unsigned char, unsigned char*);
/*---------------------End------------------------------*/


int main()
{
    CMCON = 0x07;			// Turn comparator off
    InitLCD();			    // Initialize LCD in 8bit mode

    WriteDataToLCD(0x00);   // Write Curvy Object on LCD
    WriteDataToLCD(' ');    // Space
    WriteDataToLCD(0x01);   // Write Square Box on LCD

    while(1)
    {
    }
    return 0;
}


void ToggleEpinOfLCD(void)
{
    LCD_E = 1;                // Give a pulse on E pin
    __delay_us(E_Delay);      // so that LCD can latch the
    LCD_E = 0;                // data from data bus
    __delay_us(E_Delay);
}


void WriteCommandToLCD(unsigned char Command)
{
    LCD_RS = 0;               // It is a command
    LCD_Data_Bus = Command;   // Write Command value on data bus
    ToggleEpinOfLCD();
}

void WriteDataToLCD(char LCDChar)
{
    LCD_RS = 1;               // It is data
    LCD_Data_Bus = LCDChar;   // Write Data value on data bus
    ToggleEpinOfLCD();
}

void InitLCD(void)
{
    // Firstly make all pins output
    LCD_E   	     = 0;      // E = 0
    LCD_RS    	     = 0;      // D = 0
    LCD_Data_Bus     = 0;      // CLK = 0
    LCD_E_Dir        = 0;      // Make Output
    LCD_RS_Dir       = 0;      // Make Output
    LCD_Data_Bus_Dir = 0;      // Make Output


    WriteCommandToLCD(0x38);    //function set
    WriteCommandToLCD(0x0c);    //display on,cursor off,blink off
    WriteCommandToLCD(0x01);    //clear display
    WriteCommandToLCD(0x06);    //entry mode, set increment

    /* ---------- Build Custom Characters -----------------*/
    LCDBuildChar(0, BetelObject); /* Build Character at position 0 */
    LCDBuildChar(1, ArrowObject);   /* Build Character at position 1 */
}



void WriteStringToLCD(const char *s)
{
    while(*s)
    {
        WriteDataToLCD(*s++);   // print first character on LCD
    }
}


void ClearLCDScreen(void)
{
    WriteCommandToLCD(0x01);    // Clear the screen
    __delay_ms(2);              // Delay for cursor to return at zero position
}



void LCDBuildChar(unsigned char loc, unsigned char *p)
{
    unsigned char i = 0;

    if(loc<8)                                 //If valid address
    {
        WriteCommandToLCD(0x40+(loc*8));      //Write to CGRAM
        for(i=0; i<8; i++)
        {
            WriteDataToLCD(p[i]);
        }             //Write the character pattern to CGRAM
    }
    WriteCommandToLCD(0x80);                  //shift back to DDRAM location 0
}

Proteus Simulation:

lcd interfacing with PIC

 

Code Analysis:

After seeing the code I hope that you will find the interfacing of LCD is easy with PIC. So let us decode each function which uses in LCD interfacing.

InitLCD():

It uses to initialize the LCD and create the custom character. I have used to array for beetle and arrow. The value of the array will change if you will change your custom design. There are many tools available to create a custom design. You can also create your custom design online, personally I found that the custom character generator tool is good to create a custom character.

/*Hex value to create the pattern (Arrow)*/
unsigned char ArrowObject[] = {0x00,0x04,0x08,0x1f,0x08,0x04,0x00,0x00};
/*Hex value to create the pattern (betel leaf)*/
unsigned char BetelObject[]= {0x00,0x1b,0x15,0x11,0x0a,0x04,0x00,0x00};

 

void InitLCD(void)
{
    // Firstly make all pins output
    LCD_E          = 0;      // E = 0
    LCD_RS           = 0;      // D = 0
    LCD_Data_Bus     = 0;      // CLK = 0
    LCD_E_Dir        = 0;      // Make Output
    LCD_RS_Dir       = 0;      // Make Output
    LCD_Data_Bus_Dir = 0;      // Make Output
    WriteCommandToLCD(0x38);    //function set
    WriteCommandToLCD(0x0c);    //display on,cursor off,blink off
    WriteCommandToLCD(0x01);    //clear display
    WriteCommandToLCD(0x06);    //entry mode, set increment
    /* ---------- Build Custom Characters -----------------*/
    LCDBuildChar(0, BetelObject); /* Build Character at position 0 */
    LCDBuildChar(1, ArrowObject);   /* Build Character at position 1 */
}

 

WriteCommandToLCD():

 

2) Display charging a battery on 16×2 alphanumeric LCD:

 

/* Name     : main.c
*  Purpose  : Main file for displaying custom characters on LCD with PIC16F628A.
*  Author   : Amlendra Kumar
*  Website  : https://aticleworld.com
*/
#include<htc.h>

// Configuration word for PIC16F877A
__CONFIG( FOSC_HS & WDTE_OFF & PWRTE_ON & CP_OFF & BOREN_ON
          & LVP_OFF & CPD_OFF & WRT_OFF & DEBUG_OFF);



// Define CPU Frequency
// This must be defined, if __delay_ms() or
// __delay_us() functions are used in the code
#define _XTAL_FREQ   20000000


// Define Pins
#define LCD_E    		RA1   // Enable pin for LCD
#define LCD_RS	 		RA0   // RS pin for LCD
#define LCD_Data_Bus 	PORTB // Data bus for LCD


// Define Pins direction registers
#define LCD_E_Dir     		TRISA1
#define LCD_RS_Dir   	 	TRISA0
#define LCD_Data_Bus_Dir 	TRISB


// ------- Define Custom Characters ----------- //

const unsigned char MyDesignObject[][8] =
{
    {0x00, 0x00, 0x0A, 0x1F, 0x1F, 0x0E, 0x04, 0x00},
    {0x0E, 0x1B, 0x11, 0x11, 0x11, 0x11, 0x1F, 0x00},
    {0x0E, 0x1B, 0x11, 0x11, 0x11, 0x1F, 0x1F, 0x00},
    {0x0E, 0x1B, 0x11, 0x11, 0x1F, 0x1F, 0x1F, 0x00},
    {0x0E, 0x1B, 0x11, 0x1F, 0x1F, 0x1F, 0x1F, 0x00},
    {0x0E, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x00},
    {0x00, 0x04, 0x02, 0x1F, 0x02, 0x04, 0x00, 0x00},
    {0x00, 0x00, 0x0E, 0x11, 0x11, 0x0A, 0x1B, 0x00}
};
/*---------------------------------------------------*/

// Constants for delay
#define E_Delay       500

/*---------Start Function Declarations for LCD-------------*/
void WriteCommandToLCD(unsigned char);
void WriteDataToLCD(char);
void InitLCD(void);
void WriteStringToLCD(const char*);
void ClearLCDScreen(void);
void LCDBuildChar(unsigned char, unsigned char*);
/*---------------------End------------------------------*/


int main()
{
    unsigned char index = 0;
    CMCON = 0x07;	// Turn comparator off
    InitLCD();		// Initialize LCD in 8bit mode

    for(index =0; index < sizeof(MyDesignObject)/sizeof(MyDesignObject[0]); ++index)
    {
        WriteDataToLCD(index); /* Build Character at position index*/
    }
    while(1)
    {
    }

    return 0;
}

void ToggleEpinOfLCD(void)
{
    LCD_E = 1;                // Give a pulse on E pin
    __delay_us(E_Delay);      // so that LCD can latch the
    LCD_E = 0;                // data from data bus
    __delay_us(E_Delay);
}

void WriteCommandToLCD(unsigned char Command)
{
    LCD_RS = 0;               // It is a command
    LCD_Data_Bus = Command;   // Write Command value on data bus
    ToggleEpinOfLCD();
}

void WriteDataToLCD(char LCDChar)
{
    LCD_RS = 1;               // It is data
    LCD_Data_Bus = LCDChar;   // Write Data value on data bus
    ToggleEpinOfLCD();
}

void InitLCD(void)
{
    unsigned char index =0;
    // Firstly make all pins output
    LCD_E   	     = 0;      // E = 0
    LCD_RS    	     = 0;      // D = 0
    LCD_Data_Bus     = 0;      // CLK = 0
    LCD_E_Dir        = 0;      // Make Output
    LCD_RS_Dir       = 0;      // Make Output
    LCD_Data_Bus_Dir = 0;      // Make Output

    ///////////////// Reset process from data sheet //////////////
    __delay_ms(40);
    WriteCommandToLCD(0x30);

    __delay_ms(6);
    WriteCommandToLCD(0x30);

    __delay_us(300);
    WriteCommandToLCD(0x30);

    __delay_ms(2);
    /////////////////////////////////////////////////////

    WriteCommandToLCD(0x38);    //function set
    WriteCommandToLCD(0x0c);    //display on,cursor off,blink off
    WriteCommandToLCD(0x01);    //clear display
    WriteCommandToLCD(0x06);    //entry mode, set increment

    /* ---------- Build Custom Characters -----------------*/
    for(index =0; index < 8; ++index)
    {
        LCDBuildChar(index, MyDesignObject[index]); /* Build Character at position index*/
    }
}

void WriteStringToLCD(const char *s)
{
    while(*s)
    {
        WriteDataToLCD(*s++);   // print first character on LCD
    }
}

void ClearLCDScreen(void)
{
    WriteCommandToLCD(0x01);    // Clear the screen
    __delay_ms(2);              // Delay for cursor to return at zero position
}

void LCDBuildChar(unsigned char loc, unsigned char *p)
{
    unsigned char i = 0;
    if(loc<8)                                 //If valid address
    {
        WriteCommandToLCD(0x40+(loc*8));      //Write to CGRAM
        for(i=0; i<8; i++)
        {
            WriteDataToLCD(p[i]);
        }             //Write the character pattern to CGRAM
    }
    WriteCommandToLCD(0x80);                  //shift back to DDRAM location 0
}

Proteus Simulation:

lcd custom character

 

Recommended Post: