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

ILI9488 SPI clock speed limits

My guess is 60mhz is pushing it, especially depending on how well the display is connected (hard connections short lines...)
Am I missing something? According to the ILI9488 data sheet, V001, unlocked PDF copy, page 328, the min 4-line SPI SPI clock cycle period is 50 nsec for writes and 150 nsec for reads. That makes SPI clock a max of 20 MHz for Writes (Twc in the datasheet) and 6.67 MHz for Reads (Trc). We ran into a similar issue on the ILI9341 where most drivers ignored the datasheet speed limits and operation was not reliable. It seems a bit odd to me that ILI9488 writes are 3X faster than reads. Even odder, 3-line SPI write is slower: Twc = 66 nsec. Am I looking at the wrong data sheet? Does your current WIP driver use different write and read SPI clock speeds?
 
@defragster
Sorry I forgot about that post with all the other stuff going on.

@KurtE
Had some time before the CAT scan so here are the changes to test the SPI pins that I think you suggested:
1. In .cpp I now have this for begin
Code:
void ILI9341_t3n::begin(void)
{
    // verify SPI pins are valid;
	// allow user to say use current ones...
#ifdef DEBUG_ASYNC_UPDATE
	//Serial.printf("????? _dmasettings[0] %x %x\n", (uint32_t)&_dmasettings[0], (uint32_t)_dmasettings[0].TCD); Serial.flush();
#endif
	if ((_mosi != 255) || (_miso != 255) || (_sclk != 255)) {
		if (!(_pspin->pinIsMOSI(_mosi)) || !(_pspin->pinIsMISO(_miso)) || !(_pspin->pinIsSCK(_sclk))) {
			#ifdef SPIN1_OBJECT_CREATED			
			if (SPIN1.pinIsMOSI(_mosi) && SPIN1.pinIsMISO(_miso) && SPIN1.pinIsSCK(_sclk)) {
				_pspin = &SPIN1;
#ifdef KINETISK
				_pkinetisk_spi = &_pspin->port();
#elif defined(__IMXRT1052__) || defined(__IMXRT1062__)  // Teensy 4.x 
				_pimxrt_spi = &_pspin->port();
#else
				_pkinetisl_spi = &_pspin->port();
#endif				
				//Serial.println("ILI9341_t3n: SPIN1 automatically selected");
			} else {
				#ifdef SPIN2_OBJECT_CREATED			
				if (SPIN2.pinIsMOSI(_mosi) && SPIN2.pinIsMISO(_miso) && SPIN2.pinIsSCK(_sclk)) {
					_pspin = &SPIN2;
					_pkinetisk_spi = &_pspin->port();
					//Serial.println("ILI9341_t3n: SPIN2 automatically selected");
				} else {
				#endif
			#endif
					uint8_t mosi_sck_bad = false;
					Serial.print("ILI9341_t3n: Error not valid SPI pins:");
					if(!(_pspin->pinIsMOSI(_mosi)))  {
						Serial.print(" MOSI");
						mosi_sck_bad = true;
					}
					if (!_pspin->pinIsSCK(_sclk)) {
						Serial.print(" SCLK");
						mosi_sck_bad = true;
					}

					// Maybe allow us to limp with only MISO bad
					if(!(_pspin->pinIsMISO(_miso))) {
						Serial.print(" MISO");
						_miso = 0xff;	// set miso to 255 as flag it is bad
					}
					Serial.println();
					
					if (mosi_sck_bad) {
	    				return; // not valid pins...
					}
				#ifdef SPIN2_OBJECT_CREATED			
	    		}
	    		#endif
			#ifdef SPIN1_OBJECT_CREATED			
			}
			#endif

		}
		//Serial.printf("MOSI:%d MISO:%d SCK:%d\n\r", _mosi, _miso, _sclk);			
        _pspin->setMOSI(_mosi);
        if (_miso != 0xff) _pspin->setMISO(_miso);
        _pspin->setSCK(_sclk);
	}

	_pspin->begin();
#ifdef KINETISK
	if (_pspin->pinIsChipSelect(_cs, _dc)) {
		pcs_data = _pspin->setCS(_cs);
		pcs_command = pcs_data | _pspin->setCS(_dc);
	} else {
		// See if at least DC is on chipselect pin, if so try to limp along...
		if (_pspin->pinIsChipSelect(_dc)) {
			pcs_data = 0;
			pcs_command = pcs_data | _pspin->setCS(_dc);
			pinMode(_cs, OUTPUT);
			_csport    = portOutputRegister(digitalPinToPort(_cs));
			_cspinmask = digitalPinToBitMask(_cs);
			*_csport |= _cspinmask;
		} else {
			pcs_data = 0;
			pcs_command = 0;
  			Serial.println("ILI9341_t3n: Error not DC is not valid hardware CS pin");
			return;
		}
	}
#elif defined(__IMXRT1052__) || defined(__IMXRT1062__)  // Teensy 4.x 
	_csport = portOutputRegister(_cs);
	_cspinmask = digitalPinToBitMask(_cs);
	pinMode(_cs, OUTPUT);	
	DIRECT_WRITE_HIGH(_csport, _cspinmask);
	_spi_tcr_current = _pimxrt_spi->TCR; // get the current TCR value 

	// TODO:  Need to setup DC to actually work.
	if (_pspin->pinIsChipSelect(_dc)) {
	 	_pspin->setCS(_dc);
	 	_dcport = 0;
	 	_dcpinmask = 0;
	} else {
		Serial.println("ILI9341_t3n: Error not DC is not valid hardware CS pin");
		_dcport = portOutputRegister(_dc);
		_dcpinmask = digitalPinToBitMask(_dc);
		pinMode(_dc, OUTPUT);	
		DIRECT_WRITE_HIGH(_dcport, _dcpinmask);
	}
	maybeUpdateTCR(LPSPI_TCR_PCS(1) | LPSPI_TCR_FRAMESZ(7));

#else
	// TLC
	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;
#endif	

	// toggle RST low to reset
	if (_rst < 255) {
		pinMode(_rst, OUTPUT);
		digitalWrite(_rst, HIGH);
		delay(5);
		digitalWrite(_rst, LOW);
		delay(20);
		digitalWrite(_rst, HIGH);
		delay(150);
	}
/*
	uint8_t x = readcommand8(ILI9341_RDMODE);
	Serial.print("\nDisplay Power Mode: 0x"); Serial.println(x, HEX);
	x = readcommand8(ILI9341_RDMADCTL);
	Serial.print("\nMADCTL Mode: 0x"); Serial.println(x, HEX);
	x = readcommand8(ILI9341_RDPIXFMT);
	Serial.print("\nPixel Format: 0x"); Serial.println(x, HEX);
	x = readcommand8(ILI9341_RDIMGFMT);
	Serial.print("\nImage Format: 0x"); Serial.println(x, HEX);
	x = readcommand8(ILI9341_RDSELFDIAG);
	Serial.print("\nSelf Diagnostic: 0x"); Serial.println(x, HEX);
*/	
	beginSPITransaction();
	const uint8_t *addr = init_commands;
	while (1) {
		uint8_t count = *addr++;
		if (count-- == 0) break;
		writecommand_cont(*addr++);
		while (count-- > 0) {
			writedata8_cont(*addr++);
		}
	}
	writecommand_last(ILI9341_SLPOUT);    // Exit Sleep
	endSPITransaction();
	delay(120); 		
	beginSPITransaction();
	writecommand_last(ILI9341_DISPON);    // Display on
	endSPITransaction();

#ifdef DEBUG_ASYNC_LEDS
	pinMode(DEBUG_PIN_1, OUTPUT);
	pinMode(DEBUG_PIN_2, OUTPUT);
	pinMode(DEBUG_PIN_3, OUTPUT);
#endif
}

