16x2 generic LCD goes dark between character update, any fix?

I have a one of these generic 16x2 LCD’s.

When character box is supposed to be refreshed, the new characters pixel’s light REALLY dark before fading into the desired contrast.

The issue is that when the pixels light up with a very high contrast, it takes a while to fade. Because of this I am left with a pretty poor ~half second readable refresh time.

Here is my question. ** Is there any spot on the board where I can place or change an existing resistor to limit the contrast current when a character is initially set?**

If the display did not initialize into such a dark value, I believe it would take less time to fade. I used a 1500Ω resistor to limit contrast. I would potentially like to try 500-1000Ω for the initialization.

The generic 16x2 LCDs using an HD44780U compatible controller does not have to be, and should not be, refreshed. Once you write a character to the display it remains there, with no further action from you, until you either overwrite it with new information or you remove the power.

In order for us to fully understand what is actually happening you should supply us with a listing of the code that you are using along with a photograph of your setup.

Don

We got a little terminology mixed up!

When I said refresh, I meant when the lcd board receives a new character to display.

Here’s a picture of the issue.

Now that I think about it, it’s probably a poor quality display.

I do not think anymore that it is the voltage initiating the pixels that is too high, but rather the slow speed of the pixels flipping states.

The setup is typical.

-VSS grnd
-VDD +5v
-V0 1500Ω before grnd
-RW grnd
-K (backlight cathode) 500Ω before grnd

remaining pins are as initialized in the code

#include <LiquidCrystal.h>

const int rs = 8, en = 7, d4 = 12, d5 = 11, d6 = 10, d7 = 9;
LiquidCrystal lcd(rs, en, d4, d5, d6, d7);

void setup() {
  lcd.begin(16, 2);
  lcd.print("hello, world!");
}

void loop() {

  if(millis() % 500 == 0) {
    lcd.setCursor(0, 1);
    lcd.print(millis());
  }
  
}

What happens when you use the loop() code that is in the Arduino 'Hello world' example?

lcd.setCursor(0, 1);

lcd.print(millis() / 1000);

Don

My code is the same thing.

The % is a modulo.

%500 is update every half sec %250 is uparte every quarter spec

The image I posted above shows how it looks when it updates every ms.

By skipping millis()/1000, I don't truncate the milliseconds.

That didn't answer my question.

Don

The image I posted above shows how it looks when it updates every ms.

That is exactly what you would expect if the characters are being overwritten with different characters 1000 times per second. At what rate of overwriting does it give an acceptable appearance for you ?

This is not very clean because, if the commands in the loop execute within 1 mS, they will be repeated, possibly multiple times, albeit with the same characters, until the next milli second. You could add a delay of 1 mS after the lcd.print to prevent that and see what happens:

if(millis() % 500 == 0) {
    lcd.setCursor(0, 1);
    lcd.print(millis());
  }

From reply #2:

Now that I think about it, it’s probably a poor quality display.

A simple technique to find out is to use code that is known to work.

Don

circwhat: My code is the same thing.

The % is a modulo.

%500 is update every half sec %250 is uparte every quarter spec

The image I posted above shows how it looks when it updates every ms.

By skipping millis()/1000, I don't truncate the milliseconds.

Your code and what Don suggested are not the same thing. The code Don suggests updates the display as fast as possible but only changes the value being updated/written to the display updates at most once per second. The timing of the code in loop() does not matter.

Your code is attempting to detect a time interval based on looking at the millis() value. to decide when to run code to update the display. If that code happens to catch the millis counter value when it is EXACTLY an even multiple of the modulo value the update code is run. This is not guaranteed to happen every interval of time unless the code can look at the millis value more often than once/ms. The timing of the code in loop() very much matters.

It is not possible to update the display every ms. The LiquidCrystal code simply cannot update the display that fast with the output you are creating.

Here is the details of the timing problem: (numbers assume a 16Mhz AVR) The current LiquidCrystal library on IDE 1.8.5 takes 285us to send a single byte to the display. The older LiquidCrystal library on pre 1.6.6 takes 353us to send a single byte to the display.

Your display update code sets the cursor position then prints a number:

   lcd.setCursor(0, 1);
    lcd.print(millis());

To set the cursor position, you have to send a byte to the display. Then there is a byte for each printed digit. Even if you are using IDE 1.8.5 as soon as the millis() number is larger than 999 (bigger than 3 digits) the amount of time to set the cursor and print the millis() number is going to be more than 1ms. 285us + 3 * 285us = 1.140ms And this is just the overhead to send the bytes, and does not include any other overhead like the Print class code to form the digits or the other loop() code or the code outside of loop() that calls loop().

