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

@KurtE

I have been whining a bunch to myself about switching between the processor, that I why my desk is so clutter. So I fully understand :)

As for the PR, process it when you feel like, then unless there is something major, move on - don't know if you plan on adding support of 3.2? For LC don't know if you want go down that rabbit hole - may be deeper than you think especially with SPI :)
 
Hi @mjs513 - For 3.5, I will play a little more. Currently I have two 80 pixel (240 bytes) buffers in the displays object, which for example on T3.6 and T4, using scatter/gather of DMA, I have chains setup that interrupt on each element, to refill it's buffer from the next 80 pixels, while DMA is still active on the logical other element on the chain... So should never stop outputting.

But T3.5's DMA has issues with Scatter/gather. So I am only using the first buffer, I then have the DMA interrupt and stop on the end of each buffer, where I refill the next pixels, and then startup the next dma... So gaps, including the interrupt overhead, as well as the time to fill in the next buffer...

Now what I would like to try on T3.5, is more similar to T3.6/4 stuff, still do the stop/restart, but, like the other fill in buffer 1 after I start the first DMA, and then in the ISR try to quickly switch the buffer and restart DMA, and then fill in the alternate buffer for the next interrupt... Will see if I can get it to work and also see if it speeds it up, which I of course could use LA to measure, but the last program I posted should also do it in easy way to follow...

Yes we should make it work, except like ili9341_t3n, the frame buffer including async should be disabled...

As for clutter, this is just the one side of desk, not showing table to the side nor the built in shelving :eek:
IMG_0740.jpg
 
@KurtE

After seeing that picture I don't think I should be complaining about clutter on my desk. :)

Anyway just tried it with all three processors and looks like you got it fixed. Yeah!

Will go with any way you want for 3.2/LC. Don't know too much about the LC though - have one but no idea where it is right now.
 
The Speed up appears to have worked :D
If you run the program on #175 where I stated:
Code:
And it is showing about 193 for straight update and 206 ...

With the updates the Async count is showing about 162 :D And I think it is still working..

Anyway create PR...
 
@KurtE

Ok - merge completed. Congratulations. Just tried to compile for T3.2, no framebuffer test. Have some work to do on setting SPI. Nothing major it doesn't look like. Will work on that and see what happens with T3.2 and let you know.

Go take a break and play with your puppy :)
 
Indeed the code 7 mins ago runs T_3.5 and T_3.6 right now - now branch gone?
T_3.5 Screen numbers :: 193 and 161
T_3.6 Screen numbers :: 137 and 137 << This is ODD that both the same ??
T$ Screen numbers :: 152 and 165

The Display Paul found I got two of with bad TFT_MISO is nice with inline connector matching the 9341. The prior Amazon linked for shield layout looks painful - and especially having 1 of 17 controllers not a known 9488 :(

Teensy64 since I have two displays is easy - that one is secured against standoff - the other T_3.5 uses the OSH PJRC test board with header and the T$ has a ribbon of wires secured on display end with masking tape to make like a plug header for inline connector.

I put this entry to the code for device swapping:
Code:
#ifdef __MK66FX1M0__
#define __C64__ 1
#define TFT_RST 255
7#elif defined(__MK64FX512__)
#define TFT_RST 255 // 8  // PJRC_OSH_test has RST@3.3V, T$ is on pin 8
#else
#define TFT_RST 8  // PJRC_OSH_test has RST@3.3V, T$ is on pin 8
#endif

#include <ILI9488_t3.h>
#include <ILI9488_t3_font_Arial.h>
#include <ILI9488_t3_font_ArialBold.h>
#if defined(__C64__)
#define SCK       14
#define MISO      39
#define MOSI      28
#define TFT_DC 20
#define TFT_CS 21
ILI9488_t3 tft = ILI9488_t3( &SPI, TFT_CS, TFT_DC, TFT_RST, MOSI, SCK, MISO );
#else
#define TFT_DC 9
#define TFT_CS 10
ILI9488_t3 tft = ILI9488_t3(&SPI, TFT_CS, TFT_DC, TFT_RST);
#endif

<update>: Pulled Master github.com/mjs513/ILI9488_t3 and T$ and T_36 working the same
 
@mjs513 and @defragster

Yes when Mike merges in PR requests, when I receive the email of the merge, I open it up on github and then do a delete of the branch (button press on the merge page...)

So everything should be back to master branch... I am sunk up..

I also tried building for T3.2 and run into variable undefined... for Frame count...

Will go hug puppy and wait for @mjs513 updates... Note: I looked up at Commits list on:
https://github.com/mjs513/ILI9488_t3/commits/master

And it is interesting with the different Merge PR's that there is a logical button for <verified>... I have never done that on my projects, not sure what it does?

Edit: Verified - http://blog.brew.com.hk/verified-commits-github/ - just means I think that the person who did the commit has a verified signature not that the actual commit was verified to work...
 
@KurtE and @defragster

Just pushed a change to the master to make the lib work on a T3.2. Just tested the graphicstest sketch and it went through the tests without a problem. Didn't record anything though sorry.

Only tested with no framebuffer but palette looks like it will work as well. Don't think framebuffer/DMA works on the T3.2?

EDIT: Problem I had with framecount is that it was defined twice :) Easy fix, other fix was to put a #if framebuffer around initDMASettings. Also had to fix SPI test to distinguish between T3.5/3.6 and T3.2. Once I did that it ran ok.
 
