Highly optimized ILI9341 (320x240 TFT color display) library

For the fonts in SmartMatrix I used the bdf2c utility which I liked because the ascii art inside the generated .c file is actually editable so you could tweak a font easily inside the C file if you wanted to. All the fonts I chose for SmartMatrix were <= 8 pixels wide so I'm not sure how well bdf2c works with larger widths, I think there's just a comma in the middle of the art. Anyway, it's probably a little late, but sharing in case that idea might be useful.

https://github.com/pixelmatix/bdf2c
an example:
https://github.com/pixelmatix/SmartMatrix/blob/master/Font_gohufont6x11b.c
 
Yeah, documentation is need, but things are still changing pretty rapidly at this moment.

Today I wrote this Perl script which automates the font conversion process.

https://github.com/PaulStoffregen/ILI9341_t3/blob/master/extras/ttf_to_ili9341.pl

So to convert a TTF font to a pair of .C and .H files to be used with ILI9341_t3, you need to compile the bdf_to_ili9341 code and make sure your system has the "otf2bfd" program (on Ubuntu, you can install it with apt-get), and then just run that script. :)

Of course, if you want different font sizes than 8, 9, 10, 11, 12, 13, 14, 16, 18, 20, 24, 28, 32, 40, 48, 60, 72, 96, you'd edit the script to add the font size you want. Or you could manually run otf2bdf, perhaps edit the .BDF file, and then manually run bdf_to_ili9341 to get the .C file, and manually create the .h file like the script does.
 
For the fonts in SmartMatrix I used the bdf2c utility which I liked because the ascii art inside the generated .c file is actually editable so you could tweak a font easily inside the C file if you wanted to.

Yeah, that's a great solution for smaller size (low res) fonts. Sadly, it doesn't scale up well to the much high resolution of ILI9341.

The need for good font conversion to a special format that's size and speed efficient was (until a few days ago) the big missing piece.
 
I've created another github repository for extra ILI9431 fonts. The idea is to keep the ILI9341_t3 library from growing to dozens or even hundreds of megabytes.

https://github.com/PaulStoffregen/ILI9341_fonts

As you can see, I've converted quite a few. Many more will likely be added... feel free to request any you like (with a .TTF file).

To use these, just copy the .C/.H pair for any font into your sketch. Then add #include "font_name.h" in your code, and use tft.setFont(name), where "name" is any of the names defined in the .H file. Then tft.print(), tft.println() or tft.printf() will use the font you've selected.

If you want to go back to Adafruit's default font, use tft.setFontAdafruit().
 
More font testing.... Chancery and Comic Sans

comicsans.jpg
 
Here's the fonts converted so far (opps, not including regular Arial)

All of these are available in 18 sizes: 8, 9, 10, 11, 12, 13, 14, 16, 18, 20, 24, 28, 32, 40, 48, 60, 72, 96

ArialBlack_Impact.jpg


Chancery_ComicSans.jpg


Courier.jpg
 
Panel Meter Font

That Font Awesome truly is awesome.

Does anyone know of a good open source complete LCD and Dot Matrix font?
I still like to use them to emulate panel meter readouts and such.
 
Just wanted to stop by and say really awesome work Paul! This is a huge addition to your already popular optimized ILI9341 library. I can see the benefits of the Font-Awesome icon set already, especially for interactive GUIs.

And I think I mentioned this to you before, but I'm a graphic designer/typographer (font designer) so if you have any ideas for custom icons or anything else just let me know - I'd be happy to design and generate the font files for you to convert.

Joe
 
Looks really nice Paul.

How much more time is required to refresh the new fonts compared with the original font for the ILI9381_t3?
 
Just wanted to stop by and say really awesome work Paul! This is a huge addition to your already popular optimized ILI9341 library. I can see the benefits of the Font-Awesome icon set already, especially for interactive GUIs.

And I think I mentioned this to you before, but I'm a graphic designer/typographer (font designer) so if you have any ideas for custom icons or anything else just let me know - I'd be happy to design and generate the font files for you to convert.

Joe

I don't speak for Paul, but I think it would be cool if PJRC had a 'house style' for GUI elements. I'm thinking button borders, menu indicators, sliders, dividers, tick marks, shadows, highlights, etc. The graphical goodies that make the platform look all dressed up and ready to go out.

There are various ways of adding position input such as a touch panel, joystick, PS/2 mouse and of course keyboard input, too. I think it's just a matter of time before people want to use these displays as control panels, not just as readouts or simple up/down/left/right menu systems.

Or it might be just me. I've build a couple of very simple joysticks using copper clad board and the Teensy capacitive touch read feature. I implemented mouse pointer support, but haven't fixed all the bugs in reading pixels back from the display. Someday I hope to publish both.
 
I mostly agree pictographer, the question is how many people would use those features compared to those who would want to make something custom.

And a little showing off.
Here is a shot of my WIP gocart dash with an Adafruit 2.8" ILI9341 resistive touch screen:)

FZy8W2K.jpg
 
There is a little thing that could perhaps be optimized :

In ILI9341_t3.h is setAddr():
Code:
...
protected
...
#if 1    
//new:
    uint16_t oldx0 = 0;
    uint16_t oldx1 = 0;
    uint16_t oldy0 = 0;
    uint16_t oldy1 = 0;
    
    
    void setAddr(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1)     
     __attribute__((always_inline)) {
        if (oldx0!=x0 || oldx1!=x1) {
            writecommand_cont(ILI9341_CASET); // Column addr set
            writedata16_cont(x0);   // XSTART
            oldx0 = x0;
            oldx1 = x1;
            writedata16_cont(x1);   // XEND                        
        }
        if (oldy0!=y0 || oldy1!=y1) {
            writecommand_cont(ILI9341_PASET); // Row addr set
            writedata16_cont(y0);   // YSTART
            oldy0=y0;
            oldy1=y1;
            writedata16_cont(y1);   // YEND                        
        }
    }
#endif
#if 0    
    //old
    void setAddr(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1)
      __attribute__((always_inline)) {
            writecommand_cont(ILI9341_CASET); // Column addr set
            writedata16_cont(x0);   // XSTART
            writedata16_cont(x1);   // XEND
            writecommand_cont(ILI9341_PASET); // Row addr set
            writedata16_cont(y0);   // YSTART
            writedata16_cont(y1);   // YEND
    }
#endif

The easy "trick" is, not to transmit new start/end values over SPI to the controller.

Comparison:
Code:
[TABLE="width: 678"]
[TR]
[TD][/TD]
[TD]       Patch[/TD]
[TD]      Original[/TD]
[TD]    Speedup[/TD]
[TD][/TD]
[/TR]
[TR]
[TD]Screen fill[/TD]
[TD="align: right"]281178[/TD]
[TD="align: right"]281274[/TD]
[TD="align: right"]96[/TD]
[TD]//because 5x times Screen fill[/TD]
[/TR]
[TR]
[TD]Text[/TD]
[TD="align: right"]19852[/TD]
[TD="align: right"]20626[/TD]
[TD="align: right"]774[/TD]
[TD][/TD]
[/TR]
[TR]
[TD]Lines[/TD]
[TD="align: right"]73444[/TD]
[TD="align: right"]73457[/TD]
[TD="align: right"]13[/TD]
[TD][/TD]
[/TR]
[TR]
[TD]Horiz/Vert Lines[/TD]
[TD="align: right"]23073[/TD]
[TD="align: right"]23257[/TD]
[TD="align: right"]184[/TD]
[TD][/TD]
[/TR]
[TR]
[TD]Rectangles (outline)[/TD]
[TD="align: right"]14553[/TD]
[TD="align: right"]14693[/TD]
[TD="align: right"]140[/TD]
[TD][/TD]
[/TR]
[TR]
[TD]Rectangles (filled)[/TD]
[TD="align: right"]585222[/TD]
[TD="align: right"]585159[/TD]
[TD="align: right"]-63[/TD]
[TD][/TD]
[/TR]
[TR]
[TD]Circles (filled)[/TD]
[TD="align: right"]99668[/TD]
[TD="align: right"]100628[/TD]
[TD="align: right"]960[/TD]
[TD][/TD]
[/TR]
[TR]
[TD]Circles (outline)[/TD]
[TD="align: right"]87716[/TD]
[TD="align: right"]95870[/TD]
[TD="align: right"]8154[/TD]
[TD][/TD]
[/TR]
[TR]
[TD]Triangles (outline)[/TD]
[TD="align: right"]17866[/TD]
[TD="align: right"]17867[/TD]
[TD="align: right"]1[/TD]
[TD][/TD]
[/TR]
[TR]
[TD]Triangles (filled)[/TD]
[TD="align: right"]198047[/TD]
[TD="align: right"]200015[/TD]
[TD="align: right"]1968[/TD]
[TD][/TD]
[/TR]
[TR]
[TD]Rounded rects (outline)[/TD]
[TD="align: right"]40094[/TD]
[TD="align: right"]40103[/TD]
[TD="align: right"]9[/TD]
[TD][/TD]
[/TR]
[TR]
[TD]Rounded rects (filled)[/TD]
[TD="align: right"]642633[/TD]
[TD="align: right"]642474[/TD]
[TD="align: right"]-159[/TD]
[TD][/TD]
[/TR]
[TR]
[TD]Done![/TD]
[TD][/TD]
[TD][/TD]
[TD][/TD]
[TD][/TD]
[/TR]
[/TABLE]

