Electronic Hobbyists Corner

Home
Site Information
Contact Details

Projects
Technical Articles
Reference Section
Component Tutorials

Site Map
Links
Using The HD44780 Liquid Crystal Display
Host Interface
Interface Operation
Instruction Set
LCD Initialization
Display Mapping
Downloads



LCD Displays:
Liquid crystal displays have become a popular means of visual output for a range of electronic devices such as digital watches, alarm clocks, CD players, mobile phones and so on. They come in a huge variety of shapes and sizes for just about any application and can even be used to display color graphics. This article will focus on the popular HD44780 based black and white character LCDs.

This particular display is readily available from most electronic retail outlets for reasonable prices, (usually around NZ$25) is relatively easy to use with an 8bit microcontroller and offers the usual advantages of such as low current consumption and high contrast in direct sunlight. Most importantly the built in controller takes care of all the ugly details of keeping the goop under the glass happy, leaving the host microcontroller to do other things without having to maintain the display.

This is what the average 4 line 20 character display looks like.


4x20 HD44780 LCD


Sizes other than 4x20 are available such as 1x8, 1x16, 2x20 and 2x40. 4x40 displays are also available but these displays have two controllers on them because the HD44780 can only handle 80 character cells.

Host Interface:
Before a LCD can be of much use to anyone it needs a host to tell it what to display. This is usually a microcontroller of some sort but can be anything capable of generating the correct signals for the display controller, such as a PC parallel port. The examples in this page will use a PIC16F628 as the host.

The physical connections from the host to the LCD are standardised for most displays which use a single controller. (anything up to 80 characters) Usually the connections are made with a SIL header 14 or 16 conductors wide, as can be seen on the top left of the display pictured above. The first 14 conductors are shown in the pinout below, and are almost always in this arrangement.

Pin Description
1 Ground.
2 Positive supply.
3 Contrast control.
4 Register Select signal.
5 Read/Write, signal.
6 Enable or Strobe.
7 D0, data bit 0.
8 D1, data bit 1.
9 D2, data bit 2.
10 D3, data bit 3.
11 D4, data bit 4.
12 D5, data bit 5.
13 D6, data bit 6.
14 D7, data bit 7.

Pins 1 and 2 are the power supply for the controller and display, which must be within the range of 4.5V to 5.5V for normal operation. Modern clones of the HD44780 are often built on CMOS technology allowing operation down to 2.5V, meaning some displays will retain their configuration and contents outside the normal supply range but the display itself will likely dim and become unreadable until the supply voltage returns to the valid range.

The contrast control (pin 3) is an external adjustment for the display driving voltage which directly effects the contrast of "set" pixels. You can apply any voltage inside the supply rails with contrast increasing the closer this voltage is to ground. Connecting this pin to ground (maximum contrast) often works fine, otherwise a 10KΩ pot is the simplest solution.

The Register Select signal (pin 4) determines which internal register is addressed for a read or write operation, the instruction register or the data register. Commands are written to the instruction register to perform operations like display blanking and scrolling and the busy flag is read from the instruction register to determine if the controller is ready to accept another command. The data register is written to put characters on the display and read to retrieve the contents of the display.

The Read/Write signal (pin 5) controls the direction of the data bus and therefore defines a read or write operation. When this signal is low the data bus is high impedance and the host can write to the register selected by pin 4. When this signal is high the data bus is driven by the display and can be read by the host. Be sure to release the bus (put into high impedance mode) before attempting a read operation to avoid bus contention. Many applications only require that the display be written to allowing this signal to be connected directly to ground.

The Enable or Strobe (pin 6) is a "go" signal to the display controller. All signals on the interface are ignored until a rising edge is detected on this pin. This allows RS, R/W and D0-D7 to be set up correctly rather than the controller responding to one signal before another is set and doing something funky. Most parallel buses work in this way.

The data bus (pins 7 to 14) transfer data to and from the display.

Most displays which are equipped with LED back-lights allow a direct 5V drive from pins 15 and 16. (with pin 15 more positive) If your display has these pins it's usually safe to assume current limiting is built into the display. When the back-light connections are on the side of the display and marked "A" and "K" then this may not be the case. For displays with EL back-lights there is usually similar connections on the edge of the display to which ~90VAC must be applied.

An example schematic for connecting a HD44780 compatible display to the PIC16F628:


8bit host interface to PIC16F628


