Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Display freezes when saving images to SD card #617

Closed
embedded-kiddie opened this issue Sep 13, 2024 Discussed in #616 · 1 comment
Closed

Display freezes when saving images to SD card #617

embedded-kiddie opened this issue Sep 13, 2024 Discussed in #616 · 1 comment

Comments

@embedded-kiddie
Copy link

Hi there,

I started a discussion here and with the help by @tobozo I've narrowed down the problem but it's still not resolved so I decided to open an issue.

Discussed in #616

Originally posted by embedded-kiddie September 12, 2024
First of all, thank you for providing us with such a great library.

I am currently working on building a project that will display the images from a camera on an LCD display and save the images to an SD card when I touch the screen.

Using Adafruit's ST7789 driver and GFX library, as well as the XPT2046 touchscreen library, I have confirmed that basic functions work.

So I changed the graphics library to LovyanGFX (I recently ran benchmarks of the major graphics libraries and I found LovyanGFX is the fastest in every category!), and now I'm facing an issue where the display and touchscreen stops working when accessing the SD card.

I think this issue may be related to this comment by @lovyan03:

Just to be sure, is there an SD card (TF card) connected on the same SPI bus?
If an SD card is present and you have not gone through the procedure to change the SD to SPI mode, the SD card may malfunction and prevent communication with the display.

To simplify this issue, I ran the SavePNG, but it stopped after displaying PNG save test.

PXL_20240912_074254464

And when I tried inserting SPI.begin(...) just before do {...} while (...); as shown below, a white square PNG image was saved (I think this means the save failed), but also the message PNG save success. was not displayed.

  // Initialize SD card interface
  SPI.begin(TFT_SCLK, TFT_MISO, TFT_MOSI, SD_CS);

  do {
    SD.end();
    delay(1000);

    SD.begin(SDCARD_SS_PIN, SDCARD_SPI, SPI_READ_FREQUENCY);

  } while (!saveToSD());

Please help me to solve this issue.
Thanks in advance.


Here is the information about my development environment.

Development environment

My custom LGFX_XIAO_ESP32S3_ST7789.hpp

My custom LGFX_XIAO_ESP32S3_ST7789.hpp

#pragma once

#define TFT_WIDTH  240 // ST7789 240 x 240 and 240 x 320
#define TFT_HEIGHT 320 // ST7789 240 x 320

// Seeed Studio XIAO ESP32-S3
#define TFT_SCLK      D8
#define TFT_MISO      D9
#define TFT_MOSI      D10
#define TFT_CS        D2
#define TFT_RST       D1
#define TFT_DC        D0
#define TOUCH_CS      D3
#define TOUCH_IRQ     D7
#define SD_CS         D6
#define SPI_MODE      SPI_MODE3

#define SPI_FREQUENCY       80000000  // 80 MHz
#define SPI_READ_FREQUENCY  25000000
#define SPI_TOUCH_FREQUENCY   250000 // 250 KHz

#define LGFX_USE_V1

#include <LovyanGFX.hpp>

// ESP32でLovyanGFXを独自設定で利用する場合の設定例

/// 独自の設定を行うクラスを、LGFX_Deviceから派生して作成します。
class LGFX : public lgfx::LGFX_Device
{
// 接続するパネルの型にあったインスタンスを用意します。
lgfx::Panel_ST7789      _panel_instance;


// パネルを接続するバスの種類にあったインスタンスを用意します。
lgfx::Bus_SPI       _bus_instance;   // SPIバスのインスタンス

// タッチスクリーンの型にあったインスタンスを用意します。(必要なければ削除)
lgfx::Touch_XPT2046          _touch_instance;

public:

  // コンストラクタを作成し、ここで各種設定を行います。
  // クラス名を変更した場合はコンストラクタも同じ名前を指定してください。
  LGFX(void)
  {
    { // バス制御の設定を行います。
      auto cfg = _bus_instance.config();      // バス設定用の構造体を取得します。

      // SPIバスの設定
      cfg.spi_host    = SPI3_HOST;            // 使用するSPIを選択
      cfg.spi_mode    = SPI_MODE;             // SPI通信モードを設定 (0 ~ 3)
      cfg.freq_write  = SPI_FREQUENCY;        // 送信時のSPIクロック
      cfg.freq_read   = SPI_READ_FREQUENCY;   // 受信時のSPIクロック
      cfg.spi_3wire   = false;                // 受信をMOSIピンで行う場合はtrueを設定
      cfg.use_lock    = true;                 // トランザクションロックを使用する場合はtrueを設定
      cfg.dma_channel = SPI_DMA_CH_AUTO;      // 使用するDMAチャンネルを設定
      cfg.pin_sclk    = TFT_SCLK;             // SPIのSCLKピン番号を設定
      cfg.pin_mosi    = TFT_MOSI;             // SPIのMOSIピン番号を設定
      cfg.pin_miso    = TFT_MISO;             // SPIのMISOピン番号を設定 (-1 = disable)
      cfg.pin_dc      = TFT_DC;               // SPIのD/Cピン番号を設定  (-1 = disable)

      _bus_instance.config(cfg);              // 設定値をバスに反映します。
      _panel_instance.setBus(&_bus_instance); // バスをパネルにセットします。
    }

    { // 表示パネル制御の設定を行います。
      auto cfg = _panel_instance.config();    // 表示パネル設定用の構造体を取得します。

      cfg.pin_cs           = TFT_CS;          // CSが接続されているピン番号   (-1 = disable)
      cfg.pin_rst          = TFT_RST;         // RSTが接続されているピン番号  (-1 = disable)
      cfg.pin_busy         = -1;              // BUSYが接続されているピン番号 (-1 = disable)

      cfg.panel_width      = TFT_WIDTH;       // 実際に表示可能な幅
      cfg.panel_height     = TFT_HEIGHT;      // 実際に表示可能な高さ
      cfg.offset_x         = 0;               // パネルのX方向オフセット量
      cfg.offset_y         = 0;               // パネルのY方向オフセット量
      cfg.offset_rotation  = 0;               // 回転方向の値のオフセット 0~7 (4~7は上下反転)
      cfg.dummy_read_pixel = 8;               // ピクセル読出し前のダミーリードのビット数
      cfg.dummy_read_bits  = 1;               // ピクセル以外のデータ読出し前のダミーリードのビット数
      cfg.readable         =  true;           // データ読出しが可能な場合 trueに設定
      cfg.invert           = false;           // パネルの明暗が反転してしまう場合 trueに設定
      cfg.rgb_order        = false;           // パネルの赤と青が入れ替わってしまう場合 trueに設定
      cfg.dlen_16bit       = false;           // 16bitパラレルやSPIでデータ長を16bit単位で送信するパネルの場合 trueに設定
      cfg.bus_shared       = true;            // SDカードとバスを共有している場合 trueに設定(drawJpgFile等でバス制御を行います)

      _panel_instance.config(cfg);
    }

    { // タッチスクリーン制御の設定を行います。(必要なければ削除)
      auto cfg = _touch_instance.config();

      cfg.x_min      = 0;                     // タッチスクリーンから得られる最小のX値(生の値)
      cfg.x_max      = TFT_WIDTH-1;           // タッチスクリーンから得られる最大のX値(生の値)
      cfg.y_min      = 0;                     // タッチスクリーンから得られる最小のY値(生の値)
      cfg.y_max      = TFT_HEIGHT-1;          // タッチスクリーンから得られる最大のY値(生の値)
      cfg.pin_int    = TOUCH_IRQ;             // INTが接続されているピン番号
      cfg.bus_shared = true;                  // 画面と共通のバスを使用している場合 trueを設定
      cfg.offset_rotation = 0;                // 表示とタッチの向きのが一致しない場合の調整 0~7の値で設定

// SPI接続の場合
      cfg.spi_host = SPI3_HOST;               // 使用するSPIを選択 (HSPI_HOST or VSPI_HOST)
      cfg.freq = SPI_TOUCH_FREQUENCY;         // SPIクロックを設定
      cfg.pin_sclk = TFT_SCLK;                // SCLKが接続されているピン番号
      cfg.pin_mosi = TFT_MOSI;                // MOSIが接続されているピン番号
      cfg.pin_miso = TFT_MISO;                // MISOが接続されているピン番号
      cfg.pin_cs   = TOUCH_CS;                //   CSが接続されているピン番号

      _touch_instance.config(cfg);
      _panel_instance.setTouch(&_touch_instance);  // タッチスクリーンをパネルにセットします。
    }

    setPanel(&_panel_instance); // 使用するパネルをセットします。
  }
};
Modified SavePNG.ino

Modified SavePNG.ino

#if defined (ARDUINO_WIO_TERMINAL)
  #include <Seeed_FS.h>
  #include <SD/Seeed_SD.h>
#else
  #include <SD.h>
  #include <SPIFFS.h>
#endif

//#include <LovyanGFX.hpp>
#include "LGFX_XIAO_ESP32S3_ST7789.hpp"
static LGFX lcd;

static constexpr char filename[] = "/lovyangfx_test.png";

