Optimized ILI9341 TFT Library

Status
Not open for further replies.

luke.saber

Active member
Hi Paul, I wanted to get your input on how I could utilize your Optimized ILI9341 TFT Library with a different display. I had trouble finding what exactly needed to be added to the code or library to use the FIFO with a different display driver. Specifically, I am using the module in the link below. I have also attached the libraries I am currently using, via google docs, which include example sketches. Essentially, I would like to have my display function as fast as you have been able to achieve. Your help would be greatly appreciated, thank you.

https://www.amazon.com/gp/product/B07GSL66HS/ref=oh_aui_search_asin_title?ie=UTF8&psc=1\

https://drive.google.com/open?id=19PHgOLyFpmr4Qcif7A97f0p0Ldp9w_lR


Best wishes,

Lucas Saber
 
No chance. Completely different chip.

But you can try to use the ILI9342_t3 library SPI functions for your display..
 
Last edited:
Is there any way that I could make similar modifications to the current library I am using to utilize the Teensy 3.2 FIFO capability? I essentially want to speed up my display and the FIFO seems to be the right path to take.
 
Is there any way that I could make similar modifications to the current library I am using to utilize the Teensy 3.2 FIFO capability? I essentially want to speed up my display and the FIFO seems to be the right path to take.

Yes, read the code, try to understand how it works, and think about what you can use for the other display.
 
I'll do my best to break apart the code and implement its parts into the current library I have. However, I'm sure I'll be asking many questions about it in the near future.
 
ILI9341_t3.h - below "protected".
But I'd look at the cpp file too, to understand how the code gets called.
 
You might also check to see if any of the drivers that was done by the member sumotoy would work: https://github.com/sumotoy

He also extracted the FIFO code from the ili9341_t3 library and applied it to several other drivers. However I have not seen any sign of him for a long while now (2-3 years) so if you run into issues with his libraries...
 
I have been trying to incorporate the functions into the library that my display uses and have not had much luck. The main issue is that I don't know where I should apply these functions in relation to the functions already used to make the display work. I have a vague idea of where I should start, but I am not getting very far. Any guidance on utilizing the optimized library's functions would be greatly appreciated.
 
What library are you starting from? That is do you have a working library you can point to for your display?
It is easier to give more hints, when we have an idea of your starting point.
 
I am starting with a library called "LCD_WIKI". I have a link for it at the top of the thread. I could not find any other libraries that supported this display, unfortunately. If there is another one available to use I could try that too. However, my main issue lies in making the display run as fast as possible utilizing the FIFO.
 
Upon further research, I think the LCD_WIKI library uses the arduino SPI protocols instead of the ones for the teensy.
 
The URL on that Amazon page doesn't work for me.

I tried a quick search for "LCD_WIKI" and found a bunch of pages about a 3.5 inch display used on Raspberry Pi... but nothing that looked anything like the 1.6 inch SSD1283A display from the Amazon page mentioned in your first message.

