Highly optimized ILI9341 (320x240 TFT color display) library

BBoyes,

The way to eliminate the flicker and tearing you're observing is to use a buffer in SRAM, make all the "onscreen" changes to that, then when you're done making all the changes you want to see on the screen, write the contents of the buffer to the ILI9341's memory using DMA. FrankB provides a great starting point/example of this here: https://forum.pjrc.com/threads/3670...een-DMA-Buffer-for-Teensy-3-5-Teensy-3-6-only

Frank's example can be adapted to a variety of displays and resolutions and you'll be surprised at the performance enhancement the buffer/DMA approach will yield.
 
Last edited:
I recently found (by accident) that you could run this library on the larger Adafruit 3.5'' TFT (using the HX8357). The instruction sets are similar enough that it essentially worked out of the box.

So, I launched a port of this library for the HX8357D and B (copying in the correct initialization routines and commands from the Adafruit HX8357 library). Runs about 3x as fast for most functions on my Teensy 3.5

Code:
/* This Library (on Teensy 3.5 120Mhz, Fast optimization) */
HX8357 Test!
Display Power Mode: 0xCE
MADCTL Mode: 0xE0
Pixel Format: 0x2
Image Format: 0x0
Self Diagnostic: 0xE0
Benchmark                Time (microseconds)
Screen fill              449409
Text                     109286
Lines                    117080
Horiz/Vert Lines         36600
Rectangles (outline)     20353
Rectangles (filled)      1087322
Circles (filled)         147947
Circles (outline)        120168
Triangles (outline)      25393
Triangles (filled)       356333
Rounded rects (outline)  49729
Rounded rects (filled)   1183395
Done!

/* Standard Adafruit_HX8357 Library (on Teensy 3.5 120Mhz, Fast Optimization) */
Display Power Mode: 0xCE
MADCTL Mode: 0xE0
Pixel Format: 0x2
Image Format: 0x0
Self Diagnostic: 0xE0
Benchmark                Time (microseconds)
Screen fill 		     N/A
Text                     407743
Lines                    1352147
Rectangles (outline)     63736
Rectangles (filled)      N/A
Circles (filled)         N/A
Circles (outline)        579367
Triangles (outline)      N/A
Triangles (filled)       1117238
Rounded rects (outline)  150835
Rounded rects (filled)   2447902
Done!

Here's the link: https://github.com/Asaurus1/HX8357_t3.

One thing, Paul. I want to allow the use of the ILI9341_fonts library without having to fork that as well and rename everything to HX8357_font...etc. Since your header files #include the original "ILI9341.h" file, tho, I'm not sure of an elegant way to do this. Any thoughts?

EDIT: Tried making the fonts work. Biggest issue is as soon as you include ili9341_t3.h, Arduino tries to compile everything in that library's folder as well, so you get multiple definitions of the GFX and other such libraries. There's really no good way around this that I can see, unless the fonts library were to be redefined as a "T3" library with its own header file.
 
Last edited:
Hi
Please help me to make this code work

#include <SPI.h>
#include <ILI9341_t3.h>
#include <font_Michroma.h>

#define TFT_DC 9
#define TFT_CS 10

ILI9341_t3 tft = ILI9341_t3(TFT_CS, TFT_DC);
float v = 12345;

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

Serial.begin(9600);

//tft.fillScreen(ILI9341_BLACK);


tft.setFont(Michroma_32);

}

void loop(void) {
v += 0.001;


//tft.fillRect(0, 0, 310, 60, ILI9341_BLACK);
tft.setCursor(20, 20);
tft.setTextColor(ILI9341_YELLOW);
tft.print(v);
tft.setCursor(20, 70);
tft.setTextColor(ILI9341_YELLOW);
tft.print("Volt");

}

I just want to display "Float" with this Library
Thank you
 
Might help to know what your current code is doing and what it is not doing. i.e. what is wrong with your current code. Is it that the current code outputs your floating point number with the old bits just writing over previous value, ending up soon with almost complete yellow blob? Is it not outputting anything?

