Speed of writing to a 16x2 LCD display

I wanted to see how fast I could write to a 16x2 LCD display, so I did some testing.

I ran this sketch

#include <LiquidCrystal.h>

// initialize the library by associating any needed LCD interface pin
// with the arduino pin number it is connected to
const uint8_t rs = 12, en = 11, d4 = 5, d5 = 4, d6 = 3, d7 = 2;
LiquidCrystal lcd(rs, en, d4, d5, d6, d7);

//                        111111
//       index  0123456789012345
char crazy[] = "0000000000000000";
const uint8_t crazyOnesIndex = 15;

void setup() {
  lcd.begin(16, 2);  
  lcd.setCursor(0,0);
  lcd.print("MY CRAZY COUNTER");
}

void loop() {
  crazy[crazyOnesIndex]++;
  for (uint8_t i=crazyOnesIndex; i>=1; i--) {
    if (crazy[i]>'9') {
      crazy[i] -= 10;
      crazy[i-1]++;
    }
  }
  // none of us will be alive when it gets to 10 quadrillion
  // neither will our great-grandchildren
  // neither will their great-grand-children
  // so who cares what happens then

  lcd.setCursor(0, 1);
  lcd.print(crazy);
}

and used an analog stopwatch to time how long it took to count to 100000. It took 8 minutes and 8 seconds. That works out to approximately 205 counts per second, or one count every 4.9 milliseconds.

Note that it is a 16-digit counter, and that for each count, I wrote the whole 16 digits to the display. This is because I wanted to test how long it takes to write a full line to the display. (I assume that the LiquidCrystal library does not take shortcuts.)

My questions are:

  1. Is it reasonable to assume that, with the LiquidCrystal library, writing to this display will always be this fast? (In other words: Can I rely on a 16-character write to this display not to take more than about 5 milliseconds?)

  2. Is the speed purely a function of the library, or does it also depend on the display itself? (In other words: What happens if I swap out this display for a 16x2 LCD display from a different manufacturer? Will it affect the speed?)

Go on. You can read the datasheet. A single command or data write is 38us with the internal RC oscillator running at a typical speed.

Add a good safety margin in case you have a slow RC. Then call it 50us. It makes the sums easier.
This would be 20000 characters a second.

In 8 minutes you could have written 160000 9600000 characters or 20000 1200000 8-digit ascii strings like "99999999"

However you look at it, it is too fast for humans to read and definitely too fast for the Liquid Crystals.

Bill Perry can probably tell you just how inefficient different libraries are. I doubt if any of them achieve 50us.
I suspect that his library has a reasonable performance. But does it matter? You would never be able to read at this speed.

David.

Edit. corrected my maths.

david_prentice:
You would never be able to read at this speed.

The "bottleneck" which concerns me is not human reading speed but rather the processor's cycles getting chewed up.

If I want to read a sensor once every 10 milliseconds, will I have time to write to the display in between, or will something have to give way?

Go on. Think about it. Your sensor is likely to be less than 12-bits i.e. 4 digits. At worst case you have to write 4 new digits.

That would be 4 x 50us or 200us if your library was written efficiently.

The standard library is not efficient. An I2C backpack is even slower. But it can still manage 4 digits in less than 10ms.

Only you know what your application requires. Only you know whether your users have X-Ray vision or can run faster than a speeding bullet.

David.

david_prentice:
Go on. Think about it. Your sensor is likely to be less than 12-bits i.e. 4 digits. At worst case you have to write 4 new digits.

Again you are jumping to conclusions.

With a bit of cleverness, you can cram a date and time into one line of a 16x2 display. I want to be able to write a date and time without breaking rhythm, and without having to code around the possibility that a write to the display will exceed, or even come uncomfortably close to, 10 milliseconds.

I have told you the theoretical time for a HD44780 and similar 16x2 controllers.
All 32 characters visible on the LCD would take 1600us.

If you are "cramming" a timestamp onto one 16-char line, it would take at least 800us for 16 chars.

