SparkFun Forums 

Where electronics enthusiasts find answers.

Topics pertaining to the Arduino Core & software used with the Artemis module and Artemis development boards.
User avatar
By robin_hodgson
#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:
  • 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
The Apollo3 processor and its underlying HAL SPI driver have their own set of requirements that must be managed:
  • 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 performance enhancements for Apollo3 are mostly based on maximizing the number of bytes sent in an SPI transfer. Since the Apollo3 has a relatively high SPI transfer setup cost, it is far more efficient to perform 1 SPI transfer of N bytes than the default GFX approach of performing N SPI transfers of 1 byte. To avoid triggering the bug mentioned above, all Apollo3 transfers in the library are converted to be unidirectional.

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!
The original library will remain untouched by anything that follows. Changes will only be made to the files in the new ‘Adafruit_GFX_Library_apollo3’ folder!
  • 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:
That’s all: rebuild your GFX application and you should be good to go. If you turn on verbose mode during the Arduino build process, you will see this buried in the output:
Code: Select all
 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
This indicates that the change to the apollo3 version of the library.properties file was recognized by the Arduino build system.

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. :)
#226811
Sorry to necro this thread, but I recently got a 3.5" 480x320 HXD8357D TFT display from Adafruit (https://www.adafruit.com/product/2050#technical-details). After trying to drive it with an Arduino Uno, I wasn't happy with the results I was getting (lots of flickering on updates, etc). It also just didn't have enough memory to support custom fonts, or off-screen buffers. I decided to give the SF RedBoard Artemis a try to drive it. After noticing the poor performance of the default Adafruit_GFX library I found this page.

I have followed the advice in this post and while the display does seem a little faster, it's certainly not the improvement achieved by robin_hodgson. It still takes multiple seconds to clear the display. I'm using the graphicstest example sketch found in the Adafruit_GFX library as a benchmark.

I also did try increasing the SPI frequency to about as high as the display can operate (40000000L) using the begin() method on the TFT object. I did also try to change the SPI mode but that simply made the display malfunction.

I was curious if there were some more steps that I could take in trying to reach level of performance reported in this post.

Any advice is greatly appreciated. Also I'm not tied to this GFX library but I would like something that has some similar features, so if there is an alternative for the Apollo3 that is more performant, I am willing to make the switch.
User avatar
By robin_hodgson
#226819
Did you follow all the instructions from the original post? It sounds like your build is either not using the modified Adafruit_SPITFT library files that I supplied, or for some reason, it is not using the Apollo3 speed-optimized routines inside the new library.

If you only see a tiny speedup, that is probably due to you running the SPI clock faster. The real speed problem is that the basic Adafruit_SPITFT driver does a ton of distinct SPI transactions which are cheap on an 8-bit Atmel processor, but quite expensive on an Apollo3. The modified driver I supplied is designed to coalesce low-level SPI operations into as few transactions as possible.

Inspect your verbose build output and make sure that your build is actually using the library files that I supplied and not the generic ones. Or modify my library files to do some printf debugging so that you can prove that they are being called.
#226834
Thanks for the pointers. The AM_PART_APOLLO3 macro was not being defined so none of the updated code was being executed. There was also an error with the hwspi._spi->transferOut and hwspi._spi->transferIn methods not being found. Looking in the current Arduino reference I could only find the transfer(*buf, len) method, which mostly works, but there are some odd patterns during the drawing of the triangles and rounded rects.
I couldn't find any reference to the transferOut and transferIn methods.

All that being said, after defining the AM_PART_APOLLO3 macro I'm seeing much speedier response.
User avatar
By Dark-Knight
#232412
I realize this is a very old topic now, but I was hoping you could detail out the build environment you were using when this was working. I have been unable to recreate your arduino like draw speeds, and in fact am getting about 10x slower results than an UNO when using the adafruit GFX library, even with your modified files. Using verbose logging, and by removing the regular library I am quite certain it's using the modified 1.9.0 version I created per your instructions.

As a starting point, I tried using board definitions and libraries that would have been released prior to July 2020 when you post this, and I am unable to get it to compile at all with any Apollo3 board definitions older than 2.0.

Using Sparkfun's Hyperdisplay library and example does work, but also appears to be drawing extraordinarily slow at roughly the same rate as the Adafruit library.

I'm at a loss as how to proceed other than to return the Redboard and choose a different platform to build on. Any suggestions you might have regarding the above to get a faster display would be appreciated.

Times:

Sparkfun Artemis:

02:29:30.348 -> Display Power Mode: 0xCA
02:29:30.348 -> MADCTL Mode: 0x24
02:29:30.348 -> Pixel Format: 0x2
02:29:30.348 -> Image Format: 0xC0
02:29:30.348 -> Self Diagnostic: 0xE0
02:29:30.348 -> Benchmark Time (microseconds)
02:29:30.348 -> Screen fill 23129328
02:29:54.112 -> Text 1193651
02:30:02.957 -> Lines 11468135
02:31:07.764 -> Horiz/Vert Lines 1893860
02:31:14.809 -> Rectangles (outline) 1207512
02:31:21.172 -> Rectangles (filled) 48005297
02:32:15.827 -> Circles (filled) 5538829
02:32:26.027 -> Circles (outline) 5013224
02:32:31.549 -> Triangles (outline) 2602378
02:32:39.340 -> Triangles (filled) 15679052
02:33:03.018 -> Rounded rects (outline) 2348387
02:33:10.528 -> Rounded rects (filled) 47762824
02:34:03.687 -> Done!

Arduino Uno:

02:35:46.628 -> Image Format: 0x0
02:35:46.628 -> Self Diagnostic: 0x0
02:35:46.628 -> Benchmark Time (microseconds)
02:35:46.675 -> Screen fill 1496888
02:35:48.638 -> Text 175020
02:35:52.137 -> Lines 1435584
02:35:59.494 -> Horiz/Vert Lines 127044
02:36:00.426 -> Rectangles (outline) 83792
02:36:01.309 -> Rectangles (filled) 3107152
02:36:05.332 -> Circles (filled) 498780
02:36:06.122 -> Circles (outline) 613904
02:36:07.242 -> Triangles (outline) 316496
02:36:08.363 -> Triangles (filled) 1358164
02:36:10.824 -> Rounded rects (outline) 242988
02:36:11.894 -> Rounded rects (filled) 3134828
02:36:15.823 -> Done!
User avatar
By Dark-Knight
#232414
I managed to somewhat mitigate the problem, but considering the old versions, I'm not sure I'd call it solved.

Using:

Apollo3 Boards version: 1.2.3
Adafruit busio version: 1.3.3
Adafruit GFX Library 1.9.0 w/ modifications

AND in Adafruit_SPITFT.cpp changing every instance of:

defined(AM_PART_APOLLO3)

to:

defined(AM_PART_APOLLO3) || defined(ARDUINO_ARCH_APOLLO3)

This is a big reason why the optimizations weren't working, but as soon as I fixed that, it started throwing compile errors for SPI pin errors until I reverted to pre 2.0 board def, which broke busio until I reverted that to a particular older version. Neither 1.3 & 1.4 of busio work. I really do not have the expertise to troubleshoot or even begin to fix what's wrong here.

I finally got it to compile though, and it's pretty fast. Screen fills are 0.28 Seconds instead of 23 seconds, 82x faster. That's even faster than an UNO. You can't even really see the color flash tests in the beginning they go by so fast.

Sparkfun should really make a more functional Hyperdisplay library that can do it this fast right from the example with new board definitions. As it is, it looks like the Hyperdisplay might be using software SPI.

Modified Libraries:

07:21:03.131 -> ILI9341 Test!
07:21:03.598 -> Display Power Mode: 0xCA
07:21:03.598 -> MADCTL Mode: 0x24
07:21:03.598 -> Pixel Format: 0x2
07:21:03.598 -> Image Format: 0xC0
07:21:03.598 -> Self Diagnostic: 0xE0
07:21:03.598 -> Benchmark Time (microseconds)
07:21:03.598 -> Screen fill 282899
07:21:04.393 -> Text 293317
07:21:07.755 -> Lines 3204072
07:21:21.217 -> Horiz/Vert Lines 40770
07:21:21.827 -> Rectangles (outline) 34755
07:21:22.437 -> Rectangles (filled) 592403
07:21:23.603 -> Circles (filled) 559900
07:21:24.215 -> Circles (outline) 1408372
07:21:26.176 -> Triangles (outline) 685189
07:21:27.393 -> Triangles (filled) 535259
07:21:29.218 -> Rounded rects (outline) 432123
07:21:30.205 -> Rounded rects (filled) 721966
07:21:31.512 -> Done!
User avatar
By robin_hodgson
#232421
It looks like something changed in Arduino-land over the last two-ish years. Which is always what happens in Arduino-land, which makes it kind of perpetually irritating for me to use. I actually try to avoid Arduino code, if possible. In this case, the GFX & SPITFT libraries are too complicated to replicate. My usual approach is to pull Arduino libraries into my own non Arduino-based build system. I fake up any Arduino calls that a library might make to be served from my own 'fake Arduino" library. Like the SPI library, for example. For almost all my GFX-based projects, I am using my own hardware SPI library which avoids the issues of things like software-based SPI happening behind my back. But that all said, the project that started this thread used the Arduino system from top to bottom. I wanted to be able to transfer the source code in a simple fashion if anyone wanted to replicate the entire project. Good intentions, but it would appear that the churn under the surface of Arduino has broken things. Or maybe it is something totally different, and my morning coffee hasn't kicked in yet.

The original project still controls my toaster oven and cooks all my new Artemis PCB projects. The GFX animations on the LCD screen were just for fun. In fact, the whole LCD screen was just for fun.
You do not have the required permissions to view the files attached to this post.
 Topic permissions

You can post new topics in this forum
You can reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum