ILI9341 very strange issue

Hi there,
thanks for reading!

I'm using a ILI9341 2.8" LCD screen via SPI with a ATSAMD21. Using DMA with a 24Mhz SPI. I'm being able to fill screen buffer in about 60mS. So, around 18fps.. It's all good. But I'm seeing the display refresh!!! it takes like 300mS to refresh the entire display.

I'm sending a white screen, then waiting for a second, then sending a black screen, and looping this.

I attach a video...

ANY HELP OR IDEA about what's happening is more than welcomed. I don't understand. I've been researching without any luck.

You would only be able to achieve 10fps on an ESP32 because decoding Jpegs requires a fair bit of processor power. For example it takes an ESP32 ~100ms (90ms 8 bit parallel, 110ms SPI at 40MHz) to fetch the jpeg from FLASH, decode it and write it to the display. At this speed you will see each image being "wiped" onto the display and this may not be acceptable. This was posted at: esp32 and "naked" ili9341 in fastest mode posible? - Displays - Arduino Forum. You will probably need a second sisplaymemory so you can simply switch after loading it, that is the way we use to do it.

  1. Please quote the Arduino board you are using. Just quote name for official Arduino. Post link for custom board.

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

  3. Please post a link to any special libraries you have used.
    (If they are installed by the IDE Library Manager, just quote the library name)

Most libraries come with examples, run the example(s).
quote example name. report any problem.

Library examples try to show the controller behaviour. e.g. graphics, colours, directions, ...

If all examples work, copy-paste your home-grown sketch. (the whole code so that we can build it).
Very few libraries use DMA. You need a better "test" than tft.fillScreen(BLACK) and tft.fillScreen(WHITE)

A 240x320 screen with SPI @ 24MHz should take 51.2ms for fillScreen().
We need to see your sketch and your library to see why your video shows visible drawing.

I don't know why gilshultz mentioned JPEG. The SAMD21 can decode JPEGs reasonably fast but it will still be 100s of ms. The "drawing" of a 240x320 JPEG should only take about 60ms.


Without understanding the hardware and software setup you have does make a definitive answer difficult.

However, the processor you have probably does not have much RAM. So screen updateds are likely to be done a few lines at a time which can be inefficient. Although the scope trace source is not explained it looks like there are large gaps with short fast bursts of data? This is consistent with a small DMA buffer which induces significant software delays in the transfer loop. In principle it should be possible to recycle the DMA buffer for a screen clear or area fill but I have no idea whether this option is available to you.

Another apporach commonly used is a "ping pong" double buffer, where one buffer is DMA'ed out by hardware and the other buffer is filled by software. When DMA is complete on one buffer the buffer pointers can be swapped and the process repeated. This means the software is doing something useful while DMA is in progress.

You may also find that the DMA code as written is actually is wasting time, perhaps due to the small buffer size, and so you would be better off just sending data direct to the SPI port without DMA.

We don't know what ILI9341 screen or what SAMD21 board. But we can guess.

However we have no idea what software you might be using.
Please provide the information requested in #2.

DMA enables you to process the next data while the previous data is being sent.
e.g. decode the next JPEG tile.

Arduino libraries often block. e.g. Wire.h
Which defeats the object of interrupts, DMA, ... etc.

Arduino SPI libraries are almost always crap. e.g. long gaps between each SPI byte.


Guys, thank you for your comments.
First, I must make a correction. Display is a 2.4" (not a 2.8"). It's the generic one. IDK, there are so many out there... this is where I bought it:

and the board is this:

I'm using Adafruit ILI9341 library as base, modified to work with ATSAMD21 and DMA.

I have a 4KiB dedicated RAM buffer to send it over DMA. Timming is working quite well.
I don't see how can I be messing it up, being that the oscilloscope shows I'm sending image data in about 60mS. Then, I'm doing nothing for almost a second long. So, sending a black image, and then a white one, should probe that I'm really sending an entire 320x240x2 bytes per frame (if I didn't, black and white should start to mix more and more in each frame).
So, I do know I'm sending an entire frame in about that time (60ms), so I should not see the refresh like in the video I posted before. It should be almost instantaneous to the human eye.
I really can't think of any other reason than the internal refresh of the LCD screen (image RAM buffer to screen) being completly LOW, around 3Hz (instead of 71Hz or more, like LCD datasheet shows).

Whether you use polled SPI or DMA it only makes a difference if you can use the DMA time for computing the next set of pixels.

You just calculate the SPI traffic time. e.g. for a full screen: 153k SPI bytes @ 8MHz is 153ms.
51ms @ 24MHz. 31ms @ 40MHz.

I have never seen a TFT library for SAMD21 that uses DMA.

Of course SAM3X, STM32, Teensy, ESP32, ... often use DMA.

Decoding JPEG or GIF image probably takes significantly longer to calculate than it does to blit the pixels.


p.s. Arduino SPI libraries tend to be complete crap. The most extreme crap being Nano 33BLE.

Hi David,

well, I have created my own RLE compression algorythm and I'm decompressing it while sending img data through DMA. As I said, I started with the Adafruit ILI9341 repository , and modified it to send data over DMA. I'm being able to send data really fast. That's not the issue. Issue is that the screen refresh is lame.

I thought maybe somebody had the same issue and maybe could help me understand/solve it.
Maybe my screen are complete crap. I don't know what else could be happening if not.

No, I had just never seen any Public TFT libraries for SAMD21 DMA.

Whether you poll or use DMA you are still stuck with 24MHz SPI on a 48MHz M0 ARM chip.
i.e. 51ms for your 240x320 pixels @ 24MHz, 102ms @ 12MHz, 154ms @ 8MHz.

In practice, animations often just "manipulate" small areas of the screen. So you can achieve better FPS values.

You can compose the next tile buffer while DMA is blitting the current tile buffer.
Look at Bodmer's TFT_eSPI examples. Especially JPEG.

Marek wrote his ILI9341_due library many years ago.

In an ideal world, Arduino SPI library would have a SPI_transfer(txbuffer, rxbuffer, n) function. If rxbuffer is NULL it would ignore the read values.
In this ideal world certain cores would use the hardware to its best capability.


Hello there,

I started with a SPI DMA Example for this board, and then modified Adafruit's library to fill rectangles using SPI DMA.
And yes, I'm sending a buffer through DMA while decompressing the next one, but using a custom RLE algorithm.
SPI transfer rate is down a little by a few tech reasons, so @ 24 Mhz I'm getting around 60ms for a complete 240x320 pixels (CPU @ 48Mhz) rectangle fill.

My reason to suspect about screen's internal refresh rate is that I'm seeing an entire screen frame being sent in about 60mS in my oscilloscope, but frame data drew slowly. You can pretty see with your eyes the filling screen process.
So, if data is sent to screen in about 60mS, what other possible responsability might I have from my microcontroller side? It's already sent. I cannot even hold a lot of display's data with this few RAM and ROM, so data is really already in screen's memory... but slowly shown in it.

As always, thanks for reading.

Post a link to your Github page or wherever.

I don't know what your code is. Or what is sent on the SPI bus. I would set the SPI bus to 12MHz and capture with a Saleae Logic Analyser. Much easier than a scope.

60ms sounds fine for fillScreen(colour)
you should get similar time for pushColors(buf, n) even if you are sending smallish rectangles at a time.

The two DMA operations are different. fillScreen() is sending a single colour multiple times. pushColors() is sending a N blocks of different pixels.


This topic was automatically closed after 84 days. New replies are no longer allowed.