Any faster ways to update an LCD screen?

hey all. I have a quick question with a problem i'm having.

The loop of my code has to run at least 60 times a second. But is adjustable and will run as slow as twice a second in some situations that will be determined by me. I have 2 pots being read on the analog. and i'm trying to show the variables from the pots on my LCD. Here's my problem. without any LCD printing, the code runs 185 times a second. If i try to print 4 lines to the lcd, 2 text, and 2 number values, the fastest my code will run is 22 times a second, which won't work for me. If i remove the 2 text lines and only print the 2 number values it will run 85 times a second... which will work, but i was hoping to have the text to go along with it if possible.

i have basic LCD experience so there's a lot i don't know. I tried to set it all up in the setup, and then update only the numbers, but that doesn't seem to work because the lcd.clear() removes everything and then i'm left with only the numbers.

below is my code, any suggestions would be appreciated. I can work with only numbers, but this is a issue i've had in the past and hope to find a workaround.

//Nano  ATmega328P processor

#include <Wire.h> 
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x27,20,4);
                // LCD SDA = A4 pin
                // LCD SDL = A5 pin

volatile uint32_t count;

int pwmDriver = 9;
int counter = 5;

int onPot = A0;
int gatePot = A1;

long gateTimer;                //pot variable count
long pulseCount;



void setup() {

  TCCR1A = 0;                                    // Clear the TCCR1A register
  TCCR1B = _BV(CS12) | _BV(CS11) | _BV(CS10);    // Set T1 (D5) as the timer 1 clock source triggered on the rising edge 
  TIMSK1 = _BV(TOIE1);                           // Enable overflow interrupts on timer 1


 pinMode(counter, INPUT);
 
    DDRB = B00000010;        // pinMode(pwmDriver, OUTPUT);
 
 lcd.init();                     
 lcd.backlight();

  int onReading = analogRead(onPot);                          
  int pulseCount = map(onReading, 1023, 0, 1, 20);  

  int gateReading = analogRead(gatePot);                          
  int gateTimer = map(gateReading, 1023, 0, 1, 500);

     lcd.setCursor(0,0);
     lcd.print("Pulses... ");
     lcd.setCursor(12,0);
     lcd.print(pulseCount);
     
     lcd.setCursor(0,1);
     lcd.print("Gate On.. ");
     lcd.setCursor(12,1);
     lcd.print(gateTimer);     

     delay(3000);
}

void loop() {

PORTB = B00000010;          // digitalWrite(pwmDriver, HIGH);

  int onReading = analogRead(onPot);                          
  int pulseCount = map(onReading, 1023, 0, 1, 20);  

  int gateReading = analogRead(gatePot);                          
  int gateTimer = map(gateReading, 1023, 0, 1, 500);

      
 //  lcd.setCursor(0,0);
 //  lcd.print("Pulses... ");
     lcd.setCursor(12,0);
     lcd.print(pulseCount);
     
 //  lcd.setCursor(0,1);
 //  lcd.print("Gate On.. ");
     lcd.setCursor(12,1);
     lcd.print(gateTimer);     
       
 
     TCNT1=0; 
      
   while (TCNT1 <= pulseCount){
    PORTB = B00000000;          // digitalWrite(pwmDriver, LOW);
   }
   
    PORTB = B00000010;          // digitalWrite(pwmDriver, HIGH);

  delay(gateTimer);
   
  lcd.clear();
}   

Then don't use LCD.clear. Print ONLY when some value is different from is on the screen. Yes, you will need to save the values and compare them.

I'll try that out. thanks

Update as few characters, in as few cycles/passes as possible.

There’s no need to refresh an LCD at anything greater than 5-10Hz, because the LCD chemistry and the viewer won’t be able to ro see it.

The most common method (saves cpu time as well), is to use a timed event to update only the relevant bits of the screen every 100-200mS.

2 Likes

Unfortunately LCDs are slow, in fact, you might want to consider the update code as blocking.

If something important needs to execute, then do not update your LCD.

Say you are needing a 10 ms HIGH pulse and you are using a millis( ) (BWD) timer to create this pulse.
A two line LCD can take ~25ms to update, therefore our pulse could be 10 ms but some time it could be 35 ms.

Put a limit when your LCD update code can run.
i.e. if(pulse == LOW && it is time to update LCD) { . . . }

1 Like

This is crazy-town stuff. If you think you can read a changing LCD output at anything like that speed, you are just kidding yourself or lying. Reading an LCD that changes at 1Hz is hard enough. In short, you are the problem, not the display.

If you really need a visual output, there is a faint possibility that you can do it with a better choice of display. A large OLED may be able to give you a graph, say five seconds long, by virtue of only having to change one pixel each time round your loop.

1 Like

Not sure what the previous poster is talking about....

Anyway, a lot of good programming advice has been given so far. However, you have not mentioned what what type of LCD display you are using. Are you using a 320x240 LCD graphic display, or a simple 16x2 LCD character display?

From your description, I assume you are using a 16x2 (or a 20x4) LCD character display. Regardless of what display you are using, as mentioned, only update the data needed, not the whole display. Timing is also a thing too.

You also haven't mentioned what kind of arduino you are running this on. I will assume an UNO, as that what most of us start out with. The UNO is really great, but there are faster options out there. The arduino Zero is 4x faster and has 8x the memory.

Hope this helps,
Randy

if you want an easy way to speed up writing characters to the LCD, switch libraries.
If you switch to the hd44780 library and use the hd44780_I2Cexp i/o class with no other changes, the updates will be much faster as LiquidCrystal_I2C on a 16mHz AVR takes about 1500us to write a byte to the display. hd44780 does it in about 550us or about 3X faster.