2. I commented the duplicated lines in the constructor:
Code:
/*
 	uint32_t *pa = (uint32_t*)((void*)spi_port);
	_spi_hardware = (SPIClass::SPI_Hardware_t*)(void*)pa[1];
    #ifdef KINETISK
	_pkinetisk_spi = (KINETISK_SPI_t *)(void*)pa[0];
	_fifo_size = _spi_hardware->queue_size;		// remember the queue size
	#elif defined(__IMXRT1052__) || defined(__IMXRT1062__)
	_pimxrt_spi = (IMXRT_LPSPI_t *)(void*)pa[0];
	#endif
*/
 
@mjs513 @defragster

...

@defragster - I need to go back and reread your questions
...

Yeah - it is either something dumb on my part - but possibly something in the library with transition between FB and LIVE is confused. Perhaps if I went back to your first unmodified example ... got it ... post #38

Put some more moderate mods for delays to show FB active and it is working:
{{ EDITED 5pm 4/18 }}
Code:
//=============================================================================
// 1st:: https://forum.pjrc.com/threads/55735-ILI9488_t3-Support-for-the-ILI9488-on-T3-x-and-beyond?p=203151&viewfull=1#post203151
// 2nd:: https://forum.pjrc.com/threads/55735-ILI9488_t3-Support-for-the-ILI9488-on-T3-x-and-beyond?p=203577&viewfull=1#post203577
//=============================================================================

