Implementing HD44780 LCD specs correctly.

Go To Last Post
11 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

In another thread regarding LCD's not working, I made the observation, that the clocking of the data was not according to the HD44780 (and other LCD's) datasheet. In fact when I looked closer it seems that the majority of LCD code does exactly the same, they apply a +ve going clock pulse after the data has been written, whereas the specification shows a -ve going E pulse after the data has been written.
I have been doing this for years as well, so I decided to rewrite some of my existing code( which was based on he AVR-libc examples), implementing the specification fully. I believe that it works better then the original as I don't have the problem of the display sometimes not resetting correctly any more.

Previous post referred to https://www.avrfreaks.net/index.php?name=PNphpBB2&file=viewtopic&t=108247&postdays=0&postorder=asc&start=20
Discussion invited!

Proposed low level driver. I need to revise the delays as the fractional _delay_us() calls will take more much longer to execute. I will change to line "nops" after doing some simulation.

static void  hd44780_outnibble(uint8_t nibble, uint8_t rs) 
{ 
#if LCD_READ_ENABLED==TRUE 
   HD44780_PORT_RW &= ~_BV(HD44780_RW); 
#endif 
   if (rs) 
     HD44780_PORT_RS |= _BV(HD44780_RS); 
   else 
     HD44780_PORT_RS &= ~_BV(HD44780_RS); 
   _delay_us(0.1); 
   HD44780_PORT_EN |= _BV(HD44780_E); 
    _delay_us(0.1);    
   //merge control bits 
   HD44780_PORT_DT = (HD44780_PORT_DT & ~HD44780_DATA) | ((nibble << HD44780_D4) & HD44780_DATA); 
   _delay_us(0.2); 
   HD44780_PORT_EN &= ~(_BV(HD44780_E)); 
   _delay_us(0.1); 
   //Ehigh must be at least 250nS 
   //tcycE must be at least 500nS.       
}

Attachment(s): 

Charles Darwin, Lord Kelvin & Murphy are always lurking about!
Lee -.-
Riddle me this...How did the serpent move around before the fall?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I don't believe the spec says to write the data after the beginning of the E pulse. I believe it is simply documenting that the display is a latch (data latched by the E pulse) and that the data must be valid for some setup time before the trailing edge (and some hold time after), but that the data can be valid before that as well. Like e.g. a 74HC373 latch.

If the data was supposed to be written after the beginning of the E pulse, there would be some timing diagram connection between the beginning of the E pulse and the beginning of the data.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I came to the same conclusion as kk6gm. You don't have to write the data while E is high, but you can.

Don

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I concur with the two previous posts.

As of January 15, 2018, Site fix-up work has begun! Now do your part and report any bugs or deficiencies here

No guarantees, but if we don't report problems they won't get much of  a chance to be fixed! Details/discussions at link given just above.

 

"Some questions have no answers."[C Baird] "There comes a point where the spoon-feeding has to stop and the independent thinking has to start." [C Lawson] "There are always ways to disagree, without being disagreeable."[E Weddington] "Words represent concepts. Use the wrong words, communicate the wrong concept." [J Morin] "Persistence only goes so far if you set yourself up for failure." [Kartman]

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Just to repeat myself and others here:

As the datasheet says, the data bus must be valid the given time before and after the falling edge of E.

So it is perfectly OK even if the data is set up long before falling edge of E, so long that it is stable even before rising edge of E. It makes no difference. However the order it is done in code causes different delays between operations and thus may make it work better because you can interleave the operations better with proper delays. Plus you can set E high and set the data bus in one operation, delay, and then set E low while preserving data bus, so this reduces the amount of port writes. Helps if the display is connected through SPI or I2C buses with an IO expander.

But what is important that most people do not realize is that RS and RW lines must be stable given time before rising edge of E. And that the RS, RW and DATA all need to be stable for the given time after falling edge of E.

Also many people driving their LCDs do not realize that while RS and RW and even DATA lines have an internal pull-up, but the E line does not, and thus E line is floating in thin air, probably clocking data to display, when the AVR is still in reset.

So it is best to have something like a 10 kohm pull-down on E line to avoid problems.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks guys! OK, I can see that! I have gone with the salient parameters AND made it follow how the timing diagram looks, but there is no obvious parameter to suggest that data cannot be written before E goes high.
Japael, :

Quote:
However the order it is done in code causes different delays between operations and thus may make it work better because you can interleave the operations better with proper delays.

Are you saying that the traditional code is better in that regard or that the way that I have done it may be better?

The suggestion regarding a pull-up (or pull-down) is a good idea that is worth recommending when LCD issues occur.

Charles Darwin, Lord Kelvin & Murphy are always lurking about!
Lee -.-
Riddle me this...How did the serpent move around before the fall?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I know I've typed this into another thread recently, but I'll repeat it here since this is a good thread to have it in. Simplifying the timing diagram (for writing) as I suggested above, you end up with five timing constraints:

* RS and R/W must be stable for at least 40 ns before E goes high (tAS)
* E must be high for at least 230 ns (PWEH)
* Data lines must be stable for at least 80 ns before E goes low (tDSW)
* Data lines, RS and R/W must be stable for at least 10 ns after E goes low (tH and tAH)
* A complete E cycle must be at least 500 ns (tcycE)

As of January 15, 2018, Site fix-up work has begun! Now do your part and report any bugs or deficiencies here

