16×2 LCD Interfacing in 8bit mode

lcd_real

Hello there!! In this post I’ll tell you about 16×2 LCD’s and their interfacing in 8 bit mode. As you all know LCD stands for liquid crystal display. Now earlier we used to use 7 segment displays for display purposes, but now LCD’s are preferred. The main reason is we need less number of databus lines for interfacing LCD’s as compared to 7 segment displays. Other reason is we can print various characters on the screen. Now the basic characters are already saved inside CGROM(Character Generator ROM). So you need to send only the ASCII values in order to display the character on screen. So let us see what a 16×2 LCD reakky means. It has 2 rows and 16 columns. So basically a 16×2 LCD has 32 blocks where one can display data. Each block has certain number of pixels. You can draw your own character by saving the pattern of pixels.

Ok then let’s begin. First things first lets get the datasheet. The link is : Datasheet Link

Please download the datasheet because a datasheet tells you everything there is to know the electrical parameters, command registers, pin-outs and so on. Assuming you have the datasheet with you, let’s go further.

Pin-out:

Lcd16x2Now there is a protruding rectangular portion on this LCD. This will help you identify which pin is which. Now let us see what each pin does exactly. But then they have also printed 16 and 1 on the back of LCD, so no need to worry about connecting the pins inverted.

lcd_pinoutThe pin features are explained in the table. The contrast adjust input is nothing but output taken from a pot.contrast-input

So basically when you vary the pot , you get different values of voltage from the voltage divider network. And thus you can change the contrast to suit your visual needs. (Caution: Do not give the LCD voltage greater than 5 Volts. Your LCD may get damaged. By more I’m not talking about 5.1 Volts but 6 V and beyond.)

RS, R/W,E are the control signals of LCD. DB0 to DB7 are the databus lines. You send the command word as well as the data to be written on this bus.

Let’s see a little about the control signals first.

RS : This stands for register select. The two registers in LCD are the data register and the command word/code register. In order to tell LCD that the bits on databus are for which register we make use of RS control signal via the RS pin. When you make this pin high you select data register, where you’ll send the ASCII values to be displayed on screen. When you make RS low you select the command word register where you’ll be sending all the commands for configuring and initializing the LCD.

RS = 1 —–> Data Register

RS = 0 —–> Command Code Register

R/W : This stands for read or write. The read is active high signal and write is active low. Thus when you want to read from the LCD you make the signal on this pin high and when you want to write you make the signal on this pin low.

R/W = 1 —-> Read Operation

R/W = 0 —-> Write Operation.

(For those who are wondering why W has no bar on its top indicating an active low signal, there should be one. Its just that I don’t know how to type W bar!!)

E : This stands for enable. This is a edge triggering signal which is used while writing or reading data to/from LCD respectively. E line is negative edge triggered for write while it is positive edge triggered for the read. The timing diagram given in datasheet tells about the minimum delay between the level transitions.

E = high to low / negative edge triggered —-> Write

E = low to high / positive edge triggered  —-> Read

Busy Flag :  The concept of busy flag is beautiful. Now the LCD internal processor takes time to latch and make the necessary adjustments as per the command word. While the LCD’s internal processor is busy this flag is set. So one should check the status of this flag before sending the next command word or data. D7 is the busy flag pin. You’ll have to configure the port pin connected to D7 pin as input while checking the flag condition. Along with this we need to make RS = 0 and R/W = 1 , since this is read operation and busy flag is given by command code register mode.

Busy Flag = 1 —-> LCD Busy

Busy Flag = 0 —-> LCD can take next data/command

Well you can give delays also for LCD to finish work, but this is better way if you have enough port pins. Because for reading busy flag status you need R/W signal and thus a port pin.

List of LCD Instructions

commands

Using the above table you can make any command byte. For example we’ll be using this LCD in 8 bit mode so make DL = 1, N = 1 and F =0 respectively. The hexadecimal value that we get is 0x38/038h. This is the command word that we must send to the LCD to initialize it in 8 bit mode and use 2 lines with 5×7 dots.

Command Codes

So these are few of the instruction codes that you come across frequently. Of course you can make these on your own by using the command code syntax table.

DDRAM address:

Display data random access memory. This is where the data you send to data register is stored. And it so happens that you can send the address of block to the command code register to position the cursor at that particular block. For example you want to position the cursor at row 2 column 10 , just send 0CAh to the command code register. So that is about the DDRAM and positioning the cursor.

ddram

Connection Diagram:

interfacing121120131277

The connections are shown in the above pictures.

Code:

/*  8bit_lcd.h
 *  Created on: 12-Nov-2013
 *  Author: Manpreet
 */


#include <msp430g2553.h>;
#define DR		  	P2OUT = P2OUT | BIT0 		// define RS high
#define CWR		   	P2OUT = P2OUT &amp; (~BIT0)	// define RS low
#define READ     	P2OUT = P2OUT | BIT1  	// define Read signal R/W = 1 for reading
#define WRITE    	P2OUT = P2OUT &amp; (~BIT1) 	// define Write signal R/W = 0 for writing
#define ENABLE_HIGH P2OUT = P2OUT | BIT2		// define Enable high signal
#define ENABLE_LOW  P2OUT = P2OUT &amp; (~BIT2)		// define Enable Low signal
unsigned int i;
unsigned int j;
void delay(unsigned int k)
{
	for(j=0;j&lt;=k;j++)
	{
		for(i=0;i&lt;100;i++);

	}

}
void data_write(void)
{
	ENABLE_HIGH;
	delay(2);
	ENABLE_LOW;
}

void data_read(void)
{
	ENABLE_LOW;
	delay(2);
	ENABLE_HIGH;
}

void check_busy(void)
{
	P1DIR &amp;= ~(BIT7); // make P1.7 as input
	while((P1IN&amp;BIT7)==1)
	{
		data_read();
	}
	P1DIR |= BIT7;  // make P1.7 as output
}

void send_command(unsigned char cmd)
{
		check_busy();
		WRITE;
		CWR;
		P1OUT = (P1OUT &amp; 0x00)|(cmd);
		data_write();								// give enable trigger

}

void send_data(unsigned char data)
{
		check_busy();
		WRITE;
		DR;
		P1OUT = (P1OUT &amp; 0x00)|(data);
		data_write();								// give enable trigger
}

void send_string(char *s)
{
	while(*s)
	{
		send_data(*s);
		s++;
	}
}

void lcd_init(void)
{
		P2DIR |= 0xFF;
		P1DIR |= 0xFF;
		P2OUT &amp;= 0x00;
		P1OUT &amp;= 0x00;
		send_command(0x38); // 8 bit mode
		send_command(0x0E); // clear the screen
		send_command(0x01); // display on cursor on
		send_command(0x06);// increment cursor
		send_command(0x80);// cursor position
}

Sample Code

/* LCD_own.c
* Created on: 12-Nov-2013
* Author: Manpreet
* In this program we interface the lcd in 8 bit mode. We send strings and display it on the screen.
*/
#include "8bit_lcd.h"

void main(void)
{
WDTCTL = WDTPW + WDTHOLD; // stop watchdog timer
lcd_init();
send_string("Manpreet Singh");
send_command(0xC0);
send_string("Minhas");
while(1){}
}

For code explanation watch:

Output:

output

This is all regarding the 8 bit mode interfacing. I’ll be writing  about 4 bit mode and creating custom character if I successfully learn and perform the same. Thank you for reading this post. Hope it was useful and informative.

References :

The 8051 Microcontroller and Embedded Systems using Assembly and C by Mazidi (ISBN-978-81-317-1026-5)

HD44780U Datasheet (Download Link)

Advertisements