LOOKS GOOD:: got Master zip from 17m back and it runs recent sketch 3.5/3.6/T$

Almost want to add constraints in code to see if it stays happy on 9341 - it does run but ends up with center screen digits split - but gotta run. Will try with T_3.2 later.
T_3.5 now showing 160 and 172 at 120 MHz

Why does T_3.6 have same #'s ... 137?
 
@mjs513 Sounds good - As for palette for T3.2, maybe not... Unless of course we update the non frame buffer code to use the palette..

As for TLC - Again probably not worth the effort as I am not sure how much of a gain you would get performance wise over the hacked up Adafruit library, although it would give you support for SPI1 and the other features...

Currently about the only thing that was brought in by current stuff was some code for readRect... I might take a 30 minute attempt and see how bad it would be...
 
@mjs513 Sounds good - As for palette for T3.2, maybe not... Unless of course we update the non frame buffer code to use the palette..

As for TLC - Again probably not worth the effort as I am not sure how much of a gain you would get performance wise over the hacked up Adafruit library, although it would give you support for SPI1 and the other features...

Currently about the only thing that was brought in by current stuff was some code for readRect... I might take a 30 minute attempt and see how bad it would be...

Yeah probably right - I thought it was working but I was wrong. I don't think it is necessary to update the code for palette for the T3.2.

As for TLC. I agree don't think its worth it. Think TLC is going to take a lot more effort than you think.
 
Think TLC is going to take a lot more effort than you think.

You are probably right, but I did put up a version that crawls along...

And I did sort of a compact version of the ::begin() function to map it for SPI versus SPI1...

Was thinking that most of it could be used for all of the board types... That is the beginning parts would be the same up to the RED line.

Code:
#elif defined(KINETISL)
	if ((_mosi != 255) || (_miso != 255) || (_sclk != 255)) {
		// Lets verify that all of the specifid pins are valid... right now only care about MSOI and sclk... 
		if (! (((_mosi == 255) || spi_port->pinIsMOSI(_mosi)) && ((_sclk == 255) || spi_port->pinIsSCK(_sclk)))) {
			// one of those two pins are not valid, lets try to see if there is a valid one
			// In this case we will not check for 255 as we assume both most be specified...
			if (SPI.pinIsMOSI(_mosi) && SPI.pinIsSCK(_sclk)) {
				spi_port = &SPI;
			[COLOR="#008000"]} else if (SPI1.pinIsMOSI(_mosi) && SPI1.pinIsSCK(_sclk)) {
				spi_port = &SPI1;[/COLOR]
			} else {
				Serial.println("SPI Pins are not valid");
				return; 	// we will probably crash!
			}
		}

		// lets setup any non standard IO pins.
		if (_mosi != 255) spi_port->setMOSI(_mosi);
		if (_sclk != 255) spi_port->setSCK(_sclk);
		if (_miso != 255) {
			if (spi_port->pinIsMISO(_miso)) 
				spi_port->setMISO(_miso);
		}
	}
	uint32_t *pa = (uint32_t*)((void*)spi_port);
	_spi_hardware = (SPIClass::SPI_Hardware_t*)(void*)pa[1];
	[COLOR="#FF0000"]_pkinetisl_spi = (KINETISL_SPI_t *)(void*)pa[0];[/COLOR]

	pcs_data = 0;
	pcs_command = 0;
	pinMode(_cs, OUTPUT);
	_csport    = portOutputRegister(digitalPinToPort(_cs));
	_cspinmask = digitalPinToBitMask(_cs);
	*_csport |= _cspinmask;
	pinMode(_dc, OUTPUT);
	_dcport    = portOutputRegister(digitalPinToPort(_dc));
	_dcpinmask = digitalPinToBitMask(_dc);
	*_dcport |= _dcpinmask;
	_dcpinAsserted = 0;

	spi_port->begin();
The only thing would be code to know if SPI1 and SPI2 are valid...
That is the part in green above could be
#
Code:
                       #if defined(__MK64FX512__) || defined(__MK66FX1M0__) || defined(KINETISL)
			else if (SPI1.pinIsMOSI(_mosi) && SPI1.pinIsSCK(_sclk)) {
				spi_port = &SPI1;
                       #if defined(__MK64FX512__) || defined(__MK66FX1M0__) 
			else if (SPI1.pinIsMOSI(_mosi) && SPI1.pinIsSCK(_sclk)) {
				spi_port = &SPI1;
                        #endif
                        #endif
Actually in cases like this I wish we had added defines into SPI library like we did for Wire. has
#define WIRE_IMPLEMENT_WIRE
#define WIRE_IMPLEMENT_WIRE1
#define WIRE_IMPLEMENT_WIRE2

I was thinking of at least maybe hacking this code maybe in this cpp file, define something like:

SPI_IMPLEMENTS_SPI1
SPI_IMPLEMENTS_SPI2

depending on which processor...

What do you think?
 
@KurtE

Well if you want to go down the path to see if LC works I'll be there :)

Anyway if you do the defines you could put them in the .h like you with wire and then just reference them in the .cpp? You would need to restructure the begin(void) function though I would think. Don't know enough if it would be better or not. I have no problem either way I'm just the instigator on this project although I am looking at 3d rendering for the 9488: https://www.mrt-prodz.com/blog/view/2015/05/tiny-3d-engine-on-the-atmega328-arduino-uno
 
I just put code below in 9488.h and the recent KurtE DEMO box on blue cross works on T$ ili9341 ! Time on screen is 77 and 82

Could almost add begin(x,y) if all the #defines changed to : _width and _height ... and more ...

Lines are perfectly clear and all colors are good. Though the bottom line must be off as it is obscured by the touch panel edge unless viewed straight on - better with ILI9488_TFTWIDTH 239
Code:
#if 1
#define ILI9488_TFTWIDTH  240
#define ILI9488_TFTHEIGHT 320
#else
#define ILI9488_TFTWIDTH  320
#define ILI9488_TFTHEIGHT 480
#endif
 
That 3D modelling on a 16MHz AVR_328 is impressive!

Would be so nice if the 9488 would get into 2 byte color mode to reduce the SPI xfer!
I find this in the recently linked ili9488-unlocked.pdf:
4.9. DSI Transmission Data Format
4.9.1. 16-bit per Pixel, Long Packet, Data Type 00 1110 (0Eh)
Packed Pixel Stream 16-Bit Format is a Long packet, used to transmit image data formatted as 16-bit pixels to a
Video Mode display module. The packet consists of a DI byte, a two-byte WC, an ECC byte, a payload of length
WC bytes and a two-byte checksum. Pixel format is red (5 bits), green (6 bits), and blue (5 bits), in that order.
...

Doing something hackie'ish with begin(x,y) ... more soon
 
Last edited:
Haque ... Almost works - rotation has hardcode #define feeding it rather than swap() - so below is a circular ref - so that breaks this initial effort - not sure if it is worth correcting that - or just restoring my files? Other issue would be FrameBuffer Alloc? More things?

9488.c added code:
Code:
void ILI9488_t3::begin(int16_t x, int16_t y)
{
	_width = x;
	_height = y;
	begin();
}

9488.h added/changed:
Code:
#if 1
#define ILI9488_TFTWIDTH  (_width)
#define ILI9488_TFTHEIGHT (_height)
#define ILI9488_TFTWIDTH_DEFAULT  320
#define ILI9488_TFTHEIGHT_DEFAULT 480
#else
#define ILI9488_TFTWIDTH  320
#define ILI9488_TFTHEIGHT 480
#endif

// ...

	void begin(int16_t x, int16_t y);

// ... class define init

	int16_t _width=ILI9488_TFTWIDTH_DEFAULT, _height=ILI9488_TFTHEIGHT_DEFAULT; // Display w/h as modified by current rotation
 
@defragster and @mjs513 and ...

Will probably play a little more with TLC stuff, as the SPI output is real real real bad... screenshot.jpg
 
@KurtE

Had a feeling you were going to run into problems with SPI - may have to reduce the speed as well. When tonton81 was working on one of his library he had a bunch of problems with TLC SPI.

@defragster
Sounds like you are already working on the 3d rendering sketch :) If you are his blog may have help - also check the version of GFX he is using. Maybe he is doing it that way to satisfy that.
 
@mjs513 - TLC ...

Some of the issues is that a lot of the processor functions moved to C file out of header... For TLC, I did move some of the internal one back to header file and sped up some.
I was playing some with my color_test program and changed the loop function, to print out some timing information:

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
ILI9488_t3 tft = ILI9488_t3(&SPI, TFT_CS, TFT_DC, TFT_RST);
//=============================================================================
// Setup
//=============================================================================
void setup()
{
  pinMode(7, OUTPUT);
  digitalWrite(7, HIGH);
  while (!Serial && millis() < 3000) ;
  Serial.begin(115200);
  delay(50);
  tft.begin();
  tft.setRotation(3); // 180
  delay(100);

  tft.fillScreen(ILI9488_BLACK);
  delay(250);
  tft.fillScreen(ILI9488_RED);
  delay(250);
  tft.fillScreen(ILI9488_BLUE);
  delay(250);
  tft.fillScreen(ILI9488_GREEN);
  delay(250);
  tft.fillScreen(ILI9488_BLACK);


}

void drawColor(uint16_t x, uint16_t y, const char *psz, uint16_t color)
{
  tft.setFontAdafruit();
  tft.setTextColor(color);
  tft.setTextSize(2);
  tft.setCursor(x, y);
  tft.print(psz);
  tft.drawRect(x + 100, y, 50, 50, color);
  tft.fillRect(x + 110, y + 10, 30, 30, color);
  tft.drawLine(x + 100, y + 70, x + 200, y + 70, color);
  tft.drawLine(x + 220, y, x + 220, y + 70, color);
  tft.drawLine(x + 100, y + 70, x + 220, y, color);
  tft.drawCircle(x + 50, y + 50, 28, color);
  tft.fillCircle(x + 50, y + 50, 20, color);
  tft.setFont(Arial_12_Bold);
  tft.setCursor(x + 160, y + 50);
  tft.print(psz);
}


//=============================================================================
// Loop
//=============================================================================
bool use_frame_buffer = false;
void loop()
{
  uint32_t time_start = millis();
#if defined(ENABLE_ILI9488_FRAMEBUFFER)
  tft.useFrameBuffer(use_frame_buffer);
#endif  
  tft.setFont(Arial_18_Bold);
  tft.setCursor(0, 150);
#if defined(ENABLE_ILI9488_FRAMEBUFFER)
  if (use_frame_buffer) {
    tft.fillScreen(ILI9488_GREENYELLOW);
  } else {
    tft.fillScreen(ILI9488_BLACK);
  }
#else
    tft.fillScreen(ILI9488_BLACK);
#endif  
  
  drawColor(0, 0, "Red", ILI9488_RED);
  drawColor(0, 80, "Green", ILI9488_GREEN);
  drawColor(0, 160, "Blue", ILI9488_BLUE);
  drawColor(0, 240, "White", ILI9488_WHITE);

  drawColor(240, 0, "Yellow", ILI9488_YELLOW);
  drawColor(240, 80, "Orange", ILI9488_ORANGE);
  drawColor(240, 160, "Cyan", ILI9488_CYAN);
  drawColor(240, 240, "Pink", ILI9488_PINK);
#if defined(ENABLE_ILI9488_FRAMEBUFFER)
  if (use_frame_buffer) {
    tft.updateScreen();
    use_frame_buffer = false;
  } else {
    use_frame_buffer = true;
  }
#endif
  if (Serial) {
    Serial.println(millis()-time_start, DEC);
  }
  delay(2500);
}
With the current checked in stuff it was showing about 2021... (so 2 seconds)...

I have now inlined some of the functions and unwound some of the code in write16bitColor, in this case to look like:
Code:
void ILI9488_t3::write16BitColor(uint16_t color, bool last_pixel){
...
#elif defined(KINETISL)
  uint8_t r = (color & 0xF800) >> 11;

  r = (r * 255) / 31;
  setDataMode();
  outputToSPI(r);
  uint8_t g = (color & 0x07E0) >> 5;
  g = (g * 255) / 63;
  outputToSPIAlready8Bits(g);
  uint8_t b = color & 0x001F;
  b = (b * 255) / 31;
  outputToSPIAlready8Bits(b);
  if (last_pixel) {
	waitTransmitComplete();
  } 

#endif
  // #endif
}
So some of the conversion code from 16 bit to 24 bit is done at the byte level to try to keep things moving...
So with these (unchecked in changes) timing is now down to about 1138 (almost twice as fast).

More I could try, but something new and shiny coming soon!
 
Good luck KurtE with the LC - that is a very slow place to start.

3D - nope just followed the link and saw the demo vids - surprising what the AVR could do. Have to go restore TyComm after putting on the new TeensyDuino - and maybe try to build the USBHost Mass Storage.

... just saw I cruised through post #8,000 ...

Edit - I put on TD 1.46b11 and re-integrated TyComm - also got the then current Master 9488 and it compiles and runs on the T$. I updated TSET to handle the renamed B1 and added b2 T$'s as written in boards.txt - I even made a branch to hold the pre_TD1.46b11 version
 
Last edited:
@KurtE - @defragster

Think maybe you should do the PR this weekend since next week we all are going to be "playing something new and shiny". You might say, better clear the decks for action.

BTW: Decided to give that 3D-engine a try on a ILI9488 and T$. It does work with almost no changes. But the objects are designed for 1.8in screen so a little tiny. Have to try a get a bigger wireframe object.
 
@mjs513 and @defragster and ...

Yes - new shiny stuff Monday...

With TLC it is showing several places for speed improvements that I think will help all of the different boards, some more than others, which I will try to try out today... Busy with 4 leg...

Example: One of the speed ups for TLC was changing the function: write16BitColor, current code computes r, g, b first then starts output...
Faster to first do r, startup transfer of r, then compute g... Also helped to have the helper functions back in header file compiled inline... Currently I have only done that part for TLC...

So may try moving some of the main helpers back into header file... (in-lined)...

Next change I would like to try is, to reduce how many times I need to do the color conversion...
That is for example if You call HLine, the code at the end (non frame buffer) does: do { write16BitColor(color); } while (--w > 0);

Well suppose I put in a function, write16BitColor(color, count, last_pixel, that did something like for TLC..:
Code:
void ILI9488_t3::write16BitColor(uint16_t color, uint16_t count, bool last_pixel){
#elif defined(KINETISL)
  uint8_t r = (color & 0xF800) >> 11;

  r = (r * 255) / 31;
  setDataMode();
  outputToSPI(r);
  uint8_t g = (color & 0x07E0) >> 5;
  g = (g * 255) / 63;
  outputToSPIAlready8Bits(g);
  uint8_t b = color & 0x001F;
  b = (b * 255) / 31;
  outputToSPIAlready8Bits(b);
  while (--count {
      outputToSPIAlready8Bits(r);
      outputToSPIAlready8Bits(g);
      outputToSPIAlready8Bits(b);
  }
  if (last_pixel) {
	waitTransmitComplete();
  } 

#endif
  // #endif
}
I think it will improve the performance of TLC by a lot, will hopefully get a chance to try soon. But I also think it will improve the other boards as well.

One thing I wish we had was graphictest program and set of numbers for several of the boards to get an idea of how much if any things speed up...

EDIT: OK there is a graphictest9488.ino, but it is in a folder named graphictest... So Arduino does not show it in examples...

EDIT2: Current Beta1 T4 Graphic test output (Had to also edit to put in &SPI in constructor...
Code:
ILI9488 Test!
Display Power Mode: 0x0
MADCTL Mode: 0x0
Pixel Format: 0x0
Image Format: 0x0
Self Diagnostic: 0x0
Benchmark                Time (microseconds)
Screen fill              734502
Text                     16412
Lines                    211351
Horiz/Vert Lines         62178
Rectangles (outline)     34663
Rectangles (filled)      1775578
Circles (filled)         232585
Circles (outline)        178366
Triangles (outline)      45419
Triangles (filled)       564945
Rounded rects (outline)  74145
Rounded rects (filled)   1951858
Done!

For TLC with the changes I did yesterday (would be good to see the older version... but still gives some reference)
Code:
Benchmark                Time (microseconds)
Screen fill              4533779
Text                     108260
Lines                    1146963
Horiz/Vert Lines         396634
Rectangles (outline)     220043
Rectangles (filled)      13611554
Circles (filled)         1628627
Circles (outline)        1143814
Triangles (outline)      242199
Triangles (filled)       4183305
Rounded rects (outline)  460970
Rounded rects (filled)   12607105
 
Last edited:
@KurtE - @defragster

Yep - I had did a mod for the 9488graphicstest but I thought I changed the directory name - need to start making lists of things to change.

I can't exactly remember why I had to move the preprocessor stuff into the .cpp because it was throwing errors when I was making changes to SPI - I think.

Reducing the number of time to make the color changes is a great idea. The funny thing is I just saw a similar function in the 3d engine sketch -spiWrite16Bit(color, w)….

As an aside I thing Paul said spi wasn't working on the 1062?

EDIT: Just updated the master to correct graphicstest directory and added &SPI to all the examples before I forgot or got sidetracked.
 
Back
Top