Character LCD Interfacing with PIC Microcontroller 4 bit mode

16*2 LCD Interfacing with PIC Microcontroller in 4-bit Mode

In this blog post, we will learn 4 bit LCD interfacing with PIC Microcontroller (PIC16F877A). In my previous blog post, we discussed “how to interface 16×2 LCD with PIC Microcontroller (PIC16F877A) in an 8-bit Mode“.

Here I assume that you already know how to interface LCD in 8-bit mode. Here we will also see the C program to interface LCD in 4-bit mode with circuit diagram.

As we know microcontroller has a fixed number of GPIO pins. So generally, LCD is interfaced in 4-bit mode with microcontrollers to save I\O pins of microcontrollers. Before beginning any further I assume that you know the difference between 4-bit and 8-bit LCD interfacing mode with microcontrollers.

 

Note: In LCD 4-bit data write only on the upper nibble of the data bus, means that only D4, D5, D6, and D7 data bus are useful.

 

Pin Configuration of 16*2 Alphanumeric LCD:

A 16×2 Liquid Crystal Display has two rows and each row contains 16 columns. There are 16 pins in the LCD module, the pin configuration us given below,

lcd pins

 

 

 

Some useful commands for 16×2 Alphanumeric LCD:

Below I am mentioning few commands related to the 16×2 LCD. You can interface 16×2 LCD in two-mode 8bit and 4bit.

 

Steps to send command on 16×2 LCD:

  • E=1; enable pin should be high.
  • RS=0; Register select should be low.
  • R/W=0; Read/Write pin should be low.

 

Steps to send the character on 16×2 LCD:

  • E=1; enable pin should be high.
  • RS=1; Register select should be high.
  • R/W=0; Read/Write pin should be low.

Connection of 16×2 LCD in 4-bit mode

In 4-bit mode, the only upper nibble of the data bus is used for reading and writing. So D4, D5, D6, and D7 will only attach with GPIO of the microcontroller for reading and writing. In below Image LCD data pins attached with PORT 2. Another connection of LCD 4-bit is the same as in the 8-bit mode.

LCD PIN Configuration

 

 

In this blog post, I have written codes to display a moving message “Hello world!”. I have used MPLAB v8.85 with the HI-TECH C v9.83 compiler to creating this project “16*2 Character LCD Interfacing with PIC Microcontroller in 4-bit Mode”.

 

/* Name     : main.c
*  Purpose  : Main file for LCD 4 bit-mode.
*  Author   : Amlendra Kumar
*  Website  : https://aticleworld.com
*/
#include<htc.h>
#include<string.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    			RB0     // Enable pin for LCD
#define LCD_RS	 			RB1     // RS pin for LCD
#define LCD_Data_Bus_D4		RB4		// Data bus bit 4
#define LCD_Data_Bus_D5		RB5		// Data bus bit 5
#define LCD_Data_Bus_D6		RB6		// Data bus bit 6
#define LCD_Data_Bus_D7		RB7		// Data bus bit 7

// Define Pins direction registrers
#define LCD_E_Dir     			TRISB0
#define LCD_RS_Dir   	 		TRISB1
#define LCD_Data_Bus_Dir_D4   	TRISB4
#define LCD_Data_Bus_Dir_D5     TRISB5
#define LCD_Data_Bus_Dir_D6  	TRISB6
#define LCD_Data_Bus_Dir_D7 	TRISB7

// Constants
#define E_Delay       500


// Function Declarations
void WriteCommandToLCD(unsigned char);
void WriteDataToLCD(char);
void InitLCD(void);
void WriteStringToLCD(const char*);
void ClearLCDScreen(void);


int main(void)
{
    CMCON = 0x07;// Turn comparator off
    InitLCD();  // Initialize LCD in 8bit mode
    int siLoop;
    int msgLen = 0;
    const char *msg ="Hello World!";
    
    msgLen = strlen(msg);
    while(1)
    {
        WriteCommandToLCD(0x8f); /*Address of DDRAM*/
        WriteStringToLCD(msg);  // Write Hello World on LCD
        for(siLoop=0; siLoop < msgLen; siLoop++)
        {
            WriteCommandToLCD(0x1c);
            __delay_us(100000);      // so that LCD can latch the
        }
    }
    
    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

    PORTB &= 0x0F;			  // Make Data pins zero
    PORTB |= (Command&0xF0);  // Write Upper nibble of data
    ToggleEpinOfLCD();		  // Give pulse on E pin

    PORTB &= 0x0F;			  // Make Data pins zero
    PORTB |= ((Command<<4)&0xF0); // Write Lower nibble of data
    ToggleEpinOfLCD();		  // Give pulse on E pin
}