Obviously you can un-comment your fillrect statement which would fix this at the cost of a lot of flashing. Also siince there are no delays between the calls, it will blank it out right after it outputs it.

I would suggest using Opaque text, with the calls like: tft.setTextColor(ILI9341_YELLOW, ILI9341_BLACK);
However I don't think the current version of this library supports this in the drawFontChar. I have a version of it in my ili9341_t3n library (github kurte) that supports it.

But that still may not be sufficient. As you could get values output like: 12345.0 12345.001 ... Where the sizes of the output text may not be the same, and so the new output, may not fully overwrite the previous one. Again few ways to fix. Could use something to format the floating point number to some fixed size, like maybe something like sprintf, or you could get the text cursor X after each call and if it is less than previous call, do a fillrect to background color for that area not covered, or...

Hope that helps.

P.S. - To output the word float: you could just do tft.print("Float"); :lol: ;)

Note: it helps to read your code if you use the code tag (with that new shiny # button on the quick reply
 
Hi
Thank you for your fast reply.
When i'm compiling
Kurts_ILI9341_t3n_FB_and_clip_tests on Teensy 3.2

i'm getting this error

no matching function for call to 'Adafruit_GFX_Button::initButton(ILI9341_t3n*, int, int, int, int, int, int, int, const char [3], int)'

Please help
 
I updated the test program to use the right font file names. Other than that it compiled for T3.6... Will look at 3.2, but this program is mainly setup for Frame buffer and only 3.6 has enough memory to work...
 
You might try the test program I use with Teensy 3.2 and Paul's adapter. I have the CS changed so it works with Ethernet too but you can undo those changes. It's similar to Paul's sample but I added more output to show the variation in x y and z over 5 seconds, to help me pick practical touch zone sizes (finger is not so precise as a stylus, so too-small buttons won't be selected reliably).
https://github.com/systronix/W5500_Test
and specifically you want the ILI9341_Test program. It works with both PJRC and eBay 2.8" screens and one from BuyDisplay. All with Teensy 3.2.
 
Thank you !!!!!!!
It's working perfect, NO flicker
But there is one Big problem for me, as soon as i'm adding the fonts, what i need to use, i'm getting this error: conflicting declaration 'typedef struct ILI9341_t3_font_t ILI9341_t3_font_t'
The fonts i want to add are GOODTIME.h and Michroma.h

One more thing, the rectangle is sitting on top of the digits perfectly on Left, Right and on Top, but on the bottom, the is big gap, please see the picture
you'll know what I'm talking about, i hop you can make all 4 sides to be same and the minimum.
Thank you, Thank you again
IMG_7424.JPG
 
The other font files are including ili9341_t3.h file for the font definition, and will bring in other stuff, so edit to name of this library...

Bottom and right spacing is setup to go to next row and column, one way to fix is to use clipping rectangle set to only the area you want to draw in.
 
Hi
I'm having hard time to add this fonts (GOODTIME.h and Michroma.h)
Please give me more detailed explanation
Thank you
 
Hi,
just a short question: is it possible to draw thicker lines on the display. As a workaround I use "tft.drawRect()" instead of "tft.drawLine()", but that might not be the best solution.
Thanks.
Peter
 
A line is a pixel high - draw multiple lines as needed - or call fillRect() that does that with a bit less overhead in param checking and SPI commands.
 
Hi
I'm having hard time to add this fonts (GOODTIME.h and Michroma.h)
Please give me more detailed explanation
Thank you

Sorry I don't know these files so first would need to see them.

But for example Paul's github project: ili9341_fonts has the file font_Michroma.h and .c

If it were me, I would copy those two files to a new directory, also under libraries.
maybe something like: ili9341_t3n_fonts and give the header file and maybe the .c file a new name like: ili9341_t3n_font_Michroma.h as to not have the Arduino preprocessor grab the unmodified version.

I would then edit the .h file and change it like:
Code:
#ifndef _ILI9341_t3_font_Michroma_
#define _ILI9341_t3_font_Michroma_

#include "ILI9341_t3.h"