I can't believe that you want to print the date every 10ms.

Note that Arduino "libraries" have to support random wiring e.g. with digitalWrite()
This means that they seldom achieve the 50us figure.

If you have a genuine need for "best performance" someone might help you. Note that it will probably mean you have to wire to non-random port pins.

David.

odometer:
The "bottleneck" which concerns me is not human reading speed but rather the processor's cycles getting chewed up.

This is becoming - has become - a very perverse and pointless "discussion". The display lag is of the order of a tenth of a second, and even if it could update that fast, it would be illegible.

odometer:
If I want to read a sensor once every 10 milliseconds, will I have time to write to the display in between, or will something have to give way?

The question is simply meaningless. You only ever wish to update the display if it is at least 100 ms since the last update and the data to be displayed has changed. In which case, you update whichever elements (characters) are different.

If you wish to display a bargraph, you generally use a moving average or peak hold and decay function to slow the response even further to make it useful.

The time it takes to update the display can be important. In some cases there may be critical other tasks that need to be done and if updating the display takes too long events might be missed. So having a fast update can be related to ensuring that other code is run in time rather than trying to simply update the display faster.

Post #1 talks about the hd44780 theoretical timing; however, the IDE bundled LiquidCrystal library gets no where near that level of timing.
From an actual timing overhead perspective, there are several sources of overhead.
The main culprit being sloppy and/or less than optimal or even poor coding.
One big culprit is the Arduino supplied digital i/o functions like digitalWrite() and pinMode() in the core library.
Those are killers. They take anywhere between 4.5 to 6us depending on the IDE version when running on a 16Mhz AVR.
They could be re-written to be MUCH faster.
For example, the Teensy versions are as much as 40x faster. (as low as 62.5ns)
In some cases, like the Adafruit I2C LCD library, the library code is elegant but horribly inefficient and very slow.
In the case of LiquidCrystal, it also suffers from doing some things less efficiently than it could be so it pays not only the price for the slow digital write functions, but also some additional overhead.

hd44780 is structured in a way to reduce and eliminate most if not all the overhead of the slow digital i/o functions by doing things in a more efficient way that allows the AVR to run in parallel with the LCD executing its commands.
This allows it to hide the overhead of the Arduino digital i/o functions.

In terms of actual timing, you can install the hd44780 library can get actual timing numbers.
The included LCDiSpeed sketch will tell you the time to transfer a byte to the display and the time to write to every position on the display.

It can be run using the hd44780 library or other libraries like the bundled LiquidCrystal or the Adafruit_LiquidCrystal libraries.
Using IDE version 1.8.7 on a 16mHZ AVR using direct arduino pin control with IDE supplied core functions:
(Teensy would be faster)
The hd44780_library with the pinIO class transfers a byte to the LCD in 92us.
The bundled LiquidCrystal library transfers a byte to the LCD in 285us.
The Adafruit_LiquidCystal library transfers a byte to the LCD in 373us.

So you can see while all three libraries are using the same Arduino IDE supplied digital i/o routines, how the library is structured and talks to the LCD can make a difference.

--- bill

2 Likes

bperrybap:
The time it takes to update the display can be important. In some cases there may be critical other tasks that need to be done and if updating the display takes too long events might be missed. So having a fast update can be related to ensuring that other code is run in time rather than trying to simply update the display faster.

Finally, someone who gets it!

1 Like

No. Everyone understands execution time, efficiency, throughput, ...

You have to put these things into perspective.

If you have a good reason for operating the LCD at 50us per character, we can show you how.
If you want to spend less than 4us servicing each character, we can show you this too.

Bill's library achieves 92us throughput. I doubt if many projects would notice Adafruit's 373us.
Arduino constructors are designed for punters that do random wiring.
Using an I2C backpack is much SLOWER but hey-ho, the hardware is very convenient.

As with most things in life, you can achieve a lot by using a bit of intelligence and going steadily. e.g. only printing changed letters.

David.

david_prentice:
As with most things in life, you can achieve a lot by using a bit of intelligence and going steadily. e.g. only printing changed letters.

The solutions I prefer are those which dispense with the problem entirely.

Problem: beard
"Solution": razor
Real solution: electrolysis

Problem: finding a street address
"Solution": map and/or GPS
Real solution: what they do in Washington, D.C.

Problem: displaying AM and PM on a clock
"Solution": indicator light for PM
Real solution: count 0 to 23 instead of 1 to 12

I am not looking for a "solution", but rather a solution.

David,
Perhaps you are missing what odometer was considering.
I thought he was fairly clear that he is wanting to ensure that a single LCD update does not take so long that he misses reading his sensor every 10 ms.

Updating the LCD in 50us per character is simply not possible when staying inside the Arduino environment and using LiquidCrystal, particularly on a 16Mhz AVR like the m328 or the 2560.
Yes, you can use raw port i/o to get there, but that is stepping outside of the Arduino APIs and would also require not using the LiquidCrystal library.
The only portable way to get there is to use another LCD library and another core or processor like
the Teensy 2 series or pic32 based one, or a esp8266.
The esp8266 has extremely fast i/o even when using the existing semantics of digitalWrite() and pinMode().

There are applications that will have issues with Adafruit's 373 us / char overhead, particularly since the current Print class API is blocking and does not allow anything else to run until all characters in a string are printed.
Yes you could wonk up the sketch code to break things into smaller printable bits but that can make things really messy and complicated especially if printing things like numbers which is formatted/handled by the Print class.

--- bill

1 Like

I was just trying to put some realism to the question.

You can write a custom class that achieves the theoretical times with a Uno. i.e. 50us or better if you poll BUSY.

But even if you use the bog-standard Arduino library it means you could print 35 letters in a 10ms period.

In practice, you are unlikely to change more than 16 letters at a time. Typically only one or two digits might change. Often no letters get changed at all.

No human is going to notice if an LCD update slips into the next 10ms slot.

You know the Arduino System better than me. I suspect that interrupts will still be working even if the LiquidCrystal is still busy.

David.

NOTE: the number of characters I mention below at 26 is wrong.
David was correct at 35
(see next post)

david_prentice:
I was just trying to put some realism to the question.

You can write a custom class that achieves the theoretical times with a Uno. i.e. 50us or better if you poll BUSY.

Not using Arduino APIs. Just using pinMode() to flip the pins from output to input and back to output takes longer than the 50us.

But even if you use the bog-standard Arduino library it means you could print 35 letters in a 10ms period.

At 373us per character for the LiquidCrystal library you can only print 26 characters and that is printing a constant string with no other processing.

In practice, you are unlikely to change more than 16 letters at a time. Typically only one or two digits might change. Often no letters get changed at all.

No human is going to notice if an LCD update slips into the next 10ms slot.

But this isn't about the update interval of the LCD, it is about the timing overhead of updating the display, which adds latency to other things being processed in loop().
For example, using the LiquidCrystal library you could only print around 26 characters in 10ms. Actually it will be less than that if you need to poll a sensor every 10ms since there is other overhead and then there is the time to actually poll the sensor.
Not to mention overhead for other things that may need to get done.

I suspect that interrupts will still be working even if the LiquidCrystal is still busy.

They are. And IMO, when real time things need to be done interrupts are the only way to ensure that things get procssed in time.
However, many people seem to avoid using them and depend on loop() processing & polling.
And for that to work, latency becomes an issue so things like how fast the display updates, can really matter.

--- bill

oops. :fearful: So, sorry I used the Adafruit 373us number instead of the LiquidCrystal 285us number, so yes you are correct that it is 35 characters not 26.

So yes there may be time to do a "typical" update of the display (which is only a portion of the display) and still have time to meet a 10ms sensor polling interval.

But for anything critical, I'd still recommend using interrupts. Then the timing of the display updates would likely become irrelevant.

--- bill