kopia lustrzana https://github.com/espressif/esp-idf
Merge branch 'feat/mipi_dsi_vsync_end' into 'master'
feat(mipi_dsi): add callback to notify vsync end See merge request espressif/esp-idf!30140pull/13651/head
commit
7079c09c5a
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
||||
/**
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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));
|
||||
|
||||
|
|
Ładowanie…
Reference in New Issue