Speed of TFT screens, refreshing whole screen, flickering on SPI

I have an ESP32 running in Arduino IDE. I own a small 128x128 TFT ILI9163 display (Exactly like this one) connected through SPI.

I was wondering about the speed at which one can reasonably expect to refresh the whole screen.

I wrote a small demo that fills the screen memory with a solid color, waits 100ms, and redraws with another - in a loop. Here is the result: Tft display flickering - YouTube

As you can see the screen updates, but is redrawn not instantly which causes like a horizontal line that goes from bottom to top. What causes that?

In that example, I tried various libraries, etc. and they all produced the same result. I rewrote the driver myself, just to basic minimum, but it produces the result in the video. When I measure millis() before and after the method for sending the data via SPI, it says 10ms, which would mean it's as good as 100FPS, so why can I see that updating screen from top to bottom?

Does it have to do with the speed of SPI itself? I mean its 16bpp 128x128 area, so about 32kb. How fast can SPI send data to the device's memory? I tried playing with different SPI frequencies, but it always creates that effect, just slows down its duration.

I also tried my Arduino Mega board, and it's exactly the same. I mean the drawing routine is slower, obviously, but the effect of drawing the screen top to bottom is still there.

I know there exist different strategies for optimizing the display procedure (eg. double buffering, for which ESP32 has enough RAM, redrawing only changed pixels, etc.), but I am wondering about the feasibility of having somewhat acceptable FPS during a video playback or some basic game emulation.
I've seen NES and DOOM clones runing somewhat smoothly, how can they achieve that using SPI screens?

You can redraw most TFTs fairly quickly e.g. 50ns per pixel.
For a 128x128 screen this is 820us with 8080-16 interface.

With 16MHz SPI this would be 16.4ms. I suspect the controller will accept 40MHz SPI. i.e. 3.3ms

So you can redraw the whole screen memory faster than the controller would scan all the raster lines.

In practice, you just need to use a little common sense when designing any game or graphics.
This means redrawing small areas of a screen.

David.

To be honest I don't know what I'm fighting against, is that flicker caused by the SPI speed? Certainly not by CPU, as the drawing routine takes 10ms.

david_prentice:
So you can redraw the whole screen memory faster than the controller would scan all the raster lines.

Is that what causes the flicker?

I mean, as it is now, at around 20fps I get flicker when I start to update around 10% (1600) of the pixels on the screen. 10% is not a lot - with double buffering, if a background sprite (or a block of text) is somewhat detailed and not uniform, I cannot scroll it any way because it flickers.

@fretdancer

You are observing "screen tearing" or "tearing effect", read about it here.

The way around it is to get a display with the V-sync pin available so each new image is drawn in synchronism with the display refresh. The ILI9163 datasheet will explain.

bodmer:
The way around it is to get a display with the V-sync pin available so each new image is drawn in synchronism with the display refresh. The ILI9163 datasheet will explain.

That's it! I found that these screens indeed have a tearing effect output pin, which tells me exactly what I need to know. The problem is the pin is not exposed. What could I do about it? I mean I could just disassemble the little box on which the screen sits and access that pin?

But none of the screens that are available have the vsync (or tearing output) pin exposed... Even those that people used in NES or DOOM ports. So how can they achieve that?

Is there a way to read that pin value through SPI? Or maybe some other way?

Some controllers have a setting for tearing effect handling. Look at the datasheet of yours.

ZinggJM:
Some controllers have a setting for tearing effect handling. Look at the datasheet of yours.

