How to fix tearing on my 7-inch RGB Display?

Description

I am running a 7-inch RGB Display. 16Bit parallel with DE Signal.

It works well, except that when the display updates, it starts ‘tearing’. (See Photos below)

What MCU/Processor/Board and compiler are you using?

ESP32-S3 Arduino

ESP32 RGB Peripheral

What do you want to achieve?

My display looks fine when its idle:

But when any pixels get updated, it starts tearing like crazy:

I can only make it work by lowering the Display Clock (DCLK) to 14MHZ. But then the whole display flickers, it needs to be at least 24 MHZ.

The tearing seems to happen during esp_lcd_panel_draw_bitmap()

What have you tried so far?

  • 10%, 20%, 30% … buffer
  • full screen buffer
  • One Buffer
  • Two Buffers
  • Buffer location PSRAM or internal
  • full_refresh = 1
  • lv_disp_flush_ready() in callback function esp_lcd_rgb_panel_frame_trans_done_cb_t
  • disp_drv.render_start_cb
  • VSYNC and HSYNC lines instead of DE

I know LVGL provides ‘render_start_cb’. Can I use that to somehow vsync my output?

Code to reproduce


#define EXAMPLE_LCD_PIXEL_CLOCK_HZ (40 * 1000 * 1000)
#define EXAMPLE_PIN_NUM_HSYNC 46
#define EXAMPLE_PIN_NUM_VSYNC 3
#define EXAMPLE_PIN_NUM_DE 46
#define EXAMPLE_PIN_NUM_PCLK 9
#define EXAMPLE_PIN_NUM_DATA0 14   // B0
#define EXAMPLE_PIN_NUM_DATA1 13   // B1
#define EXAMPLE_PIN_NUM_DATA2 12   // B2
#define EXAMPLE_PIN_NUM_DATA3 11   // B3
#define EXAMPLE_PIN_NUM_DATA4 10   // B4
#define EXAMPLE_PIN_NUM_DATA5 39   // G0
#define EXAMPLE_PIN_NUM_DATA6 38   // G1
#define EXAMPLE_PIN_NUM_DATA7 45   // G2
#define EXAMPLE_PIN_NUM_DATA8 48   // G3
#define EXAMPLE_PIN_NUM_DATA9 47   // G4
#define EXAMPLE_PIN_NUM_DATA10 21  // G5
#define EXAMPLE_PIN_NUM_DATA11 1   // R0
#define EXAMPLE_PIN_NUM_DATA12 2   // R1
#define EXAMPLE_PIN_NUM_DATA13 42  // R2
#define EXAMPLE_PIN_NUM_DATA14 41  // R3
#define EXAMPLE_PIN_NUM_DATA15 40  // R4
#define EXAMPLE_PIN_NUM_DISP_EN -1

/*Change to your screen resolution*/
static const uint16_t screenWidth = 800;
static const uint16_t screenHeight = 480;

/* Display flushing */
void my_disp_flush(lv_disp_drv_t *disp_drv, const lv_area_t *area, lv_color_t *color_map) {

  esp_lcd_panel_handle_t panel_handle = (esp_lcd_panel_handle_t)disp_drv->user_data;
  int offsetx1 = area->x1;
  int offsetx2 = area->x2;
  int offsety1 = area->y1;
  int offsety2 = area->y2;

  // pass the draw buffer to the driver
  esp_lcd_panel_draw_bitmap(panel_handle, offsetx1, offsety1, offsetx2 + 1, offsety2 + 1, color_map);

}

void my_touchpad_read(lv_indev_drv_t *indev_driver, lv_indev_data_t *data) {

  tp.read();

  if (tp.isTouched) {
    data->point.x = tp.points[0].x;
    data->point.y = tp.points[0].y;
    data->state = LV_INDEV_STATE_PRESSED;
  } else {
    data->state = LV_INDEV_STATE_RELEASED;
  }
}


static bool flush_ready(esp_lcd_panel_handle_t panel_handle, esp_lcd_rgb_panel_event_data_t *edata, void *user_ctx) {
  lv_disp_drv_t *disp_drv = (lv_disp_drv_t *)user_ctx;
  lv_disp_flush_ready(disp_drv);  

  return false;
}

static void render_start_cb(lv_disp_drv_t *disp_driver) {

}

void monitor_cb(lv_disp_drv_t *disp_drv, uint32_t time, uint32_t px) {
  Serial.println(printf("%d px refreshed in %d ms\n", px, time));
}

