OCFreaks!

Interfacing 16X2 LCD with LPC2148 tutorial

Introduction
Interfacing a 16X2 or 16X4 LCD module with a 3.3V MCU is not same as interfacing with MCUs like AVR which operate on 5Volts. As per request by some of the readers of previous articles on lpc2148 this article is on Interfacing a 5V LCD Module with LPC2148 MCU and in general for any ARM or 3.3V MCU .

Whats next? : A simple Library for LCD interfacing with LPC214x and LPC176x MCUs. And more tutorials on Timer , PWM , UART .. for lpc214x , lpc176x.

For this article I’ve used the readily available Chinese 16X2 LCD Module : JHD-162A. The Data sheet for JHD-162A 16X2 LCD Module is located at : http://www.egochina.net.cn/eBay/Download/JHD162A.pdf I would strongly suggest that you keep that datasheet open when reading this article to avoid any sort of confusion.

Before Starting the Tutorial , as a motivation , I would like to show the final outcome of this tutorial. The pic below shows the LPC2148 development board interfaced with JHD162A LCD Module via 2x HCF4050B ICs. I’ll come to HCF4050B IC shortly .. at the moment lets get started with the basics.

16X2 LCD Basics :

This particular chinese LCD i.e JHD162A has KS0066U controller or similar to the famous HD44780U. It Consists of 2 Rows with 16 Characters on each. It has a 16 pin Interface. Operates on 5V and has LED backlight. Works in 2 Modes :

Standard Pinout is as follows :

To keep things simple lets group the pins into 3 :
1) Power Pins : Pin 1,2,3,15,16
2) Control Pins : Pin 4,5,6
3) Data Pins : Pin 7 to 14

Now lets see some of the important pins before we actually start writing programs for LCD:
  • Contrast Voltage (VEE) : Instead of using a trim pot just connect a 1K resistor to VEE in series and ground it. That gives the best contrast of what I’ve seen.
  • RS – short for Register select (Control Pin) : Used to switch been Instruction and Data Mode. RS = High for Instruction Mode and RS = Low for Data mode.
  • R/W – Read or Write (Control Pin): R/W = High for Read Mode and R/W = Low for Write. Since we are going to use Write Mode only we will permanently ground it using a pull-down resistor of 4.7K Ohms. Caution : If you are planning to use Read Mode with 3.3V MCUs you’ll need a Bi-directional level shifter which can shift 5V to 3.3V and Vice-Versa.
  • Enable (Control Pin) : This is similar to a trigger pin. Each Data/Instruction is executed by the LCD module only when a pulse is applied to Enable pin. More specifically the it is executed at the falling edge of the pulse.

If you want to know the fundamentals and internal operation of LCD Modules I would recommend visiting these links :

Interfacing 5V LCD Module with lpc214x:

5V LCD wont operate on 3.3V and also since lpc214x runs on 3.3V and 5Volts might fuse some of its GPIO pin we are gonna need a level shifter or translator that can shift 3.3Volts to 5Volts for LCD Module to be safe. Vikram Sharma M. has successfully interfaced JHD162A with MSP430 MCU using CD4050B IC as explained in his post @ http://msharmavikram.wordpress.com/2012/08/14/3-mistakes-of-lcd-with-msp430-solved/ .

Some of the buffers / level shifters than can be used are : SN74AHC244N , SN74AC241N , CD74AC244E , CD4050B , etc.. out of which CD4050B is readily available. I purchased a few HCF4050 which is same as CD4050B except it is manufactured by ST Microelectronics and not Texas Instruments.

HCF4050B is a non-inverting Hex Buffer. In our case we’ll be needing 2 of them at minimum since we are going to use 8+2=10 pins from MCU which need to be shifted to 5Volts. There are going to be 8 Data pins and 2 control pins connected from MCU to the LCD Module using 2x HCF4050B.

NOTE : As far as possible use the 1st eight consecutive pins(at any cost!) from any port of the MCU as Data Pins which will make sending character data or commands(Instructions) to LCD very easy. For e.g. in our case we will use P0.0 to P0.7 as data pins and P1.16 as RS pin and P1.17 as Enable pin.

Connections from lpc214x to LCD module will be as shown below. I’ve also made a PCB for the schematic if bread-board is not your cup of tea.(Frankly , since I was running out of time I did it using a bread-board. If some one is having any problems with PCB please let me know.)

Pin Connections from MCU->HFC4050B->LCD:

1)Pin Connections from MCU to HCF4050B ICs :
IC1 : P1.16->Pin3 , P1.17->Pin5 , P0.0->Pin7 , P0.1->Pin9 , P0.2->Pin11 , P0.3->Pin14
IC2 : P0.4->Pin3 , P0.5->Pin5 , P0.6->Pin7 , P0.7->Pin9 (Pins 11,14 of IC2 are NC)

2)Pin Connections from HCF4050B to LCD Module :
IC1 : Pin2->RS , Pin4->Enable , Pin6->DB0 , Pin10->DB1 , Pin12->DB2 , Pin15->DB3
IC2 : Pin2->DB4 , Pin4->DB5 , Pin6->DB6 , Pin10->DB7 (Pin 12,15 of IC2 are NC)

Download links to PCB pdf and Eagle 6.2+ (Schematic+Board) Design Files:

1) lcd_interface_arm_mcu_PCB-pdf.rar
2) lcd_interface_arm_mcu-EAGLE-6.2+design-files.rar

Components required at bare minimum :

NOTE : You need to connect the ‘GND’ from MCU Board to ‘LCD’s GND’ else its not gonna work. Diode D1 and Decoupling Capacitors C1 & C2 are optional. D1 can be replaced by a short and C1,C2 can be ignored.

Initializing LCD Module :

After you have cross-checked all your connections from MCU to HFC4050 to LCD Module its time now to Display text on LCD. But before that we need to initialize the LCD properly. Also NOTE that : as per the Datasheet – before initializing LCD we need to wait for a minimum time of about 15ms after the input voltage supply is stable and >4.5Volts.

The 1st thing required for this is that RS must be held LOW and Enable also LOW in the beginning. Now we must supply some commands using the Data Pins to LCD. But this command wont be executed until we supply a pulse to Enable Pin. After supplying the command we make enable High and then Low after a short delay(i.e a Pulse) and the command is executed.

Codes for various instructions can be obtained from below table : (Refer to page 12 of the Datasheet)

For beginners we are only interested in following commands (which are also executed in given order during initialization):
  1. Function Set
  2. Display Switch
  3. Input Set
  4. Screen Clear Command
  5. DDRAM AD Set – Resposition Cursor Command
  • In our case we are going to use 8Bit Mode with 2Rows and 5×10 Font style which gives Function set as 0x3C.
  • Next we issue Display On , Cursor On , Blink On which gives Display Switch as 0x0F.
  • Next we select Increment Mode which gives Input Set as 0x06.
  • Now we clear the screen and set cursor at Home which gives Screen Clear as 0x01.
  • Also the command to reposition the cursor at Home at anytime is 0x80 and to reposition it at the 1st column of the second row is 0xC0.

To cut-short or in simple words: We need to supply 5 commands in given order to the Data Pins with a small amount of delay in between to initialize the LCD properly. These commands are : 0x3C , 0x0F , 0x06 , 0x01 , 0x80(actually not required). Now since we have used the the 1st eight pins of port 0 on lpc214x we can directly assign these values to the IO0PIN register. Note that Bit 0 i.e LSB in IO0PIN is towards extreme right and the bit number increases as we move towards the left approaching the MSB. Also since we have connected P0.0 to Data Bit 0 pin , P0.1 to Data bit 1 and so on … we have a one to one consecutive mapping between them MCU pins and LCD Data pins.

NOTE: During Initialization , issuing 0x80 command to reposition the cursor at home will be of no use since it would already be at Home after issuing Screen Clear Command. Also we need to Supply a Pulse to Enable after issuing each command and wait for a short while for the LCD controller to process it. Datasheet says a delay of 40us is required at minimum. But even a delay of 1ms wont hurt us to be on the safer side.

Correct Sequence for Initializing LCD module (8 Bit Interface) is as given :
  1. After LCD Module is powered on and MCU boots wait for 20ms (Specifically >15ms)
  2. Now make RS and Enable LOW making sure that R/W is permanently grounded.
  3. Issue Function set command – 0x3C and Pulse Enable (wait for >40us after pulsing).
  4. Issue Display Switch – 0x0F and Pulse Enable (wait for >40us after pulsing).
  5. Issue Input Set – 0x06 and Pulse Enable (wait for >40us after pulsing).
  6. Issue Screen Clear – 0x01 and Pulse Enable (wait for >1.64ms after pulsing).
  7. Issue DDRAM AD Set – 0x80 and Pulse Enable (wait for >40us after pulsing).

Now LCD is ready for Printing!

After Initializing the LCD you would see a Cursor Blinking at in 1st column at row 1 :

Printing Characters on LCD Module :

Now that we have initialized LCD correctly its time to send some characters. For this we enter into Data Mode by making RS High. After that any number / code applied to Data pins will be converted into corresponding Character and displayed. The codes for the characters are ASCII Codes given by the font table as show below :

Hence to print a character on LCD we just need to apply the 8bit ASCII code to Data pins. This can be done easily by type casting the character into an integer (which is done automatically by the compiler – but we are doing it here explicitly). Refer to Page 14 of datasheet for font table.

Now , after printing 16 characters you might wanna print more characters in 2nd Row. This is done by entering into Instruction Mode by holding RS=LOW and giving command 0xC0 which repositions the cursor at 2nd Row & 1st column. Needless to say that we need to again give a Pulse on Enable pin to process it. After that we put RS to high and get back in Data mode.

Note on Repositioning the Cursor : As we have seen , the command for repositioning the cursor at home is 0x80. Think of 0x80 as the base address of Display Data Ram(DDRAM). Hence 0x80 will set cursor at 1st Row, 1st Column. If you add 0x01 to 0x80 the cursor will go to 2nd column in 1st row. Likewise if you add 0x0F to 0x80 you’ll end up at 1st row , last column. Similar to this the base address for 2nd Row is 0x40 and not 0x10! 0x10 is the base address of 3rd row and 0x50 is base address for 4th row in the case of 16×4 LCD Modules. So to position the cursor at 2nd row, 1st column we must add 0x40 to 0x80 which gives 0x80+0x40=0xC0. Hence after giving command ‘0xC0’ cursor comes down to 2nd row & 1st col.

More on DDRAM and CGROM @ http://www.8051projects.net/lcd-interfacing/basics.php

Programs for Lpc2148 to print Characters on LCD module :

1st Let me explain a few functions that I’ve made in the programs below:

Program #1 : Basic Program without precise Timing & no PLL setup.


/*
 (C) OCFreaks! | Umang Gajera 2012-13.
 More Embedded tutorials @ www.ocfreaks.com/cat/embedded
 >>Program for Interfacing 16X2 LCD Module with LPC2148
*/

#include <lpc214x.h>

/*
 Connections from LPC2148 to LCD Module:
 P0.0 to P0.7 used as Data bits.
 P1.16 connected to pin4 i.e. RS	- Command / Data
 P1.17 connected to pin6 i.e. E - Enable
 Pin5 of LCD Module i.e. 'R/W' connected to ground
*/	

void initLCD(void);
void enable(void);
void LCD_WriteChar(char c);
void LCD_WriteString(char * string);
void LCD_Cmd(unsigned int cmd);
void delay(void);


int main(void)
{
	initLCD(); //LCD Now intialized and ready to Print!
	LCD_WriteString(".: Welcome to :.");
	LCD_Cmd(0x80 + 0x40); //Come to 2nd Row
	LCD_WriteString("www.OCFreaks.com");
	while(1); // Loop forever	 
	return 0; //This won't execute :P
}

void initLCD(void)
{
	IO0DIR = 0xFF; //P0.0 to P0.7 configured as Output - Using 8 Bit mode
	IO1DIR |= (1<<16) | (1<<17); //P1.16 and P1.17 configured as Output - Control Pins
	IO0PIN = 0x0; //Reset Port0 to 0.	
	IO1PIN = 0x0; //Reset Port1 to 0 - Which also makes RS and Enable LOW.

	//LCD Initialization Sequence Now starts
	delay(); //Initial Delay
	LCD_Cmd(0x3C); //Function Set Command : 8 Bit Mode , 2 Rows , 5x10 Font Style
	LCD_Cmd(0x0F); //Display Switch Command : Display on , Cursor on , Blink on
	LCD_Cmd(0x06); //Input Set : Increment Mode 
	LCD_Cmd(0x01); //Screen Clear Command , Cursor at Home
	LCD_Cmd(0x80); //Not required the 1st time but needed to reposition the cursor at home after Clearing Screen 
	//Done!
}

void enable(void)
{
	delay();
	IO1PIN |=  (1<<17);//Enable=High
	delay();
	IO1PIN &= ~(1<<17);//Enable=Low
	delay();
}

void LCD_WriteChar(char c)
{
	IO1PIN |= (1<<16); //Switch to Data Mode
	IO0PIN = (int) c; //Supply Character Code
	enable(); //Pulse Enable to process it
}

void LCD_WriteString(char * string)
{
	int c=0;
	while (string[c]!='\0')
	{
		LCD_WriteChar(string[c]);
		c++;
	}
}
		
void LCD_Cmd(unsigned int cmd)
{
	IO1PIN = 0x0; //Enter Instruction Mode
	IO0PIN = cmd; //Supply Instruction/Command Code
	enable(); //Pulse Enable to process it
}

void delay(void)
{
	int i=0,x=0;
	for(i=0; i<19999; i++){ x++; }
}

Program #2 : Program with PLL* setup and precise Timing**. (Xtal=12Mhz , CCLK=60Mhz)

*PLL tutorial for LPC2148 @ https://www.ocfreaks.com/lpc214x-pll-tutorial-for-cpu-and-peripheral-clock/
**Tutorial on Timer for LPC2148 @ https://www.ocfreaks.com/lpc2148-timer-tutorial/


/*
 (C) OCFreaks! | Umang Gajera 2012-13.
 More Embedded tutorials @ www.ocfreaks.com/cat/embedded
 >>Program2 for Interfacing 16X2 LCD Module with LPC2148
 >>using Timer0 and CPU @ 60Mhz
*/


#include <lpc214x.h>

/*
 XTAL Freq=12 Mhz , CCLK=60Mhz , PCLK=60Mhz
 Using common delay of 2ms for all operations
 	
 Connections from LPC2148 to LCD Module:
 P0.0 to P0.7 used as Data bits.
 P1.16 connected to pin4 i.e. RS    - Command / Data
 P1.17 connected to pin6 i.e. E - Enable
 Pin5 of LCD Module i.e. 'R/W' connected to ground
*/ 

#define PLOCK 0x00000400

void initLCD(void);
void enable(void);
void LCD_WriteChar(char c);
void LCD_WriteString(char * string);
void LCD_Cmd(unsigned int cmd);
void delayMS(unsigned int milliseconds);

void setupPLL0(void);
void feedSeq(void);
void connectPLL0(void);


int main(void)
{
    setupPLL0();
    feedSeq(); //sequence for locking PLL to desired freq.
    connectPLL0();
    feedSeq(); //sequence for connecting the PLL as system clock
   
    //SysClock is now ticking @ 60Mhz!
       
    VPBDIV = 0x01; // PCLK is same as CCLK i.e 60Mhz
    
	//PLL0 Now configured!

	initLCD(); //LCD Now intialized and ready to Print!
    LCD_WriteString(".: Welcome to :.");
    LCD_Cmd(0x80 + 0x40); //Come to 2nd Row
    LCD_WriteString("www.OCFreaks.com");
    while(1); // Loop forever    
    return 0; //This won't execute :P
}

