ILI9341_t3/ILI9341_t3n implementing const reads and static SPI class

honey_the_codewitch

Active member
I've run into a problem with the optimized ILI9341 driver implementations by Paul S. and Kurt E.

The issue is this: Reading from the display can't be done with a const class reference because the methods like readRect() are not marked const, which creates some encapsulation issues.

It's also a show stopper for me for reasons - long story that doesn't bear explaining here.

Unfortunately the way the drivers are implemented it is not possible to make them const.

The reason being is that the SPI class and related is held around with the class instance instead of a separate static class.

You'll perhaps note that TFT_eSPI does const reads and is implemented with a class that looks like a normal class, but is actually backed by all static member fields.

That itself creates a disadvantage in that you can't have two instances of the TFT_eSPI class, so driving multiple displays is problematic.

There's a way to have the best of both worlds here. The way to do it is to create a static template class whose arguments are the SPI host # to use (0, 1, or 2 on the Teensy 4.1 for example, for SPI, SPI1, and SPI2) and the CS pin #. That way all the static fields are specific to the template instantiation, and thus allowing you multiple instances as long as each one is on its own CS line.

You then reference that static class from your instance class.

I've done almost all this, and made a proof of concept here: https://github.com/codewitch-honey-crisis/teensy_gfx_test/blob/master/include/teensy_gfx.hpp

It implements a driver for htcw_gfx, not the ILI9341_t3 API, but it should illustrate the concept.

The trouble is, this sketch doesn't work. It fills the screen with purple (like it's supposed to), but then waits a bit, then reboots with no dump to serial instead of displaying the font.

The trouble almost has to be in the teensy_tft_spi_driver<> template class, because the actual driver that uses it is fairly straightforward, and I've implemented it many times, just not for the teensy.

Here's what I'd love to see:

The ILI9341_t3n driver refactored such that it can do const reads.

And/or

A correction to my code to fix the issue with it. I can't find the problem for the life of me.

I know this is asking a lot, but I figured I'd shoot the moon here and maybe get lucky. :)
 
Last edited:
Sorry I am not really understanding some of this. For example:
The issue is this: Reading from the display can't be done with a const class reference because the methods like readRect() are not marked const, which creates some encapsulation issues.
Code:
  void readRect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t *pcolors);

Mark what const? The method? Or the pcolors? Obviously pcolors pointer can not be const:
So I am assuming the method itself:
Which as I understand it means:
[/QUOTE]
Code:
  void readRect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t *pcolors) const;
Which says that I won't change the state of any of the member variables. I am not sure how many of the states that might change, but I can imagine
things like the remembering which X's and Y 's setAddr, let alone frame buffer states, dma settings...

So verry unlikely that I will refactor everything for this.
I might take a quick look later to see if reasonably isolated issue, but my gut says otherwise.
 
Sorry, to clarify what I'd like to see is this: readRect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t *pcolors) const;

The way to make const would be like I said, to move all that internal state and SPI code to statics inside template classes, and then instance those *templates* with CS pin # as an argument, similar to what I did in that code I posted.

But yeah, it's not isolated issue. It involves restructuring the driver.

Frankly, I'd just rewrite it but even after looking at the manual and your code, and porting your code twice it still doesn't work. Unfortunately there's no error message either, just a pause and a reboot, which sucks.
 
good luck!

But if you have not already done so, it sometimes helps to have something like:

Code:
void setup() {
    while (!Serial && millis() < 5000) ;
    if (CrashReport) Serial.print(CrashReport);
...

And if you do get crashes, you can add in the Breadcrumbs to help localize where.
 
Hi,

I do not have much to contribute here but, as a last resort, you can always perform a dirty const_cast<> to cast away the constness or your calling method in order to call a non-const method from the driver... But I guess the C++ standard say doing so is undefined behavior (although in practice it will probably work fine).

I agree that, in principle, it makes sense for a method like ReadRect() to be marked as const because calling it does not really change the external state from the class user point of view... So maybe the SPI object should have the "mutable" attribute ?

If you have trouble with SPI, DMA etc.. you can also look at my own ILI9341driver (but only for T4) https://github.com/vindar/ILI9341_T4. It is originally based on Kurte's t3n code but I have since rewritten many parts of it so there might also be thing that could interest you in there.... And by the way, in my code, I do set the Stream* object as mutable for the purpose of keeping some methods const :)
 
Thank you so much! I'll definitely have a look at your code.

Casting away const won't work because it would involve detrimentally modifying an existing cross platform graphics library I wrote (https://honeythecodewitch.com/gfx) :(

Also I've run into scary issues doing that due to certain optimizations/assumptions being made by the compiler, at least if I understood what was happening correctly.

I've solved the SPI vs const issue before as I said by moving it to be static, and then i create multiple statics (one for each CS line in use) and drive the SPI using that class (all static methods). Templates make this relatively straightforward, as long as it was all designed with that in mind.
 
Back
Top