espressif / esp-iot-solution

Espressif IoT Library. IoT Device Drivers, Documentations And Solutions.
Apache License 2.0
1.91k stars 762 forks source link

st7701 RGB+SPI display rotation support (AEGHB-721) #380

Closed raoulh closed 2 months ago

raoulh commented 3 months ago

Answers checklist.

General issue report

Hi,

I have a project using an esp32-s3 and a connected display that uses RGB+3 wire SPI. The LCD is a quad 480x480 pixels screen. I'm configuring the display using esp_lcd_st7701, esp_lcd and lvgl_port. It works good and I have a nice LVGL9 UI that runs at 60FPS using direct mode and no tearing.

I configured the display as such:

esp_lcd_rgb_panel_config_t rgb_panel_config =
    {
        .data_width = 16, // RGB565 in parallel mode, thus 16bit in width
        .bits_per_pixel = 16,
        .psram_trans_align = 64,
        .clk_src = LCD_CLK_SRC_PLL160M,
        .disp_gpio_num = GPIO_NUM_NC,
        .pclk_gpio_num = GPIO_LCD_PCLK,
        .vsync_gpio_num = GPIO_LCD_VSYNC,
        .hsync_gpio_num = GPIO_LCD_HSYNC,
        .de_gpio_num = GPIO_LCD_DE,
        .data_gpio_nums =
        {
            GPIO_LCD_B0, GPIO_LCD_B1, GPIO_LCD_B2, GPIO_LCD_B3, GPIO_LCD_B4,         
            GPIO_LCD_G0, GPIO_LCD_G1, GPIO_LCD_G2, GPIO_LCD_G3, GPIO_LCD_G4, GPIO_LCD_G5,
            GPIO_LCD_R0, GPIO_LCD_R1, GPIO_LCD_R2, GPIO_LCD_R3, GPIO_LCD_R4,
        },
        .timings =
        {
            .pclk_hz = 10 * 1000 * 1000,
            .h_res = LCD_WIDTH,
            .v_res = LCD_HEIGHT,
            // The following parameters should refer to LCD spec
            .hsync_back_porch = 44,
            .hsync_front_porch = 50,
            .hsync_pulse_width = 2,
            .vsync_back_porch = 18,
            .vsync_front_porch = 16,
            .vsync_pulse_width = 2,
            .flags.pclk_active_neg = 0, // RGB data is clocked out on falling edge
        },
        .num_fbs = 2,
        .flags.fb_in_psram = 1, // allocate frame buffer in PSRAM
        .bounce_buffer_size_px = LCD_WIDTH * 10,
    };

    st7701_vendor_config_t vendor_config =
    {
        .rgb_config = &rgb_panel_config,
        .init_cmds = square480_lcd, // LCD custom initialization commands
        .init_cmds_size = sizeof(square480_lcd) / sizeof(st7701_lcd_init_cmd_t),
        .flags =
        {
            .auto_del_panel_io = 0,         /**
                                             * Set to 1 if panel IO is no longer needed after LCD initialization.
                                             * If the panel IO pins are sharing other pins of the RGB interface to save GPIOs,
                                             * Please set it to 1 to release the pins.
                                             */
            .mirror_by_cmd = 1,             // Set to 1 if mirror is implemented by LCD command
        },
    };

    const esp_lcd_panel_dev_config_t panel_config =
    {
        .reset_gpio_num = -1,                           // Set to -1 if not use
        .rgb_ele_order = LCD_RGB_ELEMENT_ORDER_RGB,     // Implemented by LCD command `36h`
        .bits_per_pixel = 16,                           // Implemented by LCD command `3Ah` (16/18/24)
        .vendor_config = &vendor_config,
    };

    ESP_ERROR_CHECK(esp_lcd_new_panel_st7701(io_handle, &panel_config, &panel_handle));

    ESP_ERROR_CHECK(esp_lcd_panel_reset(panel_handle));
    ESP_ERROR_CHECK(esp_lcd_panel_init(panel_handle));

    // Init LVGL after display initialization

    lvgl_port_display_cfg_t disp_cfg =
    {
        .io_handle = io_handle,
        .panel_handle = panel_handle,
        .double_buffer = true,
        .hres = LCD_WIDTH,
        .vres = LCD_HEIGHT,
        .monochrome = false,
        .color_format = LV_COLOR_FORMAT_RGB565,
        /* Rotation values must be same as used in esp_lcd for initial settings of the screen */
        .rotation =
        {
            .swap_xy = false,
            .mirror_x = false,
            .mirror_y = false,
        },
        .flags =
        {
            .buff_dma = true,
            .buff_spiram = false,
            .full_refresh = false,
            .direct_mode = true,
            .swap_bytes = false,
        }
    };

    const lvgl_port_display_rgb_cfg_t rgb_cfg =
    {
        .flags =
        {
            .bb_mode = true,
            .avoid_tearing = true,
        }
    };

    const lvgl_port_cfg_t lvgl_cfg = ESP_LVGL_PORT_INIT_CONFIG();
    ESP_RETURN_ON_ERROR(lvgl_port_init(&lvgl_cfg), TAG, "Failed to initialize LVGL port");

I want now to be able to rotate the screen dynamically using:

lv_display_set_rotation(display, rotation);

From my understanding esp_lvgl_port will handle the screen rotation here: https://github.com/espressif/esp-bsp/blob/master/components/esp_lvgl_port/src/lvgl9/esp_lvgl_port_disp.c#L442

by calling esp_lcd_panel_swap_xy and esp_lcd_panel_mirror accordingly to the requested rotation. I have set .mirror_by_cmd = 1 in st7701_vendor_config_t so it means mirroring is handled by the ST7701 chip directly using SPI commands. This works.

The issue I have is that only mirroring is happening but swapping x/y is not done using my actual configuration and thus rotation 90 and 270 are not working as expected. Only rotation to 180 is Ok because there is no swapping involved.

How to make rotation work correctly (and fast). I also tried to use esp_lcd_rgb software rotation by disabling direct_mode and avoid_tearing but it's slow and contains tearing effects. When I rotate to 90 I get 2-3 FPS only. This is not a solution.

Could you tell me what are ma solution to have a good and working rotation solution for the ST7701 with esp_lcd APIs?

Lzw655 commented 3 months ago

Hi @raoulh,

Most RGB LCDs can't rotate the screen by sending a command like SPI or I80 LCDs. The ESP32-S3 can rotate the screen only through software, but this will lower the FPS. Unfortunately, there's no good solution yet for the screen rotation on ESP32-S3.