lovyan03 / LovyanGFX

SPI LCD graphics library for ESP32 (ESP-IDF/ArduinoESP32) / ESP8266 (ArduinoESP8266) / SAMD51(Seeed ArduinoSAMD51)
Other
1.02k stars 187 forks source link

How can LovyanGFX support the sdmmc ? #513

Open zhjygit opened 3 months ago

zhjygit commented 3 months ago

Here, i have a esp32s3-groom1-N16R8-lcd4.3,my tf cart is sdmmc type. If I want to read the png maps from sd card and draw the picture on the tft display via the gps location datas. Most of the code on github is about the spiSD card, how can LovyanGFX support the sdmmc? Waiting for you reply. Here is my project:https://github.com/jgauchia/IceNav-v3, this project uses the LovyanGFX and spiSD card. Maybe you can solve the problem.

github-actions[bot] commented 2 months ago

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

tobozo commented 2 months ago

just include FS.h or SD.h before including LovyanGFX (e.g. insert #include <FS.h> at first line of hardware/tft.h) and it will expose LGFX filesystem related functions.

however it's more practical to just stick with Stream related functions e.g. :

auto jpgFile = SD.open("/path/to/my-image.jpg");
tft.drawJpg( &jpgFile );
daverathbone commented 2 months ago

This works for Arduino ESP32 , have not tried yet with ESP32 IDF >5.00 and written the function changes:- Also not tested with ESP S3 or ESP C3 yet....

// updated include order as per Tobozo comment below
#define LGFX_USE_V1
#include <FS.h>
#include <SD.h>
#include <SPI.h>
#include <LovyanGFX.hpp>

There is no SDcard SPI CS in LovyanGFX LCD SPI setup So native file handling has to be set up with includes FS/SD as normal for SD card (See SDTest in exmples for ESP32) SD card is SPI and if your LCD is an SPI type you will be sharing MISIO,MOSI and CLK so cfg.bus_shared = true; !!!

I used CS as 5 for LCD and CS as 4 for SD card Here is my SD card pin out (Arduino default VSPI) 3V3 to 3V3 pin CS 4 (GPIO4) -- NOT Default for Arduino SD card MOSI 23 (GPIO23) --Default for Arduino SD card CLK 18 (GPIO18)- -Default for Arduino SD card MISO 19 (GPIO19)--Default for Arduino SD card GND to GND pin

* Place your LGFX setup class next *** Use Examples Howtouse/usersetting as a guide translate Japanese with google translate

I have ILI9488 (SPI) LCD , with Touch IC FT6336 (I2C) and LED Backlight that can use PWM

// NOT SHOWN IN FULL 
class LGFX : public lgfx::LGFX_Device
{
     lgfx::Panel_ILI9488     _panel_instance;
     lgfx::Bus_SPI           _bus_instance;   // SPI bus instance
     lgfx::Light_PWM         _light_instance;
     lgfx::Touch_FT5x06       _touch_instance; // FT5206, FT5306, FT5406, FT6206, FT6236, FT6336, FT6436
//..................

// Configure bus control settings........... 
     cfg.spi_host = VSPI_HOST; // Select the SPI to use VSPI_HOST 
// ................
     cfg.pin_sclk =  18; // Set SPI SCLK pin number
     cfg.pin_mosi = 23; // Set the SPI MOSI pin number
     cfg.pin_miso = 19; // Set SPI MISO pin number (-1 = disable)
     cfg.pin_dc =     22; // Set SPI D/C pin number (-1 = disable)
//.............
// Set display panel control......................
     cfg.pin_cs = 5; // Pin number to which CS is connected (-1 = disable)
     cfg.pin_rst = 3 ; // Pin number to which RST is connected (-1 = disable)  (I use EN pin)
//.............
// IPS LCD's may need cfg.invert = true;
// remember !! cfg.bus_shared = true;
//.............

// Set backlight control ......................
     cfg.pin_bl = 21;  // Pin number to which the backlight is connected
//Configure touch screen control settings. ...................
//...................
     cfg.pin_int    = 25;  //Pin number to which Touch  INT is connected
// ..................
// For I2C connection
     cfg.i2c_port = 1; // Select I2C to use (0 or 1)
     cfg.i2c_addr = 0x38; // I2C device address number
     cfg.pin_sda = 33; // Pin number to which SDA is connected
     cfg.pin_scl = 32; // Pin number to which SCL is connected
     cfg.freq = 400000; // Set I2C clock
  _touch_instance.config(cfg);
      _panel_instance.setTouch(&_touch_instance);  // タッチスクリーンをパネルにセットします。
    }
     setPanel(&_panel_instance); // Set the panel to use.
   }
};