57 thoughts on “16×2 LCD Interfacing in 8bit mode

  1. Pingback: 16×2 LCD interfacing in 4 bit mode | My journey with MSP430 LaunchPad

      • LCD_DATA = ( ( c >> 4 ) & 0x0F );
        LCD_STROBE();
        LCD_DATA = ( c & 0x0F );
        LCD_STROBE();
        }
        would u pls tell me the meaning of this?
        thanks

      • Okay lets start from basic. You need to send the ASCII value of the character to the LCD controller so that it may display the same. These ASCII values are 7/8 bit in size. Now if you are using 8 bit mode then there is no need to worry as you have sufficient data lines to send the data in a single go. But when you use 4 bit mode you need to send the same data as lower 4 bits called nibble and higher 4 bits. So you rotate the data you need to send by 4 bits so that the higher nibble comes to the lower nibble location send those 4 bits and then send the lower nibble. It is specified in the data sheet that higher nibble followed by lower nibble. So in embedded c we make use of shift operator to achieve bit rotation which in assembly is achieved by using rotate instructions.

  2. You have mentioned in your post as ‘I’ve written a seperate header file which can be included in any c program to use its functions’.
    I dint get how to do this? Could you please explain this concisely.

  3. /* 8bit_lcd.h
    * Created on: 12-Nov-2013
    * Author: Manpreet
    */
    #include
    #define DR P2OUT = P2OUT | BIT0 // define RS high
    #define CWR P2OUT = P2OUT & (~BIT0) // define RS low
    #define READ P2OUT = P2OUT | BIT1 // define Read signal R/W = 1 for reading
    #define WRITE P2OUT = P2OUT & (~BIT1) // define Write signal R/W = 0 for writing
    #define ENABLE_HIGH P2OUT = P2OUT | BIT2 // define Enable high signal
    #define ENABLE_LOW P2OUT = P2OUT & (~BIT2) // define Enable Low signal
    unsigned int i;
    unsigned int j;
    void delay(unsigned int k)
    {
    for(j=0;j<=k;j++)
    {
    for(i=0;i<1000;i++);

    }

    }
    void data_write(void)
    {
    ENABLE_HIGH;
    delay(2);
    ENABLE_LOW;
    }

    void data_read(void)
    {
    ENABLE_LOW;
    delay(2);
    ENABLE_HIGH;
    }

    void check_busy(void)
    {
    P1DIR &= ~(BIT7); // make P2.3 as input
    while((P1IN&BIT7)==1)
    {
    data_read();
    }
    P1DIR |= BIT7; // make P2.3 as output
    }

    void send_command(unsigned char cmd)
    {
    check_busy();
    WRITE;
    CWR;
    P1OUT = (P1OUT & 0×00)|(cmd);
    data_write(); // give enable trigger

    }

    void send_data(unsigned char data)
    {
    check_busy();
    WRITE;
    DR;
    P1OUT = (P1OUT & 0×00)|(data);
    data_write(); // give enable trigger
    }

    void send_string(char *s)
    {
    while(*s)
    {
    send_data(*s);
    s++;
    }
    }

    void lcd_init(void)
    {
    P2DIR |= 0xFF;
    P1DIR |= 0xFF;
    P2OUT &= 0×00;
    P1OUT &= 0×00;
    send_command(0×38); // 8 bit mode
    send_command(0x0E); // clear the screen
    send_command(0×01); // display on cursor on
    send_command(0×06);// increment cursor
    send_command(0×80);// cursor position
    }
    This is the code of header file. Just save this as lcd.h file(There is an option for adding header file to your project. Just right click and add. Or you may use text editor and save as .h file.). This will create one header file. Then you have to copy this lcd.h file in your project directory. (Note if you use code composer studio to do this the file is already stored in the current project working folder.). Then to call the header file just wtite #include "lcd.h" at the start of your project's main.c or c file. This is the standard procedure for creating any header file and invoking it in your program. So if you come across any peripheral or set of code that you would be requiring again and again just make an header file once and then just include it. This way you can save time by not writing that piece of code again and again.

  4. Thank you. I made this and loaded on to my msp430g2553 launchpad and connected the lcd jhd 162a LCD to launchpad as show in the fig. I m not getting the output. LCD just showing blocks in whole line. PLS help me to get through this
    I have adjusted contrast too. 😦

  5. How are you giving 5V to your LCD? This can be one of the reasons for what is happening with you. Use the test points provided on the launchpad itself. Near the usb connector there are two holes. These holes give 5V potential difference. So just solder male headers to your launchpad and then take 5V to a breadboard and then give it to your LCD.(The right hole is positive and the left hole is for ground.) Use a multimeter to check the voltage at the Vcc pin w.r.t ground. It should be round about 5V. Last thing would be check your pot terminal whether it is giving variable voltage or not. Main thing is take 5V from launchpad.

  6. Good day Manpreet Singh Minhas

    I really find all your information helpful. There is a matter that I would like to discuss with you as I need some help.

    Is it possible that you can send me your email address?

    Regards
    Jp Mostert

  7. Hi Manpreet ,
    I am not fully understand the check_busy function.

    void check_busy(void)
    {
    P1DIR &= ~(BIT7); // make P2.3 as input
    while((P1IN&BIT7)==1)
    {
    data_read();
    }
    P1DIR |= BIT7; // make P2.3 as output
    }

    -I know BIT7 = 10000000, right?. So P1DIR &= ~(BIT7) => P1DIR=11111111 & 01111111 = 01111111. I don’t understand your comment is “P2.3 input”, I think it is P1.7 as input and P1.0 -> P1.6 is output. Right or wrong for me?.

    -While loop take P1IN, so I suppose P1in = 1xxxxxx ( x is because P1.0 -> p1.6 is output it may be 0 or 1 as I think). So P1IN&BIT7= 1xxxxxx & 1000000, it is not zero, also not one.

    I am new for programming, so I may wrong at something, please explain it, thanks.

  8. Respected Sir
    I am new to coding will you please explain me the following loop…
    void send_string(char s)

    {

    while(s)

    {

    send_data(*s);

    s++;

    }

    }

    and what about this i could not get
    P1DIR |= BIT7; // make P2.3 as output

    • void send_string(char *s)
      {
      while(*s)
      {
      send_data(*s);
      s++;
      }
      }
      We had written a function that sends one byte of data called send_data(). Now you want to send many bytes i.e. a string. So we have used the concept of pointers here. String as a character array. We just send the bytes one by one. while(*s) will execute till it encounters a null character which is at the end of the string.

      And it is make P1.7 as output. Sorry about the errors. I have changed those in the post. And sorry about the super late reply as well.

  9. Hi Manpreet

    Thanks for such a wonderful post. I have just started programming MSP430.

    After loading the code to msp430, I see nothing on the LCD display, even when I press reset.
    When I power off and then power on, it shows only first list ie MANPREET SINGH. Also when I press reset, display becomes blank again.

    I also tried your post from http://www.instructables.com/id/Interfacing-16×2-LCD-with-msp430-launchpad-in-8-bi/step3/Code-and-Output/ but it had following difference in delay function:

    void delay(unsigned int k)
    {
    for(j=0;j<=k;j++)
    {
    for(i=0;i<1000;i++); // in above post you have put 100 instead of 1000.

    }

    for me code with 1000 is not showing anything on LCD display even after restarting.

    What could be the issue??

    Thanks
    Anil

    • Normally that happens when the LCD is not getting proper supply. Also see to it that your circuit has a common ground. Its peculiar that only first line is displaying. Is your controller getting reset when you press the button? My launchpad had some issue with the soldering so that button was not working properly so do check using a multimeter whether the connection is proper. If you want to test your reset button just write a code that toggles the red led. So that way when you press the button the led will blink and you will come to know whether it is working or not.

      • Thanks for reply, you guessed it correct, I have connected MSP430 Vcc(3.3v) to LCD’s Vcc(required 5v). Now I have taken connection from 5v test point, issue fixed.

  10. hi manpreet,

    i am trying to implement the above code for msp4305529 with changes in the header file but it shows 1 fatal error , could you pleas help?

    also while running code in 4 bit mode initially i got output but now the display is showing nothing, while checking with multimeter i found only RS and D4 are high remaining all are low.
    what would be the possible errors

    • What is the error that you are getting. Fatal error as in? Please tell in detail about the error. If initially you got the output then it means that either the LCD is not getting proper supply or its blown. So check that it is getting 5V.

  11. Dear Sir,
    I am try to interface 16×2 LCD display with msp430f5528 microcontroller. I have used your code. after build project I found error #66: expected a “;” how can i fix this error. sir please help me. I am beginners in msp430 microcontroller. I am using code composer studio v6.

  12. BIT0, BIT1…… BIT7… all theseu defined in the program…. plz tel me whether they are user defined or system defined

      • Hi Manpreet. I hope you are still active on this blog. I am trying to run LCD on 2 wires using serial shift register 74HC595. Here is short description.Controller is pic16f72. Q2 of 595 is connected to Enable pin of LCD. Q3 of 595 is connected to RS pin of LCD. Q4-Q7 of 595 is connected to D4-D7 data pins of LCD. ME and OE pins of 595 to the ground. Wrote a code. Sent data nibble by nibble with enable pin high first and then with same data with enable pin low after some delay. Tried different ways. But somehow nothing is appearing on LCD. Can you guide me a little.

  13. Hi Manpreet,
    thanks for your neatly presented post.with your post i have interfaced lcd and displayed a string on lcd. actually iam using MSP430F427A microcontrolle now i need to take the data from internal adc(sd16) and display it on lcd. for this i have written the code but it is not working. could you please tell me where i have done the mistake? my code is

    #include

    //#include

    #define lcd_out

    #define DR P1OUT = P1OUT | BIT4 // define RS high

    #define CWR P1OUT = P1OUT & (~BIT4) // define RS low

    #define READ P1OUT = P1OUT | BIT7 // define Read signal R/W = 1 for reading

    #define WRITE P1OUT = P1OUT & (~BIT7) // define Write signal R/W = 0 for writing

    #define ENABLE_HIGH P1OUT = P1OUT | BIT6 // define Enable high signal

    #define ENABLE_LOW P1OUT = P1OUT & (~BIT6) // define Enable Low signal

    #define Num_of_Results 8

    unsigned int i;

    unsigned int j;

    unsigned int temp,scale0,scale1;

    void display(unsigned num){

    //void display(){
    char scale[] = “0.0”;

    //scale[0] = (num/100)%10 + 48;
    //scale[2] = (num/10)%10 + 48;

    //scale[0]=(((temp%1000)%100)/10)+48;
    //scale[2]=(((temp%1000)%100)%10)+48;

    //scale[0] = num/1000+48; // for thousands place
    //scale[1] = (num/100)%10+48; //for hundreds place

    //scale[0]= (num/10)+48; //for tens place
    scale[0]= (num/10)%10+48; //for tens place
    scale[2]= (num%10)+48; // for ones place

    //scale[0]= (temp/10)%10+48; //for tens place
    //scale[2]= (temp%10)+48; // for ones place

    //scale0= (num/10)%10+48; //for tens place
    //scale1= (num%10)+48; // for ones plac

    send_string(“WELCOME TO RTS”);

    send_command(0xC0);

    send_string(scale);

    //send_string(scale0);
    //send_string(scale1);

    //send_string(scale[1]);
    //send_command(0xof,weighs);
    }
    void delay(unsigned int k)
    {
    for(j=0;j<=k;j++)
    {
    for(i=0;i>4) & 0x0F); // send higher nibble

    data_write(); // give enable trigger

    P1OUT = (P1OUT & 0xF0)|(cmd & 0x0F); // send lower nibble

    data_write(); // give enable trigger
    }
    void send_data(unsigned char data)
    {
    check_busy();

    WRITE;

    DR;

    P1OUT = (P1OUT & 0xF0)|((data>>4) & 0x0F); // send higher nibble

    data_write(); // give enable trigger

    P1OUT = (P1OUT & 0xF0)|(data & 0x0F); // send lower nibble

    data_write(); // give enable trigger
    }
    void send_string(char *s)
    {
    while(*s)
    {
    send_data(*s);
    s++;
    }
    }
    void lcd_init(void)
    {
    P1DIR |= 0xFF;

    P1OUT &= 0x00;

    send_command(0x33);

    send_command(0x32);

    send_command(0x28); // 4 bit mode

    send_command(0x0E); // clear the screen

    send_command(0x01); // display on cursor on

    send_command(0x06); // increment cursor

    send_command(0x80); // row 1 column 1
    }

    int main(void)
    {
    P2DIR = 0xFF; // All P2.x outputs

    P2OUT &= 0x00; // All P2.x reset

    volatile unsigned int i; // Use volatile to prevent removal
    // by compiler optimization

    WDTCTL = WDTPW + WDTHOLD; // Stop WDT

    FLL_CTL0 |= XCAP14PF; // Configure load caps

    for (i = 0; i < 10000; i++); // Delay for 32 kHz crystal to
    // stabilize

    SD16CTL = SD16REFON + SD16SSEL0; // 1.2V ref, SMCLK

    SD16CCTL0 |= SD16GRP; // Group with CH1

    SD16CCTL1 |= SD16GRP; // Group with CH2

    SD16CCTL2 |= SD16IE; // Enable interrupt

    for (i = 0; i < 0x3600; i++); // Delay for 1.2V ref startup

    __enable_interrupt(); // Enable general interrupts

    SD16CCTL2 |= SD16SC; // Set bit to start conversion

    __bis_SR_register(LPM0_bits); // Enter LPM0

    }

    #if defined(__TI_COMPILER_VERSION__) || defined(__IAR_SYSTEMS_ICC__)

    #pragma vector=SD16_VECTOR

    __interrupt void SD16ISR(void)

    #elif defined(__GNUC__)

    void __attribute__ ((interrupt(SD16_VECTOR))) SD16ISR (void)

    #else

    #error Compiler not supported!

    #endif
    {
    //static unsigned int index = 0;

    switch (SD16IV)
    {
    case 2: // SD16MEM Overflow

    break;

    case 4: // SD16MEM0 IFG

    break;

    case 6: // SD16MEM1 IFG

    break;

    case 8: // SD16MEM2 IFG

    temp = SD16MEM1; // Save CH0 results (clears IFG)
    //temp = 1.2;
    //if (++index == Num_of_Results)
    // {
    //index = 0; // SET BREAKPOINT HERE
    // }

    break;
    // lcd_init();

    //char buf[2];

    //sprintf(buf, "% d", temp); //Read Value to Buf

    //lcd_puts(buf); //Print Value to LCD

    //send_string(temp);

    //display(temp);

    //delay_ms(1000);

    //break;
    }
    //do{
    //temp=temp*1200/65535; //Convert ADC value to mV

    lcd_init();

    display(temp);
    //}
    while(1);
    }

    please help me.

    thanks.

  14. void lcd_init(void)
    {
    P1DIR |= 0xFF;
    P1OUT &= 0x00;
    send_command(0x33);
    send_command(0x32);
    send_command(0x28); // 4 bit mode
    send_command(0x0E); // clear the screen
    send_command(0x01); // display on cursor on
    send_command(0x06); // increment cursor
    send_command(0x80); // row 1 column 1
    }
    HERE , I WANT TO KNOW THE FUNCTION OF VALUES (0X33) & (0X32)

  15. I am encountering a very strange problem. My lcd is able to print integers through this code but prints garbage when I send a string.
    Please help!

    • You have to look this in the datasheet timing diagram section. So for example you need the timing details for the write mode you will get the values from the datasheet. Then you can either calculate precise delay or just a ball park figure to execute your command. Hope this helps.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s