LCD Interfacing with PIC Microcontroller using MPLABX and XC8

Published  January 24, 2017   39
Aswinth Raj
Author
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
Have any question realated to this Article?

Ask Our Community Members

Comments

Submitted by ANG on Fri, 05/05/2017 - 19:47

Permalink

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.

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!!

Submitted by MOMO on Sat, 05/20/2017 - 05:05

Permalink

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!!

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!!

Submitted by TK on Tue, 09/19/2017 - 19:56

Permalink

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

Submitted by TK on Tue, 09/19/2017 - 20:05

Permalink

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

Submitted by Othman on Wed, 12/06/2017 - 14:52

Permalink

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

Submitted by steven collins on Mon, 01/22/2018 - 16:54

Permalink

hi I am a complete beginner with c code. can you explain the meaning of Lcd_Cmd and how program recognises it.

"Lcd_Cmd" is a function, to know what it does you can look into the function definition of void Lcd_Cmd.

 Simply put, it reads the HEX value as input parameters and sets the LCD register bits accordingly 

Submitted by Markpolo on Wed, 02/07/2018 - 08:55

Permalink

Hi, thanks for the code you have given.

However, I can't seem to display anything on the 16x2 lcd module. The lcd is not broken and microcontroller seems to work perfectly fine, although what I used is the pic16f1937. Any thoughts on how will I make it work?

Yup, my connections are correct. It is also successful in the debugging stage in the mplab x. There is something tho I think in the establishment of the delays. According to pic.h header when using the __delay_ms macro we should take note of the following:

// NOTE: To use the macros below, YOU must have previously defined _XTAL_FREQ
#define __delay_us(x) _delay((unsigned long)((x)*(_XTAL_FREQ/4000000.0)))
#define __delay_ms(x) _delay((unsigned long)((x)*(_XTAL_FREQ/4000.0)))

I don't understand this much but I think when you used 5ms for example the delay is not 5ms.
Another thing, in your code, did you intentionally write Lcd_clear only and did not include "void"?

Submitted by vrushali on Sun, 04/08/2018 - 15:11

Permalink

hello sir.....i tried implementing your code but all i could see were black boxes on my lcd...also the compiler showed error in using __delay_ms() function.....plz guide

If the compiler has showed error, it means your program is not compiled and hence not uploaded to board. So do not expect any output without the program being uploaded successfully/ What error did you face with the __delay_ms() paste the error here so that you can get help

Submitted by Ramdas on Sun, 04/08/2018 - 17:51

Permalink

Build in MPLAB X IDE OK. But simulation ended with error message - DSIM model LCDALPHA.DLL failed to authorise - missing or invalid customer key and Mixed model PIC16.DLL failed to authorise - missing or invalid customer key.

Please help me in resolving the issue.

Submitted by Steffen on Mon, 05/07/2018 - 02:26

Permalink

Leider viel Fehler da drinnen:
Was soll das bspw. ?
für (int i = 1065244; i <= 0; i-) NOP (); //int viel zu groß, Zeit zu lang!!!
Warum nicht gleich wie unten Zeit mit delay_ms() ?

Submitted by Robert on Tue, 07/17/2018 - 22:19

Permalink

Hello Sir,
When I complied, the below 3 items shown "Unable to identifier". Could you please teach me how to slove this issue. Thank you.
1. for(int i=1065244; i<=0; i--) NOP();
2. for(int i=2130483; i<=0; i--) NOP();
3. for(int i=2130483; i<=0; i--) NOP();

Submitted by Sijin on Sat, 07/21/2018 - 15:36

Permalink

i am stuck in this tutorial not being able to undrstand the header file.
i also want to know how you read the data sheet??because as a beginner i don't understand how touse the data sheet,should i read the whole 60 pages data sheet......if so pic microcontroller has a lot of pages in its data sheet.....pls reply....

Submitted by Albert on Thu, 08/02/2018 - 20:32

Permalink

In looking at LCD_Start I noticed that LCD_CMD was repeated multiple times using the same hex value. What is the purpose of repeatedly sending the same hex value thru LCD_CMD, and therefore thru Lcd_Set_Bit? Thanks.

Under void Lcd_Print_Char why was 2130483 chosen for the value of int and what does that portion of the function do?

Tried this one.  It compiled with warnings with C90 specified in XC8 Global settings.  Worked fine. don't understand  if(data_bit& 1).  It's been a long time since I learned C.  Was before CCP.

Hello !

Thanks for this tutorial!  It was really helpful.

I have just a doubt. How can I show the value of a variable on the LCD? I've tried putting the variable on the Lcd_Print_char and Lcd_Print_String, but it just doesn't show on the LCD.

Could you help me with that?

Thanks !

Thank you for this.

I used the code for PIC16F88 microcontroller.

Initially, I did not set LVP=OFF and the LCD was not displaying anything. But then I checked your code again and realized the problem.

Good job!