16×2 LCD interfacing in 4 bit mode

Hello there!! After trying many times I finally managed to interface LCD in 4 bit mode. I’m writing this post as soon as I got the output and understood what was going wrong. First things first, if you are reading this article directly without knowing about 8 bit mode interfacing then you should read my blog post on 8 bit LCD interfacing. Assuming that you know what 8 bit mode is, let’s begin with the 4 bit mode interfacing.

Need for 4 bit mode

Well for interfacing anything with any processor we need system-bus (data-bus, address-bus and control-bus). In our case for 8 bit mode the 8 data pins (D0-D7) are the data and address bus while the 3 control pins(RS, R/W and E) are the control bus. Thus using these we can control the peripheral that we are interfacing. We are greedy so we want to interface as many peripherals as possible with the same microcontroller. This requires either large number of ports or we need to be smart and utilize what we have to the fullest. Thus first thing is we try to do is reduce the number of pins required for controlling the peripheral. Here comes the need for 4 bit mode. Thus we reduce the port pins required from 11 to 7. It might not seem much, but for a small microcontroller like msp430g2553 with limited port pins this is really a big amount. Now coming to the other method. Maybe we can use demultiplexing , in this way we can use ‘n’ lines to share the system bus with ‘2^n’ devices. I got one tip that we can use SIPO shift register for sending data. Now this will require only 5 port pins. Three control pins and two for serial data and clock.

4 bit mode working

In 4 bit mode we send the data nibble by nibble, first upper nibble and then lower nibble. For those of you who don’t know what a nibble is: a nibble is a group of four bits, so the lower four bits (D0-D3) of a byte form the lower nibble while the upper four bits (D4-D7) of a byte form the higher nibble. This enables us to send 8 bit data be it the ASCII code or the command code by using 4 pins instead of 8. The connections remain identical. The only change is that the lower nibble pins of LCD are unused.

Initializing the LCD in 4 bit mode

This is perhaps the most tricky part of this interfacing. When LCD is powered ON it is by default in 8 bit mode.

reset

This is the command code format for function set operation. Using this we form the command code for 4 bit mode operation.

upper part

mode_set

The function set bits are explained in the following section.

functions_mode_set_bits

We need 4 bit mode so make DL ‘0’. Thus the upper nibble of the command code is 0010b which is 0x02. If we need 2 lines we set N and the normal font so F is 0 thus lower nibble comes out to be 1000b i.e 0x08. Thus total command code is 0x28.

Before sending this 0x28 we need to perform a specific initialization.

initialization

So we send 0x33 as the command code. This will do the initial 8 bit mode starting of LCD. Now after this we need to send 0x32.(Note : We’re using the nibble method so what will go for 0x33 is 3 followed by 3 and for 0x32 is 3 followed by 2.) I was not able to do the initialization. But then I suddenly remembered about a workshop that I had attended on msp430. The professor had taught LCD interfacing in 4 bit mode but due to shortage of time could not go into the details. He had just told about the nibble sending part and that we need to send command codes to initialize. So I opened his file and saw this 0x33 and 0x32 and tried sending it. And to my surprise I got my name on the LCD. So this credit goes to him. His name is Gurjeet Singh Gill.

Now coming back to the topic at hand. So now we have initialized the LCD in 4 bit mode. All we need to learn is how to send the value nibble by nibble in c and without affecting other port pins except the ones that we use for sending data. (In assembly its just the rotate instruction.)

Nibble sending logic

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

In this we make use of masking concept and logical shift operation in c. Now in my program I’m using P1.0 – P1.3 pins as the data pins. Thus I’ll explain the logic accordingly. Here data is the parameter that is passed in the function. I shift the data right by four bits, thus bringing the higher nibble in the lower nibble location. Mask the upper part. Then I make the P1OUT lower nibble 0 so that ‘or’ing with nibble data will give nibble data on P1OUT pin. (x and 0 is 0 ; x and 1 is x; x or 1 is 1 and x or 0 is x—-> (x&0)|(data_bit)= 0|data_bit= data_bit. Thus we get data bit on the port pin without affecting P1.4-P1.7) This takes care of upper nibble.

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

Now lower nibble involves the same operations except the shifting operation.

Code

