problem displaying BMP on ST7735 and T3.1

Status
Not open for further replies.

el_supremo

Well-known member
I'm having problems displaying a BMP file on a ST7735 and T3.1. The problem does not occur on a T3. I'm using the distributed libraries in Arduino 1.6.5-r2 and TD 1.24 beta 3.
With this code from Adafruit (I changed it to use SDFat because SD wouldn't work at all) the parrot.bmp image distributed with the Adafruit library won't display properly (nor will any other BMP). I eventually found that the problem was fixed by adding a delay of just one microsecond before the next buffer was read from the uSD card.
In the code below there's a delayMicroseconds commented at about line 204. As is, the display fails. If I include the delay, the display works.
I presume that this delay gives something (control signal such as the CS) time to settle.
Can someone try this and let me know if it also fails without the delay but works with it?

Pete

Code:
/***************************************************
  This is an example sketch for the Adafruit 1.8" SPI display.
  This library works with the Adafruit 1.8" TFT Breakout w/SD card
  ----> http://www.adafruit.com/products/358
  as well as Adafruit raw 1.8" TFT display
  ----> http://www.adafruit.com/products/618

  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
==============================================================
Pete - El_Supremo
The SD library doesn't work.
Change this to use sdfat which works
150716
- changed read16 and read32 to use bmpFile directly. Otherwise it
  keeps rereading from the beginning of the file.
- Weird timing problem which is fixed by putting a delay of just one
  microsecond in the inner loop, before the bmpFile.read
 ****************************************************/

#include <Adafruit_GFX.h>    // Core graphics library
#include <Adafruit_ST7735.h> // Hardware-specific library

#include <SPI.h>
#include <SdFat.h>
#include <SdFatUtil.h>

SdFat sd;
// Input file
SdFile bmpFile;

#if defined(__SAM3X8E__)
    #undef __FlashStringHelper::F(string_literal)
    #define F(string_literal) string_literal
#endif

// This Teensy3 native optimized version requires specific pins
//
#define sclk 13  // SCLK can also use pin 14
#define mosi 11  // MOSI can also use pin 7
#define cs   10  // CS & DC can use pins 2, 6, 9, 10, 15, 20, 21, 22, 23
#define dc   9   //  but certain pairs must NOT be used: 2+10, 6+9, 20+23, 21+22
#define rst  8   // RST can use any pin
#define sdcs 4   // CS for SD card, can use any pin

Adafruit_ST7735 tft = Adafruit_ST7735(cs, dc, mosi, sclk, rst);

void setup(void) {
  Serial.begin(9600);
  while(!Serial);
  delay(1000);
  
  // Our supplier changed the 1.8" display slightly after Jan 10, 2012
  // so that the alignment of the TFT had to be shifted by a few pixels
  // this just means the init code is slightly different. Check the
  // color of the tab to see which init code to try. If the display is
  // cut off or has extra 'random' pixels on the top & left, try the
  // other option!
  // If you are seeing red and green color inversion, use Black Tab

  // If your TFT's plastic wrap has a Black Tab, use the following:
  //tft.initR(INITR_BLACKTAB);   // initialize a ST7735S chip, black tab
  // If your TFT's plastic wrap has a Red Tab, use the following:
  tft.initR(INITR_REDTAB);   // initialize a ST7735R chip, red tab
  // If your TFT's plastic wrap has a Green Tab, use the following:
  //tft.initR(INITR_GREENTAB); // initialize a ST7735R chip, green tab

  Serial.print("Initializing SD card...");

  if (!sd.begin(sdcs, SPI_HALF_SPEED)) sd.initErrorHalt();
  Serial.println("OK!");

  bmpDraw("parrot.bmp", 0, 0);
}

void loop() {
  if(Serial.available() == 0)return;
  Serial.read();
}

// 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.

// Size of the buffer in PIXELS
// actual size of buffer in bytes is 3*BUFFPIXEL
// was originally 20 but BUGFIX (see buffidx below) 
// now allows buffer size to be greater than 255 chars
// (i.e. BUFFPIXEL can now be > 85)
#define BUFFPIXEL 128

void bmpDraw(char *filename, uint8_t x, uint8_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)
//>>>> BUG. buffidx must be 16-bits (not uint8_t) to allow for larger buffers
  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();

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

  tft.fillScreen(0);
  
  Serial.println();
  Serial.print("Loading image '");
  Serial.print(filename);
  Serial.println('\'');

  // Open the input file
  if(!bmpFile.open(filename, O_READ)) {
    Serial.print("Failed to open input file ");
    Serial.println(filename);
    return;
  }

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

        goodBmp = true; // Supported BMP format -- proceed!
        Serial.print("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;

        // Set TFT address window to clipped image bounds
        tft.setAddrWindow(x, y, x+w-1, y+h-1);
        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.curPosition() != pos) { // Need seek?
            bmpFile.seekSet(pos);
            buffidx = sizeof(sdbuffer); // Force buffer reload
          }

          for (col=0; col<w; col++) { // For each pixel...
            // Time to read more pixel data?
            if (buffidx >= sizeof(sdbuffer)) { // Indeed
// fails without this delay
//delayMicroseconds(1);
              if(bmpFile.read(sdbuffer, sizeof(sdbuffer)) != sizeof(sdbuffer))
                Serial.println("Read failed");
              buffidx = 0; // Set index to beginning
            }
            // Convert pixel from BMP to TFT format, push to display
            b = sdbuffer[buffidx++];
            g = sdbuffer[buffidx++];
            r = sdbuffer[buffidx++];
            tft.pushColor(tft.Color565(r,g,b));
          } // end pixel
        } // end scanline
        Serial.print("Loaded in ");
        Serial.print(millis() - startTime);
        Serial.println(" ms");
      } // end goodBmp
    }
  }
  bmpFile.close();
  if(!goodBmp) Serial.println("BMP format not recognized.");
}

// These functions 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(SdFile f) {
  uint16_t result;
  char *p;

  p = (char *)&result;
  bmpFile.read(p++,1); // LSB
  bmpFile.read(p++,1);
  return result;
}

uint32_t read32(SdFile f) {
  uint32_t result;
  char *p;

  p = (char *)&result;
  bmpFile.read(p++,1); // LSB
  bmpFile.read(p++,1);
  bmpFile.read(p++,1);
  bmpFile.read(p++,1); // MSB
  return result;
}
 
Status
Not open for further replies.
Back
Top