Go Down

Topic: Some trouble with drawBitmap from SD on a ST7735 TFT color 1.8 128x160  (Read 217 times) previous topic - next topic

Quentinemusee

Hello.
If I ask you some help, it is because a whole day of research wasn't enough to solve my problem.
I'm trying to draw a bitmap on my display (reference here) with my teensy 4.0 (same thing). I got an exemple script on the internet with the famous void bmpDraw(char *filename, uint8_t x, uint16_t y), but what my display print isn't what it is supposed to print, and I did not see anywhere on the internet someone with the same problem.
Here is the function :

#define BUFFPIXEL 20

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)
 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();

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

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

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

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

       // Set TFT address window to clipped image bounds
       tft.startWrite();
       tft.setAddrWindow(x, y, w, h);

       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?
           tft.endWrite();
           bmpFile.seek(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
             bmpFile.read(sdbuffer, sizeof(sdbuffer));
             buffidx = 0; // Set index to beginning
             tft.startWrite();
           }

           // 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
       tft.endWrite();
       Serial.print(F("Loaded in "));
       Serial.print(millis() - startTime);
       Serial.println(" ms");
     } // end goodBmp
   }
 }

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

I hope you'll be able to help me, thanks alot.
 Quentin

david_prentice

The function looks fine.   What does your xxxxx.bmp file look like?   You should display it on the PC first.
I presume you are using Adafruit_ST7735 library.

I do not trust your hardware.   The SD pins are not soldered.
I do not trust your link.  It shows a write-only 128x160 display with 11x1 header.   Your "photo" shows a regular 128x160 with 8x1 header

David.

Quentinemusee

Hello David,
my bmp files look normal, they just have no problem.
I'm not lying about my devices, I'm asking myself if I would try to use another pin for the SD_CS than the TFT_CS, I don't know.
That is verry strange, I did more researches on the forum, and no one had the same problem.
Someone had a random pixelisation and tried to use an older version of the st7735.
After reinstalling a cleaner Arduino IDE, here is what I got (attached) insted of a beautiful tiger.
I hope we'll find a solution :)
Thanks again,
Quentin

david_prentice

I used IrfanView to crop your dddddd.jpg into a 128x160 image.
Then saved as dog.jpg and dog.bmp

I copy-pasted your bmpDraw() function to a sketch.   (please use code-windows in future)
But had to add the appropriate glue to make it a buildable program:
Code: [Select]

#include <Adafruit_ST7735.h>
Adafruit_ST7735 tft(10, 9, 8);

#include <SD.h>

void setup(void)
{
    Serial.begin(9600);
    tft.initR(INITR_BLACKTAB);
    SD.begin(4);
}

void loop(void)
{
    bmpDraw("dog.bmp", 0, 0);
}


uint16_t read16(File &f) {
    uint16_t result;
    f.read(&result, sizeof(result));
    return result;
}

uint32_t read32(File &f) {
    uint32_t result;
    f.read(&result, sizeof(result));
    return result;
}

#define BUFFPIXEL 20

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)
    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();

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

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

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

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

                // Set TFT address window to clipped image bounds
                tft.startWrite();
                tft.setAddrWindow(x, y, w, h);

                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?
                        tft.endWrite();
                        bmpFile.seek(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
                            bmpFile.read(sdbuffer, sizeof(sdbuffer));
                            buffidx = 0; // Set index to beginning
                            tft.startWrite();
                        }

                        // 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
                tft.endWrite();
                Serial.print(F("Loaded in "));
                Serial.print(millis() - startTime);
                Serial.println(" ms");
            } // end goodBmp
        }
    }

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


It built and ran just fine.
Code: [Select]

Using library Adafruit_ST7735_and_ST7789_Library at version 1.5.6 in folder: C:\Users\David Prentice\Documents\Arduino\libraries\Adafruit_ST7735_and_ST7789_Library
Using library Adafruit_GFX_Library at version 1.6.1 in folder: C:\Users\David Prentice\Documents\Arduino\libraries\Adafruit_GFX_Library
Using library SPI at version 1.0 in folder: C:\Program Files (x86)\Arduino-1.8.9\hardware\arduino\avr\libraries\SPI
Using library SD at version 1.2.3 in folder: C:\Program Files (x86)\Arduino-1.8.9\libraries\SD
"C:\\Program Files (x86)\\Arduino-1.8.9\\hardware\\tools\\avr/bin/avr-size" -A "C:\\Users\\DAVIDP~1\\AppData\\Local\\Temp\\arduino_build_735705/quentinemusee_BMP.ino.elf"
Sketch uses 20696 bytes (64%) of program storage space. Maximum is 32256 bytes.
Global variables use 992 bytes (48%) of dynamic memory, leaving 1056 bytes for local variables. Maximum is 2048 bytes.


If you ever have a problem with a BMP file(s),  put it in a ZIP and attach the ZIP.

David.

david_prentice

Ah-ha.  I see you are using a Teensy.   PJRC in their wisdom choose to package obsolete Adafruit libraries with the Teensy installation.

This guarantees that you will have problems with properly maintained Adafruit libraries from the Library Manager.

I built and ran on a Uno.   I assume it works fine on my Teensy3.2 (when I remove PJRC's obsolete stuff).  I don't have a Teensy4.0

David.

Quentinemusee

Hello.
Thanks you alot David.
I'll try your code at the end of the day, and I'll give you feedback.
I saw that you used both different pins for the SD_CS and the TFT_CS what I didn't do in my code, could it be the origin on the problem according to you ? I wanted to use the same SPI pin communication, but it may be not correct.
In addition, I'll try to remove PJRC's obsolete stuff, but what are you exactly talking about ? Isn't the Adafruit libraries installed with the Teensy installation supposed to be in my libraries folders ? In this case, do I just need to replace it with the newer version of Adafruit libraries ? You're maybe talkind about the Adafruit_Circuit_Playground, a library I don't know ? :)
Thank you for paying attention to my problem, I'll give you feedback as soon as posible.
  Quentin

david_prentice

PJRC put several libraries into their "installation directory".
This means that they are used in preference to the User libraries.   And you get obsolete versions.

Adafruit libraries should work on all targets.   If PJRC have some new hardware,  they just have to assist Adafruit via GitHub by offering "pull requests".

If the build reports multiple Adafruit libraries it is generally worth using the "up to date" version available via the Library Manager.

David.

Quentinemusee

Oh I see !
I did some researches, and as you said, I found a library folder on Arduino/hardware/teensy/avr/libraries
This may be part of the problem, if Arduino prefer to use the libraries installed there instead of these installed on the casual library folder, then should I delete the library installed on my hardware/teensy ?
I'll do some researches and test on the subject. What did you exactly do with your teensy 3.2 please ? Did you delete the Adafruit Teensy ibraries, or did you replace them ?
Thanks again.
  Quentin

david_prentice

I installed a fresh Teensyduino v1.48 and built the sketch:
Code: [Select]

Multiple libraries were found for "Adafruit_GFX.h"
 Used: C:\Program Files (x86)\Arduino-1.8.9\hardware\teensy\avr\libraries\Adafruit_GFX
 Not used: C:\Users\David Prentice\Documents\Arduino\libraries\Adafruit_GFX_Library
Multiple libraries were found for "SD.h"
 Used: C:\Program Files (x86)\Arduino-1.8.9\hardware\teensy\avr\libraries\SD
 Not used: C:\Program Files (x86)\Arduino-1.8.9\libraries\SD
Using library Adafruit_ST7735_and_ST7789_Library at version 1.5.6 in folder: C:\Users\David Prentice\Documents\Arduino\libraries\Adafruit_ST7735_and_ST7789_Library
Using library Adafruit_GFX at version 1.5.6 in folder: C:\Program Files (x86)\Arduino-1.8.9\hardware\teensy\avr\libraries\Adafruit_GFX
Using library SPI at version 1.0 in folder: C:\Program Files (x86)\Arduino-1.8.9\hardware\teensy\avr\libraries\SPI
Using library SD at version 1.2.2 in folder: C:\Program Files (x86)\Arduino-1.8.9\hardware\teensy\avr\libraries\SD

You can see where multiple libraries occur.   It is absolutely correct to use the hardware-specific Teensy versions of SPI and (possibly) SD
There should not be a Teensy version of Adafruit_GFX
Mercifully,  it is using the User library Adafruit_ST7735

Adafruit update their libraries quite often.   It is BAD NEWS if the Adafruit_GFX library gets out of sync.

David.

Quentinemusee

You were absolutely right, everything is working well now !
Thanks alot, really, I would have never thought about uninstalling the Adafruit libraries from the teensyduino, I didn't even know about this library location ! You saved my school project by giving me some possible answers.
If someone else has the same problem as me, I will give a little summary of everything I use now :
-I'm using the exactly same code as David gave me, so I Had to separate both TFT_CS and SD_CS pins (respectively the pin 10 and the pin 4).
-I'm using the exactly same version of the 4 library as David.
-I uninstall and install again a clean Arduino, and I did not install any Adafruit library during the teensyduino setup.

Thanks again David !
  Quentin

Go Up