#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()
{
	tft.begin();
	tft.setRotation(3); // 180
	while (!Serial && millis() < 4000 );
	Serial.println("\n" __FILE__ " " __DATE__ " " __TIME__);

	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);
}

bool use_frame_buffer = false;
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);
}

void drawColorDelay(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);
	delay(30);
	tft.fillRect(x + 110, y + 10, 30, 30, color);
	delay(30);
	tft.drawLine(x + 100, y + 70, x + 200, y + 70, color);
	delay(30);
	tft.drawLine(x + 220, y, x + 220, y + 70, color);
	tft.drawLine(x + 100, y + 70, x + 220, y, color);
	if (use_frame_buffer && (color == ILI9488_GREEN || color == ILI9488_CYAN || color == ILI9488_YELLOW)) {
		tft.updateScreen();
		delay(210);
	}
	delay(30);
	tft.drawCircle(x + 50, y + 50, 28, color);
	tft.fillCircle(x + 50, y + 50, 20, color);
	delay(30);
	tft.setFont(Arial_12_Bold);
	tft.setCursor(x + 160, y + 50);
	tft.print(psz);
}


//=============================================================================
// Loop
//=============================================================================
int hh=0;
void loop()
{
	if ( hh>10 ) 	{
		for ( int oo=0; oo<4; oo++ )
		loop1();
		delay(500);
	}
	tft.useFrameBuffer(use_frame_buffer);
	tft.setFont(Arial_18_Bold);
	tft.setCursor(0, 150);
	if (use_frame_buffer) {
		tft.fillScreen(ILI9488_DARKGREY);
	} else {
		tft.fillScreen(ILI9488_BLACK);
	}
	int myX1, myX2;
	if ( use_frame_buffer ) {
		myX1 = 240;
		myX2 = 0;
	}
	else {
		myX1 = 0;
		myX2 = 240;
	}
	drawColor(myX1, 0, "Red", ILI9488_RED);
	drawColor(myX1, 80, "Green", ILI9488_GREEN);
	drawColor(myX1, 160, "Blue", ILI9488_BLUE);
	drawColor(myX1, 240, "White", ILI9488_WHITE);
	//if (!use_frame_buffer) delay(1000);
	drawColor(myX2, 0, "Yellow", ILI9488_YELLOW);
	drawColor(myX2, 80, "Orange", ILI9488_ORANGE);
	drawColor(myX2, 160, "Cyan", ILI9488_CYAN);
	drawColor(myX2, 240, "Pink", ILI9488_PINK);
	if (use_frame_buffer) {
		tft.updateScreen();
		use_frame_buffer = false;
	} else {
		use_frame_buffer = true;
	}
	hh++;
	delay(200);
}

void loop1()
{
	hh=0;
	tft.useFrameBuffer(use_frame_buffer);
	tft.setFont(Arial_18_Bold);
	tft.setCursor(0, 150);
	if (use_frame_buffer) {
		tft.fillScreen(ILI9488_DARKGREY);
	} else {
		tft.fillScreen(ILI9488_BLACK);
	}
	int myX1, myX2;
	if ( use_frame_buffer ) {
		myX1 = 240;
		myX2 = 0;
	}
	else {
		myX1 = 0;
		myX2 = 240;
	}
	drawColor(myX1, 0, "Red", ILI9488_RED);
	drawColor(myX1, 80, "Green", ILI9488_GREEN);
	drawColor(myX1, 160, "Blue", ILI9488_BLUE);
	drawColor(myX1, 240, "White", ILI9488_WHITE);
	if (!use_frame_buffer) delay(1000);
	drawColor(myX2, 0, "Yellow", ILI9488_YELLOW);
	drawColor(myX2, 80, "Orange", ILI9488_ORANGE);
	drawColor(myX2, 160, "Cyan", ILI9488_CYAN);
	drawColor(myX2, 240, "Pink", ILI9488_PINK);
	if (use_frame_buffer) {
		tft.updateScreen();
		use_frame_buffer = false;
	} else {
		use_frame_buffer = true;
	}
	delay(2500);
}
 
Last edited:
@bboyes - Yes that is the speeds that I saw as well (Page 332 of PDF)... So I figured trying for 30mhz was already pushing it...

@mjs513 and @defragster - I put in a hack to get the T3.5 to output using DMA at least on SPI0... (Pushed up to my WIP branch)

As I mentioned I was losing the CS pin... So the Hack is when/if you call updateScreenAsync on T3.5 it will convert to using the CS pin as a digital pin that is controlled as part of the begin/endTransaction code...

Test program is running... Not sure yet if there are some offsets of data or not... May need to try it out...

Then need to look at thigns in previous postiings...
 
Interesting on the speeds - with direct PCB header to T_3.6 [Teensy64] and T$'s 4.5" ribbon wires to breadboard I've not seen any glitches - read speed may be slower than needed - but write speed min was 30 MHz - but works fine as noted below at 60 and 72 MHz requested.

And bumps from 30 to 60 and 72 clearly complete transfers faster.

The code below is nice for faster speed and I've run for days with it. Perhaps putting it in HEADER with a USER_OVERCLOCK to enable for the end use case where longer wires or setup cause it to be intermittent?

Code:
#if defined(__MK64FX512__) || defined(__MK66FX1M0__)
#if F_BUS >= 64000000
#define ILI9341_SPICLOCK 64000000
#define ILI9341_SPICLOCK_READ 4000000
#else
#define ILI9341_SPICLOCK 30000000
#define ILI9341_SPICLOCK_READ 2000000
#endif
#elif defined(__IMXRT1052__) || defined(__IMXRT1062__)  // Teensy 4.x
#define ILI9341_SPICLOCK 72000000
#define ILI9341_SPICLOCK_READ 4000000
#else
#define ILI9341_SPICLOCK 30000000
#define ILI9341_SPICLOCK_READ 2000000
#endif

I know when I tried TOUCH SPI faster than that in the PJRC file it went wacky ... i.e. going over 2 MHz :: 'SPISettings(2000000, MSBFIRST, SPI_MODE0)'
 
@defragster - I added the if-defines for SPI Clock into the master at the last update. So it made it in.

@KurtE and everyone,
I added the corrections to the init table as well as the pin tests for SPI identified in post #152
 
@mjs513 - Was thinking of doing PR of T3.5 stuff. Still have a few fixes to do, like figure out why offset by a pixel...

We may also need to do some merge fixes, as both fixed table... But should be easy... I also put in change to readRectangle code to use fifo queue size instead of 4 as I mentioned previously...
 
@KurtE

Wasn't sure when you were going to do the PR so I went ahead and updated the table. No problem with the PR. Whenever you want to do have at it.
 
@mjs513 - Totally understand

Note: I squashed all of the changes of the T3.5 stuff back into one commit and it looks like it will merge without conflict :D
https://github.com/mjs513/ILI9488_t3/pull/4

Figured it would be good to get everything back into one place and then try to fix the issues (that I most likely created :D )

Note: Here is simple test app that shows the pixels being offset or slightly different between
updateDisplay() and updateDisplayAsync()...
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);

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

//=============================================================================
// Setup
//=============================================================================
void setup()
{
  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);

  tft.fillScreen(ILI9488_BLACK);
  tft.useFrameBuffer(true);
  tft.setPallet(our_pallet, sizeof(our_pallet) / sizeof(our_pallet[0]));  tft.colorsArePalletIndex(true);
}
//=============================================================================
// Loop
//=============================================================================
void loop() {
  tft.fillScreen(0);  // Turn all black;
  tft.drawLine(0, 0, tft.width()-1, 0, 1);
  tft.drawLine(0, 0, 0, tft.height()-1, 1);
  tft.drawLine(0, tft.height()-1, tft.width()-1, tft.height()-1, 2);
  tft.drawLine(tft.width()-1, 0, tft.width()-1, tft.height()-1, 2);

  tft.drawLine(0, 0, tft.width(), tft.height(), 3);
  tft.drawLine(0, tft.height()-1, tft.width()-1, 0, 3);
  tft.updateScreen();
  delay(1000);
  tft.updateScreenAsync();
  tft.waitUpdateAsyncComplete();
  delay(1000); 
}
At some point need to retry it on T3.6 and T4 to make sure they are working properly.
 
@KurtE

You are right - there were no merge conflicts. The merge is complete - will give it a test :)
 
@KurtE

Bad news, just tested on a T3.5, T3.6 and the T$ using the following sketch:
Code:
//=============================================================================
//=============================================================================
#include <ILI9488_t3.h>
#include <ILI9488_t3_font_Arial.h>
#include <ILI9488_t3_font_ArialBold.h>

