EmbeddedRelated.com
Forums
Memfault Beyond the Launch

HD44780 Initialisation (20x4 display in 4-bit mode)

Started by Kevin Townsend October 4, 2008
Here is the latest incarnation of the code if anyone has enough
patience or interest to look through it.

I'm using an Olimex H2148 board, and as far as I can tell the pins I'm
using (P1.16...22) are not used on this board.

http://www.olimex.com/dev/images/lpc-h2148-sch.gif

*********** START hd44780.c *********************
#include "lpc210x.h"
#include "hd44780.h"
#include "delay.h"

//
// Each operation is 83.33ns. Cumlative delays make be longer, but
not shorter.
// This delay assumes a 48Mhz clock and MAM=partial
//
#define delay166ns() do { __asm__ volatile ("push {r0}\r\npop
{r0}\r\n"); } while (0)

//
// 166ns measured
//
static inline void lcdDelay140ns (void)
{
delay166ns ();
}

//
// 333ns measured
//
static inline void lcdDelay320ns (void)
{
delay166ns ();
delay166ns ();
}

//
// 500ns measured
//
static inline void lcdDelay450ns (void)
{
delay166ns ();
delay166ns ();
delay166ns ();
}

//
// 666ns measured
//
static inline void lcdDelay550ns (void)
{
delay166ns ();
delay166ns ();
delay166ns ();
delay166ns ();
}

static inline void lcdEnableHigh()
{
GPIO1_IOSET = PIN_E;
}

static inline void lcdEnableLow()
{
GPIO1_IOCLR = PIN_E;
}

static inline void lcdEnableStrobe (void)
{
lcdEnableHigh ();
lcdDelay450ns ();
lcdEnableLow ();
lcdDelay550ns ();
}

void hd77480_Send_4Bits(unsigned char v)
{
// Clear all bits by default
GPIO1_IOCLR = ( PIN_D4 | PIN_D5 | PIN_D6 | PIN_D7 );

// Set individual bits
if (v & 0x01)
GPIO1_IOSET = PIN_D4;
if (v & 0x02)
GPIO1_IOSET = PIN_D5;
if (v & 0x04)
GPIO1_IOSET = PIN_D6;
if (v & 0x08)
GPIO1_IOSET = PIN_D7;
}

void hd44780_SendCommand(hd44780_Commands_t command)
{
// Make sure that D4, D5, D6, D7, RS, RW, E are set to output ports
GPIO1_IODIR |= (PIN_D4 | PIN_D5 | PIN_D6 | PIN_D7 | PIN_RS | PIN_RW
| PIN_E);

// Write higher order bits
lcdEnableHigh ();
lcdDelay450ns ();
hd77480_Send_4Bits ((command >> 4) & 0x0F);
lcdDelay140ns ();
lcdEnableLow ();
lcdDelay550ns ();

// Write lower order bits
GPIO1_IOSET = PIN_E;
lcdDelay450ns ();
hd77480_Send_4Bits ((command) & 0x0F);
lcdDelay140ns ();
GPIO1_IOCLR = PIN_E;

// Delay before exiting
lcdDelay550ns ();
lcdDelay550ns ();
}

void hd44780_Init(void)
{
PCB_PINSEL1 = PCB_PINSEL1_P016_GPIO;
// Set D4, D5, D6, D7, RS, RW, E to output ports
GPIO1_IODIR |= (PIN_D4 | PIN_D5 | PIN_D6 | PIN_D7 | PIN_RS | PIN_RW
| PIN_E);

// wait for LCD to reset itself
DelayMS (20);

// Set default states
GPIO1_IOCLR = PIN_RS;
GPIO1_IOCLR = PIN_RW;
GPIO1_IOCLR = PIN_E;

// Wait for device to power up
DelayMS (500);

// Start initialisation sequence
for (int counter = 0; counter < 3; counter++) // Repeat 3 times
{
// RS RW DB7 DB6 DB5 DB4
// 0 0 0 0 1 1
GPIO1_IOSET = PIN_E;
lcdDelay450ns ();
hd77480_Send_4Bits (0x03);
GPIO1_IOCLR = PIN_E;
DelayMS (120);
}

// RS RW DB7 DB6 DB5 DB4
// 0 0 0 0 1 0
GPIO1_IOSET = PIN_E;
lcdDelay450ns ();
hd77480_Send_4Bits (0x02); // Set interface to be 4 bits long
GPIO1_IOCLR = PIN_E;
DelayMS (100);

// Function set (specify the number of lines and character font)
// RS RW DB7 DB6 DB5 DB4
// 0 0 0 0 1 0
// 0 0 N F * *
hd44780_SendCommand (HD44780_SETFUNC_4BIT_2LINE_5x8);

// Display OFF
// RS RW DB7 DB6 DB5 DB4
// 0 0 0 0 0 0
// 0 0 1 0 0 0
hd44780_SendCommand (HD44780_DISPOFF);

// Display CLEAR
// RS RW DB7 DB6 DB5 DB4
// 0 0 0 0 0 0
// 0 0 0 0 0 1
hd44780_SendCommand (HD44780_CLEAR_DISPLAY);

// Set Entry Mode
// RS RW DB7 DB6 DB5 DB4
// 0 0 0 0 0 0
// 0 0 0 1 I/D S
hd44780_SendCommand (HD44780_ENTRYMODE_INCR_NOSHIFT);
}

void hd44780_WriteChar(char c)
{
GPIO1_IOSET = PIN_E; // Set E high
GPIO1_IOSET = PIN_RS; // Set RS high

hd44780_SendCommand (c); // Write character to ram

DelayUS (50); // Wait 50 microseconds

GPIO1_IOCLR = PIN_E; // Set E low
GPIO1_IOCLR = PIN_RS; // Set RS low

DelayUS (50); // Wait 50 microseconds
}

void hd44780_DisplayString(char* str)
{
unsigned int i=0;

while (str[i] != '\0')
{
if (str[i] == '\n')
hd44780_SendCommand(HD44780_MOVE_CURSOR_DOWN); // New line
else
hd44780_WriteChar(str[i]);

i++;
}
}
*************** END hd44780.c ****************************

*************** START hd44780.h **************************

#define LCD_COMMAND_CLEAR 0x01
#define LCD_COMMAND_HOME 0x02
#define LCD_COMMAND_MODE 0x04
#define LCD_COMMAND_DISPLAY 0x08
#define LCD_COMMAND_SHIFT 0x10
#define LCD_COMMAND_FUNCTION 0x20
#define LCD_COMMAND_CGRAM 0x40
#define LCD_COMMAND_DDRAM 0x80

#define LCD_MODE_SHIFT_NO 0x00
#define LCD_MODE_SHIFT_YES 0x01
#define LCD_MODE_DECREMENT 0x00
#define LCD_MODE_INCREMENT 0x02
#define LCD_MODE_MASK 0x03

#define LCD_SHIFT_CURSOR 0x00
#define LCD_SHIFT_DISPLAY 0x04
#define LCD_SHIFT_LEFT 0x00
#define LCD_SHIFT_RIGHT 0x08
#define LCD_SHIFT_MASK 0x0c

#define LCD_FUNCTION_FONT_5X8 0x00
#define LCD_FUNCTION_FONT_5X11 0x04
#define LCD_FUNCTION_LINES_1 0x00
#define LCD_FUNCTION_LINES_2 0x08
#define LCD_FUNCTION_BUS_4 0x00
#define LCD_FUNCTION_BUS_8 0x10
#define LCD_FUNCTION_MASK 0x1c

#define LCD_DISPLAY_BLINK_OFF 0x00
#define LCD_DISPLAY_BLINK_ON 0x01
#define LCD_DISPLAY_CURSOR_OFF 0x00
#define LCD_DISPLAY_CURSOR_ON 0x02
#define LCD_DISPLAY_DISPLAY_OFF 0x00
#define LCD_DISPLAY_DISPLAY_ON 0x04
#define LCD_DISPLAY_MASK 0x07

typedef enum
{
PIN_RS = 0x00010000, // 1.16 Register Select
PIN_RW = 0x00020000, // 1.17 Read/Write
PIN_E = 0x00040000, // 1.18 Enable
PIN_D4 = 0x00080000, // 1.19 Data 4
PIN_D5 = 0x00100000, // 1.20 Data 5
PIN_D6 = 0x00200000, // 1.21 Data 6
PIN_D7 = 0x00400000 // 1.22 Data 7
}
hd44780_Pins_t;

typedef enum
{
HD44780_CLEAR_DISPLAY = 0x01, // Clear LCD Display
1.52 mS
HD44780_HOME = 0x02, // Move the cursor to
the home position 1.52 mS
HD44780_DISPON = 0x0C, // Turn display on
with no cursor and no blink 37 S
HD44780_DISPON_CURSUNDERNEATH = 0x0E, // Turn display on,
with cursor under 37 S
HD44780_DISPON_CURSBLINKING = 0x0F, // Turn display on,
with blinking 37 S
HD44780_DISPOFF = 0x08, // Turn display off
37 S
HD44780_ENTRYMODE_INCR_NOSHIFT = 0x06, // Auto increment
cursor position, no shift 37 S (Default entry mode)
HD44780_ENTRYMODE_INCR_SHIFT = 0x07, // Auto increment
cursor position, shift text 37 S
HD44780_ENTRYMODE_DECR_NOSHIFT = 0x04, // Auto decrement
cursor position, no shift 37 S
HD44780_ENTRYMODE_DECR_SHIFT = 0x05, // Auto decrement
cursor position, shift text 37 S
HD44780_MOVE_CURSOR_RIGHT = 0x14, // Shift cursor one
character to the right 37 S
HD44780_MOVE_CURSOR_LEFT = 0x10, // Shift cursor one
character to the left 37 S
HD44780_MOVE_CURSOR_UP = 0x80, // Shift cursor up one
character 37 S
HD44780_MOVE_CURSOR_DOWN = 0xC0, // Shift cursor down
one character 37 S
HD44780_SHIFT_TEXT_RIGHT = 0x1C, // Shift text one
space to the right 37 S
HD44780_SHIFT_TEXT_LEFT = 0x18, // Shift text one
space to the left 37 S
HD44780_SETFUNC_8BIT_2LINE_5x8 = 0x38, // 8 bit interface, 2
line display, 5x8 pix chars 37 S
HD44780_SETFUNC_8BIT_1LINE_5x8 = 0x30, // 8 bit interface, 1
line display, 5x8 pix chars 37 S
HD44780_SETFUNC_4BIT_2LINE_5x8 = 0x28, // 4 bit interface, 2
line display, 5x8 pix chars 37 S
HD44780_SETFUNC_4BIT_1LINE_5x8 = 0x20, // 4 bit interface, 1
line display, 5x8 pix chars 37 S
HD44780_BUSY = 0x80, // Check if the
controller is busy (1) or not (0)
HD44780_LINE2 = 0x40, // LCD display address
offset for line 2 (20x4)
HD44780_LINE3 = 0x14, // LCD display address
offset for line 3 (20x4)
HD44780_LINE4 = 0x54 // LCD display address
offset for line 4 (20x4)
}
hd44780_Commands_t;

// Method Prototypes
void hd77480_Send_4Bits(unsigned char v);
void hd44780_SendCommand(hd44780_Commands_t command);
void hd44780_Init(void);
void hd44780_DisplayString(char* str);

*************** END hd44780.h **************************

An Engineer's Guide to the LPC2100 Series

I've added LCD functionality to LPC-2148 demo code that I'm getting ready to
release. In the mean time, I've put the lcd_4bit.c file that I got working
today up at http://jcwren.com/arm The actual display I tested with is a
CrystalFontz CFAH1602Z-YYH-ET. This is a standard HD44780-based LCD, so it
should work the same as yours.
Until you get your display working, I'd suggest replacing
the lcdWaitWhileBusy() routine with a hard 30 millisecond delay. It'll make
the display look like a teletype when it's writing (which is sorta cool, if
you don't need to print lots of stuff).

I'm still looking at what you've currently got to see if I can find where it
went wrong.

--jc

I also plan to use a 16X2 LCD with the LPCH2148.  I am at the same stage as you are. 1 row blanc and other black.
I ran into the same issue with the PIC16F684 while trying to interface that to the LCD. I did not proceed further to resolve that completely.

I am therefore planning on a differnt approach with the LPCH2148. I have got the schematic for the IAR LPC 2148 R2T. I will be connecting the LPCH2148 with the LCD per the schematic and then run the sample code that comes with the IAR. I am hoping this will work. Later I will modify the code to suit the pinout I decide.

-sumit
----- Original Message ----
From: J.C. Wren
To: l...
Sent: Sunday, 5 October, 2008 7:32:28 PM
Subject: Re: [lpc2000] Re: HD44780 Initialisation (20x4 display in 4-bit mode)
I've added LCD functionality to LPC-2148 demo code that I'm getting ready to
release. In the mean time, I've put the lcd_4bit.c file that I got working
today up at http://jcwren. com/arm The actual display I tested with is a
CrystalFontz CFAH1602Z-YYH- ET. This is a standard HD44780-based LCD, so it
should work the same as yours.
Until you get your display working, I'd suggest replacing
the lcdWaitWhileBusy( ) routine with a hard 30 millisecond delay. It'll make
the display look like a teletype when it's writing (which is sorta cool, if
you don't need to print lots of stuff).

I'm still looking at what you've currently got to see if I can find where it
went wrong.

--jc




Add more friends to your messenger and enjoy! Go to http://messenger.yahoo.com/invite/


yes, it is normal.

If u had two line display one of the line will be.

in four lines alternate lines would be darker.

Natarajan

--- On Mon, 10/6/08, Kevin Townsend wrote:

From: Kevin Townsend
Subject: [lpc2000] Re: HD44780 Initialisation (20x4 display in 4-bit mode)
To: l...
Date: Monday, October 6, 2008, 3:57 AM

I was wondering if someone could confirm for me if it was normal that
when I add power to my 20x4 display, and connect the third pin to a
10K resistor on pin 1 and 2 (for contrast), that I have two lines of
solid blocks and two empty lines. I've attached a photo for reference
sake:

http://img503. imageshack. us/my.php? image=hd44780def aultstatevy5. jpg

Also, I hooked up a cheapo ebay oscilloscope that I bought, and
noticed when monitoring the different pins that the 4 data bits seems
to work (I see activity going up and down), but I don't seem to get
any response out of the E pin when it should be bouncing up and down
as I enter the following methods:

static inline void lcdEnableHigh( )
{
GPIO1_IOSET = PIN_E;
}

static inline void lcdEnableLow( )
{
GPIO1_IOCLR = PIN_E;
}

Admittedly, I'm completely new to all this, but shouldn't the IOSET
and IOCLR cause the pin to go up and down on the 'scope? I'm sure it
it connected to the probe properly, and the connection is fine between
the 2148 and the LCD board ... any ideas what might be explain this?
I've attached another image to show what I mean, with the breakpoint
on the left (after I've entered the method a few times):

http://img117. imageshack. us/my.php? image=escopedda7 .jpg

I appreciate any feedback anyone can offer.






I spent a bit of time individually checking all of the pins on my
H2148 board, and noticed something really odd that I'm sure is the
cause of the problem.

Of the pins between 1.16 and 1.31 on the H2148, 1.26..31 are reserved
for JTAG, but I expected 1.16..25 (except 1.24) to be available for
normal GPIO. When I hooked each pin up to an oscilloscope, though, it
seems that only 1.16 and 1.22-25 are actually working when I set and
clear the pins. Looking at the schematic, I can't see any reason pins
1.17..21 aren't working, and I passed 'PCB_PINSEL1 = 0x00000000;' to
make sure that GPIO is selected.

Does this make sense to anyone? If the board is toasted, wouldn't all
the pins fail?

http://www.olimex.com/dev/images/lpc-h2148-sch.gif

Kevin.
You need to set PINSEL2 to 0. The default is for those pins to come up for
the trace port.

--jc

On Mon, Oct 6, 2008 at 12:02 PM, Kevin Townsend wrote:

> I spent a bit of time individually checking all of the pins on my
> H2148 board, and noticed something really odd that I'm sure is the
> cause of the problem.
>
> Of the pins between 1.16 and 1.31 on the H2148, 1.26..31 are reserved
> for JTAG, but I expected 1.16..25 (except 1.24) to be available for
> normal GPIO. When I hooked each pin up to an oscilloscope, though, it
> seems that only 1.16 and 1.22-25 are actually working when I set and
> clear the pins. Looking at the schematic, I can't see any reason pins
> 1.17..21 aren't working, and I passed 'PCB_PINSEL1 = 0x00000000;' to
> make sure that GPIO is selected.
>
> Does this make sense to anyone? If the board is toasted, wouldn't all
> the pins fail?
> http://www.olimex.com/dev/images/lpc-h2148-sch.gif
>
> Kevin.
>
>
>

--- In l..., "J.C. Wren" wrote:
>
> You need to set PINSEL2 to 0. The default is for those pins to come
up for
> the trace port.
>
> --jc

Thanks again, and sorry if this is all fairly basic stuff.

Out of curiousity, though, what is the logic behind the numbering on
the following defines (ex. P13626, P12516). I don't remember seeing
PINSEL2 in the previous lpc210x.h file.

#define PCB_PINSEL2_P13626_GPIO ((unsigned int) 0x00000000)
#define PCB_PINSEL2_P13626_DEBUG ((unsigned int) 0x00000004)
#define PCB_PINSEL2_P13626_MASK ((unsigned int) 0x00000004)

#define PCB_PINSEL2_P12516_GPIO ((unsigned int) 0x00000000)
#define PCB_PINSEL2_P12516_TRACE ((unsigned int) 0x00000008)
#define PCB_PINSEL2_P12516_MASK ((unsigned int) 0x00000008)

In any case, thanks again for your help ... I really appreciate you
taking the time to share your expertise, and hope someone else finds
it useful as well down the road.

Kevin
PCB_PINSEL2_P12516_ means PINSEL2, Port 1, pins 25 through 16. Due to a
printing error in the manual, PCB_PINSEL2_P13626_ should REALLY be
PCB_PINSEL2_P12626_ or PCB_PINSEL2_P126_, since it only handles P1.26, not
P1.36 through P1.26. It ain't easy coming with names that aren't 50
characters long :)

For all my curmudgeonishnish and smacking down people that ask stupid
questions, I actually enjoy helping people that are putting effort into
trying to learn. It's very obvious that you're trying to figure out what's
wrong, and you're asking good questions, not "my proggie doesn't work why
please help NOW ur answer urgently neaded".

-jc

On Mon, Oct 6, 2008 at 12:38 PM, Kevin Townsend wrote:

> --- In l... , "J.C. Wren"
> wrote:
> >
> > You need to set PINSEL2 to 0. The default is for those pins to come
> up for
> > the trace port.
> >
> > --jc
>
> Thanks again, and sorry if this is all fairly basic stuff.
>
> Out of curiousity, though, what is the logic behind the numbering on
> the following defines (ex. P13626, P12516). I don't remember seeing
> PINSEL2 in the previous lpc210x.h file.
>
> #define PCB_PINSEL2_P13626_GPIO ((unsigned int) 0x00000000)
> #define PCB_PINSEL2_P13626_DEBUG ((unsigned int) 0x00000004)
> #define PCB_PINSEL2_P13626_MASK ((unsigned int) 0x00000004)
>
> #define PCB_PINSEL2_P12516_GPIO ((unsigned int) 0x00000000)
> #define PCB_PINSEL2_P12516_TRACE ((unsigned int) 0x00000008)
> #define PCB_PINSEL2_P12516_MASK ((unsigned int) 0x00000008)
>
> In any case, thanks again for your help ... I really appreciate you
> taking the time to share your expertise, and hope someone else finds
> it useful as well down the road.
>
> Kevin
>
>
>

JC is correct about setting PINSEL2 to 0. This will set those pins
back to GPIO if they were set to the ETM function. However, they do
not default to the ETM function. The state of P1.20 is sampled
coming out of reset and if low, the ETM function is selected,
otherwise GPIO is selected.

I just brought up a new proto where this "bit" me.

Jeff

--- In l..., "J.C. Wren" wrote:
>
> You need to set PINSEL2 to 0. The default is for those pins to
come up for
> the trace port.
>
> --jc
>
> On Mon, Oct 6, 2008 at 12:02 PM, Kevin Townsend
wrote:
>
> > I spent a bit of time individually checking all of the pins on
my
> > H2148 board, and noticed something really odd that I'm sure is
the
> > cause of the problem.
> >
> > Of the pins between 1.16 and 1.31 on the H2148, 1.26..31 are
reserved
> > for JTAG, but I expected 1.16..25 (except 1.24) to be available
for
> > normal GPIO. When I hooked each pin up to an oscilloscope,
though, it
> > seems that only 1.16 and 1.22-25 are actually working when I set
and
> > clear the pins. Looking at the schematic, I can't see any reason
pins
> > 1.17..21 aren't working, and I passed 'PCB_PINSEL1 0x00000000;' to
> > make sure that GPIO is selected.
> >
> > Does this make sense to anyone? If the board is toasted,
wouldn't all
> > the pins fail?
> >
> >
> > http://www.olimex.com/dev/images/lpc-h2148-sch.gif
> >
> > Kevin.
> >
> >
> >
>
>

After WAY too much time, I finally got my LCD working tonight gasp of relief here>. I still feel pretty dumb for having to spend a
week to accomplish this, but not as dumb as I would have felt if I
gave up and just accepted my overwhelming incompetence. :-) In any
case, thanks a lot for all the excellent (and patient) help I got on
this forum.

There's just one last question if anyone has time for it. I can
display things on the top two lines of the display, but I can't seem
to get any text displayed on lines 3 or 4 of the display. Does anyone
happen to know why this might be or know of an example of code for 4
line displays? I assume it's just an addressing issue, and I've
noticed that lines 1 and 3 seem to be line '1' and lines 2 and 4 seem
to be line '2'. I'm sure I'll figure it out eventually, but if
someone knows off the top of their head how to write to each line, it
might save me a bit more head scratching myself.

Kevin.

Memfault Beyond the Launch