ILI9341 Display Issue

Status
Not open for further replies.

Raymond_B

Well-known member
Working on a small project that is outputting data to a ILI9341 display from Paul wired per the instructions. The data is updating pretty fast (reading it off CAN bus @500k). The screen does not seem to clear data on fields that are constantly changing. I am not sure how to address this in the code. I tried doing a simple screen fill, but it just makes the screen flicker.

I am pulling 3.3v for the display and CAN transceiver from the Teensy's 3.3v pin if that matters. Also I do have a 100ohm resistor on LED to VIN as well.

Project is a Teensy 3.2 reading data off a CAN bus using a WaveShare SN65HVD230 transceiver. Attached is the sketch I am using.

Here is a picture, notice the issue at the bottom for the TPS value

614000C6-E462-40EA-BCB7-D2ADE695E79F_zps2g5hfeh2.jpg



View attachment RB_CAN_Logging.ino
 
That's normal behavior. Try to delete only the text that need to be rewitten. And rewrite this part only.

tft.fillRect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color)
 
Last edited:
That's normal behavior. Try to delete only the text that need to be rewitten. And rewrite this part only.

tft.fillRect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color)

Thank you, I noticed all that does is isolate the flicker to the area specified. Is that simply a limitation of the LCD panel?

On edit: I introduced a small delay off 500ms, that combined with the above seems to work well. It still has a "blink" affect, but not too bad.
 
Last edited:
Yep, and only re-draw if the text has changed.

OK thank you, this display is not the final one I'd like to use, but I wanted something inexpensive to test and learn with. A delay of 150ms looks to be the "smoothest" I can make it. When I do get to the point of looking for a larger (~7") display are there any you'd recommend for data that will be updated quite often? This is all engine data, which I hope to display in a nice digital dash eventually so there are a lot of items that will change quickly.

Maybe that is a better question for the project guidance section?
 
only re-draw if the text has changed.
If you do this per character your flicker will be incredibly isolated.

Either that or change the drawText to draw a black background behind it when it's called. This may already exist, I haven't actually used this library or screen
 
OK thank you, this display is not the final one I'd like to use, but I wanted something inexpensive to test and learn with. A delay of 150ms looks to be the "smoothest" I can make it. When I do get to the point of looking for a larger (~7") display are there any you'd recommend for data that will be updated quite often? This is all engine data, which I hope to display in a nice digital dash eventually so there are a lot of items that will change quickly.

Maybe that is a better question for the project guidance section?

You'll have this kind of problem with every Display.
The common technique to prevent flickering is double-buffering. That means you have two RAM-Buffers on the Display,
when the 1st is diplayed, you write the second, and vice-versa. This way, the whole content is displayed at once.
But i don't know which displays with SPI support a second buffer.
Another, additional techniqe, is to wait for the vertial-sync.
Or, re-write only the PIXELS that are changed.
Maybe there are more tricks.

But perhaps it's better to display bar-graphs, or another kind of "analogue" display ! They are better readable.

https://www.youtube.com/watch?v=iWcqSE_yjT4
 
Last edited:
Maybe there are more tricks.

Maybe we could do something creative here....

Unfortunately we don't have access to the vertical sync signal and we don't have a lot of RAM to buffer all the pixels. But we could dedicate a moderate amount of RAM to remembering several strings and the coordinately where they were written. Then we could create a function which draws two strings at the same time, rendering each line of the old string in black or whatever the background color is, and the new string in the foreground color. Done line by line, maybe this would minimize flicker?
 
I did a function for that in my last touch sample with the scroll areas. That would be cool, so much to track you'd want a structure /handle to x, y, fgc, bgc , font, Last_txt? Without that controlled, the overlay would be lossy.

User code to:set font , fgc=bgc, locate, print,fgc&bgc, locate, print, update text is tedious/repetitive but effective. Also given font and area extent a .clear(bgc) rectangle could be done
 
Last edited:
Maybe we could do something creative here....

Unfortunately we don't have access to the vertical sync signal and we don't have a lot of RAM to buffer all the pixels. But we could dedicate a moderate amount of RAM to remembering several strings and the coordinately where they were written. Then we could create a function which draws two strings at the same time, rendering each line of the old string in black or whatever the background color is, and the new string in the foreground color. Done line by line, maybe this would minimize flicker?

Ages ago, I suggested the string drawing function could be modified to take two strings, old and new. For each pixel, there are three cases:

  • No need to update
  • Needs to be updated with the background color
  • Needs to be updated with the foreground color
Someone posted the code to do this and claimed it worked.