#define TEENSY64

#if defined(__MK66FX1M0__) && !defined(TEENSY64)
#define TFT_RST 255
#define TFT_DC 20
#define TFT_CS 21
ILI9488_t3n tft = ILI9488_t3n(&SPI, TFT_CS, TFT_DC, TFT_RST);
#elif defined(__IMXRT1052__) || defined(__IMXRT1062__)
// On Teensy 4 beta with Paul's breakout out:
// Using pins (MOSI, MISO, SCK which are labeled on Audio board breakout location
// which are not in the Normal processor positions
// Also DC=10(CS), CS=9(BCLK) and RST 23(MCLK)
#define TFT_RST 23
#define TFT_DC 9
#define TFT_CS 10
ILI9488_t3 tft = ILI9488_t3(&SPI, TFT_CS, TFT_DC, TFT_RST);
#elif defined(TEENSY64)
#define TFT_RST 255
#define TFT_DC 20
#define TFT_CS 21
#define TFT_SCK 14
#define TFT_MISO 39
#define TFT_MOSI 28
ILI9488_t3 tft = ILI9488_t3(&SPI, TFT_CS, TFT_DC, TFT_RST, TFT_MOSI, TFT_SCK, TFT_MISO);
#else
#define TFT_RST 8
#define TFT_DC 9
#define TFT_CS 10
ILI9488_t3 tft = ILI9488_t3(&SPI, TFT_CS, TFT_DC, TFT_RST);
#endif

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

//=============================================================================
// Setup
//=============================================================================
void setup()
{
  tft.begin();
  tft.setRotation(3); // 180
  delay(100);

  tft.fillScreen(ILI9488_BLACK);
  tft.useFrameBuffer(true);
  tft.setPallet(our_pallet, sizeof(our_pallet) / sizeof(our_pallet[0]));
  tft.colorsArePalletIndex(true);
  delay(250);
}
//=============================================================================
// Loop
//=============================================================================
int zz = 0;
uint32_t non_frame_buffer_time[2] = {0, 0};
uint32_t frame_buffer_time[2] = {0, 0};
void loop()
{
  // First simple pallet output sync output
  tft.fillScreen(0);
  uint16_t x = 5;
  uint16_t y = 5;

  for (uint8_t i = 1; i < (sizeof(our_pallet) / sizeof(our_pallet[0])); i++) {
    tft.drawRect(x, y, tft.width() - x * 2, tft.height() - y * 2, i);
    x += tft.width() / 16;
    y += tft.height() / 16;
  }
  tft.updateScreen();
  delay(2500);

  x = 5;
  y = 5;

  // next simple pallet Async output;
  for (uint8_t i = 1; i < (sizeof(our_pallet) / sizeof(our_pallet[0])); i++) {
    tft.fillRect(x, y, tft.width() - x * 2, tft.height() - y * 2, i);
    x += tft.width() / 16;
    y += tft.height() / 16;
  }
  tft.updateScreenAsync();  // Try an async update...
  tft.waitUpdateAsyncComplete();

  delay(2500);

  // Next lets try some continuous outputs...
  // Lets update the screen in 16 chunks, every 2 frames.
  uint32_t next_update_frame_count = 2;
  tft.setTextColor(0);
  tft.setTextSize(2);
  tft.setCursor(5, 5);
  tft.print("Cont...");
  tft.updateScreen();
  tft.updateScreenAsync(true);  // Try an async update...
  for (uint16_t i = 1; i <= 16; i++) {
    // wait for the next frame...
    while (tft.frameCount() < next_update_frame_count) ;
    x = 5;
    y = 5;
    tft.setClipRect(0, 0, tft.width(), i * (tft.height() / 16));
    for (uint8_t i = 1; i < (sizeof(our_pallet) / sizeof(our_pallet[0])); i++) {
      tft.fillRect(x, y, tft.width() - x * 2, tft.height() - y * 2, 8 - i);
      x += tft.width() / 16;
      y += tft.height() / 16;
    }
    next_update_frame_count += 2;
  }
  tft.setClipRect();
  tft.endUpdateAsync();  // Tell system to cancel async updates
  tft.waitUpdateAsyncComplete();

  delay(1000);

 tft.fillScreen(0);  // Turn all read;
  tft.drawRect(0, 0, tft.width()-1, tft.height()-1, 1);
  tft.drawLine(0, 0, tft.width(), tft.height(), 2);
  tft.drawLine(0, tft.height()-1, tft.width()-1, 0, 2);
  tft.updateScreen();
  delay(500);
  tft.updateScreenAsync();
  tft.waitUpdateAsyncComplete();
  delay(1000);

  for (uint16_t kk = 0; kk < 4; kk++) {
    for (uint16_t jj = 1; jj < sizeof(our_pallet) / sizeof(our_pallet[0]); jj++) {
      for (uint16_t i = 1; i <= 16; i++) {
        x = 5;
        y = 5;
        tft.setClipRect(0, 0, tft.width(), i * (tft.height() / 16));
        for (uint8_t i = 1; i < (sizeof(our_pallet) / sizeof(our_pallet[0])); i++) {
          tft.fillRect(x, y, tft.width() - x * 2, tft.height() - y * 2, ((jj + i) % 9));
          x += tft.width() / 16;
          y += tft.height() / 16;
        }
      }
      tft.updateScreen();
      tft.waitUpdateAsyncComplete();
    }
  }
  delay(1000);
}

T3.5 - Worked fine - just as it should

T3.6 - The simple pallet Async output test, the rectangle was shifted over by about 0.5in and the missing part on the right of the screen was wrapped around on the left side and the sketch froze when it got to the continuous outputs test

T$ = same as for T3.6 except the sketch ran fine but anything with framebuffer/async wrapped.

Now the problem could be with the code....
 
Open sketch w/mods above p#153 working T$ > Latest commit a446d4b an hour ago from github.com/mjs513/ILI9488_t3

Above code runs ~10 times no delays during draw - 5 each with and without FrameBuffer - then runs two each with displays to allows watching some drawing - then repeats. It looks to be working well on T$.

FrameBuffer draws on DarkGray background and also it toggles the left and right half colors

@mjs513 - I see the SPI SPEED #defines are there. When I get T_3.5 up at 120 I'll look to have it pick 60 MHz on f_BUS==60

<edit>: Just put 9488 on a T_3.5 PJRC OSH board and p#135 code works with RST=255 at 120 MHz - and 144 and 168
 
Last edited:
@KurtE
Just ran your posted sketch in post#159. I am seeing the rectangle/diag lines shift to the left by a pixel like you said but then on the tft.updateScreenAsync(); it shift to the right by about 1/2 inch.
 
Hi @mjs513...
Looks like I need to move the displlay again to different Teensy... Not sure if T36 or 4?

Will try 3.6
 
I'm not sure I remember enough to ask an intelligent question, but here goes. I am trying to dig up an old project of mine that used the ILI9341_t3DMA library that was really a proof of concept at the time I implemented it. However, as I recall, it was the only one at the time that was using the full framebuffer and DMA for the synchronous write. What I'm wondering is - now with your latest libraries (which look great, btw!), can I use the framebuffer and DMA together for the synchronous, or one-shot writes to the lcd? If I read this from Kurt's github, it sounds like it:

The code now has support to use DMA for Asynchronous updates of the screen. You can choose to do the updates once or in continuous mode. Note: I mainly use the oneshot as I prefer more control on when the screen updates which helps to minimize things like flashing and tearing. Some of the New methods for this include:

But I want to make sure I'm understanding it correctly. I can't use the asynchronous write because I'm muxing the SPI bus in order to control a whole set of screens as though they are just one. This means that I cannot switch to another screen until after the DMA completes, but since I have multiple screens, I need each to be as fast as possible to do a full refresh of the entire set as fast as possible.
 
@mjs513 - I pushed up a fix, that looks like it fixes T3.6... Have not tried T4 yet, but fingers crossed!

I don't see any shifting on T3.6 so updated real simple app, to put square in for Async update...
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);

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

//=============================================================================
// Setup
//=============================================================================
void setup()
{
  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);

  tft.fillScreen(ILI9488_BLACK);
  tft.useFrameBuffer(true);
  tft.setPallet(our_pallet, sizeof(our_pallet) / sizeof(our_pallet[0]));  tft.colorsArePalletIndex(true);
}
//=============================================================================
// Loop
//=============================================================================
void loop() {
  tft.fillScreen(0);  // Turn all read;
  tft.drawLine(0, 0, tft.width()-1, 0, 1);
  tft.drawLine(0, 0, 0, tft.height()-1, 1);
  tft.drawLine(0, tft.height()-1, tft.width()-1, tft.height()-1, 2);
  tft.drawLine(tft.width()-1, 0, tft.width()-1, tft.height()-1, 2);

  tft.drawLine(0, 0, tft.width(), tft.height(), 3);
  tft.drawLine(0, tft.height()-1, tft.width()-1, 0, 3);
  tft.updateScreen();
  delay(1000);
  tft.drawRect(tft.width()/2-25, tft.height()/2-25, 50, 50, 4);
  tft.updateScreenAsync();
  tft.waitUpdateAsyncComplete();
  delay(1000); 
}
 
P167 code nice on T_3.5 - it is centered and shows color chase on edges as I assume it changes modes - alternate with and without white center box.

On T_3.6 it is nicely centered with latest Oops_t36_fix ZIP, HOWEVER - I do not see the edge chase/color change on T_3.6_Teensy36 - the white center box draws and undraws - but no color line border change.

<EDIT>:: Glancing at code - I wonder if it is the T_3.5 that is broken as there is not any attempt to switch modes - but the T_3.5 border is rotating/shifting the colors ...
{update} - looking at T_3.5 right edge the edge isn't chasing - but shifting - green right line is hitting top left edge and full left edge - that goes yellow.

Before and after latest code on both of those the prior post with P153 code works as expected
 
Last edited:
@KurtE -- @defragster

Ran the sketch in p161 and the latest GITHUB update seems to fix the problem.

T4 running p167 isn't showing the green lines towards the connector(left) right side looks good (green lines only on right and bottom of screen), On the T3.5 I see the Green lines on right/bottom then it will change to yellow/green on the left/bottom but the yellow line is shifted to the left by a pixel,, there is also a short green line on top of the screen when it switches (top left).

So easy to get things screwy when you are working with three different boards :)
 
@mjs513 ... ;)

Yep the screwy part is the DMA/SPI work differently on all three boards. So Before T3.5 support I tried to make sure that T3.6 (last one tested) was not showing any offsetting... Did that again last night... Looks like I need to do same back to T4... Back to moving lots of wires around :D Maybe should have ordered more than one of these displays... (but again this one on Ebay they only had one...).

Wonder how well the one from Amazon Prime would work? (https://smile.amazon.com/Touch-Screen-Module-Resolution-Arduino-Mega2560/dp/B07F2XM5HW/)
Or course I always wonder about descriptions like:
This 3.5inch TFT LCD Module is equipped with high resolution, Resolution reaches to 480 x 320, Controller ili9481 ili9468, ili9488 hx8357, or r61581.

Then maybe at some point soon back to T3.5... I am guessing timing issues... Like maybe the dma stuff was not ready when the queue started and it filled some of the output queue with garbage or the like... Or maybe swallowed byte...
 
I'm not sure I remember enough to ask an intelligent question, but here goes. I am trying to dig up an old project of mine that used the ILI9341_t3DMA library that was really a proof of concept at the time I implemented it. However, as I recall, it was the only one at the time that was using the full framebuffer and DMA for the synchronous write. What I'm wondering is - now with your latest libraries (which look great, btw!), can I use the framebuffer and DMA together for the synchronous, or one-shot writes to the lcd? If I read this from Kurt's github, it sounds like it:



But I want to make sure I'm understanding it correctly. I can't use the asynchronous write because I'm muxing the SPI bus in order to control a whole set of screens as though they are just one. This means that I cannot switch to another screen until after the DMA completes, but since I have multiple screens, I need each to be as fast as possible to do a full refresh of the entire set as fast as possible.

Note: ILI9341_t3DMA library is by @Frank B, so he can better answer on how to use his library...

I have ILI9341_t3n which from the inspiration of Frank's library I then added frame buffer, and then have three different ways to update the display from the frame buffer, these same functions are now being implemented in this library)
a) updateScreen - This is synchronous - it will not return until the update is done. The speed of this is like doing a fillScreen() call...
b) updateScreenAsync() - One shot (note there is an optional parameter which defaults to false) - This does a one shot update of the screen and returns after it starts, and there are calls to either wait until it completes or test if still running.
c) updateScreenAsync(true) - this starts a continuous update.

Also it is unclear to me from your description on how you are using it and how you want/expect it to work. If you want to update multiple displays that use a MUX to control... Then doing Asynchronous updates may or may not work for you. Again depending on your work flow...

Using Frame buffer - I should point out only enough memory for one of these... So I assume when you tell the MUX that you wish to change screens, you have to redraw the whole frame for the new selected screen.

