Interfacing LCD 16x2 in 4-bit mode with PIC18F4550

Introduction

LCDs (Liquid Crystal Displays) are used for displaying status or parameters in embedded systems.

LCD 16x2 is a 16 pin device which has 8 data pins (D0-D7) and 3 control pins (RS, RW, EN). The remaining 5 pins are for supply and backlight for the LCD.

The control pins help us configure the LCD in command mode or data mode. They also help configure read mode or write mode and also when to read or write.

LCD 16x2 can be used in 4-bit mode or 8-bit mode depending on the requirement of the application. In order to use it, we need to send certain commands to the LCD in command mode and once the LCD is configured according to our need, we can send the required data in data mode.

For more information about LCD 16x2 and how to use it, refer to the topic LCD 16x2 display module in the sensors and modules section.

LCD16x2 Pins
LCD16x2 Pinout

 

LCD 16x2 4-bit Mode 

  • In 4-bit mode, data/command is sent in a 4-bit (nibble) format.
  • To do this 1st send Higher 4-bit and then send lower 4-bit of data/command.
  • Only 4 data (D4 - D7) pins of 16x2 of LCD are connected to the microcontroller and other control pins i.e. RS (Register select), RW (Read/write), E (Enable) are connected to other GPIO Pins of the controller.
  • Therefore, due to such connections, we can save four GPIO pins which can be used for another application.

 

Interfacing Diagram

LCD16x2 Interfacing with PIC18F4550
LCD16x2 Interfacing in 4-bit Mode with PIC18F4550

 

Programming LCD16x2 with PIC18F4550 

Initialization

  1. Wait for 15ms, Power-on initialization time for LCD16x2.
  2. Send 0x02 command which initializes LCD 16x2 in 4-bit mode.
  3. Send 0x28 command which configures LCD in 2-line, 4-bit mode, and 5x8 dots.
  4. Send any Display ON command (0x0E, 0x0C)
  5. Send 0x06 command (increment cursor)
void LCD_Init()
{
    LCD_Port = 0;         /*PORT as Output Port*/
    MSdelay(15);          /* 15 ms, Power-On delay*/
    LCD_Command(0x02);    /*send for initialization of LCD with nibble method */
    LCD_Command(0x28);    /*use 2 line and initialize 5*7 matrix in (4-bit mode)*/
    LCD_Command(0x01);    /*clear display screen*/
    LCD_Command(0x0c);    /*display on cursor off*/
    LCD_Command(0x06);    /*increment cursor (shift cursor to right)*/	
}

 

Now we successfully initialized LCD & it is ready to accept data in 4-bit mode to display.

To send command/data to 16x2 LCD we have to send a higher nibble followed by a lower nibble. As 16x2 LCD’s D4 - D7 pins are connected as data pins, we have to shift the lower nibble to the right by 4 before transmitting.

 

Command write function

  1. First Send Higher nibble of command.
  2. Make RS pin low, RS=0 (command reg.)
  3. Make RW pin low, RW=0 (write operation) or connect it to ground.
  4. Give High to Low pulse at Enable (E).
  5. Send lower nibble of command.
  6. Give High to Low pulse at Enable (E).
void LCD_Command(unsigned char cmd )
{
	ldata = (ldata & 0x0f) |(0xF0 & cmd);  /*Send higher nibble of command first to PORT*/ 
	RS = 0;  /*Command Register is selected i.e.RS=0*/ 
	EN = 1;  /*High-to-low pulse on Enable pin to latch data*/ 
	NOP();
	EN = 0;
	MSdelay(1);
    ldata = (ldata & 0x0f) | (cmd<<4);  /*Send lower nibble of command to PORT */
	EN = 1;
	NOP();
	EN = 0;
	MSdelay(3);
}

 

Data write function 

  1. First Send a Higher nibble of data.
  2. Make RS pin high, RS=1 (data reg.)
  3. Make RW pin low, RW=0 (write operation) or connect it to ground.
  4. Give High to Low pulse at Enable (E).
  5. Send lower nibble of data.
  6. Give High to Low pulse at Enable (E).
void LCD_Char(unsigned char dat)
{
	ldata = (ldata & 0x0f) | (0xF0 & dat);  /*Send higher nibble of data first to PORT*/
	RS = 1;  /*Data Register is selected*/
	EN = 1;  /*High-to-low pulse on Enable pin to latch data*/
	NOP();
	EN = 0;
	MSdelay(1);
    ldata = (ldata & 0x0f) | (dat<<4);  /*Send lower nibble of data to PORT*/
	EN = 1;  /*High-to-low pulse on Enable pin to latch data*/
	NOP();
	EN = 0;
	MSdelay(3);
}

 

16x2 LCD 4-bit mode program for PIC18F4550

