From 303bf2b6e056b38b53b8f6a607543db12279e232 Mon Sep 17 00:00:00 2001 From: xuxin Date: Fri, 22 Mar 2024 17:27:27 +0800 Subject: [PATCH 1/2] feat(LVGL port): Support for RGB screen. --- bsp/esp32_s3_lcd_ev_board/Kconfig | 73 -- bsp/esp32_s3_lcd_ev_board/README.md | 9 +- bsp/esp32_s3_lcd_ev_board/idf_component.yml | 5 + .../include/bsp/display.h | 17 - .../include/bsp/esp32_s3_lcd_ev_board.h | 14 +- bsp/esp32_s3_lcd_ev_board/include/bsp/touch.h | 1 - .../priv_include/bsp_lvgl_port.h | 33 +- bsp/esp32_s3_lcd_ev_board/src/bsp_lvgl_port.c | 638 +----------------- bsp/esp32_s3_lcd_ev_board/src/bsp_sub_board.c | 78 +-- .../src/esp32_s3_lcd_ev_board.c | 54 +- .../esp_lvgl_port/include/esp_lvgl_port.h | 2 + .../include/esp_lvgl_port_disp.h | 9 +- .../esp_lvgl_port/src/lvgl8/esp_lvgl_port.c | 16 +- .../src/lvgl8/esp_lvgl_port_disp.c | 124 +++- .../src/lvgl8/esp_lvgl_port_usbhid.c | 8 +- .../esp_lvgl_port/src/lvgl9/esp_lvgl_port.c | 14 +- .../src/lvgl9/esp_lvgl_port_disp.c | 147 +++- 17 files changed, 331 insertions(+), 911 deletions(-) diff --git a/bsp/esp32_s3_lcd_ev_board/Kconfig b/bsp/esp32_s3_lcd_ev_board/Kconfig index e867d4a7..005e4e89 100644 --- a/bsp/esp32_s3_lcd_ev_board/Kconfig +++ b/bsp/esp32_s3_lcd_ev_board/Kconfig @@ -87,28 +87,12 @@ menu "Board Support Package" bool "Auto refresh mode" help Refresh the LCD in the most common way. - config BSP_LCD_RGB_REFRESH_MANUALLY - bool "Manually refresh mode" - help - Refresh the LCD in a specific task instead of automatically refreshing. config BSP_LCD_RGB_BOUNCE_BUFFER_MODE bool "Bounce buffer mode" help Enable bounce buffer mode can achieve higher PCLK frequency at the cost of higher CPU consumption. endchoice - config BSP_LCD_RGB_REFRESH_TASK_PERIOD - int "Minimum Period(ms) of LCD refreshing task" - depends on BSP_LCD_RGB_REFRESH_MANUALLY - default 10 - help - This configuration does not necessarily represent the actual refresh cycle of the RGB interface. - - config BSP_LCD_RGB_REFRESH_TASK_PRIORITY - int "Priority of LCD refreshing task" - depends on BSP_LCD_RGB_REFRESH_MANUALLY - default 2 - config BSP_LCD_RGB_BOUNCE_BUFFER_HEIGHT depends on BSP_LCD_RGB_BOUNCE_BUFFER_MODE int "Bounce buffer height" @@ -118,39 +102,6 @@ menu "Board Support Package" endmenu menu "Display" - config BSP_DISPLAY_LVGL_TASK_PRIORITY - int "LVGL task priority" - default 2 - help - The Board Support Package will create a task that will periodically handle LVGL operation in lv_timer_handler(). - - config BSP_DISPLAY_LVGL_TASK_CORE_ID - int "LVGL task pinned core ID" - default -1 - range -1 1 - help - Core to pin LVGL task. -1 means no pinning. - - config BSP_DISPLAY_LVGL_TASK_DELAY - int "LVGL task minimum delay time (ms)" - default 10 - range 1 100 - help - Minimum delay time for LVGL task. It should be larger if the task watchdog is triggered frequently. - - config BSP_DISPLAY_LVGL_TASK_STACK_SIZE_KB - int "LVGL task stack size (KB)" - default 4 - help - Size(KB) of LVGL task stack. - - config BSP_DISPLAY_LVGL_TICK - int "LVGL tick period" - default 5 - range 1 100 - help - Period of LVGL tick timer. - config BSP_DISPLAY_LVGL_AVOID_TEAR bool "Avoid tearing effect" depends on BSP_LCD_RGB_BUFFER_NUMS > 1 @@ -168,30 +119,6 @@ menu "Board Support Package" bool "Direct mode" endchoice - choice BSP_DISPLAY_LVGL_ROTATION - depends on BSP_DISPLAY_LVGL_AVOID_TEAR - depends on BSP_LCD_RGB_BUFFER_NUMS = 3 - prompt "Select rotation" - default BSP_DISPLAY_LVGL_ROTATION_NONE - config BSP_DISPLAY_LVGL_ROTATION_NONE - bool "Rotation 0" - config BSP_DISPLAY_LVGL_ROTATION_90 - bool "Rotation 90" - config BSP_DISPLAY_LVGL_ROTATION_180 - bool "Rotation 180" - config BSP_DISPLAY_LVGL_ROTATION_270 - bool "Rotation 270" - help - Rotate screen when avoid tearing effect is enabled. Need to set BSP_LCD_RGB_BUFFER_NUMS to 3. - endchoice - - config BSP_DISPLAY_LVGL_ROTATION_DEGREE - int - default 0 if BSP_DISPLAY_LVGL_ROTATION_NONE - default 1 if BSP_DISPLAY_LVGL_ROTATION_90 - default 2 if BSP_DISPLAY_LVGL_ROTATION_180 - default 3 if BSP_DISPLAY_LVGL_ROTATION_270 - choice BSP_DISPLAY_LVGL_BUF_CAPS depends on !BSP_DISPLAY_LVGL_AVOID_TEAR prompt "Select LVGL buffer memory capability" diff --git a/bsp/esp32_s3_lcd_ev_board/README.md b/bsp/esp32_s3_lcd_ev_board/README.md index cce95e23..7b852036 100644 --- a/bsp/esp32_s3_lcd_ev_board/README.md +++ b/bsp/esp32_s3_lcd_ev_board/README.md @@ -29,19 +29,13 @@ Here are some useful configurations in menuconfig that can be customed by user: * `BSP_LCD_RGB_BUFFER_NUMS`: Configure the number of frame buffers. The anti-tearing function can be activated only when set to a value greater than one. * `BSP_LCD_RGB_REFRESH_MODE`: Choose the refresh mode for the RGB LCD. * `BSP_LCD_RGB_REFRESH_AUTO`: Use the most common method to refresh the LCD. - * `BSP_LCD_RGB_REFRESH_MANUALLY`: Refresh the LCD within a dedicated task. This can help manage PSRAM bandwidth. * `BSP_LCD_RGB_BOUNCE_BUFFER_MODE`: Enabling bounce buffer mode can lead to a higher PCLK frequency at the expense of increased CPU consumption. **This mode is particularly useful when dealing with [screen drift](https://docs.espressif.com/projects/esp-faq/en/latest/software-framework/peripherals/lcd.html#why-do-i-get-drift-overall-drift-of-the-display-when-esp32-s3-is-driving-an-rgb-lcd-screen), especially in scenarios involving Wi-Fi usage or writing to Flash memory.** This feature should be used in conjunction with `ESP32S3_DATA_CACHE_LINE_64B` configuration. For more detailed information, refer to the [documentation](https://docs.espressif.com/projects/esp-idf/en/latest/esp32s3/api-reference/peripherals/lcd.html#bounce-buffer-with-single-psram-frame-buffer). * `BSP_DISPLAY_LVGL_BUF_CAPS`: Select the memory type for the LVGL buffer. Internal memory offers better performance. * `BSP_DISPLAY_LVGL_BUF_HEIGHT`: Set the height of the LVGL buffer, with its width aligning with the LCD's width. The default value is 100, decreasing it can lower memory consumption. * `BSP_DISPLAY_LVGL_AVOID_TEAR`: Avoid tearing effect by using multiple buffers. This requires setting `BSP_LCD_RGB_BUFFER_NUMS` to a value greater than 1. * `BSP_DISPLAY_LVGL_MODE`: - * `BSP_DISPLAY_LVGL_FULL_REFRESH`: Use LVGL full-refresh mode. Set `BSP_LCD_RGB_BUFFER_NUMS` to `3` will get higher FPS when enable `BSP_DISPLAY_LVGL_ROTATION_NONE`. + * `BSP_DISPLAY_LVGL_FULL_REFRESH`: Use LVGL full-refresh mode. Set `BSP_LCD_RGB_BUFFER_NUMS` to `3` will get higher FPS`. * `BSP_DISPLAY_LVGL_DIRECT_MODE`: Use LVGL's direct mode. - * `BSP_DISPLAY_LVGL_ROTATION`: Rotate the screen clockwise. This requires setting `BSP_LCD_RGB_BUFFER_NUMS` to `3`. **The rotation is software-based and will substantially reduce FPS if enabled.** - * `BSP_DISPLAY_LVGL_ROTATION_NONE`: No rotation. - * `BSP_DISPLAY_LVGL_ROTATION_90`: 90-degree rotation. - * `BSP_DISPLAY_LVGL_ROTATION_180`: 180-degree rotation. - * `BSP_DISPLAY_LVGL_ROTATION_270`: 270-degree rotation. Based on the above configurations, there are three different anti-tearing modes can be used: @@ -59,6 +53,7 @@ Based on the above configurations, there are three different anti-tearing modes ### Dependencies | component | version | |------------------------------------------------------------------------------------------------------------------------|----------| +| [esp_lvgl_port](https://components.espressif.com/components/esp_lvgl_port) | ^2 | | [espressif/button](https://components.espressif.com/components/espressif/button) |>=2.5,<4.0| | [espressif/esp_codec_dev](https://components.espressif.com/components/espressif/esp_codec_dev) | ^1 | | [espressif/esp_io_expander_tca9554](https://components.espressif.com/components/espressif/esp_io_expander_tca9554) | ^1 | diff --git a/bsp/esp32_s3_lcd_ev_board/idf_component.yml b/bsp/esp32_s3_lcd_ev_board/idf_component.yml index c48dace3..b944345d 100644 --- a/bsp/esp32_s3_lcd_ev_board/idf_component.yml +++ b/bsp/esp32_s3_lcd_ev_board/idf_component.yml @@ -47,5 +47,10 @@ dependencies: version: ">=2.5,<4.0" public: true + esp_lvgl_port: + version: "^2" + override_path: '../../components/esp_lvgl_port' + public: true + examples: - path: ../../examples/display_lvgl_demos diff --git a/bsp/esp32_s3_lcd_ev_board/include/bsp/display.h b/bsp/esp32_s3_lcd_ev_board/include/bsp/display.h index 81b42c85..e70eecc3 100644 --- a/bsp/esp32_s3_lcd_ev_board/include/bsp/display.h +++ b/bsp/esp32_s3_lcd_ev_board/include/bsp/display.h @@ -31,12 +31,6 @@ typedef struct { int max_transfer_sz; /*!< Maximum transfer size, in bytes. */ } bsp_display_config_t; -/** - * @brief LCD transmit done callback function type - * - */ -typedef bool (*bsp_display_trans_done_cb_t)(esp_lcd_panel_handle_t handle); - /** * @brief Create new display panel * @@ -62,17 +56,6 @@ typedef bool (*bsp_display_trans_done_cb_t)(esp_lcd_panel_handle_t handle); */ esp_err_t bsp_display_new(const bsp_display_config_t *config, esp_lcd_panel_handle_t *ret_panel, esp_lcd_panel_io_handle_t *ret_io); -/** - * @brief Register a callback function which will be called when finish refreshing - * - * @param[in] callback The function to be registered. It should return true if need to yield, otherwise return false - * - * @return - * - ESP_OK Success - * - ESP_ERR_INVALID_ARG Callback function should be placed in IRAM, use `IRAM_ATTR` to define it - */ -esp_err_t bsp_display_register_trans_done_callback(bsp_display_trans_done_cb_t callback); - /** * @brief Set display's brightness (Useless, just for compatibility) * diff --git a/bsp/esp32_s3_lcd_ev_board/include/bsp/esp32_s3_lcd_ev_board.h b/bsp/esp32_s3_lcd_ev_board/include/bsp/esp32_s3_lcd_ev_board.h index 624bcbbd..4b522ba5 100644 --- a/bsp/esp32_s3_lcd_ev_board/include/bsp/esp32_s3_lcd_ev_board.h +++ b/bsp/esp32_s3_lcd_ev_board/include/bsp/esp32_s3_lcd_ev_board.h @@ -22,6 +22,7 @@ #include "iot_button.h" #include "lvgl.h" #include "bsp/display.h" +#include "esp_lvgl_port.h" #include "sdkconfig.h" @@ -98,6 +99,7 @@ extern "C" { */ typedef struct { void *dummy; /*!< Prepared for future use. */ + lvgl_port_cfg_t lvgl_port_cfg; } bsp_display_cfg_t; /************************************************************************************************** @@ -309,7 +311,6 @@ esp_err_t bsp_audio_poweramp_enable(bool enable); #define BSP_LCD_V_RES bsp_display_get_v_res() /* LVGL related parameters */ -#define LVGL_TICK_PERIOD_MS (CONFIG_BSP_DISPLAY_LVGL_TICK) #define LVGL_BUFFER_HEIGHT (CONFIG_BSP_DISPLAY_LVGL_BUF_HEIGHT) #if CONFIG_BSP_DISPLAY_LVGL_PSRAM #define LVGL_BUFFER_MALLOC (MALLOC_CAP_SPIRAM) @@ -370,17 +371,6 @@ bool bsp_display_lock(uint32_t timeout_ms); */ void bsp_display_unlock(void); -/** - * @brief Rotate screen - * - * @note Display must be already initialized by calling `bsp_display_start()` - * @note This function can't work with the anti-tearing function. Please use the `BSP_DISPLAY_LVGL_ROTATION` configuration instead. - * - * @param[in] disp: Pointer to LVGL display - * @param[in] rotation: Angle of the display rotation - */ -void bsp_display_rotate(lv_disp_t *disp, lv_disp_rot_t rotation); - /** * @brief Get display horizontal resolution * diff --git a/bsp/esp32_s3_lcd_ev_board/include/bsp/touch.h b/bsp/esp32_s3_lcd_ev_board/include/bsp/touch.h index 79356b68..1b66f39c 100644 --- a/bsp/esp32_s3_lcd_ev_board/include/bsp/touch.h +++ b/bsp/esp32_s3_lcd_ev_board/include/bsp/touch.h @@ -17,7 +17,6 @@ #pragma once #include "esp_err.h" -#include "esp_lcd_types.h" #include "esp_lcd_touch.h" #ifdef __cplusplus diff --git a/bsp/esp32_s3_lcd_ev_board/priv_include/bsp_lvgl_port.h b/bsp/esp32_s3_lcd_ev_board/priv_include/bsp_lvgl_port.h index 34cc0f47..b01f7efe 100644 --- a/bsp/esp32_s3_lcd_ev_board/priv_include/bsp_lvgl_port.h +++ b/bsp/esp32_s3_lcd_ev_board/priv_include/bsp_lvgl_port.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -18,36 +18,13 @@ extern "C" { #endif /** - * @brief Initialize LVGL port + * @brief Initialize LCD panel and LVGL portation * - * @param[in] lcd: LCD panel handle - * @param[in] tp: Touch handle - * @param[out] disp: LVGL display device - * @param[out] indev: LVGL input device + * @note This function initialize RGB LCD panel and add add disp to LVGL. * - * @return - * - ESP_OK: Success - * - ESP_ERR_INVALID_ARG: Invalid argument - * - Others: Fail + * @return Pointer to LVGL display or NULL when error occured */ -esp_err_t bsp_lvgl_port_init(esp_lcd_panel_handle_t lcd, esp_lcd_touch_handle_t tp, lv_disp_t **disp, lv_indev_t **indev); - -/** - * @brief Take LVGL mutex - * - * @param[in] timeout_ms: Timeout in [ms]. 0 will block indefinitely. - * - * @return - * - true: Mutex was taken - * - false: Mutex was NOT taken - */ -bool bsp_lvgl_port_lock(uint32_t timeout_ms); - -/** - * @brief Give LVGL mutex - * - */ -void bsp_lvgl_port_unlock(void); +lv_disp_t *bsp_display_lcd_init(); #ifdef __cplusplus } diff --git a/bsp/esp32_s3_lcd_ev_board/src/bsp_lvgl_port.c b/bsp/esp32_s3_lcd_ev_board/src/bsp_lvgl_port.c index a62d67bc..9b03987b 100644 --- a/bsp/esp32_s3_lcd_ev_board/src/bsp_lvgl_port.c +++ b/bsp/esp32_s3_lcd_ev_board/src/bsp_lvgl_port.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -11,473 +11,21 @@ #include "esp_lcd_panel_rgb.h" #include "esp_timer.h" #include "esp_lcd_touch.h" -#include "lvgl.h" #include "bsp_err_check.h" #include "bsp/display.h" #include "bsp/esp32_s3_lcd_ev_board.h" static const char *TAG = "bsp_lvgl_port"; -static SemaphoreHandle_t lvgl_mux; // LVGL mutex -static TaskHandle_t lvgl_task_handle = NULL; -#if CONFIG_BSP_DISPLAY_LVGL_ROTATION_DEGREE != 0 -static void *get_next_frame_buffer(esp_lcd_panel_handle_t panel_handle) +lv_disp_t *bsp_display_lcd_init() { - static void *next_fb = NULL; - static void *fb[2] = { NULL }; - if (next_fb == NULL) { - BSP_ERROR_CHECK_RETURN_NULL(esp_lcd_rgb_panel_get_frame_buffer(panel_handle, 2, &fb[0], &fb[1])); - next_fb = fb[1]; - } else { - next_fb = (next_fb == fb[0]) ? fb[1] : fb[0]; - } - return next_fb; -} - -IRAM_ATTR static void rotate_copy_pixel(const uint16_t *from, uint16_t *to, uint16_t x_start, uint16_t y_start, uint16_t x_end, uint16_t y_end, uint16_t w, uint16_t h, lv_disp_rot_t rotate) -{ - int from_index = 0; - - int to_bytes_per_line; - int to_index = 0; - int to_index_const = 0; - - switch (rotate) { - case LV_DISP_ROT_90: - to_index_const = (w - x_start - 1) * h; - for (int from_y = y_start; from_y < y_end + 1; from_y++) { - from_index = from_y * w + x_start; - to_index = to_index_const + from_y; - for (int from_x = x_start; from_x < x_end + 1; from_x++) { - *(to + to_index) = *(from + from_index); - from_index += 1; - to_index -= h; - } - } - break; - case LV_DISP_ROT_180: - to_index_const = h * w - x_start - 1; - for (int from_y = y_start; from_y < y_end + 1; from_y++) { - from_index = from_y * w + x_start; - to_index = to_index_const - from_y * w; - for (int from_x = x_start; from_x < x_end + 1; from_x++) { - *(to + to_index) = *(from + from_index); - from_index += 1; - to_index -= 1; - } - } - break; - case LV_DISP_ROT_270: - to_index_const = (x_start + 1) * h - 1; - for (int from_y = y_start; from_y < y_end + 1; from_y++) { - from_index = from_y * w + x_start; - to_index = to_index_const - from_y; - for (int from_x = x_start; from_x < x_end + 1; from_x++) { - *(to + to_index) = *(from + from_index); - from_index += 1; - to_index += h; - } - } - break; - default: - break; - } -} -#endif /* CONFIG_BSP_DISPLAY_LVGL_ROTATION_DEGREE */ - -#if CONFIG_BSP_DISPLAY_LVGL_AVOID_TEAR -#if CONFIG_BSP_DISPLAY_LVGL_DIRECT_MODE -typedef struct { - uint16_t inv_p; - uint8_t inv_area_joined[LV_INV_BUF_SIZE]; - lv_area_t inv_areas[LV_INV_BUF_SIZE]; -} lv_port_dirty_area_t; - -static lv_port_dirty_area_t dirty_area; - -static void flush_dirty_save(lv_port_dirty_area_t *dirty_area) -{ - lv_disp_t *disp = _lv_refr_get_disp_refreshing(); - dirty_area->inv_p = disp->inv_p; - for (int i = 0; i < disp->inv_p; i++) { - dirty_area->inv_area_joined[i] = disp->inv_area_joined[i]; - dirty_area->inv_areas[i] = disp->inv_areas[i]; - } -} - -typedef enum { - FLUSH_STATUS_PART, - FLUSH_STATUS_FULL -} lv_port_flush_status_t; - -typedef enum { - FLUSH_PROBE_PART_COPY, - FLUSH_PROBE_SKIP_COPY, - FLUSH_PROBE_FULL_COPY, -} lv_port_flush_probe_t; - -/** - * @brief Probe dirty area to copy - * - * @note This function is used to avoid tearing effect, and only work with LVGL direct-mode. - * - */ -static lv_port_flush_probe_t flush_copy_probe(lv_disp_drv_t *drv) -{ - static lv_port_flush_status_t prev_status = FLUSH_PROBE_PART_COPY; - lv_port_flush_status_t cur_status; - uint8_t probe_result; - lv_disp_t *disp_refr = _lv_refr_get_disp_refreshing(); - - uint32_t flush_ver = 0; - uint32_t flush_hor = 0; - for (int i = 0; i < disp_refr->inv_p; i++) { - if (disp_refr->inv_area_joined[i] == 0) { - flush_ver = (disp_refr->inv_areas[i].y2 + 1 - disp_refr->inv_areas[i].y1); - flush_hor = (disp_refr->inv_areas[i].x2 + 1 - disp_refr->inv_areas[i].x1); - break; - } - } - /* Check if the current full screen refreshes */ - cur_status = ((flush_ver == drv->ver_res) && (flush_hor == drv->hor_res)) ? (FLUSH_STATUS_FULL) : (FLUSH_STATUS_PART); - - if (prev_status == FLUSH_STATUS_FULL) { - if ((cur_status == FLUSH_STATUS_PART)) { - probe_result = FLUSH_PROBE_FULL_COPY; - } else { - probe_result = FLUSH_PROBE_SKIP_COPY; - } - } else { - probe_result = FLUSH_PROBE_PART_COPY; - } - prev_status = cur_status; - - return probe_result; -} - -#if CONFIG_BSP_DISPLAY_LVGL_ROTATION_DEGREE != 0 -static inline void *flush_get_next_buf(void *panel_handle) -{ - return get_next_frame_buffer(panel_handle); -} - -/** - * @brief Copy dirty area - * - * @note This function is used to avoid tearing effect, and only work with LVGL direct-mode. - * - */ -static void flush_dirty_copy(void *dst, void *src, lv_port_dirty_area_t *dirty_area) -{ - lv_coord_t x_start, x_end, y_start, y_end; - for (int i = 0; i < dirty_area->inv_p; i++) { - /* Refresh the unjoined areas*/ - if (dirty_area->inv_area_joined[i] == 0) { - x_start = dirty_area->inv_areas[i].x1; - x_end = dirty_area->inv_areas[i].x2; - y_start = dirty_area->inv_areas[i].y1; - y_end = dirty_area->inv_areas[i].y2; - - rotate_copy_pixel(src, dst, x_start, y_start, x_end, y_end, LV_HOR_RES, LV_VER_RES, CONFIG_BSP_DISPLAY_LVGL_ROTATION_DEGREE); - } - } -} - -static void flush_callback(lv_disp_drv_t *drv, const lv_area_t *area, lv_color_t *color_map) -{ - esp_lcd_panel_handle_t panel_handle = (esp_lcd_panel_handle_t) drv->user_data; - const int offsetx1 = area->x1; - const int offsetx2 = area->x2; - const int offsety1 = area->y1; - const int offsety2 = area->y2; - void *next_fb = NULL; - lv_port_flush_probe_t probe_result = FLUSH_PROBE_PART_COPY; - - /* Action after last area refresh */ - if (lv_disp_flush_is_last(drv)) { - /* Check if the `full_refresh` flag has been triggered */ - if (drv->full_refresh) { - /* Reset flag */ - drv->full_refresh = 0; + esp_lcd_panel_io_handle_t io_handle = NULL; + esp_lcd_panel_handle_t panel_handle = NULL; // LCD panel handle - // Roate and copy data from the whole screen LVGL's buffer to the next frame buffer - next_fb = flush_get_next_buf(panel_handle); - rotate_copy_pixel((uint16_t *)color_map, next_fb, offsetx1, offsety1, offsetx2, offsety2, LV_HOR_RES, LV_VER_RES, CONFIG_BSP_DISPLAY_LVGL_ROTATION_DEGREE); - - /* Switch the current RGB frame buffer to `next_fb` */ - esp_lcd_panel_draw_bitmap(panel_handle, offsetx1, offsety1, offsetx2 + 1, offsety2 + 1, next_fb); - - /* Waiting for the current frame buffer to complete transmission */ - ulTaskNotifyValueClear(NULL, ULONG_MAX); - ulTaskNotifyTake(pdTRUE, portMAX_DELAY); - - /* Synchronously update the dirty area for another frame buffer */ - flush_dirty_copy(flush_get_next_buf(panel_handle), color_map, &dirty_area); - flush_get_next_buf(panel_handle); - } else { - /* Probe the copy method for the current dirty area */ - probe_result = flush_copy_probe(drv); - - if (probe_result == FLUSH_PROBE_FULL_COPY) { - /* Save current dirty area for next frame buffer */ - flush_dirty_save(&dirty_area); - - /* Set LVGL full-refresh flag and set flush ready in advance */ - drv->full_refresh = 1; - lv_disp_flush_ready(drv); - - /* Force to refresh whole screen, and will invoke `flush_callback` recursively */ - lv_refr_now(_lv_refr_get_disp_refreshing()); - } else { - /* Update current dirty area for next frame buffer */ - next_fb = flush_get_next_buf(panel_handle); - flush_dirty_save(&dirty_area); - flush_dirty_copy(next_fb, color_map, &dirty_area); - - /* Switch the current RGB frame buffer to `next_fb` */ - esp_lcd_panel_draw_bitmap(panel_handle, offsetx1, offsety1, offsetx2 + 1, offsety2 + 1, next_fb); - - /* Waiting for the current frame buffer to complete transmission */ - ulTaskNotifyValueClear(NULL, ULONG_MAX); - ulTaskNotifyTake(pdTRUE, portMAX_DELAY); - - if (probe_result == FLUSH_PROBE_PART_COPY) { - /* Synchronously update the dirty area for another frame buffer */ - flush_dirty_save(&dirty_area); - flush_dirty_copy(flush_get_next_buf(panel_handle), color_map, &dirty_area); - flush_get_next_buf(panel_handle); - } - } - } - } - - lv_disp_flush_ready(drv); -} -#else -static inline void *flush_get_next_buf(void *buf) -{ - lv_disp_t *disp = _lv_refr_get_disp_refreshing(); - lv_disp_draw_buf_t *draw_buf = disp->driver->draw_buf; - return (buf == draw_buf->buf1) ? draw_buf->buf2 : draw_buf->buf1; -} - -/** - * @brief Copy dirty area - * - * @note This function is used to avoid tearing effect, and only work with LVGL direct-mode. - * - */ -static void flush_dirty_copy(void *dst, void *src, lv_port_dirty_area_t *dirty_area) -{ - lv_coord_t x_start, x_end, y_start, y_end; - uint32_t copy_bytes_per_line; - uint32_t h_res = LV_HOR_RES; - uint32_t bytes_per_line = h_res * 2; - uint8_t *from, *to; - for (int i = 0; i < dirty_area->inv_p; i++) { - /* Refresh the unjoined areas*/ - if (dirty_area->inv_area_joined[i] == 0) { - x_start = dirty_area->inv_areas[i].x1; - x_end = dirty_area->inv_areas[i].x2 + 1; - y_start = dirty_area->inv_areas[i].y1; - y_end = dirty_area->inv_areas[i].y2 + 1; - - copy_bytes_per_line = (x_end - x_start) * 2; - from = src + (y_start * h_res + x_start) * 2; - to = dst + (y_start * h_res + x_start) * 2; - for (int y = y_start; y < y_end; y++) { - memcpy(to, from, copy_bytes_per_line); - from += bytes_per_line; - to += bytes_per_line; - } - } - } -} - -static void flush_callback(lv_disp_drv_t *drv, const lv_area_t *area, lv_color_t *color_map) -{ - esp_lcd_panel_handle_t panel_handle = (esp_lcd_panel_handle_t) drv->user_data; - const int offsetx1 = area->x1; - const int offsetx2 = area->x2; - const int offsety1 = area->y1; - const int offsety2 = area->y2; - - lv_port_flush_probe_t probe_result; - /* Action after last area refresh */ - if (lv_disp_flush_is_last(drv)) { - /* Check if the `full_refresh` flag has been triggered */ - if (drv->full_refresh) { - /* Reset flag */ - drv->full_refresh = 0; - - /* Switch the current RGB frame buffer to `color_map` */ - esp_lcd_panel_draw_bitmap(panel_handle, offsetx1, offsety1, offsetx2 + 1, offsety2 + 1, color_map); - - /* Waiting for the last frame buffer to complete transmission */ - ulTaskNotifyValueClear(NULL, ULONG_MAX); - ulTaskNotifyTake(pdTRUE, portMAX_DELAY); - - /* Synchronously update the dirty area for another frame buffer */ - flush_dirty_copy(flush_get_next_buf(color_map), color_map, &dirty_area); - drv->draw_buf->buf_act = (color_map == drv->draw_buf->buf1) ? drv->draw_buf->buf2 : drv->draw_buf->buf1; - } else { - /* Probe the copy method for the current dirty area */ - probe_result = flush_copy_probe(drv); - - if (probe_result == FLUSH_PROBE_FULL_COPY) { - /* Save current dirty area for next frame buffer */ - flush_dirty_save(&dirty_area); - - /* Set LVGL full-refresh flag and set flush ready in advance */ - drv->full_refresh = 1; - lv_disp_flush_ready(drv); - - /* Force to refresh whole screen, and will invoke `flush_callback` recursively */ - lv_refr_now(_lv_refr_get_disp_refreshing()); - } else { - /* Switch the current RGB frame buffer to `color_map` */ - esp_lcd_panel_draw_bitmap(panel_handle, offsetx1, offsety1, offsetx2 + 1, offsety2 + 1, color_map); - - /* Waiting for the last frame buffer to complete transmission */ - ulTaskNotifyValueClear(NULL, ULONG_MAX); - ulTaskNotifyTake(pdTRUE, portMAX_DELAY); - - if (probe_result == FLUSH_PROBE_PART_COPY) { - /* Synchronously update the dirty area for another frame buffer */ - flush_dirty_save(&dirty_area); - flush_dirty_copy(flush_get_next_buf(color_map), color_map, &dirty_area); - } - } - } - } - - lv_disp_flush_ready(drv); -} -#endif /* CONFIG_BSP_DISPLAY_LVGL_ROTATION_DEGREE */ - -#elif CONFIG_BSP_DISPLAY_LVGL_FULL_REFRESH && CONFIG_BSP_LCD_RGB_BUFFER_NUMS == 2 - -static void flush_callback(lv_disp_drv_t *drv, const lv_area_t *area, lv_color_t *color_map) -{ - esp_lcd_panel_handle_t panel_handle = (esp_lcd_panel_handle_t) drv->user_data; - const int offsetx1 = area->x1; - const int offsetx2 = area->x2; - const int offsety1 = area->y1; - const int offsety2 = area->y2; + bsp_display_config_t disp_config = { 0 }; - /* Switch the current RGB frame buffer to `color_map` */ - esp_lcd_panel_draw_bitmap(panel_handle, offsetx1, offsety1, offsetx2 + 1, offsety2 + 1, color_map); - - /* Waiting for the last frame buffer to complete transmission */ - ulTaskNotifyValueClear(NULL, ULONG_MAX); - ulTaskNotifyTake(pdTRUE, portMAX_DELAY); - - lv_disp_flush_ready(drv); -} - -#elif CONFIG_BSP_DISPLAY_LVGL_FULL_REFRESH && CONFIG_BSP_LCD_RGB_BUFFER_NUMS == 3 - -#if CONFIG_BSP_DISPLAY_LVGL_ROTATION_DEGREE == 0 -static void *lvgl_port_rgb_last_buf = NULL; -static void *lvgl_port_rgb_next_buf = NULL; -static void *lvgl_port_flush_next_buf = NULL; -#endif - -void flush_callback(lv_disp_drv_t *drv, const lv_area_t *area, lv_color_t *color_map) -{ - esp_lcd_panel_handle_t panel_handle = (esp_lcd_panel_handle_t) drv->user_data; - const int offsetx1 = area->x1; - const int offsetx2 = area->x2; - const int offsety1 = area->y1; - const int offsety2 = area->y2; - -#if CONFIG_BSP_DISPLAY_LVGL_ROTATION_DEGREE != 0 - void *next_fb = get_next_frame_buffer(panel_handle); - - /* Rotate and copy dirty area from the current LVGL's buffer to the next RGB frame buffer */ - rotate_copy_pixel((uint16_t *)color_map, next_fb, offsetx1, offsety1, offsetx2, offsety2, LV_HOR_RES, LV_VER_RES, CONFIG_BSP_DISPLAY_LVGL_ROTATION_DEGREE); - - /* Switch the current RGB frame buffer to `next_fb` */ - esp_lcd_panel_draw_bitmap(panel_handle, offsetx1, offsety1, offsetx2 + 1, offsety2 + 1, next_fb); -#else - drv->draw_buf->buf1 = color_map; - drv->draw_buf->buf2 = lvgl_port_flush_next_buf; - lvgl_port_flush_next_buf = color_map; - - /* Switch the current RGB frame buffer to `color_map` */ - esp_lcd_panel_draw_bitmap(panel_handle, offsetx1, offsety1, offsetx2 + 1, offsety2 + 1, color_map); - - lvgl_port_rgb_next_buf = color_map; -#endif - - lv_disp_flush_ready(drv); -} -#endif - -static bool lcd_trans_done(esp_lcd_panel_handle_t handle) -{ - BaseType_t need_yield = pdFALSE; -#if CONFIG_BSP_DISPLAY_LVGL_FULL_REFRESH && (CONFIG_BSP_LCD_RGB_BUFFER_NUMS == 3) && (CONFIG_BSP_DISPLAY_LVGL_ROTATION_DEGREE == 0) - if (lvgl_port_rgb_next_buf != lvgl_port_rgb_last_buf) { - lvgl_port_flush_next_buf = lvgl_port_rgb_last_buf; - lvgl_port_rgb_last_buf = lvgl_port_rgb_next_buf; - } -#else - // Notify that the current RGB frame buffer has been transmitted - xTaskNotifyFromISR(lvgl_task_handle, ULONG_MAX, eNoAction, &need_yield); -#endif - return (need_yield == pdTRUE); -} - -#else - -void flush_callback(lv_disp_drv_t *drv, const lv_area_t *area, lv_color_t *color_map) -{ - esp_lcd_panel_handle_t panel_handle = (esp_lcd_panel_handle_t) drv->user_data; - const int offsetx1 = area->x1; - const int offsetx2 = area->x2; - const int offsety1 = area->y1; - const int offsety2 = area->y2; - - /* Just copy data from the color map to the RGB frame buffer */ - esp_lcd_panel_draw_bitmap(panel_handle, offsetx1, offsety1, offsetx2 + 1, offsety2 + 1, color_map); - - lv_disp_flush_ready(drv); -} - -#endif /* CONFIG_BSP_DISPLAY_LVGL_AVOID_TEAR */ - -static void update_callback(lv_disp_drv_t *drv) -{ - esp_lcd_panel_handle_t panel_handle = (esp_lcd_panel_handle_t) drv->user_data; - - switch (drv->rotated) { - case LV_DISP_ROT_NONE: - esp_lcd_panel_swap_xy(panel_handle, false); - esp_lcd_panel_mirror(panel_handle, false, false); - break; - case LV_DISP_ROT_90: - esp_lcd_panel_swap_xy(panel_handle, true); - esp_lcd_panel_mirror(panel_handle, false, true); - break; - case LV_DISP_ROT_180: - esp_lcd_panel_swap_xy(panel_handle, false); - esp_lcd_panel_mirror(panel_handle, true, true); - break; - case LV_DISP_ROT_270: - esp_lcd_panel_swap_xy(panel_handle, true); - esp_lcd_panel_mirror(panel_handle, true, false); - break; - } -} - -static lv_disp_t *display_init(esp_lcd_panel_handle_t lcd) -{ - BSP_NULL_CHECK(lcd, NULL); - - static lv_disp_draw_buf_t disp_buf = { 0 }; // Contains internal graphic buffer(s) called draw buffer(s) - static lv_disp_drv_t disp_drv = { 0 }; // Contains LCD panel handle and callback functions + BSP_ERROR_CHECK_RETURN_NULL(bsp_display_new(&disp_config, &panel_handle, &io_handle)); // alloc draw buffers used by LVGL void *buf1 = NULL; @@ -494,169 +42,31 @@ static lv_disp_t *display_init(esp_lcd_panel_handle_t lcd) #else // To avoid the tearing effect, we should use at least two frame buffers: one for LVGL rendering and another for RGB output buffer_size = BSP_LCD_H_RES * BSP_LCD_V_RES; -#if (CONFIG_BSP_LCD_RGB_BUFFER_NUMS == 3) && (CONFIG_BSP_DISPLAY_LVGL_ROTATION_DEGREE == 0) && CONFIG_BSP_DISPLAY_LVGL_FULL_REFRESH - // With the usage of three buffers and full-refresh, we always have one buffer available for rendering, eliminating the need to wait for the RGB's sync signal - BSP_ERROR_CHECK_RETURN_NULL(esp_lcd_rgb_panel_get_frame_buffer(lcd, 3, &lvgl_port_rgb_last_buf, &buf1, &buf2)); - lvgl_port_rgb_next_buf = lvgl_port_rgb_last_buf; - lvgl_port_flush_next_buf = buf2; -#elif (CONFIG_BSP_LCD_RGB_BUFFER_NUMS == 3) && (CONFIG_BSP_DISPLAY_LVGL_ROTATION_DEGREE != 0) - // Here we are using three frame buffers, one for LVGL rendering, and the other two for RGB driver (one of them is used for rotation) - void *fbs[3]; - BSP_ERROR_CHECK_RETURN_NULL(esp_lcd_rgb_panel_get_frame_buffer(lcd, 3, &fbs[0], &fbs[1], &fbs[2])); - buf1 = fbs[2]; -#else - BSP_ERROR_CHECK_RETURN_NULL(esp_lcd_rgb_panel_get_frame_buffer(lcd, 2, &buf1, &buf2)); -#endif + BSP_ERROR_CHECK_RETURN_NULL(esp_lcd_rgb_panel_get_frame_buffer(panel_handle, 2, &buf1, &buf2)); #endif /* CONFIG_BSP_DISPLAY_LVGL_AVOID_TEAR */ - // initialize LVGL draw buffers - lv_disp_draw_buf_init(&disp_buf, buf1, buf2, buffer_size); + const lvgl_port_display_cfg_t disp_cfg = { + .io_handle = io_handle, + .panel_handle = panel_handle, + .buffer_size = buffer_size, + .user_buf1 = buf1, + .user_buf2 = buf2, - ESP_LOGD(TAG, "Register display driver to LVGL"); - lv_disp_drv_init(&disp_drv); -#if CONFIG_BSP_DISPLAY_LVGL_ROTATION_90 || CONFIG_BSP_DISPLAY_LVGL_ROTATION_270 - disp_drv.hor_res = BSP_LCD_V_RES; - disp_drv.ver_res = BSP_LCD_H_RES; -#else - disp_drv.hor_res = BSP_LCD_H_RES; - disp_drv.ver_res = BSP_LCD_V_RES; + .hres = BSP_LCD_H_RES, + .vres = BSP_LCD_V_RES, + + .RGB = true, + .flags = { +#if CONFIG_BSP_LCD_RGB_BOUNCE_BUFFER_MODE + .bb_mode = 1, #endif - disp_drv.flush_cb = flush_callback; - disp_drv.drv_update_cb = update_callback; - disp_drv.draw_buf = &disp_buf; - disp_drv.user_data = lcd; #if CONFIG_BSP_DISPLAY_LVGL_FULL_REFRESH - disp_drv.full_refresh = 1; + .full_refresh = 1, #elif CONFIG_BSP_DISPLAY_LVGL_DIRECT_MODE - disp_drv.direct_mode = 1; + .direct_mode = 1, #endif - return lv_disp_drv_register(&disp_drv); -} - -static void touchpad_read(lv_indev_drv_t *indev_drv, lv_indev_data_t *data) -{ - esp_lcd_touch_handle_t tp = (esp_lcd_touch_handle_t)indev_drv->user_data; - assert(tp); - - uint16_t touchpad_x; - uint16_t touchpad_y; - uint8_t touchpad_cnt = 0; - /* Read data from touch controller into memory */ - esp_lcd_touch_read_data(tp); - - /* Read data from touch controller */ - bool touchpad_pressed = esp_lcd_touch_get_coordinates(tp, &touchpad_x, &touchpad_y, NULL, &touchpad_cnt, 1); - if (touchpad_pressed && touchpad_cnt > 0) { - data->point.x = touchpad_x; - data->point.y = touchpad_y; - data->state = LV_INDEV_STATE_PRESSED; - ESP_LOGD(TAG, "Touch position: %d,%d", touchpad_x, touchpad_y); - } else { - data->state = LV_INDEV_STATE_RELEASED; - } -} - -static lv_indev_t *indev_init(esp_lcd_touch_handle_t tp) -{ - BSP_NULL_CHECK(tp, NULL); - - static lv_indev_drv_t indev_drv_tp; - - /* Register a touchpad input device */ - lv_indev_drv_init(&indev_drv_tp); - indev_drv_tp.type = LV_INDEV_TYPE_POINTER; - indev_drv_tp.read_cb = touchpad_read; - indev_drv_tp.user_data = tp; - - return lv_indev_drv_register(&indev_drv_tp); -} - -static void tick_increment(void *arg) -{ - /* Tell LVGL how many milliseconds have elapsed */ - lv_tick_inc(LVGL_TICK_PERIOD_MS); -} - -static esp_err_t tick_init(void) -{ - // Tick interface for LVGL (using esp_timer to generate 2ms periodic event) - const esp_timer_create_args_t lvgl_tick_timer_args = { - .callback = &tick_increment, - .name = "LVGL tick" - }; - esp_timer_handle_t lvgl_tick_timer = NULL; - BSP_ERROR_CHECK_RETURN_ERR(esp_timer_create(&lvgl_tick_timer_args, &lvgl_tick_timer)); - return esp_timer_start_periodic(lvgl_tick_timer, LVGL_TICK_PERIOD_MS * 1000); -} - -static void lvgl_port_task(void *arg) -{ - ESP_LOGI(TAG, "Starting LVGL task"); - while (1) { - bsp_display_lock(0); - uint32_t task_delay_ms = lv_timer_handler(); - bsp_display_unlock(); - if (task_delay_ms > 500) { - task_delay_ms = 500; - } else if (task_delay_ms < CONFIG_BSP_DISPLAY_LVGL_TASK_DELAY) { - task_delay_ms = CONFIG_BSP_DISPLAY_LVGL_TASK_DELAY; } - vTaskDelay(pdMS_TO_TICKS(task_delay_ms)); - } -} - -esp_err_t bsp_lvgl_port_init(esp_lcd_panel_handle_t lcd, esp_lcd_touch_handle_t tp, lv_disp_t **disp, lv_indev_t **indev) -{ - BSP_NULL_CHECK(lcd, ESP_ERR_INVALID_ARG); - BSP_NULL_CHECK(tp, ESP_ERR_INVALID_ARG); - BSP_NULL_CHECK(disp, ESP_ERR_INVALID_ARG); - BSP_NULL_CHECK(indev, ESP_ERR_INVALID_ARG); - - lv_init(); - BSP_ERROR_CHECK_RETURN_ERR(tick_init()); - BSP_NULL_CHECK(*disp = display_init(lcd), ESP_FAIL); - BSP_NULL_CHECK(*indev = indev_init(tp), ESP_FAIL); - -#if CONFIG_BSP_DISPLAY_LVGL_ROTATION_90 - esp_lcd_touch_set_swap_xy(tp, true); - esp_lcd_touch_set_mirror_y(tp, true); -#elif CONFIG_BSP_DISPLAY_LVGL_ROTATION_180 - esp_lcd_touch_set_mirror_x(tp, true); - esp_lcd_touch_set_mirror_y(tp, true); -#elif CONFIG_BSP_DISPLAY_LVGL_ROTATION_270 - esp_lcd_touch_set_swap_xy(tp, true); - esp_lcd_touch_set_mirror_x(tp, true); -#endif - - lvgl_mux = xSemaphoreCreateRecursiveMutex(); - BSP_NULL_CHECK(lvgl_mux, ESP_FAIL); - ESP_LOGI(TAG, "Create LVGL task"); - BaseType_t core_id = (CONFIG_BSP_DISPLAY_LVGL_TASK_CORE_ID < 0) ? tskNO_AFFINITY : CONFIG_BSP_DISPLAY_LVGL_TASK_CORE_ID; - BaseType_t ret = xTaskCreatePinnedToCore( - lvgl_port_task, "LVGL", CONFIG_BSP_DISPLAY_LVGL_TASK_STACK_SIZE_KB * 1024, NULL, - CONFIG_BSP_DISPLAY_LVGL_TASK_PRIORITY, &lvgl_task_handle, core_id - ); - if (ret != pdPASS) { - ESP_LOGE(TAG, "Failed to create LVGL task"); - return ESP_FAIL; - } -#if CONFIG_BSP_DISPLAY_LVGL_AVOID_TEAR - bsp_display_register_trans_done_callback(lcd_trans_done); -#endif - - return ESP_OK; -} - -bool bsp_lvgl_port_lock(uint32_t timeout_ms) -{ - assert(lvgl_mux && "bsp_lvgl_port_init must be called first"); - - const TickType_t timeout_ticks = (timeout_ms == 0) ? portMAX_DELAY : pdMS_TO_TICKS(timeout_ms); - return xSemaphoreTakeRecursive(lvgl_mux, timeout_ticks) == pdTRUE; -} + }; -void bsp_lvgl_port_unlock(void) -{ - assert(lvgl_mux && "bsp_lvgl_port_init must be called first"); - xSemaphoreGiveRecursive(lvgl_mux); + return lvgl_port_add_disp(&disp_cfg); } diff --git a/bsp/esp32_s3_lcd_ev_board/src/bsp_sub_board.c b/bsp/esp32_s3_lcd_ev_board/src/bsp_sub_board.c index 164d6a6c..c7c763f5 100644 --- a/bsp/esp32_s3_lcd_ev_board/src/bsp_sub_board.c +++ b/bsp/esp32_s3_lcd_ev_board/src/bsp_sub_board.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -35,45 +35,12 @@ #endif static const char *TAG = "bsp_sub_board"; -static bsp_display_trans_done_cb_t trans_done = NULL; -#if CONFIG_BSP_LCD_RGB_REFRESH_MANUALLY -static TaskHandle_t lcd_task_handle = NULL; -#endif /************************************************************************************************** * * Display Panel Function * **************************************************************************************************/ -IRAM_ATTR static bool rgb_lcd_on_vsync_event(esp_lcd_panel_handle_t panel, const esp_lcd_rgb_panel_event_data_t *edata, void *user_ctx) -{ - BaseType_t need_yield = pdFALSE; -#if CONFIG_BSP_LCD_RGB_REFRESH_MANUALLY - xTaskNotifyFromISR(lcd_task_handle, ULONG_MAX, eNoAction, &need_yield); -#endif - if (trans_done) { - if (trans_done(panel)) { - need_yield = pdTRUE; - } - } - - return (need_yield == pdTRUE); -} - -#if CONFIG_BSP_LCD_RGB_REFRESH_MANUALLY -static void lcd_task(void *arg) -{ - ESP_LOGI(TAG, "Starting LCD refresh task"); - - TickType_t tick; - for (;;) { - esp_lcd_rgb_panel_refresh((esp_lcd_panel_handle_t)arg); - tick = xTaskGetTickCount(); - ulTaskNotifyTake(pdTRUE, portMAX_DELAY); - vTaskDelayUntil(&tick, pdMS_TO_TICKS(CONFIG_BSP_LCD_RGB_REFRESH_TASK_PERIOD)); - } -} -#endif esp_err_t bsp_display_new(const bsp_display_config_t *config, esp_lcd_panel_handle_t *ret_panel, esp_lcd_panel_io_handle_t *ret_io) { @@ -165,9 +132,6 @@ drift, please enable `ESP32S3_DATA_CACHE_LINE_32B` instead"); }, .timings = SUB_BOARD2_480_480_PANEL_60HZ_RGB_TIMING(), .flags.fb_in_psram = 1, -#if CONFIG_BSP_LCD_RGB_REFRESH_MANUALLY - .flags.refresh_on_demand = 1, -#endif .num_fbs = CONFIG_BSP_LCD_RGB_BUFFER_NUMS, #if CONFIG_BSP_LCD_RGB_BOUNCE_BUFFER_MODE .bounce_buffer_size_px = BSP_LCD_SUB_BOARD_2_H_RES * CONFIG_BSP_LCD_RGB_BOUNCE_BUFFER_HEIGHT, @@ -196,14 +160,6 @@ drift, please enable `ESP32S3_DATA_CACHE_LINE_32B` instead"); #else BSP_ERROR_CHECK_RETURN_ERR(esp_lcd_new_panel_gc9503(io_handle, &rgb_conf, &panel_handle)); #endif - esp_lcd_rgb_panel_event_callbacks_t cbs = { -#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 1, 2) && CONFIG_BSP_LCD_RGB_BOUNCE_BUFFER_MODE - .on_bounce_frame_finish = rgb_lcd_on_vsync_event, -#else - .on_vsync = rgb_lcd_on_vsync_event, -#endif - }; - esp_lcd_rgb_panel_register_event_callbacks(panel_handle, &cbs, NULL); break; } case SUB_BOARD_TYPE_3_800_480: { @@ -238,9 +194,6 @@ drift, please enable `ESP32S3_DATA_CACHE_LINE_32B` instead"); }, .timings = SUB_BOARD3_800_480_PANEL_35HZ_RGB_TIMING(), .flags.fb_in_psram = 1, -#if CONFIG_BSP_LCD_RGB_REFRESH_MANUALLY - .flags.refresh_on_demand = 1, -#endif .num_fbs = CONFIG_BSP_LCD_RGB_BUFFER_NUMS, #if CONFIG_BSP_LCD_RGB_BOUNCE_BUFFER_MODE .bounce_buffer_size_px = BSP_LCD_SUB_BOARD_3_H_RES * CONFIG_BSP_LCD_RGB_BOUNCE_BUFFER_HEIGHT, @@ -252,14 +205,6 @@ drift, please enable `ESP32S3_DATA_CACHE_LINE_32B` instead"); panel_conf.data_gpio_nums[7] = BSP_LCD_SUB_BOARD_2_3_DATA7_R16; } BSP_ERROR_CHECK_RETURN_ERR(esp_lcd_new_rgb_panel(&panel_conf, &panel_handle)); - esp_lcd_rgb_panel_event_callbacks_t cbs = { -#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 1, 2) && CONFIG_BSP_LCD_RGB_BOUNCE_BUFFER_MODE - .on_bounce_frame_finish = rgb_lcd_on_vsync_event, -#else - .on_vsync = rgb_lcd_on_vsync_event, -#endif - }; - esp_lcd_rgb_panel_register_event_callbacks(panel_handle, &cbs, NULL); break; } default: @@ -267,15 +212,6 @@ drift, please enable `ESP32S3_DATA_CACHE_LINE_32B` instead"); } BSP_ERROR_CHECK_RETURN_ERR(esp_lcd_panel_init(panel_handle)); -#if CONFIG_BSP_LCD_RGB_REFRESH_MANUALLY - ESP_LOGI(TAG, "Create LCD task"); - BaseType_t ret = xTaskCreate(lcd_task, "LCD", 2048, panel_handle, CONFIG_BSP_LCD_RGB_REFRESH_TASK_PRIORITY, &lcd_task_handle); - if (ret != pdPASS) { - ESP_LOGE(TAG, "Failed to create LCD task"); - return ESP_FAIL; - } -#endif - if (ret_panel) { *ret_panel = panel_handle; } @@ -286,18 +222,6 @@ drift, please enable `ESP32S3_DATA_CACHE_LINE_32B` instead"); return ESP_OK; } -esp_err_t bsp_display_register_trans_done_callback(bsp_display_trans_done_cb_t callback) -{ -#if CONFIG_LCD_RGB_ISR_IRAM_SAFE - if (callback) { - ESP_RETURN_ON_FALSE(esp_ptr_in_iram(callback), ESP_ERR_INVALID_ARG, TAG, "Callback not in IRAM"); - } -#endif - trans_done = callback; - - return ESP_OK; -} - /************************************************************************************************** * * Touch Panel Function diff --git a/bsp/esp32_s3_lcd_ev_board/src/esp32_s3_lcd_ev_board.c b/bsp/esp32_s3_lcd_ev_board/src/esp32_s3_lcd_ev_board.c index a9569528..0a98ec03 100644 --- a/bsp/esp32_s3_lcd_ev_board/src/esp32_s3_lcd_ev_board.c +++ b/bsp/esp32_s3_lcd_ev_board/src/esp32_s3_lcd_ev_board.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -26,6 +26,8 @@ #include "bsp_lvgl_port.h" #include "bsp_probe.h" +#include "esp_lvgl_port.h" + #define BSP_ES7210_CODEC_ADDR (0x82) /* Can be used for `i2s_std_gpio_config_t` and/or `i2s_std_config_t` initialization */ @@ -58,9 +60,12 @@ static const audio_codec_data_if_t *i2s_data_if = NULL; /* Codec data interface static i2s_chan_handle_t i2s_tx_chan = NULL; static i2s_chan_handle_t i2s_rx_chan = NULL; static esp_io_expander_handle_t io_expander = NULL; // IO expander tca9554 handle -static lv_indev_t *disp_indev = NULL; static adc_oneshot_unit_handle_t bsp_adc_handle = NULL; +static lv_disp_t *disp; +static lv_indev_t *disp_indev = NULL; +static esp_lcd_touch_handle_t tp; // LCD touch handle + static const button_config_t bsp_button_config[BSP_BUTTON_NUM] = { { .type = BUTTON_TYPE_GPIO, @@ -311,6 +316,20 @@ esp_err_t bsp_audio_poweramp_enable(bool enable) return ESP_OK; } +static lv_indev_t *bsp_display_indev_init(lv_disp_t *disp) +{ + BSP_ERROR_CHECK_RETURN_NULL(bsp_touch_new(NULL, &tp)); + assert(tp); + + /* Add touch input (for selected screen) */ + const lvgl_port_touch_cfg_t touch_cfg = { + .disp = disp, + .handle = tp, + }; + + return lvgl_port_add_touch(&touch_cfg); +} + /********************************************************************************************************** * * Display Function @@ -318,20 +337,20 @@ esp_err_t bsp_audio_poweramp_enable(bool enable) **********************************************************************************************************/ lv_disp_t *bsp_display_start(void) { - return bsp_display_start_with_config(NULL); + bsp_display_cfg_t cfg = { + .lvgl_port_cfg = ESP_LVGL_PORT_INIT_CONFIG() + }; + + return bsp_display_start_with_config(&cfg); } lv_disp_t *bsp_display_start_with_config(const bsp_display_cfg_t *cfg) { - (void)cfg; - bsp_display_config_t disp_config = { 0 }; - esp_lcd_panel_handle_t lcd = NULL; // LCD panel handle - esp_lcd_touch_handle_t tp = NULL; // LCD touch panel handle - lv_disp_t *disp = NULL; + BSP_ERROR_CHECK_RETURN_NULL(lvgl_port_init(&cfg->lvgl_port_cfg)); /* lvgl task, tick etc*/ - BSP_ERROR_CHECK_RETURN_NULL(bsp_display_new(&disp_config, &lcd, NULL)); - BSP_ERROR_CHECK_RETURN_NULL(bsp_touch_new(NULL, &tp)); - BSP_ERROR_CHECK_RETURN_NULL(bsp_lvgl_port_init(lcd, tp, &disp, &disp_indev)); + BSP_NULL_CHECK(disp = bsp_display_lcd_init(), NULL); + + BSP_NULL_CHECK(disp_indev = bsp_display_indev_init(disp), NULL); return disp; } @@ -357,23 +376,14 @@ esp_err_t bsp_display_backlight_on(void) return bsp_display_brightness_set(100); } -void bsp_display_rotate(lv_disp_t *disp, lv_disp_rot_t rotation) -{ -#if CONFIG_BSP_DISPLAY_LVGL_AVOID_TEAR - ESP_LOGE(TAG, "Unable to rotate the display using the `bsp_display_rotate()` function when the anti-tearing function is enabled. Please use the `BSP_DISPLAY_LVGL_ROTATION` configuration instead."); -#else - lv_disp_set_rotation(disp, rotation); -#endif -} - bool bsp_display_lock(uint32_t timeout_ms) { - return bsp_lvgl_port_lock(timeout_ms); + return lvgl_port_lock(timeout_ms); } void bsp_display_unlock(void) { - bsp_lvgl_port_unlock(); + lvgl_port_unlock(); } /************************************************************************************************** diff --git a/components/esp_lvgl_port/include/esp_lvgl_port.h b/components/esp_lvgl_port/include/esp_lvgl_port.h index 6c15776b..2a83a62b 100644 --- a/components/esp_lvgl_port/include/esp_lvgl_port.h +++ b/components/esp_lvgl_port/include/esp_lvgl_port.h @@ -120,6 +120,8 @@ esp_err_t lvgl_port_stop(void); */ esp_err_t lvgl_port_resume(void); +bool lvgl_port_task_notify(void); + #ifdef __cplusplus } #endif diff --git a/components/esp_lvgl_port/include/esp_lvgl_port_disp.h b/components/esp_lvgl_port/include/esp_lvgl_port_disp.h index a6eaf8af..edc65190 100644 --- a/components/esp_lvgl_port/include/esp_lvgl_port_disp.h +++ b/components/esp_lvgl_port/include/esp_lvgl_port_disp.h @@ -44,20 +44,27 @@ typedef struct { uint32_t buffer_size; /*!< Size of the buffer for the screen in pixels */ bool double_buffer; /*!< True, if should be allocated two buffers */ uint32_t trans_size; /*!< Allocated buffer will be in SRAM to move framebuf */ + void *user_buf1; /*!< OPTIONAL: A buffer to be used by LVGL to draw the image */ + void *user_buf2; /*!< OPTIONAL: Optionally specify a second buffer to render and flush */ uint32_t hres; /*!< LCD display horizontal resolution */ uint32_t vres; /*!< LCD display vertical resolution */ bool monochrome; /*!< True, if display is monochrome and using 1bit for 1px */ bool mipi_dsi; /*!< True, if display is MIPI-DSI */ + bool RGB; /*!< True, if display is RGB */ lvgl_port_rotation_cfg_t rotation; /*!< Default values of the screen rotation */ struct { unsigned int buff_dma: 1; /*!< Allocated LVGL buffer will be DMA capable */ unsigned int buff_spiram: 1; /*!< Allocated LVGL buffer will be in PSRAM */ - unsigned int sw_rotate: 1; /*!< Use software rotation (slower) */ + unsigned int sw_rotate: 1; /*!< Use software rotation (slower), LVGL V8 support*/ unsigned int swap_bytes: 1; /*!< Swap bytes in RGB656 (16-bit) before send to LCD driver */ + + unsigned int full_refresh: 1; /*!< 1: Always make the whole screen redrawn */ + unsigned int direct_mode: 1; /*!< 1: Use screen-sized buffers and draw to absolute coordinates */ + unsigned int bb_mode: 1; } flags; } lvgl_port_display_cfg_t; diff --git a/components/esp_lvgl_port/src/lvgl8/esp_lvgl_port.c b/components/esp_lvgl_port/src/lvgl8/esp_lvgl_port.c index 423f0e20..82ba87c0 100644 --- a/components/esp_lvgl_port/src/lvgl8/esp_lvgl_port.c +++ b/components/esp_lvgl_port/src/lvgl8/esp_lvgl_port.c @@ -33,6 +33,7 @@ typedef struct lvgl_port_ctx_s { * Local variables *******************************************************************************/ static lvgl_port_ctx_t lvgl_port_ctx; +static TaskHandle_t lvgl_port_task_handle; /******************************************************************************* * Function definitions @@ -68,9 +69,9 @@ esp_err_t lvgl_port_init(const lvgl_port_cfg_t *cfg) BaseType_t res; if (cfg->task_affinity < 0) { - res = xTaskCreate(lvgl_port_task, "LVGL task", cfg->task_stack, NULL, cfg->task_priority, NULL); + res = xTaskCreate(lvgl_port_task, "LVGL task", cfg->task_stack, NULL, cfg->task_priority, &lvgl_port_task_handle); } else { - res = xTaskCreatePinnedToCore(lvgl_port_task, "LVGL task", cfg->task_stack, NULL, cfg->task_priority, NULL, cfg->task_affinity); + res = xTaskCreatePinnedToCore(lvgl_port_task, "LVGL task", cfg->task_stack, NULL, cfg->task_priority, &lvgl_port_task_handle, cfg->task_affinity); } ESP_GOTO_ON_FALSE(res == pdPASS, ESP_FAIL, err, TAG, "Create LVGL task fail!"); @@ -139,6 +140,15 @@ void lvgl_port_unlock(void) xSemaphoreGiveRecursive(lvgl_port_ctx.lvgl_mux); } +IRAM_ATTR bool lvgl_port_task_notify(void) +{ + BaseType_t need_yield = pdFALSE; + + // Notify that the current RGB frame buffer has been transmitted + xTaskNotifyFromISR(lvgl_port_task_handle, ULONG_MAX, eNoAction, &need_yield); + + return (need_yield == pdTRUE); +} /******************************************************************************* * Private functions *******************************************************************************/ @@ -165,7 +175,7 @@ static void lvgl_port_task(void *arg) lvgl_port_task_deinit(); /* Close task */ - vTaskDelete( NULL ); + vTaskDelete(NULL); } static void lvgl_port_task_deinit(void) diff --git a/components/esp_lvgl_port/src/lvgl8/esp_lvgl_port_disp.c b/components/esp_lvgl_port/src/lvgl8/esp_lvgl_port_disp.c index 0796073e..b8c637d2 100644 --- a/components/esp_lvgl_port/src/lvgl8/esp_lvgl_port_disp.c +++ b/components/esp_lvgl_port/src/lvgl8/esp_lvgl_port_disp.c @@ -18,6 +18,9 @@ #if (CONFIG_IDF_TARGET_ESP32P4 && ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 3, 0)) #include "esp_lcd_mipi_dsi.h" #endif +#if CONFIG_IDF_TARGET_ESP32S3 && ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0) +#include "esp_lcd_panel_rgb.h" +#endif #if (ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(4, 4, 4)) || (ESP_IDF_VERSION == ESP_IDF_VERSION_VAL(5, 0, 0)) #define LVGL_PORT_HANDLE_FLUSH_READY 0 @@ -40,6 +43,10 @@ typedef struct { lv_color_t *trans_buf; /* Buffer send to driver */ uint32_t trans_size; /* Maximum size for one transport */ SemaphoreHandle_t trans_sem; /* Idle transfer mutex */ + + struct { + unsigned int flush_byself: 1; + } flags; } lvgl_port_display_ctx_t; /******************************************************************************* @@ -51,6 +58,9 @@ static bool lvgl_port_flush_io_ready_callback(esp_lcd_panel_io_handle_t panel_io #if (CONFIG_IDF_TARGET_ESP32P4 && ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 3, 0)) static bool lvgl_port_flush_panel_ready_callback(esp_lcd_panel_handle_t panel_io, esp_lcd_dpi_panel_event_data_t *edata, void *user_ctx); #endif +#if CONFIG_IDF_TARGET_ESP32S3 && ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0) +static bool lvgl_port_flush_vsync_ready_callback(esp_lcd_panel_handle_t panel_io, const esp_lcd_rgb_panel_event_data_t *edata, void *user_ctx); +#endif #endif static void lvgl_port_flush_callback(lv_disp_drv_t *drv, const lv_area_t *area, lv_color_t *color_map); static void lvgl_port_update_callback(lv_disp_drv_t *drv); @@ -69,7 +79,6 @@ lv_disp_t *lvgl_port_add_disp(const lvgl_port_display_cfg_t *disp_cfg) lv_color_t *buf3 = NULL; SemaphoreHandle_t trans_sem = NULL; assert(disp_cfg != NULL); - assert(disp_cfg->io_handle != NULL); assert(disp_cfg->panel_handle != NULL); assert(disp_cfg->buffer_size > 0); assert(disp_cfg->hres > 0); @@ -78,6 +87,7 @@ lv_disp_t *lvgl_port_add_disp(const lvgl_port_display_cfg_t *disp_cfg) /* Display context */ lvgl_port_display_ctx_t *disp_ctx = malloc(sizeof(lvgl_port_display_ctx_t)); ESP_GOTO_ON_FALSE(disp_ctx, ESP_ERR_NO_MEM, err, TAG, "Not enough memory for display context allocation!"); + memset(disp_ctx, 0, sizeof(lvgl_port_display_ctx_t)); disp_ctx->io_handle = disp_cfg->io_handle; disp_ctx->panel_handle = disp_cfg->panel_handle; disp_ctx->control_handle = disp_cfg->control_handle; @@ -86,32 +96,37 @@ lv_disp_t *lvgl_port_add_disp(const lvgl_port_display_cfg_t *disp_cfg) disp_ctx->rotation.mirror_y = disp_cfg->rotation.mirror_y; disp_ctx->trans_size = disp_cfg->trans_size; - uint32_t buff_caps = MALLOC_CAP_DEFAULT; - if (disp_cfg->flags.buff_dma && disp_cfg->flags.buff_spiram && (0 == disp_cfg->trans_size)) { - ESP_GOTO_ON_FALSE(false, ESP_ERR_NOT_SUPPORTED, err, TAG, "Alloc DMA capable buffer in SPIRAM is not supported!"); - } else if (disp_cfg->flags.buff_dma) { - buff_caps = MALLOC_CAP_DMA; - } else if (disp_cfg->flags.buff_spiram) { - buff_caps = MALLOC_CAP_SPIRAM; - } + if (disp_cfg->user_buf1 || disp_cfg->user_buf2) { + buf1 = disp_cfg->user_buf1; + buf2 = disp_cfg->user_buf2; + } else { + uint32_t buff_caps = MALLOC_CAP_DEFAULT; + if (disp_cfg->flags.buff_dma && disp_cfg->flags.buff_spiram && (0 == disp_cfg->trans_size)) { + ESP_GOTO_ON_FALSE(false, ESP_ERR_NOT_SUPPORTED, err, TAG, "Alloc DMA capable buffer in SPIRAM is not supported!"); + } else if (disp_cfg->flags.buff_dma) { + buff_caps = MALLOC_CAP_DMA; + } else if (disp_cfg->flags.buff_spiram) { + buff_caps = MALLOC_CAP_SPIRAM; + } - if (disp_cfg->trans_size) { - buf3 = heap_caps_malloc(disp_cfg->trans_size * sizeof(lv_color_t), MALLOC_CAP_DMA); - ESP_GOTO_ON_FALSE(buf3, ESP_ERR_NO_MEM, err, TAG, "Not enough memory for buffer(transport) allocation!"); - disp_ctx->trans_buf = buf3; + if (disp_cfg->trans_size) { + buf3 = heap_caps_malloc(disp_cfg->trans_size * sizeof(lv_color_t), MALLOC_CAP_DMA); + ESP_GOTO_ON_FALSE(buf3, ESP_ERR_NO_MEM, err, TAG, "Not enough memory for buffer(transport) allocation!"); + disp_ctx->trans_buf = buf3; - trans_sem = xSemaphoreCreateCounting(1, 0); - ESP_GOTO_ON_FALSE(trans_sem, ESP_ERR_NO_MEM, err, TAG, "Failed to create transport counting Semaphore"); - disp_ctx->trans_sem = trans_sem; - } + trans_sem = xSemaphoreCreateCounting(1, 0); + ESP_GOTO_ON_FALSE(trans_sem, ESP_ERR_NO_MEM, err, TAG, "Failed to create transport counting Semaphore"); + disp_ctx->trans_sem = trans_sem; + } - /* alloc draw buffers used by LVGL */ - /* it's recommended to choose the size of the draw buffer(s) to be at least 1/10 screen sized */ - buf1 = heap_caps_malloc(disp_cfg->buffer_size * sizeof(lv_color_t), buff_caps); - ESP_GOTO_ON_FALSE(buf1, ESP_ERR_NO_MEM, err, TAG, "Not enough memory for LVGL buffer (buf1) allocation!"); - if (disp_cfg->double_buffer) { - buf2 = heap_caps_malloc(disp_cfg->buffer_size * sizeof(lv_color_t), buff_caps); - ESP_GOTO_ON_FALSE(buf2, ESP_ERR_NO_MEM, err, TAG, "Not enough memory for LVGL buffer (buf2) allocation!"); + /* alloc draw buffers used by LVGL */ + /* it's recommended to choose the size of the draw buffer(s) to be at least 1/10 screen sized */ + buf1 = heap_caps_malloc(disp_cfg->buffer_size * sizeof(lv_color_t), buff_caps); + ESP_GOTO_ON_FALSE(buf1, ESP_ERR_NO_MEM, err, TAG, "Not enough memory for LVGL buffer (buf1) allocation!"); + if (disp_cfg->double_buffer) { + buf2 = heap_caps_malloc(disp_cfg->buffer_size * sizeof(lv_color_t), buff_caps); + ESP_GOTO_ON_FALSE(buf2, ESP_ERR_NO_MEM, err, TAG, "Not enough memory for LVGL buffer (buf2) allocation!"); + } } lv_disp_draw_buf_t *disp_buf = malloc(sizeof(lv_disp_draw_buf_t)); ESP_GOTO_ON_FALSE(disp_buf, ESP_ERR_NO_MEM, err, TAG, "Not enough memory for LVGL display buffer allocation!"); @@ -142,12 +157,35 @@ lv_disp_t *lvgl_port_add_disp(const lvgl_port_display_cfg_t *disp_cfg) esp_lcd_dpi_panel_register_event_callbacks(disp_ctx->panel_handle, &cbs, &disp_ctx->disp_drv); #else ESP_GOTO_ON_FALSE(false, ESP_ERR_NOT_SUPPORTED, err, TAG, "MIPI-DSI is supported only on ESP32P4 and from IDF 5.3!"); +#endif + } else if (disp_cfg->RGB) { + disp_ctx->flags.flush_byself = true; +#if (CONFIG_IDF_TARGET_ESP32S3 && ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)) + /* Register done callback */ + const esp_lcd_rgb_panel_event_callbacks_t vsync_cbs = { + .on_vsync = lvgl_port_flush_vsync_ready_callback, + }; + + const esp_lcd_rgb_panel_event_callbacks_t bb_cbs = { +#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 1, 2) + .on_bounce_frame_finish = lvgl_port_flush_vsync_ready_callback, +#endif + }; + + if (disp_cfg->flags.bb_mode && (ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 1, 2))) { + ESP_ERROR_CHECK(esp_lcd_rgb_panel_register_event_callbacks(disp_ctx->panel_handle, &bb_cbs, &disp_ctx->disp_drv)); + } else { + ESP_ERROR_CHECK(esp_lcd_rgb_panel_register_event_callbacks(disp_ctx->panel_handle, &vsync_cbs, &disp_ctx->disp_drv)); + } +#else + ESP_GOTO_ON_FALSE(false, ESP_ERR_NOT_SUPPORTED, err, TAG, "RGB is supported only on ESP32S3 and from IDF 5.0!"); #endif } else { /* Register done callback */ const esp_lcd_panel_io_callbacks_t cbs = { .on_color_trans_done = lvgl_port_flush_io_ready_callback, }; + assert(disp_ctx->io_handle != NULL); esp_lcd_panel_io_register_event_callbacks(disp_ctx->io_handle, &cbs, &disp_ctx->disp_drv); } #endif @@ -159,6 +197,16 @@ lv_disp_t *lvgl_port_add_disp(const lvgl_port_display_cfg_t *disp_cfg) disp_ctx->disp_drv.full_refresh = 1; disp_ctx->disp_drv.set_px_cb = lvgl_port_pix_monochrome_callback; + } else if (disp_cfg->flags.direct_mode) { + /* When using direct_mode, there must be used full bufer! */ + ESP_GOTO_ON_FALSE((disp_cfg->hres * disp_cfg->vres == disp_cfg->buffer_size), ESP_ERR_INVALID_ARG, err, TAG, "Direct_mode must using full buffer!"); + + disp_ctx->disp_drv.direct_mode = 1; + } else if (disp_cfg->flags.full_refresh) { + /* When using full_refresh, there must be used full bufer! */ + ESP_GOTO_ON_FALSE((disp_cfg->hres * disp_cfg->vres == disp_cfg->buffer_size), ESP_ERR_INVALID_ARG, err, TAG, "Full_fresh must using full buffer!"); + + disp_ctx->disp_drv.full_refresh = 1; } disp = lv_disp_drv_register(&disp_ctx->disp_drv); @@ -260,6 +308,18 @@ static bool lvgl_port_flush_panel_ready_callback(esp_lcd_panel_handle_t panel_io return false; } #endif +#if CONFIG_IDF_TARGET_ESP32S3 && ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0) +static bool lvgl_port_flush_vsync_ready_callback(esp_lcd_panel_handle_t panel_io, const esp_lcd_rgb_panel_event_data_t *edata, void *user_ctx) +{ + BaseType_t need_yield = pdFALSE; + + lv_display_t *disp_drv = (lv_display_t *)user_ctx; + assert(disp_drv != NULL); + need_yield = lvgl_port_task_notify(); + + return (need_yield == pdTRUE); +} +#endif #endif static void lvgl_port_flush_callback(lv_disp_drv_t *drv, const lv_area_t *area, lv_color_t *color_map) @@ -291,7 +351,21 @@ static void lvgl_port_flush_callback(lv_disp_drv_t *drv, const lv_area_t *area, lv_color_t *to = NULL; if (0 == disp_ctx->trans_size) { - esp_lcd_panel_draw_bitmap(disp_ctx->panel_handle, x_start, y_start, x_end + 1, y_end + 1, color_map); + if (drv->direct_mode || drv->full_refresh) { + if (lv_disp_flush_is_last(drv)) { + /* If the interface is I80 or SPI, this step cannot be used for drawing. */ + esp_lcd_panel_draw_bitmap(disp_ctx->panel_handle, x_start, y_start, x_end + 1, y_end + 1, color_map); + /* Waiting for the last frame buffer to complete transmission */ + ulTaskNotifyValueClear(NULL, ULONG_MAX); + ulTaskNotifyTake(pdTRUE, portMAX_DELAY); + } + } else { + esp_lcd_panel_draw_bitmap(disp_ctx->panel_handle, x_start, y_start, x_end + 1, y_end + 1, color_map); + } + + if (disp_ctx->flags.flush_byself) { + lv_disp_flush_ready(drv); + } } else { y_start_tmp = y_start; max_line = ((disp_ctx->trans_size / width) > height) ? (height) : (disp_ctx->trans_size / width); diff --git a/components/esp_lvgl_port/src/lvgl8/esp_lvgl_port_usbhid.c b/components/esp_lvgl_port/src/lvgl8/esp_lvgl_port_usbhid.c index 98ff3623..c504e182 100644 --- a/components/esp_lvgl_port/src/lvgl8/esp_lvgl_port_usbhid.c +++ b/components/esp_lvgl_port/src/lvgl8/esp_lvgl_port_usbhid.c @@ -358,10 +358,10 @@ static void lvgl_port_usb_hid_task(void *arg) .callback_arg = ctx }; - ESP_ERROR_CHECK( hid_host_device_open(hid_device_handle, &dev_config) ); - ESP_ERROR_CHECK( hid_class_request_set_idle(hid_device_handle, 0, 0) ); - ESP_ERROR_CHECK( hid_class_request_set_protocol(hid_device_handle, HID_REPORT_PROTOCOL_BOOT) ); - ESP_ERROR_CHECK( hid_host_device_start(hid_device_handle) ); + ESP_ERROR_CHECK(hid_host_device_open(hid_device_handle, &dev_config)); + ESP_ERROR_CHECK(hid_class_request_set_idle(hid_device_handle, 0, 0)); + ESP_ERROR_CHECK(hid_class_request_set_protocol(hid_device_handle, HID_REPORT_PROTOCOL_BOOT)); + ESP_ERROR_CHECK(hid_host_device_start(hid_device_handle)); } break; default: diff --git a/components/esp_lvgl_port/src/lvgl9/esp_lvgl_port.c b/components/esp_lvgl_port/src/lvgl9/esp_lvgl_port.c index 4e65e5cb..e4ea5750 100644 --- a/components/esp_lvgl_port/src/lvgl9/esp_lvgl_port.c +++ b/components/esp_lvgl_port/src/lvgl9/esp_lvgl_port.c @@ -33,6 +33,7 @@ typedef struct lvgl_port_ctx_s { * Local variables *******************************************************************************/ static lvgl_port_ctx_t lvgl_port_ctx; +static TaskHandle_t lvgl_port_task_handle; /******************************************************************************* * Function definitions @@ -68,9 +69,9 @@ esp_err_t lvgl_port_init(const lvgl_port_cfg_t *cfg) BaseType_t res; if (cfg->task_affinity < 0) { - res = xTaskCreate(lvgl_port_task, "LVGL task", cfg->task_stack, NULL, cfg->task_priority, NULL); + res = xTaskCreate(lvgl_port_task, "LVGL task", cfg->task_stack, NULL, cfg->task_priority, &lvgl_port_task_handle); } else { - res = xTaskCreatePinnedToCore(lvgl_port_task, "LVGL task", cfg->task_stack, NULL, cfg->task_priority, NULL, cfg->task_affinity); + res = xTaskCreatePinnedToCore(lvgl_port_task, "LVGL task", cfg->task_stack, NULL, cfg->task_priority, &lvgl_port_task_handle, cfg->task_affinity); } ESP_GOTO_ON_FALSE(res == pdPASS, ESP_FAIL, err, TAG, "Create LVGL task fail!"); @@ -139,6 +140,15 @@ void lvgl_port_unlock(void) xSemaphoreGiveRecursive(lvgl_port_ctx.lvgl_mux); } +IRAM_ATTR bool lvgl_port_task_notify(void) +{ + BaseType_t need_yield = pdFALSE; + + // Notify that the current RGB frame buffer has been transmitted + xTaskNotifyFromISR(lvgl_port_task_handle, ULONG_MAX, eNoAction, &need_yield); + + return (need_yield == pdTRUE); +} /******************************************************************************* * Private functions *******************************************************************************/ diff --git a/components/esp_lvgl_port/src/lvgl9/esp_lvgl_port_disp.c b/components/esp_lvgl_port/src/lvgl9/esp_lvgl_port_disp.c index 84bdeffa..5e8fc18a 100644 --- a/components/esp_lvgl_port/src/lvgl9/esp_lvgl_port_disp.c +++ b/components/esp_lvgl_port/src/lvgl9/esp_lvgl_port_disp.c @@ -15,6 +15,9 @@ #if (CONFIG_IDF_TARGET_ESP32P4 && ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 3, 0)) #include "esp_lcd_mipi_dsi.h" #endif +#if CONFIG_IDF_TARGET_ESP32S3 && ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0) +#include "esp_lcd_panel_rgb.h" +#endif #if (ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(4, 4, 4)) || (ESP_IDF_VERSION == ESP_IDF_VERSION_VAL(5, 0, 0)) #define LVGL_PORT_HANDLE_FLUSH_READY 0 @@ -38,6 +41,9 @@ typedef struct { struct { unsigned int monochrome: 1; /* True, if display is monochrome and using 1bit for 1px */ unsigned int swap_bytes: 1; /* Swap bytes in RGB656 (16-bit) before send to LCD driver */ + unsigned int full_refresh: 1; /*!< 1: Always make the whole screen redrawn */ + unsigned int direct_mode: 1; /*!< 1: Use screen-sized buffers and draw to absolute coordinates */ + unsigned int flush_byself: 1; } flags; } lvgl_port_display_ctx_t; @@ -50,6 +56,9 @@ static bool lvgl_port_flush_io_ready_callback(esp_lcd_panel_io_handle_t panel_io #if (CONFIG_IDF_TARGET_ESP32P4 && ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 3, 0)) static bool lvgl_port_flush_panel_ready_callback(esp_lcd_panel_handle_t panel_io, esp_lcd_dpi_panel_event_data_t *edata, void *user_ctx); #endif +#if CONFIG_IDF_TARGET_ESP32S3 && ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0) +static bool lvgl_port_flush_vsync_ready_callback(esp_lcd_panel_handle_t panel_io, const esp_lcd_rgb_panel_event_data_t *edata, void *user_ctx); +#endif #endif static void lvgl_port_flush_callback(lv_display_t *drv, const lv_area_t *area, uint8_t *color_map); static void lvgl_port_disp_size_update_callback(lv_event_t *e); @@ -65,7 +74,6 @@ lv_display_t *lvgl_port_add_disp(const lvgl_port_display_cfg_t *disp_cfg) lv_color16_t *buf1 = NULL; lv_color16_t *buf2 = NULL; assert(disp_cfg != NULL); - assert(disp_cfg->io_handle != NULL); assert(disp_cfg->panel_handle != NULL); assert(disp_cfg->buffer_size > 0); assert(disp_cfg->hres > 0); @@ -74,6 +82,7 @@ lv_display_t *lvgl_port_add_disp(const lvgl_port_display_cfg_t *disp_cfg) /* Display context */ lvgl_port_display_ctx_t *disp_ctx = malloc(sizeof(lvgl_port_display_ctx_t)); ESP_GOTO_ON_FALSE(disp_ctx, ESP_ERR_NO_MEM, err, TAG, "Not enough memory for display context allocation!"); + memset(disp_ctx, 0, sizeof(lvgl_port_display_ctx_t)); disp_ctx->io_handle = disp_cfg->io_handle; disp_ctx->panel_handle = disp_cfg->panel_handle; disp_ctx->control_handle = disp_cfg->control_handle; @@ -82,22 +91,27 @@ lv_display_t *lvgl_port_add_disp(const lvgl_port_display_cfg_t *disp_cfg) disp_ctx->rotation.mirror_y = disp_cfg->rotation.mirror_y; disp_ctx->flags.swap_bytes = disp_cfg->flags.swap_bytes; - uint32_t buff_caps = MALLOC_CAP_DEFAULT; - if (disp_cfg->flags.buff_dma && disp_cfg->flags.buff_spiram) { - ESP_GOTO_ON_FALSE(false, ESP_ERR_NOT_SUPPORTED, err, TAG, "Alloc DMA capable buffer in SPIRAM is not supported!"); - } else if (disp_cfg->flags.buff_dma) { - buff_caps = MALLOC_CAP_DMA; - } else if (disp_cfg->flags.buff_spiram) { - buff_caps = MALLOC_CAP_SPIRAM; - } + if (disp_cfg->user_buf1 || disp_cfg->user_buf2) { + buf1 = disp_cfg->user_buf1; + buf2 = disp_cfg->user_buf2; + } else { + uint32_t buff_caps = MALLOC_CAP_DEFAULT; + if (disp_cfg->flags.buff_dma && disp_cfg->flags.buff_spiram) { + ESP_GOTO_ON_FALSE(false, ESP_ERR_NOT_SUPPORTED, err, TAG, "Alloc DMA capable buffer in SPIRAM is not supported!"); + } else if (disp_cfg->flags.buff_dma) { + buff_caps = MALLOC_CAP_DMA; + } else if (disp_cfg->flags.buff_spiram) { + buff_caps = MALLOC_CAP_SPIRAM; + } - /* alloc draw buffers used by LVGL */ - /* it's recommended to choose the size of the draw buffer(s) to be at least 1/10 screen sized */ - buf1 = heap_caps_malloc(disp_cfg->buffer_size * sizeof(lv_color16_t), buff_caps); - ESP_GOTO_ON_FALSE(buf1, ESP_ERR_NO_MEM, err, TAG, "Not enough memory for LVGL buffer (buf1) allocation!"); - if (disp_cfg->double_buffer) { - buf2 = heap_caps_malloc(disp_cfg->buffer_size * sizeof(lv_color16_t), buff_caps); - ESP_GOTO_ON_FALSE(buf2, ESP_ERR_NO_MEM, err, TAG, "Not enough memory for LVGL buffer (buf2) allocation!"); + /* alloc draw buffers used by LVGL */ + /* it's recommended to choose the size of the draw buffer(s) to be at least 1/10 screen sized */ + buf1 = heap_caps_malloc(disp_cfg->buffer_size * sizeof(lv_color16_t), buff_caps); + ESP_GOTO_ON_FALSE(buf1, ESP_ERR_NO_MEM, err, TAG, "Not enough memory for LVGL buffer (buf1) allocation!"); + if (disp_cfg->double_buffer) { + buf2 = heap_caps_malloc(disp_cfg->buffer_size * sizeof(lv_color16_t), buff_caps); + ESP_GOTO_ON_FALSE(buf2, ESP_ERR_NO_MEM, err, TAG, "Not enough memory for LVGL buffer (buf2) allocation!"); + } } disp_ctx->draw_buffs[0] = buf1; @@ -114,12 +128,25 @@ lv_display_t *lvgl_port_add_disp(const lvgl_port_display_cfg_t *disp_cfg) disp_ctx->flags.monochrome = 1; //lv_display_set_color_format(disp, LV_COLOR_FORMAT_RGB565); + lv_display_set_buffers(disp, buf1, buf2, disp_cfg->buffer_size * sizeof(lv_color16_t), LV_DISPLAY_RENDER_MODE_FULL); + } else if (disp_cfg->flags.direct_mode) { + /* When using direct_mode, there must be used full bufer! */ + ESP_GOTO_ON_FALSE((disp_cfg->hres * disp_cfg->vres == disp_cfg->buffer_size), ESP_ERR_INVALID_ARG, err, TAG, "Direct_mode must using full buffer!"); + + disp_ctx->flags.direct_mode = disp_cfg->flags.direct_mode; + + lv_display_set_buffers(disp, buf1, buf2, disp_cfg->buffer_size * sizeof(lv_color16_t), LV_DISPLAY_RENDER_MODE_DIRECT); + } else if (disp_cfg->flags.full_refresh) { + /* When using full_refresh, there must be used full bufer! */ + ESP_GOTO_ON_FALSE((disp_cfg->hres * disp_cfg->vres == disp_cfg->buffer_size), ESP_ERR_INVALID_ARG, err, TAG, "Full_fresh must using full buffer!"); + + disp_ctx->flags.full_refresh = disp_cfg->flags.full_refresh; + lv_display_set_buffers(disp, buf1, buf2, disp_cfg->buffer_size * sizeof(lv_color16_t), LV_DISPLAY_RENDER_MODE_FULL); } else { lv_display_set_buffers(disp, buf1, buf2, disp_cfg->buffer_size * sizeof(lv_color16_t), LV_DISPLAY_RENDER_MODE_PARTIAL); } - lv_display_set_flush_cb(disp, lvgl_port_flush_callback); lv_display_add_event_cb(disp, lvgl_port_disp_size_update_callback, LV_EVENT_RESOLUTION_CHANGED, disp_ctx); @@ -136,12 +163,35 @@ lv_display_t *lvgl_port_add_disp(const lvgl_port_display_cfg_t *disp_cfg) esp_lcd_dpi_panel_register_event_callbacks(disp_ctx->panel_handle, &cbs, disp_ctx->disp_drv); #else ESP_GOTO_ON_FALSE(false, ESP_ERR_NOT_SUPPORTED, err, TAG, "MIPI-DSI is supported only on ESP32P4 and from IDF 5.3!"); +#endif + } else if (disp_cfg->RGB) { + disp_ctx->flags.flush_byself = true; +#if (CONFIG_IDF_TARGET_ESP32S3 && ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)) + /* Register done callback */ + const esp_lcd_rgb_panel_event_callbacks_t vsync_cbs = { + .on_vsync = lvgl_port_flush_vsync_ready_callback, + }; + + const esp_lcd_rgb_panel_event_callbacks_t bb_cbs = { +#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 1, 2) + .on_bounce_frame_finish = lvgl_port_flush_vsync_ready_callback, +#endif + }; + + if (disp_cfg->flags.bb_mode && (ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 1, 2))) { + ESP_ERROR_CHECK(esp_lcd_rgb_panel_register_event_callbacks(disp_ctx->panel_handle, &bb_cbs, &disp_ctx->disp_drv)); + } else { + ESP_ERROR_CHECK(esp_lcd_rgb_panel_register_event_callbacks(disp_ctx->panel_handle, &vsync_cbs, &disp_ctx->disp_drv)); + } +#else + ESP_GOTO_ON_FALSE(false, ESP_ERR_NOT_SUPPORTED, err, TAG, "RGB is supported only on ESP32S3 and from IDF 5.0!"); #endif } else { /* Register done callback */ const esp_lcd_panel_io_callbacks_t cbs = { .on_color_trans_done = lvgl_port_flush_io_ready_callback, }; + assert(disp_ctx->io_handle != NULL); esp_lcd_panel_io_register_event_callbacks(disp_ctx->io_handle, &cbs, disp_ctx->disp_drv); } #endif @@ -213,8 +263,37 @@ static bool lvgl_port_flush_panel_ready_callback(esp_lcd_panel_handle_t panel_io return false; } #endif +#if CONFIG_IDF_TARGET_ESP32S3 && ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0) +static bool lvgl_port_flush_vsync_ready_callback(esp_lcd_panel_handle_t panel_io, const esp_lcd_rgb_panel_event_data_t *edata, void *user_ctx) +{ + BaseType_t need_yield = pdFALSE; + + lv_display_t *disp_drv = (lv_display_t *)user_ctx; + assert(disp_drv != NULL); + need_yield = lvgl_port_task_notify(); + + return (need_yield == pdTRUE); +} +#endif #endif +static void _lvgl_port_draw_sw_rgb565_swap(uint8_t *color_map, int x1, int y1, int x2, int y2) +{ + uint16_t *from = (uint16_t *)color_map; + + for (int y = y1; y <= y2; y++) { + for (int x = x1; x <= x2; x++) { + uint16_t color = from[y * LV_HOR_RES + x]; + + uint8_t *data = (uint8_t *)&color; + uint8_t temp = data[0]; + data[0] = data[1]; + data[1] = temp; + from[y * LV_HOR_RES + x] = color; + } + } +} + static void _lvgl_port_transform_monochrome(lv_display_t *display, const lv_area_t *area, uint8_t *color_map) { uint8_t *buf = color_map; @@ -262,10 +341,19 @@ static void lvgl_port_flush_callback(lv_display_t *drv, const lv_area_t *area, u lvgl_port_display_ctx_t *disp_ctx = (lvgl_port_display_ctx_t *)lv_display_get_user_data(drv); assert(disp_ctx != NULL); + const int offsetx1 = area->x1; + const int offsetx2 = area->x2; + const int offsety1 = area->y1; + const int offsety2 = area->y2; + //TODO: try to use SPI_SWAP_DATA_RX from https://docs.espressif.com/projects/esp-idf/en/v5.1/esp32s3/api-reference/peripherals/spi_master.html#c.SPI_SWAP_DATA_TX if (disp_ctx->flags.swap_bytes) { - size_t len = lv_area_get_size(area); - lv_draw_sw_rgb565_swap(color_map, len); + if ((disp_ctx->flags.direct_mode) || (disp_ctx->flags.full_refresh)) { + _lvgl_port_draw_sw_rgb565_swap(color_map, offsetx1, offsety1, offsetx2, offsety2); + } else { + size_t len = lv_area_get_size(area); + lv_draw_sw_rgb565_swap(color_map, len); + } } /* Transfor data in buffer for monochromatic screen */ @@ -273,12 +361,21 @@ static void lvgl_port_flush_callback(lv_display_t *drv, const lv_area_t *area, u _lvgl_port_transform_monochrome(drv, area, color_map); } - const int offsetx1 = area->x1; - const int offsetx2 = area->x2; - const int offsety1 = area->y1; - const int offsety2 = area->y2; - // copy a buffer's content to a specific area of the display - esp_lcd_panel_draw_bitmap(disp_ctx->panel_handle, offsetx1, offsety1, offsetx2 + 1, offsety2 + 1, color_map); + if ((disp_ctx->flags.direct_mode || disp_ctx->flags.full_refresh)) { + if (lv_disp_flush_is_last(drv)) { + /* If the interface is I80 or SPI, this step cannot be used for drawing. */ + esp_lcd_panel_draw_bitmap(disp_ctx->panel_handle, offsetx1, offsety1, offsetx2 + 1, offsety2 + 1, color_map); + /* Waiting for the last frame buffer to complete transmission */ + ulTaskNotifyValueClear(NULL, ULONG_MAX); + ulTaskNotifyTake(pdTRUE, portMAX_DELAY); + } + } else { + esp_lcd_panel_draw_bitmap(disp_ctx->panel_handle, offsetx1, offsety1, offsetx2 + 1, offsety2 + 1, color_map); + } + + if (disp_ctx->flags.flush_byself) { + lv_disp_flush_ready(drv); + } } static void lvgl_port_disp_size_update_callback(lv_event_t *e) From 8080622bf5b27a1cce89dc795da2317a7fa72ab1 Mon Sep 17 00:00:00 2001 From: xuxin Date: Fri, 22 Mar 2024 19:36:17 +0800 Subject: [PATCH 2/2] feat(LVGL port): delete bsp_lvgl_port.c. --- .../include/bsp/esp32_s3_lcd_ev_board.h | 11 +++ .../priv_include/bsp_lvgl_port.h | 31 -------- bsp/esp32_s3_lcd_ev_board/src/bsp_lvgl_port.c | 72 ------------------- .../src/esp32_s3_lcd_ev_board.c | 59 ++++++++++++++- 4 files changed, 69 insertions(+), 104 deletions(-) delete mode 100644 bsp/esp32_s3_lcd_ev_board/priv_include/bsp_lvgl_port.h delete mode 100644 bsp/esp32_s3_lcd_ev_board/src/bsp_lvgl_port.c diff --git a/bsp/esp32_s3_lcd_ev_board/include/bsp/esp32_s3_lcd_ev_board.h b/bsp/esp32_s3_lcd_ev_board/include/bsp/esp32_s3_lcd_ev_board.h index 4b522ba5..17c2a716 100644 --- a/bsp/esp32_s3_lcd_ev_board/include/bsp/esp32_s3_lcd_ev_board.h +++ b/bsp/esp32_s3_lcd_ev_board/include/bsp/esp32_s3_lcd_ev_board.h @@ -371,6 +371,17 @@ bool bsp_display_lock(uint32_t timeout_ms); */ void bsp_display_unlock(void); +/** + * @brief Rotate screen + * + * @note Display must be already initialized by calling `bsp_display_start()` + * @note This function can't work with the anti-tearing function. Please use the `BSP_DISPLAY_LVGL_ROTATION` configuration instead. + * + * @param[in] disp: Pointer to LVGL display + * @param[in] rotation: Angle of the display rotation + */ +void bsp_display_rotate(lv_disp_t *disp, lv_disp_rot_t rotation); + /** * @brief Get display horizontal resolution * diff --git a/bsp/esp32_s3_lcd_ev_board/priv_include/bsp_lvgl_port.h b/bsp/esp32_s3_lcd_ev_board/priv_include/bsp_lvgl_port.h deleted file mode 100644 index b01f7efe..00000000 --- a/bsp/esp32_s3_lcd_ev_board/priv_include/bsp_lvgl_port.h +++ /dev/null @@ -1,31 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD - * - * SPDX-License-Identifier: Apache-2.0 - */ - -#pragma once - -#include - -#include "esp_err.h" -#include "esp_lcd_types.h" -#include "esp_lcd_touch.h" -#include "lvgl.h" - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * @brief Initialize LCD panel and LVGL portation - * - * @note This function initialize RGB LCD panel and add add disp to LVGL. - * - * @return Pointer to LVGL display or NULL when error occured - */ -lv_disp_t *bsp_display_lcd_init(); - -#ifdef __cplusplus -} -#endif diff --git a/bsp/esp32_s3_lcd_ev_board/src/bsp_lvgl_port.c b/bsp/esp32_s3_lcd_ev_board/src/bsp_lvgl_port.c deleted file mode 100644 index 9b03987b..00000000 --- a/bsp/esp32_s3_lcd_ev_board/src/bsp_lvgl_port.c +++ /dev/null @@ -1,72 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD - * - * SPDX-License-Identifier: Apache-2.0 - */ - -#include "freertos/FreeRTOS.h" -#include "freertos/semphr.h" -#include "freertos/task.h" -#include "esp_lcd_panel_ops.h" -#include "esp_lcd_panel_rgb.h" -#include "esp_timer.h" -#include "esp_lcd_touch.h" - -#include "bsp_err_check.h" -#include "bsp/display.h" -#include "bsp/esp32_s3_lcd_ev_board.h" - -static const char *TAG = "bsp_lvgl_port"; - -lv_disp_t *bsp_display_lcd_init() -{ - esp_lcd_panel_io_handle_t io_handle = NULL; - esp_lcd_panel_handle_t panel_handle = NULL; // LCD panel handle - - bsp_display_config_t disp_config = { 0 }; - - BSP_ERROR_CHECK_RETURN_NULL(bsp_display_new(&disp_config, &panel_handle, &io_handle)); - - // alloc draw buffers used by LVGL - void *buf1 = NULL; - void *buf2 = NULL; - int buffer_size = 0; - - ESP_LOGD(TAG, "Malloc memory for LVGL buffer"); -#ifndef CONFIG_BSP_DISPLAY_LVGL_AVOID_TEAR - // Normmaly, for RGB LCD, we just use one buffer for LVGL rendering - buffer_size = BSP_LCD_H_RES * LVGL_BUFFER_HEIGHT; - buf1 = heap_caps_malloc(buffer_size * sizeof(lv_color_t), LVGL_BUFFER_MALLOC); - BSP_NULL_CHECK(buf1, NULL); - ESP_LOGI(TAG, "LVGL buffer size: %dKB", buffer_size * sizeof(lv_color_t) / 1024); -#else - // To avoid the tearing effect, we should use at least two frame buffers: one for LVGL rendering and another for RGB output - buffer_size = BSP_LCD_H_RES * BSP_LCD_V_RES; - BSP_ERROR_CHECK_RETURN_NULL(esp_lcd_rgb_panel_get_frame_buffer(panel_handle, 2, &buf1, &buf2)); -#endif /* CONFIG_BSP_DISPLAY_LVGL_AVOID_TEAR */ - - const lvgl_port_display_cfg_t disp_cfg = { - .io_handle = io_handle, - .panel_handle = panel_handle, - .buffer_size = buffer_size, - .user_buf1 = buf1, - .user_buf2 = buf2, - - .hres = BSP_LCD_H_RES, - .vres = BSP_LCD_V_RES, - - .RGB = true, - .flags = { -#if CONFIG_BSP_LCD_RGB_BOUNCE_BUFFER_MODE - .bb_mode = 1, -#endif -#if CONFIG_BSP_DISPLAY_LVGL_FULL_REFRESH - .full_refresh = 1, -#elif CONFIG_BSP_DISPLAY_LVGL_DIRECT_MODE - .direct_mode = 1, -#endif - } - }; - - return lvgl_port_add_disp(&disp_cfg); -} diff --git a/bsp/esp32_s3_lcd_ev_board/src/esp32_s3_lcd_ev_board.c b/bsp/esp32_s3_lcd_ev_board/src/esp32_s3_lcd_ev_board.c index 0a98ec03..12a07e7e 100644 --- a/bsp/esp32_s3_lcd_ev_board/src/esp32_s3_lcd_ev_board.c +++ b/bsp/esp32_s3_lcd_ev_board/src/esp32_s3_lcd_ev_board.c @@ -23,7 +23,6 @@ #include "bsp/esp32_s3_lcd_ev_board.h" #include "bsp/touch.h" #include "bsp_err_check.h" -#include "bsp_lvgl_port.h" #include "bsp_probe.h" #include "esp_lvgl_port.h" @@ -316,6 +315,59 @@ esp_err_t bsp_audio_poweramp_enable(bool enable) return ESP_OK; } +static lv_disp_t *bsp_display_lcd_init() +{ + esp_lcd_panel_io_handle_t io_handle = NULL; + esp_lcd_panel_handle_t panel_handle = NULL; // LCD panel handle + + bsp_display_config_t disp_config = { 0 }; + + BSP_ERROR_CHECK_RETURN_NULL(bsp_display_new(&disp_config, &panel_handle, &io_handle)); + + // alloc draw buffers used by LVGL + void *buf1 = NULL; + void *buf2 = NULL; + int buffer_size = 0; + + ESP_LOGD(TAG, "Malloc memory for LVGL buffer"); +#ifndef CONFIG_BSP_DISPLAY_LVGL_AVOID_TEAR + // Normmaly, for RGB LCD, we just use one buffer for LVGL rendering + buffer_size = BSP_LCD_H_RES * LVGL_BUFFER_HEIGHT; + buf1 = heap_caps_malloc(buffer_size * sizeof(lv_color_t), LVGL_BUFFER_MALLOC); + BSP_NULL_CHECK(buf1, NULL); + ESP_LOGI(TAG, "LVGL buffer size: %dKB", buffer_size * sizeof(lv_color_t) / 1024); +#else + // To avoid the tearing effect, we should use at least two frame buffers: one for LVGL rendering and another for RGB output + buffer_size = BSP_LCD_H_RES * BSP_LCD_V_RES; + BSP_ERROR_CHECK_RETURN_NULL(esp_lcd_rgb_panel_get_frame_buffer(panel_handle, 2, &buf1, &buf2)); +#endif /* CONFIG_BSP_DISPLAY_LVGL_AVOID_TEAR */ + + const lvgl_port_display_cfg_t disp_cfg = { + .io_handle = io_handle, + .panel_handle = panel_handle, + .buffer_size = buffer_size, + .user_buf1 = buf1, + .user_buf2 = buf2, + + .hres = BSP_LCD_H_RES, + .vres = BSP_LCD_V_RES, + + .RGB = true, + .flags = { +#if CONFIG_BSP_LCD_RGB_BOUNCE_BUFFER_MODE + .bb_mode = 1, +#endif +#if CONFIG_BSP_DISPLAY_LVGL_FULL_REFRESH + .full_refresh = 1, +#elif CONFIG_BSP_DISPLAY_LVGL_DIRECT_MODE + .direct_mode = 1, +#endif + } + }; + + return lvgl_port_add_disp(&disp_cfg); +} + static lv_indev_t *bsp_display_indev_init(lv_disp_t *disp) { BSP_ERROR_CHECK_RETURN_NULL(bsp_touch_new(NULL, &tp)); @@ -376,6 +428,11 @@ esp_err_t bsp_display_backlight_on(void) return bsp_display_brightness_set(100); } +void bsp_display_rotate(lv_disp_t *disp, lv_disp_rot_t rotation) +{ + ESP_LOGE(TAG, "Unable to rotate the display."); +} + bool bsp_display_lock(uint32_t timeout_ms) { return lvgl_port_lock(timeout_ms);