ILI9341 with fullscreen DMA Buffer for Teensy 3.5 / Teensy 3.6 only

Status
Not open for further replies.
Does the benchmark from above work?

I started investigation using ILI9341_DMALIB_graphicstet example from github commit 6b0b9bd723a41e88cf2c86bb76916aae62f1f29a of Oct. 31st. Is it the benchmark you are thinking of ?

As it did not work, I tried refreshOnce() instead of refresh().
I have experimented using dfillScreen and ddrawLine, to witness the fact that only one call to refreshOnce() works.

How do you guys debug teensy software? Same as I do with Serial outputs ? Or do you have a more convenient toolchain with JTAG probe ? (please reply this using PM, in order to avoid polluting the thread)
 
Hi all,
A single refreshOnce() call does the job, but a second call seems to throw an exception... I suppose (?)
Damn, debuging with only serial output is a real pain.
Yes, I ran into this earlier when I was playing with refreshOnce.

I had a fix for it that worked for me. (part of the earlier Pull request)
I updated:
Code:
void ILI9341_t3DMA::stopRefresh(void) {
  dmasettings[SCREEN_DMA_NUM_SETTINGS - 1].disableOnCompletion();
  wait();  
  SPI.endTransaction();
  [B]dmatx.disable();[/B]
  autorefresh = 0;
[B]  started = 0;[/B]
}
I added the two bold lines.

Kurt

P.S. - Great stuff
 
Yes, I ran into this earlier when I was playing with refreshOnce.

I had a fix for it that worked for me. (part of the earlier Pull request)
I updated:
Code:
void ILI9341_t3DMA::stopRefresh(void) {
  dmasettings[SCREEN_DMA_NUM_SETTINGS - 1].disableOnCompletion();
  wait();  
  SPI.endTransaction();
  [B]dmatx.disable();[/B]
  autorefresh = 0;
[B]  started = 0;[/B]
}
I added the two bold lines.


Kurt

P.S. - Great stuff


First, Kurt, please excuse, i did not merge your pullrequest, instead i closed it. this was not intended !

Ok, i took the time and tried to find the problem.
I'm not sure.. but it seems that something is wrong with replaceSettingsOnCompletion() for t3.5
Currently, i have no real idea what it is, the manuals of k64&k66 show no difference on this point..
 
Hey Frank,

I tried the modification of stopRefresh() as you suggested.

Well, I can't deny it does get better : I can now achieve 2 refreshOnce() calls in a row. But not 3...

I really don't understand, but maybe there is something to look for down that path.
 
I posted this note about TOUCH usage for that ILI9341 on this other thread: yet-another-version-of-ILI9341_t3-library-(ILI9341_t3n) - Kurt noted it applies more here:
This sounds very cool! Did I miss a note on Touch? >> To use Touch it would have to use an alternate SPI? Or on SAME SPI use the Interrupt detection code to stop and start the AUTO DMA updates and go SLOW MO normal during touch event. That should be easy to do right inside the TOUCH library code if there is an easy way to detect and disable/enable the AUTO refresh of the ILI9341. Probably take 5 minutes with those details. Would add a global and that 'detect' test on TOUCH Start (and wait for all clear on the display CS?) and global check on TOUCH Stop.

For use in TOUCH this may be called when DMA not active - is there a way to have Touch code caller know if it was active when called to know to call the equivalent startRefresh on exit? I imagine the code change when interrupts are in use would be trivial - would that call for an extra parameter when interrupt pin is given to declare DMA_Refresh sharing? Or perhaps a separate XPT2046_Touchscreen_DMA? [I'll go scan something on github]

<edit>: The stopRefresh() code doesn't check if (started) - so I assume the multiple calls and wait(); to stop it become NOP's? I see refesh() is the complement of stopRefresh(), but no way to just ask if ( autorefresh ).

<edit2>: code read error - removed
 
Last edited:
using the lib on T3.5

That's strange.
Thank you for that hint :)

I had some time to proceed with my investigation on what the hell is happening using the lib with T3.5.

I am not familiar with DMA, and without JTAG I feel like I'm a blind without a cane. I keep hitting walls.

Anyway, the fact is that the lib gets stuck at the wait() method, when waiting for transfer completion interrupt.

With Frank's mod, I can manage to get one interrupt, and thus display 2 screens with refreshOnce().

With Frank's mod PLUS this mod on the begin() method, I get to display 4 screens with refreshOnce(): (in blue bold)
Code:
void ILI9341_t3DMA::begin(void) {
  ILI9341_t3::begin();
  const uint32_t bytesPerLine = ILI9341_TFTWIDTH * 2;
  const uint32_t maxLines = (65536 / bytesPerLine);
  uint32_t i = 0, sum = 0, lines;
  do {

	//Source:
	lines = min(maxLines, ILI9341_TFTHEIGHT - sum);
	uint32_t len = lines * bytesPerLine;
	dmasettings[i].TCD->SADDR = &screen[sum][0];
	dmasettings[i].TCD->SOFF = 2;
	dmasettings[i].TCD->ATTR_SRC = 1;
	dmasettings[i].TCD->NBYTES = 2;
	dmasettings[i].TCD->SLAST = -len;
	dmasettings[i].TCD->BITER = len / 2;
	dmasettings[i].TCD->CITER = len / 2;

	//Destination:
	dmasettings[i].TCD->DADDR = &SPI0_PUSHR;
	dmasettings[i].TCD->DOFF = 0;
	dmasettings[i].TCD->ATTR_DST = 1;
	dmasettings[i].TCD->DLASTSGA = 0;
	dmasettings[i].replaceSettingsOnCompletion(dmasettings[i + 1]);
	sum += lines;
	
[COLOR="#0000CD"][B]	//Interrupt
	dmasettings[i].interruptAtCompletion();[/B][/COLOR]
	
  } while (++i < SCREEN_DMA_NUM_SETTINGS);
  dmasettings[SCREEN_DMA_NUM_SETTINGS - 1].replaceSettingsOnCompletion(dmasettings[0]);

  dmatx.begin(false);
  dmatx.triggerAtHardwareEvent(DMAMUX_SOURCE_SPI0_TX );
  dmatx = dmasettings[0];

  dmatx.attachInterrupt(dmaInterrupt);
 [COLOR="#0000CD"][B] //dmasettings[SCREEN_DMA_NUM_SETTINGS - 1].interruptAtCompletion();[/B][/COLOR]

  dfillScreen(0);
};

Probably someone who has a better comprehension of DMA, and better knowledge of the lib can better understand what is happening, and can find the fix.

I modified the bench as follows, in order to use refreshOnce() instead of refresh() :
Code:
void setup() {
  tft.begin();

  //START DMA MODE
  //tft.refresh();
    

  Serial.begin(9600);
  while (!Serial) ; // wait for Arduino Serial Monitor
  Serial.println("ILI9341 Test!"); 
  
  Serial.println(F("Benchmark                Time (microseconds)"));

  Serial.print(F("Screen fill              "));
  Serial.println(testFillScreen());
  tft.refreshOnce();delay(200);

  Serial.print(F("Text                     "));
  Serial.println(testText());
  tft.refreshOnce();delay(600);

  Serial.print(F("Lines                    "));
  Serial.println(testLines(ILI9341_CYAN));
  tft.refreshOnce();delay(200);

  Serial.print(F("Horiz/Vert Lines         "));
  Serial.println(testFastLines(ILI9341_RED, ILI9341_BLUE));
  tft.refreshOnce();delay(200);

  Serial.print(F("Rectangles (outline)     "));
  Serial.println(testRects(ILI9341_GREEN));
  tft.refreshOnce();delay(200);

  Serial.print(F("Rectangles (filled)      "));
  Serial.println(testFilledRects(ILI9341_YELLOW, ILI9341_MAGENTA));
  tft.refreshOnce();delay(200);

  Serial.print(F("Circles (filled)         "));
  Serial.println(testFilledCircles(10, ILI9341_MAGENTA));

  Serial.print(F("Circles (outline)        "));
  Serial.println(testCircles(10, ILI9341_WHITE));
  tft.refreshOnce();delay(200);

  Serial.print(F("Triangles (outline)      "));
  Serial.println(testTriangles());
  tft.refreshOnce();delay(200);

  Serial.print(F("Triangles (filled)       "));
  Serial.println(testFilledTriangles());
  tft.refreshOnce();delay(200);

  Serial.print(F("Rounded rects (outline)  "));
  Serial.println(testRoundRects());
  tft.refreshOnce();delay(200);

  Serial.print(F("Rounded rects (filled)   "));
  Serial.println(testFilledRoundRects());
  tft.refreshOnce();delay(200);

  Serial.println(F("Done!"));
  tft.stopRefresh();


}

Now, I need some sleep :D
 
Last edited:
Ok,

i give up - this scatter-gather feature does not work, and i don't know why it works with K66 and not with K64. I'm stuck.
There's an other way to do it, with more DMA-Channels & interrupts on completion.

This method will have more overhead - but ok, if you want more speed,you should buy the k66 anyway (and it has more memory).
 
Hi Frank,

and others.

I don't think I would be much help understanding the DMA problems with T3.5... But can probably setup test setup if that would help.

Also I can not tell from above if some of you are trying to use refreshOnce as the preferred way or because they are having issues with automatic refresh.

But with the current API for refresh, I am not sure that DMA is actually helping any performance. I am not sure if it is still more or less doing a full screen update as part of the start of the DMA and then would do one full update using DMA, and then waits for that to complete. I am assuming that you will find a way to get it to work, probably needing to deassert CS...

Again currently I believe refreshOnce is a synchronous call (waits until done). However I do understand that DMA could be beneficial if there was a set of asynch calls, like refreshOnceBegin, isRefreshOnceDone, waitForRefrenshOnceDone or something like that... Just a thought.

However before then, wonder if maybe have refreshOnce be implemented something like:

Code:
void ILI9341_t3DMA::refreshOnce(void) {
  if (!autorefresh) {
	writeRect( 0, 0, _width, _height, screen16);
  }
}
 
I finally found a breadcrumb to an issue that's been annoying me for days, but I don't understand it at all. Radio data on the SPI bus is corrupted if I ever call tft.refresh() or tft.refreshOnce() inside a sketch that also uses this RF24 library:

https://github.com/TMRh20/RF24

This occurs even if the entire radio segment of the code is completed before the call to this DMA library, i.e. do some things with the radio in setup and later on in setup call tft.refreshOnce(). I'm kind of at a loss right now so I thought I'd share in case this makes sense to anyone else.
 
Do you know if this is the case - to make sure it is using SPI_Transactions? It seems like the CS pin isn't reset and the radio is responding to the Display's SPI transfers:
#if defined SPI_HAS_TRANSACTION && !defined SPI_UART && !defined SOFTSPI

#define RF24_SPI_TRANSACTIONS

#endif

Without RF24_SPI_TRANSACTIONS #define'd it seems the code doesn't terminate the link.
 
Also I can not tell from above if some of you are trying to use refreshOnce as the preferred way or because they are having issues with automatic refresh.

As for me, I am trying to get refresh() to work, as it is what I'm gonna need to use in the end.
However, I use refreshOnce() as an investigation mean.
 
I make the 3.5 work this weekend -with a special solution for 3.5 using interrupts (without scatter/gather)
 
It looks more like a combined DMA/SPI problem at the moment..
I have a new version, both ways (scatter/gather + interrupts) work fine on T3.6
On 3.5, interrupts ..hm.. ok, autorefresh works, but it seems to transfer 8 bit only (instead of 16). perhaps CTAR is ignored ?
 
Ok, it is a SILICON BUG.
CTAR1 is not used when using 16-BIT writes to PUSHR with DMA....

Looks like i have to find a workaround...
 
Do you know if this is the case - to make sure it is using SPI_Transactions? It seems like the CS pin isn't reset and the radio is responding to the Display's SPI transfers:


Without RF24_SPI_TRANSACTIONS #define'd it seems the code doesn't terminate the link.

Thanks for the suggestion. I suspected that too and there was actually an issue where all the conditions should have been met to enable RF24_SPI_TRANSACTIONS but it wasn't enabled. I fixed that and the issue persists, but it probably isn't related to this library though because it shows up under other sets of circumstances too. I'm beginning to suspect a hardware issue with noise or power issues. Anyway, it is no longer a subject for this thread. If I can't track it down I'll start an appropriate thread.
 
Ok, it is a SILICON BUG.
CTAR1 is not used when using 16-BIT writes to PUSHR with DMA....

Looks like i have to find a workaround...

...and i did not find one that works reliable.

Maybe later - or perhaps someone else :)
For the T3.5, i decided NOT to use DMA anymore - the automatic refresh() is disabled for 3.5, refreshonce() uses SPIFIFO.
If you want to try it, you can add a "#define DMA" for T3.5 -but, as mentioned, it is not reliable.

The next version will remove the old ILI9341_t3.h, and all functions will be renamed to back to original names - means the "d" will be removed.
 
Last edited:
Hi Frank,

Not sure if I can help or not, but I do have most all of the graphic primitives in my version that is similar setup to either go to screen or to screen buffer.

Sorry to hear about the T3.5. Probably good I used a T3.6 in my semi-flexiboard that I built. Maybe need to partially assemble one with T3.5.
 
Hi Frank,

Thank you for the effort you put into this... even though it does not always lead to where you want.

Sounds like I'll need to buy a T3.6... at least, I have found the reason I was looking for.
 
Ok, it is a SILICON BUG.
CTAR1 is not used when using 16-BIT writes to PUSHR with DMA....

Is this something you found in the processor bugs list, or is it something you assumed from your testing ?
I looked at the silicon bugs list, and I could not link this to any.
 
The next version will remove the old ILI9341_t3.h, and all functions will be renamed to back to original names - means the "d" will be removed.

Do you know when you'll be pushing this version out? I assume the link provided at the beginning of this thread is still correct.

Looking at the history of this thread I'm left with the impression that this library pretty much requires exclusive use of the SPI bus, is that correct? Disabling DMA so other things can hop on the bus seems to pretty much defeat the purpose of going DMA in the first place.
 
Yes the link ist still correct.

Of course it requires eclusive SPI - if you use the automatic refesh. The display refreshes constantly in this mode.
But you don't need to use it and use refreshOnce() instead. This refreshes the display one time.
 
Status
Not open for further replies.
Back
Top