LCD Interfacing with PIC Microcontroller using MPLABX and XC8

This is our sixth tutorial in our PIC Tutorial Series, in this tutorial we learn Interfacing of 16x2 LCD with PIC Microcontroller. In our previous tutorials we have learnt the basics of PIC using some LED blinking Programs and have also learnt How to use Timers in PIC Microcontroller. You can check here all the tutorials on Learning PIC Microcontrollers using MPLABX and XC8 compiler.

This tutorial will be an interesting one because we will learn How to Interface 16×2 LCD with PIC16F877A, check the detailed Video at the end this tutorial. Gone are the old days where we used LEDs for user indications. Let us see how we can make our projects look more cool and useful by using LCD displays. Also check our previous articles on Interfacing LCD with 8051, with Arduino, with Raspberry Pi, with AVR.

 

Functions for Interfacing LCD with PIC Microcontroller:

To make things easier we have made a small library that could make things easy while using this LCD with our PIC16F877A. The header file "MyLCD.h" is given here for download, which contains all the necessary function to drive the LCD using PIC MCU. Library code is well explained by comment lines but if you still have doubts reach us through the comment section. Also check this article for Basic LCD working and its Pinouts.

Note: It is always recommended to know what is actually happening inside your header file because it will help you in debugging or while changing the MCU.

Now, there are two ways to add this code into your program. You can either copy all the above lines of code in MyLCD.h and paste them before the void main(). Or you can download the header file using the link and add them to the header file of your project (#include " MyLCD.h ";). This can be done by right clicking on the header file and selecting Add existing Item and browsing to this header file.

Here I have copied and pasted the header file code into my main C file. So if you are using our code, then you don’t need to download and add the header file into your program, just use the complete Code given at the end of this Tutorial. Also note that this library will only support PIC16F series PIC Microcontroller.

Here I am explaining each function inside our header file below:

void Lcd_Start(): This function should be the first function that has to be called to start working with our LCD. We should call this function only once to avoid lag in the program.

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);
}

Lcd_Clear(): This function clears the LCD screen and can be used inside loops to clear the appearance of previous data.

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

void Lcd_Set_Cursor(x pos, y pos): Once started, our LCD is ready to take commands, we can instruct the LCD to set its cursor in you preferred location by using this function.  Suppose if, we need out cursor at 5th character of 1st row. Then the function will be void Lcd_Set_Cursor(1, 5)

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 cursor
        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_Print_Char(char data)  : Once the cursor is set we can write a character to its position by simple calling this function.

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): If a group of characters is to be displayed, then the string function can be used.

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 
}

Each time the Lcd_Print_Char(char data)  is called, its respective character values is sent to the data-lines of the LCD. These characters reach the HD44780U in form of bits. Now this IC relates the bits to the character to be displayed by using its ROM memory as shown the below table. You can find bits for all the characters in the datasheet of HD44780U LCD Controller.

LCD-Module-HD44780U-bits-for-Characters

Now, since we are satisfied with our header file let’s build the circuit and test the program. Also check complete header file given in the link given above.

 

Circuit Diagram and Testing:

Below is the circuit diagram for Interfacing 16x2 LCD with PIC Microcontroller.

LCD-Interfacing-with-PIC-Microcontroller-circuit-diagram

I have not shown the Power supply or ICSP connection in the above circuit, since we are using the same board which we have used in previous tutorial, check here.

LCD-Interfacing-with-PIC-Microcontroller-16F877A

 

One important thing to notice in the program is the pin definitions of LCD:

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

These pin definitions can be changed according to the programmers hardware setup. Remember to change the respected port configuration in the main function if you change here.

 

The hardware for this project is very simple. We are going to reuse the same PIC module that we used last time and connect the LCD module to our PIC using jumper wires.

The connection can be understood by the following table:

LCD Pin No.

LCD Pin Name

MCU Pin Name

MCU Pin No.

1

Ground

Ground

12

2

VCC

+5V

11

3

VEE

Ground

12

4

Register Select

RD2

21

5

Read/Write

Ground

12

6

Enable

RD3

22

7

Data Bit 0

NC

