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();
    }
}
 
Back
Top