LGFX lcd;
/*
Example to show how to place a PNG picture on screen at any  X,,Y  or scale etc.
 Note this streams the file from the SD card and fixes the SD card CS issues I had.
 See call in loop... Note:  'Auto' is C++ in Aurdino main 
 Take care if  calling from C  maybe within a ESP IDF C based code source
*/
void drawPng(
  fs::FS &fs,
  const char *filename,
  int32_t x=0,
  int32_t y=0,
  int32_t maxW =-1,
  int32_t maxH = -1,
  int32_t offX =0,
  int32_t offY = 0,
  float scale_x =1.0F ,
  float scale_y =1.0f,
  textdatum_t datum = TL_DATUM
  ){
  auto pngFile = fs.open(filename);
  if(!pngFile){
     Serial.println("Failed to open the image file.");
  } else {
    lcd.drawPng( &pngFile,x,y,maxW,maxH,offX,offY,datum);
    pngFile.close();
  }
}

// Example function to show fies on sdcard to LCD display 
void listDir(fs::FS &fs, const char * dirname, uint8_t levels){
    Serial.printf("Listing directory: %s\n", dirname);

    File root = fs.open(dirname);
    if(!root){
        Serial.println("Failed to open directory");
        return;
    }
    if(!root.isDirectory()){
        Serial.println("Not a directory");
        return;
    }

    File file = root.openNextFile();
    lcd.setCursor(0, 0);
    lcd.setTextColor(TFT_WHITE);    
    lcd.setFont( &fonts::Font0   );
    while(file){

        if(file.isDirectory()){
            Serial.print("  DIR : ");
            Serial.println(file.name());
            lcd.print(("  DIR : "));
            lcd.println(file.name());
            if(levels){
                listDir(fs, file.path(), levels -1);
            }
        } else {
            Serial.print("  FILE: ");
            Serial.print(file.name());
            Serial.print("  SIZE: ");
            Serial.println(file.size());

            lcd.print("  FILE: ");
            lcd.print(file.name());
            lcd.print("  SIZE: ");
            lcd.println(file.size());

        }
        file = root.openNextFile();
    }
}

//Example Setup for SDcard, LCD and touch
void setup(void)
{
  Serial.begin(115200);
    if(!SD.begin(4)){  // Note call it with 4 if thats your SD card CS
        Serial.println("Card Mount Failed");
        return;
    }
    uint8_t cardType = SD.cardType();

    if(cardType == CARD_NONE){
        Serial.println("No SD card attached");
        return;
    }

    Serial.print("SD Card Type: ");
    if(cardType == CARD_MMC){
        Serial.println("MMC");
    } else if(cardType == CARD_SD){
        Serial.println("SDSC");
    } else if(cardType == CARD_SDHC){
        Serial.println("SDHC");
    } else {
        Serial.println("UNKNOWN");
    }

    uint64_t cardSize = SD.cardSize() / (1024 * 1024);
    Serial.printf("SD Card Size: %lluMB\n", cardSize);
    lcd.init();
    lcd.begin();        
    lcd.setRotation(3);  // My LCD is 420x320 ribbon to the left 
    lcd.setBrightness(255); // Change to lower LED backlight 
    lcd.clear(TFT_BLACK);
    lcd.setCursor(0, 0);
    lcd.setTextColor(TFT_WHITE);
    lcd.setTextSize((std::max(lcd.width(), lcd.height()) + 255) >> 8);

 //  Calibrate when touch is available. (Optional)
  if (lcd.touch())
  {
    if (lcd.width() < lcd.height()) lcd.setRotation(lcd.getRotation() ^ 1);

    // Draw the guide text on the screen.
    lcd.setTextDatum(textdatum_t::middle_center);
    lcd.drawString("touch the arrow marker.", lcd.width()>>1, lcd.height() >> 1);
    lcd.setTextDatum(textdatum_t::top_left);

    // Calibrate when using touch. Touch the tips of the arrows displayed at the four corners of the screen in order.
    std::uint16_t fg = TFT_WHITE;
    std::uint16_t bg = TFT_BLACK;
    if (lcd.isEPD()) std::swap(fg, bg);
    lcd.calibrateTouch(nullptr, fg, bg, std::max(lcd.width(), lcd.height()) >> 3);
  }
 // After clear screen show some text and enter loop
  lcd.clear(TFT_BLACK);
  lcd.drawString("Demo here", 0, 0);
  lcd.drawString("Draw on screen", (lcd.width()/2)-80, lcd.height()-20);
}
//Main Arduino loop
void loop(void)
{

  //Example touch loop
  int32_t x, y;
  if (lcd.getTouch(&x, &y)) {
    // make top left hand corner a touch zone
      if(x<20 && y<20){
        lcd.fillScreen(TFT_BLACK);
        drawPng(SD,"/logo.png",0,100);  // Note make sure your SDcard as a png image keep small
        listDir(SD, "/", 0);            // Show the dir with files in root (dont have lots about 6 files)
      }

    lcd.fillRect(x-2, y-2, 2, 2, TFT_WHITE);    // places dot for each touch position
  }
  delay(1); // Note on Arduino loop is FreeRTOS task loop 
  // with no vTaskDelay(pdMS_TO_TICKS(100)); so can fire watchdog time out
  // if loop run without giving some CPU time for tasks
  // I think Arduino delay is made from vTaskDelay so may fix this issue?
}
tobozo commented 2 months ago