-

8

Data Bit 1

NC

-

9

Data Bit 2

NC

-

10

Data Bit 3

NC

-

11

Data Bit 4

RD4

27

12

Data Bit 5

RD5

28

13

Data Bit 6

RD6

29

14

Data Bit 7

RD7

30

15

LED Positive

+5V

11

16

LED Negative

Ground

12

 

Now let us simply make the connections, dump the code to our MCU and verify the output.

LCD-Interfacing-with-PIC-Microcontroller-on-perf-board

If you have any trouble or doubts, please use the comment section. Also check the Demo Video given below.

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.
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 
}

 

int main()
{
    unsigned int a;
    TRISD = 0x00;
    Lcd_Start();
    while(1)
    {
        Lcd_Clear();
        Lcd_Set_Cursor(1,1);
        Lcd_Print_String("Circuit Digest");
        Lcd_Set_Cursor(2,1);
        Lcd_Print_String("WORKING!!");
        __delay_ms(2000);
    }
    return 0;
}

Video: 

Comments (13)

  • ANG's picture
    ANG

    hi, is this source code suitable for 4x20 LCD? i had modified some of the header file but can not get expected outcome for 4x20 lcd.

    May 05, 2017
  • B.Aswinth Raj's picture
    B.Aswinth Raj

    Sorry ANG!!

    This header file will support only 16x2 LCD. But, if you want to use an 4x20 you have to come up with your own library by reading the datasheet of that particular LCD. 

    Can you tell me what kind of output you are getting!! may be i can guide you in the right path !!

    Thanks!!

    May 06, 2017
  • MOMO's picture
    MOMO

    Hi,
    First of all, thank you for this awesome explanation. I'm actually working on Interfacing the PIC16F877A with a TC74 temperature sensor, can you guide me through please.
    Thanks!!

    May 20, 2017
  • B.Aswinth Raj's picture
    B.Aswinth Raj

    Hi MOMO,

    Thank you for your words.

    Interfacing TC74 with PIC requires you to know I2C In hand. I will make a tutorial to explain how to use I2C with PIC soon.  I will also consider making a tutorial interface RC74 with PIC. If you have any other sensors in mind which you use frequently and would like to have a tutorial for that, list them below it will be of great use to me!!!

    Thank you!!

    May 21, 2017
  • Ebenezer's picture
    Ebenezer

    I'm very pleased to discover your site, and I commend the great work you are doing.

    May 23, 2017
  • Kozmotronik's picture
    Kozmotronik

    Thanks you very much for The tutorial. This is one of the best one I have ever seen.

    Jul 15, 2017
  • TK's picture
    TK

    hi. I tried this code on the PIC16F690 but I can't get it to work. I changed to PortC since the 16F690 doesn't have PortD but it still doesn't work. please help

    Sep 19, 2017
  • TK's picture
    TK

    hi. I tried this code on the PIC16F690 but it doesn't work. I changed to PortC since the 16F690 doesn't have PortD but it still doesn't work. please help

    Sep 19, 2017
  • B.Aswinth Raj's picture
    B.Aswinth Raj

    Try out with simulation before proceeding with actual hardware. More over I cannot help you without looking at your codes

    Sep 21, 2017
  • WanYu's picture
    WanYu

    Hi, can you also provide the explanation for the main part. I dont understand what are you doing at that part. Thank you.

    Oct 02, 2017
  • B.Aswinth Raj's picture
    B.Aswinth Raj

    H WanYu,

    You would have to read the previous tutorial to understand the complete program

    Oct 02, 2017
  • Othman's picture
    Othman

    Hello,
    I am trying to print to a 16x2 LCD screen with my PIC16F1933 using the code above. The programs compiles and gives no errors, but nothing is printed on the sreen. Except in the header file it gives a yellow triangle with the comment: "implicit signed to unsignedconversion"
    Does someone know what the problem could be?

    Thank you in advance

    Dec 06, 2017
  • B.Aswinth Raj's picture
    B.Aswinth Raj

    The problem is not with your program. Check your connection mainlhy the contrast pins

    Dec 11, 2017

Leave a comment