It just states (http://www.displayfuture.com/Display/datasheet/controller/ST7735.pdf) that when, in a correct mode, Tearing Effect Output Line is HIGH: The LCD display is not updated from the Frame Memory. Which is all good, but how do I access that output line? Literally no TFT screen that I found has a physical output exposed for that.

From what I read in various datasheets, most of controllers have that synchronization mechanism, but no module I found exposes it. Am I missing something?

This happens when I try to give a hint: it may not be helpful at all. Sorry.

I encountered the setting for tearing effect on a ILI9806 datasheet, but have not paid attention to it.
I have seen that effect with my 5inch "Tiky" TFT, but did not bother to investigate further.
I even had to add a delay between full-screen color fill, with some processor boards.

Maybe I come back to this issue later.

Jean-Marc

Additional hint:

There is a command "Get the current scanline" 45h that may or may not be helpful.

ZinggJM:
There is a command "Get the current scanline" 45h that may or may not be helpful.

Don't you need a MISO pin exposed to read anything from a SPI device? Again, those cheap screen modules never have those :confused:

You have an ILI9163 which has a bidirectional SDA line.

david_prentice:
You have an ILI9163 which has a bidirectional SDA line.

I'm a little bit confused by this. Could you help?
How would you READ from that device if it's not a I2C controller - so it's SPI, right? And as an SPI device if it doesn't have a MISO pin, then reading anything is simply impossible?

Yes, it is confusing. I2C use SDA, SCL.
Bidirectional 8-bit SPI uses SDA, SCK, CS, DC. Bidirectional 9-bit SPI uses SDA, SCK, CS.

The AVR hardware is not capable of bidirectional SPI. But other makes of chip can handle it.
I write commands, data with hardware SPI. To read data, I disable hardware SPI, bit-bang the read.

It works but is a little messy. I have never read reg(0x45) on an ILI9163. I might try it later. After I have checked the datasheet.
Quite honestly, these small displays work very well. I have never worried about tearing. I just draw some graphics (very fast) and then leave a steady screen for the punter to view.

David.

You mean they work very well for static menus etc.?

I'm trying to write a simple space invaders game to test it.

I wrote my own driver for the display, and can send a whole frame to memory in less than 8ms. And when the frame changes so that say 10 8x8 sprites simply move a couple of pixels, you can see how slow the screen redraws them top to bottom. It flickers.

Is there any way of eliminating that tearing effect having in mind that I can't read the scan line, can't access the anti-tearing pin? Or should I just give up on these screens and conclude these aren't meant for simple animations or frequent redraws of portions of the screen?

You can either redraw the whole screen. Or just redraw areas of the screen as they change.

Post your current efforts. Are you producing a full-colour display or monochrome Space Invaders?

I am sure that you will get helpful advice.

David.

Or use a 5inch display with a capable controller and a powerful enough Arduino.

Ok, thanks guys, I fixed it.

It had nothing to do with the display, but with my inexperience with writing video routines - just like you said.

If anyone comes across this topic with a similar problem, here is what I did to optimize it, and get real-time graphics. I know it's basic stuff, but might be helpful to someone.

  1. Set the display to 12BPP, since I didn't need more colors. This allowed me to write more information to the screens RAM at once, speeding the whole process.

  2. The available screen driver libraries weren't that optimized. So I rewrote the driver, to be speedier: for example, setting the row and column address command takes two 8-bit parameters (x0,x1), but since I am only pushing pixels, I omitted the x1 parameter, saving me a SPI.write.

  3. Created two buffers for current and previous frames.
    These buffers can be set to various bits per pixel values, so you could have the whole system set to 256, 16, 4 or 2 colors. Lower setting meant you would use less RAM and would have to use some bit shifting to pack the pixel data.

  4. Created a color look-up table, which enabled me to add transparency to sprites and switchable color pallets.

  5. My drawing routine now compares the two buffers, and pushes the pixel only if its color lookup data is different than in the previous frame buffer. At the end it copies the current frame to the previous one.

  6. Most importantly, realized that colorcount of sprites is crucial! Not only because it eats up memory, but because the routine compares lookup table values. So the more colors you use in a sprite, the more likely it is that its movement would trigger redrawing of other colors.

On a ESP32, this routine gets a nice 40-50 FPS in 16 color mode: with a scrolling background and 200 16x16 sprites moving around the screen very fast.

As for the tearing effect, it is present, but is a small annoyance and becomes visible only when you pay attention to it. It happens when a lot of similar colors are redrawn horizontally.

If anyone has any more tips on how to speed this process, let me know :slight_smile:

It would be interesting to see your test program.

I can redraw whole (monochrome) frames and animate quite nicely with a Uno.

Coloured frames take considerably more Flash memory. i.e. I would need to use a Zero or STM32.
In practice, a 4-colour Palette or 16-colour Palette is sufficient for cartoon animations.

Yes, an ESP266 or ESP32 would have more Flash, more SRAM and more processing speed.

My memory of Space Invaders was on a green monochrome screen.

David.

david_prentice:
It would be interesting to see your test program.

There it is, if you’re interested: https://paste.ofcode.org/HdBQdTPWEs8Pd3Gvw6rNH6

With exactly this demo I get around 60FPS on an ESP32
On a device with less RAM you’d have to probably play with BPP setting and change the way previous frame is handled.

If you have any ideas on how to speed this up, let me know:
The drawing procedure for BPP<8 are not that fast, because of bit shifting. Any thoughts?

could you fix this, I am using arduino due and st7735 tft screen, and I have a test to print an sprite in screen, im cleaning the whole screen and I repaint again, but when I use fillScreen( color ), the screen is flickering, and if I don't use that, the drawn pixels stay in screen.