void initLCD(void)
{
    IO0DIR = 0xFF; //P0.0 to P0.7 configured as Output - Using 8 Bit mode
    IO1DIR |= (1<<16) | (1<<17); //P1.16 and P1.17 configured as Output - Control Pins
    IO0PIN = 0x0; //Reset Port0 to 0.  
    IO1PIN = 0x0; //Reset Port1 to 0 - Which also makes RS and Enable LOW.

    //LCD Initialization Sequence Now starts
    delayMS(20); //Initial Delay
    LCD_Cmd(0x3C); //Function Set Command : 8 Bit Mode , 2 Rows , 5x10 Font Style
    LCD_Cmd(0x0F); //Display Switch Command : Display on , Cursor on , Blink on
    LCD_Cmd(0x06); //Input Set : Increment Mode
    LCD_Cmd(0x01); //Screen Clear Command , Cursor at Home
    LCD_Cmd(0x80); //Not required the 1st time but needed to reposition the cursor at home after Clearing Screen
    //Done!
}

void enable(void)
{
    //Using common delay of 2ms
	delayMS(2);
    IO1PIN |=  (1<<17);//Enable=High
    delayMS(2);
    IO1PIN &= ~(1<<17);//Enable=Low
    delayMS(2);
}

void LCD_WriteChar(char c)
{
    IO1PIN |= (1<<16); //Switch to Data Mode
    IO0PIN = (int) c; //Supply Character Code
    enable(); //Pulse Enable to process it
}

void LCD_WriteString(char * string)
{
    int c=0;
    while (string[c]!='\0')
    {
        LCD_WriteChar(string[c]);
        c++;
    }
}
       
void LCD_Cmd(unsigned int cmd)
{
    IO1PIN = 0x0; //Enter Instruction Mode
    IO0PIN = cmd; //Supply Instruction/Command Code
    enable(); //Pulse Enable to process it
}

void delayMS(unsigned int milliseconds)
{
//Timers will be explained in detail in the very next tutorial @ www.ocfreaks.com/cat/embedded/
	
	T0IR = 0;
	T0CTCR = 0;
	T0PR = 60000; //60000 clock cycles @60Mhz = 1 mS
	T0PC = 0;
	T0TC = 0;
	T0TCR = 0x01; //enable timer
	
	//wait until timer counter reaches the desired dela1y	
	while(T0TC < milliseconds);
	
	T0TCR = 0x00; //disable timer
}

//---------PLL Related Functions :---------------

void setupPLL0(void)
{
    PLL0CON = 0x01; // PPLE=1 & PPLC=0 so it will be enabled
                    // but not connected after FEED sequence
    PLL0CFG = 0x24; // set the multipler to 5 (i.e actually 4)
                    // i.e 12x5 = 60 Mhz (M - 1 = 4)!!!
                    // Set P=2 since we want FCCO in range!!!
                    // So , Assign PSEL =01 in PLL0CFG as per the table.
}

void feedSeq(void)
{
    PLL0FEED = 0xAA;
    PLL0FEED = 0x55;
}

void connectPLL0(void)
{
    // check whether PLL has locked on to the  desired freq by reading the lock bit
    // in the PPL0STAT register

    while( !( PLL0STAT & PLOCK ));

    // now enable(again) and connect
    PLL0CON = 0x03;
}

Output after flashing any of the above code to MCU (Successfully Tested on Lpc2148 and Lpc1768) :

Download Links to Source Code using KEIL UV4 Project IDE :

1) Program #1 : OCFreaks.com_LPC2148_LCD_Interfacing.rar
2) Program #2 : OCFreaks.com_LPC2148_LCD_Interfacing_precise.rar

Silence please : 2 LCDs were Harmed in the making
While working on this article 2 of my Green LCDs were damaged. One got completely useless and other got partially damaged. In any case both of them are useless now.

LCD #1 : Display completely gone kaput..

LCD #2 : Display partially gone kaput..

Exit mobile version