Forum Rule: Always post complete source code & details to reproduce any issue!
Results 1 to 24 of 24

Thread: TFT LCD displaying BMP as fast as possible (ILI9341_t3)

  1. #1

    TFT LCD displaying BMP as fast as possible (ILI9341_t3)

    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!

  2. #2
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    23,039
    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.

  3. #3
    I did find http://home.comcast.net/~elmoi/bmpvu.c from WINDOWS '98!

    Paul I am gonna boldly request you bless us with your genius and write a c file that can ready 8bit BMP files :]

    Then finally these LCDs will be of use to designers and not crippled by this 1FPS performance.

  4. #4
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    23,039
    Quote Originally Posted by Fuzzy View Post
    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.

  5. #5
    Oh I will definitely run a performance test and compare your 8bit with adafruits 24bit. no problem!

  6. #6
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    23,039
    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;
    }

  7. #7
    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?

  8. #8
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    23,039
    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

  9. #9
    Niiiiice! That is certainly an improvement. Any thoughts on that 8bit BMP goodness?

  10. #10
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    23,039
    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?

  11. #11
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    23,039
    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?

  12. #12
    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.

  13. #13
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    23,039
    Maybe add a W25Q128FV chip and use SerialFlash instead of SD?

  14. #14
    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
      ----> 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 <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 by Fuzzy; 08-13-2015 at 10:02 PM.

  15. #15
    Senior Member pictographer's Avatar
    Join Date
    May 2013
    Location
    San Jose, CA
    Posts
    681
    For the sake of those that come along later and my curiosity, what did you change to make get the "code configured right"?

  16. #16
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    23,039
    Quote Originally Posted by Fuzzy View Post
    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.....

  17. #17
    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.

  18. #18
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    23,039
    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.

  19. #19
    Junior Member
    Join Date
    Sep 2015
    Posts
    1
    Quote Originally Posted by PaulStoffregen View Post
    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,

  20. #20
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    23,039
    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.

  21. #21
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    23,039
    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).

  22. #22
    Senior Member
    Join Date
    Jun 2013
    Location
    So. Calif
    Posts
    2,828
    Presumably SDIO 4 bit mode, 48MHz?

  23. #23
    Senior Member+ Frank B's Avatar
    Join Date
    Apr 2014
    Location
    Germany NRW
    Posts
    7,065
    What is the maximum SPI speed of the Display and the new Teensy3++?
    And F_BUS ?

  24. #24
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    23,039
    Quote Originally Posted by stevech View Post
    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.


    Quote Originally Posted by Frank B View Post
    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).

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •