Arduino_STM32/STM32F1/libraries/ILI9341_due_STM/examples/sdFatTftBitmap/sdFatTftBitmap.ino

320 lines
9.6 KiB
C++

/*
This sketch is demonstrating loading images from an SD card.
To run this sketch, copy images from the images folder into the root of an SD card
(either those with .bmp extension or those with .565 extension (those will load faster)).
Modify the extension of the files in loop() depending on which files you copied onto SD.
Depending on the length of the cables between Arduino and SD card pins you might want
to change SD_SPI_SPEED to either SPI_FULL_SPEED (if you have a shield directly connected to
Arduino pins) or SPI_QUARTER_SPEED if you have long cables. SPI_HALF_SPEED was the maximum
I could use as I had the display with SD slot on a breadboard.
Obviously you have to modify CS, DC pins for TFT and SD appropriately.
You can generate your own .565 images from 24bit BMPs by using BMP24toILI565 converter which
you can find in the Tools folder on GitHub:
https://github.com/marekburiak/ILI9341_due/tree/master/tools
*/
#include <SPI.h>
#include <SdFat.h>
#include <SdFatUtil.h>
#include <ILI_SdSpi.h>
#include <ILI_SdFatConfig.h>
#include <ILI9341_due_gText.h>
#include <ILI9341_due.h>
// CS and DC for the LCD
#define LCD_CS 10 // Chip Select for LCD
#define LCD_DC 9 // Command/Data for LCD
#define SD_CS 7 // Chip Select for SD card
#define BUFFPIXELCOUNT 320 // size of the buffer in pixels
#define SD_SPI_SPEED SPI_HALF_SPEED // SD card SPI speed, try SPI_FULL_SPEED
SdFat sd; // set filesystem
SdFile bmpFile; // set filesystem
//ArduinoOutStream cout(Serial);
ILI9341_due tft(LCD_CS, LCD_DC);
// store error strings in flash to save RAM
#define error(s) sd.errorHalt_P(PSTR(s))
void setup()
{
Serial.begin(9600);
tft.begin();
tft.setRotation(iliRotation270); // landscape
progmemPrint(PSTR("Initializing SD card..."));
if (!sd.begin(SD_CS, SD_SPI_SPEED)){
progmemPrintln(PSTR("failed!"));
return;
}
progmemPrintln(PSTR("OK!"));
}
void loop()
{
if (tft.getRotation() == iliRotation90 || tft.getRotation() == iliRotation270){
bmpDraw("giraffe.565", 0, 0);
delay(2000);
bmpDraw("SOLDHO~1.565", 0, 0);
delay(2000);
bmpDraw("GLOOMY~1.565", 0, 0);
delay(2000);
bmpDraw("MOTIVA~1.565", 0, 0);
delay(2000);
}
else
{
bmpDraw("smokeP.565", 0, 0);
delay(2000);
bmpDraw("origP.565", 0, 0);
delay(2000);
bmpDraw("radioP.565", 0, 0);
delay(2000);
bmpDraw("stopP.565", 0, 0);
delay(2000);
}
}
// 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 RAM but
// makes loading a little faster.
void bmpDraw(char* filename, int x, int y) {
SdFile bmpFile;
int bmpWidth, bmpHeight; // W+H in pixels
uint8_t bmpDepth; // Bit depth (currently must be 24)
uint8_t headerSize;
uint32_t bmpImageoffset; // Start of image data in file
uint32_t rowSize; // Not always = bmpWidth; may have padding
uint32_t fileSize;
boolean goodBmp = false; // Set to true on valid header parse
boolean flip = true; // BMP is stored bottom-to-top
uint16_t w, h, row, col;
uint8_t r, g, b;
uint32_t pos = 0, startTime;
if ((x >= tft.width()) || (y >= tft.height())) return;
progmemPrint(PSTR("Loading image '"));
Serial.print(filename);
Serial.println('\'');
startTime = millis();
// Open requested file on SD card
if (!bmpFile.open(filename, O_READ)) {
Serial.println("File open failed.");
return;
}
else {
//Serial.println("File opened.");
}
// Parse BMP header
if (read16(bmpFile) == 0x4D42) { // BMP signature
fileSize = read32(bmpFile);
//progmemPrint(PSTR("File size: ")); Serial.println(fileSize);
(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
headerSize = read32(bmpFile);
//progmemPrint(PSTR("Header size: ")); Serial.println(headerSize);
bmpWidth = read32(bmpFile);
bmpHeight = read32(bmpFile);
if (read16(bmpFile) == 1) { // # planes -- must be '1'
bmpDepth = read16(bmpFile); // bits per pixel
//progmemPrint(PSTR("Bit Depth: ")); Serial.println(bmpDepth);
if (read32(bmpFile) == 0) // 0 = uncompressed
{
//progmemPrint(PSTR("Image size: "));
//Serial.print(bmpWidth);
//Serial.print('x');
//Serial.println(bmpHeight);
// 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);
if (bmpDepth == 16) //565 format
{
goodBmp = true; // Supported BMP format -- proceed!
uint8_t buffer[2 * BUFFPIXELCOUNT]; // pixel buffer (contains already formatted data for ILI9341 display)
bmpFile.seekSet(54); //skip header
uint32_t totalPixels = bmpWidth*bmpHeight;
uint16_t numFullBufferRuns = totalPixels / BUFFPIXELCOUNT;
for (uint32_t p = 0; p < numFullBufferRuns; p++) {
// read pixels into the buffer
bmpFile.read(buffer, 2 * BUFFPIXELCOUNT);
// push them to the diplay
tft.pushColors565(buffer, 0, 2 * BUFFPIXELCOUNT);
}
// render any remaining pixels that did not fully fit the buffer
uint32_t remainingPixels = totalPixels % BUFFPIXELCOUNT;
if (remainingPixels > 0)
{
bmpFile.read(buffer, 2 * remainingPixels);
tft.pushColors565(buffer, 0, 2 * remainingPixels);
}
}
else if (bmpDepth == 24) // standard 24bit bmp
{
goodBmp = true; // Supported BMP format -- proceed!
uint16_t bufferSize = min(w, BUFFPIXELCOUNT);
uint8_t sdbuffer[3 * bufferSize]; // pixel in buffer (R+G+B per pixel)
uint16_t lcdbuffer[bufferSize]; // pixel out buffer (16-bit per pixel)
// BMP rows are padded (if needed) to 4-byte boundary
rowSize = (bmpWidth * 3 + 3) & ~3;
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);
}
for (col = 0; col < w; col += bufferSize)
{
// read pixels into the buffer
bmpFile.read(sdbuffer, 3 * bufferSize);
// convert color
for (int p = 0; p < bufferSize; p++)
{
b = sdbuffer[3 * p];
g = sdbuffer[3 * p + 1];
r = sdbuffer[3 * p + 2];
lcdbuffer[p] = tft.color565(r, g, b);
}
// push buffer to TFT
tft.pushColors(lcdbuffer, 0, bufferSize);
}
// render any remaining pixels that did not fully fit the buffer
uint16_t remainingPixels = w % bufferSize;
if (remainingPixels > 0)
{
bmpFile.read(sdbuffer, 3 * remainingPixels);
for (int p = 0; p < remainingPixels; p++)
{
b = sdbuffer[3 * p];
g = sdbuffer[3 * p + 1];
r = sdbuffer[3 * p + 2];
lcdbuffer[p] = tft.color565(r, g, b);
}
tft.pushColors(lcdbuffer, 0, remainingPixels);
}
}
}
else
{
progmemPrint(PSTR("Unsupported Bit Depth."));
}
if (goodBmp)
{
progmemPrint(PSTR("Loaded in "));
Serial.print(millis() - startTime);
Serial.println(" ms");
}
}
}
}
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(SdFile& f) {
uint16_t result;
((uint8_t *)&result)[0] = f.read(); // LSB
((uint8_t *)&result)[1] = f.read(); // MSB
return result;
}
uint32_t read32(SdFile& 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();
}
//void PrintHex8(uint8_t *data, uint8_t length) // prints 8-bit data in hex
//{
// char tmp[length*5+1];
// byte first;
// byte second;
// for (int i=0; i<length; i++) {
// first = (data[i] >> 4) & 0x0f;
// second = data[i] & 0x0f;
// // base for converting single digit numbers to ASCII is 48
// // base for 10-16 to become upper-case characters A-F is 55
// // note: difference is 7
// tmp[i*5] = 48; // add leading 0
// tmp[i*5+1] = 120; // add leading x
// tmp[i*5+2] = first+48;
// tmp[i*5+3] = second+48;
// tmp[i*5+4] = 32; // add trailing space
// if (first > 9) tmp[i*5+2] += 7;
// if (second > 9) tmp[i*5+3] += 7;
// }
// tmp[length*5] = 0;
// Serial.print(tmp);
//}