adafruit / Adafruit-GFX-Library

Adafruit GFX graphics core Arduino library, this is the 'core' class that all our other graphics libraries derive from
https://learn.adafruit.com/adafruit-gfx-graphics-library
Other
2.32k stars 1.52k forks source link

tft.begin(uint32 freq) does not change frequency #274

Open bdlabitt opened 4 years ago

bdlabitt commented 4 years ago

Added two lines of code to graphicstest.

#define TFT_FREQ 32000000
tft.begin(TFT_FREQ); 

Put oscilloscope probe on SPICLK at the ILI9341 display. Full code below.

/***************************************************
  This is our GFX example for the Adafruit ILI9341 Breakout and Shield
  ----> http://www.adafruit.com/products/1651

  Check out the links above for our tutorials and wiring diagrams
  These displays use SPI to communicate, 4 or 5 pins are required to
  interface (RST is optional)
  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.
  MIT license, all text above must be included in any redistribution
 ****************************************************/

#include "SPI.h"
#include "Adafruit_GFX.h"
#include "Adafruit_ILI9341.h"

// For the Adafruit shield, these are the default.
#define TFT_DC 9
#define TFT_CS 10

// Use hardware SPI (on Uno, #13, #12, #11) and the above for CS/DC
Adafruit_ILI9341 tft = Adafruit_ILI9341(TFT_CS, TFT_DC);
// If using the breakout, change pins as desired
//Adafruit_ILI9341 tft = Adafruit_ILI9341(TFT_CS, TFT_DC, TFT_MOSI, TFT_CLK, TFT_RST, TFT_MISO);

#define TFT_FREQ 33000000

void setup() {
  Serial.begin(115200);
  while(!Serial) {
    ; //wait for serialport to connect
  }

  Serial.println("ILI9341 Test!"); 

  tft.begin(TFT_FREQ);

  // read diagnostics (optional but can help debug problems)
  uint8_t x = tft.readcommand8(ILI9341_RDMODE);
  Serial.print("Display Power Mode: 0x"); Serial.println(x, HEX);
  x = tft.readcommand8(ILI9341_RDMADCTL);
  Serial.print("MADCTL Mode: 0x"); Serial.println(x, HEX);
  x = tft.readcommand8(ILI9341_RDPIXFMT);
  Serial.print("Pixel Format: 0x"); Serial.println(x, HEX);
  x = tft.readcommand8(ILI9341_RDIMGFMT);
  Serial.print("Image Format: 0x"); Serial.println(x, HEX);
  x = tft.readcommand8(ILI9341_RDSELFDIAG);
  Serial.print("Self Diagnostic: 0x"); Serial.println(x, HEX); 

  Serial.println(F("Benchmark                Time (microseconds)"));
  delay(10);
  Serial.print(F("Screen fill              "));
  Serial.println(testFillScreen());
  delay(500);

  Serial.print(F("Text                     "));
  Serial.println(testText());
  delay(3000);

  Serial.print(F("Lines                    "));
  Serial.println(testLines(ILI9341_CYAN));
  delay(500);

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

  Serial.print(F("Rectangles (outline)     "));
  Serial.println(testRects(ILI9341_GREEN));
  delay(500);

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

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

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

  Serial.print(F("Triangles (outline)      "));
  Serial.println(testTriangles());
  delay(500);

  Serial.print(F("Triangles (filled)       "));
  Serial.println(testFilledTriangles());
  delay(500);

  Serial.print(F("Rounded rects (outline)  "));
  Serial.println(testRoundRects());
  delay(500);

  Serial.print(F("Rounded rects (filled)   "));
  Serial.println(testFilledRoundRects());
  delay(500);

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

}

void loop(void) {
  for(uint8_t rotation=0; rotation<4; rotation++) {
    tft.setRotation(rotation);
    testText();
    delay(1000);
  }
}

unsigned long testFillScreen() {
  unsigned long start = micros();
  tft.fillScreen(ILI9341_BLACK);
  yield();
  tft.fillScreen(ILI9341_RED);
  yield();
  tft.fillScreen(ILI9341_GREEN);
  yield();
  tft.fillScreen(ILI9341_BLUE);
  yield();
  tft.fillScreen(ILI9341_BLACK);
  yield();
  return micros() - start;
}

