Highly optimized ILI9341 (320x240 TFT color display) library

Hello,
I'm new with this library and I found it impressive so far. I'm converting the code to use in a Bluepill (STM32F103C8), all the graphics works pretty well, but I'm stuck in the fonts. I checked all the source code in the repository https://github.com/PaulStoffregen/ILI9341_t3, in the examples and fonts but I didn't find the source code for the function "print".
For example, in this code:
Code:
    tft.setTextColor(ILI9341_YELLOW);
    tft.setFont(Arial_18);
    tft.setCursor(98, 42);
    tft.print("waiting for");
Everything is there, except the "print".
Can someone help pointing me in the right direction to find that source code?
Thanks a lot!
 
In my own version of the library (ili9341_t3n), it is also derived from Print

The Print class has a couple of virtual functions which I overwrite:
Code:
  // overwrite print functions:
  virtual size_t write(uint8_t);
  virtual size_t write(const uint8_t *buffer, size_t size);
The first one is mandatory, that is I believe it is defined as a pure virtual in the base class.
The second one is optional; default is to simply iterate over the array passed in and call the single char method.

Note: my version also supports the Adafruit fonts as well as the ILI9341 fonts...
 
Hi Chuckero,

Maybe I am missing something, but I believe that you are trying to adapt this code to some other platform STM32F103C8, which is fine as the code is all open source.

However, I am wondering if you might be better off starting with the Adafruit code base:
https://github.com/adafruit/Adafruit_ILI9341
which uses their library: https://github.com/adafruit/Adafruit-GFX-Library

And I believe they already have some support for at least some STM32 boards.

The font drawing code in most of our libraries, support both transparent and opaque text drawing.
This is controlled by the call: void setTextColor(uint16_t c, uint16_t bg);
if you call this with 2 parameters: the first is the foreground color the second is background color, it will use the bg color to fill in.
If you call this with 1 parameter, it will draw transparent and leave whatever was in the background.
 
Hello KurtE, thanks for your comments.

At this point I can not start or restart with Adafruit code base, as I already have done 95% of the work. All the communications with the LCD are done, all graphics are working perfectly and the text is also working, except the text background.

So, at this point I think I'm not using the last version of the library, or something is missing in somewhere, or I'm lost in all these thousands of lines... :confused:

With the code I got from GitHub, I was checking...

At the start:
Code:
font = NULL;

Then we set the font:
Code:
tft.setFont(Arial_18);
tft.setTextColor(ILI9341_YELLOW);

And we print something:
Code:
tft.setCursor(20, 20);
tft.print("Test");

