Playing around with yet another version of ILI9341_t3 library (ILI9341_t3n)

Trying to keep the TX FIFO full while waiting for the read is a nice idea, but isn't there a chance that this could cause a problem by reading actual pixel data if it's added to the RX FIFO before the dummy byte is read out?

Thanks Michael: I will take a look again. I was having an issue where I was not receiving any of the pixel data back and thought at first it might be a timeout issue, so I wanted to keep from getting a gap. But I was finding that it was more related to which byte I was pressing. Sending out a 0x00 I was not receiving data back. But others have not seen this issue. So will take a look again soon. Maybe once I build my 2nd "flexi" board with the touch screen.
 
Thought I would also mention, that I pushed up a few more things today to my library:

a) Example program: touchpaint_xpt2046 - Since the library is setup to use the PJRC products including the touch screen. Might be nice to have at least one of the example programs here setup for the PJRC product!

b) Merge in the header file change for detecting that Adafruit_GFX.h was already loaded and to not define the button class. Note: I did not test this, other than to verify that my current stuff still compiles. Not sure how this works in program that had the initial problem. That is it avoids defining it in the header file, but the button code will still compile in the .cpp file. So not sure if these cases if you might get link problem of duplicate symbols????

c) Updated Readme - Thought it was time to update this readme to remove the, maybe temporary comment, plus described more of the differences from this library to the ILI9341_t3 library. Things like the differences in the constructor, to allow you to pass in the SPIN object and that the current code if it finds that the MISO/MOSI/SCK pins are not valid for the SPIN passed in, it will check them against SPIN1 and SPIN2 to see if they are valid.

Also mentions, that I added the additional API's that are in a long standing Pull request to allow the user to specify a drawing Origin and clipping rectangle. Also code support for Opaque drawing of Font characters.

And in addition, mentions that there is optional code in place that for T3.5/3.6 to allow the ability to turn on a logical Frame Buffer mode, where all of the graphic primitives write to memory and there is a new api that says update the screen now...

At times I keep wondering, how much of this newer functionality makes sense to merge back into the mainline ILI9341_t3 library. I would obviously be willing to do most/all of the work. But it then brings up questions, like should things like SPIN be made more built in?

Also likewise, I wonder if I should be trying to migrate the Frame buffer stuff into Frank's great DMA version of the library. The main difference here is, in my usage cases for frame buffer, I am not looking for lots of frames per second, but instead an Update once, but in addition maybe an Update once that did not wait for the update to complete. Maybe instead it starts the update, and have query function that I could call to say are you done yet...

Now back to playing around!
 
Productive playtime! Looks good - I'll see if I can't enjoy some time with it.

A quick look and the map is compile time rotation - not runtime dynamic:
ILI9341TouchTest.ino
Indeed a mapped updated version should get checked in the PJRC tree - I really thought I did that with my prior work - like the above year old code :(

With T_3.6 having multiple usable SPI - the direction of SPIN seems good to allow it to be used reliably and consistently.

Does the ILI9341_t3n break anything or make it fatter to any bad end for T_3.2/LC and prior use?

I'm inclined to like the Update Once case for general use as I've done it. A wait-less Update would be awesome - though TOUCH would need separate SPI or a way to be aware of DMA xfer in progress.
 
Hi Kurt,

I just posted a pull request for you :)
https://github.com/KurtE/ILI9341_t3n/pull/2

Mainly it's a port of a change I made for ILI9341_t3 that fixes the readRect last pixel bug. However, while testing I also discovered problems with the standalone CS pin usage, so I fixed those as well. Figured it's something you might want to try out.
 
Hey, I was just testing some code on the Teensy 3.6, and I ran some benchmarking.

Using fillScreen from the ILI9341_t3 library:
Teensy 3.1 overclocked to 95Mhz gives 56239 microseconds (17 frames per second)
Teensy 3.6 at 180Mhz gives 44948 microseconds (22 frames per second)
Teensy 3.6 overclocked to 240Mhz gives 44936 microseconds (22 frames per second)

That's not really as much of a speedup as I was expecting.

Am I doing something wrong? Are we limited by the SPI clock speed on the screen? On the Teensy?
I've seen guys pushing out composite video on an 8 bit Arduino, so surely there is a way to update this screen fast enough!

-- Tim --
 
Hey, I was just testing some code on the Teensy 3.6, and I ran some benchmarking.