unsigned long testText() {
  tft.fillScreen(ILI9341_BLACK);
  unsigned long start = micros();
  tft.setCursor(0, 0);
  tft.setTextColor(ILI9341_WHITE);  tft.setTextSize(1);
  tft.println("Hello World!");
  tft.setTextColor(ILI9341_YELLOW); tft.setTextSize(2);
  tft.println(1234.56);
  tft.setTextColor(ILI9341_RED);    tft.setTextSize(3);
  tft.println(0xDEADBEEF, HEX);
  tft.println();
  tft.setTextColor(ILI9341_GREEN);
  tft.setTextSize(5);
  tft.println("Groop");
  tft.setTextSize(2);
  tft.println("I implore thee,");
  tft.setTextSize(1);
  tft.println("my foonting turlingdromes.");
  tft.println("And hooptiously drangle me");
  tft.println("with crinkly bindlewurdles,");
  tft.println("Or I will rend thee");
  tft.println("in the gobberwarts");
  tft.println("with my blurglecruncheon,");
  tft.println("see if I don't!");
  return micros() - start;
}

unsigned long testLines(uint16_t color) {
  unsigned long start, t;
  int           x1, y1, x2, y2,
                w = tft.width(),
                h = tft.height();

  tft.fillScreen(ILI9341_BLACK);
  yield();

  x1 = y1 = 0;
  y2    = h - 1;
  start = micros();
  for(x2=0; x2<w; x2+=6) tft.drawLine(x1, y1, x2, y2, color);
  x2    = w - 1;
  for(y2=0; y2<h; y2+=6) tft.drawLine(x1, y1, x2, y2, color);
  t     = micros() - start; // fillScreen doesn't count against timing

  yield();
  tft.fillScreen(ILI9341_BLACK);
  yield();

  x1    = w - 1;
  y1    = 0;
  y2    = h - 1;
  start = micros();
  for(x2=0; x2<w; x2+=6) tft.drawLine(x1, y1, x2, y2, color);
  x2    = 0;
  for(y2=0; y2<h; y2+=6) tft.drawLine(x1, y1, x2, y2, color);
  t    += micros() - start;

  yield();
  tft.fillScreen(ILI9341_BLACK);
  yield();

  x1    = 0;
  y1    = h - 1;
  y2    = 0;
  start = micros();
  for(x2=0; x2<w; x2+=6) tft.drawLine(x1, y1, x2, y2, color);
  x2    = w - 1;
  for(y2=0; y2<h; y2+=6) tft.drawLine(x1, y1, x2, y2, color);
  t    += micros() - start;

  yield();
  tft.fillScreen(ILI9341_BLACK);
  yield();

  x1    = w - 1;
  y1    = h - 1;
  y2    = 0;
  start = micros();
  for(x2=0; x2<w; x2+=6) tft.drawLine(x1, y1, x2, y2, color);
  x2    = 0;
  for(y2=0; y2<h; y2+=6) tft.drawLine(x1, y1, x2, y2, color);

  yield();
  return micros() - start;
}

unsigned long testFastLines(uint16_t color1, uint16_t color2) {
  unsigned long start;
  int           x, y, w = tft.width(), h = tft.height();

  tft.fillScreen(ILI9341_BLACK);
  start = micros();
  for(y=0; y<h; y+=5) tft.drawFastHLine(0, y, w, color1);
  for(x=0; x<w; x+=5) tft.drawFastVLine(x, 0, h, color2);

  return micros() - start;
}

unsigned long testRects(uint16_t color) {
  unsigned long start;
  int           n, i, i2,
                cx = tft.width()  / 2,
                cy = tft.height() / 2;

  tft.fillScreen(ILI9341_BLACK);
  n     = min(tft.width(), tft.height());
  start = micros();
  for(i=2; i<n; i+=6) {
    i2 = i / 2;
    tft.drawRect(cx-i2, cy-i2, i, i, color);
  }

  return micros() - start;
}

unsigned long testFilledRects(uint16_t color1, uint16_t color2) {
  unsigned long start, t = 0;
  int           n, i, i2,
                cx = tft.width()  / 2 - 1,
                cy = tft.height() / 2 - 1;

  tft.fillScreen(ILI9341_BLACK);
  n = min(tft.width(), tft.height());
  for(i=n; i>0; i-=6) {
    i2    = i / 2;
    start = micros();
    tft.fillRect(cx-i2, cy-i2, i, i, color1);
    t    += micros() - start;
    // Outlines are not included in timing results
    tft.drawRect(cx-i2, cy-i2, i, i, color2);
    yield();
  }

  return t;
}

unsigned long testFilledCircles(uint8_t radius, uint16_t color) {
  unsigned long start;
  int x, y, w = tft.width(), h = tft.height(), r2 = radius * 2;

  tft.fillScreen(ILI9341_BLACK);
  start = micros();
  for(x=radius; x<w; x+=r2) {
    for(y=radius; y<h; y+=r2) {
      tft.fillCircle(x, y, radius, color);
    }
  }

  return micros() - start;
}

unsigned long testCircles(uint8_t radius, uint16_t color) {
  unsigned long start;
  int           x, y, r2 = radius * 2,
                w = tft.width()  + radius,
                h = tft.height() + radius;

  // Screen is not cleared for this one -- this is
  // intentional and does not affect the reported time.
  start = micros();
  for(x=0; x<w; x+=r2) {
    for(y=0; y<h; y+=r2) {
      tft.drawCircle(x, y, radius, color);
    }
  }

  return micros() - start;
}

unsigned long testTriangles() {
  unsigned long start;
  int           n, i, cx = tft.width()  / 2 - 1,
                      cy = tft.height() / 2 - 1;

  tft.fillScreen(ILI9341_BLACK);
  n     = min(cx, cy);
  start = micros();
  for(i=0; i<n; i+=5) {
    tft.drawTriangle(
      cx    , cy - i, // peak
      cx - i, cy + i, // bottom left
      cx + i, cy + i, // bottom right
      tft.color565(i, i, i));
  }

  return micros() - start;
}

unsigned long testFilledTriangles() {
  unsigned long start, t = 0;
  int           i, cx = tft.width()  / 2 - 1,
                   cy = tft.height() / 2 - 1;

  tft.fillScreen(ILI9341_BLACK);
  start = micros();
  for(i=min(cx,cy); i>10; i-=5) {
    start = micros();
    tft.fillTriangle(cx, cy - i, cx - i, cy + i, cx + i, cy + i,
      tft.color565(0, i*10, i*10));
    t += micros() - start;
    tft.drawTriangle(cx, cy - i, cx - i, cy + i, cx + i, cy + i,
      tft.color565(i*10, i*10, 0));
    yield();
  }

  return t;
}

unsigned long testRoundRects() {
  unsigned long start;
  int           w, i, i2,
                cx = tft.width()  / 2 - 1,
                cy = tft.height() / 2 - 1;

  tft.fillScreen(ILI9341_BLACK);
  w     = min(tft.width(), tft.height());
  start = micros();
  for(i=0; i<w; i+=6) {
    i2 = i / 2;
    tft.drawRoundRect(cx-i2, cy-i2, i, i, i/8, tft.color565(i, 0, 0));
  }

  return micros() - start;
}

unsigned long testFilledRoundRects() {
  unsigned long start;
  int           i, i2,
                cx = tft.width()  / 2 - 1,
                cy = tft.height() / 2 - 1;

  tft.fillScreen(ILI9341_BLACK);
  start = micros();
  for(i=min(tft.width(), tft.height()); i>20; i-=6) {
    i2 = i / 2;
    tft.fillRoundRect(cx-i2, cy-i2, i, i, i/8, tft.color565(0, i, 0));
    yield();
  }

  return micros() - start;
}

CPU Speed 120 MHz -Os debug off

Also does not generate correct clock for other higher speed cpu clocks, or higher level of optimization. Expect SPICLK to be set to TFT_FREQ. Documentation states this will happen.

IMG_20200224_151928_resized SPI clock is 24MHz, not 32MHz

bdlabitt commented 4 years ago

Sorry, last line should read, "SPI clock is 24MHz, not 33MHz". SPI clock is not being set to TFT_FREQ.

aklein24 commented 4 years ago

I'm noticing similar behavior on a Particle Boron. Might be unrelated but seems like the same issue.

bdlabitt commented 3 years ago

I'm beginning to believe this is posted in the wrong place. If so, where should it go? ILI9341? No review or comment in over 6 months. Not even an acknowledgement of the issue. From what I can determine, proper DMA SPI management is non-trivial. Furthermore the solution appears to need to be compatible with SPI., SERCOM., startup.c, Adafruit_SPIDevice., and perhaps Adafruit_ZeroDMA.

For my application, I am not using the SD card on the TFT board. So it would be preferable to have a solution with SPI at 40-48MHz for write, and perhaps 24MHz for read for an M4 Express. By 40-48MHz it is meant a single fixed frequency somewhere around this value.

orhanyor commented 3 years ago

i was wondering about overclocking SPI speed too. i found this SPI speed related info on here : https://learn.adafruit.com/adafruit-feather-m4-express-atsamd51?view=all Look for "Max SPI and Max QSPI" in the article. this is exactly what you are looking for but it seems like adafruit removed the Max SPI option in Arduino, theres only a setting for MAX QSPI.

bdlabitt commented 2 years ago

Appears my comment was deleted. Not sure if I crossed a line. Apologize if I did. Wasn't intentional bashing.
Disappointing there is no apparent solution to this issue after 26 months. A lack of solution is ok, but some sort of explanation why this is difficult or impossible, would be greatly appreciated.

