《Arduino》開發 TFT_eSPI-master 庫 之用 ESP32 讀取SD卡上的圖片並顯示在1.14IPS屏幕上

前幾天解決了 TFT_eSPI-master 庫 圖片取模問題,但儘管是ESP32的 flash 也無法存儲太多圖片的數組,因此我找到了ESP32從SD卡讀取圖片並顯示在LCD屏幕上的方法,SD卡可以輕鬆的存儲大量圖片,後期可以做一個電子相冊,甚至播放個視頻都是可以的。

  • 所用到的Arduino庫:

1.JPEGDecoder

2.TFT_eSPI

  • 硬件連線方式:

1.SD卡(採用SPI方式連接,SD卡內的圖片請改爲240*135像素,這樣在1.14寸屏幕上顯示會更加出色)

#define SD_MISO     13
#define SD_MOSI     15
#define SD_SCLK     17
#define SD_CS       14

2.1.14寸IPS彩屏(ST7789驅動芯片)

#define TFT_MOSI            19
#define TFT_SCLK            18
#define TFT_CS              5
#define TFT_DC              16
#define TFT_RST             23                                                 

#define TFT_BL          -1  //燈光控制引腳不接
  • 程序代碼:
/*
*@功能:ESP32讀取SD卡圖片顯示在1.14IPS屏幕上
*@作者:劉澤文
*@時間:2020/3/27
*/

//引用相關庫
#include <SD.h>
#include <FS.h>
#include <SPI.h>
#include <TFT_eSPI.h>
#include <JPEGDecoder.h>


#define DEBUG 
 
#ifdef DEBUG 
#define DebugPrintln(message) Serial.println(message)
#else 
#define DebugPrintln(message)
#endif
 
#ifdef DEBUG 
#define DebugPrint(message) Serial.print(message) 
#else 
#define DebugPrint(message)
#endif

TFT_eSPI tft = TFT_eSPI(135, 240); // Invoke custom library
SPIClass sdSPI(VSPI);
#define SD_MISO     13
#define SD_MOSI     15
#define SD_SCLK     17
#define SD_CS       14

void drawSdJpeg(const char *filename, int xpos, int ypos);
void jpegRender(int xpos, int ypos);
void jpegInfo();
void showTime(uint32_t msTime);
void SD_read_Time(uint32_t msTime);

void setup()
{
  Serial.begin(9600);
  DebugPrintln();

  tft.init();
  tft.setRotation(1);
  tft.fillScreen(TFT_WHITE);
  tft.setTextSize(1);
  tft.setTextColor(TFT_MAGENTA);
  tft.setCursor(0, 0);
  tft.setTextDatum(MC_DATUM);
  tft.setTextSize(1);
  tft.setSwapBytes(true);
  delay(500);

  if (TFT_BL > 0) { // TFT_BL has been set in the TFT_eSPI library in the User Setup file TTGO_T_Display.h
     pinMode(TFT_BL, OUTPUT); // Set backlight pin to output mode
     digitalWrite(TFT_BL, TFT_BACKLIGHT_ON); // Turn backlight on. TFT_BACKLIGHT_ON has been set in the TFT_eSPI library in the User Setup file TTGO_T_Display.h
   }
  
  //掛載文件系統
  sdSPI.begin(SD_SCLK, SD_MISO, SD_MOSI, SD_CS);
  if (!SD.begin(SD_CS, sdSPI))
  {
    DebugPrintln("存儲卡掛載失敗");
    return;
  }
  uint8_t cardType = SD.cardType();

  if (cardType == CARD_NONE)
  {
    DebugPrintln("未連接存儲卡");
    return;
  }
  else if (cardType == CARD_MMC)
  {
    DebugPrintln("掛載了MMC卡");
  }
  else if (cardType == CARD_SD)
  {
    DebugPrintln("掛載了SDSC卡");
  }
  else if (cardType == CARD_SDHC)
  {
    DebugPrintln("掛載了SDHC卡");
  }
  else
  {
    DebugPrintln("掛載了未知存儲卡");
  }

  //打印存儲卡信息
  Serial.printf("存儲卡總大小是: %lluMB \n", SD.cardSize() / (1024 * 1024)); // "/ (1024 * 1024)"可以換成">> 20"
  Serial.printf("文件系統總大小是: %lluB \n", SD.totalBytes());
  Serial.printf("文件系統已用大小是: %lluB \n", SD.usedBytes());
}

void loop() {

  //測試壁紙
  for(int image_num = 1;image_num<=6;image_num++){
    char FileName[10];
    sprintf(FileName,"/Data/%d.jpg",image_num);
    drawSdJpeg(FileName, 0, 0);     // This draws a jpeg pulled off the SD Card
    delay(500);
  }

  //播放badapple,共6540幀,每秒30幀
  for(int image_num = 1;image_num<=(6540-3);image_num+=2){
    char FileName[10];
    sprintf(FileName,"/apple/%d.jpg",image_num);
    drawSdJpeg(FileName, 0, 0);     // This draws a jpeg pulled off the SD Card
  }

}