/*
 * Interfacing 16x2 LCD with PIC18F4550 in 4-bit mode
 * www.electronicwings.com
 */
 
#include <pic18f4550.h>
#include "Configuration_Header_File.h"

/*********************Definition of Ports********************************/

#define RS LATB0  /*PIN 0 of PORTB is assigned for register select Pin of LCD*/
#define EN LATB1  /*PIN 1 of PORTB is assigned for enable Pin of LCD */
#define ldata LATB  /*PORTB(PB4-PB7) is assigned for LCD Data Output*/ 
#define LCD_Port TRISB  /*define macros for PORTB Direction Register*/

/*********************Proto-Type Declaration*****************************/

void MSdelay(unsigned int );       /*Generate delay in ms*/
void LCD_Init();                   /*Initialize LCD*/
void LCD_Command(unsigned char );  /*Send command to LCD*/
void LCD_Char(unsigned char x);    /*Send data to LCD*/
void LCD_String(const char *);     /*Display data string on LCD*/
void LCD_String_xy(char, char , const char *);
void LCD_Clear();                  /*Clear LCD Screen*/


int main(void)
{    
    OSCCON = 0x72;  /*Use internal oscillator and 
                      set frequency to 8 MHz*/ 
	LCD_Init();  /*Initialize LCD to 5*8 matrix in 4-bit mode*/    
	LCD_String_xy(1,5,"Hello");  /*Display string on 1st row, 5th location*/
    LCD_String_xy(2,1,"ElectronicWings");  /*Display string on 2nd row,1st location*/           	
	while(1);		
}

/****************************Functions********************************/

void LCD_Init()
{
    LCD_Port = 0;       /*PORT as Output Port*/
    MSdelay(15);        /*15ms,16x2 LCD Power on delay*/
    LCD_Command(0x02);  /*send for initialization of LCD 
                          for nibble (4-bit) mode */
    LCD_Command(0x28);  /*use 2 line and 
                          initialize 5*8 matrix in (4-bit mode)*/
	LCD_Command(0x01);  /*clear display screen*/
    LCD_Command(0x0c);  /*display on cursor off*/
	LCD_Command(0x06);  /*increment cursor (shift cursor to right)*/	   
}

void LCD_Command(unsigned char cmd )
{
	ldata = (ldata & 0x0f) |(0xF0 & cmd);  /*Send higher nibble of command first to PORT*/ 
	RS = 0;  /*Command Register is selected i.e.RS=0*/ 
	EN = 1;  /*High-to-low pulse on Enable pin to latch data*/ 
	NOP();
	EN = 0;
	MSdelay(1);
    ldata = (ldata & 0x0f) | (cmd<<4);  /*Send lower nibble of command to PORT */
	EN = 1;
	NOP();
	EN = 0;
	MSdelay(3);
}


void LCD_Char(unsigned char dat)
{
	ldata = (ldata & 0x0f) | (0xF0 & dat);  /*Send higher nibble of data first to PORT*/
	RS = 1;  /*Data Register is selected*/
	EN = 1;  /*High-to-low pulse on Enable pin to latch data*/
	NOP();
	EN = 0;
	MSdelay(1);
    ldata = (ldata & 0x0f) | (dat<<4);  /*Send lower nibble of data to PORT*/
	EN = 1;  /*High-to-low pulse on Enable pin to latch data*/
	NOP();
	EN = 0;
	MSdelay(3);
}

void LCD_String(const char *msg)
{
	while((*msg)!=0)
	{		
	  LCD_Char(*msg);
	  msg++;	
    }
}

void LCD_String_xy(char row,char pos,const char *msg)
{
    char location=0;
    if(row<=1)
    {
        location=(0x80) | ((pos) & 0x0f);  /*Print message on 1st row and desired location*/
        LCD_Command(location);
    }
    else
    {
        location=(0xC0) | ((pos) & 0x0f);  /*Print message on 2nd row and desired location*/
        LCD_Command(location);    
    }  
    
    LCD_String(msg);
}

void LCD_Clear()
{
   	LCD_Command(0x01);  /*clear display screen*/
    MSdelay(3);
}

void MSdelay(unsigned int val)
{
 unsigned int i,j;
 for(i=0;i<val;i++)
     for(j=0;j<165;j++);  /*This count Provide delay of 1 ms for 8MHz Frequency */
}

Components Used

PICKit 4 MPLAB
PICKit 4 MPLAB
1
PIC18f4550
PIC18f4550
1
LCD16x2 Display
LCD16x2 Display
1

Downloads

PIC18F4550 LCD 16x2 4bit Mode Download
PIC18F4550 LCD 16x2 4bit Proteus Simulation Download
LCD16x2 Datasheet Download
Ad