Does having frame buffer help here? Does having frame buffer here help? I don't know. Again don't know what you are displaying/updating... If only some simple thing like updating a number or two in the display for something like Speed or miles... Maybe simple to update these directly without frame buffer... If more complex stuff then maybe...

If you decide to use Frame buffer, which of the update methods would work for you. If you want the fastest, then the A) above is it as it blasts out the whole screen as fast as it can... But if you want to use the time it is taking to update the screen to do some other stuff, like grab information from sensors... Then maybe B) is the best... But you should not change the Frame buffer until the Async update completes...

Note: There may be ways to semi relax the don't touch the frame buffer until the update completes with THIS library, although I have not exported it, but there is a sub-frame count that I keep that knows how much of the frame has been updated, and so you could potentially fill in behind this.... But ...

Hope that helps
Kurt
 
@KurtE

I have enough jumpers that I have a T4 and T3.5 setup on breadboards with the spi wires in place and use a 10pin header with long legs so I can easily move the display around. For the T3.6 I use Frank's Teensy64. So that makes my life easier :)

EDIT: Forgot - not sure if that would work. Didn't see a DC pin unless one to r-pins is equivalent? Plus its a shield which leads to its own issues.
 
@mjs513 - I am just sort of whining :lol: - It currently is not too bad unless I wish to mess things up...
As long as keep with standard pins: 8-13 I have some long breadboard pins, which pins 8-12 connect to, so move as group, from breadboard to breadboard,
then move 13, GND, 3.3v... I am using T4 on breadboard, plus T35 on breadboard, and currently T3.6 original beta board, but could use T3.6 on breadboard as well...
Frank's board and/or my own board for the ILI9341 display won't help here as my display has complete different pins (40 pin connector - not RPI)

Currently I have it on T4 and my test program #167 and don't see any shifting... Will now try one with continuous updates:
Which looks like it is working as well (I added the draw rectangle into it as well so as to see when it was displaying the updateScreen versus the updateScreenAsync.

Note: I made a local change that I also meant to do yesterday to turn of some Serial printing (out DMA information)... You might want to do as well
(line 65 in the CPP file - comment out the define)

Now back to maybe one or two more attempts with T3.5...
 
@mjs513 and @defragster - I just pushed up a new branch T35_v2 which appears like it fixed the T3.5 output shifting...
Don't think it should effect T3.6 nor 4.0 as changes are in #ifdef of the 3.5...

For the fun of it, I updated the simple test program, that only was showing differently the square near center, to measure how much time it takes to do:
updateScreen vs updateScreenAsync()/waitUpdateAsyncComplete as I know that with T3.5 there are gaps between each time I startup the DMA...
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);

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

//=============================================================================
// Setup
//=============================================================================
void setup()
{
  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);

  tft.fillScreen(ILI9488_BLACK);
  tft.useFrameBuffer(true);
  tft.setPallet(our_pallet, sizeof(our_pallet) / sizeof(our_pallet[0]));  tft.colorsArePalletIndex(true);
}
//=============================================================================
// Loop
//=============================================================================
uint32_t time_sync = 0;
uint32_t time_async = 0;

void loop() {
  tft.fillScreen(0);  // Turn all read;
  tft.drawLine(0, 0, tft.width()-1, 0, 1);
  tft.drawLine(0, 0, 0, tft.height()-1, 1);
  tft.drawLine(0, tft.height()-1, tft.width()-1, tft.height()-1, 2);
  tft.drawLine(tft.width()-1, 0, tft.width()-1, tft.height()-1, 2);
  
  tft.drawLine(0, 0, tft.width(), tft.height(), 3);
  tft.drawLine(0, tft.height()-1, tft.width()-1, 0, 3);

  tft.setTextColor(4);
  tft.setCursor(10, 150);
  tft.setTextSize(2);
  tft.print(time_sync, DEC);
  tft.setCursor(10, 170);
  tft.print(time_async, DEC);
  
  uint32_t time_start = millis();
  tft.updateScreen();
  time_sync = millis() - time_start;
  delay(1000);
  tft.drawRect(tft.width()/2-25, tft.height()/2-25, 50, 50, 4);
  time_start = millis();
  tft.updateScreenAsync();
  tft.waitUpdateAsyncComplete();
  time_async = millis() - time_start;
  delay(1000); 
}
And it is showing about 193 for straight update and 206 for async on T3.5... Have not tried it on other boards yet, but should be close to the normal update... (no gaps)...

I can do PR now, but may wait and try a few things to see if it speeds up the Async on 3.5
 
Back
Top