Tuesday, July 15, 2014

Controlling HD44780 displays

This post is a follow-on to my earlier post, What's up with HD44780 LCD displays?

A lesson in reading datasheets

A search for details on programming the HD44780 will result in many different ways of doing it.  I think one of the reasons is that datasheets are often ambiguous.  I'd say the HD44780U datasheet is not only ambiguous, it's not well organized.  When trying to understand the bus timing characteristics, you have to flip back and forth between the tables on pg. 52 and the diagrams on pg. 58.  After doing that too many times, I added the timing to the diagram:
When working with MCUs clocked up to 20Mhz, the minimum instruction time is 50ns.  Therefore if one instruction sets the R/W line low, and the next instruction sets E high, there will be at least 50ns between the two events.  Therefore when writing control code, it is safe to ignore tAS, tAH, and tH, which I've written in green.  If the next instruction after setting E high sets it back to low, the Pulse Width for E High (PW-EH) will be only 50ns.  To ensure the minimum pulse width is met, E should be kept high for at least 5 instruction times at 20Mhz or at least 4 instruction times at 16Mhz.

When analyzing the datasheet, it's helpful to engage in critical thinking, even if the author of the datasheet didn't!  The datasheet shows what timing is sufficient, however it's not completely clear on what timing is necessary.  For example, look at the RS line.  The timing diagram indicates it is sufficient to set the RS line 40ns before the E pulse, and hold it for 10ns after the E pulse.  Having a good idea of how the chip works based on the block diagram on pg 3, I'd say it's not necessary to assert RS until just before the falling edge of the E pulse.

One of the most frequent problems people seem to have controlling these devices is the initialization sequence.  In this matter the datasheet is not only unclear on what is necessary, it is even contradictory in places.  After reading about the problems people have encountered and experimenting with the devices myself, I believe I can condense what's sufficient to initialize the devices down to 6 steps:
  1. wait 15ms
  2. send command (1 E pulse) to set 8-bit interface mode
  3. wait 65us
  4. send command (1 E pulse) to set 8-bit interface mode
  5. wait 65us
  6. send command (1 E pulse) to set 4-bit interface mode
The first 15ms wait is from pg 23 of the datasheet referring to start-up initialization taking 10ms. This is probably dependent upon the internal oscillator frequency, which is typically 270kHz.  Page 55 of the datasheet shows how the frequency depends upon the voltage and the value of the external oscillation resistor Rf.
The modules have a 91k resistor on the back in the form of a small SMD part marked 913 (91 x 10^3).  With 5V power the minimum frequency would be 200kHz, or 35% slower than the typical timing listed in the datasheet.  I've put a red dot on the 3V graph to show what can happen if you try to a module at 3V that has 91k for Rf; the frequency could be as low as 150kHz, so commands could take almost twice as long as the typical values listed in the datasheet.  I bet this is one of the reasons people sometimes have problems using these displays - if the controlling code is based on the minimum frequency at 5V and the device is run at 3V, it may fail to work.

Peter Fleury's HD44780 library, and some others wait longer than the datasheet specified times to cover these differences.  For instructions the typical time required is 37us, so waiting 65us should be a safe value.  I based this on the 200kHz minimum frequency at 5V with 30% added for an extra margin of error.

The reason for steps 2-6 is because the device could be in 4 or 8-bit mode when it powers up.  The datasheet says, "If the electrical characteristics conditions listed under the table Power Supply Conditions Using Internal Reset Circuit are not met, the internal reset circuit will not operate normally and will fail to initialize the HD44780U."  All the devices I've seen do not initialize as described on pg. 23 of the datasheet.  I suspect they use cheaper clones of the HD44780U that didn't bother with the internal reset circuit.

If the device starts up in 4-bit mode, it will take two pulses on the E line to read 8 bits of an instruction.  The lower 4 bits of the instruction to set 4 or 8-bit mode do not matter.  By setting the high nibble (D4-D7) to the instruction to set 8-bit mode and then toggling E twice, it will either be processed as the same instruction twice in 8-bit mode, or one instruction in 4-bit mode.

Another HD44780 AVR library was written by Jeorg Wunsch.  The delays between instructions is 37us, so it is likely to have timing problems with displays that are running at less than the typical 270kHz frequency.  Both Peter's and Jeorg's code can write a nibble of data at a time.  Here's the code from Peter's library:
        dataBits = LCD_DATA0_PORT & 0xF0;
        LCD_DATA0_PORT = dataBits |((data>>4)&0x0F);

Corruption can occur if an ISR runs which changes the state of one of the high bits of LCD_DATA_PORT while that section of code executes.  In my LCD control code if LCD_ISR_SAFE is defined, interrupts are disabled while the nibble is written.  Another difference with my library code is it doesn't use the R/W (read/write) line.  Since the initialization code can't read the busy flag and has to used timed IO, there's almost no extra code to make all of the IO timed.  Overall the code is much smaller without reading the busy flag, and needs 6 instead of 7 IO pins to control the LCD.  Just short the RW line (pin 5 on the LCD module) to ground.

I did not use any explicit delay between turning on and off the E line.  Looking at the disassembled code, the duration of the E pulse is 7 CPU cycles, which would ensure the E pulse is at least 230ns even on an AVR overclocked to 30Mhz.   In addition to the control code, I've written a small test program.



  1. Hi. as I see You have reviewed the LCD_4Bit mode very well.
    Can You help me here, can i use Your source on my project?
    I'm using stc15f mcu and have standard problem with cpu reset and strange symbols when cleaning.


    1. My code is not for the 8051, so you'll have to make some changes to port it.