No guarantees, but if we don't report problems they won't get much of  a chance to be fixed! Details/discussions at link given just above.

 

"Some questions have no answers."[C Baird] "There comes a point where the spoon-feeding has to stop and the independent thinking has to start." [C Lawson] "There are always ways to disagree, without being disagreeable."[E Weddington] "Words represent concepts. Use the wrong words, communicate the wrong concept." [J Morin] "Persistence only goes so far if you set yourself up for failure." [Kartman]

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

In practice, most LCDs are implemented with a single PORT. So building the data, RS, WR state on the PORT in one go is fine. You then need a single strobe on the E line with the appropriate timing.

Since AVRs are clocked at <= 20MHz (50ns), the 40ns constraint is hardly a problem. However you need both the OUT and SBI as two different instructions.

The 230ns means that you need some NOPs between the SBI and CBI that performs the strobe for any AVR > 8MHz. (the CBI takes 250ns @ 8MHz)

David.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I have followed up on Johan's suggestion to remove some of the delays. Inspection of the asm shows that all timing requirements are implement requiring only one "nop" to ensure tDSW up to 20 Mhz,.

static void  hd44780_outnibble(uint8_t nibble, uint8_t rs)
{
  //all essential times are achieved by interleaving
#if LCD_READ_ENABLED
    HD44780_PORT_RW &= ~_BV(HD44780_RW);
#endif
    if (rs)
      HD44780_PORT_RS |= _BV(HD44780_RS);
    else
      HD44780_PORT_RS &= ~_BV(HD44780_RS);   
    HD44780_PORT_EN |= _BV(HD44780_E);
    HD44780_PORT_DT = (HD44780_PORT_DT & ~HD44780_DATA) | ((nibble << HD44780_D4) & HD44780_DATA);
    asm("nop"); //required to ensure data setset time 
    HD44780_PORT_EN &= ~(_BV(HD44780_E));
}

I have also taken the opportunity to split up the control lines so that they can be any bit on any port, to enable easy implementation on various hardware configurations.

Edit,
Typo

Charles Darwin, Lord Kelvin & Murphy are always lurking about!
Lee -.-
Riddle me this...How did the serpent move around before the fall?

Last Edited: Thu. Jul 7, 2011 - 11:45 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Ah-ha. You will get an SBI, OUT, NOP, CBI in a perfect world (with absolute data). No, I have not looked at the generated ASM.

So the E strobe will be 4 cycles wide (200ns @ 20MHz).

Looking at your expression, the ideal single OUT will be at least IN, ANDI, ORI, ANDI, OUT (250ns @ 20MHz). So you would need no NOP at all.

Lee, you have a Logic Analyser. You can measure it yourself in real life. Otherwise just look at the ASM.

So by re-arranging the write order, you can avoid any AVR clock dependency. Yet still obey the timing.

Mind you, LCD displays are notorious for having slow RC clocks. I would design for the HD44780 running at 70%. The bus timing constraints may be absolute. The actual operation time is dependent on the HD44870 clock cycles e.g. data-write takes 10 clocks. (37us @ 270kHz or 53us @ 190kHz)

David.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
---- hd44780.c ------------------------------------------------------------------------------------
55:       {
+000005A2:   2F98        MOV       R25,R24        Copy register
60:         if (rs)
+000005A3:   2366        TST       R22            Test for Zero or Minus
+000005A4:   F021        BREQ      PC+0x05        Branch if equal
61:           HD44780_PORT_RS |= _BV(HD44780_RS);
+000005A5:   91800065    LDS       R24,0x0065     Load direct from data space
+000005A7:   6180        ORI       R24,0x10       Logical OR with immediate
+000005A8:   C003        RJMP      PC+0x0004      Relative jump
63:           HD44780_PORT_RS &= ~_BV(HD44780_RS);   
+000005A9:   91800065    LDS       R24,0x0065     Load direct from data space
+000005AB:   7E8F        ANDI      R24,0xEF       Logical AND with immediate
+000005AC:   93800065    STS       0x0065,R24     Store direct to data space
64:         HD44780_PORT_EN |= _BV(HD44780_E);
+000005AE:   9A97        SBI       0x12,7         Set bit in I/O register
65:         HD44780_PORT_DT = (HD44780_PORT_DT & ~HD44780_DATA) | ((nibble << HD44780_D4) & HD44780_DATA);
+000005AF:   91800065    LDS       R24,0x0065     Load direct from data space
+000005B1:   709F        ANDI      R25,0x0F       Logical AND with immediate
+000005B2:   7F80        ANDI      R24,0xF0       Logical AND with immediate
+000005B3:   2B89        OR        R24,R25        Logical OR
+000005B4:   93800065    STS       0x0065,R24     Store direct to data space
66:         asm("nop"); //required to ensure tDSW at 20 Mhz. 
+000005B6:   0000        NOP                      No operation
67:         HD44780_PORT_EN &= ~(_BV(HD44780_E));
+000005B7:   9897        CBI       0x12,7         Clear bit in I/O register
68:       }
+000005B8:   9508        RET                      Subroutine return

It seems there are 9 clock cycles from the end of setting E high to the end of setting E low. I would have thought that would be 450nS. at 20 Mhz.

Yes, I will pull the logic analyzer out tomorrow and see what numbers come up.

Charles Darwin, Lord Kelvin & Murphy are always lurking about!
Lee -.-
Riddle me this...How did the serpent move around before the fall?