ILI9488_t3 - Support for the ILI9488 on T3.x and beyond...

@KurtE

For the T$ don't think it will matter when the B2 boards are release but if we want it to work with T3.5/6 then one of the options you mentioned in post#50 would probably have to be implemented if we want to use FrameBuffer and/or DMA with T3x with the ILI9488.

Right now was changing the lib so you can specify SPI port in the constructor, but looks like I would have move the processor specific stuff in the .h file to the .cpp file to make it work.
 
@Frank B, @mjs513 @defragster (and others)

Thinking about how to do Asynch Update with color index frame buffer and had an idea... Not sure how well it would work, and probably Frank has already done it...

Assume you have: it setup something like:

uint8_t frame_buffer[320*480];
uint32_t color_pallet[256];

And assume that all of our code is setup to write to the frame buffer with indexes and now we wish to do an update (Async...)

What I am thinking might work, is suppose I create a new buffer for now lets say it is sized something like:

#define DMA_COLOR_OUT_COUNT 32
uint8_t screen_output_buffer[DMA_COLOR_OUT_COUNT*3];


Now suppose I start off converting the first 32 pixels of the frame_buffer into this screen_output_buffer and then suppose we start a
DMA operation from this buffer to the appropriate SPI register (PUSHR on T3.5/6 and ... on T4),
And with this DMA operation we setup the DMA to interrupt us when Half done and Full done.

On Half done, we refill the first half of the buffer with the next 16 pixels of information, when we get the full done, if we have not finished the screen output,
we refill the last half of the buffer with the next 16 pixels colors...

Again maybe not as clean as being able to have the DMA operation do everything, but might work? Note on T4, might be able to setup the secondary buffer as 32 bit values and set the SPI frame size to 24 bit as well... Not sure...

Thoughts?
 
Thanks @defragster -

I probably saw a different version you posted? It was on the t4 thread, and did not look like it had any DMA or much of the actual functions filled in?

Code:
class ILI9488_TeensyDMA : public Print
{
  public:
  	ILI9488_TeensyDMA(uint8_t _CS, uint8_t _DC, uint8_t _RST = 255);	
	void begin(void);	
	void drawPixelDirect(int16_t x, int16_t y, uint32_t color);
	void drawPixel(int16_t x, int16_t y, uint8_t color); 
	void fillScreen(uint8_t color); //fills buffer with color
	virtual size_t write(uint8_t c);	
	uint8_t readcommand8(uint8_t c);
	uint32_t readcommand32(uint8_t c);
	inline void setAddrWindow(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1);
	uint32_t* clut; 	//color lookup-table
	uint8_t* buffer; 	//screen-buffer
	
	void drawBufferNoDMA(void);
  protected:
	inline uint8_t writeData(uint8_t data);	
	inline void writeCommand(uint8_t cmd);	
  private:
	inline void fillLineBuf(uint32_t line);	
	volatile uint32_t* csRegSet;
	volatile uint32_t* csRegClr;
	volatile uint32_t* dcRegSet;
	volatile uint32_t* dcRegClr;
	uint32_t csMask, dcMask, width, height, size;
	uint8_t rst;
	uint8_t* mem; 	// memory for screen-buffer
};
Maybe there was a later posting?
 
