Highly optimized ILI9341 (320x240 TFT color display) library

@Paul: By the way - the SD Card of the Audio Board uses Pin7 (MOSI), Pin10 (CS), Pin12(MISO), Pin14(Clk).
Whats the best way of making the SD-Card work together with the ILI9341? As the ILI9341 uses pins 9, 10, 11, 12, 13.
Is it one physical SPI on the teensy which is shared (because the pinout shows 2 x cs, 2x din, 2x dout, 2 x sck)?

So eventually the easiest way was beginTransaction() and endTransaction() for both the TFT driver
and SD-Card and share one SPI port, while using different Chip Select Pins?
 
Last edited:
@Paul: I just tried out the latest ILI9341_t3 from the github repo and it seems the current drawChar optimizations leave out
the lowest line of a character. Just run the graphicstest example and lookout for the clipped p y g characters in the testText() test.

Aside from that, the ILI9341_t3 lib rocks! Thanks for the hard work...
 
Last edited:
Fix for the drawChar problem:
Look for this in the drawChar function and replace:
for (yoff=0; yoff < 7; yoff++) {
needs to be:
for (yoff=0; yoff < 8; yoff++) {

and
for (y=0; y < 7; y++) {
needs to be:
for (y=0; y < 8; y++) {

Works so far here...

Anyway, different question: Does readPixel() work for anybody? Running a simple test only yields garbage for me. (Teensy 3.0, 48MHz, ILI9341 SPI Display)
Code:
#include <SPI.h>
#include <ILI9341_t3.h>
#define TFT_RS  8
#define TFT_DC  9
#define TFT_CS 10
ILI9341_t3 ili9341 = ILI9341_t3(TFT_CS, TFT_DC, TFT_RS);

void setup() {
 // Initialize ILI9341 Display
 ili9341.begin();
 ili9341.fillScreen(ILI9341_BLACK);  
 uint8_t x;
 uint8_t y;
 uint16_t col;
 for(y=0; y<200; y++) {
  for(x=0; x<150; x++) { 
	col = ili9341.readPixel(x,y);     // should be black, but isn't
	ili9341.drawPixel(x+150,y,col);
  }
 }
}

void loop() {
}
 
Last edited:
I tried out your fix for DrawChar and it looks good to me. I have tried it on one of my test apps.

As for Read Pixel, Nope, that was a WIP that Paul pulled in when he grabbed my version earlier.

I tried a few different things to get it to work, but then reading through the ILI9341 documents, I began to think that it may only work when you are using the chip in 8 bit parallel input/output mode. I could be wrong, but again I have not had any luck with it.

Kurt
 
I haven't touched readPixel, and I don't intend to even look at it until I have a lot more time to put into this library. Being able to read the display buffer is only going to make me want to implement alpha blending.....
 
I managed to read at least the Red or Green channel back from the ILI9341 Display:

Code:
uint16_t ILI9341_t3::readPixel(int16_t x, int16_t y)
{
	uint16_t r;

	SPI.beginTransaction(SPISettings(SPICLOCK, MSBFIRST, SPI_MODE0));

	setAddr(x, y, x, y);
	writecommand_cont(ILI9341_RAMRD); // read from RAM
	waitTransmitComplete();

	// Read two bytes of GRAM
	SPI0.PUSHR = 0 | (pcs_data << 16) | SPI_PUSHR_CTAS(0);
	r = SPI0.POPR;
	r <<= 8;
	SPI0.PUSHR = 0 | (pcs_data << 16) | SPI_PUSHR_CTAS(0);
	r |= SPI0.POPR;

	SPI.endTransaction();
	return r;
}

Unfortunatly, this will only read back the red pixel values. If you change SPI_PUSHR_CTAS(0) to SPI_PUSHR_CTAS(1), you'll get the green channel values. So for some reason, I am only reading the same byte even though I do two transfers. I don't know the intricate details of the SPI implementation, but maybe you guys have an idea for the correct setup.
 
BDF could represent larger bitmaps, but I couldn't find any BDF files online for larger fonts, nor any simple tools for converting/rendering from Truetype to BDF.

I'll look at the notes I took for my explorad project because I had to do just that - convert ttf to bdf and then to my own binary format. Maybe I can find that...

There: http://superuser.com/questions/336544/convert-ttf-to-bdf
I used ttf2bdf, but I can't exactly remember on which system. But it worked well even for a 128x128 single-glyph logo font!
 
Last edited:
I had a look at the datasheet and the ILI9341 library and it seems you're using the 4-wire interface with a separate pin to separate data from commands. I think it would be fairly easy to modifiy the DmaSpi library to allow for 9-bit transfers, so that the 3-wire interface could be used. All the data/commands could then be buffered in RAM (say 1 Kb) and sent using DMA - no need to wait for the SPI any more. Sounds good?
 
I managed to read at least the Red or Green channel back from the ILI9341 Display:
...
Unfortunatly, this will only read back the red pixel values. If you change SPI_PUSHR_CTAS(0) to SPI_PUSHR_CTAS(1), you'll get the green channel values. So for some reason, I am only reading the same byte even though I do two transfers. I don't know the intricate details of the SPI implementation, but maybe you guys have an idea for the correct setup.

I tried running it and I don't think any of it is actually working. I have been running it and several modifications, with the display and logic Analyzer attached to it. The stuff on the SPI bus is not looking valid. It is very noticeable with the SCLK not generating stuff properly. It has a bunch to do with timings and when things may or may not be valid.

Example: Lines like this:
Code:
SPI0.PUSHR = 0 | (pcs_data << 16) | SPI_PUSHR_CTAS(0);
	r |= SPI0.POPR;
The first line puts the command to output an 8 bit value of 0 out on the SPI bus. It puts the command into the FIFO queue and returns. So it never has a chance to complete before you try to pop the result off the Spi receive queue.

Again I have tried several variations which are still not working, example:
Code:
uint16_t ILI9341_t3::readPixel(int16_t x, int16_t y)
{
	uint16_t r;

    digitalWrite(2, HIGH);
	SPI.beginTransaction(SPISettings(SPICLOCK, MSBFIRST, SPI_MODE0));
    digitalWrite(3, HIGH);
	setAddr(x, y, x, y);

	writecommand_cont(ILI9341_RAMRD); // read from RAM
	while ((SPI0.SR & (15 << 12)) > (0 << 12));     // wait till queue is empty
	SPI0.SR = SPI_SR_TCF;
    while (!(SPI0.SR & SPI_SR_TCF)) ; // wait until final output done

	uint32_t tmp __attribute__((unused));
	while (SPI0.SR & 0xF0) tmp = SPI0_POPR;  // drain RX FIFO

    digitalWrite(3, LOW);

	// Read two bytes 
	SPI0.PUSHR = 0 | (pcs_data << 16) | SPI_PUSHR_CTAS(0) | SPI_PUSHR_CONT;
	SPI0.SR = SPI_SR_TCF;

    // Wait until we have input...
	while ((SPI0.SR & 0xF0) == 0) ;  // Wait for something on the RX queue
	r = SPI0.POPR;
	r <<= 8;
    digitalWrite(3, HIGH);

	SPI0.PUSHR = 0 | (pcs_data << 16) | SPI_PUSHR_CTAS(0);
    // Wait until we have input...
	while ((SPI0.SR & 0xF0) == 0) ;  // Wait for something on the RX queue
    r |= SPI0.POPR;

    digitalWrite(3, LOW);

	SPI.endTransaction();
    digitalWrite(2, LOW);
	return r;
}
Will try again later
 
I have now had some success reading a pixel :)

Not sure yet if 100% correct and also if it can be optimized some. Still debugging some as I do get some random hangs. Probably my waiting on things to complete that already did. Will probably build in simple timeouts to keep that from happening.

One of the main issues to reading the pixels is, that the data is not returned in the same format as when we wrote it out. Also there also needs to be a dummy read. The data is returned in 3 bytes, in 18 bit color mode with the RGB values in the upper 6 bits of each byte.

This information is found in the ILI9341 document starting at about page 64. I think I downloaded the datasheet from the web page on Adafruit (https://www.adafruit.com/product/1651)

Again this hangs from time to time, but in case anyone wants a current look:
Code:
uint16_t ILI9341_t3::readPixel(int16_t x, int16_t y)
{
	uint16_t rVal;

    digitalWrite(2, HIGH);
	SPI.beginTransaction(SPISettings(SPICLOCK, MSBFIRST, SPI_MODE0));
    digitalWrite(3, HIGH);

    setAddr(x, y, x, y);    writecommand_cont(ILI9341_CASET); // Column addr set
    digitalWrite(3, LOW);
    writecommand_cont(ILI9341_RAMRD); // read from RAM
    // Dummy read.
    digitalWrite(3, HIGH);
	SPI0.PUSHR = 0 | (pcs_data << 16) | SPI_PUSHR_CTAS(0) | SPI_PUSHR_CONT;
    waitFifoEmpty();    // wait for both queues to be empty.

    digitalWrite(3, LOW);

    // Now lets read in 3 bytes
    SPI0.PUSHR = 0 | (pcs_data << 16) | SPI_PUSHR_CTAS(0) | SPI_PUSHR_CONT;
	SPI0.PUSHR = 0 | (pcs_data << 16) | SPI_PUSHR_CTAS(0) | SPI_PUSHR_CONT;
	SPI0.PUSHR = 0 | (pcs_data << 16) | SPI_PUSHR_CTAS(0);
    
    // Wait until the last stuff is out
    digitalWrite(3, HIGH);
	while ((SPI0.SR & (15 << 12)) > (0 << 12));     // wait till queue is empty
	SPI0.SR = SPI_SR_TCF;
    digitalWrite(3, HIGH);
    while (!(SPI0.SR & SPI_SR_TCF)) ; // wait until final output done

    // See if we have 3 or 4 bytes in popr queue.
    
    // now lets get the value back...
	uint16_t tmp = SPI0.POPR;
    uint16_t r = SPI0.POPR;
    uint16_t g = SPI0.POPR;
    uint16_t b = SPI0.POPR;
    digitalWrite(3, LOW);

//    Serial.printf(" <%x %x %x %x>", tmp, r, g, b);
    rVal = ((r&0xf8)<<8) | ((g&0xfc)<<3) | (b >> 3);
	SPI.endTransaction();
    digitalWrite(2, LOW);
	return rVal;
}

I have also included a simple test app, that draws some rounded rectangles in different colors and then in loop tries to sample a color out of each one...
 

Attachments

  • ILI9341_Test_ReadPixel-140825a.zip
    746 bytes · Views: 604
Another quick update. I have been wondering about some other of the SR/PUSHR/POPR flags, so I tried changing the above code to use the EOQ and EOQF bits and so far I think it works.

Code:
uint16_t ILI9341_t3::readPixel(int16_t x, int16_t y)
{
	uint16_t rVal;
    SPI0.SR = SPI_SR_EOQF;  // make sure it is clear
    digitalWrite(2, HIGH);
	SPI.beginTransaction(SPISettings(SPICLOCK, MSBFIRST, SPI_MODE0));
    digitalWrite(3, HIGH);

    setAddr(x, y, x, y);    writecommand_cont(ILI9341_CASET); // Column addr set
    digitalWrite(3, LOW);
    writecommand_cont(ILI9341_RAMRD); // read from RAM
    // Dummy read.
    digitalWrite(3, HIGH);
	SPI0.PUSHR = 0 | (pcs_data << 16) | SPI_PUSHR_CTAS(0) | SPI_PUSHR_CONT;
    waitFifoEmpty();    // wait for both queues to be empty.

    digitalWrite(3, LOW);

    // Now lets read in 3 bytes
    SPI0.PUSHR = 0 | (pcs_data << 16) | SPI_PUSHR_CTAS(0) | SPI_PUSHR_CONT;
	SPI0.PUSHR = 0 | (pcs_data << 16) | SPI_PUSHR_CTAS(0) | SPI_PUSHR_CONT;
	SPI0.PUSHR = 0 | (pcs_data << 16) | SPI_PUSHR_CTAS(0) | SPI_PUSHR_EOQ;
    
    // Wait until the last stuff is out
    digitalWrite(3, HIGH);
    // Wait for End of queue flag.
    while ((SPI0.SR & SPI_SR_EOQF) == 0) ;
    SPI0.SR = SPI_SR_EOQF;  // make sure it is clear
    digitalWrite(3, HIGH);

    // See if we have 3 or 4 bytes in popr queue.
    
    // now lets get the value back...
	uint16_t tmp = SPI0.POPR;
    uint16_t r = SPI0.POPR;
    uint16_t g = SPI0.POPR;
    uint16_t b = SPI0.POPR;
    digitalWrite(3, LOW);

//    Serial.printf(" <%x %x %x %x>", tmp, r, g, b);
    rVal = ((r&0xf8)<<8) | ((g&0xfc)<<3) | (b >> 3);
	SPI.endTransaction();
    digitalWrite(2, LOW);
	return rVal;
}
So far I think it works. Another thing I have been wondering about, is how to deal with the POPR queue. What i am wondering about is potentially ROOE (SPI_MCR_ROOE). If I understand this correctly, with this bit set, the POPR FIFO will continue to overwrite the queue and only keep the last 4 entries. With this, could probably remove the use of all of the POPRs in the code and in this case simply POPR the 4 items, with the last 3 being the 3 bytes of the RGB...

Kurt
 
OK, I was missing the SPI_PUSHR_CONT,SPI_PUSHR_EOQ flags for reading more than 1 byte.

So far, this code here works fine for me: :D

Code:
// Pass 8-bit (each) R,G,B, get back 16-bit packed color
uint16_t ILI9341_t3::Color565(uint8_t r, uint8_t g, uint8_t b) {
  return ((r & 0xF8) << 8) | ((g & 0xFC) << 3) | (b >> 3);
}

// Read Pixel at x,y and get back 16-bit packed color
uint16_t ILI9341_t3::readPixel(int16_t x, int16_t y)
{
	uint8_t dummy,r,g,b;

	SPI.beginTransaction(SPISettings(SPICLOCK, MSBFIRST, SPI_MODE0));

	setAddr(x, y, x, y);
	writecommand_cont(ILI9341_RAMRD); // read from RAM
	waitTransmitComplete();

	// Push 4 bytes over SPI
	SPI0.PUSHR = 0 | (pcs_data << 16) | SPI_PUSHR_CTAS(0)| SPI_PUSHR_CONT;
	SPI0.PUSHR = 0 | (pcs_data << 16) | SPI_PUSHR_CTAS(0)| SPI_PUSHR_CONT;
	SPI0.PUSHR = 0 | (pcs_data << 16) | SPI_PUSHR_CTAS(0)| SPI_PUSHR_CONT;
	SPI0.PUSHR = 0 | (pcs_data << 16) | SPI_PUSHR_CTAS(0)| SPI_PUSHR_EOQ;

	// Wait for End of Queue
	while ((SPI0.SR & SPI_SR_EOQF) == 0) ;
	SPI0.SR = SPI_SR_EOQF;  // make sure it is clear

	// Read Pixel Data
	dummy = SPI0.POPR;	// Read a DUMMY byte of GRAM
	r = SPI0.POPR;		// Read a RED byte of GRAM
	g = SPI0.POPR;		// Read a GREEN byte of GRAM
	b = SPI0.POPR;		// Read a BLUE byte of GRAM

	SPI.endTransaction();
	return Color565(r,g,b);
}

The Color565 function is quite useful in other sketches as well. Here a little sample sketch for testing:


Code:
#include <SPI.h>
#include <ILI9341_t3.h>
#define TFT_RS  8
#define TFT_DC  9
#define TFT_CS 10
ILI9341_t3 ili9341 = ILI9341_t3(TFT_CS, TFT_DC, TFT_RS);

void setup() {
 // Initialize ILI9341 Display
 ili9341.begin();
 ili9341.fillScreen(ILI9341_GREEN);
 ili9341.fillCircle(50,50,50,ILI9341_RED);
 ili9341.fillCircle(50,50,40,ILI9341_BLUE);
 ili9341.setCursor(20,20);
 ili9341.println("Test Test Test"); 
}

void loop() {
 uint8_t x;
 uint8_t y;
 uint8_t z;
 uint16_t col;

 for(z=0; z<200; z++){
  for(y=0; y<200; y++) {
   for(x=0; x<120; x++) { 
	col = ili9341.readPixel(x,y);
	ili9341.drawPixel(x+120,y+z,col);
   }
  }
 }
}
 
Last edited:
I used ttf2bdf, but I can't exactly remember on which system.

I had no idea ttf2bdf existed. On Ubuntu 12.04, there's otf2bdf, which looks like it does the same thing. I'll give it a try later when I have more time....

I think it would be fairly easy to modifiy the DmaSpi library to allow for 9-bit transfers, so that the 3-wire interface could be used.

The hardware side might be problem, since the mode is configured by wiring inside the display which we can't easily access.
 
hellmann32, I tried out your version and it hung on me, so I slightly modified it, which appears to be working for me now.

Note: there is already a method color565 on the class (first character lowercase), which I used.

Paul, I forked your project (which was earlier your fork of my fork of Adafruit fork...) and I uploaded this change. Might want to pull it over when you add it to the next beta...

Kurt
 
This morning I have been playing around more with this. In particular I have added functions to read in a rectangle of pixels at a time, to see how well that works.
So far I have found that it speeds things up quite a bit. Currently experimenting with a write rectangle as well, which also is speeding things up a lot.

I have updated my fork on github with the code. Hellmann42, I updated your example code to try things out multiple ways, with timings to see the differences. I start off with the read and write one pixel at a time. I then go to read in one or more complete rows at a time. Code only tries those that divide evenly into 200... I try this two different ways. The first way uses current functions like the spitftbitmap function, which sets up the address window and the program then loops calling to output one value at a time. I then make a pass with a new function writeRect, which works like fillRect except it uses the passed in array of values...

Code:
#include <SPI.h>
#include <ILI9341_t3.h>
#define TFT_RS  8
#define TFT_DC  9
#define TFT_CS 10
ILI9341_t3 ili9341 = ILI9341_t3(TFT_CS, TFT_DC, TFT_RS);

void setup() {
  delay(2000);
  Serial.begin(38400);
  // Initialize ILI9341 Display
  ili9341.begin();
  ili9341.fillScreen(ILI9341_GREEN);
  ili9341.fillCircle(50,50,50,ILI9341_RED);
  ili9341.fillCircle(50,50,40,ILI9341_BLUE);
  ili9341.setCursor(20,20);
  ili9341.println("Test Test Test"); 
}

void loop() {
  uint16_t x;
  uint16_t y;
  uint16_t z;
  uint16_t col;
#define MAX_ROWS_PER_PASS 8
  uint16_t awColors[200*MAX_ROWS_PER_PASS];
  uint32_t ulStart;
  
  // First lets try speed of read/write one pixel at a time
  Serial.print("Time for one pixel at a time: ");
  ulStart = millis();
  for(z=0; z<200; z++){
    for(y=0; y<200; y++) {
      for(x=0; x<120; x++) { 
        col = ili9341.readPixel(x,y);
        ili9341.drawPixel(x+120,y+z,col);
      }
    }
  }
  Serial.println(millis()-ulStart, DEC);

  // Now lets try reading and writing n whole rows at a time;
  for (int iRowsPerPass = 1; iRowsPerPass <= MAX_ROWS_PER_PASS; iRowsPerPass++) {
    // only for those who divide into 200
    int cLoops = 200 / iRowsPerPass;
    if ((cLoops * iRowsPerPass) != 200) continue;

    Serial.print("Read ");
    Serial.print(iRowsPerPass, DEC);
    Serial.print(" Rows per Pass push: ");
    ulStart = millis();

    for(z=0; z<200; z++){
      for(y=0; y<200; y+=iRowsPerPass) {
        ili9341.readRect(0, y, 120, iRowsPerPass, awColors);  // try to read one row at a time...
        ili9341.setAddrWindow(120, y+z, 239, y+z+iRowsPerPass-1);
        for(x=0; x<120*iRowsPerPass; x++) { 
          ili9341.pushColor(awColors[x]);
        }
      }
    }
    Serial.println(millis()-ulStart, DEC);

  
    Serial.print("Read ");
    Serial.print(iRowsPerPass, DEC);
    Serial.print(" Rows per Pass writerect: ");
    ulStart = millis();

    for(z=0; z<200; z++){
      for(y=0; y<200; y+=iRowsPerPass) {
        ili9341.readRect(0, y, 120, iRowsPerPass, awColors);  // try to read one row at a time...
        ili9341.writeRect(120, y+z, 120, iRowsPerPass, awColors);  // try to read one row at a time...
      }
    }
    Serial.println(millis()-ulStart, DEC);
  }
}

Here is an output of the timings:
Code:
Time for one pixel at a time: 81819
Read 1 Rows per Pass push: 28042
Read 1 Rows per Pass writerect: 12891
Read 2 Rows per Pass push: 27745
Read 2 Rows per Pass writerect: 12604
Read 4 Rows per Pass push: 27595
Read 4 Rows per Pass writerect: 12457
Read 5 Rows per Pass push: 27566
Read 5 Rows per Pass writerect: 12429
Read 8 Rows per Pass push: 27521
Read 8 Rows per Pass writerect: 12385
Time for one pixel at a time: 81819
...
There are probably still a few bugs to work out, but thought you might have some fun playing with it.

Also some perf stuff to look at. for example I wait until each 3 byte color is returned before I start the input/output for the next pixel so there may be times the SPI buss is not fully utilized.

Edit: Also the readPixel could/should be a simple wrapper of the readRect function.

Kurt
 
Last edited:
I sped up the readRect and getting some better times, but there appears to be an issue with the writeRect as the data gets slightly screwed up in the cases where I am reading more than one row at a time.

But again the timings are better:
Code:
Time for one pixel at a time: 81816
Read 1 Rows per Pass push: 25714
Read 1 Rows per Pass writerect: 10567
Read 2 Rows per Pass push: 25381
Read 2 Rows per Pass writerect: 10242
Read 4 Rows per Pass push: 25214
Read 4 Rows per Pass writerect: 10078
Read 5 Rows per Pass push: 25180
Read 5 Rows per Pass writerect: 10047
Read 8 Rows per Pass push: 25133
Read 8 Rows per Pass writerect: 10000
Time for one pixel at a time: 81815
Read 1 Rows per Pass push: 25714
Read 1 Rows per Pass writerect: 10567

Update
As I mentioned when I did a writeRect of multiple rows of data, the data appears to get misaligned, to verify that it is the write and not the read, I put in another case in the code where it reads in multiple rows of data but only outputs one row at a time. This turns out to be slightly slower than the write all in one bunch, but the data shows up properly...

Also I updated the test program to add some text scroll area that shows the timings and also tries out to see if it would work out OK for a text scroll area as that was why I started to try to do the reads in the first place. The small area appears to work fine for me...

Here is the updated test case. Note: I have the slow version (read/write one pixel at a time) #if out as to make testing the other stuff quicker:
Code:
#include <SPI.h>
#include <ILI9341_t3.h>
#define TFT_RS  8
#define TFT_DC  9
#define TFT_CS 10
ILI9341_t3 ili9341 = ILI9341_t3(TFT_CS, TFT_DC, TFT_RS);

void setup() {
  delay(2000);
  Serial.begin(38400);
  // Initialize ILI9341 Display
  ili9341.begin();
  ili9341.fillScreen(ILI9341_GREEN);
  ili9341.fillCircle(50,50,50,ILI9341_RED);
  ili9341.fillCircle(50,50,40,ILI9341_BLUE);
  ili9341.setCursor(20,20);
  ili9341.println("Test Test Test"); 

  // How about some text scrolling.
  ili9341.fillRect(0, 205, 110, 110, ILI9341_BLACK);

}

void loop() {
  uint16_t x;
  uint16_t y;
  uint16_t z;
  uint16_t col;
#define MAX_ROWS_PER_PASS 8
  uint16_t awColors[200*MAX_ROWS_PER_PASS];
  uint32_t ulStart, ulDelta;

#if 0
  // First lets try speed of read/write one pixel at a time
  Serial.print("Time for one pixel at a time: ");
  ulStart = millis();
  for(z=0; z<200; z++){
    for(y=0; y<200; y++) {
      for(x=0; x<120; x++) { 
        col = ili9341.readPixel(x,y);
        ili9341.drawPixel(x+120,y+z,col);
      }
    }
  }
  Serial.println(ulDelta = millis()-ulStart, DEC);
  ScrollTextArea();
  ili9341.printf("S %d", ulDelta);
#endif
  // Now lets try reading and writing n whole rows at a time;
  for (int iRowsPerPass = 1; iRowsPerPass <= MAX_ROWS_PER_PASS; iRowsPerPass++) {
    // only for those who divide into 200
    int cLoops = 200 / iRowsPerPass;
    if ((cLoops * iRowsPerPass) != 200) continue;

    Serial.print("Read ");
    Serial.print(iRowsPerPass, DEC);
    Serial.print(" Rows per Pass push: ");
    ulStart = millis();

    for(z=0; z<200; z++){
      for(y=0; y<200; y+=iRowsPerPass) {
        ili9341.readRect(0, y, 120, iRowsPerPass, awColors);  // try to read one row at a time...
        ili9341.setAddrWindow(120, y+z, 239, y+z+iRowsPerPass-1);
        for(x=0; x<120*iRowsPerPass; x++) { 
          ili9341.pushColor(awColors[x]);
        }
      }
    }
    Serial.println(ulDelta = millis()-ulStart, DEC);
    ScrollTextArea();
    ili9341.printf("%dP %d", iRowsPerPass, ulDelta);

    Serial.print("Read ");
    Serial.print(iRowsPerPass, DEC);
    Serial.print(" Rows per Pass writerect: ");
    ulStart = millis();

    for(z=0; z<200; z++){
      for(y=0; y<200; y+=iRowsPerPass) {
        ili9341.readRect(0, y, 120, iRowsPerPass, awColors);  // try to read one row at a time...
        ili9341.writeRect(120, y+z, 120, iRowsPerPass, awColors);  // try to read one row at a time...
      }
    }
    Serial.println(ulDelta = millis()-ulStart, DEC);
    ScrollTextArea();
    ili9341.printf("%dW %d", iRowsPerPass, ulDelta);

    if (iRowsPerPass > 1) {
      Serial.print("Read ");
      Serial.print(iRowsPerPass, DEC);
      Serial.print(" Rows per Pass writerect: ");
      ulStart = millis();

      for(z=0; z<200; z++){
        for(y=0; y<200; y+=iRowsPerPass) {
          ili9341.readRect(0, y, 120, iRowsPerPass, awColors);  // try to read one row at a time...
          for (uint16_t iRow=0; iRow  < iRowsPerPass; iRow++) {
            ili9341.writeRect(120, y+iRow+z, 120, 1, &awColors[iRow*120]);  // try to read one row at a time...
          }
        }
      }
      Serial.println(ulDelta = millis()-ulStart, DEC);
      ScrollTextArea();
      ili9341.printf("%dC %d", iRowsPerPass, ulDelta);
    }
  }

}

void ScrollTextArea() {
  // Assume we are in the area (0, 205) - (110-314)
  uint16_t awColors[110];

  for (int y=205+16; y < 315; y++) { 
    ili9341.readRect(0, y, 110, 1, awColors);  // try to read one row at a time...
    ili9341.writeRect(0, y-16, 110, 1, awColors);  // try to read one row at a time...
  }
  ili9341.setTextSize(2);
  ili9341.fillRect(0, 316-16, 110, 16, ILI9341_BLACK);
  ili9341.setCursor(0,316-16);

}

FYI - I also tested this on the Adafruit 2.2" TFT display (https://www.adafruit.com/product/1480) that uses the ILI9340 and again as far as I can tell, it works fine on this as well
 
Last edited:
Not sure if anyone has tried the scrolling stuff out, but found the bug with the writeRect, which I uploaded the fix to my fork on github.

I am probably done with this for now. I am pretty happy with the scroll working and speed ups.
If I use the first pass, of reading one pixel in and drawing it back out, with the test to scroll the part of the screen, takes almost 82 seconds (81814ms),
When I use the read and write one row at a time this time is cut down to about 10.5 seconds, reading and writing 8 rows at a time cuts it down to 10 seconds.

Note without using the writeRect function I added but instead using the older way to write out multiple pixels that the example spitftbitmap uses (setAddrWindow and repeated calls to pushCOlor), the 10 seconds for doing 8 rows at a time goes from the 10 seconds back up to about 25 seconds.

Note: I thought about gutting the function that reads one pixel at a time and have it simply call readRect for one pixel. But doing so increased the test time from about 82 to 88.5 seconds. If I did it and inlined the function it took over 85 seconds, so I left the function intact.

Kurt
 
@Paul, I just downloaded the optimized-IL9341 library and it doesn't want to compile, I am compiling on a PC-Win 7. I tried the original by Adafruit and compiles, and runs on the Teensy 3.1. I haven't tried to debug the library beyond what it's obvious on my setup and I wanted to check if you have seen this compile error:


Arduino: 1.0.5-r2 (Windows 7), Board: "Teensy 3.1"
C:\Users\coder\Documents\Arduino\libraries\ILI9341_t3\ILI9341_t3.cpp: In member function 'void ILI9341_t3::setAddrWindow(uint16_t, uint16_t, uint16_t, uint16_t)':
C:\Users\coder\Documents\Arduino\libraries\ILI9341_t3\ILI9341_t3.cpp:47:6: error: 'class SPIClass' has no member named 'beginTransaction'
C:\Users\coder\Documents\Arduino\libraries\ILI9341_t3\ILI9341_t3.cpp:47:64: error: 'SPISettings' was not declared in this scope
C:\Users\coder\Documents\Arduino\libraries\ILI9341_t3\ILI9341_t3.cpp:50:6: error: 'class SPIClass' has no member named 'endTransaction'
C:\Users\coder\Documents\Arduino\libraries\ILI9341_t3\ILI9341_t3.cpp: In member function 'void ILI9341_t3::pushColor(uint16_t)':
C:\Users\coder\Documents\Arduino\libraries\ILI9341_t3\ILI9341_t3.cpp:55:6: error: 'class SPIClass' has no member named 'beginTransaction'
C:\Users\coder\Documents\Arduino\libraries\ILI9341_t3\ILI9341_t3.cpp:55:64: error: 'SPISettings' was not declared in this scope
C:\Users\coder\Documents\Arduino\libraries\ILI9341_t3\ILI9341_t3.cpp:57:6: error: 'class SPIClass' has no member named 'endTransaction'
C:\Users\coder\Documents\Arduino\libraries\ILI9341_t3\ILI9341_t3.cpp: In member function 'void ILI9341_t3::drawPixel(int16_t, int16_t, uint16_t)':
C:\Users\coder\Documents\Arduino\libraries\ILI9341_t3\ILI9341_t3.cpp:64:6: error: 'class SPIClass' has no member named 'beginTransaction'
C:\Users\coder\Documents\Arduino\libraries\ILI9341_t3\ILI9341_t3.cpp:64:64: error: 'SPISettings' was not declared in this scope
C:\Users\coder\Documents\Arduino\libraries\ILI9341_t3\ILI9341_t3.cpp:68:6: error: 'class SPIClass' has no member named 'endTransaction'
C:\Users\coder\Documents\Arduino\libraries\ILI9341_t3\ILI9341_t3.cpp: In member function 'void ILI9341_t3::drawFastVLine(int16_t, int16_t, int16_t, uint16_t)':
C:\Users\coder\Documents\Arduino\libraries\ILI9341_t3\ILI9341_t3.cpp:76:6: error: 'class SPIClass' has no member named 'beginTransaction'
C:\Users\coder\Documents\Arduino\libraries\ILI9341_t3\ILI9341_t3.cpp:76:64: error: 'SPISettings' was not declared in this scope
C:\Users\coder\Documents\Arduino\libraries\ILI9341_t3\ILI9341_t3.cpp:83:6: error: 'class SPIClass' has no member named 'endTransaction'
C:\Users\coder\Documents\Arduino\libraries\ILI9341_t3\ILI9341_t3.cpp: In member function 'void ILI9341_t3::drawFastHLine(int16_t, int16_t, int16_t, uint16_t)':
C:\Users\coder\Documents\Arduino\libraries\ILI9341_t3\ILI9341_t3.cpp:91:6: error: 'class SPIClass' has no member named 'beginTransaction'
C:\Users\coder\Documents\Arduino\libraries\ILI9341_t3\ILI9341_t3.cpp:91:64: error: 'SPISettings' was not declared in this scope
C:\Users\coder\Documents\Arduino\libraries\ILI9341_t3\ILI9341_t3.cpp:98:6: error: 'class SPIClass' has no member named 'endTransaction'
C:\Users\coder\Documents\Arduino\libraries\ILI9341_t3\ILI9341_t3.cpp: In member function 'void ILI9341_t3::fillRect(int16_t, int16_t, int16_t, int16_t, uint16_t)':
C:\Users\coder\Documents\Arduino\libraries\ILI9341_t3\ILI9341_t3.cpp:117:6: error: 'class SPIClass' has no member named 'beginTransaction'
C:\Users\coder\Documents\Arduino\libraries\ILI9341_t3\ILI9341_t3.cpp:117:64: error: 'SPISettings' was not declared in this scope
C:\Users\coder\Documents\Arduino\libraries\ILI9341_t3\ILI9341_t3.cpp:126:6: error: 'class SPIClass' has no member named 'endTransaction'
C:\Users\coder\Documents\Arduino\libraries\ILI9341_t3\ILI9341_t3.cpp: In member function 'uint8_t ILI9341_t3::readcommand8(uint8_t, uint8_t)':
C:\Users\coder\Documents\Arduino\libraries\ILI9341_t3\ILI9341_t3.cpp:211:9: error: 'class SPIClass' has no member named 'beginTransaction'
C:\Users\coder\Documents\Arduino\libraries\ILI9341_t3\ILI9341_t3.cpp:211:67: error: 'SPISettings' was not declared in this scope
C:\Users\coder\Documents\Arduino\libraries\ILI9341_t3\ILI9341_t3.cpp:255:9: error: 'class SPIClass' has no member named 'endTransaction'
C:\Users\coder\Documents\Arduino\libraries\ILI9341_t3\ILI9341_t3.cpp: In member function 'uint16_t ILI9341_t3::readPixel(int16_t, int16_t)':
C:\Users\coder\Documents\Arduino\libraries\ILI9341_t3\ILI9341_t3.cpp:263:9: error: 'class SPIClass' has no member named 'beginTransaction'
C:\Users\coder\Documents\Arduino\libraries\ILI9341_t3\ILI9341_t3.cpp:263:67: error: 'SPISettings' was not declared in this scope
C:\Users\coder\Documents\Arduino\libraries\ILI9341_t3\ILI9341_t3.cpp:282:9: error: 'class SPIClass' has no member named 'endTransaction'
C:\Users\coder\Documents\Arduino\libraries\ILI9341_t3\ILI9341_t3.cpp: In member function 'void ILI9341_t3::begin()':
C:\Users\coder\Documents\Arduino\libraries\ILI9341_t3\ILI9341_t3.cpp:314:10: error: 'class SPIClass' has no member named 'pinIsChipSelect'
C:\Users\coder\Documents\Arduino\libraries\ILI9341_t3\ILI9341_t3.cpp:315:18: error: 'class SPIClass' has no member named 'setCS'
C:\Users\coder\Documents\Arduino\libraries\ILI9341_t3\ILI9341_t3.cpp:316:32: error: 'class SPIClass' has no member named 'setCS'
C:\Users\coder\Documents\Arduino\libraries\ILI9341_t3\ILI9341_t3.cpp:344:6: error: 'class SPIClass' has no member named 'beginTransaction'
C:\Users\coder\Documents\Arduino\libraries\ILI9341_t3\ILI9341_t3.cpp:344:64: error: 'SPISettings' was not declared in this scope
C:\Users\coder\Documents\Arduino\libraries\ILI9341_t3\ILI9341_t3.cpp:355:6: error: 'class SPIClass' has no member named 'endTransaction'
C:\Users\coder\Documents\Arduino\libraries\ILI9341_t3\ILI9341_t3.cpp:358:6: error: 'class SPIClass' has no member named 'beginTransaction'
C:\Users\coder\Documents\Arduino\libraries\ILI9341_t3\ILI9341_t3.cpp:360:6: error: 'class SPIClass' has no member named 'endTransaction'
C:\Users\coder\Documents\Arduino\libraries\ILI9341_t3\ILI9341_t3.cpp: In member function 'void ILI9341_t3::drawLine(int16_t, int16_t, int16_t, int16_t, uint16_t)':
C:\Users\coder\Documents\Arduino\libraries\ILI9341_t3\ILI9341_t3.cpp:555:6: error: 'class SPIClass' has no member named 'beginTransaction'
C:\Users\coder\Documents\Arduino\libraries\ILI9341_t3\ILI9341_t3.cpp:555:64: error: 'SPISettings' was not declared in this scope
C:\Users\coder\Documents\Arduino\libraries\ILI9341_t3\ILI9341_t3.cpp:596:6: error: 'class SPIClass' has no member named 'endTransaction'
C:\Users\coder\Documents\Arduino\libraries\ILI9341_t3\ILI9341_t3.cpp: In member function 'void ILI9341_t3::drawRect(int16_t, int16_t, int16_t, int16_t, uint16_t)':
C:\Users\coder\Documents\Arduino\libraries\ILI9341_t3\ILI9341_t3.cpp:602:6: error: 'class SPIClass' has no member named 'beginTransaction'
C:\Users\coder\Documents\Arduino\libraries\ILI9341_t3\ILI9341_t3.cpp:602:64: error: 'SPISettings' was not declared in this scope
C:\Users\coder\Documents\Arduino\libraries\ILI9341_t3\ILI9341_t3.cpp:608:6: error: 'class SPIClass' has no member named 'endTransaction'
C:\Users\coder\Documents\Arduino\libraries\ILI9341_t3\ILI9341_t3.cpp: In member function 'void ILI9341_t3::drawChar(int16_t, int16_t, unsigned char, uint16_t, uint16_t, uint8_t)':
C:\Users\coder\Documents\Arduino\libraries\ILI9341_t3\ILI9341_t3.cpp:850:7: error: 'class SPIClass' has no member named 'beginTransaction'
C:\Users\coder\Documents\Arduino\libraries\ILI9341_t3\ILI9341_t3.cpp:850:65: error: 'SPISettings' was not declared in this scope
C:\Users\coder\Documents\Arduino\libraries\ILI9341_t3\ILI9341_t3.cpp:880:7: error: 'class SPIClass' has no member named 'endTransaction'



Thanks,
 
Sounds like you do not have Tensyduino 1.20rc2 installed. The SPI transaction code was recently added and used by this library.

Kurt
 
Thanks Kurt, I did install Tensyduino 1.20rc2, the code now compiles but it doesn't run in the board; after compilation and upload into the board the screen remains blank, no graphic or anything happens. I compiled the Adafruit again to make sure and that one still works as usual.


Thanks
 
Still something odd in ILI9341_t3::drawChar?

This is really fantastic! And I love the discussion about fonts.

I just fired this up with a T3.1, 1.20rc2, and pulled the master of ILI9341_t3 from GitHub.

I'm seeing something strange with scaled text drawn with a background.

To distill it down, I chopped up graphicstest.ino so it would do only this:

Code:
  tft.setCursor(0, 0);
  tft.setTextSize(6);
  tft.setTextColor(ILI9341_GREEN);
  tft.println("GROOP");
  tft.setTextColor(ILI9341_BLACK, ILI9341_GREEN);
  tft.println("GROOP");
  tft.setTextColor(ILI9341_GREEN);
  tft.println("GROOP");
  tft.setTextSize(3);
  tft.println();
  tft.setTextSize(6);
  tft.setTextColor(ILI9341_BLACK, ILI9341_WHITE);
  tft.println("GROOP");
  tft.setTextColor(ILI9341_GREEN);
  tft.println("GROOP");

Photo attached with the screen that results from the above code.

The top line of the scaled text is drawn with just one pixel, and the top few scan lines are drawn with background instead of character dots. Also, it looks like the fg/bg text is drawn one pixel too low, as you can barely see that the green text is slightly over the top of the reversed white text in my attachment.

Before:
IMG_1086.jpg

I don't know if this is correct because I've only read drawChar and not the rest of the file, but I saw two things that didn't look right to me. First, I don't know how setAddr works but it didn't look right to subtract one from the width but not the height:

Code:
setAddr(x, y, x + 6 * size - 1, y + 8 * size );

so I changed this to:

Code:
setAddr(x, y, x + 6 * size - 1, y + 8 * size - 1);

That fixed the size of the background but now the whole top pixel of character data is drawn as background, so it looks like it's being overwritten.

There's code at the end of drawChar that wants to write one scaled line of pixels in the background color, the 8th row of pixels that doesn't have data in the 5x7 font:

Code:
uint32_t n = 6 * size * size;
do {
	writedata16_cont(bgcolor);
	n--;
} while (n > 1);
writedata16_last(bgcolor);

But the loop already writes that 8th horizontal line of dots (it's nominally a 5x7 font, but the data is 5x8 and the loop draws all 8 lines).

So I ended up with the last lines of drawChar looking like this:

Code:
 	} else {
		// This solid background approach is about 5 time faster
		SPI.beginTransaction(SPISettings(SPICLOCK, MSBFIRST, SPI_MODE0));
		setAddr(x, y, x + 6 * size - 1, y + 8 * size - 1);
		writecommand_cont(ILI9341_RAMWR);
		uint8_t xr, yr;
		uint8_t mask = 0x01;
		uint16_t color;
		for (y=0; y < 8; y++) {
			for (yr=0; yr < size; yr++) {
				for (x=0; x < 5; x++) {
					if (font[c * 5 + x] & mask) {
						color = fgcolor;
					} else {
						color = bgcolor;
					}
					for (xr=0; xr < size; xr++) {
						writedata16_cont(color);
					}
				}
				for (xr=0; xr < size; xr++) {
					writedata16_cont(bgcolor);
				}
			}
			mask = mask << 1;
		}
// 		uint32_t n = 6 * size * size;
// 		do {
// 			writedata16_cont(bgcolor);
// 			n--;
// 		} while (n > 1);
// 		writedata16_last(bgcolor);
		SPI.endTransaction();
	}
}

After:
IMG_1087.jpg

Now appears to draw correctly, but I then noticed that someone went to pains to make sure the last data is written with writedata16_last() instead of writedata16_cont(), and I didn't do that, so I'm sure that will have to be fixed, but before I go any further, maybe someone can take a look and see if I'm on the right track. Glancing at writedata16_*, I see that there is some SPI handshaking that needs to be done, and my edits break that, which will have to be addressed by someone who knows what they are doing with Teensy's SPI.

Thanks!
-Dave
 
Multi-tech: Not sure, I would check wiring and make sure all of the IO lines are setup correctly for fast SPI... That is this code is setup to run the SPI, with the hardware SPI controlling the chip select plus the other control line, which implies that these IO lines must be one of the CS pins of the T3.1

DRG: There was a thread on the font issue a few days ago: http://forum.pjrc.com/threads/26498...ystems?p=53654&highlight=ili9341_t3#post53654

I fixed it in my fork of the library: https://github.com/KurtE/ILI9341_t3

The issue was, the code is setup to output 9*size pixels vertically, but it sets up (setAddr) for 8*size+1 rows, so when it is outputting the blank lines at the bottom of the characters, it wraps around and overwrites the first pixels...

I should probably try to setup a pull request back to the main fork, just not sure how much to put in the request. This simple fix, or should it include fix to readPixel, and/or the new readRect/writeRect that speeds things up?

Kurt
 
Last edited:
Just did a clean install of Arduino 1.0.6 and Teensy 1.20 RC3. when trying any of the examples in the ILI9341_t3 folder I keep getting a compile error.

Code:
Arduino: 1.0.6 + Td: 1.20-rc3 (Windows 7), Board: "Teensy 3.1"
C:\Program Files (x86)\Arduino\libraries\Wire\utility\twi.c:27:24: fatal error: compat/twi.h: No such file or directory
compilation terminated.
did I delete something I should not have?
 
Back
Top