Using fillScreen from the ILI9341_t3 library:
Teensy 3.1 overclocked to 95Mhz gives 56239 microseconds (17 frames per second)
Teensy 3.6 at 180Mhz gives 44948 microseconds (22 frames per second)
Teensy 3.6 overclocked to 240Mhz gives 44936 microseconds (22 frames per second)

That's not really as much of a speedup as I was expecting.

Am I doing something wrong? Are we limited by the SPI clock speed on the screen? On the Teensy?
I've seen guys pushing out composite video on an 8 bit Arduino, so surely there is a way to update this screen fast enough!

-- Tim --

Increasing the CPU speed does not necessarily increase the BUS speed (which is used for SPI).
You may want to increase F_BUS, too.
I'm running the ILI9341 with 60MHz SPI-Clock (use shortest connections possible!)
 
Tearing prevention: using ILI9341 TE signal

Wow, @KurtE where do you all find the time to do this? Impressive. I will try this library. I just skimmed this thread, and didn't see this discussed: any chance of supporting tearing prevention? We have designed a "remote display" board which uses the ER-TFT028-4 from BuyDisplay and can work with either res or cap touch options. It connects to our custom Teensy 3.2-powered board with a 2x8 100-mil header and ribbon cable, with signals buffered via 74HVC125, so the cable can be at least 2-3 feet long (maybe longer, we will find out). I have the first unit here in my queue to test. TE is brought out to Teensy 3.2 D3. My intent was to eliminate the flickering you can see on the display while updating it. This assumes I understand "tearing" in this context and we can figure out how to add this to the library.
 
Last edited:
Just don't update the lines that are displayed in that moment :) I fear, you have to modify the library - every now existing ILI9341 library.
It might help to reduce the ILI9341 refresh-rate. I don't know which rate Kurts lib uses - the ILI9341_t3 uses 119Hz (for a few weeks now)
to minimize flicker without TE.

*Edit: More exact: Don't write to areas where the current internal "Display-write-pointer" is. Otherwise all after the "pointer" is new content, all before old - this is the "flicker".
 
Last edited:
*Edit: More exact: Don't write to areas where the current internal "Display-write-pointer" is. Otherwise all after the "pointer" is new content, all before old - this is the "flicker".

Frank B, could you elaborate, i'm a little confused on your explanation.
 
Changed F_BUS in kinetis.h to improve framerate, thanks Frank B!

Increasing the CPU speed does not necessarily increase the BUS speed (which is used for SPI).
You may want to increase F_BUS, too.
I'm running the ILI9341 with 60MHz SPI-Clock (use shortest connections possible!)

Ah, thanks for the tip! I had tried changing SPICLOCK in the ILI9341_t3.cpp, but since the SPI is set to max at 1/2 F_BUS, it didn't really do anything. I didn't realize F_BUS could be changed.

It turns out it is located in "kinetis.h", and there were even some commented out values for overclocking! I tried both.

New Results:
Teensy 3.6 overclocked to 240Mhz with F_BUS at 80000000 gives 33711 microseconds (29 frames per second)
Teensy 3.6 overclocked to 240Mhz with F_BUS at 120000000 gives 22492 microseconds (44 frames per second)

That is certainly a vast improvement, although I still haven't hit my 60 FPS target. I'll have to do more tests to see if the higher clock speed still works with the Teensy Audio Board, and if it affects SD card access at all.

Thanks again for all your help!

-- Tim --
 
Hi,

I think Frank has far more understanding of this then I do. I have not updated anything for a little bit, so I may not have picked up the change that was done to ILI9341_t3 for the refresh rate. Soon I will take a look to see if there have been any updates I have not picked up.

I am not sure how the tearing effect works here and see artifacts or flicker. Also for me if you are running the display where it updates over 100 times per second, I am not sure you will notice that one frame has parts of one screen and the earlier parts has the previous screen... But maybe.

The issues I have seen with flicker and artifacts have more to do with how you write yoru code.

For example if you have a field on your display to display something like some numeric value, the obvious way to update this field is to do something like:
tft.fillRext(xField, yField, wField, hField, BackgroundColor)
tft.drawText(xField, yField, NewText); //

Note: probably don't have valid syntax here, but more to show the flow. What this will do is to first fill in the area to get rid of the stuff, then draw the new text (non opaque output). This will tend to flicker that text.

A better way is to use Opaque text, which draws over the the old text with the next text, which works better. However if the number you output before was 100 and now you are outputting 75, the new draw text may not cover up all of the old text and leave artifacts. If you are left justifying, it is easy and you can simply do a fill rect from where the text output completed and to where the previous output completed. It is a little more complex if you are doing center or right justify... But not that hard.

