Go Down

Topic: LCD menu flicker (Read 3834 times) previous topic - next topic

Pedro147

Thanks groundfungus,
I will have a look at that and see if I can incorporate it into the Tronixstuff code
http://www.pedroduino.com

bperrybap

#31
Sep 01, 2013, 04:42 am Last Edit: Sep 01, 2013, 04:44 am by bperrybap Reason: 1

Instead of trying to keep track of where I need spaces I have a function that I call before writing.

void lcdClearLine( byte line) {
 lcd.setCursor(0,line);
 lcd.print("                    ");  //number of spaces as chars wide
 lcd.setCursor(0,line);
}

I call this then print my stuff.


While that will clear the line and ensure that the display line starts clean,
it will not eliminate the flicker.
The end result of that method will still produce visible flicker.
Again, the flicker is caused by the eye seeing characters being erased (replaced by <space>)
and then being replaced/restored.
When the interface is not particularly fast, as in the case of i2c,
the timing for erasing the characters, and replacing them is longer
and so the "flicker" is even more obvious.

The flicker using that clear line method would be noticeable even if exactly the same characters
were being re-written.

The only way to eliminate the flicker is as I described above:
never use lcd.clear()  (or write spaces - which is the same thing - only sometimes can be slightly faster/better)
and then ensure that your updates overwrite
the old data so as not to leave "trash" from previous updates.


But as you all suggested if I take clear out from other areas of the code I get text printing where I do not want it. All your help has certainly helped me to understand LCD's a little more.


It isn't so much LCD's but how the eye and human brain perceive the updates.
It is the of timing sequence of events not a speed or rate of updates.
Imagine s-l-o-w-i-n-g everything down.
It is good exercise for visualizing the updates.
Imaging you are updating the LCD with exactly what is already being displayed.
And also imagine that there is a huge delay between the update of each character.
(imagine 1 second)
As you clear the screen, you would see a character disappear each second.
At the end of 16 seconds (32 if you clear the full display) all the characters have
been replaced with <space>.
Now update the display, again, a character shows up each second and at the end
of up to 32 seconds you have updated the full display back to what was previously there.
With this process there are lots of seconds where the display is incomplete.

Now change that to a process that simply overwrites the display with the new information.
If the information is the same as the previous information, you will see no changes during
the update process. Yes it may still take 16-32 seconds to update the full display
but there is never a time when characters are missing.

That is what is actually happening in real time.
The "flicker" you see is because the characters are being erased and then be replaced.
The time between them being gone and being replace is what you are perceiving as
"flicker". Since ic2 is not particularly fast, the time that characters are erased
is longer and so the perceived flicker is worse than on faster interfaces.

My recommendation is to eliminate lcd.clear() completely if you want to get rid of flicker.
If you can afford the 2k of code space, I'd recommend taking a look at using sprintf()
to create your output strings and then slam out full lines which will overwrite the previous data cleanly.

It is much easier to get your print formatting aligned and consistent when using sprintf()
vs the wimpy Arduino print() routines directly.

You could even define a common format for line1 and line2 and then call a routine
to build the buffer for printing.
Like you pass in "on"/"off" and the time values and then the formatting routine takes
the values and formats up full line buffer.

Here is a sample sprintf format string example:
Code: [Select]

char linebuf[17]; // extra character for null terminator.
sprintf(linebuf, "On%02d%02dhNow:%02d%02dh", , alarmh, alarmm, hour, minute);
lcd.print(linebuf);

or
Code: [Select]

// format routine
formatl1(char *buf, char*s1, int ah, int am, char *s2, int h, int m)
{
   sprintf(buf, "%2s%02d%02dh%4s%02d%02dh", s1, ah, am, s2, h, m);
}

char linebuf[17]; // declare buffer somewhere in code

// call formatting routine and use it
   formatl1(linebuf, "on", alarmh, alarmm, "Now:", hour, minute);
   lcd.print(linebuf);

etc...



--- bill


bperrybap

FYI,
If you want to really optimize your output, for smooth visuals, slowing things down
can be a big help in seeing what is really happening on the display.
For example, back in the late 70's & early 80's when I was doing lots of terminal emulation
and 80x24 screen applications, I would often slow down the baud rate to 300 baud to watch
the characters appear on the display.
This allowed me to see all the character updates and optimize the output to ensure
that my libraries were optimizing screen output efficiently.
Imagine doing a clear and then a full refresh of a 80x24 screen at 300 baud.
(It takes a bit more than 1 minute to fully write the screen).
Now if you only update what is needed, the time dramatically reduces.

If you want to do the same for your LCD code.
Go down into the lcd library and insert a delay
in the write() routine.
In fm's library code the write function is down in
LCD.cpp
Change this code to have the delays:
Code: [Select]
#if (ARDUINO <  100)
void LCD::write(uint8_t value)
{
   send(value, DATA);
   delay(250); // delay to slow things down
}
#else
size_t LCD::write(uint8_t value)
{
   send(value, DATA);
   delay(250); // delay to slow things down
   return 1;             // assume OK
}


Use any delay you want, but you will definitely see the effects
of inefficient LCD screen updates, if you do this.


--- bill



Pedro147

Thanks Bill. Your explanation of why clear or overwriting with blank spaces being perceived as flicker makes sense. I will also have a look at sprintf()

Just one quick point if I may. I did modify LCD.cpp to include the 250 ms delay and it would enter the chars one at a time left to right for the opening screen -

  Pedro's  Digital
On/Off LED Timer

and then the second screen -

Set On/Off LED
   timer data

Please excuse my corny text, it is just a bit of fun ... for me  XD

then the screen with the -

On0000h  Now:1359 <---(current time-not shown on screen)
Off0000h   Tmr:Off

But after settling on this screen, if I enter a button press to get into the menu to adjust any timer or clock data with the pot I cannot. I was just wondering why?
Thanks again for your always informative and well documented replies.
Pedro
http://www.pedroduino.com

bperrybap


On0000h  Now:1359 <---(current time-not shown on screen)

Not sure you what you mean by "current time-not shown on screen).
Is it the  "h" is missing?
It looks like the output is too many characters.
It looks like the output is 17 characters vs 16.
I saw that earlier, I guess I should have mentioned it.


Have fun, that's what life should be about.

--- bill

Pedro147



On0000h  Now:1359 <---(current time-not shown on screen)
Sorry it made sense to me at the time 
but I meant that 1359 is the time and that
"<---(current time-not shown on screen) " is not displayed on the screen. Hey now I am even confusing myself but definitely having fun  XD
Thanks again Bill.

http://www.pedroduino.com

joshuabardwell

Just to clarify, when I suggested writing spaces, I meant only writing spaces that would erase undesired characters. So if overwriting "P = 100%" with "P = 99%", there would be a trailing space on the second string to erase the left-over %.

bperrybap


Just to clarify, when I suggested writing spaces, I meant only writing spaces that would erase undesired characters. So if overwriting "P = 100%" with "P = 99%", there would be a trailing space on the second string to erase the left-over %.

I totally agree as that methodology is not pre-erasing characters with <space> that are about to be updated
like the lcdClearLine() groundfungus was proposing.

While it is a matter of taste, it might be better to put the space at the beginning of the number
so the % doesn't move around:
i.e.
Code: [Select]
"P = 100%"
"P =  99%"

Which you can easily get using sprintf().

Code: [Select]
sprinttf(lbuf, "P = %3d%%", percent); // %% is way to insert %

--- bill

joshuabardwell


Code: [Select]
sprinttf(lbuf, "P = %3d%%", percent); // %% is way to insert %


Bill, I'm trying to figure out if you're using some trick of sprintf() to automatically justify the number depending on its number of digits. So far, I only use the inbuilt functions of the Serial and LCD libraries to output, but it seems like you're indicating that there's a way to do it with sprintf() and get more functionality.

My method was just, "If x < 100, print one space; if x < 10, print two spaces."

bperrybap

Joshua,
I'm assuming you are not familiar with xxprintf() formating?
(Google printf and you will find plenty of information)
Its not a "trick" of xxprintf(), that's what it does.
It was designed to format text output and it does it very well.
Much better than any of the much more recent C++ stream i/o stuff that has come along since.
i.e.
stream << "string" << num
etc... simply can't do the fixed width formatting that xxprintf() does so easily.

The %3d part of the format string says print an integer as
a 3 digit field and use spaces to fill in if the number is smaller than 3 digits.
%03d does the same but will fill with zeros.

There is overhead in using xxprintf() (about 1.8k on AVR) because of the formatting capabilities
however, if you start getting into some complex formating, the code to handle
it yourself can start to get quite complex or messy.
Also changing field widths or fill characters is trival when using xxprintf() since
you just modify the formatting string. That kind of stuff is a total
pain if handling it inline with if/else etc...

However, often on Arduino projects even on the smaller AVRs, there is plenty of
room for the sprintf() code.


--- bill

joshuabardwell

Thanks, Bill. It has been, probably over ten years since I wrote C or C++, so I've forgotten even a lot of basic stuff. Can you elaborate on the syntax to send the sprintf() output to Serial, LCD, or some other stream--or provide a reference? Thanks.

bperrybap


Thanks, Bill. It has been, probably over ten years since I wrote C or C++, so I've forgotten even a lot of basic stuff. Can you elaborate on the syntax to send the sprintf() output to Serial, LCD, or some other stream--or provide a reference? Thanks.


Code: [Select]

char buf[any-size-you-need]; // declare a buffer

// format some text into the buffer as a character string
sprintf(buf, "fmtstring", args.....);
snprintf(buf, sizeof(buf), "fmtstring", args....); //protects against accidental buffer overflow.

// send it to Arduino Print class in lcd and Serial
// assumes names for lcd library objects of "lcd"
lcd.print(buf); // print a character string
Serial.print(buf); // print a character string


--- bill


Go Up