I'll try to find the posts I'm referring to... Here they are:

https://forum.pjrc.com/threads/2630...display)-library?p=83639&viewfull=1#post83639

https://forum.pjrc.com/threads/2630...display)-library?p=83887&viewfull=1#post83887
 
Last edited:
@Paul, there is a command 0x45 get_scanline.
Maybe this can be used as "sync".
A function "waitforscanline(y)" could be useful.
Then, we could simply wait and update displaycontents near that line only when that line just passed the area we're updating - or when it is "far away".
A bit tricky :) but it might be useful for some users.

Edit: A function "int getscanline(void)" would be better/more flexible :)
 
Last edited:
I must be making a rookie mistake somewhere, but I am unable to find it. Here is the sketch I have created, basically the way I formatted the data I am simply re-drawing the rightmost part of the display where the data values are. Everything works great until I try to call gauge_display(); (line 69) when I do that from within the void loop output slows down greatly. What am I doing wrong?

I put in the serial part so I could see if the output was only slow to the LCD, but it is slow in Serial Monitor too.

View attachment RB_CAN_Logging_Extended_No_Flicker.ino
 
Last edited:
I must be making a rookie mistake somewhere, but I am unable to find it. Here is the sketch I have created, basically the way I formatted the data I am simply re-drawing the rightmost part of the display where the data values are. Everything works great until I try to call gauge_display(); (line 69) when I do that from within the void loop output slows down greatly. What am I doing wrong?

I put in the serial part so I could see if the output was only slow to the LCD, but it is slow in Serial Monitor too.

View attachment 6257

Try smaller "fillRect"s for the values only, and print the fixed texts like "Air-Fuel:" once in setup() only, not again and again in loop().

All that data transmitting over SPI takes too much time.
 
This is stopping you dead : delay(150);

Here's where I'd start . . . .


Do a global : elapsedMillis DisplayTime;

in gauge_display() remove the delay() and do ::

Code:
elapsedMillis DisplayTime;
void gauge_display() {  //Prints captured data from above to display
if ( DisplayTime < 150 ) return;
DisplayTime = 0;

...
}

This : tft.fillRect( 235, 0, 160, 240, ILI9341_BLACK );
is killing your screen appearance and update speed


Lots of rewriting of STATIC screen bits it looks like:

There are seven items like this:
Code:
  tft.setCursor(10, 210);
  tft.print("Air-Fuel:");
  tft.setCursor(235, 210);
  tft.print(AFR / 10);

Split them into two functions with changes to how the update is done:

Code:
  tft.setCursor(10, 210);    // DO ONCE per clear screen
  tft.print("Air-Fuel:");    // DO ONCE per clear screen

//  tft.setCursor(235, 210);    // Do each loop when changed and then do something like this instead
//  tft.print(AFR / 10);
oldAFR = tftSCprint( oldAFR, (AFR / 10), uint16_t fgc, uint16_t bgc,   235, 210 );

Since Each of the "label" fields like "Air-Fuel:" never changes you [ Don't ] need to rewrite them if you stop clearing the screen, you can split your gauge code into two and just call the half that does the static strings ONCE in setup and any time you might do a clear_screen.


Also - If you record the 7 prior updated numbers strings - then reprint the OLD in background Color - then the new value and update the 'prior' printed - make this work:

Code:
//Self Cleaning String print
//Self Cleaning number print
uint32_t tftSCprint( uint32_t oldV, uint32_t newV, uint16_t fgc, uint16_t bgc,   int xxt, int yyt ) {

  tft.setCursor(xxt, yyt);
  tft.setTextColor(bgc);
  tft.print(oldV);
  tft.setCursor(xxt, yyt);
  tft.setTextColor(fgc);
  tft.print(newV);
  return newV;
}
 
Last edited:
Thank you both! It makes such obvious sense to move the data labels to setup.

@defragster, I follow along with what you are saying, but the last two items have me confused code-wise. For example, I am not sure how to format the item below;

Code:
uint32_t tftSCprint( uint32_t oldV, uint32_t newV, uint16_t fgc, uint16_t bgc,   int xxt, int yyt )

oldV is simply one of my 7 values correct? so oldV could be oldAFR, or oldTPS, etc.? Also uint16_t fgc simply means foreground color correct? If so I am not sure how to fill in that value. Should I decare a variable in setup or simply enter it in this part of the code?

I've tried several different things to no avail.
 
Raymond_B : see the code just above - I coded a suggestive sample:

Code:
oldAFR = tftSCprint( oldAFR, (AFR / 10), uint16_t fgc, uint16_t bgc,   235, 210 );

You need to provide data for colors : fgc, bgc

You pass in ( an INT ) for OLD and one for NEW

The NEW int passed in is returned and shown as stored in OLD for that value.

You could also do this:

Code:
newAFR = AFR / 10;
if ( newAFR != oldAFR )
oldAFR = tftSCprint( oldAFR, newAFR, uint16_t fgc, uint16_t bgc,   235, 210 );

oldAFR MUST BE STATIC/GLOBAL.

The last two params are x,y coordinates taken by replacing the SetCursor in the sample code above

You could get FANCY and put all this into an 7 deep array of everything you need.
 
I really appreciate your help on this.

I am getting the following errors with this sketch.

C:\Users\R\AppData\Local\Temp\arduino_3465ee788ead1997e3f3bddfebcd1d2e\RB_CAN_Logging_Extended_No_Flicker.ino: In function 'void gauge_display()':
RB_CAN_Logging_Extended_No_Flicker:181: error: too many arguments to function 'uint32_t tftSCprint()'
uint32_t oldTPS= tftSCprint(oldTPS, TPS/10,ILI9341_GREEN,ILI9341_GREEN,235,35);
^
C:\Users\R\AppData\Local\Temp\arduino_3465ee788ead1997e3f3bddfebcd1d2e\RB_CAN_Logging_Extended_No_Flicker.ino:162:10: note: declared here
uint32_t tftSCprint() {
^
exit status 1
too many arguments to function 'uint32_t tftSCprint()'

View attachment RB_CAN_Logging_Extended_No_Flicker_2.ino
 
Last edited:
Raymond_B - hopefully you got through that. I cut and pasted with edits to two params. That is a normal compiler error telling you the number of parameters expected by the function definition does not match the number proved when you call the function. I've been busy powering up a new onehorse ESP8266 unit to working and couldn't get back. I'll give your code a look now.

<edit> : I see you did some of my suggestions - but didn't put my name in the credit comments :eek: ... :)

You sketch calls stuff not provided so I will try to use this "//notgot " to make it run

<edit> : you've made some fundamental errors putting in my suggestions - particularly where you took a generic function and hardcoded the values into it taking off the 'variable' parameters. Hopefully in a few minutes I can show you working code.
 
Last edited:
Raymond_B - I completed three of the displayed values for you - that leaves 4 - one of which is missing (Air-Fuel).

I left a DEBUG increment section for testing since I had one of these on my desk - you can remove this.

I fixed the errors in tftSCprint() and added a float version tftSCprintF().

I also added the update limiter for 150 ms using an elapsedMillis as originally shown.

It seems this should solve most of your problems and work for you. You may need to initialize the oldXYZ values to something over a possible value, as if they match it won't do the first update.

View attachment Raymond_B_feb01a.ino
 
A bit of an off the wall suggestion but you could try one of the following:

Moving average your value so it changes more smoothly. Not really a fix though as numbers don't lend very well to rapid reading or redrawing
OR
Create a VU graphical reading. As you'll be filling in solid blocks this should eliminate any flicker. Personally I find these a lot easier to interpret at a glance
vu-meter_21083265.jpg
 
I didn't have Raymond_B's large font - but the over write BLACK the old and write the new is pretty smooth - much better than any rectangle fill over a large area as it was before.
 
Thank you so much!! I really appreciate it, now I just need to get home to test.

EDIT: OK, looking through the sketch now it makes sense what I was doing wrong and what you originally meant. The function accepts the values, somehow that was lost on me the first time around :)

Again thank you very much!
 
Last edited:
A bit of an off the wall suggestion but you could try one of the following:

Moving average your value so it changes more smoothly. Not really a fix though as numbers don't lend very well to rapid reading or redrawing
OR
Create a VU graphical reading. As you'll be filling in solid blocks this should eliminate any flicker. Personally I find these a lot easier to interpret at a glance
View attachment 6260

Thanks for the suggestion, and you're right for some types of data it makes sense to display in that format, especially for at a glance type stuff. But for others the value is what you're looking for, e.g. gear position indicator, am I in 1st, second, etc. In a larger display I'd have a combination of both.

I had two goals with this, to just learn the code and display CAN data from my fuel injection/transmission setup. Secondly once I had that part figured out I was going to use a large LCD display, something along the lines of 7" or so. It will be a "digital dash" for my vehicle using something like this https://www.itead.cc/display/nextion.html?display_size=747

But for now the little 12.00 2.8" display is fine :)
 
Status
Not open for further replies.
Back
Top