dhalbert commented 2 years ago

The SPI clock rate setting at high clock rates on the SAMD chips goes by big jumps at high frequencies, because it's a small divisor value.. You may need to try 48MHz instead of 24. See for instance https://github.com/adafruit/circuitpython/issues/464#issuecomment-347676877 and https://github.com/adafruit/circuitpython/pull/560 That discuss SAMD21 more than SAMD51, but the same problem is present.

bdlabitt commented 2 years ago

@dhalbert Thank you for a clue! Haven't jumped to the URLs yet, but notionally how does one change the clock? More specifically, which clock? Going off to read...

bdlabitt commented 2 years ago

Some good discussion in the other threads. Unfortunately, not being as skilled in the art as many are, it is unclear what to set and where to set it. A few breadcrumbs would be appreciated. Thanks!

dhalbert commented 2 years ago

I mean that to get a higher SPI frequency than 24MHz, you may need to specify 48MHz, not just 32 or 33MHz. Anything in between will just take the nearest lower possible frequency. 32MHz or 33MHz is probably not possible.

bdlabitt commented 2 years ago

It is worth a try! Not sure the display can manage it, but it is worth trying. So just tft.begin(48000000) ? Hope to try this today.

dhalbert commented 2 years ago

yes, try that, or even higher, but you have to change the Arduino menu items, as mentioned, and we don't guarantee it will work. I think 48MHz may be the max.

bdlabitt commented 2 years ago

"but you have to change the Arduino menu items" I don't understand this. Which Arduino menu items and where are they found? I don't recall seeing any menu items related to SPI. At this point I'm running 1.8.19 IDE.

dhalbert commented 2 years ago

Aha, I think the Board Support Package originally had a menu item to adjust the max SPI speed, and that was documented in the guide. However, we took that option away: https://github.com/adafruit/ArduinoCore-samd/pull/110. Instead you can set the clock source, using SPIClass::setClockSource(). The possible values are here.

The default clock source is the 48MHz clock, I think. Try 48MHz for now, without changing the clock source. If that doesn't work (24 MHz might be the max for a 48MHz clock), you can try changing the clock source to the CPU frequency (120 MHz) or the 100 MHz clock), so you can get higher frequencies.

bdlabitt commented 2 years ago

Yes. The board support package did offer this - but only for a short while! I remember using it, and then it vanished from the IDE. Thanks for some ideas to try! I will try a couple things and get back to you. Think I will be able to do this this weekend.

bdlabitt commented 2 years ago

Hmm, more difficult than I thought. I don't know which instantiation of SPI to set the clock or baudrate. Is this exposed through the Adafruit_ILI9341 interface? How would I know what the handle is? Could you provide a minimum example that might do this? tft is the module that calls SPI, not me. I did try tft.begin(48000000); That doesn't do anything, nor does 48000001. Still 24MHz. Do I make a #define in Adafruit_ILI9341.cpp for SAMD51 for SPI_DEFAULT_FREQ?

Or is this deep in the SPI library that I have to make this change? Just confused about where the change goes, and how to get the correct instance. Don't want to have to permanently maintain the spi lib for a single project, if I don't have to.

Any additional ideas or leads would be helpful. Thanks.

begin, sends a command initSPI, but it only sends frequency, not the clock source. begin also has access to setSPISpeed via Adafruit_SPITFT.h. But there doesn't seem to be a way to pass this through to SPI itself. SPI seems to over-ride the setting.

I have tried define MAX_SPI 48000000 prior to including SPI.h and Adafruit_ILI9341.h. But I don't see a way (my lack of knowledge) to setClockSource for the tft instance of SPI. The version of SPI.cpp is found in .arduino15/packages/adafruit/hardware/samd/1.7.10/libraries/SPI

bdlabitt commented 2 years ago

Also tried a `#define MAX_SPI 48000000

define SPICLOCK SERCOM_CLOCK_SOURCE_FCPU`

prior to including SPI.h and Adafruit_GFX.h and Adafruit_ILI9341.h and Adafruit_FT6206.h in my header file. Still 24MHz.

bdlabitt commented 2 years ago

For what it is worth, the PJRC lib ILI9341_t3n supports a 30MHz write rate and a 20MHz read. That would be pretty nice if something like that worked on a Feather M4. Unfortunately the PJRC lib is not compatible with Adafruit's. The incompatibility seems to be in the dma section, or that's where the compiler starts complaining!

fvzeppelin commented 1 year ago

Maybe related to https://github.com/arduino/ArduinoCore-samd/issues/680? Nevertheless, speeding up the clock doesn't help as much, since the begin_transaction/end_transaction macros slow things down.