void WriteDataToLCD(char LCDChar)
{
    LCD_RS = 1;				  // It is data

    PORTB &= 0x0F;			  // Make Data pins zero
    PORTB |= (LCDChar&0xF0);  // Write Upper nibble of data
    ToggleEpinOfLCD();		  // Give pulse on E pin

    PORTB &= 0x0F;			  // Make Data pins zero
    PORTB |= ((LCDChar<<4)&0xF0); // Write Lower nibble of data
    ToggleEpinOfLCD();		  // Give pulse on E pin
}


void InitLCD(void)
{
    // Firstly make all pins output
    LCD_E  		 		 = 0;   // E  = 0
    LCD_RS  	 		 = 0;   // RS = 0
    LCD_Data_Bus_D4		 = 0;  	// Data bus = 0
    LCD_Data_Bus_D5		 = 0;  	// Data bus = 0
    LCD_Data_Bus_D6		 = 0;  	// Data bus = 0
    LCD_Data_Bus_D7		 = 0;  	// Data bus = 0
    LCD_E_Dir    		 = 0;   // Make Output
    LCD_RS_Dir    	 	 = 0;   // Make Output
    LCD_Data_Bus_Dir_D4  = 0;   // Make Output
    LCD_Data_Bus_Dir_D5  = 0;   // Make Output
    LCD_Data_Bus_Dir_D6  = 0;   // Make Output
    LCD_Data_Bus_Dir_D7  = 0;   // Make Output

    ///////////////// Reset process from datasheet //////////////
    __delay_ms(40);

    PORTB &= 0x0F;			  // Make Data pins zero
    PORTB |= 0x30;			  // Write 0x3 value on data bus
    ToggleEpinOfLCD();		  // Give pulse on E pin

    __delay_ms(6);

    PORTB &= 0x0F;			  // Make Data pins zero
    PORTB |= 0x30;			  // Write 0x3 value on data bus
    ToggleEpinOfLCD();		  // Give pulse on E pin

    __delay_us(300);

    PORTB &= 0x0F;			  // Make Data pins zero
    PORTB |= 0x30;			  // Write 0x3 value on data bus
    ToggleEpinOfLCD();		  // Give pulse on E pin

    __delay_ms(2);

    PORTB &= 0x0F;			  // Make Data pins zero
    PORTB |= 0x20;			  // Write 0x2 value on data bus
    ToggleEpinOfLCD();		  // Give pulse on E pin

    __delay_ms(2);
    /////////////// Reset Process End ////////////////
    WriteCommandToLCD(0x28);    //function set
    WriteCommandToLCD(0x0c);    //display on,cursor off,blink off
    WriteCommandToLCD(0x01);    //clear display
    WriteCommandToLCD(0x06);    //entry mode, set increment
}


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


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

 

Proteus Simulation of 4 bit LCD interfacing with PIC:

16*2 Character LCD Interfacing with PIC Microcontroller in 4-bit Mode

 

Code Analysis:

InitLCD():

This function is used to initialize the LCD with proper commands. Below I am mentioning some commands which are used in LCD initialization.

0x28 is used for 4-bit data initialization.
0x0C for making LCD display on and cursor off.
0X01 for clearing the display of the LCD.
0x06 for increment cursor (shift cursor to right)

 

WriteCommandToLCD():

Whenever you send the command on 16×2 LCD, you have to set RS and RW pin low and E (enable) pin high. In code, I have written a function WriteCommandToLCD() which set RS pin low and E pin high. You can see the circuit I have already set RW pin low with the connection.

void WriteCommandToLCD(unsigned char Command)
{
    LCD_RS = 0;          // It is a command
    PORTB &= 0x0F;        // Make Data pins zero
    PORTB |= (Command&0xF0);  // Write Upper nibble of data
    ToggleEpinOfLCD();      // Give pulse on E pin
    PORTB &= 0x0F;        // Make Data pins zero
    PORTB |= ((Command<<4)&0xF0); // Write Lower nibble of data
    ToggleEpinOfLCD();      // Give pulse on E pin
}

 

WriteDataToLCD():

Whenever you send the character on 16×2 LCD for display, you have to set RS pin high, RW pin low and E (enable) pin high. In code, I have written a function WriteDataToLCD() which set RS pin high and E pin high. Due to the hardware connection, the RW PIN already low.

void WriteDataToLCD(char LCDChar)
{
    LCD_RS = 1;          // It is data
    PORTB &= 0x0F;        // Make Data pins zero
    PORTB |= (LCDChar&0xF0);  // Write Upper nibble of data
    ToggleEpinOfLCD();      // Give pulse on E pin
    PORTB &= 0x0F;        // Make Data pins zero
    PORTB |= ((LCDChar<<4)&0xF0); // Write Lower nibble of data
    ToggleEpinOfLCD();      // Give pulse on E pin
}

 

Recommended Post: