Due SPI slow (especially for specific pin (DueExtendedSPI))

Hi,

I was just experimenting with my Adafruit Display which is using an ILI9340 Chip. It was quite slow so I tried to tweak it a little bit. The first thing that came to my mind was to change the clockdiver.

Im using this libary: GitHub - adafruit/Adafruit_ILI9340: This is a library for the Adafruit 2.2" SPI display.

Stock clockdivider is 11 which is 7-6Mhz
Benchmark result:

Adafruit 2.2" SPI TFT Test!
Benchmark                Time (microseconds)
Screen fill		1913383
Text                     121685
Lines                    1147001
Horiz/Vert Lines         157330
Rectangles (outline)     101025
Rectangles (filled)      3972593
Circles (filled)         610004
Circles (outline)        500776
Triangles (outline)      363771
Triangles (filled)       1314108
Rounded rects (outline)  221244
Rounded rects (filled)   4345116
Done!

I changed it to 9
Benchmark results. Clockdivider = 9

Adafruit 2.2" SPI TFT Test!
Benchmark                Time (microseconds)
Screen fill				1803141
Text                     116696
Lines                    1100386
Horiz/Vert Lines         147317
Rectangles (outline)     94720
Rectangles (filled)      3743585
Circles (filled)         579950
Circles (outline)        480619
Triangles (outline)      348937
Triangles (filled)       1227087
Rounded rects (outline)  210705
Rounded rects (filled)   4093446
Done!

Which was a little bit better… Now, I just wanted to know the limit of the Display (or what happens when the signal is to fast). So I changed it to 2
Benchmark results. Clockdivider = 2

Adafruit 2.2" SPI TFT Test!
Benchmark                Time (microseconds)
Screen fill              1290915
Text                     92619
Lines                    872079
Horiz/Vert Lines         105513
Rectangles (outline)     68141
Rectangles (filled)      2680671
Circles (filled)         434087
Circles (outline)        380752
Triangles (outline)      276503
Triangles (filled)       882989
Rounded rects (outline)  162050
Rounded rects (filled)   2938015
Done!

Hm, I was puzzled… The screen is very fast and even the lines and small letters were without any fault. I thought there would be some kind of dead pixels or something, but nothing. The Display is cripy as always…
It was even working with a clockdivider of 1, so at full speed 84Mhz!

After this I tried the new “Sam feature”, adding the SPI Pin for this mode and it made it terrible slow

Benchmark results. SPI.setClockDivider(2,_cs)

Benchmark                Time (microseconds)
Screen fill              1876418
Text                     120145
Lines                    1132974
Horiz/Vert Lines         153278
Rectangles (outline)     98503
Rectangles (filled)      3896083
Circles (filled)         600755
Circles (outline)        494876
Triangles (outline)      359256
Triangles (filled)       1276280
Rounded rects (outline)  217630
Rounded rects (filled)   4259117
Done!

Can anyone explain what is happening? Is the display really running at 42Mhz? And why is the new function slowing it down so much? I’d really like to use the SPI mode for specific Pins, but not when it is that slow. I think then it will be faster to write your own output specific Clockdivider function.

-tsaG

I rewrote the library to, for extended SPI and as it is for the DUE only i tweaked and removed stuff. On a first test with a divider of 11 i got this:

Adafruit 2.2" SPI TFT Test!
Benchmark                Time (microseconds)
Screen fill              1642991
Text                     122074
Lines                    1178480
Horiz/Vert Lines         135978
Rectangles (outline)     87993
Rectangles (filled)      3412036
Circles (filled)         566893
Circles (outline)        514851
Triangles (outline)      373698
Triangles (filled)       1150626
Rounded rects (outline)  215616
Rounded rects (filled)   3746075
Done!

It is still compatible with the old library but i have to check some stuff out.

I guess you did not made the right initialization. Maybe u used pinmode for the CS of the display, this will not work when using extended SPI

void Adafruit_ILI9340::begin(void) {
  pinMode(_rst, OUTPUT);
  pinMode(_dc, OUTPUT);
  digitalWrite(_rst, LOW);

  SPI.begin(_cs);
  SPI.setClockDivider(_cs,11); // 84MHz / 11 = 7.6 MHz (full! speed!)
  SPI.setBitOrder(_cs,MSBFIRST);
  SPI.setDataMode(_cs,SPI_MODE0);

I get better results, tweaking a library:

Benchmark Time (microseconds) Screen fill 1272712 Text 83679 Lines 770077 Horiz/Vert Lines 103624 Rectangles (outline) 66612 Rectangles (filled) 2642734 Circles (filled) 408815 Circles (outline) 335850 Triangles (outline) 244213 Triangles (filled) 860011 Rounded rects (outline) 147955 Rounded rects (filled) 2890392

Improvement mostly due to optimization in this 2 functions:

void Adafruit_ILI9340::writecommand(uint8_t c) {
/*
  CLEAR_BIT(dcport, dcpinmask);
  CLEAR_BIT(clkport, clkpinmask);
  CLEAR_BIT(csport, cspinmask);
  spiwrite(c);
  SET_BIT(csport, cspinmask);
*/
  CLEAR_BIT(dcport, dcpinmask);
  CLEAR_BIT(csport, cspinmask);
  spiwrite(c);
  SET_BIT(dcport,  dcpinmask);
  SET_BIT(csport, cspinmask);
}


void Adafruit_ILI9340::writedata(uint8_t c) {
/*
  SET_BIT(dcport,  dcpinmask);
  CLEAR_BIT(clkport, clkpinmask);
  CLEAR_BIT(csport, cspinmask);
  spiwrite(c);
  SET_BIT(csport, cspinmask);
*/
  CLEAR_BIT(csport, cspinmask);
  spiwrite(c);
  SET_BIT(csport, cspinmask);
}

As you can see, rearranging lines in first "write commands" allows reduce two calls: SET_BIT and CLEAR_BIT. SPI set to 42 MHz, AFAIR.

Next logical step, is to change a SPI mode:

SPI_ConfigureNPCS(spi, ch, mode[ch] | SPI_CSR_SCBR(divider[ch]) | SPI_CSR_DLYBCT(0));

Results:

Adafruit 2.2" SPI TFT Test! Benchmark Time (microseconds) Screen fill 1254578 Text 90745 Lines 853465 Horiz/Vert Lines 103581 Rectangles (outline) 66857 Rectangles (filled) 2605449 Circles (filled) 422465 Circles (outline) 372370 Triangles (outline) 270660 Triangles (filled) 871669 Rounded rects (outline) 158699 Rounded rects (filled) 2858402 Done!

I have got this :
[codevoid Adafruit_ILI9340::writecommand(uint8_t c) 
{
 digitalWrite(_dc, LOW);
 SPI.transfer(_cs,c); 
}

void Adafruit_ILI9340::writedata(uint8_t c) 
{
 digitalWrite(_dc, HIGH);
 SPI.transfer(_cs,c); 
}

As the SPI CS is controlled by the hardware SPI.
I guess you have set the divider to 2?
I had it on 1 and my times ware halved, most time is spend setting bits, unless i rewrite the hardware SPI to 9 bits.

There is also some profit to gain to keep the CS line selected:

void Adafruit_ILI9340::drawPixel(int16_t x, int16_t y, uint16_t color) {

  if((x < 0) ||(x >= _width) || (y < 0) || (y >= _height)) return;

  setAddrWindow(x,y,x+1,y+1);

  digitalWrite(_dc, HIGH);
  SPI.transfer(_cs, color >> 8,SPI_CONTINUE);
  SPI.transfer(_cs, color );
}

I guess the most gain is now form optimizing the code itself, must study the ILI9340 to see if there more possibility’s .

on 42 mhz i have:

Adafruit 2.2" SPI TFT Test!
Benchmark                Time (microseconds)
Screen fill              984514
Text                     91129
Lines                    884904
Horiz/Vert Lines         82232
Rectangles (outline)     53828
Rectangles (filled)      2044850
Circles (filled)         379382
Circles (outline)        386404
Triangles (outline)      280569
Triangles (filled)       708223
Rounded rects (outline)  153067
Rounded rects (filled)   2259500
Done!

This display rocks, would be great to optimize till perfection and put it in the Ardunio due library.

The Due should be able draw faster. The fastest Due results posted are similar to the results I get with an UNO with some library optimisations:

Benchmark                Time (microseconds)
Screen fill              1158844
Text                     97088
Lines                    444700
Horiz/Vert Lines         95672
Rectangles (outline)     61920
Rectangles (filled)      2375936
Circles (filled)         399912
Circles (outline)        292336
Triangles (outline)      116876
Triangles (filled)       902100
Rounded rects (outline)  134092
Rounded rects (filled)   2638648
Done! Total = 8717604

This display rocks, would be great to optimize till perfection and put it in the Ardunio due library.

If you got time you can optimize my lib for ILI9341 (which should also work on ILI9340, you just need to specify the Reset pin in the constructor).