#ifndef SDCARD_SS_PIN
#define SDCARD_SS_PIN SD_CS
#endif

#ifndef SDCARD_SPI
#define SDCARD_SPI SPI
#endif

bool saveToSD(void)
{
  std::size_t dlen;
  std::uint8_t* png = (std::uint8_t*)lcd.createPng(&dlen, 0, 0, 128, 128);
  if (!png)
  {
    Serial.print("error:createPng\n");
    return false;
  }

  Serial.print("success:createPng\n");

  bool result = false;
  File file = SD.open(filename, "w");
  if (file)
  {
    file.write((std::uint8_t*)png, dlen);
    file.close();
    result = true;
  }
  else
  {
    Serial.print("error:file open failure\n");
  }

  free(png);
  return result;
}

void setup(void)
{
  Serial.begin(115200);

  lcd.init();
  lcd.setColorDepth(24);
  lcd.setColor(TFT_WHITE);

  lcd.startWrite();
  lcd.setAddrWindow(0, 0, lcd.width(), lcd.height());
  for (int y = 0; y < lcd.height(); ++y)
  {
    for (int x = 0; x < lcd.width(); ++x)
    {
      lcd.writeColor( lcd.color888(x << 1, x + y, y << 1), 1);
    }
  }
  lcd.print("PNG save test\n");
  lcd.endWrite();

  // Initialize SD card interface
  SPI.begin(TFT_SCLK, TFT_MISO, TFT_MOSI, SD_CS);

  do {
    SD.end();
    delay(1000);

    SD.begin(SDCARD_SS_PIN, SDCARD_SPI, SPI_READ_FREQUENCY);

  } while (!saveToSD());


  Serial.println("PNG save success.");
  lcd.print("PNG save success.");
}

void loop(void)
{
  lcd.drawPngFile(SD, filename, random(-20,20), random(-20, 20));
}

Wiring of my camera system

LMX90640-XIAO-ESP32-2.4 (Ver.2)

@embedded-kiddie
Copy link
Author

embedded-kiddie commented Sep 22, 2024

I solved this issue.

I had cfg.spi_host set to SPI3_HOST in my configuration file LGFX_XIAO_ESP32S3_ST7789.hpp as follows, because that's what my UNO R4 worked with.

// SPIバスの設定
      cfg.spi_host = SPI3_HOST; //VSPI_HOST;     // 使用するSPIを選択  ESP32-S2,C3 : SPI2_HOST or SPI3_HOST / ESP32 : VSPI_HOST or HSPI_HOST
      // ※ ESP-IDFバージョンアップに伴い、VSPI_HOST , HSPI_HOSTの記述は非推奨になるため、エラーが出る場合は代わりにSPI2_HOST , SPI3_HOSTを使用してください。

But I found SDSPI_DEFAULT_HOST is defined as SPI2_HOST in driver/sdspi_host.h.

#if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32S2
#define SDSPI_DEFAULT_HOST HSPI_HOST
#define SDSPI_DEFAULT_DMA  SDSPI_DEFAULT_HOST
#else
#define SDSPI_DEFAULT_HOST SPI2_HOST
#define SDSPI_DEFAULT_DMA  SPI_DMA_CH_AUTO
#endif

So change SPI3_HOST to SPI2_HOST, or specify SDSPI_DEFAULT_HOST using #include <driver/sdspi_host.h> to accommodate ESP32 variants as well.

#pragma once

#define LGFX_USE_V1

#include <LovyanGFX.hpp>

// https://github.com/espressif/esp-idf/blob/master/components/esp_driver_sdspi/include/driver/sdspi_host.h#L23-L29
#include <driver/sdspi_host.h>

// ESP32でLovyanGFXを独自設定で利用する場合の設定例

...

// SPIバスの設定
      cfg.spi_host = SDSPI_DEFAULT_HOST; // 使用するSPIを選択  ESP32-S2,C3 : SPI2_HOST or SPI3_HOST / ESP32 : VSPI_HOST or HSPI_HOST
      // ※ ESP-IDFバージョンアップに伴い、VSPI_HOST , HSPI_HOSTの記述は非推奨になるため、エラーが出る場合は代わりにSPI2_HOST , SPI3_HOSTを使用してください。

Here is a sample result from my repo.

benchmark_LovyanGFX


By the way, similar issue happened with TFT_eSPI.

In the case of TFT_eSPI, I could not find a way to change the host of the SPI bus, so instead I adjusted the SPI of the SD to TFT_eSPI as follows, which worked.

SD.begin(SDCARD_SS_PIN, tft.getSPIinstance());

I hope this helps someone.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant