Merge branch 'feat/mipi_dsi_vsync_end' into 'master'

feat(mipi_dsi): add callback to notify vsync end

See merge request espressif/esp-idf!30140
pull/13651/head
morris 2024-04-12 11:28:20 +08:00
commit 7079c09c5a
6 zmienionych plików z 89 dodań i 19 usunięć

Wyświetl plik

@ -43,6 +43,7 @@ struct esp_lcd_dpi_panel_t {
esp_async_fbcpy_handle_t fbcpy_handle; // Use DMA2D to do frame buffer copy
SemaphoreHandle_t draw_sem; // A semaphore used to synchronize the draw operations when DMA2D is used
esp_lcd_dpi_panel_color_trans_done_cb_t on_color_trans_done; // Callback invoked when color data transfer has finished
esp_lcd_dpi_panel_refresh_done_cb_t on_refresh_done; // Callback invoked when one refresh operation finished (kinda like a vsync end)
void *user_ctx; // User context for the callback
};
@ -69,16 +70,29 @@ static bool async_fbcpy_done_cb(esp_async_fbcpy_handle_t mcp, esp_async_fbcpy_ev
}
IRAM_ATTR
static bool dma_list_invalid_block_cb(dw_gdma_channel_handle_t chan, const dw_gdma_break_event_data_t *event_data, void *user_data)
static bool dma_trans_done_cb(dw_gdma_channel_handle_t chan, const dw_gdma_trans_done_event_data_t *event_data, void *user_data)
{
dw_gdma_lli_handle_t lli = event_data->invalid_lli;
bool yield_needed = false;
esp_lcd_dpi_panel_t *dpi_panel = (esp_lcd_dpi_panel_t *)user_data;
uint8_t fb_index = dpi_panel->cur_fb_index;
dw_gdma_link_list_handle_t link_list = dpi_panel->link_lists[fb_index];
// restart the DMA transfer, keep refreshing the LCD
dw_gdma_block_markers_t markers = {
.is_valid = true, // mark the block as valid so that the DMA can continue the transfer
.is_valid = true,
.is_last = true,
};
dw_gdma_lli_set_block_markers(lli, markers);
// after the item is marked as valid again, tell the DMA to continue the transfer
dw_gdma_channel_continue(chan);
return false;
dw_gdma_lli_set_block_markers(dw_gdma_link_list_get_item(link_list, 0), markers);
dw_gdma_channel_use_link_list(chan, link_list);
dw_gdma_channel_enable_ctrl(chan, true);
// the DMA descriptor is large enough to carry a whole frame buffer, so this event can also be treated as a fake "vsync end"
if (dpi_panel->on_refresh_done) {
if (dpi_panel->on_refresh_done(&dpi_panel->base, NULL, dpi_panel->user_ctx)) {
yield_needed = true;
}
}
return yield_needed;
}
// Please note, errors happened in this function is just propagated to the caller
@ -109,8 +123,8 @@ static esp_err_t dpi_panel_create_dma_link(esp_lcd_dpi_panel_t *dpi_panel)
// create DMA link lists
dw_gdma_link_list_config_t link_list_config = {
.num_items = 1, // NOTE: we assume one DMA link item can carry the whole image
.link_type = DW_GDMA_LINKED_LIST_TYPE_CIRCULAR,
.num_items = DPI_PANEL_LLI_PER_FRAME,
.link_type = DW_GDMA_LINKED_LIST_TYPE_SINGLY,
};
for (int i = 0; i < dpi_panel->num_fbs; i++) {
ESP_RETURN_ON_ERROR(dw_gdma_new_link_list(&link_list_config, &link_list), TAG, "create DMA link list failed");
@ -119,9 +133,9 @@ static esp_err_t dpi_panel_create_dma_link(esp_lcd_dpi_panel_t *dpi_panel)
// register DMA ISR callbacks
dw_gdma_event_callbacks_t dsi_dma_cbs = {
.on_invalid_block = dma_list_invalid_block_cb,
.on_full_trans_done = dma_trans_done_cb,
};
ESP_RETURN_ON_ERROR(dw_gdma_channel_register_event_callbacks(dma_chan, &dsi_dma_cbs, NULL), TAG, "register DMA callbacks failed");
ESP_RETURN_ON_ERROR(dw_gdma_channel_register_event_callbacks(dma_chan, &dsi_dma_cbs, dpi_panel), TAG, "register DMA callbacks failed");
return ESP_OK;
}
@ -356,6 +370,7 @@ static esp_err_t dpi_panel_init(esp_lcd_panel_t *panel)
dw_gdma_lli_config_transfer(dw_gdma_link_list_get_item(link_list, 0), &dma_transfer_config);
dw_gdma_block_markers_t markers = {
.is_valid = true,
.is_last = true,
};
dw_gdma_lli_set_block_markers(dw_gdma_link_list_get_item(link_list, 0), markers);
}
@ -410,11 +425,6 @@ static esp_err_t dpi_panel_draw_bitmap(esp_lcd_panel_t *panel, int x_start, int
// the buffer to be flushed is still within the frame buffer, so even an unaligned address is OK
esp_cache_msync(cache_sync_start, cache_sync_size, ESP_CACHE_MSYNC_FLAG_DIR_C2M | ESP_CACHE_MSYNC_FLAG_UNALIGNED);
// update the link connections for all DMA link lists, make draw_buf_fb_index take effect automatically in the next DMA loop
for (int i = 0; i < dpi_panel->num_fbs; i++) {
dw_gdma_lli_set_next(dw_gdma_link_list_get_item(dpi_panel->link_lists[i], 0),
dw_gdma_link_list_get_item(dpi_panel->link_lists[draw_buf_fb_index], 0));
}
dpi_panel->cur_fb_index = draw_buf_fb_index;
// invoke the trans done callback
if (dpi_panel->on_color_trans_done) {
@ -506,6 +516,7 @@ esp_err_t esp_lcd_dpi_panel_register_event_callbacks(esp_lcd_panel_handle_t pane
ESP_RETURN_ON_FALSE(panel && cbs, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
esp_lcd_dpi_panel_t *dpi_panel = __containerof(panel, esp_lcd_dpi_panel_t, base);
dpi_panel->on_color_trans_done = cbs->on_color_trans_done;
dpi_panel->on_refresh_done = cbs->on_refresh_done;
dpi_panel->user_ctx = user_ctx;
return ESP_OK;

Wyświetl plik

@ -140,20 +140,35 @@ typedef struct {
} esp_lcd_dpi_panel_event_data_t;
/**
* @brief Declare the prototype of the function that will be invoked when DPI panel finishes transferring color data
* @brief A general function callback prototype for DPI panel driver
*
* @param[in] panel LCD panel handle, which is created by factory API like esp_lcd_new_panel_dpi()
* @param[in] edata DPI panel event data, fed by driver
* @param[in] user_ctx User data
* @return Whether a high priority task has been waken up by this function
*/
typedef bool (*esp_lcd_dpi_panel_color_trans_done_cb_t)(esp_lcd_panel_handle_t panel, esp_lcd_dpi_panel_event_data_t *edata, void *user_ctx);
typedef bool (*esp_lcd_dpi_panel_general_cb_t)(esp_lcd_panel_handle_t panel, esp_lcd_dpi_panel_event_data_t *edata, void *user_ctx);
/**
* @brief Declare the prototype of the function that will be invoked
* when driver finishes coping user's color buffer to frame buffer
*/
typedef esp_lcd_dpi_panel_general_cb_t esp_lcd_dpi_panel_color_trans_done_cb_t;
/**
* @brief Declare the prototype of the function that will be invoked
* when driver finishes refreshing the frame buffer to the screen
*/
typedef esp_lcd_dpi_panel_general_cb_t esp_lcd_dpi_panel_refresh_done_cb_t;
/**
* @brief Type of LCD DPI panel callbacks
*/
typedef struct {
esp_lcd_dpi_panel_color_trans_done_cb_t on_color_trans_done; /*!< Callback invoked when color data transfer has finished */
esp_lcd_dpi_panel_color_trans_done_cb_t on_color_trans_done; /*!< Invoked when user's color buffer copied to the internal frame buffer.
This is an indicator that the draw buffer can be recycled safely.
But doesn't mean the draw buffer finishes the refreshing to the screen. */
esp_lcd_dpi_panel_refresh_done_cb_t on_refresh_done; /*!< Invoked when the internal frame buffer finishes refreshing to the screen */
} esp_lcd_dpi_panel_event_callbacks_t;
/**

Wyświetl plik

@ -26,6 +26,8 @@
#define DPI_PANEL_MAX_FB_NUM 3 // maximum number of supported frame buffers for DPI panel
#define DPI_PANEL_LLI_PER_FRAME 1 // NOTE: we assume ONE DMA link item can carry the WHOLE image (1920*1080)
#ifdef __cplusplus
extern "C" {
#endif

Wyświetl plik

@ -50,6 +50,8 @@ Before testing your LCD, you also need to read your LCD spec carefully, and then
Run `idf.py menuconfig` and go to `Example Configuration`:
* Choose whether to `Use DMA2D to copy draw buffer to frame buffer` asynchronously. If you choose `No`, the draw buffer will be copied to the frame buffer synchronously by CPU.
* Choose if you want to `Monitor FPS by GPIO`. If you choose `Yes`, then you can attach an oscilloscope or logic analyzer to the GPIO pin to monitor the FPS of the display.
Please note, the actual FPS should be **double** the square wave frequency.
### Build and Flash

Wyświetl plik

@ -5,4 +5,11 @@ menu "Example Configuration"
help
Enable this option, DMA2D will be used to copy the LVGL draw buffer to the target frame buffer.
This can save some CPU time and improve the performance.
config EXAMPLE_MONITOR_FPS_BY_GPIO
bool "Monitor FPS by GPIO"
default y
help
Enable this option, you can visualize the FPS by attaching a logic analyzer to a specific GPIO.
The GPIO will output a square wave with the frequency of FPS/2.
endmenu

Wyświetl plik

@ -51,6 +51,10 @@ static const char *TAG = "example";
#define EXAMPLE_PIN_NUM_BK_LIGHT -1
#define EXAMPLE_PIN_NUM_LCD_RST -1
#if CONFIG_EXAMPLE_MONITOR_FPS_BY_GPIO
#define EXAMPLE_PIN_NUM_FPS_MONITOR 20 // Monitor the FPS by toggling the GPIO
#endif
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////// Please update the following configuration according to your Application ///////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@ -123,6 +127,17 @@ static bool example_notify_lvgl_flush_ready(esp_lcd_panel_handle_t panel, esp_lc
return false;
}
#if CONFIG_EXAMPLE_MONITOR_FPS_BY_GPIO
static bool example_monitor_fps(esp_lcd_panel_handle_t panel, esp_lcd_dpi_panel_event_data_t *edata, void *user_ctx)
{
static int io_level = 0;
// please note, the real FPS should be 2*frequency of this GPIO toggling
gpio_set_level(EXAMPLE_PIN_NUM_FPS_MONITOR, io_level);
io_level = !io_level;
return false;
}
#endif
static void example_bsp_enable_dsi_phy_power(void)
{
// Turn on the power for MIPI DSI PHY, so it can go from "No Power" state to "Shutdown" state
@ -155,8 +170,23 @@ static void example_bsp_set_lcd_backlight(uint32_t level)
#endif
}
#if CONFIG_EXAMPLE_MONITOR_FPS_BY_GPIO
static void example_bsp_init_fps_monitor_io(void)
{
gpio_config_t monitor_io_conf = {
.mode = GPIO_MODE_OUTPUT,
.pin_bit_mask = 1ULL << EXAMPLE_PIN_NUM_FPS_MONITOR,
};
ESP_ERROR_CHECK(gpio_config(&monitor_io_conf));
}
#endif
void app_main(void)
{
#if CONFIG_EXAMPLE_MONITOR_FPS_BY_GPIO
example_bsp_init_fps_monitor_io();
#endif
example_bsp_enable_dsi_phy_power();
example_bsp_init_lcd_backlight();
example_bsp_set_lcd_backlight(EXAMPLE_LCD_BK_LIGHT_OFF_LEVEL);
@ -246,6 +276,9 @@ void app_main(void)
ESP_LOGI(TAG, "Register DPI panel event callback for LVGL flush ready notification");
esp_lcd_dpi_panel_event_callbacks_t cbs = {
.on_color_trans_done = example_notify_lvgl_flush_ready,
#if CONFIG_EXAMPLE_MONITOR_FPS_BY_GPIO
.on_refresh_done = example_monitor_fps,
#endif
};
ESP_ERROR_CHECK(esp_lcd_dpi_panel_register_event_callbacks(mipi_dpi_panel, &cbs, display));