@KurtE - you would only have to do that for the T3x. For T$ - going to keep it the same way? Don't want to assume.:) Which is the better - I really don't have the knowledge to be of much help here :(

@defragster - where did you post that 1byte code?
 
@mjs513 @defragster... - Not sure for T$ if best to keep as is with 16 bit frame buffer? As again could not do direct DMA from it in the current state. If wanted direct DMA ability would need probably 24 or 32 bit buffer. 32 bit would not fit, not sure if you could do 24 bit per write transfers through DMA, so would then maybe have to reorder the bytes in buffer and do 8 bit transfers...

So still might be an interesting exercise to try a pallet version... And see how well it works... Then can decide later on T4B2 (and release) if we want larger buffer...

At one point I thought about maybe doing this for ili9341 to maybe be able to have frame buffer for T3.2 or the like, but with 64K memory a one byte uffer would still take 76800 bytes which would not work... Could do a 4 bit per pixel pallet (16 color), but was not sure it would be worth the work?
 
@KurtE - as for your last comment on 16 colors - while probably would work - but only 16 colors - Arg.

KurtE said:
So still might be an interesting exercise to try a pallet version... And see how well it works... Then can decide later on T4B2 (and release) if we want larger buffer.
Would be interesting to see how it works.

@KurtE - @defragster - others
Just finished a incorporating SPIn directly in the constructor - should work for the T3.x but haven't tested it yet. I created a another branch for that effort. At least it compiles for T3.6 :)
 
@KurtE - @defragster and all

Looking at incorporating Teensy LC. Would the 9488 work on the LC?
 
The failure of DMA to manipulate was why FrankB stopped IIRC - that and the speed of 3 byte color xfer - It was decent to save memory having 8 bit color map - 256 colors. And the overhead of the buffer compress/access per line. It also has a 'dirty' line start stop area - but edit left and another right then includes the whole middle with only endpoint marking in that test/initial version.

256 colors would be generally good enough for info display usage … it was 'good enough' for Windows for how long :) Abusing all the Teensy RAM for display seems like it could cut short other usage. Just now finished reading … 4 bit color of 16 values would be more limiting - but if it worked well it might save valuable RAM.

I got 8 of the 8 MB RAM chips Frank located. But they are SPI and 1 bit transfer would be slow and setting up QSPI would take custom work without device pin bus wired out. If either of you see value or interest I could send you a pair - it was a whopping $10 to have 8 delivered - only $1.40 per pair. They will mount on the FrankB T_3.x memory boards. I suppose they would fit the Audio Board SPI too or Prop Shield as I think the pinout is the same for use. Take about 3-4 weeks to get if you wanted to order.

Could always set up an 'extended memory DMA' interface to cache a RAM spot to SPI_RAM … sort of like I did in that PC app where my nom de plume comes from - but that would be sketch dependent on the value and method for using that. In that PC use case it worked perfectly to have two 2KB RAM windows into the 128KB of extended RAM to save real RAM for other data. But that wouldn't apply to video - just make room for it.