void drawSdJpeg(const char *filename, int xpos, int ypos) {
  uint32_t readTime = millis();
  // Open the named file (the Jpeg decoder library will close it)
  File jpegFile = SD.open( filename, FILE_READ);  // or, file handle reference for SD library
 
  if ( !jpegFile ) {
    DebugPrint("ERROR: File \"");
    DebugPrint(filename);
    DebugPrintln ("\" not found!");
    return;
  }

  DebugPrintln("===========================");
  DebugPrint("Drawing file: "); DebugPrintln(filename);
  DebugPrintln("===========================");

  // Use one of the following methods to initialise the decoder:
  boolean decoded = JpegDec.decodeSdFile(jpegFile);  // Pass the SD file handle to the decoder,
  //boolean decoded = JpegDec.decodeSdFile(filename);  // or pass the filename (String or character array)
  SD_read_Time(millis() - readTime);

  if (decoded) {
    // print information about the image to the serial port
    jpegInfo();
    // render the image onto the screen at given coordinates
    jpegRender(xpos, ypos);
  }
  else {
    DebugPrintln("Jpeg file format not supported!");
  }
}

//####################################################################################################
// Draw a JPEG on the TFT, images will be cropped on the right/bottom sides if they do not fit
//####################################################################################################
// This function assumes xpos,ypos is a valid screen coordinate. For convenience images that do not
// fit totally on the screen are cropped to the nearest MCU size and may leave right/bottom borders.
void jpegRender(int xpos, int ypos) {
  // record the current time so we can measure how long it takes to draw an image
  uint32_t drawTime = millis();

  //jpegInfo(); // Print information from the JPEG file (could comment this line out)

  uint16_t *pImg;
  uint16_t mcu_w = JpegDec.MCUWidth;
  uint16_t mcu_h = JpegDec.MCUHeight;
  uint32_t max_x = JpegDec.width;
  uint32_t max_y = JpegDec.height;

  bool swapBytes = tft.getSwapBytes();
  tft.setSwapBytes(true);
  
  // Jpeg images are draw as a set of image block (tiles) called Minimum Coding Units (MCUs)
  // Typically these MCUs are 16x16 pixel blocks
  // Determine the width and height of the right and bottom edge image blocks
  uint32_t min_w = (mcu_w<(max_x % mcu_w)?mcu_w:(max_x % mcu_w));
  uint32_t min_h = (mcu_h<(max_y % mcu_h)?mcu_h:(max_y % mcu_h));

  // save the current image block size
  uint32_t win_w = mcu_w;
  uint32_t win_h = mcu_h;

  // save the coordinate of the right and bottom edges to assist image cropping
  // to the screen size
  max_x += xpos;
  max_y += ypos;

  // Fetch data from the file, decode and display
  while (JpegDec.read()) {    // While there is more data in the file
    pImg = JpegDec.pImage ;   // Decode a MCU (Minimum Coding Unit, typically a 8x8 or 16x16 pixel block)

    // Calculate coordinates of top left corner of current MCU
    int mcu_x = JpegDec.MCUx * mcu_w + xpos;
    int mcu_y = JpegDec.MCUy * mcu_h + ypos;

    // check if the image block size needs to be changed for the right edge
    if (mcu_x + mcu_w <= max_x) win_w = mcu_w;
    else win_w = min_w;

    // check if the image block size needs to be changed for the bottom edge
    if (mcu_y + mcu_h <= max_y) win_h = mcu_h;
    else win_h = min_h;

    // copy pixels into a contiguous block
    if (win_w != mcu_w)
    {
      uint16_t *cImg;
      int p = 0;
      cImg = pImg + win_w;
      for (int h = 1; h < win_h; h++)
      {
        p += mcu_w;
        for (int w = 0; w < win_w; w++)
        {
          *cImg = *(pImg + w + p);
          cImg++;
        }
      }
    }

    // calculate how many pixels must be drawn
    uint32_t mcu_pixels = win_w * win_h;

    // draw image MCU block only if it will fit on the screen
    if (( mcu_x + win_w ) <= tft.width() && ( mcu_y + win_h ) <= tft.height())
      tft.pushImage(mcu_x, mcu_y, win_w, win_h, pImg);
    else if ( (mcu_y + win_h) >= tft.height())
      JpegDec.abort(); // Image has run off bottom of screen so abort decoding
  }

  tft.setSwapBytes(swapBytes);

  showTime(millis() - drawTime); //將圖片顯示到屏幕所用的時間(ms)
}

void jpegInfo() {
  DebugPrintln("JPEG image info");
  DebugPrintln("===============");
  DebugPrint("Width      :");
  DebugPrintln(JpegDec.width);
  DebugPrint("Height     :");
  DebugPrintln(JpegDec.height);
  DebugPrint("Components :");
  DebugPrintln(JpegDec.comps);
  DebugPrint("MCU / row  :");
  DebugPrintln(JpegDec.MCUSPerRow);
  DebugPrint("MCU / col  :");
  DebugPrintln(JpegDec.MCUSPerCol);
  DebugPrint("Scan type  :");
  DebugPrintln(JpegDec.scanType);
  DebugPrint("MCU width  :");
  DebugPrintln(JpegDec.MCUWidth);
  DebugPrint("MCU height :");
  DebugPrintln(JpegDec.MCUHeight);
  DebugPrintln("===============");
  DebugPrintln("");
}

void showTime(uint32_t msTime) {
  DebugPrint(F(" JPEG drawn in "));
  DebugPrint(msTime);
  DebugPrintln(F(" ms "));
}

void SD_read_Time(uint32_t msTime) {
  Serial.print(F(" SD JPEG read in "));
  Serial.print(msTime);
  Serial.println(F(" ms "));
}
  • 程序效果:

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章