diff --git a/bsp/esp32_p4_function_ev_board/Kconfig b/bsp/esp32_p4_function_ev_board/Kconfig index 2dd6cc4f..c69edaf0 100644 --- a/bsp/esp32_p4_function_ev_board/Kconfig +++ b/bsp/esp32_p4_function_ev_board/Kconfig @@ -26,6 +26,15 @@ menu "Board Support Package(ESP32-P4)" default 100000 endmenu + menu "I2S" + config BSP_I2S_NUM + int "I2S peripheral index" + default 1 + range 0 2 + help + ESP32P4 has three I2S peripherals, pick the one you want to use. + endmenu + menu "uSD card - Virtual File System" config BSP_SD_FORMAT_ON_MOUNT_FAIL bool "Format uSD card if mounting fails" @@ -68,6 +77,30 @@ menu "Board Support Package(ESP32-P4)" endmenu menu "Display" + config BSP_LCD_DPI_BUFFER_NUMS + int "Set number of frame buffers" + default 1 + range 1 3 + help + Let DPI LCD driver create a specified number of frame-size buffers. Only when it is set to multiple can the avoiding tearing be turned on. + + config BSP_DISPLAY_LVGL_AVOID_TEAR + bool "Avoid tearing effect" + depends on BSP_LCD_DPI_BUFFER_NUMS > 1 + default "n" + help + Avoid tearing effect through LVGL buffer mode and double frame buffers of RGB LCD. This feature is only available for RGB LCD. + + choice BSP_DISPLAY_LVGL_MODE + depends on BSP_DISPLAY_LVGL_AVOID_TEAR + prompt "Select LVGL buffer mode" + default BSP_DISPLAY_LVGL_FULL_REFRESH + config BSP_DISPLAY_LVGL_FULL_REFRESH + bool "Full refresh" + config BSP_DISPLAY_LVGL_DIRECT_MODE + bool "Direct mode" + endchoice + config BSP_DISPLAY_BRIGHTNESS_LEDC_CH int "LEDC channel index" default 1 @@ -75,7 +108,7 @@ menu "Board Support Package(ESP32-P4)" help LEDC channel is used to generate PWM signal that controls display brightness. Set LEDC index that should be used. - + choice BSP_LCD_COLOR_FORMAT prompt "Select LCD color format" default BSP_LCD_COLOR_FORMAT_RGB565 diff --git a/bsp/esp32_p4_function_ev_board/README.md b/bsp/esp32_p4_function_ev_board/README.md index bd4bbd29..45e904d8 100644 --- a/bsp/esp32_p4_function_ev_board/README.md +++ b/bsp/esp32_p4_function_ev_board/README.md @@ -25,15 +25,15 @@ Selection color format `Board Support Package(ESP32-P4) --> Display --> Select L ### Capabilities and dependencies -| Capability | Available | Component | Version | -|-------------|------------------|----------------------------------------------------------------------------------------------------------|--------------| -| DISPLAY |:heavy_check_mark:| [espressif/esp_lcd_ek79007](https://components.espressif.com/components/espressif/esp_lcd_ek79007) |>=0.1.0,<1.0.0| -| LVGL_PORT |:heavy_check_mark:| [espressif/esp_lvgl_port](https://components.espressif.com/components/espressif/esp_lvgl_port) | ^2 | -| TOUCH |:heavy_check_mark:|[espressif/esp_lcd_touch_gt911](https://components.espressif.com/components/espressif/esp_lcd_touch_gt911)| ^1 | -| BUTTONS | :x: | | | -| AUDIO | :x: | | | -|AUDIO_SPEAKER| :x: | | | -| AUDIO_MIC | :x: | | | -| SDCARD |:heavy_check_mark:| idf | >=5.3 | -| IMU | :x: | | | +| Capability | Available | Component |Version| +|-------------|------------------|----------------------------------------------------------------------------------------------------------|-------| +| DISPLAY |:heavy_check_mark:| [espressif/esp_lcd_ek79007](https://components.espressif.com/components/espressif/esp_lcd_ek79007) | 1.* | +| LVGL_PORT |:heavy_check_mark:| [espressif/esp_lvgl_port](https://components.espressif.com/components/espressif/esp_lvgl_port) | ^2 | +| TOUCH |:heavy_check_mark:|[espressif/esp_lcd_touch_gt911](https://components.espressif.com/components/espressif/esp_lcd_touch_gt911)| ^1 | +| BUTTONS | :x: | | | +| AUDIO |:heavy_check_mark:| [espressif/esp_codec_dev](https://components.espressif.com/components/espressif/esp_codec_dev) | 1.2.* | +|AUDIO_SPEAKER|:heavy_check_mark:| | | +| AUDIO_MIC |:heavy_check_mark:| | | +| SDCARD |:heavy_check_mark:| idf | >=5.3 | +| IMU | :x: | | | diff --git a/bsp/esp32_p4_function_ev_board/esp32_p4_function_ev_board.c b/bsp/esp32_p4_function_ev_board/esp32_p4_function_ev_board.c index 6d157407..3a890820 100644 --- a/bsp/esp32_p4_function_ev_board/esp32_p4_function_ev_board.c +++ b/bsp/esp32_p4_function_ev_board/esp32_p4_function_ev_board.c @@ -30,6 +30,7 @@ #include "bsp/touch.h" #include "esp_lcd_touch_gt911.h" #include "bsp_err_check.h" +#include "esp_codec_dev_defaults.h" static const char *TAG = "ESP32_P4_EV"; @@ -43,6 +44,32 @@ static TaskHandle_t usb_host_task; // USB Host Library task #if (ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 3, 0)) static i2c_master_bus_handle_t i2c_handle = NULL; // I2C Handle #endif +static i2s_chan_handle_t i2s_tx_chan = NULL; +static i2s_chan_handle_t i2s_rx_chan = NULL; +static const audio_codec_data_if_t *i2s_data_if = NULL; /* Codec data interface */ + +/* Can be used for `i2s_std_gpio_config_t` and/or `i2s_std_config_t` initialization */ +#define BSP_I2S_GPIO_CFG \ + { \ + .mclk = BSP_I2S_MCLK, \ + .bclk = BSP_I2S_SCLK, \ + .ws = BSP_I2S_LCLK, \ + .dout = BSP_I2S_DOUT, \ + .din = BSP_I2S_DSIN, \ + .invert_flags = { \ + .mclk_inv = false, \ + .bclk_inv = false, \ + .ws_inv = false, \ + }, \ + } + +/* This configuration is used by default in `bsp_extra_audio_init()` */ +#define BSP_I2S_DUPLEX_MONO_CFG(_sample_rate) \ + { \ + .clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(_sample_rate), \ + .slot_cfg = I2S_STD_PHILIP_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_16BIT, I2S_SLOT_MODE_MONO), \ + .gpio_cfg = BSP_I2S_GPIO_CFG, \ + } esp_err_t bsp_i2c_init(void) { @@ -152,6 +179,145 @@ esp_err_t bsp_spiffs_unmount(void) return esp_vfs_spiffs_unregister(CONFIG_BSP_SPIFFS_PARTITION_LABEL); } +esp_err_t bsp_audio_init(const i2s_std_config_t *i2s_config) +{ + esp_err_t ret = ESP_FAIL; + if (i2s_tx_chan && i2s_rx_chan) { + /* Audio was initialized before */ + return ESP_OK; + } + + /* Setup I2S peripheral */ + i2s_chan_config_t chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(CONFIG_BSP_I2S_NUM, I2S_ROLE_MASTER); + chan_cfg.auto_clear = true; // Auto clear the legacy data in the DMA buffer + ESP_ERROR_CHECK(i2s_new_channel(&chan_cfg, &i2s_tx_chan, &i2s_rx_chan)); + + /* Setup I2S channels */ + const i2s_std_config_t std_cfg_default = BSP_I2S_DUPLEX_MONO_CFG(22050); + const i2s_std_config_t *p_i2s_cfg = &std_cfg_default; + if (i2s_config != NULL) { + p_i2s_cfg = i2s_config; + } + + if (i2s_tx_chan != NULL) { + ESP_ERROR_CHECK(i2s_channel_init_std_mode(i2s_tx_chan, p_i2s_cfg)); + ESP_ERROR_CHECK(i2s_channel_enable(i2s_tx_chan)); + } + + if (i2s_rx_chan != NULL) { + ESP_ERROR_CHECK(i2s_channel_init_std_mode(i2s_rx_chan, p_i2s_cfg)); + ESP_ERROR_CHECK(i2s_channel_enable(i2s_rx_chan)); + } + + audio_codec_i2s_cfg_t i2s_cfg = { + .port = CONFIG_BSP_I2S_NUM, + .tx_handle = i2s_tx_chan, + .rx_handle = i2s_rx_chan, + }; + i2s_data_if = audio_codec_new_i2s_data(&i2s_cfg); + + return ESP_OK; +} + +esp_codec_dev_handle_t bsp_audio_codec_speaker_init(void) +{ + if (i2s_data_if == NULL) { + /* Initilize I2C */ + ESP_ERROR_CHECK(bsp_i2c_init()); + /* Configure I2S peripheral and Power Amplifier */ + ESP_ERROR_CHECK(bsp_audio_init(NULL)); + } + assert(i2s_data_if); + + const audio_codec_gpio_if_t *gpio_if = audio_codec_new_gpio(); + + audio_codec_i2c_cfg_t i2c_cfg = { + .port = BSP_I2C_NUM, + .addr = ES8311_CODEC_DEFAULT_ADDR, + .bus_handle = i2c_handle, + }; + const audio_codec_ctrl_if_t *i2c_ctrl_if = audio_codec_new_i2c_ctrl(&i2c_cfg); + assert(i2c_ctrl_if); + + esp_codec_dev_hw_gain_t gain = { + .pa_voltage = 5.0, + .codec_dac_voltage = 3.3, + }; + + es8311_codec_cfg_t es8311_cfg = { + .ctrl_if = i2c_ctrl_if, + .gpio_if = gpio_if, + .codec_mode = ESP_CODEC_DEV_TYPE_OUT, + .pa_pin = BSP_POWER_AMP_IO, + .pa_reverted = false, + .master_mode = false, + .use_mclk = true, + .digital_mic = false, + .invert_mclk = false, + .invert_sclk = false, + .hw_gain = gain, + }; + const audio_codec_if_t *es8311_dev = es8311_codec_new(&es8311_cfg); + assert(es8311_dev); + + esp_codec_dev_cfg_t codec_dev_cfg = { + .dev_type = ESP_CODEC_DEV_TYPE_IN_OUT, + .codec_if = es8311_dev, + .data_if = i2s_data_if, + }; + return esp_codec_dev_new(&codec_dev_cfg); +} + +esp_codec_dev_handle_t bsp_audio_codec_microphone_init(void) +{ + if (i2s_data_if == NULL) { + /* Initilize I2C */ + ESP_ERROR_CHECK(bsp_i2c_init()); + /* Configure I2S peripheral and Power Amplifier */ + ESP_ERROR_CHECK(bsp_audio_init(NULL)); + } + assert(i2s_data_if); + + const audio_codec_gpio_if_t *gpio_if = audio_codec_new_gpio(); + + audio_codec_i2c_cfg_t i2c_cfg = { + .port = BSP_I2C_NUM, + .addr = ES8311_CODEC_DEFAULT_ADDR, + .bus_handle = i2c_handle, + }; + const audio_codec_ctrl_if_t *i2c_ctrl_if = audio_codec_new_i2c_ctrl(&i2c_cfg); + assert(i2c_ctrl_if); + + esp_codec_dev_hw_gain_t gain = { + .pa_voltage = 5.0, + .codec_dac_voltage = 3.3, + }; + + es8311_codec_cfg_t es8311_cfg = { + .ctrl_if = i2c_ctrl_if, + .gpio_if = gpio_if, + .codec_mode = ESP_CODEC_DEV_WORK_MODE_BOTH, + .pa_pin = BSP_POWER_AMP_IO, + .pa_reverted = false, + .master_mode = false, + .use_mclk = true, + .digital_mic = false, + .invert_mclk = false, + .invert_sclk = false, + .hw_gain = gain, + }; + + const audio_codec_if_t *es8311_dev = es8311_codec_new(&es8311_cfg); + assert(es8311_dev); + + esp_codec_dev_cfg_t codec_es8311_dev_cfg = { + .dev_type = ESP_CODEC_DEV_TYPE_IN, + .codec_if = es8311_dev, + .data_if = i2s_data_if, + }; + return esp_codec_dev_new(&codec_es8311_dev_cfg); +} + // Bit number used to represent command and parameter #define LCD_LEDC_CH CONFIG_BSP_DISPLAY_BRIGHTNESS_LEDC_CH @@ -261,21 +427,19 @@ esp_err_t bsp_display_new_with_handles(const bsp_display_config_t *config, bsp_l }; ESP_GOTO_ON_ERROR(esp_lcd_new_panel_io_dbi(mipi_dsi_bus, &dbi_config, &io), err, TAG, "New panel IO failed"); - esp_lcd_panel_handle_t ctrl_panel = NULL; esp_lcd_panel_handle_t disp_panel = NULL; #if CONFIG_BSP_LCD_TYPE_1024_600 // create EK79007 control panel ESP_LOGI(TAG, "Install EK79007 LCD control panel"); #if CONFIG_BSP_LCD_COLOR_FORMAT_RGB888 - const esp_lcd_dpi_panel_config_t dpi_config = EK79007_1024_600_PANEL_60HZ_CONFIG(LCD_COLOR_PIXEL_FORMAT_RGB888); + esp_lcd_dpi_panel_config_t dpi_config = EK79007_1024_600_PANEL_60HZ_CONFIG(LCD_COLOR_PIXEL_FORMAT_RGB888); #else - const esp_lcd_dpi_panel_config_t dpi_config = EK79007_1024_600_PANEL_60HZ_CONFIG(LCD_COLOR_PIXEL_FORMAT_RGB565); + esp_lcd_dpi_panel_config_t dpi_config = EK79007_1024_600_PANEL_60HZ_CONFIG(LCD_COLOR_PIXEL_FORMAT_RGB565); #endif + dpi_config.num_fbs = CONFIG_BSP_LCD_DPI_BUFFER_NUMS; + ek79007_vendor_config_t vendor_config = { - .flags = { - .use_mipi_interface = 1, - }, .mipi_config = { .dsi_bus = mipi_dsi_bus, .dpi_config = &dpi_config, @@ -290,58 +454,40 @@ esp_err_t bsp_display_new_with_handles(const bsp_display_config_t *config, bsp_l ESP_GOTO_ON_ERROR(esp_lcd_new_panel_ek79007(io, &lcd_dev_config, &disp_panel), err, TAG, "New LCD panel EK79007 failed"); ESP_GOTO_ON_ERROR(esp_lcd_panel_reset(disp_panel), err, TAG, "LCD panel reset failed"); ESP_GOTO_ON_ERROR(esp_lcd_panel_init(disp_panel), err, TAG, "LCD panel init failed"); - - /* Return all handles */ - ret_handles->io = io; - ret_handles->mipi_dsi_bus = mipi_dsi_bus; - ret_handles->panel = disp_panel; - ret_handles->control = NULL; - #else // create ILI9881C control panel ESP_LOGI(TAG, "Install ILI9881C LCD control panel"); - esp_lcd_panel_dev_config_t lcd_dev_config = { - .bits_per_pixel = 16, - .rgb_ele_order = BSP_LCD_COLOR_SPACE, - .reset_gpio_num = -1, - }; - ESP_GOTO_ON_ERROR(esp_lcd_new_panel_ili9881c(io, &lcd_dev_config, &ctrl_panel), err, TAG, "New LCD panel ILI9881C failed"); - ESP_GOTO_ON_ERROR(esp_lcd_panel_reset(ctrl_panel), err, TAG, "LCD panel reset failed"); - ESP_GOTO_ON_ERROR(esp_lcd_panel_init(ctrl_panel), err, TAG, "LCD panel init failed"); - ESP_GOTO_ON_ERROR(esp_lcd_panel_disp_on_off(ctrl_panel, true), err, TAG, "LCD panel ON failed"); - esp_lcd_panel_mirror(ctrl_panel, true, true); - - ESP_LOGI(TAG, "Install MIPI DSI LCD data panel"); - esp_lcd_dpi_panel_config_t dpi_config = { - .virtual_channel = 0, - .dpi_clk_src = MIPI_DSI_DPI_CLK_SRC_DEFAULT, - .dpi_clock_freq_mhz = BSP_LCD_PIXEL_CLOCK_MHZ, #if CONFIG_BSP_LCD_COLOR_FORMAT_RGB888 - .pixel_format = LCD_COLOR_PIXEL_FORMAT_RGB888, + esp_lcd_dpi_panel_config_t dpi_config = ILI9881C_800_1280_PANEL_60HZ_DPI_CONFIG(LCD_COLOR_PIXEL_FORMAT_RGB888); #else - .pixel_format = LCD_COLOR_PIXEL_FORMAT_RGB565, + esp_lcd_dpi_panel_config_t dpi_config = ILI9881C_800_1280_PANEL_60HZ_DPI_CONFIG(LCD_COLOR_PIXEL_FORMAT_RGB565); #endif - .video_timing = { - .h_size = BSP_LCD_H_RES, - .v_size = BSP_LCD_V_RES, - .hsync_back_porch = BSP_LCD_MIPI_DSI_LCD_HBP, - .hsync_pulse_width = BSP_LCD_MIPI_DSI_LCD_HSYNC, - .hsync_front_porch = BSP_LCD_MIPI_DSI_LCD_HFP, - .vsync_back_porch = BSP_LCD_MIPI_DSI_LCD_VBP, - .vsync_pulse_width = BSP_LCD_MIPI_DSI_LCD_VSYNC, - .vsync_front_porch = BSP_LCD_MIPI_DSI_LCD_VFP, + dpi_config.num_fbs = CONFIG_BSP_LCD_DPI_BUFFER_NUMS; + + ili9881c_vendor_config_t vendor_config = { + .mipi_config = { + .dsi_bus = mipi_dsi_bus, + .dpi_config = &dpi_config, + .lane_num = BSP_LCD_MIPI_DSI_LANE_NUM, }, - .flags.use_dma2d = true, }; - ESP_GOTO_ON_ERROR(esp_lcd_new_panel_dpi(mipi_dsi_bus, &dpi_config, &disp_panel), err, TAG, "New panel DPI failed"); - ESP_GOTO_ON_ERROR(esp_lcd_panel_init(disp_panel), err, TAG, "New panel DPI init failed"); + const esp_lcd_panel_dev_config_t lcd_dev_config = { + .reset_gpio_num = BSP_LCD_RST, + .rgb_ele_order = BSP_LCD_COLOR_SPACE, + .bits_per_pixel = 16, + .vendor_config = &vendor_config, + }; + ESP_GOTO_ON_ERROR(esp_lcd_new_panel_ili9881c(io, &lcd_dev_config, &disp_panel), err, TAG, "New LCD panel ILI9881C failed"); + ESP_GOTO_ON_ERROR(esp_lcd_panel_reset(disp_panel), err, TAG, "LCD panel reset failed"); + ESP_GOTO_ON_ERROR(esp_lcd_panel_init(disp_panel), err, TAG, "LCD panel init failed"); + ESP_GOTO_ON_ERROR(esp_lcd_panel_disp_on_off(disp_panel, true), err, TAG, "LCD panel ON failed"); +#endif /* Return all handles */ ret_handles->io = io; ret_handles->mipi_dsi_bus = mipi_dsi_bus; ret_handles->panel = disp_panel; - ret_handles->control = ctrl_panel; -#endif + ret_handles->control = NULL; ESP_LOGI(TAG, "Display initialized"); @@ -351,9 +497,6 @@ esp_err_t bsp_display_new_with_handles(const bsp_display_config_t *config, bsp_l if (disp_panel) { esp_lcd_panel_del(disp_panel); } - if (ctrl_panel) { - esp_lcd_panel_del(ctrl_panel); - } if (io) { esp_lcd_panel_io_del(io); } @@ -434,10 +577,25 @@ static lv_display_t *bsp_display_lcd_init(const bsp_display_cfg_t *cfg) .swap_bytes = (BSP_LCD_BIGENDIAN ? true : false), #endif .sw_rotate = cfg->flags.sw_rotate, /* Only SW rotation is supported for 90° and 270° */ +#if CONFIG_BSP_DISPLAY_LVGL_FULL_REFRESH + .full_refresh = true, +#elif CONFIG_BSP_DISPLAY_LVGL_DIRECT_MODE + .direct_mode = true, +#endif + } + }; + + const lvgl_port_display_dsi_cfg_t dpi_cfg = { + .flags = { +#if CONFIG_BSP_DISPLAY_LVGL_AVOID_TEAR + .avoid_tearing = true, +#else + .avoid_tearing = false, +#endif } }; - return lvgl_port_add_disp_dsi(&disp_cfg, NULL); + return lvgl_port_add_disp_dsi(&disp_cfg, &dpi_cfg); } static lv_indev_t *bsp_display_indev_init(lv_display_t *disp) diff --git a/bsp/esp32_p4_function_ev_board/idf_component.yml b/bsp/esp32_p4_function_ev_board/idf_component.yml index 189e4715..c8387bb3 100644 --- a/bsp/esp32_p4_function_ev_board/idf_component.yml +++ b/bsp/esp32_p4_function_ev_board/idf_component.yml @@ -1,4 +1,4 @@ -version: "4.0.1" +version: "4.1.0" description: Board Support Package (BSP) for ESP32-P4 Function EV Board (preview) url: https://github.com/espressif/esp-bsp/tree/master/bsp/esp32_p4_function_ev_board @@ -10,8 +10,8 @@ tags: dependencies: idf: ">=5.3" - esp_lcd_ili9881c: ">=0.2.0,<1.0.0" - esp_lcd_ek79007: ">=0.1.0,<1.0.0" + esp_lcd_ili9881c: "1.*" + esp_lcd_ek79007: "1.*" esp_lcd_touch_gt911: "^1" lvgl/lvgl: ">=8,<10" @@ -19,3 +19,7 @@ dependencies: version: "^2" public: true override_path: "../../components/esp_lvgl_port" + + esp_codec_dev: + version: "1.2.*" + public: true diff --git a/bsp/esp32_p4_function_ev_board/include/bsp/esp32_p4_function_ev_board.h b/bsp/esp32_p4_function_ev_board/include/bsp/esp32_p4_function_ev_board.h index e98f496b..2a444d07 100644 --- a/bsp/esp32_p4_function_ev_board/include/bsp/esp32_p4_function_ev_board.h +++ b/bsp/esp32_p4_function_ev_board/include/bsp/esp32_p4_function_ev_board.h @@ -15,8 +15,10 @@ #include "driver/gpio.h" #include "driver/i2c_master.h" #include "driver/sdmmc_host.h" +#include "driver/i2s_std.h" #include "bsp/config.h" #include "bsp/display.h" +#include "esp_codec_dev.h" #include "sdkconfig.h" #if (BSP_CONFIG_NO_GRAPHIC_LIB == 0) @@ -31,9 +33,9 @@ #define BSP_CAPS_DISPLAY 1 #define BSP_CAPS_TOUCH 1 #define BSP_CAPS_BUTTONS 0 -#define BSP_CAPS_AUDIO 0 -#define BSP_CAPS_AUDIO_SPEAKER 0 -#define BSP_CAPS_AUDIO_MIC 0 +#define BSP_CAPS_AUDIO 1 +#define BSP_CAPS_AUDIO_SPEAKER 1 +#define BSP_CAPS_AUDIO_MIC 1 #define BSP_CAPS_SDCARD 1 #define BSP_CAPS_IMU 0 @@ -44,6 +46,14 @@ #define BSP_I2C_SCL (GPIO_NUM_8) #define BSP_I2C_SDA (GPIO_NUM_7) +/* Audio */ +#define BSP_I2S_SCLK (GPIO_NUM_12) +#define BSP_I2S_MCLK (GPIO_NUM_13) +#define BSP_I2S_LCLK (GPIO_NUM_10) +#define BSP_I2S_DOUT (GPIO_NUM_9) +#define BSP_I2S_DSIN (GPIO_NUM_11) +#define BSP_POWER_AMP_IO (GPIO_NUM_53) + /* Display */ #if CONFIG_BSP_LCD_TYPE_1024_600 #define BSP_LCD_BACKLIGHT (GPIO_NUM_26) @@ -109,6 +119,55 @@ esp_err_t bsp_i2c_deinit(void); */ i2c_master_bus_handle_t bsp_i2c_get_handle(void); +/************************************************************************************************** + * + * I2S audio interface + * + * There are two devices connected to the I2S peripheral: + * - Codec ES8311 for output(playback) and input(recording) path + * + * For speaker initialization use bsp_audio_codec_speaker_init() which is inside initialize I2S with bsp_audio_init(). + * For microphone initialization use bsp_audio_codec_microphone_init() which is inside initialize I2S with bsp_audio_init(). + * After speaker or microphone initialization, use functions from esp_codec_dev for play/record audio. + * Example audio play: + * \code{.c} + * esp_codec_dev_set_out_vol(spk_codec_dev, DEFAULT_VOLUME); + * esp_codec_dev_open(spk_codec_dev, &fs); + * esp_codec_dev_write(spk_codec_dev, wav_bytes, bytes_read_from_spiffs); + * esp_codec_dev_close(spk_codec_dev); + * \endcode + **************************************************************************************************/ + +/** + * @brief Init audio + * + * @note There is no deinit audio function. Users can free audio resources by calling i2s_del_channel() + * @warning The type of i2s_config param is depending on IDF version. + * @param[in] i2s_config I2S configuration. Pass NULL to use default values (Mono, duplex, 16bit, 22050 Hz) + * @return + * - ESP_OK On success + * - ESP_ERR_NOT_SUPPORTED The communication mode is not supported on the current chip + * - ESP_ERR_INVALID_ARG NULL pointer or invalid configuration + * - ESP_ERR_NOT_FOUND No available I2S channel found + * - ESP_ERR_NO_MEM No memory for storing the channel information + * - ESP_ERR_INVALID_STATE This channel has not initialized or already started + */ +esp_err_t bsp_audio_init(const i2s_std_config_t *i2s_config); + +/** + * @brief Initialize speaker codec device + * + * @return Pointer to codec device handle or NULL when error occurred + */ +esp_codec_dev_handle_t bsp_audio_codec_speaker_init(void); + +/** + * @brief Initialize microphone codec device + * + * @return Pointer to codec device handle or NULL when error occurred + */ +esp_codec_dev_handle_t bsp_audio_codec_microphone_init(void); + /************************************************************************************************** * * SPIFFS 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 5efbbab2..ab78e06a 100644 --- a/components/esp_lvgl_port/include/esp_lvgl_port_disp.h +++ b/components/esp_lvgl_port/include/esp_lvgl_port_disp.h @@ -72,7 +72,7 @@ typedef struct { typedef struct { struct { unsigned int bb_mode: 1; /*!< 1: Use bounce buffer mode */ - unsigned int avoid_tearing: 1; /*!< 1: Use internal RGB buffers as a LVGL draw buffers to avoid tearing effect */ + unsigned int avoid_tearing: 1; /*!< 1: Use internal RGB buffers as a LVGL draw buffers to avoid tearing effect, enabling this option requires over two LCD buffers and may reduce the frame rate */ } flags; } lvgl_port_display_rgb_cfg_t; @@ -80,7 +80,9 @@ typedef struct { * @brief Configuration MIPI-DSI display structure */ typedef struct { - int dummy; + struct { + unsigned int avoid_tearing: 1; /*!< 1: Use internal MIPI-DSI buffers as a LVGL draw buffers to avoid tearing effect, enabling this option requires over two LCD buffers and may reduce the frame rate */ + } flags; } lvgl_port_display_dsi_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 3bdc4506..dff705b8 100644 --- a/components/esp_lvgl_port/src/lvgl8/esp_lvgl_port.c +++ b/components/esp_lvgl_port/src/lvgl8/esp_lvgl_port.c @@ -198,10 +198,10 @@ static void lvgl_port_task(void *arg) task_delay_ms = lv_timer_handler(); lvgl_port_unlock(); } - if ((task_delay_ms > lvgl_port_ctx.task_max_sleep_ms) || (1 == task_delay_ms)) { + if (task_delay_ms > lvgl_port_ctx.task_max_sleep_ms) { task_delay_ms = lvgl_port_ctx.task_max_sleep_ms; - } else if (task_delay_ms < 1) { - task_delay_ms = 1; + } else if (task_delay_ms < 5) { + task_delay_ms = 5; } vTaskDelay(pdMS_TO_TICKS(task_delay_ms)); } 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 46db46a9..f3661a28 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 @@ -57,11 +57,12 @@ static lv_disp_t *lvgl_port_add_disp_priv(const lvgl_port_display_cfg_t *disp_cf static lvgl_port_display_ctx_t *lvgl_port_get_display_ctx(lv_disp_t *disp); #if LVGL_PORT_HANDLE_FLUSH_READY static bool lvgl_port_flush_io_ready_callback(esp_lcd_panel_io_handle_t panel_io, esp_lcd_panel_io_event_data_t *edata, void *user_ctx); -#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); +#if (CONFIG_IDF_TARGET_ESP32S3 && ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)) +static bool lvgl_port_flush_rgb_vsync_ready_callback(esp_lcd_panel_handle_t panel_io, const esp_lcd_rgb_panel_event_data_t *edata, void *user_ctx); #endif #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); +static bool lvgl_port_flush_dpi_panel_ready_callback(esp_lcd_panel_handle_t panel_io, esp_lcd_dpi_panel_event_data_t *edata, void *user_ctx); +static bool lvgl_port_flush_dpi_vsync_ready_callback(esp_lcd_panel_handle_t panel_io, esp_lcd_dpi_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); @@ -97,7 +98,11 @@ lv_disp_t *lvgl_port_add_disp(const lvgl_port_display_cfg_t *disp_cfg) lv_display_t *lvgl_port_add_disp_dsi(const lvgl_port_display_cfg_t *disp_cfg, const lvgl_port_display_dsi_cfg_t *dsi_cfg) { - lv_disp_t *disp = lvgl_port_add_disp_priv(disp_cfg, NULL); + assert(dsi_cfg != NULL); + const lvgl_port_disp_priv_cfg_t priv_cfg = { + .avoid_tearing = dsi_cfg->flags.avoid_tearing, + }; + lv_disp_t *disp = lvgl_port_add_disp_priv(disp_cfg, &priv_cfg); if (disp != NULL) { lvgl_port_display_ctx_t *disp_ctx = lvgl_port_get_display_ctx(disp); @@ -105,9 +110,12 @@ lv_display_t *lvgl_port_add_disp_dsi(const lvgl_port_display_cfg_t *disp_cfg, co disp_ctx->disp_type = LVGL_PORT_DISP_TYPE_DSI; #if (CONFIG_IDF_TARGET_ESP32P4 && ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 3, 0)) - const esp_lcd_dpi_panel_event_callbacks_t cbs = { - .on_color_trans_done = lvgl_port_flush_panel_ready_callback, - }; + esp_lcd_dpi_panel_event_callbacks_t cbs; + if (dsi_cfg->flags.avoid_tearing) { + cbs.on_refresh_done = lvgl_port_flush_dpi_vsync_ready_callback; + } else { + cbs.on_color_trans_done = lvgl_port_flush_dpi_panel_ready_callback; + } /* Register done callback */ esp_lcd_dpi_panel_register_event_callbacks(disp_ctx->panel_handle, &cbs, &disp_ctx->disp_drv); #else @@ -134,12 +142,12 @@ lv_display_t *lvgl_port_add_disp_rgb(const lvgl_port_display_cfg_t *disp_cfg, co #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, + .on_vsync = lvgl_port_flush_rgb_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, + .on_bounce_frame_finish = lvgl_port_flush_rgb_vsync_ready_callback, #endif }; @@ -239,7 +247,14 @@ static lv_disp_t *lvgl_port_add_disp_priv(const lvgl_port_display_cfg_t *disp_cf #if CONFIG_IDF_TARGET_ESP32S3 && ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0) buffer_size = disp_cfg->hres * disp_cfg->vres; ESP_GOTO_ON_ERROR(esp_lcd_rgb_panel_get_frame_buffer(disp_cfg->panel_handle, 2, (void *)&buf1, (void *)&buf2), err, TAG, "Get RGB buffers failed"); +#elif CONFIG_IDF_TARGET_ESP32P4 && ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 3, 0) + buffer_size = disp_cfg->hres * disp_cfg->vres; + ESP_GOTO_ON_ERROR(esp_lcd_dpi_panel_get_frame_buffer(disp_cfg->panel_handle, 2, (void *)&buf1, (void *)&buf2), err, TAG, "Get RGB buffers failed"); #endif + + 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; } else { uint32_t buff_caps = MALLOC_CAP_DEFAULT; if (disp_cfg->flags.buff_dma && disp_cfg->flags.buff_spiram && (0 == disp_cfg->trans_size)) { @@ -354,7 +369,7 @@ 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) +static bool lvgl_port_flush_dpi_panel_ready_callback(esp_lcd_panel_handle_t panel_io, esp_lcd_dpi_panel_event_data_t *edata, void *user_ctx) { BaseType_t taskAwake = pdFALSE; @@ -370,16 +385,37 @@ static bool lvgl_port_flush_panel_ready_callback(esp_lcd_panel_handle_t panel_io return false; } + +static bool lvgl_port_flush_dpi_vsync_ready_callback(esp_lcd_panel_handle_t panel_io, esp_lcd_dpi_panel_event_data_t *edata, void *user_ctx) +{ + BaseType_t need_yield = pdFALSE; + + lv_disp_drv_t *disp_drv = (lv_disp_drv_t *)user_ctx; + assert(disp_drv != NULL); + lvgl_port_display_ctx_t *disp_ctx = disp_drv->user_data; + assert(disp_ctx != NULL); + + if (disp_ctx->trans_sem) { + xSemaphoreGiveFromISR(disp_ctx->trans_sem, &need_yield); + } + + return (need_yield == pdTRUE); +} #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) +#if (CONFIG_IDF_TARGET_ESP32S3 && ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)) +static bool lvgl_port_flush_rgb_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_disp_drv_t *disp_drv = (lv_disp_drv_t *)user_ctx; assert(disp_drv != NULL); - need_yield = lvgl_port_task_notify(ULONG_MAX); + lvgl_port_display_ctx_t *disp_ctx = disp_drv->user_data; + assert(disp_ctx != NULL); + + if (disp_ctx->trans_sem) { + xSemaphoreGiveFromISR(disp_ctx->trans_sem, &need_yield); + } return (need_yield == pdTRUE); } @@ -415,19 +451,19 @@ static void lvgl_port_flush_callback(lv_disp_drv_t *drv, const lv_area_t *area, lv_color_t *to = NULL; if (disp_ctx->trans_size == 0) { - if (disp_ctx->disp_type == LVGL_PORT_DISP_TYPE_RGB && (drv->direct_mode || drv->full_refresh)) { + if ((disp_ctx->disp_type == LVGL_PORT_DISP_TYPE_RGB || disp_ctx->disp_type == LVGL_PORT_DISP_TYPE_DSI) && (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); + xSemaphoreTake(disp_ctx->trans_sem, 0); + xSemaphoreTake(disp_ctx->trans_sem, 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->disp_type == LVGL_PORT_DISP_TYPE_RGB) { + if (disp_ctx->disp_type == LVGL_PORT_DISP_TYPE_RGB || (disp_ctx->disp_type == LVGL_PORT_DISP_TYPE_DSI && (drv->direct_mode || drv->full_refresh))) { lv_disp_flush_ready(drv); } } else { 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 25035311..2a1d0537 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 @@ -47,6 +47,7 @@ typedef struct { lv_color_t *draw_buffs[3]; /* Display draw buffers */ lv_display_t *disp_drv; /* LVGL display driver */ lv_display_rotation_t current_rotation; + SemaphoreHandle_t trans_sem; /* Idle transfer mutex */ 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 */ @@ -63,14 +64,14 @@ static lv_display_t *lvgl_port_add_disp_priv(const lvgl_port_display_cfg_t *disp #if LVGL_PORT_HANDLE_FLUSH_READY static bool lvgl_port_flush_io_ready_callback(esp_lcd_panel_io_handle_t panel_io, esp_lcd_panel_io_event_data_t *edata, void *user_ctx); #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); +static bool lvgl_port_flush_rgb_vsync_ready_callback(esp_lcd_panel_handle_t panel_io, const esp_lcd_rgb_panel_event_data_t *edata, void *user_ctx); #endif #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); +static bool lvgl_port_flush_dpi_panel_ready_callback(esp_lcd_panel_handle_t panel_io, esp_lcd_dpi_panel_event_data_t *edata, void *user_ctx); +static bool lvgl_port_flush_dpi_vsync_ready_callback(esp_lcd_panel_handle_t panel_io, esp_lcd_dpi_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_flush_wait_callback(lv_display_t *drv); static void lvgl_port_disp_size_update_callback(lv_event_t *e); static void lvgl_port_disp_rotation_update(lvgl_port_display_ctx_t *disp_ctx); static void lvgl_port_display_invalidate_callback(lv_event_t *e); @@ -109,8 +110,12 @@ lv_display_t *lvgl_port_add_disp(const lvgl_port_display_cfg_t *disp_cfg) lv_display_t *lvgl_port_add_disp_dsi(const lvgl_port_display_cfg_t *disp_cfg, const lvgl_port_display_dsi_cfg_t *dsi_cfg) { + assert(dsi_cfg != NULL); + const lvgl_port_disp_priv_cfg_t priv_cfg = { + .avoid_tearing = dsi_cfg->flags.avoid_tearing, + }; lvgl_port_lock(0); - lv_disp_t *disp = lvgl_port_add_disp_priv(disp_cfg, NULL); + lv_disp_t *disp = lvgl_port_add_disp_priv(disp_cfg, &priv_cfg); if (disp != NULL) { lvgl_port_display_ctx_t *disp_ctx = (lvgl_port_display_ctx_t *)lv_display_get_user_data(disp); @@ -118,15 +123,17 @@ lv_display_t *lvgl_port_add_disp_dsi(const lvgl_port_display_cfg_t *disp_cfg, co disp_ctx->disp_type = LVGL_PORT_DISP_TYPE_DSI; #if (CONFIG_IDF_TARGET_ESP32P4 && ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 3, 0)) - const esp_lcd_dpi_panel_event_callbacks_t cbs = { - .on_color_trans_done = lvgl_port_flush_panel_ready_callback, - }; + esp_lcd_dpi_panel_event_callbacks_t cbs; + if (dsi_cfg->flags.avoid_tearing) { + cbs.on_refresh_done = lvgl_port_flush_dpi_vsync_ready_callback; + } else { + cbs.on_color_trans_done = lvgl_port_flush_dpi_panel_ready_callback; + } /* Register done callback */ esp_lcd_dpi_panel_register_event_callbacks(disp_ctx->panel_handle, &cbs, disp); /* Apply rotation from initial display configuration */ lvgl_port_disp_rotation_update(disp_ctx); - #else ESP_RETURN_ON_FALSE(false, NULL, TAG, "MIPI-DSI is supported only on ESP32P4 and from IDF 5.3!"); #endif @@ -153,12 +160,12 @@ lv_display_t *lvgl_port_add_disp_rgb(const lvgl_port_display_cfg_t *disp_cfg, co #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, + .on_vsync = lvgl_port_flush_rgb_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, + .on_bounce_frame_finish = lvgl_port_flush_rgb_vsync_ready_callback, #endif }; @@ -171,11 +178,6 @@ lv_display_t *lvgl_port_add_disp_rgb(const lvgl_port_display_cfg_t *disp_cfg, co ESP_RETURN_ON_FALSE(false, NULL, TAG, "RGB is supported only on ESP32S3 and from IDF 5.0!"); #endif - /* Set wait callback */ - if (disp_ctx->flags.full_refresh || disp_ctx->flags.direct_mode) { - lv_display_set_flush_wait_cb(disp, lvgl_port_flush_wait_callback); - } - /* Apply rotation from initial display configuration */ lvgl_port_disp_rotation_update(disp_ctx); } @@ -227,6 +229,7 @@ static lv_display_t *lvgl_port_add_disp_priv(const lvgl_port_display_cfg_t *disp lv_color_t *buf1 = NULL; lv_color_t *buf2 = NULL; uint32_t buffer_size = 0; + SemaphoreHandle_t trans_sem = NULL; assert(disp_cfg != NULL); assert(disp_cfg->panel_handle != NULL); assert(disp_cfg->buffer_size > 0); @@ -284,7 +287,14 @@ static lv_display_t *lvgl_port_add_disp_priv(const lvgl_port_display_cfg_t *disp #if CONFIG_IDF_TARGET_ESP32S3 && ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0) buffer_size = disp_cfg->hres * disp_cfg->vres; ESP_GOTO_ON_ERROR(esp_lcd_rgb_panel_get_frame_buffer(disp_cfg->panel_handle, 2, (void *)&buf1, (void *)&buf2), err, TAG, "Get RGB buffers failed"); +#elif CONFIG_IDF_TARGET_ESP32P4 && ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 3, 0) + buffer_size = disp_cfg->hres * disp_cfg->vres; + ESP_GOTO_ON_ERROR(esp_lcd_dpi_panel_get_frame_buffer(disp_cfg->panel_handle, 2, (void *)&buf1, (void *)&buf2), err, TAG, "Get RGB buffers failed"); #endif + + 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; } else { /* 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 */ @@ -354,6 +364,9 @@ static lv_display_t *lvgl_port_add_disp_priv(const lvgl_port_display_cfg_t *disp if (disp_ctx) { free(disp_ctx); } + if (trans_sem) { + vSemaphoreDelete(trans_sem); + } } return disp; @@ -369,24 +382,44 @@ 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) +static bool lvgl_port_flush_dpi_panel_ready_callback(esp_lcd_panel_handle_t panel_io, esp_lcd_dpi_panel_event_data_t *edata, void *user_ctx) { lv_display_t *disp_drv = (lv_display_t *)user_ctx; assert(disp_drv != NULL); lv_disp_flush_ready(disp_drv); return false; } + +static bool lvgl_port_flush_dpi_vsync_ready_callback(esp_lcd_panel_handle_t panel_io, esp_lcd_dpi_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); + lvgl_port_display_ctx_t *disp_ctx = (lvgl_port_display_ctx_t *)lv_display_get_user_data(disp_drv); + assert(disp_ctx != NULL); + + if (disp_ctx->trans_sem) { + xSemaphoreGiveFromISR(disp_ctx->trans_sem, &need_yield); + } + + return (need_yield == pdTRUE); +} #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) +static bool lvgl_port_flush_rgb_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(ULONG_MAX); - lvgl_port_task_wake(LVGL_PORT_EVENT_DISPLAY, disp_drv); + lvgl_port_display_ctx_t *disp_ctx = (lvgl_port_display_ctx_t *)lv_display_get_user_data(disp_drv); + assert(disp_ctx != NULL); + + if (disp_ctx->trans_sem) { + xSemaphoreGiveFromISR(disp_ctx->trans_sem, &need_yield); + } return (need_yield == pdTRUE); } @@ -472,17 +505,6 @@ void lvgl_port_rotate_area(lv_display_t *disp, lv_area_t *area) } } - -static void lvgl_port_flush_wait_callback(lv_display_t *drv) -{ - assert(drv != NULL); - if (lv_disp_flush_is_last(drv)) { - /* Waiting for the last frame buffer to complete transmission */ - ulTaskNotifyValueClear(NULL, ULONG_MAX); - ulTaskNotifyTake(pdTRUE, portMAX_DELAY); - } -} - static void lvgl_port_flush_callback(lv_display_t *drv, const lv_area_t *area, uint8_t *color_map) { assert(drv != NULL); @@ -529,11 +551,19 @@ static void lvgl_port_flush_callback(lv_display_t *drv, const lv_area_t *area, u _lvgl_port_transform_monochrome(drv, area, color_map); } - /* Draw */ - esp_lcd_panel_draw_bitmap(disp_ctx->panel_handle, offsetx1, offsety1, offsetx2 + 1, offsety2 + 1, color_map); + if ((disp_ctx->disp_type == LVGL_PORT_DISP_TYPE_RGB || disp_ctx->disp_type == LVGL_PORT_DISP_TYPE_DSI) && (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, 0, 0, lv_disp_get_hor_res(drv), lv_disp_get_ver_res(drv), color_map); + /* Waiting for the last frame buffer to complete transmission */ + xSemaphoreTake(disp_ctx->trans_sem, 0); + xSemaphoreTake(disp_ctx->trans_sem, portMAX_DELAY); + } + } else { + esp_lcd_panel_draw_bitmap(disp_ctx->panel_handle, offsetx1, offsety1, offsetx2 + 1, offsety2 + 1, color_map); + } - /* Call flush ready only in RGB screen when not full refresh or direct mode */ - if (disp_ctx->disp_type == LVGL_PORT_DISP_TYPE_RGB && !disp_ctx->flags.full_refresh && !disp_ctx->flags.direct_mode) { + if (disp_ctx->disp_type == LVGL_PORT_DISP_TYPE_RGB || (disp_ctx->disp_type == LVGL_PORT_DISP_TYPE_DSI && (disp_ctx->flags.direct_mode || disp_ctx->flags.full_refresh))) { lv_disp_flush_ready(drv); } }