Highly optimized ILI9341 (320x240 TFT color display) library

I have sent Projectitis a PM of my problem.

I am trying to convert some fonts as per your instructions on PACKEDBDF. I converted a google font to bdf type in various sizes. I then ran ran the python program which found one of the bdf files. As you can see below I got an error, do you know what I have done wrong?

elli@Elliots-MacBook-Pro Goldman % python3 bdf_to_h.py
Processing Goldman-Regular-36@2.bdf (bdf)
FAMILY_NAME:Goldman, SIZE:35, 2bpp
Traceback (most recent call last):
File "/Users/elliotjanssens/Downloads/Goldman/bdf_to_h.py", line 296, in <module>
if encoding != (encoding_end2+1): raise Exception('ENCODING more than 2 encoding ranges ('+encoding_start1+'-'+encoding_end1+','+encoding_start2+','+encoding_end2+'), at line '+str(linenum))
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~
TypeError: can only concatenate str (not "int") to str
elli@Elliots-MacBook-Pro Goldman %

Thanks in advance, I really appreciate the aafonts as they make such a massive difference. I also appreciate the all the work, time and help from the members of this forum.
 
I got a reply and Projectitis pointed out what I had done wrong. I have now converted the font in various sizes into antialiased fonts that will work with ILI9341_t3n.

You’ve run into a limitation of the original font format that I based mine on. Your font can only have a maximum of 2 ‘runs’ of characters with no gaps in between. It’s usual to have just one run - the characters 32 to 126.

if there is a glyph missing - for example glyph 99 - then you have 2 runs: 32 - 98, and 100 - 126. This is allowed. But as soon as you have more than 2 runs you’ll get the error.

Maybe you’ve forgotten to detach and remove unused glyphs outside of the range 32 to 126 and/or forgotten to compact the character set.

Basically, this format is intended for small/compact character sets :)
 
I have two different TFT ILI9341 touch displays, running on a teensy 4.0 using the ILI9341_t3n library.
The smaller 2.8 display runs exactly as expected and it's back lit correctly.
The larger 3.2 display runs ok, but it's like the back light is too bright?

I'm not sure where to start looking to figure this out. Anyone dealt with this before or have any pointers for things I can try?
Here is a short clip to show you what it looks like:

Thanks!
 
Last edited:
I have two different TFT ILI9341 touch displays, running on a teensy 4.0 using the ILI9341_t3n library.
The smaller 2.8 display runs exactly as expected and it's back lit correctly.
The larger 3.2 display runs ok, but it's like the back light is too bright?

I'm not sure where to start looking to figure this out. Anyone dealt with this before or have any pointers for things I can try?
Here is a short clip to show you what it looks like:

Thanks!
How do you have the backlight hooked up? Like what they show on product page

could up the ohm of resisto. Could hook that pin to io pin and control using pwm…
 
How do you have the backlight hooked up? Like what they show on product page

could up the ohm of resisto. Could hook that pin to io pin and control using pwm…
Thanks Kurt.
The LED pin is conn to VIN thru a 100Ω resistor, which works fine on the smaller display. The first thing I tried was upping the resistor to 100k, but that made no difference to either display. Tried conn to 3.3V - with and without the resistor - no change to either display.

I'll look into the pwm stuff and see how that goes.
 
I would like to share how my backlight is hooked up. I use a PC817 optocoupler to control the backlight. I was just using digitalWrite pin HIGH LOW to turn the backlight on and off via a 390R resistor to the optocoupler. But then I discovered by accident (I deleted the pinMode line) that you can control the current that the PC817 allows through. It took me 6 hours to work out my screen was dim because of the missing line. Now if you use a PWM analogWrite, an analog filter (see the PJRC PWM page) and a PC817 you can control turn the backlight on and off as well as control the brightness. The filter is just a 1K resistor and a 47uf capacitor. This is not new, I made an audio amplifier that uses optocouplers to control the current. I have 5v going into the optocoupler to the backlight. I really hope this is useful to someone.
 
