TFT SPI + SD bmpFile.read() makes some bug

Hellow!
I started with Arduino and i have big problem with drawing bitmaps. The problem is not with "no work", but with the start to draw using tft.pushcolor(tft.color565(r,g,b));
it starts about the third row and about 20th column, what makes that right part of photo (about 20 px) i on the left side, and lastest row (3 px) tft.pushcolor put on the top of the screen (starts from origin 0,0).

I found that, the problem makes line " bmpFile.read(sdbuffer, sizeof(sdbuffer)); "
If I hide the line " tft.pushColor(tft.color565(r, g, b));" (get to comment), the lcd still draw time to time about 300 px (width of screen is 320px). The first px is black , and the rest are white. See photos in attachments.

Why does the bmpFile.read make any drawing? Can you explain and tell what should i do to draw correct BMP using this (First of all, i had to change #define BUFFPIXEL from 20 on 1. It is impossible to draw with value of 20.) :

#define BUFFPIXEL 1

"void bmpDraw(char *filename, int x, int 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 in buffer (R+G+B per pixel)
uint16_t lcdbuffer[BUFFPIXEL]; // pixel out buffer (16-bit 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();
uint8_t lcdidx = 0;
boolean first = true;

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

Serial.println(); Serial.print("Loading image '"); Serial.print(filename); Serial.println(''');
if ((bmpFile = SD.open(filename)) == NULL) { Serial.print("File not found"); return; }

if(read16(bmpFile) == 0x4D42) { // BMP signature
progmemPrint(PSTR("File size: ")); Serial.println(read32(bmpFile));
(void)read32(bmpFile); // Read & ignore creator bytes
bmpImageoffset = read32(bmpFile); // Start of image data
progmemPrint(PSTR("Image Offset: ")); Serial.println(bmpImageoffset, DEC);
// Read DIB header
progmemPrint(PSTR("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
progmemPrint(PSTR("111111111111111111111Bit Depth: ")); Serial.println(bmpDepth);
if((bmpDepth == 24) && (read32(bmpFile) == 0)) { // 0 = uncompressed

goodBmp = true; // Supported BMP format -- proceed!
progmemPrint(PSTR("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 -1; //do poprawy bez minus 1 jak myślę
if((y+h-1) >= tft.height()) h = tft.height() - y -1;
Serial.print(w); Serial.print("x"); Serial.println(h);

tft.drawCircle(x, y, 1, 255);
tft.drawCircle(w, h, 4, 255); delay(1000);
tft.startWrite();
tft.setAddrWindow(x, y, w, h);

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

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
}

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
}

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

/*if(lcdidx > 0) {
tft.pushColor(tft.color565(r,g,b)); Serial.print(lcdidx);
} */
Serial.println(" Zaladowane w");
Serial.print(millis() - startTime);
Serial.println(" ms");
} // end goodBmp
}
}
tft.endWrite();
bmpFile.close();
if(!goodBmp) progmemPrintln(PSTR("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;
}

// Copy string from flash to serial port
// Source string MUST be inside a PSTR() declaration!
void progmemPrint(const char *str) {
char c;
while(c = pgm_read_byte(str++)) Serial.print(c);
}

// Same as above, with trailing newline
void progmemPrintln(const char *str) {
progmemPrint(str);
Serial.println();}"

We do not know what Arduino, which library, which display, or what .BMP image you are trying to show.

Most libraries provide a .BMP example sketch and some sample .BMP files.
You just have to quote the library example by name (or an internet link )
and the .BMP file by name.

Obviously you try the examples first.

Then you write your custom sketch with your custom pictures.

If you do post your own code, copy-paste into a Code Window. (or attach as a file)

David.

In your function you have an extraneous tft.endWrite(). It should be like this:

...
                tft.startWrite();
                tft.setAddrWindow(x, y, w, h);
                tft.endWrite();
...
    }
//    tft.endWrite();
    bmpFile.close();
...

Oh, if you make BUFFPIXEL 20 it works much faster.

I had to guess some actual code e.g.

#include <SPI.h>
#include <Adafruit_GFX.h>
#include <Adafruit_ILI9341.h>
#include <SD.h>

Adafruit_ILI9341 tft(10, 9, 8);

void setup() {
    Serial.begin(9600);
    tft.begin();
    tft.setRotation(0);
    SD.begin(4);
}

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

David.

david_prentice:
We do not know what Arduino, which library, which display, or what .BMP image you are trying to show.

I am sorry.
Arduino UNO, library in code. Display china 2,4 tft SPI 240x320.

In your function you have an extraneous tft.endWrite(). It should be like this:

Thank you so much. This line was lost in oryginal code, i thought it should be added at the end.

Oh, if you make BUFFPIXEL 20 it works much faster.

No even 20 works normally. Thank You again.

Attached working code for rest of the ppl.
I do not use rst (declarated -1). 8 pin for chipselect.

arduino bmp.txt (12.5 KB)

Seriously. It is far better to post a link to the screen you have bought. It means that we can see photos of the pcb, pins, ...

The Red SPI ILI9341 displays do not have a pullup on the RST pin. So you should always connect to a GPIO pin and use full constructor.
Adafruit displays contain a RST pullup resistor. So their examples will work without a RST argument but only on Adafruit hardware.

You should always let the library handle the Chip Select CS signals. Do not digitalWrite() manually.

David.

It looks like main : https://pl.aliexpress.com/item/32617643223.html

So do you rocommend to connet it to the pin 8 ?
Alredy i use pin from 2-7 and i had to connect CS to the 8th pin. Everything works fine now.
I set up pinmode only once, because i have read it in some pages. Ok i gonna try without it.

You can use whatever GPIO pin you like on the Arduino. digital #8 is fine

const int chipSelect = 8;    //pin to select the SD card
const int SS_pin = 10;

void setup() {
  pinMode(TFT_CS, OUTPUT);
  pinMode(TFT_CS, HIGH);     //de-selects TFT
  pinMode(8, LOW);           // do NOT select the SD manually
  
  Serial.begin(9600);
  while (!Serial) { ;}
  
  Serial.print("\nInitializing SD card...");
  if (!card.init(SPI_HALF_SPEED, chipSelect)) { //this call will select the SD card

David.

Thank you.
I just corrected it
I hope many ppl will benefit it.