20-04 I2C LCD SLOOOOW to update

I have the 20x4 line LCD display with the I2C interface card on it.. http://www.ebay.com/itm/Blue-Serial-IIC-I2C-TWI-2004-204-20X4-Character-LCD-Module-Display-For-Arduino-/381375068233?hash=item58cbb9b849:g:oL8AAOSwFAZTviYj

it works fine, but is unbearably slow to update... about 78ms of time I need to perform other tasks in.. I tried to write one line at a time and in every instance it works out to about 1ms per character.

I'm using the latest "New Liquid Crystal" library available here https://bitbucket.org/fmalpartida/new-liquidcrystal/downloads/

All my text formatting is done to 4 strings before I start timing, so it looks like this

void loop()
    long int starttime = micros();
    lcd.setCursor(0, 0);



Are there any faster options? I would think that transfering 80 bytes should be doable in ~10ms or better or is I2C just that slow?

The basic Controller chip will take about 80 x 50us = 4ms to print 80 characters. If you use an I2C adapter to talk to the controller, it will be about 80 x 450us = 36ms.

Having quoted some theoretical times, you then need to consider a real life implementation. So 78ms does not sound unreasonable.

Have you tried Bill Perry's new library? (bperrybap) I would guess that he achieves the theoretical times, but I have not measured this.

LCD displays are slow. Humans can't read that fast anyway.


Is this the library you are talking about? https://bitbucket.org/bperrybap/openglcd/downloads/

I'll take a look at it and see if it does better..

My goal isn't to update more times per second, because as you said, I can't read that fast anyhow, but I have better things to do with my cpu cycles

I don't know how Bill is driving the I2C. But the Wire library uses interrupt-driven I2C with a 32-byte buffer. So you can effectively write 6 LCD bytes at a time.

The CPU overhead for interrupt comms is pretty trivial. Even though the I2C comms take a "long" time to complete, your application can be actively processing your other code.

Be realistic. Do you need to have immediate response? e.g. controlling a nuclear bomb. Or do you just want a crisp user display that looks professional?


I don't need to have immediate response, but I'm using this arduino to control other processors that time out they haven't had an update in ~10 ms or so for engine management.. as a bandaid solution I'm resending the last calculated values inbetween LCD Line updates to keep it going, but it's a very bad bottleneck at this point.. I'm hoping to get away from using a completely separate controller that is dedicated to updating the display.

I'm guessing that there's enough overhead that even sending 20 characters at a time is more than the 32 byte buffer can hold and that is preventing the code from continuing..

It's a rainy day here.. perhaps I'll have some time to try the new library today

The PCF8574 style of I2C LCD adapter requires 5 bytes per LCD command / data operation. There is little point in sending a block of characters. I doubt if it is much faster than one chacter at a time.

If you need to service engine management, concentrate on that. Updating a display is secondary. As long as you overwrite previous contents, it will look better than clearing display with a complete redraw.

Or simply keep a mirror copy of your display. Then you can minimise LCD / I2C traffic but remember than gotoxy() is as expensive as printing a character.

All this sounds complicated but you are trading speed for GPIO wiring. Adapters are convenient.


Rx7man: Is this the library you are talking about? https://bitbucket.org/bperrybap/openglcd/downloads/

No. That is for graphic lcds. You want the hd44780 library package here: https://github.com/duinoWitchery/hd44780 If you search the forum, you can find quite a few of my posts about it. It can quickly and easily be installed using the IDE library manager. The i/o class you will want is hd44780_I2Cexp Run the diag sketch (I2CexpDiag) first to make sure it is working with your h/w and that your i2c signals are properly terminated. Then you can run the LCDispeed sketches to see the performance and get the byte timing numbers. It has example sketches that run the i2c bus in 100kbit mode (the default) and also in 400kbit mode. hd44780 is quite a bit faster than fm's library and if you jump to 400kbit clocking (which is beyond spec for the PCF8574 but should work on the newer parts) it is even faster. If I remember correctly 400k mode is faster than using the IDE LiquidCrystal library with direct pin control.

david_prentice: The PCF8574 style of I2C LCD adapter requires 5 bytes per LCD command / data operation. There is little point in sending a block of characters. I doubt if it is much faster than one chacter at a time.

Actually, the number of bytes per i2c bytes per lcd command/data operation can vary quite a bit depending on how the author wrote the code.

The worst i2c LCD libraries are those from adafruit which send multiple bytes for every single signal on the LCD since they are treating the i/o expander port interface as a pseudo digitalWrite()/digitalRead() interface. They are REALLY slow.

My hd44780 library is more efficient than others. When sending a byte to the LCD, most libraries use multiple transactions, and most also use 3 bytes per nibble.