Did you guys like my posted Mod to the KurtE test sketch - the one that showed there was no buffer in use? :( I set SPI to 72 MHZ and took out the delays and it has been running 2+ hours without (flashing annoyingly) - and it has not gone WHITE - so I'm suspecting there was some specific SPI operation in the DemoSauce that took the controller offline - if it was a simple speed issue it likely wouldn't run for 30 minutes? Will have to try that on T$_1062 when such a thing exists.

re: T_LC and 9488 - if the 9341 works - wouldn't the 9488 work as well? It is all Math [non-float 32 bit 320x480] and SPI when no buffers are used.
 
Did you guys like my posted Mod to the KurtE test sketch - the one that showed there was no buffer in use? I set SPI to 72 MHZ and took out the delays and it has been running 2+ hours without (flashing annoyingly) - and it has not gone WHITE - so I'm suspecting there was some specific SPI operation in the DemoSauce that took the controller offline - if it was a simple speed issue it likely wouldn't run for 30 minutes? Will have to try that on T$_1062 when such a thing exists.

re: T_LC and 9488 - if the 9341 works - wouldn't the 9488 work as well? It is all Math [non-float 32 bit 320x480] and SPI when no buffers are used.
Yeah, that was good test case for FB and pointed out a big problem that led us down to this path.

As for the LC just thinking of the memory if we get FB working down the line. For non-buffer shouldn't be a problem. Right now just tested my SPI mod using &SPI for port address on FB_Teensy64 and the T$B1 worked without issue. So now what is left is the LC, that just needs testing on other SPI ports.
 
Yeah, that was good test case for FB and pointed out a big problem that led us down to this path.
...

Cool - I won't say as fast as it ran I could see it not working … but that not seeing the difference with full page screen interruptions made me do it stepwise - then I was :( sad to see my test worked …

FrankB's CLUT he says was just 1 hour from working … but there are a lot of function prototypes missing … then they have code for each to map to the line by line process.

72 MHz T$ still running that test 5.5 hrs. Will have to revisit DSauce and edit test/transition order/inclusion to see if the break to WHITE can be ID'd.
 
@defragster and @mjs513 and ...

Have played some today with frame buffer with 8 byte buffer and index... Still really needs work and doing...

But first test app is showing some promise.
Up on new Branch _Frame_Bufer_Pallet

Will work through a few more primitives to debug to see if they are working at all...
Code:
//=============================================================================
//=============================================================================

#include <ILI9488_t3.h>
#include <ILI9488_t3_font_Arial.h>
#include <ILI9488_t3_font_ArialBold.h>
#define TFT_RST 8
#define TFT_DC 9
#define TFT_CS 10

uint16_t our_pallet[] = {
  ILI9488_BLACK,  ILI9488_RED, ILI9488_GREEN,  ILI9488_BLUE,   ILI9488_WHITE,
  ILI9488_YELLOW, ILI9488_ORANGE, ILI9488_CYAN, ILI9488_PINK };

ILI9488_t3 tft = ILI9488_t3(TFT_CS, TFT_DC, TFT_RST);
//=============================================================================
// Setup
//=============================================================================
void setup()
{
  while (!Serial && (millis() < 3000));
  Serial.begin(115200);
  tft.begin();
  tft.setRotation(3); // 180
  delay(100);

  tft.fillScreen(ILI9488_BLACK);
  delay(250);
  }
//=============================================================================
// Loop
//=============================================================================
void loop()
{
  tft.useFrameBuffer(false);
  uint32_t start_time = millis();
  tft.fillScreen(ILI9488_BLACK);
  tft.fillScreen(ILI9488_RED);
  tft.fillScreen(ILI9488_GREEN);
  tft.fillScreen(ILI9488_BLUE);
  tft.fillScreen(ILI9488_WHITE);
  tft.fillScreen(ILI9488_YELLOW);
  tft.fillScreen(ILI9488_ORANGE);
  tft.fillScreen(ILI9488_CYAN);
  tft.fillScreen(ILI9488_PINK);
  tft.fillRect(0, 0, tft.width()/2, tft.height()/2, ILI9488_RED);
  tft.fillRect(tft.width()/2, 0, tft.width()/2, tft.height()/2, ILI9488_GREEN);
  tft.fillRect(0, tft.height()/2, tft.width()/2, tft.height()/2, ILI9488_BLUE);
  tft.fillRect( tft.width()/2, tft.height()/2, tft.width()/2, tft.height()/2, ILI9488_YELLOW);
  uint32_t non_frame_buffer_time = millis() - start_time;
  delay(1000);
  tft.setPallet(our_pallet, sizeof(our_pallet)/sizeof(our_pallet[0]));
  tft.colorsArePalletIndex(true);
  tft.useFrameBuffer(true);
  start_time = millis();
  tft.fillScreen(0);
  tft.fillScreen(8);
  tft.fillScreen(7);
  tft.fillScreen(6);
  tft.fillScreen(5);
  tft.fillScreen(4);
  tft.fillScreen(3);
  tft.fillScreen(2);
  tft.fillScreen(1);
  tft.fillRect(0, 0, tft.width()/2, tft.height()/2, 4);
  tft.fillRect(tft.width()/2, 0, tft.width()/2, tft.height()/2, 5);
  tft.fillRect(0, tft.height()/2, tft.width()/2, tft.height()/2, 6);
  tft.fillRect( tft.width()/2, tft.height()/2, tft.width()/2, tft.height()/2, 7);

  tft.updateScreen();
  uint32_t frame_buffer_time = millis() - start_time;
  delay(1000);
  tft.useFrameBuffer(false);
  tft.fillScreen(ILI9488_BLACK);
  tft.setTextColor(ILI9488_RED);
  tft.setTextSize(3);
  tft.setCursor(0, 130);
  tft.printf("Reg: %d\nFB: %d", non_frame_buffer_time, frame_buffer_time);
  tft.updateScreen();    
  delay(2500);
}
But have the ability to set pixels by pallet index... Only doing it in using frame buffer case so far...

Once I get a few more things working, then will try a DMA operation to see if it might work... Will mainly start off with T4... As that is what I have mine hooked up to..
 
Looks like it is working here KurtE with T$.
Just for fun I alternate passes doing :: if ( zz % 2 ) tft.updateScreen();

Putting this in .h:
Code:
#if defined(__MK64FX512__) || defined(__MK66FX1M0__)
#if F_BUS >= 64000000
#define ILI9488_SPICLOCK 64000000
#define ILI9488_SPICLOCK_READ 4000000
#else
#define ILI9488_SPICLOCK 30000000
#define ILI9488_SPICLOCK_READ 2000000
#endif
#elif defined(__IMXRT1052__) || defined(__IMXRT1062__)  // Teensy 4.x
#define ILI9488_SPICLOCK 72000000
#define ILI9488_SPICLOCK_READ 4000000
#else
#define ILI9488_SPICLOCK 30000000
#define ILI9488_SPICLOCK_READ 2000000
#endif

Makes before @30MHz::
Reg: 1469
FB: 157
Reg: 1469
FB ALL: 1684

Go to this @72 MHz::
Reg: 980
FB: 108
Reg: 980
FB ALL: 1147

where FB ALL is triggered with ZZ like this doing fillScreen:
Code:
	for ( int ii = 0; ii < 10; ii++) {
		tft.fillScreen(ii);
		if ( zz % 2 ) tft.updateScreen();
	}

I added three of those extra updates on the quarter screen draws and it adds 313 ms - so 104 ms per updateScreen() - since it writes the whole screen.

Will let that run to see if it triggers WHITE screen.
 
@KurtE and @defragster
Sorry - been with my granddaugther for awhile and wanted to finish off SPI.

Just pushed the change to master branch so now you do this going forward:
Code:
ILI9488_t3 tft = ILI9488_t3(&SPI, TFT_CS, TFT_DC, TFT_RST);

Needs more testing though.

Now on to the FB changes.
 
FrameBufferPalette sample running an hour with reduced delays.

Will be cool to see if DMA can be made to pull that off - doing the palette expansion on send.

Just started DemoSauce @72 MHz - no TwistyText and running the others for 1 sec. That takes out the slow one - and should result in more transition after animation and testing faster for white fail. Restored Anim# count and add show the Transition # selected. Put a pixel write/read to see if controller fails to respond indicating screen WHITE so I can stop.
 
Just loaded up @KurtE's sketch with SPI@72MHz. Got same performance results. Hate having to leave open the serial monitor.

@defragster - be interesting to see if it DSauce the screen still goes white.
 
It just went WHITE - and the pixel test is still passing - so no automation help there :(

Nice the FBP8 works the same there.

I was watching and walked away and see it has died on return - still took 30-40 minutes … but completed many more animation changes than the ~80 before at 3 secs each:
Checkerboard [3 secs]
Fr cnt (MIB!): 30 @An#396 @Tr#2 (10.00 FPS)
^---
TwistyText
Fr cnt (MIB!): 1168
---
Checkerboard [3 secs]
Fr cnt (MIB!): 30 @An#397 @Tr#0 (10.00 FPS)

For that I did enable Twisty to be sure it would fail [so I could know if pixel write&read failed] - though at 3 seconds for the others not 20.

I'll start again with no Twisty as first step only change as that one is long and none too simple … If taking out Twisty won't let it fail that would be a clue.

Would be NICE if T$ USB Read worked - I'd have it stop every 10 Anims after some point so it couldn't get away from me in case that pinpoints the issue … Could hook up Serial1 and do that …

Still running - updated output to track minutes of run time for tracking.
Cube3D [3 secs]
Fr cnt (MIB!): 156 @An#309 @Tr#3 @secs1432 @mins23 (52.00 FPS)

<update>: Still running well >> BaseAnimation* ANIMS_TEMP[] = { //_twistyText, && #define ILI9488_SPICLOCK 72000000
Leaves [3 secs]
Fr cnt (MIB!): 159 @An#1912 @Tr#3 @secs8863 @mins147 (53.00 FPS)

<EDIT>3.8 hours and running without TwistyText - going to change it up:
MagentaSquares [3 secs]
Fr cnt (MIB!): 108 @An#2955 @Tr#0 @secs13717 @mins228 (36.00 FPS)

<edit#34> - 3 hours with just these two with transition - not WHITE:
TwistyText
Fr cnt (MIB!): 1136
---
PlasmaCloud [3 secs]
Fr cnt (MIB!): 56 @An#401 @Tr#3 @secs10856 @mins180 (18.67 FPS)
<Started these 3 with transitions> :: TwistyText --- Waveform ^--- MagentaSquares > << 86 min no WHITE
<edit#74> :: Altered the Anim and Transition #'s to the '---' line to show on end of twisty too. - also pixel test there.
zzz'hours :: Twisty + _sphere3D, _checkerboard, _leaves, _cube3D, _plasmaYellow, _triangleWeb, & normal transitions w/20 secs per Anim.
 
Last edited:
@... Sound good.

Thanks for testing. As for Serial monitor, it is easy to get rid of

If I get a chance today I will experiment with DMA some, to see if my idea might work and get an idea of how well...

I have the two Serial.printf statements I added commented out and pushed up, so you can get rid of the Serial.begin... I only have it there for debugging.
 
@KurtE

I just ran the digitizer4 sketch with FB on and off. With FB off works as before the changes to FB. With FB turned on and not using palete's screen still runs but seems slower on the screen updates. Didn't time it yet just observation.

To convert it to paletes, just use the palette number where we have our existing color enumerators? Are all the primitives updated to use the palette numbers?

EDIT: Think I got my answer. Just changed the digitizer4 sketch to use palette colors with FB = True but nothing displays on the screen
 
Last edited:
With the FB turned on, there are two modes of colors:

Note: So far I have only changed the Frame buffer code to use the two ways of colors:

If you don't do anything special and do something like:
Code:
  tft.fillScreen(ILI9488_BLACK);
  tft.setTextColor(ILI9488_RED);
  tft.setTextSize(3);
  tft.setCursor(0, 130);
  tft.printf("Reg: %d\nFB: %d", non_frame_buffer_time, frame_buffer_time);
  tft.updateScreen();

The fillScreen->fillRect code calls: uint8_t color_index = mapColorToPalletIndex(color);

The mapColorToPalletIndex works two ways, if you told the system the colors are indexes: tft.colorsArePalletIndex(true);

It just uses the color as index:
Code:
	inline uint8_t mapColorToPalletIndex(uint16_t color) 
		{
			if (_pallet && _colors_are_index) return (uint8_t)color;
			return doActualConvertColorToIndex(color);		
		}
Note: it also sees if it needs to allocate a Pallet.

But in the above code to set color to red it uses brute force:
Code:
uint8_t ILI9488_t3::doActualConvertColorToIndex(uint16_t color) {
	if (_pallet == nullptr) {
		// Need to allocate it... 
		_pallet = (uint16_t *)malloc(256*sizeof(uint16_t));	// 
		// Probably should check that it did not fail... 
    	_pallet_size = 256;					// How big is the pallet
    	_pallet_count = 0;					// how many items are in it...
	}
	
	if (_colors_are_index) return (uint8_t)color;

	for (uint8_t i = 0; i < _pallet_count; i++) {
		if (_pallet[i] == color) return i;
	}
	if (_pallet_count >=  _pallet_size) return 0;
	_pallet[_pallet_count] = color;
	return _pallet_count++;
}
I thought about maybe checking some condition and assuming you passed in index, but can not just check high byte of color, as maybe you want to paint blue:
ILI9488_BLUE 0x001F

Note: There are are probably lots of optimizations I could do to this conversion. Example setTextColor just saves away the color... Only in the drawing of the characters is the colors converted to index...

But currently still more a WIP see if it makes sense mode. And if it looks promising we can see about how some of the APIS should work. Example how should Gradient fills work? Should it try to keep allocating different colors. Or should it if we pass in from index 5 to index 8, should it just use the colors in indexes 5-8? ...

But now working on DMA...
 
Thanks for the explanation. I forgot about copying the palette commands :
Code:
  tft.setPallet(our_pallet, sizeof(our_pallet)/sizeof(our_pallet[0]));
  tft.colorsArePalletIndex(true);
once I did that then of course the screen displayed the data. Understand that this is still very much a work in progress but was curious what observations I could make with existing sketch. Not that it is working I do notice (and its noticeable) that there is a bit of delay between moving the joystick and when the data displays on the screen.

The second observation which I tested with both the digitizer sketch and the your test sketch is that if I comment out serial.printf in the .cpp file the screen stays white. Once its uncommented it displays normally which was kind of strange to me.

Ok going to go figure out how my new Lewan Serial Servos work for a bit.

EDIT: Did a little closer look at the screen data it seems that the update screen is slowing down the reading data from BT. But it does track what is printed to sermon to what is on the screen.
 
Last edited:
@KurtE and @defragster
Just tested on FB_Teensy64. Lines 775/776 need to be changed to:
Code:
		uint8_t color_index =  _pfbtft[y*_width + x];
		return _colors_are_index ? color_index : _pallet[color_index];
to get it to work. After that it does work, and with Tim's SPI clock changes:
Code:
Reg:  1462
FB:    180
 
T$ @72 MHz seems to be 'generally' fine when DemoSauce runs - as noted above this is missing 3 tests run otherwise but these 7 are running with normal transitions
^TwistyText
Fr cnt (MIB!): 1176
--- @An#1381 @Tr#1 @secs30033 @mins500
^Cube3D [20 secs]
Fr cnt (MIB!): 1049 (52.45 FPS)
^--- @An#1382 @Tr#2 @secs30055 @mins500
^Leaves [20 secs]
Fr cnt (MIB!): 1075 (53.75 FPS)
^--- @An#1383 @Tr#2 @secs30078 @mins501
^Checkerboard [20 secs]
Fr cnt (MIB!): 198 (9.90 FPS)
^--- @An#1384 @Tr#1 @secs30100 @mins501
^TriangleWeb [20 secs]
Fr cnt (MIB!): 936 (46.80 FPS)
^--- @An#1385 @Tr#0 @secs30121 @mins502
^Sphere3D [20 secs]
Fr cnt (MIB!): 608 (30.40 FPS)
^--- @An#1386 @Tr#0 @secs30142 @mins502
^PlasmaYellow [20 secs]
Fr cnt (MIB!): 745 (37.25 FPS)
^--- @An#1387 @Tr#2 @secs30165 @mins502
The '^' added means a pixel was written and read back between tests to work - but the screen is still fine.

Will restore it and leave it run with other tasks today. … <edit> over 2 hours and good so far ….
<edit 5 hrs> over 5 hours and good so far …. reverting to github copy- only did should have been pixel write/read in 2 spots between tests @ 72 MHz SPI
^--- @An#861 @Tr#2 @secs18660 @mins311
^TwistyText
Fr cnt (MIB!): 1148
--- @An#862 @Tr#1 @secs18683 @mins311
^TriangleWeb [20 secs]
 
Last edited:
Back
Top