TFT LCD displaying BMP as fast as possible (ILI9341_t3)

Status
Not open for further replies.

Fuzzy

Active member
I tried the adafruit ILI9341 library and it takes about 1000ms for a 320x240 (~320kb) BMP to be drawn.
I then tried Paul's optimized library (https://github.com/PaulStoffregen/ILI9341_t3) and it takes about 800ms for the same BMP to be drawn. Nice!

Is there any way we can enable 8bit BMP files to be drawn? Or anything to speed this up? The latency is pretty much a deal-breaker for interactive design. It's great if you want a screen saver I suppose, but beyond that, I am not sure how useful the BMP draw is.

I am sure if you are a C ninja you can get 8bits going no problem. But it is surely beyond me.

Thanks!
 
Yeah, it could probably be a LOT faster. The main problem is writing one pixel at a time, which incurs a lot of overhead for each write.
 
Paul I am gonna boldly request you bless us with your genius and write a c file that can ready 8bit BMP files :]

I can't put a lot of time into this right now, but maybe I could do something quick.

You could help maximize the amount I do, by putting together a little test program that shows several images in rapid succession, looping through them. Maybe call micros() and print the time elapsed for each to the serial monitor. If you post such a test program and the collection of images, it'd save me time.... time I could use for actually digging into the performance issue.
 
Oh I will definitely run a performance test and compare your 8bit with adafruits 24bit. no problem!
 
Well, I did a little fiddling. I added some simple code to the spitftbitmap to investigate where it's spending so much time.

Code:
Try using writeRect
Loading image 'purple.bmp'
File size: 230454
Image Offset: 54
Header size: 40
Bit Depth: 24
Image size: 240x320
Loaded in 919 ms
Seek: 11573
Read: 686103
Parse: 99626
Draw: 118226

Looks like the SD card reading is the performance problem area.....

Here's the code:

Code:
/***************************************************
  This is our Bitmap drawing 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 <ILI9341_t3.h> // Hardware-specific library
#include <SPI.h>
#include <SD.h>

// TFT display and SD card will share the hardware SPI interface.
// Hardware SPI pins are specific to the Arduino board type and
// cannot be remapped to alternate pins.  For Arduino Uno,
// Duemilanove, etc., pin 11 = MOSI, pin 12 = MISO, pin 13 = SCK.

#define TFT_DC  9
#define TFT_CS 10
ILI9341_t3 tft = ILI9341_t3(TFT_CS, TFT_DC, 8);

#define SD_CS 4

void setup(void) {
  delay(2000);
  Serial.begin(9600);

  tft.begin();
  tft.fillScreen(ILI9341_BLUE);

  Serial.print("Initializing SD card...");
  while (!SD.begin(SD_CS)) {
    Serial.println("failed to access SD card!");
    delay(1000);
  }
  Serial.println("OK!");
}

void loop() {
  Serial.println();
  Serial.println("Try using writeRect");
  tft.fillScreen(ILI9341_GREEN);
  bmpDrawWriteRect("purple.bmp", 0, 0);
  delay(5000);
}

// This function opens a Windows Bitmap (BMP) file and
// displays it at the given coordinates.  It's sped up
// by reading many pixels worth of data at a time
// (rather than pixel by pixel).  Increasing the buffer
// size takes more of the Arduino's precious RAM but
// makes loading a little faster.  20 pixels seems a
// good balance.

//#define BUFFPIXEL 90 // fail
#define BUFFPIXEL 50
//===========================================================
// Try Draw using writeRect
void bmpDrawWriteRect(char *filename, uint8_t x, uint16_t y) {

  File     bmpFile;
  int      bmpWidth, bmpHeight;   // W+H in pixels
  uint8_t  bmpDepth;              // Bit depth (currently must be 24)
  uint32_t bmpImageoffset;        // Start of image data in file
  uint32_t rowSize;               // Not always = bmpWidth; may have padding
  uint8_t  sdbuffer[3*BUFFPIXEL]; // pixel buffer (R+G+B per pixel)
  uint8_t  buffidx = sizeof(sdbuffer); // Current position in sdbuffer
  boolean  goodBmp = false;       // Set to true on valid header parse
  boolean  flip    = true;        // BMP is stored bottom-to-top
  int      w, h, row, col;
  uint8_t  r, g, b;
  uint32_t pos = 0, startTime = millis();

  uint16_t awColors[320];  // hold colors for one row at a time...

  if((x >= tft.width()) || (y >= tft.height())) return;

  Serial.print(F("Loading image '"));
  Serial.print(filename);
  Serial.println('\'');

  // Open requested file on SD card
  if ((bmpFile = SD.open(filename)) == NULL) {
    Serial.print(F("File not found"));
    return;
  }

  elapsedMicros usec;
  uint32_t us;
  uint32_t total_seek = 0;
  uint32_t total_read = 0;
  uint32_t total_parse = 0;
  uint32_t total_draw = 0;

  // Parse BMP header
  if(read16(bmpFile) == 0x4D42) { // BMP signature
    Serial.print(F("File size: ")); Serial.println(read32(bmpFile));
    (void)read32(bmpFile); // Read & ignore creator bytes
    bmpImageoffset = read32(bmpFile); // Start of image data
    Serial.print(F("Image Offset: ")); Serial.println(bmpImageoffset, DEC);
    // Read DIB header
    Serial.print(F("Header size: ")); Serial.println(read32(bmpFile));
    bmpWidth  = read32(bmpFile);
    bmpHeight = read32(bmpFile);
    if(read16(bmpFile) == 1) { // # planes -- must be '1'
      bmpDepth = read16(bmpFile); // bits per pixel
      Serial.print(F("Bit Depth: ")); Serial.println(bmpDepth);
      if((bmpDepth == 24) && (read32(bmpFile) == 0)) { // 0 = uncompressed

        goodBmp = true; // Supported BMP format -- proceed!
        Serial.print(F("Image size: "));
        Serial.print(bmpWidth);
        Serial.print('x');
        Serial.println(bmpHeight);

        // BMP rows are padded (if needed) to 4-byte boundary
        rowSize = (bmpWidth * 3 + 3) & ~3;

        // If bmpHeight is negative, image is in top-down order.
        // This is not canon but has been observed in the wild.
        if(bmpHeight < 0) {
          bmpHeight = -bmpHeight;
          flip      = false;
        }

        // Crop area to be loaded
        w = bmpWidth;
        h = bmpHeight;
        if((x+w-1) >= tft.width())  w = tft.width()  - x;
        if((y+h-1) >= tft.height()) h = tft.height() - y;

        usec = 0;
        for (row=0; row<h; row++) { // For each scanline...

          // Seek to start of scan line.  It might seem labor-
          // intensive to be doing this on every line, but this
          // method covers a lot of gritty details like cropping
          // and scanline padding.  Also, the seek only takes
          // place if the file position actually needs to change
          // (avoids a lot of cluster math in SD library).
          if(flip) // Bitmap is stored bottom-to-top order (normal BMP)
            pos = bmpImageoffset + (bmpHeight - 1 - row) * rowSize;
          else     // Bitmap is stored top-to-bottom
            pos = bmpImageoffset + row * rowSize;
          if(bmpFile.position() != pos) { // Need seek?
            bmpFile.seek(pos);
            buffidx = sizeof(sdbuffer); // Force buffer reload
          }
          us = usec;
          usec -= us;
          total_seek += us;

          for (col=0; col<w; col++) { // For each pixel...
            // Time to read more pixel data?
            if (buffidx >= sizeof(sdbuffer)) { // Indeed
              us = usec;
              usec -= us;
              total_parse += us;
              bmpFile.read(sdbuffer, sizeof(sdbuffer));
              buffidx = 0; // Set index to beginning
              us = usec;
              usec -= us;
              total_read += us;
            }

            // Convert pixel from BMP to TFT format, push to display
            b = sdbuffer[buffidx++];
            g = sdbuffer[buffidx++];
            r = sdbuffer[buffidx++];
            awColors[col] = tft.color565(r,g,b);
          } // end pixel
          us = usec;
          usec -= us;
          total_parse += us;
          tft.writeRect(0, row, w, 1, awColors);
          us = usec;
          usec -= us;
          total_draw += us;
        } // end scanline
        Serial.print(F("Loaded in "));
        Serial.print(millis() - startTime);
        Serial.println(" ms");
        Serial.print("Seek: ");
        Serial.println(total_seek);
        Serial.print("Read: ");
        Serial.println(total_read);
        Serial.print("Parse: ");
        Serial.println(total_parse);
        Serial.print("Draw: ");
        Serial.println(total_draw);
      } // end goodBmp
    }
  }

  bmpFile.close();
  if(!goodBmp) Serial.println(F("BMP format not recognized."));
}



// These read 16- and 32-bit types from the SD card file.
// BMP data is stored little-endian, Arduino is little-endian too.
// May need to reverse subscript order if porting elsewhere.

uint16_t read16(File &f) {
  uint16_t result;
  ((uint8_t *)&result)[0] = f.read(); // LSB
  ((uint8_t *)&result)[1] = f.read(); // MSB
  return result;
}

uint32_t read32(File &f) {
  uint32_t result;
  ((uint8_t *)&result)[0] = f.read(); // LSB
  ((uint8_t *)&result)[1] = f.read();
  ((uint8_t *)&result)[2] = f.read();
  ((uint8_t *)&result)[3] = f.read(); // MSB
  return result;
}
 
Okay. That's fine. Because 8bit BMP files will be exactly 1/3 the size of 24bit. So using my math skills, we should be able to get it down to ~230ms ???

Playing with the buffer didn't help at all?
 
Changing the buffer to 240 helps a little. The type for buffidx needs to be changed to 16 bits if the buffer is more than 85 pixels.

Oh, opps, earlier I was testing with Teensy at only 24 MHz. It's faster with 96 MHz clock and 240 pixel buffer:

Code:
Try using writeRect
Loading image 'purple.bmp'
File size: 230454
Image Offset: 54
Header size: 40
Bit Depth: 24
Image size: 240x320
Loaded in 524 ms
Seek: 7526
Read: 435542
Parse: 21237
Draw: 58031
 
I've been working on SD library improvements. Here's the latest:

Code:
Loaded in 339 ms
Seek: 3822
Read: 252676
Parse: 22745
Draw: 58014

The latest code is now on Github.

https://github.com/PaulStoffregen/SD

To try this, you'll need to replace your copy of SD in hardware/teensy/avr/libraries/SD with this latest code. Then edit SD_t3.h to enable USE_TEENSY3_OPTIMIZED_CODE. This stuff is still considered experimental, so you must uncomment that line. Otherwise, you'll get the same old Arduino SD library.

Let me know how it works for you?
 
I believe this is very close to the maximum possible speed with shared SPI bus and single-sector reads from the SD card.

At this point, I'm not ready to try multi-sector reading... and realistically, the 64K memory in Teensy 3.1 is going to seem pretty small for caching many 2K or 4K blocks. Next year when we have Teensy3++, with dedicated SDIO bus and much more memory and much higher clock speed, I'll probably work on the SD library to take advantage of the hardware. In theory, that hardware should allow overlapping the 58 ms TFT update with loading the image from SD card, and I'm hoping to make another 2X improvement in SD speed.

But with Teensy 3.1 and shared SPI bus, I believe this is pretty close to the best speed I can give you.

Of course, you can rewrite (or add to) Adafruit's code to handle 8 or 16 bit BMP formats. I'm not personally planning to work on that. If you do get it working, I hope you'll publish the code?
 
Paul,
Unfortunately still taking > 1s:

Loading image 'm0.bmp'
File size: 230456
Image Offset: 54
Header size: 40
Bit Depth: 24
Image size: 240x320
Loaded in 1047 ms

And also now drawing a completely glitched out BMP.

I haven't tackled the 8bit code, but might have to. Of course I will publish after. The latency in the current BMP library from adafruit is pretty much a dealbreaker for most people so anything we can do to improve it would be great.
 
Sorry my bad. I didnt have the code configured right. Good news, it is running at ~500ms. Let's be ambitious and try for 100ms.

Initializing SD card...OK!
Loading image 'm0.bmp'
File size: 230456
Image Offset: 54
Header size: 40
Bit Depth: 24
Image size: 240x320
Loaded in 524 ms
Seek: 6589
Read: 380766
Parse: 65521
Draw: 63944

If I enable USE_TEENSY3_OPTIMIZED_CODE then it glitches out slightly (weird green hue and vertical shift on the image)

Code:
/***************************************************
  This is our Bitmap drawing example for the Adafruit ILI9341 Breakout and Shield
  ----> [url]http://www.adafruit.com/products/1651[/url]

  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 <Adafruit_GFX.h>    // Core graphics library
//#include "Adafruit_ILI9341.h" // Hardware-specific library
#include <ILI9341_t3.h>
#include <SPI.h>
#include <SD.h>

// TFT display and SD card will share the hardware SPI interface.
// Hardware SPI pins are specific to the Arduino board type and
// cannot be remapped to alternate pins.  For Arduino Uno,
// Duemilanove, etc., pin 11 = MOSI, pin 12 = MISO, pin 13 = SCK.

#define TFT_DC 9
#define TFT_CS 10
//Adafruit_ILI9341 tft = Adafruit_ILI9341(TFT_CS, TFT_DC);
ILI9341_t3 tft = ILI9341_t3(TFT_CS, TFT_DC, 8);

#define SD_CS 4

int pUp = 0;
int pDown = 1;
int pLeft = 2;
int pRight = 3;
int pCenter = 5;

char *mains[] = {"m0.bmp","m1.bmp","m2.bmp"};
bool main_page = true;
bool run_page = false;
bool menu_page = false;
int mainn = 0;

void setup(void) {

  Serial.begin(9600);
  Serial.println("OKOK");

  delay(1000);

  tft.begin();
  tft.fillScreen(ILI9341_BLUE);

  Serial.print("Initializing SD card...");
  if (!SD.begin(SD_CS)) {
    Serial.println("failed!");
  }else{
    Serial.println("OK!");
  }


  pinMode(pUp,INPUT_PULLUP);
  pinMode(pDown,INPUT_PULLUP);
  pinMode(pLeft,INPUT_PULLUP);
  pinMode(pRight,INPUT_PULLUP);
  pinMode(pCenter,INPUT_PULLUP);
  
  bmpDraw(mains[0], 0, 0);
  //bmpDraw("test.bmp", 0, 0);
}


void loop() {

  if(digitalRead(pUp)==LOW){
    delay(50);

    if(main_page==true){
      mainn--;
      if(mainn<0){mainn=3;}
      //tft.fillScreen(ILI9341_BLACK);
      bmpDraw(mains[mainn], 0, 0);
    }
  }
  if(digitalRead(pDown)==LOW){
    delay(50);

    if(main_page==true){
      mainn++;
      if(mainn>2){mainn=0;}
      //tft.fillScreen(ILI9341_BLACK);
      bmpDraw(mains[mainn], 0, 0);
    }
  }

  if(digitalRead(pCenter)==LOW){
    if(main_page==true){
      if(mainn==0){
        main_page=false;
        //tft.fillScreen(ILI9341_BLACK);
        bmpDraw("run.bmp", 0, 0);
      }
      if(mainn==2){
        main_page=false;
        //tft.fillScreen(ILI9341_BLACK);
        bmpDraw("menu.bmp", 0, 0);
      }
    }else{
      bmpDraw(mains[0], 0, 0);
      mainn = 0;
      main_page=true;
    }
  }
  
}


#define BUFFPIXEL 240
//===========================================================
// Try Draw using writeRect
void bmpDraw(char *filename, uint8_t x, uint16_t y) {

  File     bmpFile;
  int      bmpWidth, bmpHeight;   // W+H in pixels
  uint8_t  bmpDepth;              // Bit depth (currently must be 24)
  uint32_t bmpImageoffset;        // Start of image data in file
  uint32_t rowSize;               // Not always = bmpWidth; may have padding
  uint8_t  sdbuffer[3*BUFFPIXEL]; // pixel buffer (R+G+B per pixel)
  uint16_t  buffidx = sizeof(sdbuffer); // Current position in sdbuffer
  boolean  goodBmp = false;       // Set to true on valid header parse
  boolean  flip    = true;        // BMP is stored bottom-to-top
  int      w, h, row, col;
  uint8_t  r, g, b;
  uint32_t pos = 0, startTime = millis();

  uint16_t awColors[320];  // hold colors for one row at a time...

  if((x >= tft.width()) || (y >= tft.height())) return;

  Serial.print(F("Loading image '"));
  Serial.print(filename);
  Serial.println('\'');

  // Open requested file on SD card
  if ((bmpFile = SD.open(filename)) == NULL) {
    Serial.print(F("File not found"));
    return;
  }

  elapsedMicros usec;
  uint32_t us;
  uint32_t total_seek = 0;
  uint32_t total_read = 0;
  uint32_t total_parse = 0;
  uint32_t total_draw = 0;

  // Parse BMP header
  if(read16(bmpFile) == 0x4D42) { // BMP signature
    Serial.print(F("File size: ")); Serial.println(read32(bmpFile));
    (void)read32(bmpFile); // Read & ignore creator bytes
    bmpImageoffset = read32(bmpFile); // Start of image data
    Serial.print(F("Image Offset: ")); Serial.println(bmpImageoffset, DEC);
    // Read DIB header
    Serial.print(F("Header size: ")); Serial.println(read32(bmpFile));
    bmpWidth  = read32(bmpFile);
    bmpHeight = read32(bmpFile);
    if(read16(bmpFile) == 1) { // # planes -- must be '1'
      bmpDepth = read16(bmpFile); // bits per pixel
      Serial.print(F("Bit Depth: ")); Serial.println(bmpDepth);
      if((bmpDepth == 24) && (read32(bmpFile) == 0)) { // 0 = uncompressed

        goodBmp = true; // Supported BMP format -- proceed!
        Serial.print(F("Image size: "));
        Serial.print(bmpWidth);
        Serial.print('x');
        Serial.println(bmpHeight);

        // BMP rows are padded (if needed) to 4-byte boundary
        rowSize = (bmpWidth * 3 + 3) & ~3;

        // If bmpHeight is negative, image is in top-down order.
        // This is not canon but has been observed in the wild.
        if(bmpHeight < 0) {
          bmpHeight = -bmpHeight;
          flip      = false;
        }

        // Crop area to be loaded
        w = bmpWidth;
        h = bmpHeight;
        if((x+w-1) >= tft.width())  w = tft.width()  - x;
        if((y+h-1) >= tft.height()) h = tft.height() - y;

        usec = 0;
        for (row=0; row<h; row++) { // For each scanline...

          // Seek to start of scan line.  It might seem labor-
          // intensive to be doing this on every line, but this
          // method covers a lot of gritty details like cropping
          // and scanline padding.  Also, the seek only takes
          // place if the file position actually needs to change
          // (avoids a lot of cluster math in SD library).
          if(flip) // Bitmap is stored bottom-to-top order (normal BMP)
            pos = bmpImageoffset + (bmpHeight - 1 - row) * rowSize;
          else     // Bitmap is stored top-to-bottom
            pos = bmpImageoffset + row * rowSize;
          if(bmpFile.position() != pos) { // Need seek?
            bmpFile.seek(pos);
            buffidx = sizeof(sdbuffer); // Force buffer reload
          }
          us = usec;
          usec -= us;
          total_seek += us;

          for (col=0; col<w; col++) { // For each pixel...
            // Time to read more pixel data?
            if (buffidx >= sizeof(sdbuffer)) { // Indeed
              us = usec;
              usec -= us;
              total_parse += us;
              bmpFile.read(sdbuffer, sizeof(sdbuffer));
              buffidx = 0; // Set index to beginning
              us = usec;
              usec -= us;
              total_read += us;
            }

            // Convert pixel from BMP to TFT format, push to display
            b = sdbuffer[buffidx++];
            g = sdbuffer[buffidx++];
            r = sdbuffer[buffidx++];
            awColors[col] = tft.color565(r,g,b);
          } // end pixel
          us = usec;
          usec -= us;
          total_parse += us;
          tft.writeRect(0, row, w, 1, awColors);
          us = usec;
          usec -= us;
          total_draw += us;
        } // end scanline
        Serial.print(F("Loaded in "));
        Serial.print(millis() - startTime);
        Serial.println(" ms");
        Serial.print("Seek: ");
        Serial.println(total_seek);
        Serial.print("Read: ");
        Serial.println(total_read);
        Serial.print("Parse: ");
        Serial.println(total_parse);
        Serial.print("Draw: ");
        Serial.println(total_draw);
      } // end goodBmp
    }
  }

  bmpFile.close();
  if(!goodBmp) Serial.println(F("BMP format not recognized."));
}



// These read 16- and 32-bit types from the SD card file.
// BMP data is stored little-endian, Arduino is little-endian too.
// May need to reverse subscript order if porting elsewhere.

uint16_t read16(File &f) {
  uint16_t result;
  ((uint8_t *)&result)[0] = f.read(); // LSB
  ((uint8_t *)&result)[1] = f.read(); // MSB
  return result;
}

uint32_t read32(File &f) {
  uint32_t result;
  ((uint8_t *)&result)[0] = f.read(); // LSB
  ((uint8_t *)&result)[1] = f.read();
  ((uint8_t *)&result)[2] = f.read();
  ((uint8_t *)&result)[3] = f.read(); // MSB
  return result;
}
 
Last edited:
For the sake of those that come along later and my curiosity, what did you change to make get the "code configured right"?
 
If I enable USE_TEENSY3_OPTIMIZED_CODE then it glitches out slightly (weird green hue and vertical shift on the image)

I didn't see anything like that here.

Any chance you could try with a different SD card. If it does turn out to be the card, please save the bad one! Maybe we could swap cards? I'm trying to build up a collection of marginal SD cards for future software testing.....
 
Paul do you mind just posting the Arduino code you are using and I will test it on my end? This way we can see if it is my SD card or my BMP file.
 
It's the same code that I posted on message #6, tested with my latest SD library, with USE_TEENSY3_OPTIMIZED_CODE enabled in SD_t3.h.

Hello.

Without enabling the USE_TEENSY3_OPTIMIZED_CODE, I get average loading/rendering times around 550ms-600ms (mostly depending on the image orientation)

Code:
Loading image 'purple.bmp'
File size: 230454
Image Offset: 54
Header size: 40
Bit Depth: 24
Image size: 240x320
Loaded in 531 ms

Enabling the flag in SD_t3.h doesn't work for me. I can't access the module anymore. Or does the omtimization work only with specific teensy digital pins ?
I'm using a 2.4" ILI9341 display with the 5 pins SD connector (F_CS, SD_SCK, SD_MISO, SD_MOSI, SD_CS)

I'm actually using the default SPI pins and SD_CS on pin 15

Code:
#define TFT_DC  9
#define TFT_CS 10
ILI9341_t3 tft = ILI9341_t3(TFT_CS, TFT_DC);

#define SD_CS 15

It seems that some members of the STM_DUINO forum are working on adding DMA support to the SdFat library and get huge performances bump (even with a shared SPI For the display and the SD module). What kind of optimizations did you implement ?

Did someone try to benchmark with RAW 565 to compare loading/rendering performances ?
I've found some examples for the ili9341_due library that use function that has not been implemented in your optimized driver (or it has another name)
pushColors565 and puchColors.

I'm pretty sure with a fast SD card library and perhaps raw565 image files, it's possible to break the 100ms wall ;)

Regards,
 
You might get close to 100 ms with SPI flash chips and files stored in native 565 color format.

The SPI flash chips with the SerialFlash library are much faster than SD cards (for use on SPI bus). The chips have zero read latency, other than 4 bytes to send the command and read address.
 
Next year, when we have Teensy3++ with native SDIO, I'm going to put quite a bit more work into the SD library, and dedicate much more memory to caching. My hope is to (eventually) implement asynchronous DMA-based read-ahead to populate the cache before your code needs the data. Whether that ends up happening in mid or late 2016, or gets pushed out even farther is a good question. But eventually I'm going to do it.

Hopefully that will someday get most SD reads to be satisfied from already-cached data.... allowing for the entire display to update in 50-some milliseconds (the raw time required to write all 153600 bytes of the frame buffer).
 
Presumably SDIO 4 bit mode, 48MHz?

Yes, 4 bits.

The specific speeds available are integer division of the CPU clock speed. The integer is a prescaler 2^N, where N is 1 to 8, times a multiplier, 1 to 16.

When the CPU clock is 180 MHz, divide by 4 gives 45 MHz.

When the CPU clock is 96 MHz, divide by 2 gives 48 MHz.

When the CPU clock is 120 MHz, divide by 3 isn't available because the prescaler must be at least 2. So 30 MHz would be the fastest speed under the 50 MHz limit. That's just the way integer dividers go.

But the design of the caching and read-ahead algorithms, so the data is often already cached when needed, will probably have a much larger effect on overall application speed than raw SDIO clock rates.


What is the maximum SPI speed of the Display

That's a very good question. Some might technically have a 10 MHz spec.

Reliable specs for these displays are hard to find.

and the new Teensy3++?

30 MHz

And F_BUS ?

60 MHz is the specified max (up from 50 MHz on other chips).
 
Status
Not open for further replies.
Back
Top