Asynchronous Color Conversion
This document introduces the Async Color Convert driver in ESP-IDF. The table of contents is as follows:
Overview
ESP32-P4 provides a DMA2D engine that can offload 2D copy and color conversion work from the CPU.
This driver is useful when your application needs to:
convert an image from one pixel format to another
copy only a window of a larger image
queue multiple conversions without doing the work on the CPU
move between RGB and UYVY formats while selecting the RGB/YUV conversion standard
The Async Color Convert driver wraps DMA2D request preparation, queueing, and completion handling into a small API that supports both:
asynchronous submission with an ISR callback
a simpler blocking API built on top of the same request path
Quick Start
If you are new to this driver, start with the simplest workflow:
Install the driver
Prepare one
async_color_convert_request_tSubmit the conversion through either the blocking or non-blocking API
Consume the converted output buffer after the conversion completes
Either submit another request or uninstall the driver when finished
The typical usage flow is:
flowchart TD
install["Install driver<br/>esp_async_color_convert_install_dma2d"] --> request["Prepare request<br/>async_color_convert_request_t"]
request --> blocking["Blocking path<br/>esp_color_convert_blocking"]
request --> nonBlocking["Non-blocking path<br/>esp_async_color_convert"]
nonBlocking --> callback["Wait for callback or task notification"]
blocking --> result["Use converted buffer"]
callback --> result
result --> request
result --> uninstall["Optional cleanup<br/>esp_async_color_convert_uninstall"]
Scenario 1: Start with One Blocking Conversion
The easiest way to learn the API is to convert one image and wait until the conversion is complete.
The following flow mirrors the peripherals/dma/async_color_convert example. It converts one embedded UYVY422 image into BGR24 and then lets the application consume the converted output:
async_color_convert_handle_t conv_hdl = NULL; // Driver handle returned by the install API
async_color_convert_config_t config = {
.backlog = 1, // One in-flight request is enough for this simple blocking example
.dma_burst_size = 16, // Start with the default burst size used by the example
};
// Create one Async Color Convert driver instance backed by DMA2D.
ESP_ERROR_CHECK(esp_async_color_convert_install_dma2d(&config, &conv_hdl));
async_color_convert_request_t req = {
.src_buffer = sample_96x64_uyvy_yuv_start, // Source image can be in flash or RAM, as long as DMA can access it
.src_stride = 96, // Source image row stride, in pixels
.src_height = 64, // Source image height, in pixels
.src_x = 0, // Start from the left edge of the source image
.src_y = 0,
.dst_buffer = dst_bgr, // Destination buffer in DMA-capable RAM
.dst_stride = 96, // Destination image row stride, in pixels
.dst_height = 64, // Destination image height, in pixels
.dst_x = 0, // Write the converted output from the top-left corner
.dst_y = 0,
.copy_width = 96, // Convert the full image width, in pixels
.copy_height = 64, // Convert the full image height, in pixels
.src_color_format = ESP_COLOR_FOURCC_UYVY, // Source pixels are UYVY422
.dst_color_format = ESP_COLOR_FOURCC_BGR24, // Destination pixels are BGR24 (used as RGB888 in this driver)
.color_conv_std = COLOR_CONV_STD_RGB_YUV_BT601, // RGB/YUV standard used for this conversion pair
};
// Wait until DMA2D finishes the conversion. -1 means wait forever.
ESP_ERROR_CHECK(esp_color_convert_blocking(conv_hdl, &req, -1));
// Release the driver after all conversions are done.
ESP_ERROR_CHECK(esp_async_color_convert_uninstall(conv_hdl));
This flow introduces the most important ideas:
esp_async_color_convert_install_dma2d()creates the driver instanceasync_color_convert_request_tdescribes the source image, destination image, and conversion windowesp_color_convert_blocking()waits until the hardware finishes the conversionesp_async_color_convert_uninstall()releases the driver resources
For the blocking API, timeout_ms = -1 means wait forever. Other timeout values are currently unsupported and return ESP_ERR_INVALID_ARG.
Understanding async_color_convert_request_t
Most application issues come from building the request incorrectly, so it is worth understanding the structure carefully.
Important
In async_color_convert_request_t, all geometry fields are measured in pixels, not bytes. This includes src_stride, src_height, src_x, src_y, dst_stride, dst_height, dst_x, dst_y, copy_width, and copy_height.
src_stride and dst_stride are row strides, not conversion widths. They describe how many pixels each full image row spans in memory, so they can be larger than copy_width when converting a window inside a larger image.
The structure describes two things at the same time:
the full source and destination images in memory
the rectangular window that should be converted
The key fields are:
async_color_convert_request_t::src_bufferBase address of the source image
async_color_convert_request_t::src_strideSource image row stride in pixels
async_color_convert_request_t::src_heightSource image height in pixels
async_color_convert_request_t::src_xandasync_color_convert_request_t::src_yTop-left corner of the source window
async_color_convert_request_t::dst_bufferBase address of the destination image
async_color_convert_request_t::dst_strideDestination image row stride in pixels
async_color_convert_request_t::dst_heightDestination image height in pixels
async_color_convert_request_t::dst_xandasync_color_convert_request_t::dst_yTop-left corner of where the converted window should be written
async_color_convert_request_t::copy_widthandasync_color_convert_request_t::copy_heightSize of the rectangle to convert
async_color_convert_request_t::src_color_formatandasync_color_convert_request_t::dst_color_formatSource and destination pixel formats
async_color_convert_request_t::color_conv_stdRGB/YUV conversion standard, used for RGB <-> YUV conversions
Both the source window and destination window must stay within the bounds of their corresponding images.
Supported Conversions
The following format pairs are currently supported by this driver:
Source format |
Destination format |
Conversion standard |
|---|---|---|
|
|
N/A |
|
|
N/A |
|
|
N/A |
|
|
N/A |
|
|
N/A |
|
|
N/A |
|
|
N/A |
|
|
N/A |
|
|
N/A |
|
|
BT.601 |
|
|
BT.709 |
|
|
BT.601 |
|
|
BT.709 |
|
|
BT.601 |
|
|
BT.709 |
Note
Always set async_color_convert_request_t::src_color_format and
async_color_convert_request_t::dst_color_format.
Set async_color_convert_request_t::color_conv_std when converting between RGB and YUV.
Scenario 2: Use the Asynchronous API with a Callback
Once the blocking flow is clear, the next step is to queue a request and let the driver notify you from interrupt context when it is finished.
static bool color_conv_done_cb(async_color_convert_handle_t conv_hdl,
async_color_convert_event_data_t *edata,
void *cb_args)
{
BaseType_t high_task_wakeup = pdFALSE; // Required by FreeRTOS when an ISR wakes a task
SemaphoreHandle_t sem = (SemaphoreHandle_t)cb_args; // User context passed at submit time
// Notify a waiting task that the conversion has finished.
xSemaphoreGiveFromISR(sem, &high_task_wakeup);
// Return true when the unblocked task should run immediately after the ISR.
return high_task_wakeup == pdTRUE;
}
async_color_convert_request_t req = {
.src_buffer = src_buf, // Source image base address
.src_stride = src_width, // Source image row stride, in pixels
.src_height = src_height,
.src_x = 0,
.src_y = 0,
.dst_buffer = dst_buf, // Destination image base address
.dst_stride = dst_width, // Destination image row stride, in pixels
.dst_height = dst_height,
.dst_x = 0,
.dst_y = 0,
.copy_width = copy_width,
.copy_height = copy_height,
.src_color_format = ESP_COLOR_FOURCC_RGB16,
.dst_color_format = ESP_COLOR_FOURCC_BGR24,
};
// Queue one asynchronous request. The callback runs later in ISR context.
ESP_ERROR_CHECK(esp_async_color_convert(conv_hdl, &req, color_conv_done_cb, sem));
// Wait in task context until the callback gives the semaphore.
xSemaphoreTake(sem, portMAX_DELAY);
The callback runs in ISR context, so keep it short and only use ISR-safe APIs such as xSemaphoreGiveFromISR or xQueueSendFromISR.
Operational Notes
Driver Configuration
The driver configuration fields are:
async_color_convert_config_t::backlogMaximum number of in-flight or pending requests.
0uses a driver default.
async_color_convert_config_t::dma_burst_sizeDMA burst size in bytes.
0uses a driver default.
async_color_convert_config_t::intr_priorityDMA2D interrupt priority.
0uses the default low/medium priority.
DMA Burst Size
The dma_burst_size affects DMA transfer efficiency:
Larger burst sizes may improve throughput
Larger burst sizes can also increase bus occupancy, so they are not always best for every workload
Common starting values are 16, 32, and 64 bytes
The best value depends on the chip's DMA controller capabilities and how much memory bandwidth is shared with other active components in the system.
Thread Safety and ISR Rules
The driver is thread-safe. Requests from different tasks are serialized through the internal queue.
esp_async_color_convert()can be called from tasks to enqueue requests.The callback type
async_color_convert_isr_cb_truns in ISR context.Do not call blocking APIs from the callback.
esp_color_convert_blocking()must not be called from ISR context.
Uninstalling the Driver
When the driver is no longer needed:
// Uninstall only after all queued conversions have completed.
ESP_ERROR_CHECK(esp_async_color_convert_uninstall(conv_hdl));
If requests are still pending, esp_async_color_convert_uninstall() returns ESP_ERR_INVALID_STATE.
Application Example
peripherals/dma/async_color_convert shows a beginner-friendly blocking conversion flow:
an embedded
.yuvimage is read directly from mapped flashDMA2D converts the image from UYVY422 to BGR24
the converted output is base64-encoded and printed to the console
pytest reconstructs the image as a PNG artifact and compares it against a golden reference image
API Reference
Async Color Convert Driver Functions
Header File
This header file can be included with:
#include "esp_async_color_convert.h"
This header file is a part of the API provided by the
esp_driver_dmacomponent. To declare that your component depends onesp_driver_dma, add the following to your CMakeLists.txt:REQUIRES esp_driver_dma
or
PRIV_REQUIRES esp_driver_dma
Functions
-
esp_err_t esp_async_color_convert_install_dma2d(const async_color_convert_config_t *config, async_color_convert_handle_t *ret_hdl)
Install async color conversion driver with the DMA2D backend.
This API allocates internal resources and creates a conversion context.
- Parameters:
config -- [in] Driver configuration
ret_hdl -- [out] Returned driver handle
- Returns:
ESP_OK: Driver installed successfully
ESP_ERR_INVALID_ARG: Invalid argument
ESP_ERR_NO_MEM: Out of memory
ESP_ERR_NOT_FOUND: Required DMA2D resource is unavailable
others: Error from lower-level DMA2D driver
-
esp_err_t esp_async_color_convert_uninstall(async_color_convert_handle_t conv_hdl)
Uninstall async color conversion driver.
- Parameters:
conv_hdl -- [in] Driver handle returned by :cpp:func:
esp_async_color_convert_install_dma2d- Returns:
ESP_OK: Driver uninstalled successfully
ESP_ERR_INVALID_ARG: Invalid argument
ESP_ERR_INVALID_STATE: There are pending requests in the queue
-
esp_err_t esp_async_color_convert(async_color_convert_handle_t conv_hdl, const async_color_convert_request_t *request, async_color_convert_isr_cb_t cb_isr, void *cb_args)
Submit an asynchronous 2D color conversion request.
The request is enqueued and completed later in DMA2D interrupt context. The callback can be NULL if no completion notification is needed.
- Parameters:
conv_hdl -- [in] Driver handle returned by :cpp:func:
esp_async_color_convert_install_dma2drequest -- [in] Color conversion request
cb_isr -- [in] ISR callback invoked on conversion completion, can be NULL
cb_args -- [in] User context passed to
cb_isr
- Returns:
ESP_OK: Request accepted
ESP_ERR_INVALID_ARG: Invalid argument or invalid request fields
ESP_ERR_INVALID_STATE: No free internal transaction slot (queue full)
others: Error from lower-level DMA2D driver
-
esp_err_t esp_color_convert_blocking(async_color_convert_handle_t conv_hdl, const async_color_convert_request_t *request, int32_t timeout_ms)
Blocking 2D color conversion API built on async request path.
Note
This API must not be called from ISR context.
- Parameters:
conv_hdl -- [in] Driver handle returned by :cpp:func:
esp_async_color_convert_install_dma2drequest -- [in] Color conversion request
timeout_ms -- [in] Timeout in milliseconds. Currently only
-1is supported, which waits forever.
- Returns:
ESP_OK: Conversion completed successfully
ESP_ERR_INVALID_ARG: Invalid argument, unsupported timeout, or invalid request fields
ESP_ERR_INVALID_STATE: Called from ISR context, or queue unavailable
others: Error from lower-level DMA2D driver
Structures
-
struct async_color_convert_event_data_t
Async color conversion event data.
-
struct async_color_convert_config_t
Async color conversion driver configuration.
-
struct async_color_convert_request_t
Async color conversion request.
Coordinates and size are in pixels.
The source and destination windows are:
source: [src_x, src_x + copy_width) x [src_y, src_y + copy_height)
destination: [dst_x, dst_x + copy_width) x [dst_y, dst_y + copy_height)
Both windows must be fully inside their corresponding image bounds.
Conversion rule is inferred from source and destination formats:
If source and destination are the same format, it performs 2D copy only.
Public Members
-
const void *src_buffer
Source picture base address
-
uint32_t src_stride
Source picture row stride in pixels
-
uint32_t src_height
Source picture height in pixels
-
uint32_t src_x
Source window x offset in pixels
-
uint32_t src_y
Source window y offset in pixels
-
void *dst_buffer
Destination picture base address
-
uint32_t dst_stride
Destination picture row stride in pixels
-
uint32_t dst_height
Destination picture height in pixels
-
uint32_t dst_x
Destination window x offset in pixels
-
uint32_t dst_y
Destination window y offset in pixels
-
uint32_t copy_width
Conversion window width in pixels
-
uint32_t copy_height
Conversion window height in pixels
-
esp_color_fourcc_t src_color_format
Source pixel format
-
esp_color_fourcc_t dst_color_format
Destination pixel format
-
color_conv_std_rgb_yuv_t color_conv_std
RGB/YUV conversion standard for RGB888<->UYVY422
Type Definitions
-
typedef struct async_color_convert_context_t *async_color_convert_handle_t
Opaque handle of async color conversion driver instance.
-
typedef bool (*async_color_convert_isr_cb_t)(async_color_convert_handle_t conv_hdl, async_color_convert_event_data_t *edata, void *cb_args)
Async color conversion callback type.
Note
This callback runs in ISR context.
- Param conv_hdl:
[in] Driver handle that produced this event
- Param edata:
[in] Event data for the completed request
- Param cb_args:
[in] User context passed to :cpp:func:
esp_async_color_convert- Return:
true: a higher-priority task was woken and a yield is requested
false: no yield request