LCD’s are a useful tool when you want your microcontroller’s output to be displayed. They are available in different sizes with 16*2 Character LCD being the most common one. Here 16 indicates the number of columns and 2 indicates the number of rows. So a maximum of 32 characters can be displayed at a time. Other sizes include 8*2, 12*2, 20*2, 20*4 etc. In this article though we will be using a 16*2 character LCD.

LCD operation

To program an LCD, it must be fed with Commands and Data. Commands essentially tell your LCD which operations to perform. These are predefined hexadecimal numbers which the manufacturer has already defined for them. You simply need to write these into your LCD during programming for achieving the desired operation. Data on the, on the other hand, is the actual data you want to be displayed on your LCD.
Now how does the LCD understands whether you are writing a command or a data in it? Well, the RS(pin 4) is the answer. During programming, before writing a command, RS pin should be set to 0. whereas this bit must be set to 1 before writing data into it. Let us see the function of the remaining pins.

VSSGround pin for LCD
VCCVCC pin for LCD (5v)
VEEContrast adjustment pin
RSRegister select.
0 for command.
1 for data
R/WRead/write select bit
0 for write operation.
1 for read operation
EEnable bit.
This bit needs to be set before writing data or command
DB0-DB78 Data bits. DB0 to DB7. In four bit mode, only DB7-DB4 are used.
LED+LED's positive, must be connected to VCC.
LED-LED's negative, must be connected to VSS.

As you see from the above table the RS pin is set to 0 before writing a command, whereas it should be set to 1 before writing data. Other than pin RS, PIN R/W needs to be 0 for both, command write as well as data write. This pin is thus often grounded.


Timing representation of Bits during different operation.
Different bit Status while programming Character LCD

Bit setting during different operation

The above diagram shows the bit setting during different operation. During command write, and data write R/W pin is 0, so you can ground the R/W pin or you can still connect it to one of your microcontroller pins and set it to 0. The enable bit must be set momentarily after a read or a write and must be cleared after some delay. The enable bit is used to initiate data transfer within the LCD.


8-bit and 4-bit mode

The character LCD can be used in either 8-bit mode or 4-bit mode. In 8-bit mode, the commands, as well as the data, are directly sent using the 8 data bits DB0-DB7.
In 4-bit mode only the higher 4 bits i.e. DB7-DB4 are used. So to send an 8-bit data or command, the higher 4 bits are sent first followed by the lower 4 bits. This mode reduces the number of I/O pins required for communication but is comparably slower than 8-bit mode since data and commands are to be sent as a set 4+4 bits. The set of commands for selection of different operations is given below.

D7 D6 D5 D4 D3 D2 D1 D0 Description
0 0 0 0 0 0 0 1 Clear Display
0 0 0 0 0 0 1 * Return cursor to 1st position
0 0 0 0 0 1 ID S Set cursor move direction
0 0 0 0 1 D C B Enable display
0 0 0 1 SC RL * * Move cursor or shift display
0 0 1 DL N F * * Set interface length
Bit description

Set Cursor move direction:
ID – Increment the Cursor After Each Byte Written to Display.
S – Shift Display when Byte Written to Display.

Enable Display:
D – Turn Display On.
C – Turn Cursor On.
B – Cursor Blink On.

Move Cursor or Shift Display:
SC – Display Shift On.
RL – Direction of Shift Right(1)/Left(0)

Set Interface Length:
DL – Set Data Interface Length. Set for 8-bit. Clear for 4-bit.
N – Number of Display Lines. When set enables 2 rows. when cleared enables 1.
F – Character Font 5×10 when set to 1. 5×7 when set to 0.

How to use these commands?
Suppose you want to work with the 4-bit mode. Have a look at the last row of the above table. For 4-bit mode bit DL needs to be cleared. Next bit ‘N’ enables 2 rows when set. Finally, we will clear bit ‘F’ for character font of 5×7. So the last row of the above table becomes,
0 0 1 0 1 0 * * which is 28 in hexadecimal.
So eventually you need to pass value 0x28 as your LCD command. Similarly, other operations can be executed by writing these values as commands.


Circuit connections for interfacing character LCD with PIC16F887
Following image show interfacing of 16*2 character LCD with PIC microcontroller.

Character LCD interfacing with PIC16F887 microcontroller.


Program for Character LCD in 4-bit mode.

In the program below, I’ve used PIC16F887 microcontroller’s internal oscillator which initially by default runs at 4Mhz.

#pragma config FOSC = INTRC_NOCLKOUT// Oscillator Selection bits (INTOSCIO oscillator: I/O function on RA6/OSC2/CLKOUT pin, I/O function on RA7/OSC1/CLKIN)
#pragma config WDTE = OFF // Watchdog Timer Enable bit (WDT enabled)
#pragma config PWRTE = OFF // Power-up Timer Enable bit (PWRT disabled)
#pragma config MCLRE = ON // RE3/MCLR pin function select bit (RE3/MCLR pin function is MCLR)
#pragma config CP = OFF // Code Protection bit (Program memory code protection is disabled)
#pragma config CPD = OFF // Data Code Protection bit (Data memory code protection is disabled)
#pragma config BOREN = ON // Brown Out Reset Selection bits (BOR disabled)
#pragma config IESO = OFF // Internal External Switchover bit (Internal/External Switchover mode is disabled)
#pragma config FCMEN = OFF // Fail-Safe Clock Monitor Enabled bit (Fail-Safe Clock Monitor is disabled)
#pragma config LVP = OFF // Low Voltage Programming Enable bit (RB3 pin has digital I/O, HV on MCLR must be used for programming)

#pragma config BOR4V = BOR40V // Brown-out Reset Selection bit (Brown-out Reset set to 4.0V)
#pragma config WRT = OFF

#include <xc.h>
#define _XTAL_FREQ 4000000

void lcd_cmd(unsigned char c);
void lcd_data(unsigned char d);
void lcd_init(void);
void enable(void);
char print[] = "Techetrx is Live";
char *pointer;

void main()
pointer = &print[0];
TRISD = 0x00;
TRISC = 0x00;
PORTD = 0x00;
PORTC = 0x00;
lcd_cmd(0x80); // Setting cursor position to 1st location

while(*pointer != '\0')

void lcd_init(void)
// LCD initial initialization 


// LCD command setting initialization

lcd_cmd(0x28); // 4 bit interface length. 2 rows enabled.

lcd_cmd(0x10); // Move cursor or shift display

lcd_cmd(0x0c); // Enable display. Cursor off.

lcd_cmd(0x06); // Increment cursor position after each byte 

lcd_cmd(0x01); // clear display


void lcd_cmd(unsigned char c)
RD7 = 0;
RD6 = 0;
PORTC = (PORTC&0x0f)|(0xf0&c);
PORTC = ((PORTC&0x0f)|(0x0f&c)<<4);

void lcd_data(unsigned char d)
RD7 = 1;
RD6 = 0;
PORTC = (PORTC&0x0f)|(0xf0&d);
PORTC = ((PORTC&0x0f)|(0x0f&d)<<4);


void enable(void)
RD5 = 1;
RD5 = 0;


This is how my setup looked like. I’ve used a 12-volt adapter which is regulated to 5v using 7805. As you can see, there is no external crystal as I’m using PIC16F887’s internal oscillator. If you’re finding any difficulty, let me know in the comments. Good luck!


Following image shows practical setup for interfacing LCD with pic microcontroller.

LCD interfacing with PIC16F887 microcontroller

Images shows interfacing of PIC microcontroller with LCD.

PIC microcontroller interfacing with LCD



Electronics engineer graduated from M.H. Saboo Siddik college of engineering. Currently working as Jr. Innovative engineer. Skilled in 8051, PIC and ARM microcontrollers. Circuit analyzation and Debugging. Constantly looking to acquire more skills which would help myself to become more proficient in embedded domain. Founder and blogger at LinkedIn profile:


Leave a Reply

Your email address will not be published. Required fields are marked *