One can easily see that there isn't very many I/O pins left over, which is no good for most projects. Fortunately, there's a few things that can be done to alleviate this problem. (other than using some sort of I/O expansion)

First of all, most applications don't actually need to able to read from the display at all. (not even to read the busy flag if sufficient delays are inserted between commands) This means the R/W signal can be connected directly to ground reclaiming one I/O pin. Most applications also don't require software control of the back-light, meaning that Q1 and R1 can removed and the back-light wired directly to the power supply. Finally, the HD44780 also sports a 4bit interface where only the upper 4 bits of the data bus need to be connected to the host. (data is sent as bytes split into two nibbles) If all these points are viable the I/O count for the LCD can be reduced by half.

A second schematic for a functional interface requiring half the I/O pins:


4bit host interface to PIC16F628


The code examples on this page will all be written for this schematic, but can easily be converted to most configurations.

Interface Operation:
The actual operation of the interface can be a bit confusing at first, but is actually quite simple.... (no, really) The HD44780 bus has the basic elements of any parallel bus; data lines (D0-D7), address lines (just RS because there's only two externally addressable registers) direction control (R/W line) and a strobe signal which tells the display controller than the signals presented are valid and that it should do something about it.

A write operation involves setting the data to be written on D0-D7, deciding where to write to and setting RS accordingly, (low to write to the instruction register and high to write to the data register) ensuring R/W is driven low to indicate a write operation and pulsing the enable signal to tell the LCD to read the bus. This can be accomplished with the following PICmicro assembler code for the above schematic.

Note that this code is written for a PIC16F628 running at 20MHz and wired like the above schematic. Minor changes may be required for other devices, clock speeds and wiring. RS is a constant for the pin number of the Register Select signal and TempF is any RAM location which is not required to survive this routine.

LCD_OUT:     ; LCD_OUT: Send WREG to LCD as data
  movwf TempF ; Save byte to be sent into temporary register
       
  bsf PORTA, RS ; Set Register Select to address the data register
       
  movlw 0x0F ; Clear the upper nibble of PORTB (data bus)
  andwf PORTB, F  
       
  movf TempF, W ; Move the upper nibble of the byte to be sent to PORTB...
  andlw 0xF0  
  addwf PORTB, F ; ...without changing the lower nibble of PORTB
       
  call LCD_STROBE ; Pulse the Enable signal to write this nibble
       
  movlw 0x0F ; Clear data lines again
  andwf PORTB, F  
       
  swapf TempF, W ; Move the lower nibble of the byte to be sent to PORTB...
  andlw 0xF0  
  addwf PORTB, F ; ...without changing the rest of PORTB
       
  call LCD_STROBE ; Send second nibble to display
       
  call LCD_DELAY ; Wait for the data to be processed by the display
       
  return    

This code provides a sub routine called "LCD_OUT" which will send the contents of WREG (loaded immediately before calling the sub) to the display. The code examples below are also required to generate the enable signal and provide a delay of ~1ms for the LCD to process the data.

LCD_STROBE:     ; LCD_STROBE: Assert the LCD's enable signal for at least 1us
  bsf PORTA, E ; Set enable signal
  nop   ; Wait for 1us (200ns * 5)
  nop    
  nop    
  nop    
  nop    
  bcf PORTA, E ; Clear the enable signal
       
  return    

At 20MHz a PICmicro could send hundreds of commands while the LCD controller is still executing the first one, which is one of the most common difficulties hobbyists encounter when first using HD44780 displays. Even if your host is checking the busy flag (by reading the instruction register) this can still be a problem because the busy flag is not valid until the LCD is properly initialized. Delays are necessary to avoid lost commands.

Execution time varies between displays and commands, with character writes taking a few µs and clear display commands taking as much as a full second on larger and older displays. Generally a delay of ~1ms is sufficient for all writes on new displays. The following code will provide this delay at 20MHz.

LCD_DELAY:     ; LCD_DELAY: Pause for ~1ms to allow command execution to complete
  movlw 0xE7 ; Load counters for delay
  movwf DelayL  
  movlw 0x04  
  movwf DelayH  
       
LCD_DELAY_LOOP:      
  decfsz DelayL, F ; Decrement low byte of delay counter...
  goto $+2 ; ...if not zero, then skip decrement of high byte
  decfsz DelayH, F ; Decrement high byte of counter on underflow of low byte
  goto LCD_DELAY_LOOP ; Loop back until high byte reaches zero
       
  return    

ASCII character codes can be sent to the display with the above code but nothing will happen if the display has not be initialized, which requires that command bytes be sent as well. Sending a command is the same as sending data (first code example above) but the RS signal must be driven low otherwise the LCD will display character which has the same value as the command byte. Below is a routine to send WREG to the LCD as a command.

LCD_CMD:     ; LCD_CMD: Send WREG to LCD as a command
  movwf TempF ; Save byte to be sent into temporary register
       
  bcf PORTA, RS ; Set Register Select to address the instruction register
       
  movlw 0x0F ; Clear the upper nibble of PORTB (data bus)
  andwf PORTB, F  
       
  movf TempF, W ; Move the upper nibble of the byte to be sent to PORTB...
  andlw 0xF0  
  addwf PORTB, F ; ...without changing the lower nibble of PORTB
       
  call LCD_STROBE ; Pulse the Enable signal to write this nibble
       
  movlw 0x0F ; Clear data lines again
  andwf PORTB, F  
       
  swapf TempF, W ; Move the lower nibble of the byte to be sent to PORTB...
  andlw 0xF0  
  addwf PORTB, F ; ...without changing the rest of PORTB
       
  call LCD_STROBE ; Send second nibble to display
       
  call LCD_DELAY ; Wait for the data to be processed by the display
       
  return    

The above four routines, LCD_CMD, LCD_OUT, LCD_STROBE and LCD_DELAY provide a basic software interface for your program to talk to the display.

Instruction Set:
Before looking at display initialization, a quick summary of the HD44780 instruction set. This can also be found in the quick reference and datasheet in the downloads section below.

Command Description
01h Clear display
02h or 03h Return cursor home
06h Print from left to right without automatic display shifting
04h Print from right to left without automatic display shifting
07h Print from left to right with automatic display shifting
05h Print from right to left with automatic display shifting
08h, 09h, 0Ah or 0Bh Turn off display (contents will be remembered)
0Eh Turn on display with underline cursor
0Dh Turn on display with blinking block cursor
0Fh Turn on display with both block and underline cursors
10h, 11h, 12h or 13h Shift display one character to the left
14h, 15h, 16h or 17h Shift display one character to the right
18h, 19h, 1Ah or 1Bh Move the cursor one character to the left
1Ch, 1Dh, 1Eh or 1Fh Move the cursor one character to the right
20h, 21h, 22h or 23h Set 1 line mode (or 2 line mode on 4 line displays)
28h, 29h, 2Ah or 2Bh Set 2 line mode (or 4 line mode on 4 line displays)

The instruction set also provides a means to change the display font size from the standard 5x7 to 5x10. (in 1 line mode only) However, I have never managed to get this to work so have omitted from the above summary. Have a look at the quick reference below for the full instruction set if you wish to try your luck.

LCD Initialization:
When a HD44780 display is powered on it is in a not very useful "unknown" state. Characters sent to the controller will not appear on the display until it has been woken up and configured. The HD44780 does actually have a self-initialization feature which automatically prepares the display to receive characters, but for some silly reason this only happens if the supply rise time (how long it takes the power supply to reach the valid operation range) is short enough. Unless you want your project to be dependant on the power supply it's generally a good idea to manually initialize the LCD.

The initialization sequence in four bit mode is as follows:

Wait for at least 50ms after power supply rises...
|
...Send wakeup sequence (usually 2h three times)...
|
...Wait for 5µs or more...
|
...Send function set...
|
...Set display parameters...
|
...Set entry mode...
|
...Clear display

Some code to perform initialization for the example schematics.

LCD_INIT:     ; LCD_INIT: Initialize LCD
  movlw 0x4F ; Load 50ms delay
  movwf DelayL  
  movlw 0xC4  
  movwf DelayH  
       
INIT_DELAY:     ; Run in circles for 50ms, this gives the LCD time to power up
  decfsz DelayL, F  
  goto $+2  
  goto DelayH, F  
  goto INIT_DELAY ; Loop back until delay counters reach zero
       
  movlw 0x0F ; Clear the upper nibble of PORTB
  andwf PORTB  
       
  movlw 0x02 ; And load it with 02h, the first nibble of the "wakeup" sequence
  addwf PORTB  
       
  call LCD_STROBE ; Send nibble to display, without using LCD_CMD
       
  movlw 0x22 ; Send the second two nibbles of the "wakeup" sequence
       
  call LCD_CMD  
  movlw 0x28 ; Send the function set: 4 bits, 2 lines, 5x7 font
  call LCD_CMD  
       
  movlw 0x0C ; Setup display: Display on, no cursors
  call LCD_CMD  
       
  movlw 0x06 ; Set entry mode: Auto increment, no display shifting
  call LCD_CMD  
       
  movlw 0x01 ; Clear display
  call LCD_CMD  

Uninitialized displays usually revert to 1 line mode which results in a slightly higher contrast than 2 line mode, meaning displays often appear to show one (or two) lines of block characters when first powered up. When the display is properly initialized into 2 line mode these rows of block characters should disappear and any enabled cursors should appear.

When first starting out with LCD's, it's easy to get stuck on display initialization. It either works and your LCD comes to life, or it doesn't and you haven't a clue why. The first thing to check is that all the required signals are being driven by the host controller. Program your host to drive one signal high and test it with a multimeter, (needs to be close to 5V) reprogram to drive low and test again. Repeat this process for the other signals. If everything is working as expected, try increasing the delay between writes. (even to as much as one second) If your display begins to work, reduce the delays until things stop working again and settle for something that works without taking to long.

The usual time to initialize the display is immediately after the host microcontroller has initialized itself after a reset. However, when using four bit mode it is important to ensure that the display is not re-initialized on subsequent non-POR (power on reset) events. This is because the "wakeup" sequence consists of an odd number of nibbles when using four bit mode, which will result in the LCD getting out of sync with the incoming data bytes and start do strange and confusing things. Thankfully, the PICmicro provides a flag in the PCON register which allows the user to determine the cause the the most recent reset. The following two lines of code will invoke the LCD_INIT routine only after power on resets.

  btfss PCON, POR ; Check for POR, initialize LCD if so
  call LCD_INIT  
  bsf PCON, POR ; Set the POR bit so that subsequent resets are detected correctly

The POR flag is cleared in hardware after a power on reset and left unchanged after all other resets, therefore it is necessary to set the bit after checking it otherwise subsequent resets will appear to be power on resets as well.

Display Mapping:
When the host sends characters to the display they are held in DDRAM. (Display Data RAM) Like many memory devices, this RAM is arranged into addressable byte sized locations which can be read or written. The location of the cursor acts as an address pointer and each address location maps onto a character cell in the display. (not all DDRAM locations are visible in all display sizes, but can be made visible by shifting the display) Obviously, the host needs to know how the memory maps onto the physical display in order to place characters in ways that make sense. Below are the standard DDRAM to display mappings for common LCD sizes. (in hexadecimal)

The "..." signifies the end of the display window in the home position. Characters can be printed in all display locations as far as the end of the line, but will only be visible if the display is shifted. Note that displays which have 80 characters (such as 2x40 or 4x20) will still shift, but all display locations are visible at all times.

2x16 display:
00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F ... 27
40 41 42 43 44 45 46 47 48 49 4A 4B 4C 4D 4E 4F ... 67


2x20 display:
00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 10 11 12 13 ... 27
40 41 42 43 44 45 46 47 48 49 4A 4B 4C 4D 4E 4F 50 51 52 53 ... 67


2x40 display:
00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 01 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F 20 21 22 23 24 25 26 27
40 41 42 43 44 45 46 47 48 49 4A 4B 4C 4D 4E 4F 50 51 52 53 54 55 56 57 58 59 5A 5B 5C 5D 5E 5F 60 61 62 63 64 65 66 67


4x20 display:
00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 10 11 12 13
40 41 42 43 44 45 46 47 48 49 4A 4B 4C 4D 4E 4F 50 51 52 53
14 15 16 17 18 19 1A 1B 1C 1D 1E 1F 20 21 22 23 24 25 26 27
54 55 56 57 58 59 5A 5B 5C 5D 5E 5F 60 61 62 63 64 65 66 67


Downloads:
These downloads are provided "as is" with no warranty or guarantee of any kind.

HD44780U Datasheet - Details on the HD44780 controller itself including the interface specifications.
HD44780 Reference - The "Extended Concise LCD Datasheet", a useful quick reference of the instruction set, display addressing and bus timing.
PICmicro Assembly Routines - A collection of sub routines for the PICmicro which deal with most display operations.



If you have any comments or questions please don't hesitate to contact me.

Return To Top Last Updated: 19/02/2007 Home Page