TFT full frame buffer possible? SPI DMA?


I would like to make an analog gauge using a round TFT display. I’ve got something that works, but since I am drawing so many lines at weird angles I’m not efficiently using SPI transfers to get data on the screen. Even using selective blanking to prevent full screen writes will leave the display flickering, I’ve still got to make 2 commands per pixel, before even sending the pixel.

To fix this I would like to try and implement a full single frame buffer for a 240x240 SPI TFT display. I would like to use a ATSAME51J20 with 256KB of SRAM, so I think I have the overhead to do so if my math is right. (240x240 pixels, 16bpp, 115200 bytes of RAM!)

I think the full display update should be able to take place in under ~40ms over SPI at 24MHz, so if the changes I’d like to make in the display are fast enough I think a reasonable refresh rate is possible, maybe 20Hz? I know 24MHz SPI is iffy, but it seems to be working alright for me at the moment.

I think I should be able to do this with SPI DMA, but I am unsure how to go about setting it up, mainly linking DMA transfers. Before I go and try and make something work I figured I would ask here.

Basically, I’ll init the display without DMA and config the display to automatically wrap at the extents of the displayable screen (LCD controller is 320x240 capable.) From there I will use callbacks to manipulate the pin IO, and a “writing” flag so I am not changing RAM while it’s being shifted into the screen. I don’t think I can double buffer, much as I’d like to!

TL:DR, can DMA be used to shift 115,200 bytes of ram?

Thank you!!

Yes, you could re-draw the whole screen either polled or with DMA.
But it would be far more sensible to just update a minimal rectangle. Which is far more efficient.

Note that the Arduino SPI library is complete crap. Likewise most other “Arduino ARM SPI” classes.

If you use the raw SAM peripherals SPI will work without gaps.

You don’t say which controller. ILI9341 will work reliably at 40MHz. And Bodmer seems to have it working at 62.5MHz.


I figure because I’m drawing angled lines that cover most the screen I’d end up with about the full screen in the update window anyway.

The screen itself is a GC9A01, I think it wraps back to the start of the “frame” once you hit the last column in the last page, so once I got all that set I’d only ever need to send image data, not address stuff. While I could write smaller areas at once, I’d still be writing pretty large windows. If they get anywhere near 240x240 I’d need the free RAM anyway, so may as well buffer the full frame all the time, get a more consistent framerate as a bonus.

I’ve also been working in Atmel studio for the debugging. I probably won’t bother porting whatever I come up with back to Arduino, but I can’t stand Atmel’s drivers, frameworks, whatever it is. I prefer just writing registers direct, more the way arduino does it.

I also want to stick to around 24MHz because I think it’s a happy medium, I think according to the datahsheet 18MHz is the max supported on the SAMD/E51 hardware, else the clock starts desyncing from the data bits. I’ve got a full buffer mono SSD1326 working now, and it gets glitchy much faster than 24MHz. I need a faster logic analyzer, all I’ve got that gets close is a 1054Z and it’s very hard to see…

I guess I’ll try and come up with some code that works, I think I’ll hit the limit of consecutive DMA transfers pretty quick, so I’ll have to stack them. Wonder if I can send 32bit instead of 8, just to cut the transfers down by 4…

Thank you!!

Only you know what your data is like. There is always a balance between individual pixel addressing versus re-drawing a full rectangle.
For example. Drawing a letter in a “thin” font uses less SPI bytes with individual pixels than painting the full rectangle.

Hey-ho. You can calculate the minimum time very easily. e.g. 240x240 16-bit pixels = 38.4ms @ 24MHz. (26 fps)

In practice this gives you the performance limits. You can do all of the calculations for the “next” frame while DMA is blitting the current frame.

This uses tremendous resources. e.g. two full screen buffers. Or perhaps two half-screen buffers. Look at Bodmer’s recent example video in the Displays Forum.

No, I have never used SAME51 or GC9A01. I would need to check the datasheets. And test the actual GCA01 throughput. (the ILI9341 accepts much faster SPI than the datasheet says)
I have only used SAMD21 (which is a bit crap)

The 256x32 SSD1326 is a totally different controller designed for grayscale OLEDs.
TFT controllers tend to be much bigger and faster. After all they have more pixels and 262k colours versus 16 grayscales.

Oh, it seems a mystery why anyone would buy a grayscale display and then use in monochrome !


I think I can see how writing thin “fonts” might be faster, I guess you rotate the frame and then can draw all sequential pixels? I’ve not looked into the fastHline or fastVline code at all yet.

I can’t swing a double buffer, but since I’d be happy with 20fps as long as I can update the RAM in ~12ms I should be able to keep a solid framerate I think, I just can’t update the RAM while DMA is transferring data.

I’ll check out that library and see if I can see how the DMA is done. The 2040 is a M0 core, right? I bet quite a bit is similar.

Still think the SPI max speed is going to be limited by the processor, not the TFT. If I recall the minimum sck period on the SAMD51 is ~42ns, I’ve tried pushing to 50MHz and while the SSD1327 can take it eventually the display is corrupted randomly. I guess I should add that this is all on custom PCBs, short SPI runs, no termination yet. I think this GC9A01A might be able to do 100MHz? 10ns serial clock cycle on writes, much slower reads.

Wish I knew of some CPU that could clock SPI faster and still had canbus, far as I know STM32F4 is close, has much worse canbus (IMO), less ram and CPU frequency and only goes up to 30MHz SPI without “overclocking” and for all that it costs about double at small quantities, less package options. Meh.

Mainly switching from the “mono” greyscale oled to the IPS TFT for brightness. I went through and configured all the pixel duty cycles to get a proper looking greyscale, but in a bright sunlit environment it’s all pretty much wasted. I can see the actual output better with higher contrast. I think the OLED was ~200cd/m2, the new IPS TFT ~400cd/m2 or so with similar contrast. If it pixel fades less than the old display I should be happy!

tCYCLE for SSD1326 = 250ns. i.e. 4MHz SPI
tCYCLE for SSD1327 = 160ns. i.e. 6.25MHz SPI
tWC for GC9A01 = 10ns. i.e. 100MHz SPI

Many ARM chips can manage 100MHz SPI.
Many ARM chips can control CANBUS.

I have no experience of CANBUS. So I don’t know whether ST, Atmel, Renesas, Expressif, … are good or bad.

Seriously, 240x240 is a smallish display. You should be able to render crisp graphics with a low-end MCU. If it is a simple rotating needle pointer gauge I suspect you could even do it with a 16MHz AVR.

I glanced at the SAMD5x datasheet. You deserve a medal if you can make any sense out of it. It makes my head hurt.
In comparison, ST datasheets are clearer, better written, describe practical operation, …

I only have a SSD1327 with I2C interface. SCL = 400kHz
If you only want to show one analog gauge with 128x128 pixels I suspect that the SSD1327 can do it fine. With Anti-Aliasing it could even look very smooth.

The visual characteristics of the OLED, TFT, … panel is probably most important. e.g. sunlight
Rendering attractive graphics on the panel should be simple.

Discussion of display graphics is probably better in the Displays Forum.
Discussion of SAMD chips, SERCOM, DMA, … is better here on the Zero Forum.


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