Frank's great DMA version of the library allows you to do a bunch of your updates in offscreen dma memory and then update the screen. However if you are using the automatic updates, once it finishes downloading the screen to the the display it will start over. So whatever is in the buffer at that time will be put on the screen. So if for example you are using the FillRect/DrawText type updates you may get this type of flicker. However if you use his UpdateOnce type function, you are in control to do all of your updates and then say update me now. So hopefully when you do this, your display memory is in a good consistent state and my guess is almost always some of the update will be on one hardware screen refresh and the rest of it will be on the next (or so). I have not calculated the time it takes for you to send the full display over SPI to the display.

So in my library I was not as interested in the Automatic update code but do like the idea of off screen buffer to make it easier to do updates without having to worry about that level of flicker or artifacts. However at some point it would be great if we converged. That is I think it would be great to be able to use the DMA to say update the screen once and not have to wait for that to complete before doing something else. The something else may want to check to see if the update had completed yet before it did any more stuff that updated the display.

Note: I think I understand what Frank was saying, but not sure if I could explain it any better. What I am reading is suppose the hardware buffer goes from lets say from 0-76799 actually to make it easier suppose it goes from 0-99. Suppose the display pointer is now at 50, if you write to 0-49, that output will properly be displaying for the next frame as these locations have already been output to the display. So if you can somehow keep track of this pointer, and for example it has moved its way up to 80, you can continue to output the next frames data up to memory 79 and still not have the new data influence the current frame. The only tricky part is now the write pointer goes up to 99 and then wraps back to 0 to start the next frame. At this point you need to logically finish writing out the next frames data from where you last left off through 99 and get there before the hardware write pointer works it's way back up there. Again sorry this may not be completely clear and Frank please correct me if I misunderstood what you were saying.

Kurt
 
As far as tearing goes, perhaps the Get_Scanline commane (0x45) could be polled to detect display update synchronization, and then all pixels sent during the blanking period, or just after the necessary line has been processed?

If you have access to the TE line (how? I want to do it!) then you could put that on an input pin with an interrupt and use that to schedule your drawing the same way. Obviously, you need to set up the TE pin depending on whether you want horizontal blanking signals or not using the Tearing Effect Line ON command (35h), and what line you want it to alert you on using the Set_Tear_Scanline command (44h).

Whatever you do, some doctoring of the TFT libraries is going to be required.

-- Tim --
 
If you have access to the TE line (how? I want to do it!)
I need to test out our display PCB in the next couple of days. If it looks good perhaps we make some more and a little adapter board for Teensy and make it available to interested people? The display has each signal buffered in anticipation of driving a few feet of ribbon cable, so the buffers are needed on the Teensy end. Other than that there is nothing special about it. We made the board so it can use either the resistive or capacitive touch versions and that was more involved than we expected. I suspect the new library and some more careful programming will fix most of the artifacts we see but I am also really curious to see what we can do with the TE signal.
 
Not sure if anyone is interested, but...