The function print(), calls the function write() for each character.
Inside the function write() (https://github.com/PaulStoffregen/I...4c166ac628b38cc0b558f0e8/ILI9341_t3.cpp#L1284) there is a test that checks if the variable font is set or not, if yes, then it calls the function drawFontChar() which is the function that prints the custom fonts, otherwise, the function drawChar() is called, which is responsible to print the standard 5x8 font.

The function drawChar(), for the regular 5x8 font, can use background colors and transparent mode (https://github.com/PaulStoffregen/I...4c166ac628b38cc0b558f0e8/ILI9341_t3.cpp#L1312).

But the function drawFontChar() for custom fonts does not use background color at all (https://github.com/PaulStoffregen/I...4c166ac628b38cc0b558f0e8/ILI9341_t3.cpp#L1472).

I'm afraid to change something in that code because, maybe it's not the last version and I can loose time working on something already fixed, and that code is not simple to change it in just a few hours.

Can you please, take a look on that, just to make sure if my reasoning is correct.

Thanks again.
 
Hello,
I am trying to figure out what the length of pixels are being written on the display. I am working on a gauge that creates tick marks based off degrees and write the degree values at each tick mark. The problem is that I need to find the center of each of these numbers so they can be offset in the X coord's. any help would be great. here is the code that I am working with.

////////////////////////////////////////// Includes Information //////////////////////////////////////////
#include "SPI.h"
#include "font_Arial.h"
#include "ILI9341_t3.h"

////////////////////////////////////////// Connections Gauge 1 //////////////////////////////////////////
#define TFT1_DC 9
#define TFT1_CS 10
#define TFT1_RST 255 // 255 = unused, connect to 3.3V
#define TFT1_MOSI 11
#define TFT1_SCLK 13
#define TFT1_MISO 12
ILI9341_t3 tft1 = ILI9341_t3(TFT1_CS, TFT1_DC, TFT1_RST, TFT1_MOSI, TFT1_SCLK, TFT1_MISO);

////////////////////////////////////////// int variables //////////////////////////////////////////
int TFT_Reset = 24; // tft reset pin
int StartDeg = 45; // gauge start degrees
int EndDeg = 315; // gauge end degrees
int Ltickdegrees = 45; // long tick mark degrees
int line_OD = 120; //OD of outside radius in pixels
int tll = 15; //length of the large tick marks
int longtickcolorS = ILI9341_WHITE; // color of long tick mark
int textcolorS = ILI9341_WHITE; // color of gauge text values
int textcolorG = ILI9341_BLUE; // color of gauge name text
int Xstart = 160; // start of X line start in pixels
int Ystart = 120; // start of Y line start in pixels
int gts = 2; // gauge name text size
int ts = 2; //gauge values text size
String gname = "GAUGE"; //gauge name



void setup() {

//////////////////////////////////////////////////////////////////// Display 1 Start up ////////////////////////////
pinMode(TFT_Reset, OUTPUT);
digitalWrite(TFT_Reset, HIGH);
delay(500);
tft1.begin();
tft1.setRotation(1);
tft1.fillScreen(ILI9341_BLACK);

//////////////////////////////////////////////////////////////////// Draw numbers or names on degrees ////////////////////////////
for (int i = StartDeg; i < (EndDeg + 1); i += Ltickdegrees)
{
// start drawing the gauge tick marks
float sx = cos((i - 270) * 0.0174532925); // Coodinates of X tick line to draw
float sy = sin((i - 270) * 0.0174532925); // Coodinates of Y tick line to draw
float x0 = sx * (line_OD - tll) + Xstart; // tick mark start in pixels
float y0 = sy * (line_OD - tll) + Ystart; // tick mark start in pixels
float x1 = sx * line_OD + Xstart; // tick mark end in pixels
float y1 = sy * line_OD + Ystart; // tick mark end in pixels
tft1.drawLine(x0, y0, x1, y1, longtickcolorS); // Draw tick marks
// start drawing the gauge numbers
float x2 = sx * ((line_OD - tll)- 25) + Xstart; // text start in pixels
float y2 = sy * ((line_OD - tll)- 25) + Ystart; // text mark start in pixels
tft1.setTextColor(textcolorS); // standard tick mark colors
tft1.setTextSize(ts); // set gauge number text size
tft1.setCursor((x2-10), y2-10); // place cursor for writing text
tft1.println(i-45); // -45 is the offset for the displayed value of tick mark
delay(250); // delay to slow disply startup drawing
}

//////////////////////////////////////////////////////////////////// Draw Gauge Name ////////////////////////////
tft1.setTextColor(textcolorG); // Gauge text color
tft1.setTextSize(gts); // gauge text size
int length = gname.length(); // place gauge "string" name into variable
length = ((length * 6)*gts); //length of text in pixels
length = length/2; //divide the length of pixels by two to find center offset value
tft1.setCursor((Xstart - length), 200); //place cursor for writing text
tft1.print(gname); //print gauge name
}

void loop()
{
}
 
Did you try calling measureTextWidth() / measureTextHeight() to retrieve the bounds of the rendered text?
 
thebigg,
I did try. But I was not understanding how to use the call. could you help with an example of how to use the call
 
Code:
////////////////////////////////////////// Includes Information //////////////////////////////////////////
#include "SPI.h"
#include "font_Arial.h"
#include "ILI9341_t3.h"

////////////////////////////////////////// Connections Gauge 1 //////////////////////////////////////////
#define TFT1_DC 9
#define TFT1_CS 10
#define TFT1_RST 255 // 255 = unused, connect to 3.3V
#define TFT1_MOSI 11
#define TFT1_SCLK 13
#define TFT1_MISO 12
ILI9341_t3 tft1 = ILI9341_t3(TFT1_CS, TFT1_DC, TFT1_RST, TFT1_MOSI, TFT1_SCLK, TFT1_MISO);

////////////////////////////////////////// int variables //////////////////////////////////////////
int TFT_Reset = 24; // tft reset pin
int StartDeg = 45; // gauge start degrees
int EndDeg = 315; // gauge end degrees
int Ltickdegrees = 45; // long tick mark degrees
int line_OD = 120; //OD of outside radius in pixels
int tll = 15; //length of the large tick marks
int longtickcolorS = ILI9341_WHITE; // color of long tick mark
int textcolorS = ILI9341_WHITE; // color of gauge text values
int textcolorG = ILI9341_BLUE; // color of gauge name text
int Xstart = 160; // start of X line start in pixels
int Ystart = 120; // start of Y line start in pixels
int gts = 2; // gauge name text size
int ts = 2; //gauge values text size
String gname = "GAUGE"; //gauge name



void setup() {

	//////////////////////////////////////////////////////////////////// Display 1 Start up ////////////////////////////
	pinMode(TFT_Reset, OUTPUT);
	digitalWrite(TFT_Reset, HIGH);
	delay(500);
	tft1.begin();
	tft1.setRotation(1);
	tft1.fillScreen(ILI9341_BLACK);

	//////////////////////////////////////////////////////////////////// Draw numbers or names on degrees ////////////////////////////
	for (int i = StartDeg; i < (EndDeg + 1); i += Ltickdegrees)
	{
		// start drawing the gauge tick marks
		float sx = cos((i - 270) * 0.0174532925); // Coodinates of X tick line to draw
		float sy = sin((i - 270) * 0.0174532925); // Coodinates of Y tick line to draw
		float x0 = sx * (line_OD - tll) + Xstart; // tick mark start in pixels
		float y0 = sy * (line_OD - tll) + Ystart; // tick mark start in pixels
		float x1 = sx * line_OD + Xstart; // tick mark end in pixels
		float y1 = sy * line_OD + Ystart; // tick mark end in pixels
		tft1.drawLine(x0, y0, x1, y1, longtickcolorS); // Draw tick marks
		// start drawing the gauge numbers
		float x2 = sx * ((line_OD - tll) - 25) + Xstart; // text start in pixels
		float y2 = sy * ((line_OD - tll) - 25) + Ystart; // text mark start in pixels
		tft1.setTextColor(textcolorS); // standard tick mark colors
		tft1.setTextSize(ts); // set gauge number text size
		tft1.setCursor((x2 - 10), y2 - 10); // place cursor for writing text
		tft1.println(i - 45); // -45 is the offset for the displayed value of tick mark
		delay(250); // delay to slow disply startup drawing
	}

	//////////////////////////////////////////////////////////////////// Draw Gauge Name ////////////////////////////
	tft1.setTextColor(textcolorG); // Gauge text color
	tft1.setTextSize(gts); // gauge text size
	int length = gname.length(); // place gauge "string" name into variable
	length = ((length * 6) * gts); //length of text in pixels
	length = length / 2; //divide the length of pixels by two to find center offset value
	tft1.setCursor((Xstart - length), 200); //place cursor for writing text
	tft1.print(gname); //print gauge name
}

void loop()
{
}
Can you please enclose any posted code between CODE tags using the # button at the top of the post form.
It presents your work in a much more readable and understandable manner making it much easier for people to help you.
 
Note: there is additional stuff in my version of the library: https://github.com/KurtE/ILI9341_t3n

The code has the concept of center,
Where you can output a string and have it centered at the center of the screen:
or specific X and center y, or center x and specific y...

Also it has an extra parameter on setCursor which says center the text here:

like:
Code:
  tft.setCursor(width / 2, height - 30, true);
  tft.print("Center");
The true says to center. Note the centering only works for one output, after that the text cursor will be at the end of that output.

The example ILI_Ada_fontTest4 in my library shows exampls of this in function: displayStuff1

Note: Several of the display libraries that ship with Teensydunio I believe we added this stuff... But I don't think it made it back into the ILI9341_t3 library.

But to do it manual:
With ILI9341_t3, I believe the functions are as thebigg mentioned:
Did you try calling measureTextWidth() / measureTextHeight() to retrieve the bounds of the rendered text?
If you want the text centered at some location: x, y
then something like:

int text_width = tft.measureTextWidth("45");
int text_height = tft.measureTextHeight("45");
tft.setCursor(x - (text_width/2), y - (text_height / 2));
tft.print("45");

Assuming you want it centered at that spot... May have to fudge

With ILI9341_t3n and some of the others we have:
Code:
  void getTextBounds(const uint8_t *buffer, uint16_t len, int16_t x, int16_t y,
                     int16_t *x1, int16_t *y1, uint16_t *w, uint16_t *h);
  void getTextBounds(const char *string, int16_t x, int16_t y, int16_t *x1,
                     int16_t *y1, uint16_t *w, uint16_t *h);
  void getTextBounds(const String &str, int16_t x, int16_t y, int16_t *x1,
                     int16_t *y1, uint16_t *w, uint16_t *h);
  int16_t strPixelLen(const char *str, uint16_t cb=0xffff);  // optional number of characters...
Looks like the only example showing that is in the Kurts_ILI9341_t3n_FB_and_clip_test.ino.
Or like in the main libary .cpp file and see how it is used in the Centering code mentioned above.

The idea of these functions, is you pass in your string as well as where maybe you would be drawing it, or 0, 0 if you will compute that from this data.
And you pass in pointers to x, y, w, h to retrieve the bounding rectangle for where the text would output. Note, there are some characters in some fonts, that
may bias the text t0 actually output some pixels offset in the negative direction...
 
Hey everyone, since i´m an absolute noob when it gets to programming, i´m wondering if there was a super easy example for a DMA approach?!
I dont even know if its possible since i could not find an example for my needs :D
What i would like to have is
one framebuffer to work with (dont know if thats possible)
and a simple sketch, where i dont know, the display shows the integers from 0-9 or so, so really super simple
Maybe someone can explain, when to update the screen or other important details i have to look at

Since i´m completly out of any insights to DMA i really would love to get some tipps from you :)

TIA Stefan
 
Hello Kurt,

thank you for your answer. Yes i know your libs, but somehow i was not able to find an easy example how to use one dma framebuffer.
Maybe you can point to an example you´ve done, that would be very cool
danke aus wien
cheers stefan
 
I am using ILI9488_t3.h and it works.

However tft.print(); wont delete the old value.
ChatGTP always provides examples with tft.fillScreen(BACKGROUND_COLOR); to delete the old value but what would be a more elegant way to show e.g an ADC value or other fast changing values?
Filling the background also makes the TFT flickering.
 
There are several ways to do it.
With out flickering:

1) You can use the frame buffer.
With this you could erase the whole screen, draw everything and then do an updateScreen() call which will then do a redraw of the whole screen...

2) Use Opaque text output, this will overwrite the things in the text rectangle that are not in the character with the selected background color.

That is something like:

Code:
tft.setTextColor(WHITE, BLACK); // set the text color to white and the Background to black. 
tft.setCursor(20, 20); // set the cursor
tft.print(val, DEC);
Warning, typing on the fly and not worrying about being fully correct, like WHITE or BLACK need to be whatever the colors are for the display you are using...

Now this should in most cases cover up the bits and pieces of the characters it overwrote.

But if for example your Analog value was 325 and dropped to 50, the code above would only overwrite the 32 and not the 5..

A couple ways to handle this.

a) Cheat, if nothing is going to display directly to the right of this, simply add something like:
Code:
tft.print("   ");
After the tft.print of the value. and hopefully cover up everything from previous output, without flicker.

b) blank out after text output, until end of field..

Code:
uint16_t cx = tft.getCursorX();
fillRect(cx, 20, FIELD_SIZE - cx, 10, BLACK); // might have to play with these values, like the 10 for height, depends on font size...
c) like b, except remember the last cx_ and only blank from new cx to last cx...
Code:
uint16_t last_cx = 0;
...
uint16_t cx = tft.getCursorX();

if (cx < last_cx) fillRect(cx, 20, last_cx - cx, 10, BLACK); // might have to play with these values, like the 10 for height, depends on font size...
last_cx = cx;

Probably lots of other ways as well.
 
Hello
thank you for your answers, i know the trick of writing the old value with the background color, but since i have to draw a lot of different stuff, i think framebuffer will do the job best for me.

the question is, is there an easy to follow example for one framebuffer or do i have to use 2 buffers?
 
I used the framebuffer option with tft.updateScreenAsync(false); and It works fine. Thanks for your help!

However if I try to update the screen with a higher rate than ~7hz horizontal lines appear on the screen. Is this normal or a limitation by the LCD hardware?
 
Hi everyone,
I notice the fonts posted in https://github.com/PaulStoffregen/ILI9341_fonts do not have accents, so, is there a link or someone knows the procedure to convert TTF fontos to that format? I spent hours searching for it, but no success.
Hey @PaulStoffregen, any chance you wrote instructions for that?
Thanks in advance!
 
Last edited:
Back
Top