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:

  1. Install the driver

  2. Prepare one async_color_convert_request_t

  3. Submit the conversion through either the blocking or non-blocking API

  4. Consume the converted output buffer after the conversion completes

  5. 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:

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:

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

ESP_COLOR_FOURCC_RGB16

ESP_COLOR_FOURCC_RGB16

N/A

ESP_COLOR_FOURCC_BGR24

ESP_COLOR_FOURCC_BGR24

N/A

ESP_COLOR_FOURCC_RGB24

ESP_COLOR_FOURCC_RGB24

N/A

ESP_COLOR_FOURCC_UYVY

ESP_COLOR_FOURCC_UYVY

N/A

ESP_COLOR_FOURCC_BGR24

ESP_COLOR_FOURCC_RGB24

N/A

ESP_COLOR_FOURCC_RGB24

ESP_COLOR_FOURCC_BGR24

N/A

ESP_COLOR_FOURCC_RGB16

ESP_COLOR_FOURCC_BGR24

N/A

ESP_COLOR_FOURCC_BGR24

ESP_COLOR_FOURCC_RGB16

N/A

ESP_COLOR_FOURCC_RGB24

ESP_COLOR_FOURCC_RGB16

N/A

ESP_COLOR_FOURCC_BGR24

ESP_COLOR_FOURCC_UYVY

BT.601

ESP_COLOR_FOURCC_BGR24

ESP_COLOR_FOURCC_UYVY

BT.709

ESP_COLOR_FOURCC_RGB24

ESP_COLOR_FOURCC_UYVY

BT.601

ESP_COLOR_FOURCC_RGB24

ESP_COLOR_FOURCC_UYVY

BT.709

ESP_COLOR_FOURCC_UYVY

ESP_COLOR_FOURCC_BGR24

BT.601

ESP_COLOR_FOURCC_UYVY

ESP_COLOR_FOURCC_BGR24

BT.709

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:

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

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 .yuv image is read directly from mapped flash

    • DMA2D 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

  • components/esp_driver_dma/include/esp_async_color_convert.h

  • 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_dma component. To declare that your component depends on esp_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_dma2d

  • request -- [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_dma2d

  • request -- [in] Color conversion request

  • timeout_ms -- [in] Timeout in milliseconds. Currently only -1 is 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.

Public Members

uint32_t backlog

Number of in-flight/pending requests. 0 means driver default.

size_t dma_burst_size

DMA burst length in bytes. 0 means driver default.

uint32_t intr_priority

Interrupt priority. 0 means default low/medium priority.

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


Was this page helpful?