Yesterday I started experimenting with my version of the library here (ili9341_t3n), that on the T3.5/6 will support using DMA to be able to update the screen, when I am using the frame buffer mode I added earlier. I believe I now have the beginnings of it working. I pushed a copy up in a new branch (https://github.com/KurtE/ILI9341_t3n/tree/Try-DMA-Screen-Updates) in case anyone is interested in taking a look.

Note: most of the heavy lifting for this was done by FrankB in his ili9341_t3dma library. Most of these additions were inspired directly from his work! If desired I could try to merge in some of my updates into the main dma library and issue a Pull request...

State of things: Currently I am mainly interested in doing one shot updates. Sort of like his refreshOnce. However I wanted my version to be async as not sure if you gain much using DMA if you then directly wait for it to complete. I also wanted to address the issue, that the current refeshOnce code actually draws the screen twice. Once by doing direct SPI outputs and then turn on DMA to draw it again once... Which is not much of an issue for continuous refresh mode as you only take the hit once at startup, but for single mode, makes it slower than not using DMA... I understand why it was done as needed at least one SPI write with the appropriate upper word after the writecommand_cont(ILI9341_RAMWR); was issued. And then you need to get your outputs synchronized with memory address to screen location...
So simplest was to output whole screen, so simplest to output whole screen first to be in sync.

I solved it now in my version, currently method is called updateScreenDMA but will probably be called beginUpdateScreen once everything is working, by having this function, output the first word (color of screen[0][0] in the DMA library. Mine is vector so _pfbtft[0]. I then have my first dmasettings object start off ad _pfbtft[1] and decremented how much should be outputed by this dmasetting by one word. Which is not a problem for my one shot usage.

Note with the display size of 320x240 with 2 bytes per element and each dmasetting can output up to 64K we have 3 dmabuffers that get linked to each other:
0->1->2->0. Which in my one shot usage appears to work fine as I have it Stop/Interrupt when it finishes 2. However when/if I add continuous updates, this would introduce a problem as I shortened 0 by 1 word, so the next passes would not output enough data and the screen will not be properly in sync.

The way I am thinking of solving this is to allocate a 4th dmasetting, which is a copy of 0, except start at _pfbtft[0] and with the extra word length and then link the buffers: 0->1->2->3->1...

Then would need to make sure it is set to _dmatx = _dmasettings[0], each time I am about to start the refresh code, for either a one shot or continuous update. Not sure if that makes sense.

Some other things I need to address include, currently the code is setup to only work with SPI not SPI1 or SPI2. Things like:
_dmasettings.TCD->DADDR = &SPI0_PUSHR;
_dmatx.triggerAtHardwareEvent(DMAMUX_SOURCE_SPI0_TX );

Need to be updated to the appropriate memory locations and/or DMA source. The first one is easy with my SPIN stuff as my display object has a pointer to the SPI registers. so the first can probably just be:
_dmasettings.TCD->DADDR = &(_pkinetisk_spi->PUSHR);
The second one I could easily hack but I may update my SPIN class code to have a method which returns the appropriate DMAMUX...

But a more interesting issue/question is my library also has the ability to turn on a clipping rectangle and when it is on with the frame buffer mode, when I use the update method, it only outputs that region of memory to the display.

That is suppose I am simply updating a region of the display for some portion of the screen like: (100, 50) for width 150, height 30... And I do something like:
set clip rect, fillrect to background, draw updated text in this region, and then do update. the refresh code would only output that much data (150*30) words of data to the screen. Question is What should I do in DMA case? punt and output whole screen? Yes I don't have to wait for it, but it will tie up usage of SPI for that duration. or Punt2: if small enough just use the non-dma code?

or is there an easy way to use DMA to do this. I could of course probably set up in above case to do only full rows as this would still give me continuous memory.
But if I really only want to output that rectangle, would I need to set up each DMASetting to only do one partial row and maybe setup interrupt to maybe setup something like double buffer dmasettings? Where when one completes and the next starts, to update the pointer for the first one?...

Sounds like I need to do a few more experiments, but suggestions would be very welcome!

Kurt
 
Quick update: Did the update to not hard code SPI0_ stuff in the dma support above. This included adding methods to my SPIN objects to be able to get the approriate DMA TX event number.

Now maybe will try adding the continuous update support, although I am not sure I need this for any of my stuff...
 
Yesterday I started to play around to get ready to try continuous updates. But have been running into interesting issues with the compiler (mostly using current beta and mentioned some of the issues on the beta thread).

But another issue I am running into with the compiler (which is probably problems with my code) includes things like:
in my display class I added some DMASettings:
DMASetting _dmasettings[SCREEN_DMA_NUM_SETTINGS+1];

Where: #define SCREEN_DMA_NUM_SETTINGS (((uint32_t)((2 * ILI9341_TFTHEIGHT * ILI9341_TFTWIDTH) / 65536UL))+1)
Which comes out to 3.

(The +1) was added to handle the stuff I mentioned earlier of a duplicate item but with first byte included...

Earlier yesterday I made this array static and had a define in the cpp file to allocate it... Actually the stuff up on github was setup that way. And it appeared to be fine when using LTO, but when I tried using without LTO, the program Hung, when I tried to access the array.

I added a bunch of serial prints to see where it might be hanging...
Code:
		if (_pfbtft == NULL) {
			_pfbtft = (uint16_t *)malloc(CBALLOC);
			if (_pfbtft == NULL)
				return 0;	// failed 
			memset(_pfbtft, 0, CBALLOC);	
			uint8_t dmaTXevent = _pspin->dmaTXEvent();

			// BUGBUG:: check for -1 as wont work on SPI2 on T3.5
			uint16_t *fbtft_start_dma_addr = _pfbtft;
			uint32_t count_words_write = (CBALLOC/SCREEN_DMA_NUM_SETTINGS)/2; // Note I know the divide will give whole number
			Serial.printf("CWW: %d %d %d\n", CBALLOC, SCREEN_DMA_NUM_SETTINGS, count_words_write);
			// Now lets setup DMA access to this memory... 
			for (uint8_t i = 0; i < SCREEN_DMA_NUM_SETTINGS; i++) {
				//Source:
				Serial.println(i, DEC); 
				Serial.println((uint32_t)_dmasettings[i].TCD, HEX);
				_dmasettings[i].TCD->SOFF = 2;
				Serial.println("SOFF");
				_dmasettings[i].TCD->ATTR_SRC = 1;
				Serial.println("attr_src");
				_dmasettings[i].TCD->NBYTES = 2;
				Serial.println("Before i test");
				if (i == 0) {
					Serial.println("0 branch");
					_dmasettings[0].TCD->SADDR = &_pfbtft[1];
					Serial.println("After SADDR");
					_dmasettings[0].TCD->SLAST = -(count_words_write-1)*2;
					_dmasettings[0].TCD->BITER = count_words_write - 1;
					_dmasettings[0].TCD->CITER = count_words_write - 1;

				} else {
					_dmasettings[i].TCD->SADDR = fbtft_start_dma_addr;
					_dmasettings[i].TCD->SLAST = -count_words_write*2;
					_dmasettings[i].TCD->BITER = count_words_write;
					_dmasettings[i].TCD->CITER = count_words_write;
				}

				Serial.println("Dest"); 
				//Destination:
				_dmasettings[i].TCD->DADDR = &(_pkinetisk_spi->PUSHR);
				_dmasettings[i].TCD->DOFF = 0;
				_dmasettings[i].TCD->ATTR_DST = 1;
				_dmasettings[i].TCD->DLASTSGA = 0;
				if (i < (SCREEN_DMA_NUM_SETTINGS-1)) {
					_dmasettings[i].replaceSettingsOnCompletion(_dmasettings[i + 1]);
				} else {
					_dmasettings[i].replaceSettingsOnCompletion(_dmasettings[0]);
					_dmasettings[i].interruptAtCompletion();
				    _dmasettings[i].disableOnCompletion();
				}

				fbtft_start_dma_addr += count_words_write;
				Serial.printf("DMS %d %x: %x %d %d %d\n", i, (uint32_t)&_dmasettings[i], 
					_dmasettings[i].TCD->SADDR, _dmasettings[i].TCD->SLAST,
					_dmasettings[i].TCD->BITER, _dmasettings[i].TCD->CITER);

			}
			// Setup a duplicate one for full copy of initial part of structure.
			_dmasettings[SCREEN_DMA_NUM_SETTINGS].TCD->SOFF = 2;
			_dmasettings[SCREEN_DMA_NUM_SETTINGS].TCD->ATTR_SRC = 1;
			_dmasettings[SCREEN_DMA_NUM_SETTINGS].TCD->NBYTES = 2;
			_dmasettings[SCREEN_DMA_NUM_SETTINGS].TCD->SADDR = &_pfbtft[0];
			_dmasettings[SCREEN_DMA_NUM_SETTINGS].TCD->SLAST = -count_words_write*2;
			_dmasettings[SCREEN_DMA_NUM_SETTINGS].TCD->BITER = count_words_write;
			_dmasettings[SCREEN_DMA_NUM_SETTINGS].TCD->CITER = count_words_write;

			//Destination:
			_dmasettings[SCREEN_DMA_NUM_SETTINGS].TCD->DADDR = &(_pkinetisk_spi->PUSHR);
			_dmasettings[SCREEN_DMA_NUM_SETTINGS].TCD->DOFF = 0;
			_dmasettings[SCREEN_DMA_NUM_SETTINGS].TCD->ATTR_DST = 1;
			_dmasettings[SCREEN_DMA_NUM_SETTINGS].TCD->DLASTSGA = 0;
			_dmasettings[SCREEN_DMA_NUM_SETTINGS].replaceSettingsOnCompletion(_dmasettings[1]);

			// Setup DMA main object
			_dmatx.begin(false);
			_dmatx.triggerAtHardwareEvent(dmaTXevent);
			_dmatx = _dmasettings[0];
			_dmatx.attachInterrupt(dmaInterrupt);
		}
The line: Serial.println("SOFF");
the SOFF did not output. Now maybe I should do flushes to maybe localize farther. However if I remove the static and make it an instance variable instead of class variable then the code did not hang.... But at least my last test of it did not update the screen properly...

Still investigating!

Edit: should mention the serial print: Serial.println((uint32_t)_dmasettings.TCD, HEX);
Did output a non-null value... Thought I should verify that... Should probably also output the address of _dmasettings to see if the pointer looked OK...
 
Last edited:
Another update:

As I mentioned in another thread I found that the corruption and weird results was caused by a stupid memory corruption bug in my test program.

When I first wrote the program I did a readRect and did a writeRect to some other location. I then tried to inplace convert the bitmap to a 8bit bitmap. When I did it I knew there would only be a limited number of colors so I only allocated maybe 16 colors for the table... But things kept changing in the code and later added a gradient fill, so my conversion overloaded the color array and I corrupted memory :eek: Fixed that now made more progress with DMA stuff.

I am still trying to modify the chain after each run, but I get into trouble when I try to update the dmaChannel object after the first run. Probably need to clear some flag... Will play with that again...

Right now I am doing something a little screwy. In particular I added a 4th dmaSetting object, which I linked into the end, which only outputs the first pixel. The idea is that in continuous mode I beginTransaction and then Output the bounding rectangle, followed by the WriteMemory command, and then I output the first pixel as to properly switch the DC off, I then start the DMA process which outputs all of the rest and then wraps and writes the first element. Then in one shot mode, the interrupt is generated and dma is stopped. The interrupt process then also does the endTransaction.

Which appears to be working. But then I used logic analyzer and found as I suspected that the CS line was not released... So I added a writecommand_last(ILI9341_NOP); to the interrupt handler before the endtransaction is called... This now appears to properly release the CS

So now I think I can easily setup to do continuous output. But still with this setup, the first pixel is the last one output, although since circular probably not an issue. Only issue may be if one wants to setup interrupt to happen before first item, such that maybe external code code be called to say a new frame has started...
Now to experiment adding simple test for continuous updates.
 
Another quick update. Today I added the ability to go into continuous DMA update mode. I think I have it working... Still needs some cleaning up.

I uploaded the current stuff up into the new branch of library. I also included the updated test program that can test some of this stuff. Test program is stupid, but at least it can try out more of the stuff... I still need to do a code cleanup and remove/comment out lots of debug outputs. Also might be fun to try out the test examples for the _t3dma library to see how well this holds up.

As I mentioned the current continuous udpate test is pretty lame:
Code:
void testDMAContUpdate() {
  // Force frame buffer on
  Serial.printf("continuous DMA udpate test - Frame mode on\n"); Serial.flush();
      use_fb = 1; //
  
  tft.useFrameBuffer(use_fb);
  tft.fillScreen(ILI9341_GREEN);

  tft.updateScreenDMA(true);

  uint32_t frame_count;  

  while ((frame_count = tft.frameCount()) < 10) yield();
  tft.fillScreen(ILI9341_YELLOW);

  while ((frame_count = tft.frameCount()) < 20) yield();
  tft.fillScreen(ILI9341_RED);

  while ((frame_count = tft.frameCount()) < 30) yield();
  tft.fillScreen(ILI9341_BLACK);

  while ((frame_count = tft.frameCount()) < 40) yield();

  tft.setCursor(0, 100);
  tft.setFont(Arial_20_Bold);
  tft.println("DONE");

  tft.endUpdateScreenDMA();

  Serial.println("Finished test");


}
 
Impressive thanks for doing this KurtE and Frank for the inspiration.
I gotta give this a shot as soon as I figured out what's with the font naming.
I've been trying to do identical DMA screen update for 2 days with no results :(

Thanks ;)
 
So I finally had time to play with this Library. Although there are some issues with the dma, you can't modify the buffer while the dma is transferring the data.
Because of that issue I have some delay set up so I don't try to print to the buffet while the DMA is transferring the data.
Hey Kurt by the way the buffer Works excellent.
I love it.
IMG_demo.jpg

Of course you have to modify the #define F_BUS in kinetis.h to get the faster speed.
Also had to modify ILI9341_t3n.h :
// At all other speeds, ILI9241_KINETISK__pspi->beginTransaction() will use the fastest available clock
#define ILI9341_SPICLOCK 60000000
//#define ILI9341_SPICLOCK 30000000

Here's the sketch I was playing with.
Code:
// HariChord, 2015
// Playing with OLED Display
// Big thanks to AdaFruit for providing the wonderful libraries!

/*********************************************************************
   This is an example for our Monochrome OLEDs based on SSD1306 drivers

   Pick one up today in the adafruit shop!
   ------> http://www.adafruit.com/category/63_98

   This example is for a 128x64 size display using SPI to communicate
   4 or 5 pins are required to interface

   Adafruit invests time and resources providing this open source code,
   please support Adafruit and open-source hardware by purchasing
   products from Adafruit!

   Written by Limor Fried/Ladyada  for Adafruit Industries.
   BSD license, check license.txt for more information
   All text above, and the splash screen must be included in any redistribution
 *********************************************************************/

#define LOGO16_GLCD_HEIGHT 16
#define LOGO16_GLCD_WIDTH  16
static const unsigned char PROGMEM logo16_glcd_bmp[] =
{
  B00000000, B11000000,
  B00000001, B11000000,
  B00000001, B11000000,
  B00000011, B11100000,
  B11110011, B11100000,
  B11111110, B11111000,
  B01111110, B11111111,
  B00110011, B10011111,
  B00011111, B11111100,
  B00001101, B01110000,
  B00011011, B10100000,
  B00111111, B11100000,
  B00111111, B11110000,
  B01111100, B11110000,
  B01110000, B01110000,
  B00000000, B00110000
};


#include <SPIN.h>
#include <SPI.h>
#include "ILI9341_t3n.h"
#include "ili9341_t3n_font_ArialBold.h"

uint8_t use_dma = 0;
uint8_t use_fb = 0;

#define TFT_CS 10
#define TFT_DC  15
//#define RST 15 // reset pin / to 3.3v
ILI9341_t3n tft = ILI9341_t3n(TFT_CS, TFT_DC); // RST connected to 3.3v

elapsedMillis since_Update;
// uint8_t BackLight = 8;  // TFT PWM BackLight
// uint8_t Debug_led = 6;
float TIMER;
float TIMER2;

int nFrames = 250;

void setup()   {

  // start demo
  // ---- Configuration for the demo ----
  use_fb = 1; // 1 = Force frame buffer on
  use_dma = 0; // 1 = Force DMA on
  // ---- Configuration for the demo ----
   
  //Serial.begin(115200);
  
  tft.begin();
  tft.setRotation(3); // TFT Rotation 0~3

  tft.useFrameBuffer(use_fb); // frame buffer on
  // tft.updateScreenDMA(true); // continuous update

  // Clear the display.
  tft.fillScreen(ILI9341_BLACK);

  // Show Adafruit splashscreen image on the display hardware.
  tft.drawBitmap(160, 120,  logo16_glcd_bmp, 16, 16, ILI9341_WHITE);
  tft.updateScreenDMA(); // Update Once DMA
  delay(1000);

  tft.setFont(Arial_9_Bold);
  tft.setTextColor(ILI9341_WHITE);

  tft.updateScreenDMA(); // Update Once DMA
  delay(2000);
  // Clear the display.
  tft.fillScreen(ILI9341_BLACK);
  delay(200);

  since_Update = 0; // elapsedMillis
}

void loop() {
  for (int frame = 0; frame < nFrames; frame++)
  {
    HariChord(frame);
    if (frame == nFrames - 1) {
      TIMER2 = TIMER / 10;
      TIMER = 0;
    }
  }

  for (int frame = (nFrames ); frame >= 0; frame--)
  {
    HariChord(frame);
    if (frame == 0) {
      TIMER2 = TIMER / 10;
      TIMER = 0;
    }
  }
}

void HariChord(int frame)
{
  tft.fillScreen(ILI9341_BLACK);
  int n = 9;
  int r = frame * 125 / nFrames;
  float rot = frame * 2 * PI / nFrames;
  for (int i = 0; i < (n ); i++)
  {
    float a = rot + i * 2 * PI / n;
    int x1 = 160 + cosf(a) * r; // tft x 320 / 2
    int y1 = 120 + sinf(a) * r; // tft y 240 / 2
    for (int j = i + 1; j < n; j++)
    {
      a = rot + j * 2 * PI / n;
      int x2 = 160 + cosf(a) * r;
      int y2 = 120 + sinf(a) * r;
      tft.drawLine(x1, y1, x2, y2, ILI9341_GREEN);
    }
  }

  tft.setTextColor(ILI9341_WHITE);
  tft.setCursor(2, 2);
  tft.print("F_CPU ");
  tft.setTextColor(ILI9341_CYAN);
  tft.print(F_CPU);
  tft.setTextColor(ILI9341_WHITE);
  tft.print(" MHZ");
  tft.setTextColor(ILI9341_WHITE);
  tft.setCursor(2, 17);
  tft.print("F_BUS ");
  tft.setTextColor(ILI9341_CYAN);
  tft.print(F_BUS); // F_BUS
  tft.setTextColor(ILI9341_WHITE);
  tft.print(" MHZ");
  tft.setCursor(2, 32);
  tft.setTextColor(ILI9341_WHITE);
  tft.print("SPI ");
  tft.setTextColor(ILI9341_CYAN);
  tft.print(F_BUS / 2); // F_BUS
  tft.setTextColor(ILI9341_WHITE);
  tft.print(" MHZ");
  tft.setCursor(2, 228);
  tft.setTextColor(ILI9341_WHITE);
  tft.print("ILI9341_SPICLOCK SPI set to MAX");
  tft.setCursor(230, 228);
  tft.setTextColor(ILI9341_CYAN);
  tft.print(ILI9341_SPICLOCK); // F_BUS
  tft.setTextColor(ILI9341_WHITE);
  tft.print(" MHZ");
  tft.setCursor(2, 46);
  tft.print("frame ");
  tft.setTextColor(ILI9341_CYAN);
  tft.print(frame); //

  if (since_Update > 100) {
    since_Update = 0;
    TIMER = TIMER + 1;
  }

  tft.setCursor(2, 61);
  tft.print("TIMER ");
  tft.print(TIMER);

  tft.setCursor(2, 77);
  tft.print(nFrames);
  tft.setTextColor(ILI9341_RED);
  tft.print(" FP ");
  tft.print(TIMER2);
  tft.print(" Sec");

  if ((use_fb == 1) && (use_dma == 1)) {
    tft.updateScreenDMA(); // Update Once DMA, Buffer ON
    delay(45); // (23) F_BUS @ 120000000MHZ 60MHZ SPI
               // (30) F_BUS @ 90000000MHZ  45MHZ SPI  
               // (45) F_BUS @ 60000000MHZ  30MHZ SPI
  }
  if ((use_fb == 1) && (use_dma == 0)) {
    tft.updateScreen(); // Update Once No DMA, Buffer ON
  }
}
Thanks
 
Last edited:
And now for something again maybe completely random ;)

