- Tue Jul 14, 2020 3:52 pm
#217104
The standard Adafruit_GFX_Library graphics code will compile and run for an Artemis board as-is, but the resulting performance is so slow as to be practically unusable (See this post: viewtopic.php?f=169&t=51831). What follows are instructions to create a high-performance version of the GFX library optimized for an Apollo3 processor.
Starting with a bit of background, the GFX library was originally developed with the following assumptions:
The process to create an optimized Apollo3 GFX library will assume that you have already installed the Adafruit_GFX_Library version 1.9.0, which is the latest version of the released library at the time of this writing.
It is not a good idea to make source code changes directly to an installed Arduino GFX library. Those changes would get overwritten every time the Arduino system updated the GFX library. The workaround is to create an Apollo3-specific version of the GFX library. This new version of the GFX library will live beside the standard GFX library and will be used for Apollo3 builds only. For example, if you use the same Arduino installation to create AVR or SAMD builds for a different project that also uses the GFX library, those builds will continue to use the standard GFX library. The downside of this approach is that the new Apollo3-specific library will be stuck at the version where it was created. However, the apollo3 changes are almost certainly easily merged back into updated source files.
To make the Apollo3-specific GFX library, perform the following steps:
Upload the result and your GFX operations should run nice and fast now. If not, check where you put the library, check that it got named properly, check that you made the right change to the library.properties file, and make sure that your Adafruit_SPITFT.cpp file has some references to 'apollo3' inside it.
Results: My measurements show that with the original library, erasing a full 240x320 display would take 2.34 seconds. Copying a full 240x320 big-endian bitmap (153,600 bytes of data) was slightly worse at 2.38 seconds. With the apollo3 optimizations included, both an erase or a full-screen bitmap copy take under 57 milliseconds, or about 42 times faster. Side note: I like that the Artemis has no trouble swallowing a 153K test bitmap into its flash.
Testing: These replacement SPITFT files are only guaranteed to work with GFX library version 1.9.0. The resulting library has been tested with with 1.44" 128x128 color displays using an ST7735 controller, and 2.8" 320x240 color displays using an ILI9341 controller. However, any display supported by the standard GFX library should work fine.
Further Reading: If you are interested looking at the changes, edit the new Adafruit_SPITFT.cpp and search for all instances of ‘apollo3’. I also made a change to drawRGBBitmap(), which is the routine that copies memory-resident data from flash or RAM directly to the display. The original version always assumes that the data being copied is little-endian. The new version allows you to choose. If you create your memory-resident bitmap data in big-endian format, the new version of drawRGBBitmap() will allow you to skip the endian conversion process that would otherwise occur every single time you drew the bitmap.
What is still sub-optimal: drawing anything based on lots of individual pixels, like lines or circles or complex proportional fonts. Pixel writing times have been sped up by a factor of about two, but performing all the pixel operations to get the dots in a potentially large, complex font character onto the display just plain adds up. Even after the Apollo3 mods, writing an individual pixel to the display still requires sending something like 13 bytes. This might not be too bad except that those 13 bytes require 6 distinct SPI transfers (if memory serves). If pixel update rates matter to you, you might consider doing all your drawing using a RAM-based GFXcanvas16 object. With a canvas object, all the pixel operations turn into RAM writes which are quite fast compared to SPI transfer operations. Once all the updates are done, you can blast the whole canvas over as one massive screen update using drawRGBBitmap() since the redraw time is only 56 milliseconds regardless of how many pixels changed state. It might be worth considering if you are trying to perform a lot of complicated pixel operations and need a faster display update. The downside is that a GFXcanvas16 object for a 240x320 16-bit color display will use nearly 154K of SRAM. On most other little processors, that would be a total joke. On an Apollo3, it's a choice.
Starting with a bit of background, the GFX library was originally developed with the following assumptions:
- Single byte SPI transfers are extremely cheap to set up
- All SPI transfers are bi-directional
- The LCD display controllers are all big-endian
- The underlying processor is little-endian
- Apollo3 SPI transfer buffers must start on a word-aligned address, even if only 1 byte is being transferred
- SPI transfers are expensive to set up by the HAL, but are quite time-efficient during the actual data transfer
- There seems to be a bug if a bidirectional transfer is followed by a unidirectional write-only transfer (see viewtopic.php?f=169&t=53262)
The process to create an optimized Apollo3 GFX library will assume that you have already installed the Adafruit_GFX_Library version 1.9.0, which is the latest version of the released library at the time of this writing.
It is not a good idea to make source code changes directly to an installed Arduino GFX library. Those changes would get overwritten every time the Arduino system updated the GFX library. The workaround is to create an Apollo3-specific version of the GFX library. This new version of the GFX library will live beside the standard GFX library and will be used for Apollo3 builds only. For example, if you use the same Arduino installation to create AVR or SAMD builds for a different project that also uses the GFX library, those builds will continue to use the standard GFX library. The downside of this approach is that the new Apollo3-specific library will be stuck at the version where it was created. However, the apollo3 changes are almost certainly easily merged back into updated source files.
To make the Apollo3-specific GFX library, perform the following steps:
- Install Adafruit_GFX_Library version 1.9.0
- Go to the main library directory where the Adafruit_GFX_Library got installed
- Make a copy of the original Adafruit_GFX_Library and all its contents. Paste the new copy back into in the main library directory beside the original library folder, then rename the new copy 'Adafruit_GFX_Library_apollo3'. Use that exact name: it must end in lowercase 'apollo3' and spaces are not allowed!
- Edit the file 'library.properties' and change the line that says 'architectures=*' to say 'architectures=apollo3'. This tells the Arduino build system that this new library will be the preferred GFX library for any build based on an apollo3 architecture.
- Download the new versions of Adafruit_SPITFT.cpp and Adafruit_SPITFT.h into the new ‘Adafruit_GFX_Library_apollo3’ directory overwriting the versions in that directory. These two files contain the processor-specific parts of the GFX library.
I am not able to attach the source files this post, so I have them set up here as downloadable google drive links:- Adafruit_SPITFT.cpp: https://drive.google.com/file/d/18RWVsS ... sp=sharing
- Adafruit_SPITFT.h: https://drive.google.com/file/d/1SMMFo1 ... sp=sharing
Code: Select all
This indicates that the change to the apollo3 version of the library.properties file was recognized by the Arduino build system. Multiple libraries were found for "Adafruit_GFX.h"
Used: C:\<somepath>\Arduino\libraries\Adafruit_GFX_Library_apollo3
Not used: C:\<somepath>\Arduino\libraries\Adafruit_GFX_Library
Upload the result and your GFX operations should run nice and fast now. If not, check where you put the library, check that it got named properly, check that you made the right change to the library.properties file, and make sure that your Adafruit_SPITFT.cpp file has some references to 'apollo3' inside it.
Results: My measurements show that with the original library, erasing a full 240x320 display would take 2.34 seconds. Copying a full 240x320 big-endian bitmap (153,600 bytes of data) was slightly worse at 2.38 seconds. With the apollo3 optimizations included, both an erase or a full-screen bitmap copy take under 57 milliseconds, or about 42 times faster. Side note: I like that the Artemis has no trouble swallowing a 153K test bitmap into its flash.
Testing: These replacement SPITFT files are only guaranteed to work with GFX library version 1.9.0. The resulting library has been tested with with 1.44" 128x128 color displays using an ST7735 controller, and 2.8" 320x240 color displays using an ILI9341 controller. However, any display supported by the standard GFX library should work fine.
Further Reading: If you are interested looking at the changes, edit the new Adafruit_SPITFT.cpp and search for all instances of ‘apollo3’. I also made a change to drawRGBBitmap(), which is the routine that copies memory-resident data from flash or RAM directly to the display. The original version always assumes that the data being copied is little-endian. The new version allows you to choose. If you create your memory-resident bitmap data in big-endian format, the new version of drawRGBBitmap() will allow you to skip the endian conversion process that would otherwise occur every single time you drew the bitmap.
What is still sub-optimal: drawing anything based on lots of individual pixels, like lines or circles or complex proportional fonts. Pixel writing times have been sped up by a factor of about two, but performing all the pixel operations to get the dots in a potentially large, complex font character onto the display just plain adds up. Even after the Apollo3 mods, writing an individual pixel to the display still requires sending something like 13 bytes. This might not be too bad except that those 13 bytes require 6 distinct SPI transfers (if memory serves). If pixel update rates matter to you, you might consider doing all your drawing using a RAM-based GFXcanvas16 object. With a canvas object, all the pixel operations turn into RAM writes which are quite fast compared to SPI transfer operations. Once all the updates are done, you can blast the whole canvas over as one massive screen update using drawRGBBitmap() since the redraw time is only 56 milliseconds regardless of how many pixels changed state. It might be worth considering if you are trying to perform a lot of complicated pixel operations and need a faster display update. The downside is that a GFXcanvas16 object for a 240x320 16-bit color display will use nearly 154K of SRAM. On most other little processors, that would be a total joke. On an Apollo3, it's a choice.