mirror of
https://github.com/espressif/esp-idf.git
synced 2026-09-22 13:01:16 +03:00
feat(rgb_lcd): support draw bitmap hook
This commit is contained in:
committed by
Chen Ji Chang
parent
33fb0f6daf
commit
f2cf926e0a
@@ -86,7 +86,7 @@ MIPI DSI Interfaced LCD
|
||||
|
||||
#. Configure draw bitmap hook function (optional)
|
||||
|
||||
If you want to use DMA2D to implement draw bitmap, the driver has already implemented the DMA2D draw bitmap hook function, you only need to call :func:`esp_lcd_dpi_panel_enable_dma2d` to enable it.
|
||||
If you want to accelerate 2D bitmap copy with DMA2D, the driver already provides a built-in DMA2D bitmap copy hook. You only need to call :func:`esp_lcd_dpi_panel_enable_dma2d` to enable it.
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
@@ -105,6 +105,58 @@ MIPI DSI Interfaced LCD
|
||||
};
|
||||
ESP_ERROR_CHECK(esp_lcd_dpi_panel_register_hooks(mipi_dpi_panel, &hooks, &user_ctx));
|
||||
|
||||
If the custom hook is asynchronous — for example, the hook starts a PPA transfer and returns immediately while the hardware continues processing pixels in the background — call :cpp:member:`esp_lcd_draw_bitmap_hook_data_t::on_hook_end` only after the hardware operation has actually finished. ``on_hook_end`` is implemented and filled into ``hook_data`` by the DPI panel driver; you do not need to write it yourself, only call it when the asynchronous operation completes to notify the driver that the draw transaction is finished. If a color transfer done callback has been registered, it is invoked at that time as well.
|
||||
|
||||
The panel driver does not wait for a previous draw to finish; synchronization is the custom hook's responsibility. The simplest approach is to serialize draws (do not start a new one before the previous one completes), as shown in the example below. If you want to submit multiple draws concurrently using an accelerator transaction queue such as PPA's, keep a separate ``hook_data`` copy per transaction, keep the source buffer valid until hardware completion, and handle overlapping destination regions carefully.
|
||||
|
||||
The following snippet shows an asynchronous custom hook: it starts PPA and returns immediately, then calls ``on_hook_end`` from the PPA completion callback. The example serializes draws with a semaphore so only one draw is in flight at a time:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
typedef struct {
|
||||
esp_lcd_panel_handle_t panel;
|
||||
esp_lcd_draw_bitmap_hook_data_t hook_data;
|
||||
SemaphoreHandle_t draw_sem;
|
||||
// ... other fields, e.g. ppa_client_handle_t
|
||||
} draw_bitmap_hook_ctx_t;
|
||||
|
||||
static bool ppa_trans_done_callback(ppa_client_handle_t ppa_client, ppa_event_data_t *edata, void *user_ctx)
|
||||
{
|
||||
draw_bitmap_hook_ctx_t *ctx = (draw_bitmap_hook_ctx_t *)user_ctx;
|
||||
bool need_yield = false;
|
||||
|
||||
// on_hook_end is provided by the DPI panel driver; just call it when done
|
||||
if (ctx->hook_data.on_hook_end) {
|
||||
if (ctx->hook_data.on_hook_end(ctx->panel)) {
|
||||
need_yield = true;
|
||||
}
|
||||
}
|
||||
|
||||
BaseType_t task_woken = pdFALSE;
|
||||
xSemaphoreGiveFromISR(ctx->draw_sem, &task_woken);
|
||||
if (task_woken == pdTRUE) {
|
||||
need_yield = true;
|
||||
}
|
||||
return need_yield;
|
||||
}
|
||||
|
||||
static esp_err_t custom_draw_bitmap_hook(esp_lcd_panel_handle_t panel,
|
||||
const esp_lcd_draw_bitmap_hook_data_t *hook_data,
|
||||
void *user_ctx)
|
||||
{
|
||||
draw_bitmap_hook_ctx_t *ctx = (draw_bitmap_hook_ctx_t *)user_ctx;
|
||||
|
||||
// Simplest sync: wait until the previous draw finishes
|
||||
xSemaphoreTake(ctx->draw_sem, portMAX_DELAY);
|
||||
|
||||
// Save hook_data so the completion callback can call on_hook_end later
|
||||
ctx->hook_data = *hook_data;
|
||||
|
||||
// Start an asynchronous PPA transfer, then return immediately
|
||||
// ppa_do_scale_rotate_mirror(...);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
Power Supply for MIPI DPHY
|
||||
--------------------------
|
||||
|
||||
|
||||
@@ -312,6 +312,76 @@ This mode is similar to :ref:`bounce_buffer_with_single_psram_frame_buffer`, but
|
||||
In a well-designed embedded application, situations where the DMA cannot deliver data as fast as the LCD consumes it should be avoided. However, such scenarios can theoretically occur. In the {IDF_TARGET_NAME} hardware, this results in the LCD outputting dummy bytes while the DMA waits for data. If the DMA were to run in a continuous stream, it could cause a desynchronization between the LCD address from which the DMA reads data and the address from which the LCD peripheral outputs data, leading to a **permanently** shifted image.
|
||||
To prevent this, you can either enable the :menuitem:`CONFIG_LCD_RGB_RESTART_IN_VSYNC` option, allowing the driver to automatically restart the DMA during the VBlank interrupt, or call :cpp:func:`esp_lcd_rgb_panel_restart` to manually restart the DMA. Note that :cpp:func:`esp_lcd_rgb_panel_restart` does not restart the DMA immediately; instead, the DMA will be restarted at the next VSYNC event.
|
||||
|
||||
Draw Bitmap Hook Function
|
||||
-------------------------
|
||||
|
||||
If you want to accelerate 2D bitmap copy with DMA2D, the driver already provides a built-in DMA2D bitmap copy hook. You only need to call :cpp:func:`esp_lcd_rgb_panel_enable_dma2d` to enable it.
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
ESP_ERROR_CHECK(esp_lcd_rgb_panel_enable_dma2d(panel_handle));
|
||||
|
||||
If you need more advanced applications, you can add a custom hook for draw bitmap, such as using PPA to implement rotation, scaling, etc.
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
esp_lcd_panel_hooks_t hooks = {
|
||||
.draw_bitmap_hook = custom_draw_bitmap_hook,
|
||||
};
|
||||
ESP_ERROR_CHECK(esp_lcd_rgb_panel_register_hooks(panel_handle, &hooks, &user_ctx));
|
||||
|
||||
If the custom hook is asynchronous — for example, the hook starts a PPA transfer and returns immediately while the hardware continues processing pixels in the background — call :cpp:member:`esp_lcd_draw_bitmap_hook_data_t::on_hook_end` only after the hardware operation has actually finished. ``on_hook_end`` is implemented and filled into ``hook_data`` by the RGB panel driver; you do not need to write it yourself, only call it when the asynchronous operation completes to notify the driver that the draw transaction is finished. If a color transfer done callback has been registered, it is invoked at that time as well.
|
||||
|
||||
The panel driver does not wait for a previous draw to finish; synchronization is the custom hook's responsibility. The simplest approach is to serialize draws (do not start a new one before the previous one completes), as shown in the example below. If you want to submit multiple draws concurrently using an accelerator transaction queue such as PPA's, keep a separate ``hook_data`` copy per transaction, keep the source buffer valid until hardware completion, and handle overlapping destination regions carefully.
|
||||
|
||||
The following snippet shows an asynchronous custom hook: it starts PPA and returns immediately, then calls ``on_hook_end`` from the PPA completion callback. The example serializes draws with a semaphore so only one draw is in flight at a time:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
typedef struct {
|
||||
esp_lcd_panel_handle_t panel;
|
||||
esp_lcd_draw_bitmap_hook_data_t hook_data;
|
||||
SemaphoreHandle_t draw_sem;
|
||||
// ... other fields, e.g. ppa_client_handle_t
|
||||
} draw_bitmap_hook_ctx_t;
|
||||
|
||||
static bool ppa_trans_done_callback(ppa_client_handle_t ppa_client, ppa_event_data_t *edata, void *user_ctx)
|
||||
{
|
||||
draw_bitmap_hook_ctx_t *ctx = (draw_bitmap_hook_ctx_t *)user_ctx;
|
||||
bool need_yield = false;
|
||||
|
||||
// on_hook_end is provided by the RGB panel driver; just call it when done
|
||||
if (ctx->hook_data.on_hook_end) {
|
||||
if (ctx->hook_data.on_hook_end(ctx->panel)) {
|
||||
need_yield = true;
|
||||
}
|
||||
}
|
||||
|
||||
BaseType_t task_woken = pdFALSE;
|
||||
xSemaphoreGiveFromISR(ctx->draw_sem, &task_woken);
|
||||
if (task_woken == pdTRUE) {
|
||||
need_yield = true;
|
||||
}
|
||||
return need_yield;
|
||||
}
|
||||
|
||||
static esp_err_t custom_draw_bitmap_hook(esp_lcd_panel_handle_t panel,
|
||||
const esp_lcd_draw_bitmap_hook_data_t *hook_data,
|
||||
void *user_ctx)
|
||||
{
|
||||
draw_bitmap_hook_ctx_t *ctx = (draw_bitmap_hook_ctx_t *)user_ctx;
|
||||
|
||||
// Simplest sync: wait until the previous draw finishes
|
||||
xSemaphoreTake(ctx->draw_sem, portMAX_DELAY);
|
||||
|
||||
// Save hook_data so the completion callback can call on_hook_end later
|
||||
ctx->hook_data = *hook_data;
|
||||
|
||||
// Start an asynchronous PPA transfer, then return immediately
|
||||
// ppa_do_scale_rotate_mirror(...);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
API Reference
|
||||
-------------
|
||||
|
||||
|
||||
@@ -86,7 +86,7 @@ MIPI DSI 接口的 LCD
|
||||
|
||||
#. 配置绘制位图钩子函数(可选)
|
||||
|
||||
若想使用 DMA2D 实现绘制位图,驱动程序内部已实现 DMA2D 绘制位图的钩子函数,用户只需调用 :func:`esp_lcd_dpi_panel_enable_dma2d` 即可。
|
||||
若想使用 DMA2D 加速位图的复制,驱动程序内部已实现基于 DMA2D 的位图复制钩子函数,用户只需调用 :func:`esp_lcd_dpi_panel_enable_dma2d` 即可。
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
@@ -105,6 +105,58 @@ MIPI DSI 接口的 LCD
|
||||
};
|
||||
ESP_ERROR_CHECK(esp_lcd_dpi_panel_register_hooks(mipi_dpi_panel, &hooks, &user_ctx));
|
||||
|
||||
如果自定义钩子是异步的——例如钩子函数启动 PPA 后立即返回,真正的像素处理仍由硬件在后台执行——则必须在硬件操作真正完成后再调用 :cpp:member:`esp_lcd_draw_bitmap_hook_data_t::on_hook_end`。该回调由 DPI 面板驱动实现并填入 ``hook_data``,用户无需自行编写,只需在异步操作完成时调用它,以通知驱动结束本次绘制事务;若已注册颜色传输完成回调,也会在此时被调用。
|
||||
|
||||
面板驱动本身不会等待上一笔绘制结束,同步由自定义钩子自行负责。最简单的做法是串行执行(上一笔完成前不启动下一笔),如下面示例所示。若希望利用 PPA 等加速器的事务队列并发提交多笔绘制,则需要为每笔事务单独保存 ``hook_data``、保证源 buffer 在硬件完成前有效,并处理好目标区域重叠等问题。
|
||||
|
||||
下面是一个异步自定义钩子的示意代码:钩子启动 PPA 后立即返回,并在 PPA 完成回调中调用 ``on_hook_end``。示例采用串行方式,用信号量保证同一时间只有一笔绘制在进行:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
typedef struct {
|
||||
esp_lcd_panel_handle_t panel;
|
||||
esp_lcd_draw_bitmap_hook_data_t hook_data;
|
||||
SemaphoreHandle_t draw_sem;
|
||||
// ... 其他字段,例如 ppa_client_handle_t
|
||||
} draw_bitmap_hook_ctx_t;
|
||||
|
||||
static bool ppa_trans_done_callback(ppa_client_handle_t ppa_client, ppa_event_data_t *edata, void *user_ctx)
|
||||
{
|
||||
draw_bitmap_hook_ctx_t *ctx = (draw_bitmap_hook_ctx_t *)user_ctx;
|
||||
bool need_yield = false;
|
||||
|
||||
// on_hook_end 由 DPI 面板驱动提供,操作完成后直接调用即可
|
||||
if (ctx->hook_data.on_hook_end) {
|
||||
if (ctx->hook_data.on_hook_end(ctx->panel)) {
|
||||
need_yield = true;
|
||||
}
|
||||
}
|
||||
|
||||
BaseType_t task_woken = pdFALSE;
|
||||
xSemaphoreGiveFromISR(ctx->draw_sem, &task_woken);
|
||||
if (task_woken == pdTRUE) {
|
||||
need_yield = true;
|
||||
}
|
||||
return need_yield;
|
||||
}
|
||||
|
||||
static esp_err_t custom_draw_bitmap_hook(esp_lcd_panel_handle_t panel,
|
||||
const esp_lcd_draw_bitmap_hook_data_t *hook_data,
|
||||
void *user_ctx)
|
||||
{
|
||||
draw_bitmap_hook_ctx_t *ctx = (draw_bitmap_hook_ctx_t *)user_ctx;
|
||||
|
||||
// 最简单的同步方式:等待上一笔完成后再启动本笔
|
||||
xSemaphoreTake(ctx->draw_sem, portMAX_DELAY);
|
||||
|
||||
// 保存 hook_data,供完成回调稍后调用 on_hook_end
|
||||
ctx->hook_data = *hook_data;
|
||||
|
||||
// 启动异步 PPA 传输后立即返回
|
||||
// ppa_do_scale_rotate_mirror(...);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
关于 MIPI DPHY 的供电
|
||||
---------------------
|
||||
|
||||
|
||||
@@ -312,6 +312,76 @@ bounce buffer 与 PSRAM frame buffer
|
||||
虽说在设计良好的嵌入式应用程序中, DMA 传递数据的速度不应该赶不上 LCD 读取数据的速度。但理论上,此种情况还是有可能出现的。在 {IDF_TARGET_NAME} 的硬件中,这种情况会导致 LCD 在 DMA 等待数据时单纯输出 dummy 字节。若以流式传输运行 DMA,则 DMA 会将读取到的数据传输到某个 LCD 地址,同时 LCD 也会将数据输出到某个 LCD 地址,但上述两个地址可能会不同步,导致图像 **永久** 偏移。
|
||||
为防止类似情况发生,可以启用 :menuitem:`CONFIG_LCD_RGB_RESTART_IN_VSYNC` 选项,以便驱动程序在 VBlank 中断时自动重启 DMA;或者也可以调用 :cpp:func:`esp_lcd_rgb_panel_restart`,手动重启 DMA。请注意,调用 :cpp:func:`esp_lcd_rgb_panel_restart` 不会立即重启 DMA,DMA 只会在下一个 VSYNC 事件中重启。
|
||||
|
||||
绘制位图钩子函数
|
||||
----------------
|
||||
|
||||
若想使用 DMA2D 加速位图的复制,驱动程序内部已实现基于 DMA2D 的位图复制钩子函数,用户只需调用 :cpp:func:`esp_lcd_rgb_panel_enable_dma2d` 即可。
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
ESP_ERROR_CHECK(esp_lcd_rgb_panel_enable_dma2d(panel_handle));
|
||||
|
||||
若需更高级的应用,用户可为绘制位图添加自定义钩子,例如通过 PPA 实现旋转、缩放等操作。
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
esp_lcd_panel_hooks_t hooks = {
|
||||
.draw_bitmap_hook = custom_draw_bitmap_hook,
|
||||
};
|
||||
ESP_ERROR_CHECK(esp_lcd_rgb_panel_register_hooks(panel_handle, &hooks, &user_ctx));
|
||||
|
||||
如果自定义钩子是异步的——例如钩子函数启动 PPA 后立即返回,真正的像素处理仍由硬件在后台执行——则必须在硬件操作真正完成后再调用 :cpp:member:`esp_lcd_draw_bitmap_hook_data_t::on_hook_end`。该回调由 RGB 面板驱动实现并填入 ``hook_data``,用户无需自行编写,只需在异步操作完成时调用它,以通知驱动结束本次绘制事务;若已注册颜色传输完成回调,也会在此时被调用。
|
||||
|
||||
面板驱动本身不会等待上一笔绘制结束,同步由自定义钩子自行负责。最简单的做法是串行执行(上一笔完成前不启动下一笔),如下面示例所示。若希望利用 PPA 等加速器的事务队列并发提交多笔绘制,则需要为每笔事务单独保存 ``hook_data``、保证源 buffer 在硬件完成前有效,并处理好目标区域重叠等问题。
|
||||
|
||||
下面是一个异步自定义钩子的示意代码:钩子启动 PPA 后立即返回,并在 PPA 完成回调中调用 ``on_hook_end``。示例采用串行方式,用信号量保证同一时间只有一笔绘制在进行:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
typedef struct {
|
||||
esp_lcd_panel_handle_t panel;
|
||||
esp_lcd_draw_bitmap_hook_data_t hook_data;
|
||||
SemaphoreHandle_t draw_sem;
|
||||
// ... 其他字段,例如 ppa_client_handle_t
|
||||
} draw_bitmap_hook_ctx_t;
|
||||
|
||||
static bool ppa_trans_done_callback(ppa_client_handle_t ppa_client, ppa_event_data_t *edata, void *user_ctx)
|
||||
{
|
||||
draw_bitmap_hook_ctx_t *ctx = (draw_bitmap_hook_ctx_t *)user_ctx;
|
||||
bool need_yield = false;
|
||||
|
||||
// on_hook_end 由 RGB 面板驱动提供,操作完成后直接调用即可
|
||||
if (ctx->hook_data.on_hook_end) {
|
||||
if (ctx->hook_data.on_hook_end(ctx->panel)) {
|
||||
need_yield = true;
|
||||
}
|
||||
}
|
||||
|
||||
BaseType_t task_woken = pdFALSE;
|
||||
xSemaphoreGiveFromISR(ctx->draw_sem, &task_woken);
|
||||
if (task_woken == pdTRUE) {
|
||||
need_yield = true;
|
||||
}
|
||||
return need_yield;
|
||||
}
|
||||
|
||||
static esp_err_t custom_draw_bitmap_hook(esp_lcd_panel_handle_t panel,
|
||||
const esp_lcd_draw_bitmap_hook_data_t *hook_data,
|
||||
void *user_ctx)
|
||||
{
|
||||
draw_bitmap_hook_ctx_t *ctx = (draw_bitmap_hook_ctx_t *)user_ctx;
|
||||
|
||||
// 最简单的同步方式:等待上一笔完成后再启动本笔
|
||||
xSemaphoreTake(ctx->draw_sem, portMAX_DELAY);
|
||||
|
||||
// 保存 hook_data,供完成回调稍后调用 on_hook_end
|
||||
ctx->hook_data = *hook_data;
|
||||
|
||||
// 启动异步 PPA 传输后立即返回
|
||||
// ppa_do_scale_rotate_mirror(...);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
API 参考
|
||||
--------
|
||||
|
||||
|
||||
Reference in New Issue
Block a user