TFT Display with Custom Fonts

Using these displays with an Argon:
ST7735 128x160 TFT SPI
ILI9341 240x320 TFT SPI

I would like to use larger custom fonts, but the refresh rate is very low. When using the built in fonts the update is fast with no noticeable flicker. With the custom fonts I only fill the area that needs to be set back to the background with a rectangle.

Currently using these libraries:
Adafruit_GFX_RK
Adafruit_ST7735_RK or Adafruit_ILI9341_RK

I have seen Arduinos use these same displays with much better refresh rates. There appears to be more optimized libraries available for them with custom fonts, can these be ported to Particle or should I be using different libraries or techniques?

SPI Clock Rate:
I’m using hardware SPI with the default clock rate of 8MHz, which was measured on the clock pin. I would like to increase this, but I can’t figure out how to change it. Setting “_freq” to 15000000 has no effect. Can this be done?

Thanks

I have been using both these displays for a while now and recently tried a 3.5" TFT (Adafruit with touch display HX8357) driven by an Argon. The speed issue is with the Argon SPI bus - the standard library uses 8MHz which is very slow (I think because of limit on SPI1), the speed can be increased to 32MHz on SPI which is better but not as good as the Photon 60MHz! The other factor is to ensure the SPI is using DMA as this speeds up writing larger areas to the screen memory. You need to look at Adafruit_SPITFT.cpp /.h to see what has been set and modify it.

1 Like

I will look into increasing the SPI CLK.

Re-writing the SPI routines to use DMA is beyond me right now.

There is a older version of this library available in the IDE, but I can’t get it to work at all, it is supposed to be very fast for other devices.

The limitation with refresh speed is not really dependent on SPI bus speed, at least that’s what I realized when I changed the SPI clock from 8MHz to 24MHz on Argon and Boron. The time is takes to update the whole 320*240 screen is pretty much the same ~1.5 sec.
I think it’s due to the way RTOS works, with task scheduling and other background operations. (I tested with single threaded block but didn’t change a thing in my case)

The trick I found to somehow get reasonable refresh times is by using the AdafruitGFX canvas objects, specifically GFXcanvas16 object. I define the size of the canvas to be about the size of the area of the screen I’d like to refresh, specify both text color and background color and then send the buffer to the display internal frame buffer. See code snippet below:

canvas50by50 = new GFXcanvas16(50, 50);

canvas50by50->drawCircle(24, 24, 24, WHITE); // g-ball chart circle

canvas50by50->drawFastHLine(0, 24, 50, WHITE); // target H line

canvas50by50->drawFastVLine(24, 0, 50, WHITE); // target V line

// reset gball location to center

int16_t gball_center_x = 24;

int16_t gball_center_y = 24;

// longitudinal accel along Z-axis

// longitudinal acceleration displacement scaled for: 30 pixels/0.5G (auto-compensated for tilt)

gball_center_y = gball_center_y + int(accel[2]*60.0); //accelerating  accel_z < 0 --> y down --> ball up

// lateral acceleration displacement scaled for: 25 pixels/0.5G along X-axis (not affected by tilt in this use case)

if (accel[0] >= 0.04 || accel[0] < -0.04 ) {

gball_center_x = gball_center_x + int(accel[0]*50.0); //turn left --> accel > 0 --> x up --> ball right

}

canvas50by50->fillCircle(gball_center_x, gball_center_y, 4, FLUO_GREEN);

// now transfer the canvas' buffer using a faster function(and hopefully DMA in the near future)

display.drawRGBBitmap(70, 14, canvas50by50->getBuffer(), 50, 50); **// ~ 64 ms**

delete canvas50by50;

Now GFXcanvas16 object is a memory hoarder, taking 2 x width x length bytes, but it’s the faster to transfer compared to GFXcanvas1 and GFXcanvas8, and with dynamic allocation, you can delete once no longer need as shown in the code example above.
Hopefully @rickkas7 will spoil us with DMA transfers for AdafruitGFX on 3rd gen devices in the future.

Can the canvas’ be used for custom fonts or only graphics?

That would be great, it looks like GFX libraries for most other devices have been greatly optimized over the years.

You can use GFXcanvas the same way you use AdafruitGFX object, including any special font that comes with the library. Here’s another example that shows how it works:

GFXcanvas16 *rh_canvas = new GFXcanvas16(100, 30);
rh_canvas->setTextColor(ILI9341_CYAN, ILI9341_BLACK);
rh_canvas->setFont(&amp;FreeSans12pt7b);
rh_canvas->setCursor(0, 24);
rh_canvas->printf("%.0f", env_data.rel_hum);
rh_canvas->setFont(&amp;FreeSans9pt7b);
rh_canvas->print(" %RH");
tft.drawRGBBitmap(20, 160, rh_canvas->getBuffer(), 100, 30);
delete rh_canvas;

One thing to keep in mind is that the coordinates you set with setCursor() are relative to the canvas, and not to the whole display.

1 Like

DMA is marked as experimental in the Adafruit_SPITFT.cpp code. I wasn’t suggesting you implement DMA!

I have seen some speed increase with 32MHz on the Argon but agree with @jaafar that it isn’t x4 faster and with the 480x320 resolution screen it is very slow. Maybe the whole SPI bus implementation is slower than the Photon? Another Gen3 issue?

Using GFXcanvas16 helps and makes it usable for my application.

I still can’t get the SPI clk to go over 8MHz though. Changing the setting in Adafruit_ST77xx.cpp does nothing.

#define SPI_DEFAULT_FREQ 24000000

I also tried changing _freq, freq and using initSPI(24000000)

What am I missing?

Are you using Workbench or web IDE? If workbench then you can do a thorough search of all the places it is declared and set. I think the issue may be that the default frequency is overwritten and hence it is forced back to be 8MHz. There are a lot of conditional compile statements.

GFXcanvas16 is a neat solution for quick updates but in any case with these TFTs you are writing to the screen memory.

Workbench

I have other changes in the library, had to flip the red and blue 565 colors, and that works.

I changed every _freq, freq, initSPI(freq) and hard coded the change as well, but it always outputs 8MHz. Can’t find where it is forced back to 8MHz.

It could be that the SPI speed setting function isn’t working?

Funny that you’re running into the SPI setting issue as well, I cannot for the life of me get the SPI clock to go faster than 8MHz either (and I’m working with the TFT). I changed the values in the library up AND down, and the clock speed stayed the same.

I am pretty certain that the SPI speed setting function is working because I tried changing the speed with the touch screen controller and it does work any faster than 1MHz - thus with a Adafruit 3.5" screen with touch the whole SPI bus has to be run at 1MHz hence why it is so slow. I need to get out the screen and try it without the touch display controller.

hm that’s a clue. I’m not using it with the touch controller right now and seeing the same behavior. Maybe the clock speed is changing but the effect is negligible? I didn’t see any way to read the actual SPI clock speed, how are you doing that?

The problem is with SPITFT.h/.cpp this implementation of a multi-device SPI interface by Adafruit as used by Adafruit_GFX. I believe that every SPI begin transmission has the SPISettings in it and there is no DMA which is definitely not what was in the previous iteration of Adafruit_mfGFX. This must however be something to do with the Particle implementation of SPI for the Gen3 devices because I have moved my Gen2 devices to use the Adafruit_GFX library and I am not seeing this throttling issue. @rickkas7 could you help give us the benefit of your knowledge here? Thanks

Yes, changing the manipulation of the CS and DC lines from digitalWrite to pinSetFast/pinResetFast should dramatically improve performance. Switching to SPI DMA would also help.

However, using the offscreen bitmap is a really good way to improve performance in general. The problem is that each pixel write is a separate SPI transaction when directly writing to the display.

Super fast response! I can change the SPITFTMacros.h file and try the pinSetFast/pinResetFast.
Do you happen to know if the SPI DMA implementation works - it is surrounded by warnings of being highly experimental!

I haven’t tried the SPI DMA. I didn’t try the pinSetFast/pinResetFast either, but I know someone who did and it made a big difference on Gen 3.

I have gone back to my test sketch to use pinSetFast and pinResetFast and I am now finding it impossible to get the following to build. There are all sorts of ARDUINO not defined issues, then SPISettings then…

The includes in my sketch look like this

#include <Adafruit_HX8357_RK.h>
#include <Adafruit_STMPE610_RK.h>
#include <SdFat.h>
#include "Adafruit_ImageReader.h"
#include "FreeMono18pt7b.h"
#include "FreeMono9pt7b.h"

There seem to have been some changes in these libraries, so I deleted and reinstalled:

dependencies.Adafruit_HX8357_RK=1.0.8
dependencies.Adafruit_STMPE610_RK=1.0.1
dependencies.SdFat=1.0.16
dependencies.Adafruit_ImageReader=1.0.9
dependencies.Adafruit_GFX_RK=1.5.6

The errors suggest there is some mismatch -

In file included from /Users/wjsteen/Documents/Intelligent_Furniture_Project/VSC_Projects/zioxi_locker_ui/lib/Adafruit_GFX_RK/src/Adafruit_SPITFT.h:26:0,
                 from /Users/wjsteen/Documents/Intelligent_Furniture_Project/VSC_Projects/zioxi_locker_ui/lib/Adafruit_HX8357_RK/src/Adafruit_HX8357.h:36,
                 from /Users/wjsteen/Documents/Intelligent_Furniture_Project/VSC_Projects/zioxi_locker_ui/lib/Adafruit_HX8357_RK/src/Adafruit_HX8357_RK.h:4,
                 from /Users/wjsteen/Documents/Intelligent_Furniture_Project/VSC_Projects/zioxi_locker_ui/src/zioxi_locker_ui.ino:8:
/Users/wjsteen/Documents/Intelligent_Furniture_Project/VSC_Projects/zioxi_locker_ui/lib/Adafruit_GFX_RK/src/Adafruit_GFX.h:6:0: warning: "ARDUINO" redefined
  #define ARDUINO 157
 ^
In file included from ./inc/SPI.h:1:0,
                 from /Users/wjsteen/Documents/Intelligent_Furniture_Project/VSC_Projects/zioxi_locker_ui/lib/Adafruit_GFX_RK/src/Adafruit_SPITFT.h:25,
                 from /Users/wjsteen/Documents/Intelligent_Furniture_Project/VSC_Projects/zioxi_locker_ui/lib/Adafruit_HX8357_RK/src/Adafruit_HX8357.h:36,
                 from /Users/wjsteen/Documents/Intelligent_Furniture_Project/VSC_Projects/zioxi_locker_ui/lib/Adafruit_HX8357_RK/src/Adafruit_HX8357_RK.h:4,
                 from /Users/wjsteen/Documents/Intelligent_Furniture_Project/VSC_Projects/zioxi_locker_ui/src/zioxi_locker_ui.ino:8:
./inc/Arduino.h:16:0: note: this is the location of the previous definition
 #define ARDUINO 10800
 ^
/Users/wjsteen/Documents/Intelligent_Furniture_Project/VSC_Projects/zioxi_locker_ui/src/zioxi_locker_ui.ino:73:62: error: use of deleted function 'Adafruit_HX8357::Adafruit_HX8357(const Adafruit_HX8357&)'
 Adafruit_HX8357 tft = Adafruit_HX8357(TFT_CS, TFT_DC, TFT_RST);
                                                              ^
In file included from /Users/wjsteen/Documents/Intelligent_Furniture_Project/VSC_Projects/zioxi_locker_ui/lib/Adafruit_HX8357_RK/src/Adafruit_HX8357_RK.h:4:0,
                 from /Users/wjsteen/Documents/Intelligent_Furniture_Project/VSC_Projects/zioxi_locker_ui/src/zioxi_locker_ui.ino:8:
/Users/wjsteen/Documents/Intelligent_Furniture_Project/VSC_Projects/zioxi_locker_ui/lib/Adafruit_HX8357_RK/src/Adafruit_HX8357.h:127:7: note: 'Adafruit_HX8357::Adafruit_HX8357(const Adafruit_HX8357&)' is implicitly deleted because the default definition would be ill-formed:
 class Adafruit_HX8357 : public Adafruit_SPITFT {
       ^
/Users/wjsteen/Documents/Intelligent_Furniture_Project/VSC_Projects/zioxi_locker_ui/lib/Adafruit_HX8357_RK/src/Adafruit_HX8357.h:127:7: error: use of deleted function 'Adafruit_SPITFT::Adafruit_SPITFT(const Adafruit_SPITFT&)'
In file included from /Users/wjsteen/Documents/Intelligent_Furniture_Project/VSC_Projects/zioxi_locker_ui/lib/Adafruit_HX8357_RK/src/Adafruit_HX8357.h:36:0,
                 from /Users/wjsteen/Documents/Intelligent_Furniture_Project/VSC_Projects/zioxi_locker_ui/lib/Adafruit_HX8357_RK/src/Adafruit_HX8357_RK.h:4,
                 from /Users/wjsteen/Documents/Intelligent_Furniture_Project/VSC_Projects/zioxi_locker_ui/src/zioxi_locker_ui.ino:8:
/Users/wjsteen/Documents/Intelligent_Furniture_Project/VSC_Projects/zioxi_locker_ui/lib/Adafruit_GFX_RK/src/Adafruit_SPITFT.h:118:7: note: 'Adafruit_SPITFT::Adafruit_SPITFT(const Adafruit_SPITFT&)' is implicitly deleted because the default definition would be ill-formed:
 class Adafruit_SPITFT : public Adafruit_GFX {
       ^
/Users/wjsteen/Documents/Intelligent_Furniture_Project/VSC_Projects/zioxi_locker_ui/lib/Adafruit_GFX_RK/src/Adafruit_SPITFT.h:404:9: error: union member 'Adafruit_SPITFT::<anonymous union>::hwspi' with non-trivial 'constexpr Adafruit_SPITFT::<anonymous union>::<anonymous struct>::<constructor>(const Adafruit_SPITFT::<anonymous union>::<anonymous struct>&)'
       } hwspi;                     ///< Hardware SPI values
         ^
/Users/wjsteen/Documents/Intelligent_Furniture_Project/VSC_Projects/zioxi_locker_ui/src/zioxi_locker_ui.ino: In function 'void setup()':
/Users/wjsteen/Documents/Intelligent_Furniture_Project/VSC_Projects/zioxi_locker_ui/src/zioxi_locker_ui.ino:85:19: warning: unused variable 'stat' [-Wunused-variable]
   ImageReturnCode stat;       // Status from image-reading functions

ARDUINO issues I can fix but the use of deleted function I really can’t see the problem.

Try rewriting it this way

Adafruit_HX8357 tft(TFT_CS, TFT_DC, TFT_RST);