Since I had a Teensy-lC out to play the Teensyview, thought I might see if makes sense to try building a faster version of the ili9341_t3? library to run on the TLC.

Yes - the majority of the main speedup for the T3.x is being able to encode the DC signal on the FIFO queue, which the TLC does not have. But the Adafruit library does eat up a lot of time using SPI.transfer which makes 0 use of the possible double buffering of SPI...

So I am building a version of my ili9341_t3n library to be able to run on TLC. So far I have only worried about output, that is I have not done the work for readRegister, readPixel...

But it looks a little promising:
Adafruit graphic test times:

Code:
Pixel Format: 0x5
Image Format: 0x9C
Self Diagnostic: 0xC0
Benchmark                Time (microseconds)
Screen fill              2072355
Text                     198012
Lines                    1973266
Horiz/Vert Lines         172931
Rectangles (outline)     113501
Rectangles (filled)      4304145
Circles (filled)         809886
Circles (outline)        859115
Triangles (outline)      625707
Triangles (filled)       1520295
Rounded rects (outline)  334939
Rounded rects (filled)   4739699
Done!

With my hacked up version:
Code:
screen fill              1170400
Text                     94588
Lines                    473102
Horiz/Vert Lines         97449
Rectangles (outline)     63078
Rectangles (filled)      2413485
Circles (filled)         480895
Circles (outline)        535116
Triangles (outline)      111196
Triangles (filled)       868565
Rounded rects (outline)  203795
Rounded rects (filled)   2670383
Obviously not near as fast of T3.x but it is a gain. Also running with smallest code option. Currently I have not put this into my main branch,
but probably will soon as it should all compile out if not on TLC... But currently up at: https://github.com/KurtE/ILI9341_t3n/tree/Teensy-LC-support

Again I know completely random.
 
Display Recommendation

Update: Teensy-LC read functions appear to work now... So merged in to master branch.
I have a teensy 3.6 do you recommend a TFT display that I might use with your library. I'm new at the teensy and would like something I'm likely to not get frustrated with. I was able to get a 1.8" 128x160 RGB TFT_LCD ST7735 to function OK but it is too small and not enough resolution for me. Thank you!
 
Back
Top