I am having trouble using IRREMOTE with ILI9341_T4. When drawing to the frame buffer large fonts (80 pixels high) and receiving IR I have some sort of conflict which causes the teensy 4.1 to crash. I think because IRREMOTE uses an interrupt, it interrupts the screen update. Doing the exact same updates but using the touch screen as an input works fine and using the IR without updating the screen works fine too. My program checks the touchscreen every 50ms and the IR every 130ms. I have a counter I can use on the main loop() and it sits at about 565,000 and drops to about 492,000 when updating the screen so I think my code is efficient (the counter was at 18,000 before a bit of optimisation). Is there a way to schedule the tft update, the IRREMOTE or drawing to the frame buffer. I am not sure how the tft.update() works, I changed the SPI speed from 20 to 40mhz and the refresh to 200hz but that did not improve anything. Decreasing or increasing the frame rate does not make any difference. I tried starting and stopping the IR during screen updates but the IR did not receive any signals. I have not tried is combining the the IR read and tft update using a timer to sync the two and search for timings that don't cause a conflict. Any ideas would be much appreciated.
C-like:
void loop()
{
    ////////////////////////////////////////////////////////////
    // INSTANCES                                              //
    ////////////////////////////////////////////////////////////

    //  iMAINVIEW

    dc.imainview();
    if (dc.activeinstance != iMAINVIEW)
    {
        // iSTANDBY mode

        dc.istandby();

        // iMENUs

        dc.imenu();
        dc.imenuinput();
        dc.imenusetup();
        dc.imenuscreen();
        dc.imenucolors();
        dc.imenupreamp();
        dc.imenuir();
        dc.imenuir1();
        dc.imenuir2();

        // iSETs

        dc.isetfixed();
        dc.isetoffset();
        dc.isetinputname();
        dc.isetbrightness();
        dc.isetbacklight();
        dc.isetgainspeed();

        // iMISCs

        dc.iinfoview();
    }
    //////////////////////////////////////////////////////////////
    // TASKS                                                    //
    // housekeeping fuctions that run all the time              //
    //////////////////////////////////////////////////////////////

    dc.tasks();
    irread(); // sets dc.irresult to the currently pressed/received ir remote button/signal or to IDLE if not pressed/recognised
    displayfb();

    //  to monitor performance
    monitor();
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//  fuctions using IRremote.h
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

/// @brief This is used to set class myDisplayControl variable irresult to a CONTROLS enum using irrcommand
inline void irread()
{
    static uint32_t wait = millis();
    if (millis() - wait > 130) //////////////////////////////  CRITICAL - Below 125 does not seem to work! Don't know why; hate this
    {
        if (IrReceiver.decode())
        {
            if (IrReceiver.decodedIRData.flags & IRDATA_FLAGS_IS_REPEAT) // if repeat dont change ircomand
            {
                IrReceiver.decodedIRData.flags = 0;
                IrReceiver.resume();
            }
            else
            {
                IrReceiver.resume();
                IrReceiver.decodedIRData.flags = 0;
                if (IrReceiver.decodedIRData.command != 0)             // this beats an annoying conflict that happens when udating too much of the screen
                    dc.ircommand = IrReceiver.decodedIRData.command; // by ignoring the 0 command. Suprised it worked.
                dc.irtrigger = true;
                Serial.println(IrReceiver.decodedIRData.command);
            }
        }
        else
        {
            dc.ircommand = 0;
            dc.irtrigger = true;
            IrReceiver.resume();
        }

        wait = millis();
    }
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//  screen
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

inline void displayfb()
{
    static uint32_t fbtimer = millis();
    if (millis() - fbtimer > 16)
    {
        if (IrReceiver.isIdle())
            tft.update((uint16_t *)im.data());

        fbtimer = millis();
    }
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//  all the other fuctions
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

inline void monitor()
{
    static uint32_t loopcount;
    static uint32_t looptimer = 0;
    if (millis() - looptimer > 1000)
    {
        Serial.printf("Loops per second: %d\n", loopcount);
        loopcount = 0;
        looptimer = millis();
    }
    loopcount += 1;
}
 
inline void displayfb() should not have 'if (IrReceiver.isIdle())', that was a failed solution to my problem.
 
I have sort of solved my crashing teensy problem. My fbtimer is now at 1ms, this causes the screen to update almost as soon as the frame buffer has been drawn on. My volume ic has 255 steps so even stepping at 20ms it takes 5 seconds to get from min to max volume and this now dictates the frame rate. The problem is still there but changing the scheduling means it does not crash now. It still manages to do loop() between 300,000 and 400,000 times a second depending on if it is updating the screen.
C-like:
inline void displayfb()
{
    static uint32_t fbtimer = millis();
    if (millis() - fbtimer > 1)
    {
        tft.update((uint16_t *)im.data());
        fbtimer = millis();
    }
}
 
Going over and updating old GUI libraries. Running a Teensy 3.2 with Adafruit 1947 touch shield. I'm having issues with ILI9341_t3 and SD cards. I can't seem to get them to open .bmp files. Oddly, there are a couple it can open. Reading through this thread, I tried bumping the speed down from #define SPICLOCK 30000000
to
#define SPICLOCK 24000000
This helped a -little- bit. One more file was able to open on my test program. But still the problem persists. Was there ever any definite fix for this? I'm in over my head here.

Thanks!

-jim lee
 
Sorry, hard to know what is going on.
How is your display hooked up? In particular, to some circuit board where hopefully you have reasonably
short connections, or with breadboard and jumper wires?

Would help to see what code you are trying. For example what library you are using to read the SD. SD or SDFat or ...

Which IDE and version? and Teensy code...

How is the SD hooked up? Are you using the one on the display or another one?

I can't seem to get them to open .bmp files
By this do you mean that literally cannot open the file or it is not displaying the data on the screen?

Have you tried moving your SD card to a PC and doing a verification that the data on the disk is not corrupt. And that the files
exist.

Sorry I know, not much help
 
Try bumping SPI clock down to 10 MHz. I seem to recall that the SPI clock was set higher than the data sheet for many displays would support. But that memory may not be reliable. We made a buffered/differential display interface design specifically for a manufacturing test fixture where the display must be located a couple of feet from the unit under test. It works reliably over three+ feet of cable but if I recall we run SPI clock at 10 MHz. If I get a chance I will look this up. Now that I think about it, it may already be in our (systronix) Github code repo.
Bruce
 
@bboyes - Good idea... If the problem is with the display.

As I mentioned in previous post, could not tell if he is having the issue with the display or with the SD...

If it is the SD, then it may depend on which library he is using... But fix might be similar...
Although I believe by default: SD.begin(cs_pin) defaults to 16mhz which usually handles longer wires and the like.

But SD allows you to all the SDFat begins with different options. If you are doing that and/or using SDFat directly you should look at
your options. You get an idea of them by looking at the SD exaple SDFat_Usage.ino

For example you might have something like:
Code:
ok = SD.sdfs.begin(SdSpiConfig(chipSelect, SHARED_SPI, SD_SCK_MHZ(24)));
To run faster... If so try slower...

Or maybe you have a begin like:
Code:
ok = SD.sdfs.begin(SdSpiConfig(chipSelect, DEDICATED_SPI, SD_SCK_MHZ(16)));
Where you specified that the SPI is dedicated to this SD (or SdFat) object. That is no other devices are connected up
to it. If this is specified and you do have something else connected, like the display, this can cause things to fail.

But again just throwing 🎯
 
Try bumping SPI clock down to 10 MHz. I seem to recall that the SPI clock was set higher than the data sheet for many displays would support. But that memory may not be reliable. We made a buffered/differential display interface design specifically for a manufacturing test fixture where the display must be located a couple of feet from the unit under test. It works reliably over three+ feet of cable but if I recall we run SPI clock at 10 MHz. If I get a chance I will look this up. Now that I think about it, it may already be in our (systronix) Github code repo.
Bruce
I discovered with ST7789 OLED displays, I needed to use a 11Mhz SPI bus to prevent the display from being corrupted on some displays. I found of the displays I had, the Adafruit displays needed 11-12Mhz, while the Newhaven displays could run with 18Mhz. I was using a soldered prototype board with a pinout for jumper wires to connect the display. I tended to use 6" jumper wires. If there was anything else on the SPI bus, I tended to need pull-up resistors on each of the CS pins and maybe on the D/C pins.
 
wait! The display works fine. speedy quick! This is what I'm testing on.

shapeimage_2.png


inside there is a Teensy 3.2 soldered to an Adafruit 1947 cap touch screen "shield". The issue is that I can not open most .bmp files. I assume the hardware is fine, because I can swap back to the Adafruit drivers and everything is fine. Oh, (reading Kurt's post) using whatever SD came with teensyduino. Arduino 1.8.16 - teensyduino 1.55

C++:
if (!SD.begin(4)) {
   Serial.println("No SD card!");
}

Anyhow.. it's the actual ..

C++:
aFile = SD.open(filePath);

that is failing.

I didn't try anything to make it run faster. I -did- try slowing it down. And, as I said before, it worked a tiny bit better. One file I wasn't able to open before opend, but non of the others.

The SD is the one mounted on the Display they share the SPI bus. Each has his own chip select pin. In this case there is only the two things on the SPI bus that I'm aware of.


-jim lee
 
Been a long time since much done with the ili9341 - nad not using the SD adapter then - not sure it was seen in use/working back then?

TeensyDuino current is 1.59 - so lots of long tested updates since 1.55 - not sure if any SPI or other changes might change the usability?

Teensyduino 1.55 Released
Sep 15, 2021
 
inside there is a Teensy 3.2 soldered to an Adafruit 1947 cap touch screen "shield". The issue is that I can not open most .bmp files. I assume the hardware is fine, because I can swap back to the Adafruit drivers and everything is fine. Oh, (reading Kurt's post) using whatever SD came with teensyduino. Arduino 1.8.16 - teensyduino 1.55
Anyhow.. it's the actual ..

C++:
aFile = SD.open(filePath);
Sorry still only enough information to throw 🎯 and hope something hits

Adatfruit drivers? I assume the display driver? But are you also saying Adafruit's version of SDFat?

What is in the filePath? when it succeeds and when it fails? Is this root directory? or sub-directory..
Can you print out the path when it fails? Maybe not built correctly? Maybe something is trashing the memory? Maybe timing?

When are you trying to open the file? At program startup? Or sometime later? If at startup, what is the order that you do things? Do you try to start the SD first and open the file and then do the begin on the TFT or did you do the TFT first? Why this could matter is maybe not both CS's are pulled high (either by software or hardware) when the board boots. For example, if you are trying to read from SD and the TFT CS is floating, at times the display may try to grab data at same time...
You might try at the beginning of setup to do something like:
Code:
setup() {
  pinMode(TFT_CS, OUTPUT);
  digitalWrite(TFT_CS, HIGH);
  pinMode(SD_CS, OUTPUT);
  digitalWrite(SD_CS, HIGH);
  ...
Obviously substitute whatever you are using for TFT_CS, SD_CS...

as @defragster mentioned:
TeensyDuino current is 1.59 - so lots of long tested updates since 1.55 - not sure if any SPI or other changes might change the usability?
You might try updating to 1.59 maybe installed on Arduino 1.8.19 or 2.x and see if that helps.

When the build completes, what does it say about how much memory is free?

Sorry again only throwing out ideas to hopefully help with locating the issue
 
What does the "b side" of your project look like, where all the components come together?. I don't know, maybe a loose wire somewhere?
 
OK. yes my IDE is a bit out of date..

I'm NOT using SDFat I'm using SD.

The hardware IS that Star Trek game you see there. I've been running this handheld for years doing different development projects on it.

What I mean by Adafruit drivers, maybe not the right term? Typically, on this hardware setup, I run the Adafruit screen library for running this display. The Adafruit 1947. I have glue code that I use to adapt different libraries for different displays to run by my GUI library.

When I swap out the Adafruit library for the r3 one, the display seems to draw faster, but the SD.open() command tends to no longer work. Nothing else is changed. Swap the Adafruit library back in, everything is wonderful again, I can open files. But the display is kinda' slow.

Global variables use 7,516 bytes.

Now this..

Code:
setup() {
  pinMode(TFT_CS, OUTPUT);
  digitalWrite(TFT_CS, HIGH);
  pinMode(SD_CS, OUTPUT);
  digitalWrite(SD_CS, HIGH);
  ...

I've never done this because I was under the understnading that the SPI library or possibly the display library was taking cre of this. Is this not the case? Is everyone doing it like this?

-jim lee
 
I've never done this because I was under the understnading that the SPI library or possibly the display library was taking cre of this. Is this not the case? Is everyone doing it like this?
Most libraries do their own, but they don’t know anything about other devices. Some devices put PU resistors on their CS pins. But some may not. So if strange starting up issues arise, I will do something like I mentioned
 
Back
Top