As the millis() number gets more digits the timing extends even more.

If the modulo code is attempting to update the display more often than the amount of time it takes to update the display it obviously won't work as expected.


I think before you go too much further, I think it would be good to show us how you have hooked up your contrast pin on the LCD module. All you said was:

I used a 1500Ω resistor to limit contrast.

That doesn't fully tell us how you hooked it up.

In the larger picture, trying to update the display every ms with a value that changes every ms makes no sense. Even if it was possible to update the the display that fast, the human eye takes several 10s of ms to be able to see something. So the lower digits would just be a blur.

And then there is the actual time it takes the liquid crystal liquid in the LCD to actually flip the molecules to change a pixel. Some of these low cost LCDs can take many 10s of ms up to 100+ms to fully flip pixels. So depending on what the code is doing, the speed of the liquid crystal, and the temperature, you can potentially see some ghosting.

That said, a slow liquid Crystal liquid may not be your only issue. The issue can be aggravated by how you are updating the display, how often what is being written to the display is changing, as well as our own eyes. i.e. it can take as long as 50ms for the "pixels" inside our eyes to fade.

Now if you say you see ghosting if you set your modulo value to 1000 or 500. Then you have a slow LCD display or you have some kind of contrast control issue.

The LCDs I have are cheap LCDs off ebay, and you definitely can see the that the liquidCrystal takes a bit of time to flip pixels. It is perhaps not quite a quarter second. So if even if you are displaying something that is only changing once per second, if you look really closely, you can see a bit of overlap as the pixels change from one digit to another.

I notice these effects much more on the reverse displays (dark background with light pixels). I'm guessing that this may be because the persistence of vision inside the eye. So the brain perceives the and senses old pixels as still being on just a bit longer then they actually are which contributes to the issue.

--- bill

Just as an update to finish the thread.

I have tried the following alternative:

    lcd.setCursor(0, 1);  
    lcd.print(millis()/100);
    delay(500);

It still exhibits the dark to white fade in. This can be most apparent on the 2nd character in the image above as the 6 fades into a 7.

I understand about timing issues, and that the modulo conditional statement could be skipped. This was just a quick proof of concept.

@bperrybap

-V0 had 1500Ω before grnd

remaing pins were as follows:

-VSS grnd -VDD +5v -V0 1500Ω before grnd -RW grnd -K (backlight cathode) 500Ω before grnd

Thanks for the insight everyone

I think you are running into the normal/typical operation of these types of LCDs. The liquid crystal used in these cheap LCDs is fairly slow at rotating its molecules to flip the pixels. So even though the human eye is not very fast, it can still often see and perceive the pixel transitions when you look very closely.

Pixel flipping/changing is not instantaneous, the molecules in the fluid have to rotate in the fluid to block the light or let the light flow through. So as the molecules rotate, the pixels either fade out or brighten up.

These low cost LCDs are not like some of the higher speed LCDs used in the newer lcd monitors or TVs where the pixels can flip in a just a few ms, these LCDs take many 10s or even 100+ ms to fully "flip".

--- bill

bperrybap:
I think you are running into the normal/typical operation of these types of LCDs.
The liquid crystal used in these cheap LCDs is fairly slow at rotating its molecules to flip the pixels.

Yup. I’ve noticed the same thing.

@circwhat:
I looked at your test code, and modified it so it updates only once every 500 milliseconds. (You can change this interval as you please, just by changing the value of the constant UPDATE_INTERVAL.)
I have not actually tried this code (I haven’t the hardware handy), but I think it will work.
Here you go:

#include <LiquidCrystal.h>

const int rs = 8, en = 7, d4 = 12, d5 = 11, d6 = 10, d7 = 9;
LiquidCrystal lcd(rs, en, d4, d5, d6, d7);

// variable to help us remember when the display was last due to be updated
unsigned long lastMillis = 0UL;

// constant for the interval at which we update our display
const unsigned long UPDATE_INTERVAL = 500UL;

void setup() {
  lcd.begin(16, 2);
  lcd.print("hello, world!");
}

void loop() {
  
  unsigned long thisMillis = millis();
  
  // update display only at the chosen interval
  if((thisMillis - lastMillis) >= UPDATE_INTERVAL) {
    lcd.setCursor(0, 1);
    lcd.print(thisMillis);    
    lastMillis += UPDATE_INTERVAL;
  }
  
}