#define placement

kenhorner

Active member
You wizards are all likely better coders than I, but I keep getting issues with trying to redefine the size of a TFT screen using the Teensy version of the
ILI9341_t3.h library. I'd like to be able to just leave the library down in the Teensy hardware path alone and compile for different size screens by redefining the height and width up in the code. The question is should I put my defines ahead of the library include or after? I get compiler warnings either way. And sometimes the new size works, and sometimes it does not and uses whatever values the library has in it.

e.g., the after option.....

#include "ILI9341_t3.h"
...
#define ILI9341_TFTWIDTH 320 // Override the PJRC ILI9341 library
#define ILI9341_TFTHEIGHT 480

or the before option.....

#define ILI9341_TFTWIDTH 320 // Override the PJRC ILI9341 library
#define ILI9341_TFTHEIGHT 480
...
#include "ILI9341_t3.h"

Advice please.
 
I'd like to be able to just leave the library down in the Teensy hardware path alone and compile for different size screens by redefining the height and width up in the code.

You can't. The library isn't designed for customized graphics size. No amount of #define stuff in your program can tell the library to work differently.

To make this sort of change, you'll need to edit the library code.
 
In general terms in C and C++, the pattern is to define the macros before the include statements, so that in the included (library!) code you do
Code:
#ifndef   FOO
#define   FOO  456  // Default
#endif /* FOO */
This means that if FOO is not defined before the include statement, it will default to 456. Re-defining it afterwards won't affect the already included code. The // Default comment is only for us humans, because the preprocessor will drop it automatically; it is not part of the macro definition. If the default value has limitations, that is where you put the valid range.

This is the pattern you need to modify the library to conform to.

Alas, it only works if the library uses the macro consistently, and does not have hidden requirements for the constant value. For example, a library intended for 160×128 – 320×240 displays, might internally use an uint8_t or unsigned char for the latter coordinate, so the implementation limits the maximum supported size to ×255. I like to thoroughly comment such requirements where the #define is, but most authors do not bother. Thus, you do need to carefully read through the library code to check for such limitations, and whether the library code mistakenly uses a numeric constant instead of the macro somewhere.
 
Nominal.

Yep, I get that last part. Plus, you can't override the library by putting a copy in the local arduino/libraries folder. Have to dig down into the core libs. It's one of those Teensy idiosyncrasies. But I like Paul's products too much not to deal with it. I could use another non-Teensy version of it, from Adafruit, or others, but then I'd have to cope with a couple of issues there too. As Heinlein said "TANSTAAFL."

Thanks
 
Plus, you can't override the library by putting a copy in the local arduino/libraries folder.

Arduino IDE has complex rules it follows when it finds 2 copies of any library. Best to turn on verbose output during compile in File > Preferences. Then look for the info it prints with the full pathname of every library it used and any if found but didn't use. Your first step it to just make a copy and check that Arduino IDE really is finding your copy. If the IDE just won't use your other copy, but it is showing up as a duplicate in that verbose output, you might get a better answer about why on Arduino's forum. Per is active there and he knows these details really well. But please be aware you will have to give detailed info for anyone to really help. Just saying it doesn't work without giving the verbose output and probably more details isn't going to go anywhere.
 
Have to dig down into the core libs. It's one of those Teensy idiosyncrasies. But I like Paul's products too much not to deal with it.
Oh, I just meant that it is nontrivial to do in any case, especially when you only have 320×240 display modules at hand; and not specific to Teensy at all. Making a library too general is feature creep, and since features you cannot test are likely to be buggy, it is not a good thing. It is better to keep it to what you can use and test, and extend or rewrite it later when you know more about the use cases.

I've loved Teensies since I got my first Teensy 2.0++, especially because I can modify the core libs if I want to, but I don't have to for typical cases, because Paul does pretty darn good libraries (and responds excellently when someone suggests an useful change). This one originated from Adafruit, but LadyAda et al. also do pretty acceptable libraries.

In this case, after a quick look at the library sources, I *think* –– I didn't even compile-check this! –– that modifying the ILI9341_t3::setRotation() method would make the most sense here. In ILI9341_t3.h, change the declaration into
Code:
        void setRotation(uint8_t r, int16_t width=ILI9341_TFTWIDTH, int16_t height=ILI9341_TFTHEIGHT);
and in ILI9341_t3.cpp, its implementation into
Code:
void ILI9341_t3::setRotation(uint8_t m, int16_t w, int16_t h)
{
    if (w < 2) w = ILI9341_TFTWIDTH;
    if (h < 2) h = ILI9341_TFTHEIGHT;
    rotation = m % 4; // can't be higher than 3
    beginSPITransaction(_clock);
    writecommand_cont(ILI9341_MADCTL);
    switch (rotation) {
    case 0:
        writedata8_last(MADCTL_MX | MADCTL_BGR);
        _width  = w;
        _height = h;
        break;
    case 1:
        writedata8_last(MADCTL_MV | MADCTL_BGR);
        _width  = h;
        _height = w;
        break;
    case 2:
        writedata8_last(MADCTL_MY | MADCTL_BGR);
        _width  = w;
        _height = h;
        break;
    case 3:
        writedata8_last(MADCTL_MX | MADCTL_MY | MADCTL_MV | MADCTL_BGR);
        _width  = h;
        _height = w;
        break;
    }
    endSPITransaction();
    cursor_x = 0;
    cursor_y = 0;
}
so that immediately after calling tft.begin();, you also call tft.setRotation(rotation, width, height); to set the display orientation and size. In my opinion, this is the least intrusive change, as it allows existing code to work as-is with no behaviour change (that is, tft.setRotation(rotation) was and still would be equivalent to tft.setRotation(rotation, ILI9341_TFTWIDTH, ILI9341_TFTHEIGHT);). I would also remove the macro definitions for WIDTH and HEIGHT in ILI9341_t3.cpp for consistency, as they're only used in the ILI9341_t3::ILI9341_t3() constructor, replacing them there with ILI9341_TFTWIDTH and ILI9341_TFTHEIGHT, respectively.

