mirror of
https://github.com/YuzuZensai/MCUFRIEND_kbv.git
synced 2026-01-06 04:32:38 +00:00
add OpenSmart BMP example
This commit is contained in:
273
examples/showBMP_OpenSmart_Uno/showBMP_OpenSmart_Uno.ino
Normal file
273
examples/showBMP_OpenSmart_Uno/showBMP_OpenSmart_Uno.ino
Normal file
@@ -0,0 +1,273 @@
|
|||||||
|
// OPEN-SMART boards share the TFT data bus with SPI on 10, 11, 13
|
||||||
|
// SD.h always configures SPI for each call.
|
||||||
|
// SD.h does not turn SPI off when it has finished the call.
|
||||||
|
// Chan's fatFS (on which SD.h is based) did turn SPI on and off as required.
|
||||||
|
// You can disable this macro on a Mega because SPI is not shared
|
||||||
|
#define RESTORE_SPI_GPIO() SPCR = 0
|
||||||
|
|
||||||
|
// MCUFRIEND UNO shields have microSD on pins 10, 11, 12, 13
|
||||||
|
// The official <SD.h> library only works on the hardware SPI pins
|
||||||
|
// e.g. 11, 12, 13 on a Uno (or STM32 Nucleo)
|
||||||
|
//
|
||||||
|
// copy all your BMP files to the root directory on the microSD with your PC
|
||||||
|
// (or another directory)
|
||||||
|
|
||||||
|
#include <SPI.h> // f.k. for Arduino-1.5.2
|
||||||
|
//#define USE_SDFAT
|
||||||
|
#include <SD.h> // Use the official SD library on hardware pins
|
||||||
|
|
||||||
|
#include <Adafruit_GFX.h> // Hardware-specific library
|
||||||
|
#include <MCUFRIEND_kbv.h>
|
||||||
|
MCUFRIEND_kbv tft;
|
||||||
|
|
||||||
|
#if defined(ESP32)
|
||||||
|
#define SD_CS 5
|
||||||
|
#elif defined(USE_OPENSMART_SHIELD_PINOUT_UNO)
|
||||||
|
#define SD_CS 5
|
||||||
|
#else
|
||||||
|
#define SD_CS 10 // regular Mcufriend Uno Shield
|
||||||
|
#endif
|
||||||
|
#define NAMEMATCH "" // "" matches any name
|
||||||
|
//#define NAMEMATCH "tiger" // *tiger*.bmp
|
||||||
|
#define PALETTEDEPTH 0 // do not support Palette modes
|
||||||
|
//#define PALETTEDEPTH 8 // support 256-colour Palette
|
||||||
|
|
||||||
|
char namebuf[32] = "/"; //BMP files in root directory
|
||||||
|
//char namebuf[32] = "/bitmaps/"; //BMP directory e.g. files in /bitmaps/*.bmp
|
||||||
|
|
||||||
|
File root;
|
||||||
|
int pathlen;
|
||||||
|
|
||||||
|
void setup()
|
||||||
|
{
|
||||||
|
uint16_t ID;
|
||||||
|
Serial.begin(9600);
|
||||||
|
Serial.print("Show BMP files on TFT with ID:0x");
|
||||||
|
ID = tft.readID();
|
||||||
|
Serial.println(ID, HEX);
|
||||||
|
if (ID == 0x0D3D3) ID = 0x9481;
|
||||||
|
tft.begin(ID);
|
||||||
|
tft.fillScreen(0x001F);
|
||||||
|
tft.setTextColor(0xFFFF, 0x0000);
|
||||||
|
bool good = SD.begin(SD_CS);
|
||||||
|
if (!good) {
|
||||||
|
Serial.print(F("cannot start SD"));
|
||||||
|
while (1);
|
||||||
|
}
|
||||||
|
root = SD.open(namebuf);
|
||||||
|
pathlen = strlen(namebuf);
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop()
|
||||||
|
{
|
||||||
|
char *nm = namebuf + pathlen;
|
||||||
|
File f = root.openNextFile();
|
||||||
|
uint8_t ret;
|
||||||
|
uint32_t start;
|
||||||
|
if (f != NULL) {
|
||||||
|
#ifdef USE_SDFAT
|
||||||
|
f.getName(nm, 32 - pathlen);
|
||||||
|
#else
|
||||||
|
strcpy(nm, (char *)f.name());
|
||||||
|
#endif
|
||||||
|
f.close();
|
||||||
|
RESTORE_SPI_GPIO();
|
||||||
|
strlwr(nm);
|
||||||
|
if (strstr(nm, ".bmp") != NULL && strstr(nm, NAMEMATCH) != NULL) {
|
||||||
|
Serial.print(namebuf);
|
||||||
|
Serial.print(F(" - "));
|
||||||
|
tft.fillScreen(0);
|
||||||
|
start = millis();
|
||||||
|
ret = showBMP(namebuf, 5, 5);
|
||||||
|
switch (ret) {
|
||||||
|
case 0:
|
||||||
|
Serial.print(millis() - start);
|
||||||
|
Serial.println(F("ms"));
|
||||||
|
delay(5000);
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
Serial.println(F("bad position"));
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
Serial.println(F("bad BMP ID"));
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
Serial.println(F("wrong number of planes"));
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
Serial.println(F("unsupported BMP format"));
|
||||||
|
break;
|
||||||
|
case 5:
|
||||||
|
Serial.println(F("unsupported palette"));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
Serial.println(F("unknown"));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else root.rewindDirectory();
|
||||||
|
}
|
||||||
|
|
||||||
|
#define BMPIMAGEOFFSET 54
|
||||||
|
|
||||||
|
#define BUFFPIXEL 20
|
||||||
|
|
||||||
|
uint16_t read16(File& f) {
|
||||||
|
uint16_t result; // read little-endian
|
||||||
|
f.read((uint8_t*)&result, sizeof(result));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t read32(File& f) {
|
||||||
|
uint32_t result;
|
||||||
|
f.read((uint8_t*)&result, sizeof(result));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t showBMP(char *nm, int x, int y)
|
||||||
|
{
|
||||||
|
File bmpFile;
|
||||||
|
int bmpWidth, bmpHeight; // W+H in pixels
|
||||||
|
uint8_t bmpDepth; // Bit depth (currently must be 24, 16, 8, 4, 1)
|
||||||
|
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[(1 << PALETTEDEPTH) + BUFFPIXEL], *palette = NULL;
|
||||||
|
uint8_t bitmask, bitshift;
|
||||||
|
boolean flip = true; // BMP is stored bottom-to-top
|
||||||
|
int w, h, row, col, lcdbufsiz = (1 << PALETTEDEPTH) + BUFFPIXEL, buffidx;
|
||||||
|
uint32_t pos; // seek position
|
||||||
|
boolean is565 = false; //
|
||||||
|
|
||||||
|
uint16_t bmpID;
|
||||||
|
uint16_t n; // blocks read
|
||||||
|
uint8_t ret;
|
||||||
|
|
||||||
|
if ((x >= tft.width()) || (y >= tft.height()))
|
||||||
|
return 1; // off screen
|
||||||
|
|
||||||
|
bmpFile = SD.open(nm); // Parse BMP header
|
||||||
|
bmpID = read16(bmpFile); // BMP signature
|
||||||
|
(void) read32(bmpFile); // Read & ignore file size
|
||||||
|
(void) read32(bmpFile); // Read & ignore creator bytes
|
||||||
|
bmpImageoffset = read32(bmpFile); // Start of image data
|
||||||
|
(void) read32(bmpFile); // Read & ignore DIB header size
|
||||||
|
bmpWidth = read32(bmpFile);
|
||||||
|
bmpHeight = read32(bmpFile);
|
||||||
|
n = read16(bmpFile); // # planes -- must be '1'
|
||||||
|
bmpDepth = read16(bmpFile); // bits per pixel
|
||||||
|
pos = read32(bmpFile); // format
|
||||||
|
if (bmpID != 0x4D42) ret = 2; // bad ID
|
||||||
|
else if (n != 1) ret = 3; // too many planes
|
||||||
|
else if (pos != 0 && pos != 3) ret = 4; // format: 0 = uncompressed, 3 = 565
|
||||||
|
else if (bmpDepth < 16 && bmpDepth > PALETTEDEPTH) ret = 5; // palette
|
||||||
|
else {
|
||||||
|
bool first = true;
|
||||||
|
is565 = (pos == 3); // ?already in 16-bit format
|
||||||
|
// BMP rows are padded (if needed) to 4-byte boundary
|
||||||
|
rowSize = (bmpWidth * bmpDepth / 8 + 3) & ~3;
|
||||||
|
if (bmpHeight < 0) { // If negative, image is in top-down order.
|
||||||
|
bmpHeight = -bmpHeight;
|
||||||
|
flip = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
w = bmpWidth;
|
||||||
|
h = bmpHeight;
|
||||||
|
RESTORE_SPI_GPIO();
|
||||||
|
if ((x + w) >= tft.width()) // Crop area to be loaded
|
||||||
|
w = tft.width() - x;
|
||||||
|
if ((y + h) >= tft.height()) //
|
||||||
|
h = tft.height() - y;
|
||||||
|
|
||||||
|
if (bmpDepth <= PALETTEDEPTH) { // these modes have separate palette
|
||||||
|
//bmpFile.seek(BMPIMAGEOFFSET); //palette is always @ 54
|
||||||
|
bmpFile.seek(bmpImageoffset - (4<<bmpDepth)); //54 for regular, diff for colorsimportant
|
||||||
|
bitmask = 0xFF;
|
||||||
|
if (bmpDepth < 8)
|
||||||
|
bitmask >>= bmpDepth;
|
||||||
|
bitshift = 8 - bmpDepth;
|
||||||
|
n = 1 << bmpDepth;
|
||||||
|
lcdbufsiz -= n;
|
||||||
|
palette = lcdbuffer + lcdbufsiz;
|
||||||
|
for (col = 0; col < n; col++) {
|
||||||
|
pos = read32(bmpFile); //map palette to 5-6-5
|
||||||
|
palette[col] = ((pos & 0x0000F8) >> 3) | ((pos & 0x00FC00) >> 5) | ((pos & 0xF80000) >> 8);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RESTORE_SPI_GPIO();
|
||||||
|
// 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).
|
||||||
|
uint8_t r, g, b, *sdptr;
|
||||||
|
int lcdidx, lcdleft;
|
||||||
|
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; ) { //pixels in row
|
||||||
|
lcdleft = w - col;
|
||||||
|
if (lcdleft > lcdbufsiz) lcdleft = lcdbufsiz;
|
||||||
|
for (lcdidx = 0; lcdidx < lcdleft; lcdidx++) { // buffer at a time
|
||||||
|
uint16_t color;
|
||||||
|
// Time to read more pixel data?
|
||||||
|
if (buffidx >= sizeof(sdbuffer)) { // Indeed
|
||||||
|
bmpFile.read(sdbuffer, sizeof(sdbuffer));
|
||||||
|
buffidx = 0; // Set index to beginning
|
||||||
|
r = 0;
|
||||||
|
}
|
||||||
|
switch (bmpDepth) { // Convert pixel from BMP to TFT format
|
||||||
|
case 32:
|
||||||
|
case 24:
|
||||||
|
b = sdbuffer[buffidx++];
|
||||||
|
g = sdbuffer[buffidx++];
|
||||||
|
r = sdbuffer[buffidx++];
|
||||||
|
if (bmpDepth == 32) buffidx++; //ignore ALPHA
|
||||||
|
color = tft.color565(r, g, b);
|
||||||
|
break;
|
||||||
|
case 16:
|
||||||
|
b = sdbuffer[buffidx++];
|
||||||
|
r = sdbuffer[buffidx++];
|
||||||
|
if (is565)
|
||||||
|
color = (r << 8) | (b);
|
||||||
|
else
|
||||||
|
color = (r << 9) | ((b & 0xE0) << 1) | (b & 0x1F);
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
case 4:
|
||||||
|
case 8:
|
||||||
|
if (r == 0)
|
||||||
|
b = sdbuffer[buffidx++], r = 8;
|
||||||
|
color = palette[(b >> bitshift) & bitmask];
|
||||||
|
r -= bmpDepth;
|
||||||
|
b <<= bmpDepth;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
lcdbuffer[lcdidx] = color;
|
||||||
|
|
||||||
|
}
|
||||||
|
RESTORE_SPI_GPIO();
|
||||||
|
tft.pushColors(lcdbuffer, lcdidx, first);
|
||||||
|
first = false;
|
||||||
|
col += lcdidx;
|
||||||
|
} // end cols
|
||||||
|
} // end rows
|
||||||
|
tft.setAddrWindow(0, 0, tft.width() - 1, tft.height() - 1); //restore full screen
|
||||||
|
ret = 0; // good render
|
||||||
|
}
|
||||||
|
bmpFile.close();
|
||||||
|
RESTORE_SPI_GPIO();
|
||||||
|
return (ret);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user