You definitely want to eliminate using clear() as that takes 2ms to execute inside the LCD which blocks other updates.

If your power is clean, your wires are short, and you have no other slow i2c devices, you can bump the i2c clock rate up to 400kHz from its default of 100kHz.
At 400kHz the hd44780 library can write a byte to the LCD in about 200us.

--- bill

The big big problem with most LCD libraries is that they don’t monitor the LCD’s busy pin, but simply insert a delay that they hope will be long enough. As each sort of operation takes a different time a lot of these delays are too long and slow the down the response time. I have found at least a four fold increase in speed monitoring the busy pin.

Also the rest of the advice is good.
Only write when you need to and only write the part of the screen that has changed.

if you just remove .clear() what are the speeds?

I have a 20x4 lcd with a nano. I also have a couple small oled's, but i really haven't messed with them much yet.

it looks like i just need to update the data after each loop instead of clearing and rewriting the whole display.

the lcd.clear() only takes about 3ms to run. but it's been at the end of the loop so that doesn't effect too much. But reprinting those 4 lines i've been using takes about 40ms which really effects the pwm code i'm using.

On the AVR platform, if the code/library is using the portable API functions to do the pin control vs directly touching the h/w registers, then using BUSY will always be slower than doing some sort of timed delays.
And counter-intuitively, 8 bit will be slower than 4 bit mode when using BUSY.

Using BUSY status will be slower than timed delays if the code/library is using the portable digital i/o routines provided by the Arduino core library. i.e. pinMode(), digitalRead(), digitalWrite()
This is because the way the Arduino.cc team has implemented these functions, particularly on the AVR platform, has so much overhead that the time it takes to flip all the data bus pin from output to input mode so the status can be read, then read the busy state, then flip the pins back to output mode is longer than the time it takes for the LCD to execute most instructions. Much longer.
The only exception to this is for the HOME and CLEAR instructions which for many LCDs is actually shorter than the time noted in the datasheet.
But if optimizing display updates those instructions should not be used anyway.
And for most applications HOME is not necessary since setting the cursor to 0,0 is what most people want anyway and setting the cursor position is MUCH faster than HOME.

So in reality for Arduino portable code (which is code that uses the Arduino APIs) not using BUSY is actually a faster way to update the LCD.

The hd44780 library handles the LCD instructions differently than other libraries.
It allows the LCD to execute the instruction in parallel while the Arduino processor is running rather than the simpler way of just doing blind full length instruction time delays after sending an instruction to the LCD.

--- bill

You can calculate the LCD update time using the timing numbers I provided.
With the library you are using, it takes 1.5ms per character you print to the LCD on a 16mHz AVR.
You can lower that quite a bit if you switch LCD libraries.

--- bill

I will check that out when i have the time. The biggest problem i have is that i'm somewhat new to arduino and coding, and there always seem to be at least 20 ways of doing any one thing and i always run into limitations and then have to find a better way to do it. But I do need to learn how to update the code when needed instead of the way i've been doing it.

if time matters you should could consider to use the LCD without an I2C expander and connect the LCD directly to your Arduino.
If you run out of pins, you could try SPI instead of I2C. With SPI the transmission times will be reduced to about 25% in comparison to I2C.
There is some potential when you check how the print library, the I2C library and the LCD library are working together, but even with modifcations in print - the bottleneck will be I2C.

I only mentioned OLED because they are much faster than LCD, but they are not a silver bullet. Still, as I understand your problem, anything is better than a 20x4, and a graphic LCD may be quite OK. This would be easily tested with a library example, and the principle could be established with the OLED you already have.

In this case, they seem to be using the I2C-based LCD library, so none of that applies, right? (Well, I guess at least one I2C implementation simply uses an I2C port expander to connect to a a traditional parallel LCD, so you MIGHT be able to deal with pins individually, and it would be even worse than digitalWrite/etc. But I don't THINK that that's how they do it...)

It still matters but yes polling BUSY over I2C will be even slower than polling with direct pin access.
Some i2c based hd44780 devices can not be read from like the AIP31068L based devices due to a h/w bug. And if using ones that use a port expander backpack, it is much slower due to so many i2c transfers needed to read the pins.
But the point is that using BUSY is never faster than timed delays unless you have direct pin control access and, if on an AVR it also requires using direct register access to by pass the portable Arduino pin i/o api.

I once had a discussion with a person here on the forum that wanted to try to increase performance by trying to poll BUSY across I2C through the I2C expander. They rapidly abandoned it when I explained to them the overhead involved.

And many people think that direct pin control is always going to be faster than I2C. But this isn't always the case.
For example, on a 16 Mhz AVR,
the LiquidCrystal library takes 284us to transfer a byte to the LCD through the print() function.
The hd44780 library using the hd44780_I2C exp i/o class using a PCF8574 i/o expander clocked at 400kHz (instead of the default 100Khz) will transfer the byte in 200us.

The hd44780 library can hide much of the pin i/o overhead so if using the hd44780 library hd44780_pinIO i/o class instead of the LiquidCrytal API, the byte transfer time is reduced to 91us and that is using the same portable pin i/o API functions used by LiquidCrystal.

So this why I said a performance bump can be had by simply changing libraries.
The hd44780 library will give a 3X or more improvement over LiquidCrystal_I2C or LiquidCrystal on AVR platforms with no other changes.

--- bill

Not sure where you are at here at all. Using busy in my EXPERIENCE is always faster than using a delay. I have done it, I have measured the speed increase of doing it. How could it not be faster than seeing when the chip is actually free to accept another command. Remember the LCD takes a variable amount of time to do any one command, using a delay HAS to set the time to the maximum time it could take.

I think you should reconsider your comments.

Who is polling this pin over I2C?
It needs to be polled at the driver end.