To quickly try to answer your question, these SPI displays usually have some bytes which are the command that begins a message, and others which are the data for that command. I'm not familiar with SSD1283A, and I can't find the library... so this advice is pretty generic and not as helpful as it could have been (if you'd provided a direct link to the actual source code).

Inside ILI9341_t3, you'll find 6 functions which do the actual work of communicating with the display

void writecommand_cont(uint8_t c)
void writedata8_cont(uint8_t c)
void writedata16_cont(uint16_t d)
void writecommand_last(uint8_t c)
void writedata8_last(uint8_t c)
void writedata16_last(uint16_t d)

These all send to the display, as you could guess from "write". 2 of them send commands (which are always single bytes), 4 of them send data. Two of the data functions send 16 bits at once, which is slightly faster than sending as 2 separate bytes. All of these come in 2 flavors, ending "_cont" or "_last". The basic idea is "_cont" means continue, that you aren't done writing. The "_last" functions mean this will be the very last function you call for whatever drawing operation you're doing.

For normal Arduino SPI code, you would use digitalWrite() or a port register write to change the D/C pin (apparently called "A0" on this SSD1283A display) to tell the display whether the arriving bytes are command or data, and then use SPI.transfer() to send either command or data bytes. The CS pins is also controlled this way with normal Arduino code.

So to convert code written for the normal SPI library to this 6-function scheme, you would look at every call to SPI.transfer(). For *all* of them, you need to figure out if it's sending a command or data byte. You also need to determine if it is the last one to transfer data, which isn't always as simple is it seems since some drawing might involve calling other functions. In some cases, like where a function is called from another, so its last SPI.transfer() isn't the final transfer, but its last transfer is the final on when called directly or used in other ways, you will need to duplicate that code. Every SPI.transfer() needs to be uniquely mapped to either a "_cont" or a "_last".

If you do this, you'll end up using 4 of the 6 optimized functions. If many places, you'll probably also find pairs SPI.transfer() which can be turned into a single 16 bit transfer, which is slightly faster.

These 6 fast functions automatically control the DC & CS pins, so you'll delete all those digitalWrite or port register writes for those pins.

ILI9341_t3 also contains some impressive algorithm optimization, specially in the drawing of circles and diagonal lines. The simplest but slowest way, which most libraries do, involves treating each pixel separately. ILI9341_t3 finds groups that have the same X or Y coordinate and does them as a line, which avoids several bytes of command overhead for the remaining pixels in the line. This is much harder, since it requires rewriting the code... but then you already have working code in ILI9341_t3 to use.

But the first step is just finding all the SPI.transfer() functions and for every single one, determine from the surrounding code whether it's sending a command byte or a data bytes, and whether or not it's the final transfer which ends the drawing operation.


If things don't work at first, you're almost certainly going to need an oscilloscope (3 channels minimum) or logic analyzer to view the SPI waveforms. Fortunately there are many cheap logic analyzers on the market. Some are limited in sampling frequency, but you can just configure for a slower SPI clock while testing the code, then speed it up after you've made it work.

The process of troubleshooting involves crafting tiny program which just do one operation, like a single pixel or even just a single address range setup (not necessarily even writing any pixels). The smaller the communication, the easier it is to look at it on a scope screen or in logic analyzer software (like Saleae Logic). You'll run that same tiny operation on the original slow library and then on your attempt at porting to the fast FIFO-based code, and compare the results. Little-by-little, you'll get the functions working. It's a process involving a lot of tedious work. Hopefully this lengthy message gives you an idea how to go about it?
 
For the most part SPI should be SPI... That is normally if you try to compile something with SPI library, it will hopefully get the right version...

Again does the version of software up on the google drive work for you, to actually get something to work on that display on a Teensy?

If so you the can start to try to optimize it (or rewrite..).

But again not sure how specific this code is to your display?

But if you look at the code, like how it fills a rectangle you will see code like:
Code:
void LCDWIKI_SPI::Fill_Rect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color)
{
	int16_t end;
	if (w < 0) 
	{
        w = -w;
        x -= w;
    }                           //+ve w
    end = x + w;
    if (x < 0)
    {
        x = 0;
    }
    if (end > Get_Width())
    {
        end = Get_Width();
    }
    w = end - x;
    if (h < 0) 
	{
        h = -h;
        y -= h;
    }                           //+ve h
    end = y + h;
    if (y < 0)
    {
        y = 0;
    }
    if (end > Get_Height())
    {
        end = Get_Height();
    }
    h = end - y;
   [COLOR="#FF0000"] Set_Addr_Window(x, y, x + w - 1, y + h - 1);
	CS_ACTIVE;[/COLOR]
    if(lcd_driver == ID_932X)
	{
		writeCmd8(ILI932X_START_OSC);
			
	} 		
	[COLOR="#FF0000"]writeCmd8(CC);	[/COLOR]	
	if (h > w) 
	{
        end = h;
        h = w;
        w = end;
    }
	while (h-- > 0) 
	{
		end = w;
		do 
		{
   			[COLOR="#FF0000"]writeData16(color);[/COLOR]
        } while (--end != 0);
	}
	if(lcd_driver == ID_932X)
	{
		Set_Addr_Window(0, 0, width - 1, height - 1);
	}
	else if(lcd_driver == ID_7575)
	{
		Set_LR();
	}
[COLOR="#FF0000"]	CS_IDLE;[/COLOR]
}
I set some interesting things in RED in the above listing. You then need to look at each of these functions.... like Set_Addr_Window, which for 1283, does things lik:
Code:
else if(lcd_driver == ID_1283A)
	{
		int16_t t1,t2;
		switch(rotation)
		{
			case 0:
			case 2:
				t1=x1;
				t2=x2;
				x1=y1+2;
				x2=y2+2;
				y1=t1+2;
				y2=t2+2;
				break;				
			case 1:
			case 3:			
				y1=y1+2;
				y2=y2+2;
				break;
		}
		writeCmd8(XC);
		writeData8(x2);
		writeData8(x1);
		writeCmd8(YC);
		writeData8(y2);
		writeData8(y1);
		writeCmd8(0x21);
		writeData8(x1);
		writeData8(y1);
		writeCmd8(CC);
	}
You see that it does a lot of writeCmd8 and writeData8... Which you can start to translate into the functions like in the ili9341_t3 library.
The above might look something like:
Code:
writecommand_cont(XC);
		writedata8_cont(x2);
		writedata8_cont(x1);
		writecommand_cont(YC);
		writedata8_cont(y2);
		writedata8_cont(y1);
		writecommand_cont(0x21);
		writedata8_cont(x1);
		writedata8_cont(y1);
		writecommand_cont(CC);
You would again need to make sure DC and CS pins are on Hardware CS pins... Or you could do like ILI9341_t3n, where I made it that only the DC pin needed to be on hardware CS pin, you got a slight boast in speed from the CS pin, but mainly with DC...
You would need to make similar changes to the fillRect function, where you would change to writecommand_cont and the write of colors, you might use the writedata16_cont for all of the words, or better you figure out how many of these words to write (w*h) and write that count -1 with the writedata16_cont and the last one with writedata16_last.
...

Then depending on how you handle CS pin, you might use or not use functions like cs_active...
 
Thank you Paul, this will definitely help me with my endeavors. I really appreciate your response being so quick. Also, thank you for the detailed explanation Kurt, I appreciate that very much as well. Both of your inputs will be really helpful to me. I'll let you know if I have any additional issues along the way.
 
I believe the best thing you can do is to build command buffer and use DMA to transfer the buffer to the TFT asynchronously. If you keep sending the data to the TFT over SPI synchronously as you draw like in the ILI9341 lib, you'll end up wasting a lot of MCU cycles while waiting for SPI transfers (i.e. in waitFifoNotFull()/waitTransmitCompete() functions) because of tiny SPI FIFO (only 4 bytes AFAIK). IIRC SPICLOCK is only ~30MHz which means only 24fps at 320x240x16bpp and all spare MCU cycles wasted on SPI waits.
 
So long story short I decided to change displays to a better quality one. I did this because I needed a higher definition display for my project. I switched to the display in the following link:
https://www.adafruit.com/product/3787
This utilized adafruit's GFX display library and their ST7789 display library. Where in that library should I look to replace the SPI functions with the ones in Paul's optimized libraries. I know that I need to use those libraries instead of what is being used now, I am just not sure where I should start looking in adafruit's code.
 
Status
Not open for further replies.
Back
Top