Header file :

// Author : Manpreet Singh Minhas
// This file is for 4 bit mode LCD interfacing with msp430g2553 chip
// 16x2 LCD is used
#include <msp430g2553.h>
#define DR P1OUT = P1OUT | BIT4 // define RS high
#define CWR P1OUT = P1OUT & (~BIT4) // define RS low
#define READ P1OUT = P1OUT | BIT5 
// define Read signal R/W = 1 for reading
#define WRITE P1OUT = P1OUT & (~BIT5) 
// 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
unsigned int i;
unsigned int j;
void delay(unsigned int k)
{
for(j=0;j<=k;j++)
{
for(i=0;i<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 &= ~(BIT3); // make P1.3 as input
while((P1IN&BIT3)==1)
{
data_read();
}
P1DIR |= BIT3; // make P1.3 as output
}

void send_command(unsigned char cmd)
{

check_busy();
WRITE;
CWR;
P1OUT = (P1OUT & 0xF0)|((cmd>>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
}

Main Code:

/* LCD_own.c
* Created on: 12-Nov-2013
* Author: Manpreet
* In this program we interface the lcd in 4 bit mode. We send strings and display it on the screen.
*/
#include "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){}
}

P1.0 – D4            Pin11

P1.1 – D5             Pin12

P1.2 – D6             Pin13

P1.3 – D7             Pin14

P1.4 – RS             Pin4

P1.5 – R/W         Pin5

P1.6 – E                 Pin6

Advertisements

29 thoughts on “16×2 LCD interfacing in 4 bit mode

  1. I have ordered such a Display. Now i am waiting for it to try at first the 8bit mode and then this one. And i have seen i have to spent more time on hexadecimal. For me it is a pain with These hex values.

  2. hello manpreet sir , i am interfacing ultrasonic sensor hcsr-04 with msp430g2553 and i want to display the distance value on the lcd , so how it can be done . because each time the distance value will go on changing and i cannot send a fixed hexadecimal value to the lcd . so please help . waiting for your reply .

    • If you have learnt how to measure the echo pulse high time duration then it is really simple. ‘range = high level time * 170 m’ is the formula for the distance in meters.

      
      
      unsigned int range;
      range = time*170;
      send_int(range); // This is available in the thermometer blog post. Just include the header file.
      

      • thank you sir , for your reply .
        sir i am trying to interface ultrasonic sensor hcsr-04 with msp430g2553 . i want to measure the distance of the obstacle placed in front of the sensor . will u please provide me the code .
        i tried a lot but i am not getting it sir . this is my final year project , so please help me sir .
        waitng for your reply.

    • Since you are a newbie I would like to tell you this that whenever you get any error and you ask it to someone its your moral obligation to post the error screen shot so that the other person is able to understand what the error really is. So kindly provide sufficient details.

  3. Hey Manpreet,

    Im using my msp430g2553 for uart applications also, so can you please help me out with the other pins for lcd i.e other than 1.1 and 1.2. And can you please give me the complete code,so that i can just paste it on ccs software, i would be very thankful to you if you do so.

  4. Thank you very much for this code. Using this code I could write a code for AVR ATmega 32 microcontroller. I was struggling to program LCD with my AVR development board. Now its working.

  5. Hi i am working on ARM 2138 and trying to interface a 16*2 LCD in 4bit mode. please help me with the same. i cant get the output. some problem in the code. please help!! RS connected to P1.24, enable at P0.7, RW is grounded in my circuit. The data lines are D7- P0.3 , D6- P0.4 , D5-P0.5 , D4-P0.6

    #include”lpc213x.h”
    #include

    #define EN (1<<7)
    #define RS (1<PS-Primer 2148<"};
    unsigned char msg1[]= {":: LCD Demo! ::"};

    int main()
    {
    PINSEL2 = 0;
    PINSEL0 = 0;
    IODIR0 = 0x00000078;
    IODIR1 = 0x01000000;
    lcd_initialise();
    lcd_display();
    return 0;
    }

    void lcd_initialise(void)
    {

    int i=0;
    lcd_convert(0x30);
    lcd_delay(16);
    lcd_convert(0x30);
    lcd_delay(16);
    lcd_convert(0x30);
    lcd_delay(16);
    lcd_convert(0x20);
    lcd_delay(16);

    for(i=0;i<4;i++)
    {
    IOCLR0 = 0xF << 3;
    lcd_cmd(cmd[i]);
    lcd_delay(15);

    }

    }

    void lcd_cmd(unsigned char data)
    {
    IOCLR1 |= RS; //RS
    lcd_convert(data);
    }

    void lcd_delay(unsigned int n)
    {
    unsigned int i,j;
    for(i=0;i<n;i++)
    for(j=0;j<12000;j++);
    }

    void lcd_display (void)
    {
    int i=0,j=0;
    /* first line message */
    lcd_cmd(0x80);
    lcd_delay(15);

    while(msg[i]!='')

    {
    lcd_delay(15);
    lcd_data(msg[i]);
    i++;
    }

    lcd_delay(15);
    /* second line message */
    lcd_cmd(0xc0);
    lcd_delay(15);

    while(msg1[j]!='')

    {
    lcd_delay(5);
    lcd_data(msg1[j]);
    j++;
    }

    lcd_delay(15);
    }

    void lcd_convert(char c)
    {

    if(c & 0x08) IOSET0 = 1 << 3; else IOCLR0 = 1 << 3;
    if(c & 0x04) IOSET0 = 1 << 4; else IOCLR0 = 1 << 4;
    if(c & 0x02) IOSET0 = 1 << 5; else IOCLR0 = 1 << 5;
    if(c & 0x01) IOSET0 = 1 << 6; else IOCLR0 = 1 << 6;

    IOSET0 = EN;
    lcd_delay(15);
    IOCLR0 = EN;
    lcd_delay(15);

    if(c & 0x80) IOSET0 = 1 << 3; else IOCLR0 = 1 << 3;
    if(c & 0x40) IOSET0 = 1 << 4; else IOCLR0 = 1 << 4;
    if(c & 0x20) IOSET0 = 1 << 5; else IOCLR0 = 1 << 5;
    if(c & 0x10) IOSET0 = 1 << 6; else IOCLR0 = 1 << 6;

    IOSET0 = EN;
    lcd_delay(15);
    IOCLR0 = EN;
    lcd_delay(15);

    }

    void lcd_data (unsigned char data)
    { IOSET1 |= RS; //0x1000; //RS
    lcd_convert(data);
    }

  6. i can use 4 bit lcd interface with atmega16.the code are work in proteus but did not work in hardware and change delay data /command function. all pin of lcd connection are properly and please give me suggestion. code:

    unsigned char data0[11]=”ENGINEERS”;
    unsigned char data1[10]=”GARAGE”;
    unsigned int adc_val;

    main()
    {
    int i=0;
    DDRC=0xFF;
    DDRA=0x00;
    lcd_init();
    ADC_init();
    dis_cmd(0x01);
    while(data0[i]!=’\0′)
    {
    dis_data(data0[i]);
    _delay_ms(50);
    i++;
    }

    dis_cmd(0xC5);

    i=0;
    while(data1[i]!=’\0′)
    {
    dis_data(data1[i]);
    _delay_ms(50);
    i++;
    }
    _delay_ms(1000);
    while(1);
    }

    void lcd_init() // fuction for intialize
    {
    dis_cmd(0x02); // to initialize LCD in 4-bit mode.
    dis_cmd(0x28); //to initialize LCD in 2 lines, 5X7 dots and 4bit mode.
    dis_cmd(0x0C);
    dis_cmd(0x06);
    dis_cmd(0x83);
    _delay_ms(1);
    }

    void dis_cmd(char cmd_value)
    {
    char cmd_value1;

    cmd_value1 = cmd_value & 0xF0; //mask lower nibble because PA4-PA7 pins are used.
    lcdcmd(cmd_value1); // send to LCD
    _delay_us(50);
    cmd_value1 = ((cmd_value<<4) & 0xF0); //shift 4-bit and mask
    lcdcmd(cmd_value1); // send to LCD
    _delay_us(50);
    }

    void dis_data(char data_value)
    {
    char data_value1;

    data_value1=data_value&0xF0;
    lcddata(data_value1);
    _delay_us(750);

    data_value1=((data_value<<4)&0xF0);
    lcddata(data_value1);
    _delay_us(750);
    }

    void lcdcmd(char cmdout)
    {
    PORTC=cmdout;//|0x08;
    PORTC&=~(1<<rs);
    PORTC&=~(1<<rw);
    PORTC|=(1<<en);
    _delay_ms(25);
    PORTC&=~(1<<en);
    }

    void lcddata(char dataout)
    {
    PORTC=dataout;// | 0x0a;
    PORTC|=(1<<rs);
    PORTC&=~(1<<rw);
    PORTC|=(1<<en);
    _delay_ms(25);
    PORTC&=~(1<<en);
    }

    //LCD STRING FUNCTION FOR PREFINED DATA
    void LCD_str(unsigned char *p)
    {
    //unsigned char ch;
    while((*p)!='\0')
    {
    //ch=*p;
    dis_data(*p);
    p++;
    }
    }

  7. Hello, would this program work if the LCD commands send_string were not in the main() code? Such as in a WDT interrupt if the LCD text was constantly changing?
    Thanks for your help in advance!

  8. i reffered your code towrite my own, however there is a bug.The lcd just turns on on proteus but does not display anything..could you please help me resolve this..

    #include
    #define RS BIT1 //p2.0
    #define EN BIT0//p2.1

    void lcd_cmd (volatile char c);
    void lcd_data(char d);
    void lcd_init();
    void lcd_string(char *p);
    int main(void) {
    WDTCTL = WDTPW | WDTHOLD; // Stop watchdog timer
    P2DIR|=RS;//p2.0 as output
    P2DIR|=EN;//p2.1 as output
    P2OUT &= 0x00;
    P3DIR=0XF0;//p3- D4 to D7 as output
    while(1)
    {
    lcd_init();
    lcd_data(“S”);
    }

    return 0;
    }
    void lcd_cmd (volatile char c)
    {
    __delay_cycles(50000);
    __delay_cycles(50000);
    __delay_cycles(50000);

    volatile char temp;
    P2OUT&=~RS;
    temp=c;
    temp&=0XF0;
    P3OUT&=0X0F;
    P3OUT|=temp;
    P2OUT^=EN;
    __delay_cycles(50000);
    P2OUT^=EN;

    temp=c<<4;
    temp&=0XF0;
    P3OUT&=0X0F;
    P3OUT|=temp;
    P2OUT^=EN;
    __delay_cycles(50000);
    P2OUT^=EN;
    }
    void lcd_data(char d)
    {
    __delay_cycles(50000);
    __delay_cycles(50000);
    __delay_cycles(50000);
    volatile char temp;
    P2OUT|=RS;
    temp=(d-1);
    temp&=0XF0;
    P3OUT&=0X0F;
    P3OUT|=temp;
    P2OUT^=EN;
    __delay_cycles(50000);
    P2OUT^=EN;

    temp=(d-1);
    temp=temp<<4;
    temp&=0XF0;
    P3OUT&=0X0F;
    P3OUT|=temp;
    P2OUT^=EN;
    __delay_cycles(50000);
    P2OUT^=EN;
    }
    void lcd_init()
    {
    lcd_cmd(0X33);
    lcd_cmd(0X32);
    lcd_cmd(0X28); //selecting bus width=4;
    lcd_cmd(0X01);
    lcd_cmd(0X06);
    lcd_cmd(0X80);
    }
    i read somewhere that instead of using check_busy() function we can simply use delay and connect R/W to ground.So i have written the code accordingly.

    Please answer as soon as possible….:D
    Regards Shreya

  9. #include <msp430g2553.h>
    Sir, what is the meaning of using this header instead of using #include
    and please help me with these errors

    1.#13 expected a file name lcd.h /lcddisplay line 11 C/C++ Problem
    2.gmake: *** [main.obj] Error 1 lcddisplay C/C++ Problem
    3.gmake: Target ‘all’ not remade because of errors. lcddisplay C/C++ Problem

    • lcd.h file is missing from your directory. You need to copy the code for lcd header file in from my post and save it in the project directory. That should resolve the issue. Also reading the errors every now and then could save you so much time. Because most of the times errors are self explanatory and can be resolved easily. Hope this helps. Good luck!

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