fbiego / Lumia-ESP32

A demo project for WT32-SC01 module
MIT License
67 stars 10 forks source link

SC01 plus sponsorship #1

Closed modi12jin closed 2 years ago

modi12jin commented 2 years ago
#define LGFX_USE_V1

#include <lvgl.h>
#include "demos/lv_demos.h"
#include <LovyanGFX.hpp>

class LGFX : public lgfx::LGFX_Device
{

   lgfx::Panel_ST7796 _panel_instance; 

   lgfx::Bus_Parallel8 _bus_instance;

   lgfx::Light_PWM _light_instance;

   lgfx::Touch_FT5x06  _touch_instance; 

public:
   LGFX(void)
   {
      {  
         auto cfg = _bus_instance.config();

         cfg.port = 0;  
         cfg.freq_write = 40000000;
         cfg.pin_wr = 47;           // WR を接続しているピン番号
         cfg.pin_rd = -1;           // RD を接続しているピン番号
         cfg.pin_rs = 0;           // RS(D/C)を接続しているピン番号
         cfg.pin_d0 = 9;            // D0を接続しているピン番号
         cfg.pin_d1 = 46;            // D1を接続しているピン番号
         cfg.pin_d2 = 3;            // D2を接続しているピン番号
         cfg.pin_d3 = 8;            // D3を接続しているピン番号
         cfg.pin_d4 = 18;            // D4を接続しているピン番号
         cfg.pin_d5 = 17;            // D5を接続しているピン番号
         cfg.pin_d6 = 16;            // D6を接続しているピン番号
         cfg.pin_d7 = 15;            // D7を接続しているピン番号

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

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

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

         // ※ 以下の設定値はパネル毎に一般的な初期値が設定さ BUSYが接続されているピン番号 (-1 = disable)れていますので、不明な項目はコメントアウトして試してみてください。

         cfg.memory_width = 320;   // ドライバICがサポートしている最大の幅
         cfg.memory_height = 480;  // ドライバICがサポートしている最大の高さ
         cfg.panel_width = 320;    // 実際に表示可能な幅
         cfg.panel_height = 480;   // 実際に表示可能な高さ
         cfg.offset_x = 0;         // パネルのX方向オフセット量
         cfg.offset_y = 0;         // パネルのY方向オフセット量
         cfg.offset_rotation = 1;  //值在旋转方向的偏移0~7(4~7是倒置的)
         cfg.dummy_read_pixel = 8; // 在读取像素之前读取的虚拟位数
         cfg.dummy_read_bits = 1;  // 读取像素以外的数据之前的虚拟读取位数
         cfg.readable = false;      // 如果可以读取数据,则设置为 true
         cfg.invert = true;       // 如果面板的明暗反转,则设置为 true
         cfg.rgb_order = false;    // 如果面板的红色和蓝色被交换,则设置为 true
         cfg.dlen_16bit = false;   // 对于以 16 位单位发送数据长度的面板,设置为 true
         cfg.bus_shared = false;    // 如果总线与 SD 卡共享,则设置为 true(使用 drawJpgFile 等执行总线控制)

         _panel_instance.config(cfg);
      }

    { // バックライト制御の設定を行います。(必要なければ削除)
      auto cfg = _light_instance.config();    // バックライト設定用の構造体を取得します。

      cfg.pin_bl = 45;              // バックライトが接続されているピン番号
      cfg.invert = false;           // バックライトの輝度を反転させる場合 true
      cfg.freq   = 44100;           // バックライトのPWM周波数
      cfg.pwm_channel = 1;          // 使用するPWMのチャンネル番号

      _light_instance.config(cfg);
      _panel_instance.setLight(&_light_instance);  // バックライトをパネルにセットします。
    }

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

      cfg.x_min      = 0;    // タッチスクリーンから得られる最小のX値(生の値)
      cfg.x_max      = 319;  // タッチスクリーンから得られる最大のX値(生の値)
      cfg.y_min      = 0;    // タッチスクリーンから得られる最小のY値(生の値)
      cfg.y_max      = 479;  // タッチスクリーンから得られる最大のY値(生の値)
      cfg.pin_int    = 7;   // INTが接続されているピン番号
      cfg.bus_shared = false; // 如果您使用与屏幕相同的总线,则设置为 true
      cfg.offset_rotation = 0;// 显示和触摸方向不匹配时的调整 设置为 0 到 7 的值

// I2C接続の場合
      cfg.i2c_port = 0;      // 使用するI2Cを選択 (0 or 1)
      cfg.i2c_addr = 0x38;   // I2Cデバイスアドレス番号
      cfg.pin_sda  = 6;     // SDAが接続されているピン番号
      cfg.pin_scl  = 5;     // SCLが接続されているピン番号
      cfg.freq = 400000;     // I2Cクロックを設定

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

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

// 準備したクラスのインスタンスを作成します。
LGFX tft;

/*更改为您的屏幕分辨率*/
static const uint32_t screenWidth = 480;
static const uint32_t screenHeight = 320;

static lv_disp_draw_buf_t draw_buf;
static lv_color_t buf[2][screenWidth * 30];

#if LV_USE_LOG != 0
/* Serial debugging */
void my_print(lv_log_level_t level, const char *file, uint32_t line, const char *fn_name, const char *dsc)
{
   Serial.printf("%s(%s)@%d->%s\r\n", file, fn_name, line, dsc);
   Serial.flush();
}
#endif

/* Display flushing */
void my_disp_flush(lv_disp_drv_t *disp, const lv_area_t *area, lv_color_t *color_p)
{ 
  if(tft.getStartCount()==0)
  {
    tft.endWrite();    
  }

  tft.pushImageDMA( area->x1
                  , area->y1
                  , area->x2 - area->x1 + 1
                  , area->y2 - area->y1 + 1
                  , ( lgfx::swap565_t * )&color_p->full);                  

  lv_disp_flush_ready(disp); /* tell lvgl that flushing is done */
}

/*Read the touchpad*/
void my_touchpad_read(lv_indev_drv_t *indev_driver, lv_indev_data_t *data)
{
   uint16_t touchX, touchY;

   bool touched =tft.getTouch(&touchX, &touchY);

   if(!touched)
   {
      data->state = LV_INDEV_STATE_REL;
   }
   else
   {
      data->state = LV_INDEV_STATE_PR;

      /*Set the coordinates*/
      data->point.x = touchX;
      data->point.y = touchY;
   }
}

void setup()
{
   Serial.begin(115200); /* prepare for possible serial debug */
   Serial.println("Hello Arduino! (V8.2.X)");
   Serial.println("I am LVGL_Arduino");

   tft.init();  
   tft.initDMA();
   tft.startWrite();     

   lv_init(); 

#if LV_USE_LOG != 0
   lv_log_register_print_cb(my_print); /* register print function for debugging */
#endif

   lv_disp_draw_buf_init(&draw_buf, buf[0], buf[1], screenWidth * 30);

   /*Initialize the display*/
   static lv_disp_drv_t disp_drv;
   lv_disp_drv_init(&disp_drv);
   /*Change the following line to your display resolution*/
   disp_drv.hor_res = screenWidth;
   disp_drv.ver_res = screenHeight;
   disp_drv.flush_cb = my_disp_flush;
   disp_drv.draw_buf = &draw_buf;
   lv_disp_drv_register(&disp_drv);

   /*Initialize the (dummy) input device driver*/
   static lv_indev_drv_t indev_drv;
   lv_indev_drv_init(&indev_drv);
   indev_drv.type = LV_INDEV_TYPE_POINTER;
   indev_drv.read_cb = my_touchpad_read;
   lv_indev_drv_register(&indev_drv);

#if 0
   /* Create simple label */
   lv_obj_t *label = lv_label_create( lv_scr_act() );
   lv_label_set_text( label, "Hello Arduino! (V8.2.X)" );
   lv_obj_align( label, LV_ALIGN_CENTER, 0, 0 );
#else
   /* Try an example from the lv_examples Arduino library
      make sure to include it as written above.
   lv_example_btn_1();
   */

   // uncomment one of these demos
   lv_demo_widgets();            // OK
   //lv_demo_benchmark();          // OK
   //lv_demo_keypad_encoder(); 
   // works, but I haven't an encoder
   //lv_demo_music();              // NOK
   //lv_demo_printer();
   //lv_demo_stress();             // seems to be OK
#endif
   Serial.println("Setup done");
}

void loop()
{
   lv_timer_handler(); /* let the GUI do its work */
   delay(5);
}

@fbiego With LovyanGFX you have better framerates

fbiego commented 2 years ago

is this for the plus module? I see you are using parallel bus for the display.

modi12jin commented 2 years ago

is this for the plus module? I see you are using parallel bus for the display.

SC01 plus indeed, I have tried your code in SC01 plus and switched to LovyanGFX, it works great

youtube video:

SC01-plus analog windows mobile phone

fbiego commented 2 years ago

hello @modi12jin I just got the Plus module, but I have trouble programming it with Platformio. Which IDE are you using and which board should be selected?

modi12jin commented 2 years ago

hello @modi12jin I just got the Plus module, but I have trouble programming it with Platformio. Which IDE are you using and which board should be selected?

Arduino IDE 2.0

截图 2022-10-08 18-47-20

截图 2022-10-08 18-47-01

截图 2022-10-08 18-46-39

截图 2022-10-08 18-46-20

modi12jin commented 2 years ago

@fbiego I saw your SC01 PLIUS video on youtube very smooth

SC01 PLUS for testing I2S and SDIO

//https://github.com/schreibfaul1/ESP32-audioI2S
#include "Arduino.h"
#include "WiFiMulti.h"
#include "Audio.h"
#include "SD_MMC.h"
#include "FS.h"

#define SD_CS 41
#define SDMMC_CMD 40
#define SDMMC_CLK 39
#define SDMMC_D0 38

#define I2S_DOUT 37
#define I2S_BCLK 36
#define I2S_LRC 35

Audio audio;

// WiFiMulti wifiMulti;
// String ssid = "MERCURY_2FF2";
// String password = "1234567890";

void setup() {
    Serial.begin(115200);
    pinMode(SD_CS, OUTPUT);      
    digitalWrite(SD_CS, HIGH);    
    SD_MMC.setPins(SDMMC_CLK,SDMMC_CMD,SDMMC_D0);
    SD_MMC.begin("/root",true);

    // WiFi.mode(WIFI_STA);
    // wifiMulti.addAP(ssid.c_str(), password.c_str());
    // wifiMulti.run();
    // if(WiFi.status() != WL_CONNECTED){
    //     WiFi.disconnect(true);
    //     wifiMulti.run();
    // }           

    audio.setPinout(I2S_BCLK, I2S_LRC, I2S_DOUT);
    audio.setVolume(21); // 0...21

    audio.connecttoFS(SD_MMC, "/320k_test.mp3");
    //audio.connecttohost("http://music.163.com/song/media/outer/url?id=1873321491.mp3"); //  128k mp3  
}

void loop()
{
    audio.loop();
}

SC01 PLUS Specification Manual

ZX3D50CE02S-USRC-4832.pdf

Official Online Tool

https://8ms.xyz/appshop

modi12jin commented 2 years ago

@fbiego I believe it would be cool to add video playback inside your code

SC01-plus MiniTV(ZX3D50CE02S Alias SC01-plus)

https://www.instructables.com/Mini-Retro-TV/

https://github.com/moononournation/MiniTV

/***
 * Required libraries:
 * https://github.com/lovyan03/LovyanGFX.git
 * https://github.com/pschatzmann/arduino-libhelix.git
 * https://github.com/bitbank2/JPEGDEC.git
 */
// auto fall back to MP3 if AAC file not available
#define AAC_FILENAME "/44100.aac"
#define MP3_FILENAME "/44100.mp3"
#define MJPEG_FILENAME "/480_12fps.mjpeg"
#define FPS 12
#define MJPEG_BUFFER_SIZE (480 * 270 * 2 / 10)
#define AUDIOASSIGNCORE 1
#define DECODEASSIGNCORE 1
#define DRAWASSIGNCORE 0
#define LGFX_USE_V1

#include <WiFi.h>
#include <FS.h>
#include <SD_MMC.h>
#include <LovyanGFX.hpp>

#define SD_CS 41
#define SDMMC_CMD 40
#define SDMMC_CLK 39
#define SDMMC_D0 38

class LGFX : public lgfx::LGFX_Device
{

   lgfx::Panel_ST7796 _panel_instance; 

   lgfx::Bus_Parallel8 _bus_instance;

   lgfx::Light_PWM _light_instance;

   lgfx::Touch_FT5x06  _touch_instance; 

public:
   LGFX(void)
   {
      {  
         auto cfg = _bus_instance.config();

         cfg.port = 0;  
         cfg.freq_write = 40000000;
         cfg.pin_wr = 47;           // WR を接続しているピン番号
         cfg.pin_rd = -1;           // RD を接続しているピン番号
         cfg.pin_rs = 0;           // RS(D/C)を接続しているピン番号
         cfg.pin_d0 = 9;            // D0を接続しているピン番号
         cfg.pin_d1 = 46;            // D1を接続しているピン番号
         cfg.pin_d2 = 3;            // D2を接続しているピン番号
         cfg.pin_d3 = 8;            // D3を接続しているピン番号
         cfg.pin_d4 = 18;            // D4を接続しているピン番号
         cfg.pin_d5 = 17;            // D5を接続しているピン番号
         cfg.pin_d6 = 16;            // D6を接続しているピン番号
         cfg.pin_d7 = 15;            // D7を接続しているピン番号

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

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

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

         // ※ 以下の設定値はパネル毎に一般的な初期値が設定さ BUSYが接続されているピン番号 (-1 = disable)れていますので、不明な項目はコメントアウトして試してみてください。

         cfg.memory_width = 320;   // ドライバICがサポートしている最大の幅
         cfg.memory_height = 480;  // ドライバICがサポートしている最大の高さ
         cfg.panel_width = 320;    // 実際に表示可能な幅
         cfg.panel_height = 480;   // 実際に表示可能な高さ
         cfg.offset_x = 0;         // パネルのX方向オフセット量
         cfg.offset_y = 0;         // パネルのY方向オフセット量
         cfg.offset_rotation = 1;  //值在旋转方向的偏移0~7(4~7是倒置的)
         cfg.dummy_read_pixel = 8; // 在读取像素之前读取的虚拟位数
         cfg.dummy_read_bits = 1;  // 读取像素以外的数据之前的虚拟读取位数
         cfg.readable = false;      // 如果可以读取数据,则设置为 true
         cfg.invert = true;       // 如果面板的明暗反转,则设置为 true
         cfg.rgb_order = false;    // 如果面板的红色和蓝色被交换,则设置为 true
         cfg.dlen_16bit = false;   // 对于以 16 位单位发送数据长度的面板,设置为 true
         cfg.bus_shared = false;    // 如果总线与 SD 卡共享,则设置为 true(使用 drawJpgFile 等执行总线控制)

         _panel_instance.config(cfg);
      }

    { // バックライト制御の設定を行います。(必要なければ削除)
      auto cfg = _light_instance.config();    // バックライト設定用の構造体を取得します。

      cfg.pin_bl = 45;              // バックライトが接続されているピン番号
      cfg.invert = false;           // バックライトの輝度を反転させる場合 true
      cfg.freq   = 44100;           // バックライトのPWM周波数
      cfg.pwm_channel = 1;          // 使用するPWMのチャンネル番号

      _light_instance.config(cfg);
      _panel_instance.setLight(&_light_instance);  // バックライトをパネルにセットします。
    }

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

      cfg.x_min      = 0;    // タッチスクリーンから得られる最小のX値(生の値)
      cfg.x_max      = 319;  // タッチスクリーンから得られる最大のX値(生の値)
      cfg.y_min      = 0;    // タッチスクリーンから得られる最小のY値(生の値)
      cfg.y_max      = 479;  // タッチスクリーンから得られる最大のY値(生の値)
      cfg.pin_int    = 7;   // INTが接続されているピン番号
      cfg.bus_shared = false; // 如果您使用与屏幕相同的总线,则设置为 true
      cfg.offset_rotation = 0;// 显示和触摸方向不匹配时的调整 设置为 0 到 7 的值

// I2C接続の場合
      cfg.i2c_port = 0;      // 使用するI2Cを選択 (0 or 1)
      cfg.i2c_addr = 0x38;   // I2Cデバイスアドレス番号
      cfg.pin_sda  = 6;     // SDAが接続されているピン番号
      cfg.pin_scl  = 5;     // SCLが接続されているピン番号
      cfg.freq = 400000;     // I2Cクロックを設定

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

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

LGFX tft;

/* variables */
static int next_frame = 0;
static int skipped_frames = 0;
static unsigned long start_ms, curr_ms, next_frame_ms;

/* audio */
#include "esp32_audio_task.h"

/* MJPEG Video */
#include "mjpeg_decode_draw_task.h"

// pixel drawing callback
static int drawMCU(JPEGDRAW *pDraw) {
  if(tft.getStartCount()==0)
  {
    tft.endWrite();    
  }  
  tft.pushImageDMA(pDraw->x, pDraw->y, pDraw->iWidth, pDraw->iHeight,(lgfx::rgb565_t *)pDraw->pPixels);
  return 1;
} /* drawMCU() */

void setup() {
  disableCore0WDT();

  WiFi.mode(WIFI_OFF);
  Serial.begin(115200);

  // Init Display
  tft.init();  
  tft.initDMA();
  tft.startWrite(); 
  tft.fillScreen(TFT_BLACK);

  esp_err_t ret_val = i2s_init(I2S_NUM_0, 44100, -1 /* MCLK */, 36 /* SCLK */, 35 /* LRCK */, 37 /* DOUT */, -1 /* DIN */);
  if (ret_val != ESP_OK) {
    Serial.printf("i2s_init failed: %d\n", ret_val);
  }
  i2s_zero_dma_buffer(I2S_NUM_0);

  pinMode(SD_CS, OUTPUT);
  digitalWrite(SD_CS, HIGH);
  SD_MMC.setPins(SDMMC_CLK, SDMMC_CMD, SDMMC_D0);
  if (!SD_MMC.begin("/root", true))/* 1-bit SD bus mode */
  {
    Serial.println("ERROR: File system mount failed!");
    tft.println("ERROR: File system mount failed!");
  } else {
    bool aac_file_available = false;
    File aFile = SD_MMC.open(AAC_FILENAME);
    if (aFile) {
      aac_file_available = true;
    } else {
      aFile = SD_MMC.open(MP3_FILENAME);
    }

    if (!aFile || aFile.isDirectory()) {
      Serial.println("ERROR: Failed to open " AAC_FILENAME " or " MP3_FILENAME " file for reading");
      tft.println("ERROR: Failed to open " AAC_FILENAME " or " MP3_FILENAME " file for reading");
    } else {
      File vFile = SD_MMC.open(MJPEG_FILENAME);
      if (!vFile || vFile.isDirectory()) {
        Serial.println("ERROR: Failed to open " MJPEG_FILENAME " file for reading");
        tft.println("ERROR: Failed to open " MJPEG_FILENAME " file for reading");
      } else {
        mjpeg_setup(&vFile, MJPEG_BUFFER_SIZE, drawMCU,
                    false /* useBigEndian */, DECODEASSIGNCORE, DRAWASSIGNCORE);
        BaseType_t ret_val;
        if (aac_file_available) {
          ret_val = aac_player_task_start(&aFile, AUDIOASSIGNCORE);
        } else {
          ret_val = mp3_player_task_start(&aFile, AUDIOASSIGNCORE);
        }
        if (ret_val != pdPASS) {
          Serial.printf("Audio player task start failed: %d\n", ret_val);
          tft.printf("Audio player task start failed: %d\n", ret_val);
        }

        start_ms = millis();
        curr_ms = millis();
        next_frame_ms = start_ms + (++next_frame * 1000 / FPS / 2);
        while (vFile.available() && mjpeg_read_frame())  // Read video
        {
          total_read_video_ms += millis() - curr_ms;
          curr_ms = millis();

          if (millis() < next_frame_ms)  // check show frame or skip frame
          {
            // Play video
            mjpeg_draw_frame();
            total_decode_video_ms += millis() - curr_ms;
            curr_ms = millis();
          } else {
            ++skipped_frames;
            //Serial.println("Skip frame");
          }

          while (millis() < next_frame_ms) {
            vTaskDelay(pdMS_TO_TICKS(1));
          }

          curr_ms = millis();
          next_frame_ms = start_ms + (++next_frame * 1000 / FPS);
        }
        Serial.println("AV end");
        vFile.close();
        aFile.close();
      }
    }
  }
}

void loop() {
}
fbiego commented 1 year ago

Great, I will check that out