My library uses a single transaction and sends 2 i2c bytes per nibble. For example: Most libraries like fm's send 2 * (i2cSTART + i2cADDRbyte, + 3 + i2cEND) I send i2cSTART + i2cADDRbyte + 2 * (2 ) + I2cEND My library avoids an extra i2cSTART, i2cADDRbyte, i2cEND and 2 extra bytes for every single character printed.

Transferring more bytes per i2c transaction can help as the over i2c overhead is reduced START and STOP times plus the i2c address byte for i2c increase the overhead and if they can be avoided, it can help. Unfortunately, the print() interface in Arduino is single character to the lower layers, so it isn't possible to use print() to send multiple characters to the lower library to try to speed transfers to the display. The Print class will simply hand them 1 at a time to the h/w i/o library using the write() interface.

Another thing that hd44780 does to improve performance is handling the hd44780 LCD command execution timing in a much smarter way. This allows the Arduino to run in parallel with the LCD. So while every other library does a blind/dumb delay to wait for a command to complete, hd4480 will only wait if necessary. This allows the things like CPU overhead and i2c transfer time to reduce any needed command execution delay all the way down to zero if those other overheads were longer than the LCD command execution.

Another thing that can speed things up is smaller/stronger pullups. While it is just a small amount, if the pullups are stronger (smaller resistor value), the signals rise faster. This will cause the i2c h/w in the AVR to run just a tiny bit faster as rise time will be shorter and the chip will see bus signals just a tiny bit faster. Normally, this isn't something I'd ever want to deal with or count on as it such a small factor. A bigger factor is to run the bus at 400khz rather than the default 100khz if possible

--- bill

While you normally shouldn't need to - especially if running at 400khz, if you really want/need to push the envelop even further, you can set the hd44780 LCD command execution timing to smaller values (shorter times). This would need to be carefully tested to if you are telling the hd44780 library that the execution times are shorter than the timing values in the LCD datasheet.

--- bill

Just to give you some hd44780 library timing information
(You can get this using LCDiSpeed )

hd44780_I2Cexp at 100khz clock
byte xfer time is 549us
full 20x4 frame update is 46.1ms

hd44780_I2Cexp at 400khz clock
byte xfer time is 198us
full 20x4 frame update is 16.65ms

hd44780_pinIO with direct pin control:
byte xfer time is 92us
full 20x4 frame update is 7.69ms

Using IDE LiquidCrystal with direct pin control:
byte xfer time is 285us
full 20x4 frame update is 23.95ms

Note this was using IDE 1.8.1 on 16Mhz AVR parts.
The byte xfer time is the average byte transfer time when writing characters to the entire display.
If you use older IDEs the LiquidCrystal times will be quite a bit slower.

So you can see i2c with hd44780 at 400khz clock is actually faster than the stock IDE LiquidCrystal library that is using direct pin control

— bill

Thanks Bill!

Setting "Wire.setClock(800000)" helped drastically, down to 22ms for a full screen refresh.. I can work with this. I'll check out your library and try and get that to work as well (It looks complicated) and get a little more speed out of it yet.

Currently it's taking 2.4ms to do 9 analog reads, convert to a voltage, and format the entire screen.. Yes, now it's very much faster than I can read!

OK, I got your library figured out... usage is a little less straightforward than some, but it works well.

Yes, I got the results you mentioned, 16.7ms for 80 char refresh at 400 khz.. at 800khz which is the fastest I was able to successfully operate at it went down to 13.2ms.

I think I can call this issue solved.. 7x faster, and darned close to what I figured ought to be possible when I said ~10ms.. sure a far cry from 78ms! I knew there was room for improvement there, I can just about type that fast :P

Thanks again Bill!

Rx7man: OK, I got your library figured out... usage is a little less straightforward than some, but it works well.

This I don't understand. Can you explain a bit more what you mean by "usage is a little less straightforward than some"?

In terms of usage of the library, if you use the auto self configuration, hd44780_I2Cexp actually much simpler than any of the other i2c lcd libraries since you don't have to fill in constructor initialization parameters. And since the auto configuration works at run time, you can swap out LCD/backpacks and it will auto detect the new one on the next reboot even if the i2c address or pin mapping changes.

In terms of the actual API, the library tries to offer compatibility with existing LCD library interfaces when possible. It supports all the LCD 1.0 API functions and almost all LiquidCrystal API functions. The LiquidCrystal init() function will not be supported since it is h/w specific and hd44780 is designed to have a consistent API across all supported h/w.

--- bill

It just took me a little bit to figure out which sketch to use.. I really do applaud the autoconfig function and the simplicity once you get the first bit figured out.. works really slick! Yes, other LCD libraries are a bit kludgy with the initialization parameters.. I was speaking more about other types of libraries.. granted they are simpler usually as well.. I was using the TLC5940 PWM board, Include header, instantiate, set the values