#ifdef __cplusplus
extern "C" {
#endif

extern const ILI9341_t3_font_t Michroma_8;
extern const ILI9341_t3_font_t Michroma_9;
extern const ILI9341_t3_font_t Michroma_10;
extern const ILI9341_t3_font_t Michroma_11;
...

To the different header file name
Code:
#ifndef _ILI9341_t3_font_Michroma_
#define _ILI9341_t3_font_Michroma_

#include "ILI9341_t3n.h"

#ifdef __cplusplus
extern "C" {
#endif

extern const ILI9341_t3_font_t Michroma_8;
extern const ILI9341_t3_font_t Michroma_9;
extern const ILI9341_t3_font_t Michroma_10;
extern const ILI9341_t3_font_t Michroma_11;

Would then change the .c file to the new name of the header file.
Code:
#include "font_Michroma.h"

static const unsigned char Michroma_8_data[] = {
0x00,0x00,0x04,0x01,0x94,0x05,0x98,0x20,0x03,0x25,
0xC9,0x14,0x08,0x93,0xD1,0x02,0x47,0xF8,0x24,0x7F,
...

to
Code:
#include "ili9341_t3n_font_Michroma.h"

static const unsigned char Michroma_8_data[] = {
0x00,0x00,0x04,0x01,0x94,0x05,0x98,0x20,0x03,0x25,
0xC9,0x14,0x08,0x93,0xD1,0x02,0x47,0xF8,0x24,0x7F,
...
 
Thank you
I'm still trying to import this library (Michroma), and i ask friend of my for help, and mean time can you please
see if impossible to make the bottom part of the rectangle smallest possible?
I'm loosing lot of space on the screen because of that.
Thank you again
 
As mentioned, That spacing at the bottom is dictated by the font. In particular it handles cases like:
Code:
tft.setTextColor(ILI9341_YELLOW, ILI9341_BLACK);
tft.println("Text1");
tft.println("line 2");
So that all of the spacing between the two lines of text will be drawn in the background color.

But there are things built in to allow you to control some of this. That is to use a clipping rectangle.
Like in your previous code, there was something like:
Code:
//tft.fillRect(0, 0, 310, 60, ILI9341_BLACK);
tft.setCursor(20, 20);
tft.setTextColor(ILI9341_YELLOW);
tft.print(v);
tft.setCursor(20, 70);
tft.setTextColor(ILI9341_YELLOW);
tft.print("Volt");
You could turn on the clipping just below where the text draws... Again I don't know this font or the like but it might be something like:
Code:
tft.setClipRect(0, 0, tft.width(), 60, ILI9341_BLACK);  // Set clipping rectangle,  so nothing draws below pixel 60...
tft.setCursor(20, 20);
tft.setTextColor(ILI9341_YELLOW);
tft.print(v);
tft.setCursor(20, 70);
tft.setTextColor(ILI9341_YELLOW);
tft.print("Volt");
tft.setClipRect();   // clear out the clipping
Again you will need to experiment with where to set the rectangle for clipping, but does allow you control where things are drawn.
 
120ms delay on startup

Hi, I can't understand this delay.
After the SLEEP-OUT command is sent, the ILI9341 need up to 5ms to be active:
https://www.newhavendisplay.com/app_notes/ILI9341.pdf - page 101

In the Adafruit library, they send
SLEEP-OUT
delay(120)
DISPLAY-ON
delay(120)​

In the T3 library, I see:
SLEEP-OUT
delay(120)
DISPLAY-ON

But if I understand it correctly, 120ms is the time you need to weak it up, only if you sent the SLEEP-IN command. On startup you don't need this long delay.

I was trying with a delay of 5ms and it works, but maybe this is not a good idea if I understood it wrong (from datasheet).

Another way, maybe, is to read the powermode after 5ms and see if it need more time to sleep out:
Code:
...
writecommand_last(ILI9341_SLPOUT);    // Exit Sleep
SPI.endTransaction();

// delay(120);
delay(10);
if((readcommand8(ILI9341_RDMODE) & 0x10) == 0x00) delay(110); // still in sleep in mode

SPI.beginTransaction(SPISettings(SPICLOCK, MSBFIRST, SPI_MODE0));
writecommand_last(ILI9341_DISPON);    // Display on
SPI.endTransaction();
...

I know, begin is executed only 1 time, but in a "fast" library also a fast startup is a nice feature :)

I am only asking because it works on my ILI9341 display and maybe this 120ms delay is not really necessary.
 
Last edited:
Hi I have some noob questions.

Q1: What is the purpose of the 100ohm resistor for the LED pin on the pjrc store page? Do I damage the display if I direct connect the LED to VIN supply? Is the LED pin a DC power supply input? Or is this a high impedance voltage controlled input to control brightness / dimming.

Q2: I already have a PCB design with only 4 free IO channels using Teensy 3.2. Unfortunately none of the free IO pins are the dedicated SPI pins probably needed for SPI. Will the display library allow using any generic IO pin at a slower speed? I'm guessing no, but I thought I would ask.

-Noob out.
 
Last edited:
Hi, I can't understand this delay.
After the SLEEP-OUT command is sent, the ILI9341 need up to 5ms to be active:
https://www.newhavendisplay.com/app_notes/ILI9341.pdf - page 101.

Trust the data sheet. I've seen a lot of things in libraries not supported by the data sheet. Hopefully the manufacturer knows how to use their parts and the people writing the libraries read the data sheets. This seems to not always happen.

Here's another for you, but in the opposite direction. Your link to the ILI9341 data sheet, page 243 shows the minimum SCL period to be 100 nsec write and 150 nsec read. This means SPI clock should be no faster than 10 MHz write and 6.6 MHz read. Yet you will find the default SPI clock for the ILI9341 library set at (IIRC) 24 MHz! It usually works, at room temperature, etc. Apparently this part is very conservatively rated. We run our SPI clock at 10 MHz, but that's my hardware design training.
 
The LED pin is providing power to the LED. The resistor is a current-limiting resistor. If we assume a 3V forward voltage on the LED and a 5V power supply, a 100Ω resistor limits the current to 20mA. (Note: I've not measured the forward voltage on the ILI9341, so this estimate is only a ballpark figure.)

The most common set of pins to use for SPI on the Teensy 3.2 are 10–13, but there are a few other options. You need to look at the reference manual for the processor and the pin mappings available. (Section 10.3.1 K20 Signal Multiplexing and Pin Assignments) You'll also need to know the mapping between the Teensy pin numbers and the ports on the chip (on the schematic for the board).
 
Backlight current limit resistor

Hi I have some noob questions.

Q1: What is the purpose of the 100ohm resistor for the LED pin on the pjrc store page? Do I damage the display if I direct connect the LED to VIN supply? Is the LED pin a DC power supply input?

It is the input to the white backlight LEDs on the module, and "it depends". The best schematic I could find for these modules shows the LED anode (positive voltage) and 3 different LED cathodes on the hotbar-soldered 18 pin LCD connector. Only the anode is provided on the module 14-pin header. The TJC-024-9341 data sheet version A, H24TM84A, says LED current is 60 mA typ and LED forward voltage is 3.0 min, 3.2 typical, 3.3 max. So you want to run the backlight on 5V. Assuming no on-board LED resistors, 5-3.0 = 2.0V you need to drop in the worst case where LEDs only drop 3V. 2V/60 mA = 33 ohms. IIRC we used 39 since we had some. This makes the display noticeably more bright and colors more saturated. 39 ohms is somewhat conservative and shouldn't overdrive the LEDs if my math and assumptions are correct. Try it and you may like how the display looks with 3X more backlight drive.

Incidentally, 60 mA into 3 LEDs is 20 mA per LED which is a pretty typical value for a small white LED. So the number makes sense. I took a broken display apart some while ago but now I can't remember any more details.

I'll post documentation referred to here in a separate reply once it's online.

Some small LCD modules already have a backlight current limit resistor installed so be on the watch for that in the general solution. These don't seem to, so yes, you will damage them by directly connecting LED to 5V.
 
Last edited:
Links to datasheets for ILI9341, LCD, and typical LCD module

Here are documents for the ILI9341 module, LCD itself, and v1.1 of the ILI9341 data sheet, in our repo ILI9431_t3 and here's a shortcut to the documentation folder itself.

Our repo is a fork of Paul's with a few small things added to slow the SPI clock to comply with the data sheet, and also fix a FIFO issue which caused read errors (more on that later once I get my head around it better). After some more testing we will submit a pull request to Paul.
 
Last edited:
Hi all!

I have a problem with the readRect() and readPixel() functions. Within my project I have something like this piece of code:

Code:
	ILI9341_t3 tft = ILI9341_t3(TFT_CS, TFT_DC);

	int16_t x = 0; int16_t y = 0; 
	int16_t w = 10; int16_t h = 10; 
	uint16_t *pcolors;
	pcolors = (uint16_t*)malloc( sizeof(uint16_t) * w * h );

	//tft.fillRect(x, y, w ,h,ILI9341_RED);
	tft.fillRect(x, y, w ,h,ILI9341_GREEN);
	//tft.fillRect(x, y, w ,h,ILI9341_BLUE);

	tft.readRect(x, y, w ,h, pcolors);

	tft.fillRect(x,y,w,h, ILI9341_BLACK );

	tft.writeRect(x,y,w,h,pcolors);

Basically I want to backup an area of the screen, put something else there, and later restore the original state.

My setup is:
- Teensy 3.2
- a 320x240 ILI9341 TFT LCD display (no touch)
- Teensy runs @72MHz default speed.​

What I have tried:

- played with SPI_CLOCK speeds:
- different for read and write
- same for read and write
- several speeds from 1MHz to 36MHz​

- pcolors with pre-allocation and without

- used readRect() and used own function iterating through readPixel()

- ILI9341_t3 library from
- PaulStoffregen
- KurtE
- systronix​

- colored the area in question in RED, GREEN or BLUE (to see if at least some bytes are read out correctly)​


Results:
- At no point did it work as intended.
- Very seldomly the area got refilled white (or better greyish)
- A few times randomly colored pixel appeared.
- But most of the time there was just blackness.​

I did a Serial.printf() of the r,g,b-values within the readRect()- and readPixel()-function and they just stay at zero. (Or a few at small random values in the "colored pixel"-cases)


After a lot of googeling and several days of testing I have decided I'm too stupid and I have to ask someone who is more experienced in this matter. Which I hereby do:

Can someone please have a look and help?

Has someone tried readRect()/writeRect() with Teensy3.2 successfully? If so - with what code and library and speed settings?
 
Last edited:
After a lot of googeling and several days of testing I have decided I'm too stupid and I have to ask someone who is more experienced in this matter. Which I hereby do:

Can someone please have a look and help?

Has someone tried readRect()/writeRect() with Teensy3.2 successfully? If so - with what code and library and speed settings?

I've had plenty of success with doing this, but initially spent a lot of time troubleshooting some odd colour issues. (I'm using Teensy 3.6, but it's unlikely to be related to your version of Teensy)

The colour issues were caused by SPI speed during read. Reducing it helped my display keep up with outputting serial data. Higher speed caused colour drifts as the values read back from the display were inconsistent.

Then I tried another display (higher density) and have not been able to make it work at all yet. I used an oscilloscope to look at the MISO waveform, and it's too low and noisy for Teensy to register the values at any speed. I'm going to try a custom PCB to see if that cleans up the signal enough to be serviceable. Otherwise I'll need to abandon the display and find another.

Double check you're using the correct MISO pin in hardware and software. If you're using a breadboard, try another one. I've had other projects with intermittent signal problems because the contacts were damaged from using pin headers instead of flip-pins (see https://www.tindie.com/products/OSHChip/flip-pins-for-teensy-35-or-36-4-sets/). Keep that connection short and secure.

If possible, check the waveform on an oscilloscope to confirm it meets the required logic levels for Teensy. Chase down that before spending much more time trying different code and/or clock speeds.
 
Back
Top