The reason for this is that ILI9341_t3.cpp may be compiled separately from the user sketch, so the default values of the macros will always be used for the library code; any definitions by the user code are just not visible then! Fortunately, the ILI9341_t3 object contains private _width and _height members, which are used consistently. The only exception is the constructor (which used derivative macros WIDTH and HEIGHT for some reason to assign them their default values), and the setRotation(), which has to use the ILI9341_TFTWIDTH and ILI9341_TFTHEIGHT macros because the previous orientation is not recorded so it is impossible to tell which of current _width and _height is which. Passing the new width and height as parameters, with defaulting to the original values if they are not specified, is the simplest option for supporting different-sized ILI9341-based displays, and should work well even when compiled separately.

(A separate question is how to document the parameters in a way users will understand. The specified width and height with this change is according to orientation 0 or 2, not in the orientation they are setting. This is to allow the backwards-compatible defaults to work.)

I cannot in good conscience suggest that @PaulStoffregen include such a change in the official version, because all my ILI9341-based displays are 320×240, too (BuyDisplay/EastRising 2.8" 320x240 IPS modules, to be specific), and I cannot really test the change! And such changes should always be tested (on both 320×240 and different-sized ILI9341-based display modules), no matter how seemingly simple or obvious, before inclusion in such a widely-used library. Theory is nice and useful, but testing in practice rules.
 
Last edited:
especially when you only have 320×240 display modules at hand .... and since features you cannot test are likely to be buggy, it is not a good thing. It is better to keep it to what you can use and test,

My thoughts exactly. So far all the ILI9341 displays I have (and I do have many) are all 320x240 pixels.

@kenhorner - my question for you: do you actually have a ILI9341 display with different resolution? If so, can you give info about where to buy it?
 
Wow guys, I can't believe you are taking this much time with this. And yes, Paul, to your earlier comment, I've been building systems for 50 years and I know I can't just make assertions about things not working. You are right and right to point it out. But the truth is things have worked fine for the last 8 years, and I only now started seeing problems with the new Ard 2.2 IDE when I switched over from 1.18 a month ago. But maybe that's just a coincidence. However, I am going to do a test or two with Nominal's code. It might just be workable. And, I have TFTs of several dimensions on different devices so testing is feasible. Got to find the time.

As to where to buy, the 320x480s are Adafruit units (prod #2050). Ladyada makes some good gear. The Teensy ILI9341 library has worked fine with the display and the Adafruit Graphics library for 6-7 years of service so far. Originally, the first robot subsystem was built with an Arduino Mega and used her Adafruit_ILI9341 library. Then we needed more speed and moved to some T3.2 and 3.5 units and shifted to Paul's library. Now we're porting to T4.0 and 4.1s.
 
Adafruit #2050, 3.5" 320×480 TFT, actually uses the HX8357D controller and the Adafruit_TFTLCD library.

It should not be too difficult to copy the ILI9341_t3 library as HX8357D_t4 –– that way there is no library resolution issues! –– and change the initialization commands and the rotation constants, as the rest is the same for both ILI9341 and HX8357D.

Note that Adafruit_TFTLCD::begin() takes the controller type as an optional parameter (0x9325 for ILI9325, 0x9328 for ILI9328, 0x9341 for ILI9341, 0x8357 for HX8357D and compatibles, 0x7575 for HX8347G and compatibles; and stores the corresponding ID_932x, ID_9341, ID_HX8357D, or ID_7575 as the driver member variable, which it checks for some operations. The readID() method indicates autodetection is possible, but likely a bit, uh, unreliable, based on the comments in the code. I'm hoping you don't need that ;).

Of those, ILI9325 and ILI9328 are annoying to handle, as they don't support true rotation, and the coordinates must be adjusted for every operation; and ID_7575/HC8347G requires its modification window to be set for most/all commands. That leaves, heh, just ILI9341 and HX8357D.

If you want and are not in a hurry, I could take a look at a first version for the conversion. No promises, though.

I myself prefer the BuyDisplay/EastRising IPS panels with various controllers. Although many do support SPI, I like to use the parallel interface (but same data as SPI) myself, and am not so familiar with ILI9341_t3 or other libraries. Also, these are just the panels, so one has to set the mode control pins and handle the backlight powering oneself.
 
Nominal,
I just wanted to follow up and tell you that your idea worked. Thanks. To make sure the Ard IDE would find the revised library, I renamed it. That also isolated any programs that use the original library unless they get modified. And it added a little complexity as the two Arial fonts in the folder had an #include <ILI9341_t3.h> statement in them. So I changed those references, but left all the library examples alone. Net net, it was a successful kluge. I used three test assemblies I had on hand with different screen sizes, changed their Set Rotation code to include the correct screen dimensions and voila.... compiling and running each, one after the other, set each display correctly. No need to go over to the library itself and change TFTHEIGHT and TFTWIDTH values to match screen sizes. I ran many of the Adafruit GFX test examples and the code that supports each assembly and encountered no errors or display glitches on any of the screens. Clearly, not a full test suite but more than enough to satisfy me and make my life easier. And the original libraries are unchanged so there should be no slow creep consequences. Thanks again.
 
Back
Top