sukesh-ak / ESP32-TUX

ESP32-TUX - ESP32 / ESP32-XX Touch UX Template using LVGL to get you started
https://tux.sukesh.me
MIT License
236 stars 55 forks source link

Scrolling performance tuning for all boards #9

Closed modi12jin closed 8 months ago

modi12jin commented 1 year ago
// Display callback to flush the buffer to screen
void display_flush(lv_disp_drv_t *disp, const lv_area_t *area, lv_color_t *color_p)
{
    uint32_t w = (area->x2 - area->x1 + 1);
    uint32_t h = (area->y2 - area->y1 + 1);

    lcd.startWrite();
    lcd.setAddrWindow(area->x1, area->y1, w, h);
    lcd.pushPixels((uint16_t *)&color_p->full, w * h, true);
    lcd.endWrite();

    //lcd.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);
}

@sukesh-ak I saw the code to start DMA in your code, I wonder why you didn't start it?

modi12jin commented 1 year 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);
}

@sukesh-ak This is my code in arduino and it works fine. I don't know if it is suitable for use in IDF

modi12jin commented 1 year ago

lv_conf.h

/*Swap the 2 bytes of RGB565 color. Useful if the display has an 8-bit interface (e.g. SPI)*/
#define LV_COLOR_16_SWAP 1

@sukesh-ak Sorry to bother you, I'm sure you know, but I still want to remind you to modify the code in this file

sukesh-ak commented 1 year ago

@modi12jin Thank you for the reminder. Performance tuning is in my TODO list.

Short story: Initially when I started on this project I had only one board WT32-SC01 and also was running on IDF 4.x.

Since I was waiting for more boards with different controller interface and also waiting for IDF 5.0 release, I put this task for later. Main reason was that with static buffer and with Wi-Fi provisioning, I was hitting either stack overflow or out-of-memory errors. This is taken care of now with CONFIG_SPIRAM_TRY_ALLOCATE_WIFI_LWIP=y setting so no more memory issues. https://github.com/sukesh-ak/ESP32-TUX/blob/c05773004d7ae76f2b29779a975ddf8e0b9e3616/sdkconfig.defaults.esp32#L3

I have 4 boards with different interface. 2 - SPI, 1 - 8Bit and 1 - 16Bit. I did a quick test now and couldn't find any performance difference since FPS still shows 33.

This is my code in arduino and it works fine. I don't know if it is suitable for use in IDF

Arduino is C++ and IDF by default is C. That's the main difference. LovyanGFX in C++ and lvgl is C. I am using C++ for IDF. So, this portion of the code won't change much between them.

I believe this should be using >0 as the condition since it's more like reference counting as I understand.

  if(tft.getStartCount()==0)
  {
    tft.endWrite();    
  }

Will push an update with DMA changes though I don't see any performance changes for now.

What board are you using and what FPS are you getting on it? Will need to spend more time on the performance tuning soon.

modi12jin commented 1 year ago

What board are you using and what FPS are you getting on it?

I use SC01-Plus, using LVGL test frame example can reach more than 100 average frames

If you encounter the problem of insufficient flush, I can tell you that I know SC01-Plus manufacturer, they have changed to 16MB version flush

My youtube channel shows me using SC01-Plus https://www.youtube.com/channel/UC5nQMeJ1WGByzPvumLnR8Yw

sukesh-ak commented 1 year ago

What board are you using and what FPS are you getting on it?

I use SC01-Plus, using LVGL test frame example can reach more than 100 average frames

If you encounter the problem of insufficient flush, I can tell you that I know SC01-Plus manufacturer, they have changed to 16MB version flush

My youtube channel shows me using SC01-Plus https://www.youtube.com/channel/UC5nQMeJ1WGByzPvumLnR8Yw

Thanks for the link. Looks amazing but guess you are using Arduino_GFX for that sample. Have you checked FPS count with LovyanGFX yet?

I am in touch with the manufacturer as well. Thanks for the offer. I was informed that 16MB is getting ready but that was Flash and not PSRAM which is 2MB. Not sure about the status yet.

Will keep this open as a task for performance tuning.

sukesh-ak commented 1 year ago

@modi12jin Here are the performance tuning tips for LovyanGFX https://twitter.com/lovyan03/status/1504298962341498880

modi12jin commented 1 year ago

Have you checked FPS count with LovyanGFX yet?

I only test LovyanGFX (40MHZ, enable DMA, SRAM. Using psram will affect the frame rate, I believe you know the reason, so it is recommended to use OSPI interface PSRAM) to test the frame rate using the example that comes with LVGL, and not using the LovyanGFX example to test the frame rate.

Using Chen Liang's minitv code, and replacing it with LovyanGFX, I can achieve 19FPS, use psram, and use dual-core to handle decoding and screen and audio

I was informed that 16MB is getting ready but that was Flash and not PSRAM which is 2MB.

Since 2MB of PSRAM is a QSPI interface, if 8MB becomes an OSPI interface, it may be necessary to redraw the circuit board.

modi12jin commented 1 year ago

@modi12jin 以下是LovyanGFX的性能调整技巧 https://twitter.com/lovyan03/status/1504298962341498880

grateful

sukesh-ak commented 1 year ago

Have you checked FPS count with LovyanGFX yet?

I only test LovyanGFX (40MHZ, enable DMA, SRAM. Using psram will affect the frame rate, I believe you know the reason, so it is recommended to use OSPI interface PSRAM) to test the frame rate using the example that comes with LVGL, and not using the LovyanGFX example to test the frame rate.

Using Chen Liang's minitv code, and replacing it with LovyanGFX, I can achieve 19FPS, use psram, and use dual-core to handle decoding and screen and audio

I was informed that 16MB is getting ready but that was Flash and not PSRAM which is 2MB.

Since 2MB of PSRAM is a QSPI interface, if 8MB becomes an OSPI interface, it may be necessary to redraw the circuit board.

I switched back to static buffer without the dynamic alloc to PSRAM. But no difference in FPS.

I think the slowness might be due to enabling multi-task support for the display with Semaphore. When I started I didn't know about lv_msg option. But now I use lv_msg so multi-task support is ideally not required. Will spend sometime to debug the lost frame-rate in the Semaphore part of the code.

modi12jin commented 1 year ago

https://docs.espressif.com/projects/espressif-esp-iot-solution/en/latest/display/screen.html

RGB interface The ESP32-S3 chip has an RGB interface that supports parallel RGB565 mode and serial RGB888 mode. Both the RGB interface and the 8080 interface are parallel interfaces. An important difference is that the RGB interface screen generally does not have video memory, which requires updating all pixel data on the screen every refresh cycle. Since there is not enough RAM inside the ESP32-S3 chip for video memory, it is necessary to connect an external PSRAM and place the video memory on PSRAM. Due to the large bandwidth required by the video memory, it is necessary to use 8-line PSRAM in order to achieve a good display effect, and set the PSRAM clock frequency to at least 80MHz.

sukesh-ak commented 1 year ago

https://docs.espressif.com/projects/espressif-esp-iot-solution/en/latest/display/screen.html

RGB interface The ESP32-S3 chip has an RGB interface that supports parallel RGB565 mode and serial RGB888 mode. Both the RGB interface and the 8080 interface are parallel interfaces. An important difference is that the RGB interface screen generally does not have video memory, which requires updating all pixel data on the screen every refresh cycle. Since there is not enough RAM inside the ESP32-S3 chip for video memory, it is necessary to connect an external PSRAM and place the video memory on PSRAM. Due to the large bandwidth required by the video memory, it is necessary to use 8-line PSRAM in order to achieve a good display effect, and set the PSRAM clock frequency to at least 80MHz.

Thanks for the link. One of the reasons for moving to IDF 5.0 was to shift to using BSP like here and publish to components website for all these supported boards.

This will make it easier to add dependency and get it working quickly for new projects. That's the next step for the project. Need more time too.

modi12jin commented 1 year ago

@sukesh-ak Espressif is developing a chip with jpeg hardware decoding, but it is unclear when it will be released

sukesh-ak commented 1 year ago

@sukesh-ak Espressif is developing a chip with jpeg hardware decoding, but it is unclear when it will be released

Hope it comes soon. I also noticed that IDF has a compile option for linux. Not checked it out yet. ELF file is already being created anyway.

modi12jin commented 1 year ago

@sukesh-ak Espressif is developing a chip with jpeg hardware decoding, but it is unclear when it will be released

Hope it comes soon. I also noticed that IDF has a compile option for linux. Not checked it out yet. ELF file is already being created anyway.

It may be prepared for ESP64, I confirmed the existence of this on Twitter and IDF GitHub

modi12jin commented 1 year ago

Have you checked FPS count with LovyanGFX yet?

I only test LovyanGFX (40MHZ, enable DMA, SRAM. Using psram will affect the frame rate, I believe you know the reason, so it is recommended to use OSPI interface PSRAM) to test the frame rate using the example that comes with LVGL, and not using the LovyanGFX example to test the frame rate. Using Chen Liang's minitv code, and replacing it with LovyanGFX, I can achieve 19FPS, use psram, and use dual-core to handle decoding and screen and audio

I was informed that 16MB is getting ready but that was Flash and not PSRAM which is 2MB.

Since 2MB of PSRAM is a QSPI interface, if 8MB becomes an OSPI interface, it may be necessary to redraw the circuit board.

I switched back to static buffer without the dynamic alloc to PSRAM. But no difference in FPS.

I think the slowness might be due to enabling multi-task support for the display with Semaphore. When I started I didn't know about lv_msg option. But now I use lv_msg so multi-task support is ideally not required. Will spend sometime to debug the lost frame-rate in the Semaphore part of the code.

Do you mean that the lower right corner of LVGL is always frame 33?

sukesh-ak commented 1 year ago

@modi12jin Yes, you can enable it in the config

/*1: Show CPU usage and FPS count*/
#define LV_USE_PERF_MONITOR 1
#if LV_USE_PERF_MONITOR
    #define LV_USE_PERF_MONITOR_POS LV_ALIGN_BOTTOM_RIGHT
#endif
modi12jin commented 1 year ago

@modi12jin 是的,您可以在配置中启用它

/*1: Show CPU usage and FPS count*/
#define LV_USE_PERF_MONITOR 1
#if LV_USE_PERF_MONITOR
    #define LV_USE_PERF_MONITOR_POS LV_ALIGN_BOTTOM_RIGHT
#endif

33 This number is calculated independently

It means that only the frame rate in the lower right corner is calculated

LVGL supports partial refresh

sukesh-ak commented 1 year ago

@modi12jin 是的,您可以在配置中启用它

/*1: Show CPU usage and FPS count*/
#define LV_USE_PERF_MONITOR 1
#if LV_USE_PERF_MONITOR
    #define LV_USE_PERF_MONITOR_POS LV_ALIGN_BOTTOM_RIGHT
#endif

33 This number is calculated independently

It means that only the frame rate in the lower right corner is calculated

LVGL supports partial refresh

Interesting. The display works well with my project. Only lag is when scrolling and if background image is enabled. Without it its much smoother.

Need to create simple samples for each board to test the speed in near future.

modi12jin commented 1 year ago

Interesting. The display works well with my project. Only lag is when scrolling and if background image is enabled. Without it its much smoother.

@sukesh-ak I can't give the reason why LVGL will be stuck when starting the image, because I am not an LVGL expert

sukesh-ak commented 1 year ago

What do you think on porting the project to other platforms? Have a discussion topic here. https://github.com/sukesh-ak/ESP32-TUX/discussions/10

modi12jin commented 1 year ago

mmexport1671190732432

Tell him to change LV_DISP_DEF_REFR_PERIOD to 10 or 5 and you will understand

@sukesh-ak Consult Chen Liang, his tips for you

sukesh-ak commented 1 year ago

LV_DISP_DEF_REFR_PERIOD

Thank you for the tip. It was mentioned in the tweet by Lovyan. It changes the FPS since it calculates according to that. But performance wise not much changes visible.

I had it set to 16 earlier and reverted because it showed 60FPS but screen looked with similar perf. I have set to 5 now. Will see if it makes any difference in real experience.

modi12jin commented 1 year ago

LV_DISP_DEF_REFR_PERIOD

谢谢你的提示。洛维扬在推文中提到了这一点。它更改了 FPS,因为它根据该计算。但性能方面没有太大变化。

我之前将其设置为 16 并恢复,因为它显示 60FPS,但屏幕看起来具有相似的性能。我现在设置为 5。将看看它是否在实际体验中有任何不同。

@sukesh-ak Chen Liang has recorded a video explaining it

https://www.bilibili.com/video/BV1bR4y1k7n8

sukesh-ak commented 1 year ago

LV_DISP_DEF_REFR_PERIOD

谢谢你的提示。洛维扬在推文中提到了这一点。它更改了 FPS,因为它根据该计算。但性能方面没有太大变化。 我之前将其设置为 16 并恢复,因为它显示 60FPS,但屏幕看起来具有相似的性能。我现在设置为 5。将看看它是否在实际体验中有任何不同。

@sukesh-ak Chen Liang has recorded a video explaining it

https://www.bilibili.com/video/BV1bR4y1k7n8

Thank you. Yes, when I change to 5, the FPS shows 200 but user experience in scrolling didn't change.

modi12jin commented 1 year ago

LV_DISP_DEF_REFR_PERIOD

谢谢你的提示。洛维扬在推文中提到了这一点。它更改了 FPS,因为它根据该计算。但性能方面没有太大变化。 我之前将其设置为 16 并恢复,因为它显示 60FPS,但屏幕看起来具有相似的性能。我现在设置为 5。将看看它是否在实际体验中有任何不同。

@sukesh-ak Chen Liang has recorded a video explaining it https://www.bilibili.com/video/BV1bR4y1k7n8

Thank you. Yes, when I change to 5, the FPS shows 200 but user experience in scrolling didn't change.

You can increase the size of the buffer to see if there is any improvement

sukesh-ak commented 1 year ago

I will write a smaller sample or use lvgl benchmark sample for comparison. https://github.com/lvgl/lvgl/tree/master/demos/benchmark