void setup() {

  Serial.begin(115200);

  static lv_disp_drv_t disp_drv;
  lv_disp_drv_init(&disp_drv);

  esp_lcd_panel_handle_t panel_handle = NULL;
  esp_lcd_rgb_panel_config_t panel_config = {
    .clk_src = LCD_CLK_SRC_PLL160M,
    .timings = {
      .pclk_hz = EXAMPLE_LCD_PIXEL_CLOCK_HZ,
      .h_res = screenWidth,
      .v_res = screenHeight,
      .hsync_pulse_width = 1,
      .hsync_back_porch = 46,
      .hsync_front_porch = 210,
      .vsync_pulse_width = 1,
      .vsync_back_porch = 23,
      .vsync_front_porch = 100,
      .flags{
        //.hsync_idle_low = 1,
        //.vsync_idle_low = 1,
        .de_idle_high = 0,
        .pclk_active_neg = 1,
        .pclk_idle_high = 0 } },
    .data_width = 16,  // RGB565 in parallel mode, thus 16bit in width
    .psram_trans_align = 64,
    //.hsync_gpio_num = EXAMPLE_PIN_NUM_HSYNC,
    //.vsync_gpio_num = EXAMPLE_PIN_NUM_VSYNC,
    .de_gpio_num = EXAMPLE_PIN_NUM_DE,
    .pclk_gpio_num = EXAMPLE_PIN_NUM_PCLK,
    .data_gpio_nums = {
      EXAMPLE_PIN_NUM_DATA0,
      EXAMPLE_PIN_NUM_DATA1,
      EXAMPLE_PIN_NUM_DATA2,
      EXAMPLE_PIN_NUM_DATA3,
      EXAMPLE_PIN_NUM_DATA4,
      EXAMPLE_PIN_NUM_DATA5,
      EXAMPLE_PIN_NUM_DATA6,
      EXAMPLE_PIN_NUM_DATA7,
      EXAMPLE_PIN_NUM_DATA8,
      EXAMPLE_PIN_NUM_DATA9,
      EXAMPLE_PIN_NUM_DATA10,
      EXAMPLE_PIN_NUM_DATA11,
      EXAMPLE_PIN_NUM_DATA12,
      EXAMPLE_PIN_NUM_DATA13,
      EXAMPLE_PIN_NUM_DATA14,
      EXAMPLE_PIN_NUM_DATA15,
    },
    .disp_gpio_num = EXAMPLE_PIN_NUM_DISP_EN,
    .on_frame_trans_done = flush_ready,
    .user_ctx = &disp_drv,
    .flags{ .disp_active_low = 1, .relax_on_idle = 0, .fb_in_psram = 1 }
  };

  ESP_ERROR_CHECK(esp_lcd_new_rgb_panel(&panel_config, &panel_handle));
  ESP_ERROR_CHECK(esp_lcd_panel_reset(panel_handle));
  ESP_ERROR_CHECK(esp_lcd_panel_init(panel_handle));

  String LVGL_Arduino = "Hello Arduino! ";
  LVGL_Arduino += String('V') + lv_version_major() + "." + lv_version_minor() + "." + lv_version_patch();
  Serial.println(LVGL_Arduino);

  lv_init();

  static lv_disp_draw_buf_t draw_buf;
//static lv_color_t buf1[screenWidth * 80];
//static lv_color_t buf2[screenWidth * 80];
  
  void *buf3 = heap_caps_malloc(screenWidth * 30, MALLOC_CAP_8BIT | MALLOC_CAP_SPIRAM);
  void *buf4 = heap_caps_malloc(screenWidth * 30, MALLOC_CAP_8BIT | MALLOC_CAP_SPIRAM);

  lv_disp_draw_buf_init(&draw_buf, buf3, buf4, screenWidth * 30);

  /*Initialize the display*/
  disp_drv.hor_res = screenWidth;
  disp_drv.ver_res = screenHeight;
  disp_drv.flush_cb = my_disp_flush;
  disp_drv.draw_buf = &draw_buf;
  disp_drv.full_refresh = 0;
  disp_drv.user_data = panel_handle;
  disp_drv.monitor_cb = monitor_cb;
  disp_drv.render_start_cb = render_start_cb;

  lv_disp_t *disp;
  disp = lv_disp_drv_register(&disp_drv);


  tp.begin();
  tp.setRotation(ROTATION_INVERTED);


  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);


  ui_init();

  /*
    static uint32_t user_data = 10;
    lv_timer_t * timer = lv_timer_create(my_timer, 500,  NULL);    
*/
}

unsigned long previousMillis = 0;
const long interval = 5;

void loop() {

  unsigned long currentMillis = millis();

  if (currentMillis - previousMillis >= interval) {
    previousMillis = currentMillis;
    lv_timer_handler(); /* let the GUI do its work */
  }
}

I have same issue when use pixel clock is 40 MHz (same your code) but use clock 8 MHz can fix it but i see lcd refresh blink

1 Like