Marc's Public Blog - Arduino Hacking

All | Aquariums | Arduino | Btrfs | Cars | Cats | Clubbing | Computers | Dining | Diving | Electronics | Exercising | Festivals | Flying | Halloween | Hbot | Hiking | Linux | Linuxha | Mexico | Monuments | Museums | Outings | Public | Rc | Sciencemuseums | Solar | Tfsf | Trips

>>> Back to post index <<<

2022/01/02 FastLED_ArduinoGFX::TFT on top of Framebuffer::GFX for SPI TFTs like SSD1331 or ILI9341
π 2022-01-02 01:01 in Arduino

Give me the code

  • Introduction

    I had already written FastLED_SPITFT::GFX to display Framebuffer::GFX code on TFTs, but that was using the Adafruit drivers that were of varying quality (one didn't support HWSPI on ESP32, so it was very slow).

    Later, I found out about Arduino_GFX from Leung CHAN, which is a unified driver for a lot of TFTs of much better quality than the adafruit drivers. There is support for: GC9A01, GC9106, HX8347C, HX8347D, HX8352C, HX8357A, HX8357B, ILI9225, ILI9341, ILI9341, ILI9342, ILI9481, ILI9486, ILI9488, ILI9488, ILI9806, JBT6K71, NT35310, NT35510, NT39125, R61529, SEPS525, SSD1283A, SSD1331, SSD1351, SSD1351, ST7735, ST7735, ST7735, ST7789, ST7789, ST7789, ST7789, ST7789, ST7796
    all with a single driver, a single interface, and better speed than Adafruit drivers. Good job Leung, thanks.

    Now, why would you use my FastLED_ArduinoGFX::TFT layer, especially when Arduino::GFX has some support for Canvas (equivalent to FrameBuffer?)

  • First, using a Framebuffer (or canvas) allows for running clear and doing a flush all at once when drawing the next frame is finished
  • Framebuffer allows you to read back pixels from the framebuffer to shift them or mirror them, or even to dim them (FastLED allows diming the entire framebuffer to give effects that fade old pixels with time)
  • FastLED and SmartMatrix (or rpi-rgb-panel) all have code that is based on a framebuffer with RGB88 pixels (24bits) which is why Framebuffer::GFX comes in and why I wrote it. All this code works against Framebuffer::GFX and can be displayed on any supported backend.
  • Support for FastLED and LEDMatrix 2D APIs (which in turn require FastLED CRGB (RGB88) pixel storage, again supported by Framebuffer::GFX but not the Adafruit or Arduino_GFX TFT drivers.
  • See the Framebuffer::GFX that explains support

    So, this is why FastLED_ArduinoGFX::TFT is here. If you want more APIs than just Adafruit::GFX, and you want your code to also work on all other supported backends (FastLED Matrix, SmartMatrix, RGBPanel on rPi, or even running directly on linux to write/debug your arduino code on linux with gdb or ASAN memory sanitizer)

    This same LEDMatrix demo now works a lot faster thanks to Arduino_GFX copying data to the TFT faster:

    This is 24bpp FastLED/LEDMatrix code running on a 16bpp TFT via Framebuffer::GFX
    This is 24bpp FastLED/LEDMatrix code running on a 16bpp TFT via Framebuffer::GFX

    Basic code example

    This basic example is the simplest and skips neomatrix_config.h by defining things "in line", in the code. It's easier to understand, but defeats the main advantage of neomatrix_config.h, which is to have all your definitions outsdie of your code, and allows updating your hardware info in a single file while having all your demo code still work on new hardware by just modifying one common file.
    Have a look at this simple file:

    Running Framebuffer::GFX code on a TFT, FastLED_NeoMatrix_SmartMatrix_LEDMatrix_GFX_Demos and neomatrix_config.h

    This is the recommended way to do things, have a look at any code in . It all uses which is a bit of a handful, but if you read it carefully, the ifdefs take care of things.
    The short version is all you need to do, is
    #define ILI9341
    #include "neomatrix_config.h"
    and the rest should work on its own (although you do have to define the TFT pins in the file)

    If you think the file is way too big and hard to understand, you can look at the shorter TFT only version:

    Running Framebuffer::GFX code portions of a TFT

    If you have bigger displays like an ILI9341, that's 320*240*24bpp or 230KB for a full 24bpp framebuffer. That fits on a teensy 3.6 or better, but not on an ESP32 where the memory is not contiguous (unless you use PSRAM which neomatrix_config will automatically use for you).
    This means that without enough memory, you can define a smaller framebuffer that only covers portion of the TFT, and then render the framebuffer at the desired offset. Check out this example to see how it works:

    This is the end result, you can see that the ILI9341 is spilt in two, the top half is mapped to a framebuffer, the 2nd part uses direct adafruit::GFX rendering through Arduino_GFX:

    Multiple TFTs and multiple framebuffers

    But the more exciting thing is that Arduino::GFX supports multiple TFTs are the same time, so I modified neomatrix_config to support multiple TFTs with different framebuffers.
    You can look at this dual display example:
    It supports arrays to store values for each TFT:
    Arduino_DataBus *bus_[TFTCNT];
    Arduino_TFT *tft_[TFTCNT];
    CRGB *matrixleds_[TFTCNT];
    FastLED_ArduinoGFX_TFT *matrix_[TFTCNT];
    uint16_t mw_[TFTCNT];
    uint16_t mh_[TFTCNT];
    uint16_t tftw_[TFTCNT];
    uint16_t tfth_[TFTCNT];
    uint8_t gfx_scale_[TFTCNT];
    const char *tftname_[TFTCNT];

    As a hint:

  • tft_[0] gives access to the raw TFT #1 via Arduino::GFX, the idea is not to use it, but you can
  • matrixleds_[0] gives access to the first FastLED pixel array (used by some fastled code that uses XY())
  • matrix_[0] is the adafruit::GFX compatible object
  • ledmatrix1 (defined in tft_init, ), is the LEDMatrix object for the LEDMatrix API, as well as LEDText and LEDSprite if you want to use them (because of templates, it cannot be in an array)
  • tftw/tftw are size of the physical TFT, mw/mh are size of the framebuffer, framebuffer can be smaller than the TFT if there isn't enough RAM to have a framebuffer as big as the TFT, like ILI9341 on ESP32 without PSRAM
  • gfx_scale is used to keep track of a framebuffer smaller than the TFT (for instance on ESP32 without PSRAM, you can have a half height framebuffer that is then displayed twice, or can be reset with new data and applied to the top of the screen and later the bottom of the screen)
  • tft_name is simply used to keep track of what TFT name that index is, used for debugging
  • End result

    I was able to write this demo, which you can find at:

  • 2x SSD1306 over I2C (driven by adafruit driver)
  • 1x SSD1331 over SPI via CS1 (driven by Arduino_GFX + FastLED_ArduinoGFX_TFT)
  • 1x ILI9341 over SPI via CS2 (driven by Arduino_GFX + FastLED_ArduinoGFX_TFT for top half and direct driven for bottom half)
  • Video result:

    Have Fun!

    Enjoy, do your own!

    More pages: August 2023 June 2023 May 2023 March 2022 January 2022 December 2020 March 2020 January 2020 May 2019 April 2019 March 2019 January 2019 July 2018 May 2018 April 2018 January 2018 June 2017 April 2017 January 2017 February 2016 January 2015 September 2013 January 2012 December 2011 May 2011 January 2011

    >>> Back to post index <<<

    Contact Email