ths is wrong:

#define LGFX_USE_V1
#include <LovyanGFX.hpp>
#include "FS.h"
#include "SD.h"
#include "SPI.h

by doing so you just create two different instances of LovyanGFX, one with filesystem support (sketch scope) and one without it (global scope).

This works for Arduino ESP32

and it probably fails with platformio and esp-idf

filesystem related libraries should be included before LovyanGFX to prevent this:

#define LGFX_USE_V1
#include <FS.h>
#include <SD.h>
#include <SPI.h>
#include <LovyanGFX.hpp>
mhaberler commented 2 months ago

@daverathbone if this works for you, please share a link I am particularly interested in working examples using SdFat / exFat working with SPI shared between SD and display

thanks!

daverathbone commented 2 months ago

@daverathbone if this works for you, please share a link I am particularly interested in working examples using SdFat / exFat working with SPI shared between SD and display

thanks!

The format for your SDcard is handled external to the display code suggest you get an example running with your SD card format first , e.g. read file, open file etc. then the streaming or file handling should use the standard C/C++ file handling within LovyanGFX calls.

mhaberler commented 2 months ago

@daverathbone I considered that, but the application requires a 64bit filesystem and DataWrapper is all 32bit

I want to nail down the specific requirements in setup and calling sequence to have both the SD and display on the same bus. Very easy to create to have a display operation kill SD I/O and vice versa.

This repo https://github.com/GOB52/M5Stack_FlipBookSD has a working example of SdFat I/O and M5Unified/M5GFX - I want the same for LovyanGFX.

The whole thing is a bit of black art; for example it took me the above repo to get this to work - it is unclear to me why

        M5.Display.startWrite();
        M5.Display.clear(TFT_RED);
        M5.Display.endWrite();

is ok, but

        display.startWrite();
        display.clear(TFT_RED);
        display.endWrite();

brings down the house.

tobozo commented 2 months ago

@mhaberler

M5Unified can manage more than one display, so aliasing display to M5.Display before the init occured may be the reason why you get a crash when using the alias.

M5Unified.hpp

    M5GFX Display;  // setPrimaryされたディスプレイのインスタンス
    M5GFX &Lcd = Display;

Personnally I find the macro to be more reliable than the global declaration:

#define myDisplay M5.Display
mhaberler commented 2 months ago

oh ok, observed behaviour starts making sense now - will adopt your macro practice

how did you find your way to this result ;-?

edit: tried, house still standing ;)

tobozo commented 2 months ago

how did you find your way to this result ;-?

by pulling my hair with M5Stack-SD-Updater context guessing

mhaberler commented 2 months ago

great, so the M5Unified version works fine with simultaneous display and SD/SdFat use

trying to use non-M5 boards, I massaged that code to use LovyanGFX and I'm back at square 1 - using display disables SD card

is this possible in principle after all, or does the status https://github.com/lovyan03/LovyanGFX/issues/491 apply (i.e. unresolved)?

works fine for target coreS3-pmtiles-m5unified, breaks for coreS3-pmtiles-lovyangfx https://github.com/mhaberler/embedded-protomaps/commits/lovyangfx/

sukesh-ak commented 1 month ago

If display and SD card are on the same SPI Bus, then you need to do this

https://github.com/sukesh-ak/ESP32-TUX/blob/47639648a37ffc9ef9c2a748eeb9761894b9238a/main/devices/conf_WT32SCO1.h#L120

cfg.bus_shared = true;    // Set to true when sharing the bus with sd card (bus control is performed with drawJpgFile, etc.)

And then don't initialize the bus again. Both options are demonstrated in my ESP32-TUX project.

tobozo commented 1 month ago

found this old gist with a very slow buf safe approach to sharing the bus, not sure if it's still working though

github-actions[bot] commented 1 week ago

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

mhaberler commented 1 week ago

not stale