Most of the time, it is faster.
But sometimes it is slower (i don't know why..? Interrupts ? the "if.." can't take more than a few cycles?), and this is the reason why i did'nt made a "pullrequest". What do you think ?

Maybe, a better solution is to make two new functions: setAddrX() and setAddrY() and edit the whole lib ?
 
Last edited:
What if you just did half the speedup: for unchanged rows only? It seems cols more likely to change for normal texting (and all else in the benchmark). That would minimize the loss cases - and on some row based improvements the column check could be a wash?

A quick grep points out that line/pixel/rect functions hit this of course - but would never likely hit the same points twice in normal usage.

I wonder how much you lose using the old code - and just always doing the assignments to old[x,y]'s? If this is trivial then maybe do that in the old func and call the optimized version for drawChar?
 
What if you just did half the speedup: for unchanged rows only? It seems cols more likely to change for normal texting (and all else in the benchmark). That would minimize the loss cases - and on some row based improvements the column check could be a wash?

A quick grep points out that line/pixel/rect functions hit this of course - but would never likely hit the same points twice in normal usage.

I wonder how much you lose using the old code - and just always doing the assignments to old[x,y]'s? If this is trivial then maybe do that in the old func and call the optimized version for drawChar?

I found some more "issues", i think i can upload a new version of the library to my repository in some days.
For example, a change of all 16-bit variables to 32 bit leads to much less codesize and a bit more speed too.

Interesting: The execution-time is not constant, even with disabled interrupts.
 
Time is an illusion. Lunchtime doubly so.

:cool:
Code:
[TABLE="width: 430"]
[TR]
[TD][/TD]
[TD="align: right"]After(us)[/TD]
[TD="align: right"]Before(us)[/TD]
[TD="align: right"]Speedup(us)[/TD]
[/TR]
[TR]
[TD]Screen fill[/TD]
[TD="align: right"]281222[/TD]
[TD="align: right"]     281274[/TD]
[TD="align: right"]52[/TD]
[/TR]
[TR]
[TD]Text[/TD]
[TD="align: right"]19040[/TD]
[TD="align: right"]20626[/TD]
[TD="align: right"]1586[/TD]
[/TR]
[TR]
[TD]Lines[/TD]
[TD="align: right"]73260[/TD]
[TD="align: right"]73457[/TD]
[TD="align: right"]197[/TD]
[/TR]
[TR]
[TD]Horiz/Vert Lines[/TD]
[TD="align: right"]23064[/TD]
[TD="align: right"]23257[/TD]
[TD="align: right"]193[/TD]
[/TR]
[TR]
[TD]Rectangles (outline)[/TD]
[TD="align: right"]14557[/TD]
[TD="align: right"]14693[/TD]
[TD="align: right"]136[/TD]
[/TR]
[TR]
[TD]Rectangles (filled)[/TD]
[TD="align: right"]585140[/TD]
[TD="align: right"]585159[/TD]
[TD="align: right"]19[/TD]
[/TR]
[TR]
[TD]Circles (filled)[/TD]
[TD="align: right"]95012[/TD]
[TD="align: right"]100628[/TD]
[TD="align: right"]5616[/TD]
[/TR]
[TR]
[TD]Circles (outline)[/TD]
[TD="align: right"]85021[/TD]
[TD="align: right"]95870[/TD]
[TD="align: right"]10849[/TD]
[/TR]
[TR]
[TD]Triangles (outline)[/TD]
[TD="align: right"]17839[/TD]
[TD="align: right"]17867[/TD]
[TD="align: right"]28[/TD]
[/TR]
[TR]
[TD]Triangles (filled)[/TD]
[TD="align: right"]197659[/TD]
[TD="align: right"]200015[/TD]
[TD="align: right"]2356[/TD]
[/TR]
[TR]
[TD]Rounded rects (outline)[/TD]
[TD="align: right"]38885[/TD]
[TD="align: right"]40103[/TD]
[TD="align: right"]1218[/TD]
[/TR]
[TR]
[TD]Rounded rects (filled)[/TD]
[TD="align: right"]641962[/TD]
[TD="align: right"]642474[/TD]
[TD="align: right"]512[/TD]
[/TR]
[/TABLE]

https://github.com/FrankBoesing/ILI9341_t3
Tests appreciated... should be 100% compatible, but you never know...
 
Last edited:
I refreshed my Arduino/Teensyduino installation and finally got a chance to try out the new font library.

I'll say that the new fonts look really nice, and the display is very responsive.

I noticed two small issues while playing around with the library.
One is that the text background color feature is not working.
The other is that on my installation, tft.println() doesn't seem to send a line feed. I need to set the cursor position to avoid overprinting the previous line of text.

Here's some info on my setup.
OS: Windows 8.1
Arduino_1.6.5_R5
Teensyduino_1.25_B2
Tools/Options Teensy 3.1 / 120 MHz optimized

Here's a photo and the code I'm using:
20150828_143918.jpg
Code:
#include "SPI.h"
#include "ILI9341_t3.h"
#include "font_AwesomeF000.h"
#include "font_ChanceryItalic.h"
#include "font_ComicSansMSBOLD.h"

#define TFT_RST 8
#define TFT_DC  9
#define TFT_CS 10
ILI9341_t3 tft = ILI9341_t3(TFT_CS, TFT_DC, TFT_RST);
#define RGB(r,g,b) (r<<11|g<<5|b)  //  R = 5 bits 0 to 31, G = 6 bits 0 to 63, B = 5 bits 0 to 31

void setup() {
 tft.begin();
 tft.fillScreen(ILI9341_BLACK);
 tft.setTextColor(ILI9341_YELLOW);
 tft.setRotation(1);

 tft.setFont(ComicSansMS_32_Bold);
 tft.setCursor(0,10);
 tft.setTextColor(ILI9341_YELLOW);
 tft.println("COMICSANS");
 
 tft.setFont(Chancery_40_Italic);
 tft.setCursor(0,60);
 tft.setTextColor (RGB(0,0,63), RGB(31,63,31));   // <--- Should be white background
 tft.println("CHANCERY");
 
 tft.setFont(AwesomeF000_32);
 tft.setCursor(0,160);
 tft.setTextColor (ILI9341_RED, ILI9341_WHITE);   // <--- Should be white background
 tft.println("RSTLNEY");
}

void loop() {


  tft.setFont(ComicSansMS_32_Bold);
  tft.setCursor(0,110);
  int x = 1;
  
     for (int i = 1; i > -1; i = i + x){    
     tft.setTextColor (RGB(31,i,31));
     tft.println("0123456789:");
     if (i == 63) x = -1;
     //delay(20);
    }
}

Also, Frank B's I ran your speedup modification, both with the sketch above and with graphicstest.ino without any problems.
Here are my results:
Code:
graphicstest.ino with Frank B. Speedup

OS: Windows 8.1
Arduino_1.6.5_R5
Teensyduino_1.25_B2
Tools/Options Teensy 3.1 / 120 MHz optimized

Results:

ILI9341 Test!
Display Power Mode: 0xCE
MADCTL Mode: 0x24
Pixel Format: 0x2
Image Format: 0x0
Self Diagnostic: 0xE0
Benchmark                Time (microseconds)
Screen fill              224832
Text                     13387
Lines                    58508
Horiz/Vert Lines         18315
Rectangles (outline)     11597
Rectangles (filled)      467674
Circles (filled)         69101
Circles (outline)        54274
Triangles (outline)      14208
Triangles (filled)       154384
Rounded rects (outline)  27250
Rounded rects (filled)   511220
Done!

Thanks Paul and Frank, and everyone else that worked on this.
I'm looking forward to rolling this into my next project.
 
Last edited:
Back
Top