ILI9488 slow background change

I am using a 3.5" ILI9488 display with an ESP32 but when updating the display (changing background color for example) i can see the display being updated from the top to the bottom.

I have set the SPI_FREQUENCY to 40Mhz (read this is the max value for this driver).
//#define SPI_FREQUENCY 40000000

I tried setting this value higher and it seems to update quicker (except 80000000, this gives strange display effects), but even 240000000 works (but isn't much faster).

Is this normal or can this be improved?
I also tried the LVGL library and the examples work, but as soon as the entire display updates (background color change or scroll) it is being rendered very slowly from top to bottom.

I read the ILI9431 should be faster but i don't know if changing to this display will help (i need 3.5").

I have seen video's where people use these displays to show games and they have 20+ fps (while the LVGL benchmark shows an average of 4fps on my display)

Please post a link to the actual display that you have bought.

Please quote the library that you are using by name.
And the name of any library setup file

And obviously the name of the library example.

It takes a few seconds to copy-paste or type these links and names.
Then we don't have to guess !!

David.

I'm sorry

The display i am using is: 2.4/2.8/3.2/3.5 Inch Tft Lcd scherm Touch Panel 40 Pin Socket Mcu I8080 8/16BIT Spi 3/4 Draad Pcb Connector Ips Full View|3.5 inch tft|lcd display screeninch tft lcd - AliExpress

I selected the 3.5" no touch ILI9488 version

The library i am using is: TFT_eSPI (version 2.3.67)

tft.fillScreen(TFT_BLACK);
delay(1000);
tft.fillScreen(TFT_GREEN);

Even this shows the color change from top to bottom in a slow rolling way.

I also tested with LVGL but the result is the same but a lot more code (which i didn't make, just tried the online examples)

Well, I'd say line 14 of your posted code is at fault.

Here is a way to update a display that might be of use to you.

void fDoTheDisplayThing( void * parameter )
{
  tft.init( 240, 320 ); // Init ST7789 320x240
  tft.setRotation( 3 );
  tft.setTextSize( 3 );
  tft.fillScreen( ST77XX_BLACK );
  tft.setTextWrap( false );
  struct stu_eData px_eData;
  const int brightness = 250;
  ledcWrite( 4, brightness ); //backlight set
  const int MaxString      = 20;
  String oldTempString     = "";
  String oldHumidityString = "";
  String oldAQIString      = "";
  String oldRainfall       = "";
  String oldWindDirection  = "";
  String oldAirPressure    = "";
  String oldRMO            = "";
  String oldPM2            = "";
  //String oldPower          = "";
  String oldC02 = "";
  oldHumidityString.reserve( MaxString );
  oldWindDirection.reserve( MaxString );
  oldAirPressure.reserve( MaxString );
  oldTempString.reserve( MaxString );
  oldAQIString.reserve( MaxString );
  oldRainfall.reserve( MaxString );
  //oldPower.reserve( MaxString );
  oldRMO.reserve( MaxString );
  oldPM2.reserve( MaxString );
  oldC02.reserve( MaxString );
  bool Tick = true;
  const int numOfColors = 40;
  /* https://chrishewett.com/blog/true-rgb565-colour-picker/#:~:text=A%20true%20RGB565%20colour%20picker%2021st%20Oct%202017,in%205%20bits%20and%20green%20in%206%20bits. */
  int colors[numOfColors] = { ST77XX_BLACK, ST77XX_RED, ST77XX_WHITE, ST77XX_BLUE, ST77XX_GREEN, ST77XX_CYAN, ST77XX_MAGENTA, ST77XX_YELLOW, 0xd55b, 0xee09,
                              0x2e15, 0xcb43, 0x6bad, 0x126f, 0x1264, 0xe264, 0xe7e4, 0x87e4, 0x87fe, 0x876a,
                              0xe304, 0x1cc4, 0xf4c4, 0xf4da, 0xcf66, 0xa879, 0x7f28, 0x4f37, 0xfa97, 0x6195,
                              0X8162, 0xc962, 0x517b, 0x325b, 0xea5b, 0x179b, 0xff80, 0xf960, 0x416d, 0x7bd1
                            };
  int colorCounter = 1;
  for (;;)
  {
    if ( xQueueReceive(xQ_eData, &px_eData, portMAX_DELAY) == pdTRUE )
    {
      tft.setCursor( 0, 0 );
      tft.setTextColor( colors[0] );
      tft.print( oldTempString );
      tft.setCursor( 0, 0 );
      tft.setTextColor( colors[colorCounter] );
      oldTempString = "";
      if ( Tick )
      {
        oldTempString.concat( "iTemp " + String(px_eData.Temperature) + "F" );
      } else {
        oldTempString.concat( "Wind Chill " + String(px_eData.WindChill) + "F" );
      }
      tft.println( oldTempString );
      tft.setCursor( 0, 30 );
      tft.setTextColor( colors[0] );
      tft.print( oldHumidityString );
      tft.setCursor( 0, 30 );
      tft.setTextColor( colors[colorCounter] );
      oldHumidityString = "";
      if ( Tick )
      {
        oldHumidityString.concat( "iHum  " + String(px_eData.Humidity) + "%" );
      } else {
        if ( (px_eData.SunRiseHr < 10) & (px_eData.SunRiseMin < 10) )
        {
          oldHumidityString.concat( "sRise 0" + String(px_eData.SunRiseHr) + "0" + String(px_eData.SunRiseMin) );
        }
        if ( (px_eData.SunRiseHr >= 10) & (px_eData.SunRiseMin < 10) )
        {
          oldHumidityString.concat( "sRise " + String(px_eData.SunRiseHr) + "0" + String(px_eData.SunRiseMin) );
        }
        if ( (px_eData.SunRiseHr < 10) & (px_eData.SunRiseMin >= 10) )
        {
          oldHumidityString.concat( "sRise 0" + String(px_eData.SunRiseHr) + String(px_eData.SunRiseMin) );
        }
        if ( (px_eData.SunRiseHr >= 10) & (px_eData.SunRiseMin >= 10) )
        {
          oldHumidityString.concat( "sRise " + String(px_eData.SunRiseHr) + String(px_eData.SunRiseMin) );
        }
      }
      tft.println( oldHumidityString );
      tft.setCursor( 0, 60 );
      tft.setTextColor( colors[0] );
      tft.print( oldAirPressure );
      tft.setCursor( 0, 60 );
      tft.setTextColor( colors[colorCounter] );
      oldAirPressure = "";
      if ( Tick )
      {
        //oldAirPressure.concat( "Pres " + String(px_eData.Pressure) + "mmHg" );
        oldAirPressure.concat( "Dew Pt. " + String(px_eData.DewPoint) + "F" );
      } else {
        if ( px_eData.SunSetMin < 10 )
        {
          oldAirPressure.concat( "sSet " + String(px_eData.SunSetHr) + "0" + String(px_eData.SunSetMin) );
        }
        if ( px_eData.SunRiseMin >= 10 )
        {
          oldAirPressure.concat( "sSet " + String(px_eData.SunSetHr) + String(px_eData.SunSetMin) );
        }
      }
      tft.println( oldAirPressure );
      tft.setCursor( 0, 90 );
      tft.setTextColor( colors[0] );
      tft.print( oldAQIString );
      tft.setCursor( 0, 90 );
      tft.setTextColor( colors[colorCounter] );
      oldAQIString = "";
      oldAQIString.concat( "iAQI " + String(px_eData.IAQ) + "%" );
      tft.println( oldAQIString );
      tft.setCursor( 0, 120 );
      tft.setTextColor( colors[0] );
      tft.print( oldRMO );
      tft.setCursor( 0, 120 );
      tft.setTextColor( colors[colorCounter] );
      oldRMO = "";
      //if ( Tick )
      //{
        oldRMO.concat( "iRM0 " + String(px_eData.RM0) + "%" );
      //} else {
      //  oldRMO.concat( "iCO2 " + String(CO2) + "ppm" );
      //}
      tft.println( oldRMO );
      tft.setCursor( 0, 150 );
      tft.setTextColor( colors[0] );
      tft.print( oldPM2 );
      tft.setCursor( 0, 150 );
      tft.setTextColor( colors[colorCounter] );
      oldPM2 = "";
      oldPM2.concat( "PM2 " + String(px_eData.PM2) + "ug/m3" );
      tft.println( oldPM2 );
      tft.setCursor( 0, 180 );
      tft.setTextColor( colors[0] );
      //tft.print( oldPower );
      tft.print( oldC02 );
      tft.setCursor( 0, 180 );
      tft.setTextColor( colors[colorCounter] );
      //oldPower = "";
      oldC02 = "";
      //oldPower.concat(  String(px_eData.WSV) + " Volts" );
      oldC02.concat(  "iCO2 " + String(x_eData.CO2) + "ppm" );
      //oldPower.concat(  String(px_eData.WSV) + "V " + String(int(px_eData.WSC * 1000.0f)) + "mA " + String((int(px_eData.WSP * 1000.0f))) + "mW" );
      //tft.println( oldPower );
      tft.println( oldC02 );
      colorCounter++;
      if ( colorCounter > (numOfColors - 1) )
      {
        colorCounter = 1;
      }
      Tick = !Tick;
      //log_i( " high watermark % d",  uxTaskGetStackHighWaterMark( NULL ) );
    } //if ( xQueueReceive(xQ_eData, &px_eData, portMAX_DELAY) == pdTRUE )
  } //for (;;)
  vTaskDelete( NULL );
} //void fDoTheDisplayTHing( void * parameter )

I store the previous value in variable. For an update.

tft.setTextColor( colors[0] );
      tft.print( oldTempString );

I turn text color to black, write the old string to the location, which clears the display info when it becomes the background color, then set the text color to the color in use, then set the new text, saving it into the previous string variable.

Thanks, i do exactly the same when the display shows the time.
When the time ("HH:MM") is not the same as the previous time, i write the previous time in black and than the new time in white.

This works okay (i can still see the slow update a bit but it is not that problematic).
In some cases i want to show an image (icon with a checkmark). To do that i need to remove the text (i can use the same method like you), but to write the image, it shows how slow the response time is. After that i need to clear the image and write the time again and then the same slow response is visible.

Is this normal behaviour for an ESP32 with this type of display or am i just doing something wrong?

Thanks for the link. You appear to have a bare module with a 40-pin ZIF socket adapter.
So you can configure the IM# (interface pins) for 3Wire SPI, 4Wire SPI, 8080-8, 8080-16, ...

Since you don't quote the name of the Setup.h file you are using we have no idea which interface or how you have wired it.

And since you don't quote the name of any library example I assume you don't want an answer.

David.

I have IM0, IM1 and IM2 connected to 3.3v (in any other configuration i couldn't get the display to work).

I am sorry but i think i misunderstood your question.
This is my setup.h file:

#define ILI9488_DRIVER

#define TFT_MISO 19
#define TFT_MOSI 23
#define TFT_SCLK 18
#define TFT_CS   27   // Chip select control pin
#define TFT_DC    2   // Data Command control pin
#define TFT_RST   4   // Reset pin (could connect to RST pin)
//#define TFT_RST  -1 // Set TFT_RST to -1 if display RESET is connected to ESP32 board RST

#define SMOOTH_FONT

#define SPI_FREQUENCY  79000000

And since you don't quote the name of any library example I assume you don't want an answer.
What do you mean with this?

I am using my own code to display text or an image.

This is my TFT code (example):

TFT_eSPI display = TFT_eSPI();

void setup() 
{
  display.init();
  display.setRotation(1);
  display.fillScreen(TFT_BLACK);
  display.setSwapBytes(true);
}

void loop() 
{
  tft.fillScreen(TFT_BLACK);
  delay(5000);
  tft.fillScreen(TFT_GREEN);
  delay(5000);
}

From the datasheet
image
So you are using 4-wire SPI

Bodmer provides example setups e.g.
C:\Users\ ... \Documents\Arduino\libraries\TFT_eSPI\User_Setups\Setup21_ILI9488.h

Bodmer provides example sketches e.g.
C:\Users\ ... \Documents\Arduino\libraries\TFT_eSPI\examples\480 x 320\TFT_graphicstest_one_lib\TFT_graphicstest_one_lib.ino

It is always wise to run library examples before you try any custom code.

Your custom setup and custom sketch might be perfectly good.
But it is safer to start with stuff from the library author.

David.

Thanks, 4-wire SPI can be fast or did i choose the wrong connection?

I have uploaded the demo and everything works. But i also see the display being rendered (way faster than for example on an arduino) but still "slower" than all of the display at once

This is the example result:

13:00:53.282 -> TFT_eSPI library test!
13:00:53.699 -> Benchmark Time (microseconds)
13:00:53.746 -> Screen fill 502024
13:00:54.208 -> Text 21745
13:00:54.347 -> Lines 244502
13:00:55.698 -> Horiz/Vert Lines 42475
13:00:55.835 -> Rectangles (outline) 23642
13:00:55.973 -> Rectangles (filled) 1212973
13:00:57.316 -> Circles (filled) 143923
13:00:57.549 -> Circles (outline) 163136
13:00:57.736 -> Triangles (outline) 48407
13:00:57.873 -> Triangles (filled) 410079
13:00:58.428 -> Rounded rects (outline) 74546
13:00:58.611 -> Rounded rects (filled) 1228466
13:00:59.960 -> Done!

Is it really too difficult to quote accurate information ?

TFT_eSPI library test!   ST7796S @ 27MHz
Benchmark                Time (microseconds)
Screen fill              466081
...

TFT_eSPI library test!   ILI9488 @ 27MHz
Benchmark                Time (microseconds)
Screen fill              731526
...

TFT_eSPI library test!   ILI9488 @ 40MHz
Benchmark                Time (microseconds)
Screen fill              502037
...

So I conclude that you are probably using 40MHz with a 240MHz ESP32.
You won't get much better than 502ms for drawing 5 fillScreen()s on an SPI ILI9488. 80MHz causes glitches. 40MHz seems to draw ok. Obviously the recommended 27MHz is "safer".

Note that the ILI9488 always requires 3 SPI bytes per pixel. The ST7796S (and ILI9341) can use 2 SPI bytes per pixel.

I strongly advise you to run the example programs. If you are unhappy with performance or have problems quote the example by name. e.g. TFT_graphicstest_one_lib.ino
And copy-paste any "edits" if you have altered the original files.

Obviously explain "your problem"

David.

What information didn't i quote?
I posted the output of your suggested file. I think i explained "my problem" clear enough but apperently this behaviour is normal for that display since fillscreen should take 502ms

It is not difficult to type / copy-paste information. e.g. actual filename, actual setup file, actual SCK frequency

However you can do some maths. 5 screens of 320x480 pixels @ 3 SPI bytes (40MHz)
5 * 320 * 480 * 3 * 0.2us = 460800 us

Ok. 502ms is slower than 461ms. But 461ms is the absolute theoretical best time.

If you really want to fill a screen in less than 92160 us you will need a different TFT. Or run it in 8080-8 parallel mode. The ESP32 has enough GPIO pins and Bodmer supports 8080-8 interface.

What performance do you want to achieve ?

David.

The ESP32 maybe has enough gpio pins but i have used all of them. I optimized my code to only redraw parts of the display in black before showing different things. This works okay.
It is not the best performance but not too bad either.

My prefered performance achievement would be that you cannot see the display being "written" and every image or text update would be shown directly on the display. But maybe that is impossible.

I also can't change anything by hardware because the PCB's have already been ordered

Please put some numbers into your requirements e.g. fillScreen() < 50us

For any Graphics application you only want to redraw small areas of the screen. There are many techniques for making text update smoothly.
Bodmer shows you in his examples.

ILI9341 and ST7796S are much nicer to use with SPI. i.e. 2-bytes vs 3-bytes per pixel. But overall performance of ILI9488 is quite acceptable.

Regarding pcb hardware. You can guestimate whether you need a different TFT or interface at the design stage.

You can buy ready-made SPI and ready-made Parallel screens for a prototype. You don't want to order pcbs before you know how the prototype performs.

David.

It is difficult to put some numbers on it, the faster the better.
I tried to find a ILI9341 3.5" bare display but i can't seem to find it.

Does this also need different wiring?

Sit down with a nice cup of tea.

ILI9341 is 240x320 and comes with 2.2 inch - 3.2 inch panels.
ILI9488 is 320x480 and comes with 3.5 inch - 4.0 inch panels.

I strongly recommend running ALL of Bodmer's TFT_eSPI examples.
If you have a question, quote the specific example by name.

Since your ILI9488 is bigger than many of the other supported controllers it means that examples will always "fit" on your screen.

David.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.