异步内存复制

[English]

概述

ESP32-S2 有一个 DMA 引擎,能够以异步方式帮助 CPU 完成内部内存复制操作。

异步 memcpy API 中封装了所有 DMA 配置和操作,esp_async_memcpy() 的签名与标准 C 库的 memcpy 函数基本相同。

DMA 允许多个内存复制请求在首个请求完成之前排队,即允许计算和内存复制的重叠。此外,通过注册事件回调函数,还可以知道内存复制请求完成的准确时间。

配置并安装驱动

安装异步 memcpy 驱动的方法取决于底层 DMA 引擎:

  • esp_async_memcpy_install_cpdma() 用于安装基于 CP DMA 引擎的异步 memcpy 驱动。

  • esp_async_memcpy_install() 是一个通用 API,用于安装带有默认 DMA 引擎的异步 memcpy 驱动。如果 SoC 具有 CP DMA 引擎,则默认 DMA 引擎为 CP DMA,否则,默认 DMA 引擎为 AHB GDMA。

async_memcpy_config_t 中设置驱动配置:

  • backlog:此项用于配置首个请求完成前可以排队的最大内存复制事务数量。如果将此字段设置为零,会应用默认值 4。

  • sram_trans_align:声明 SRAM 中数据地址和复制大小的对齐方式,如果数据没有对齐限制,则设置为零。如果设置为四的倍数值(即 4X),驱动程序将内部启用突发模式,这有利于某些和性能相关的应用程序。

  • psram_trans_align:声明 PSRAM 中数据地址和复制大小的对齐方式。如果 memcpy 的目标地址位于 PSRAM 中,用户必须给出一个有效值(只支持 16、32、64)。如果设置为零,会默认采用 16 位对齐。在内部,驱动程序会根据对齐方式来配置 DMA 访问 PSRAM 时所用的块大小。

  • flags:此项可以启用一些特殊的驱动功能。

async_memcpy_config_t config = ASYNC_MEMCPY_DEFAULT_CONFIG();
// 更新底层 DMA 引擎支持的最大数据流
config.backlog = 8;
async_memcpy_handle_t driver = NULL;
ESP_ERROR_CHECK(esp_async_memcpy_install(&config, &driver)); // 使用默认 DMA 引擎安装驱动

发送内存复制请求

使用 esp_async_memcpy() API 将内存复制请求发送到 DMA 引擎。在驱动程序成功安装后才能调用该 API。此 API 是线程安全的,因此可以从不同的任务中调用。

与 libc 版本的 memcpy 不同,你可以选择给 esp_async_memcpy() 设置一个回调函数,以便在内存复制完成时收到通知。注意,回调是在 ISR 上下文中执行的,请不要在回调中调用任何阻塞函数。

回调函数的原型是 async_memcpy_isr_cb_t。回调函数只有在借助 RTOS API(如 xSemaphoreGiveFromISR())唤醒了高优先级任务后才能返回 true。

// 回调实现,在 ISR 上下文中运行
static bool my_async_memcpy_cb(async_memcpy_handle_t mcp_hdl, async_memcpy_event_t *event, void *cb_args)
{
    SemaphoreHandle_t sem = (SemaphoreHandle_t)cb_args;
    BaseType_t high_task_wakeup = pdFALSE;
    xSemaphoreGiveFromISR(semphr, &high_task_wakeup); // 如果解锁了一些高优先级任务,则将 high_task_wakeup 设置为 pdTRUE
    return high_task_wakeup == pdTRUE;
}

// 创建一个信号量,在异步 memcpy 完成时进行报告
SemaphoreHandle_t semphr = xSemaphoreCreateBinary();

// 从用户的上下文中调用
ESP_ERROR_CHECK(esp_async_memcpy(driver_handle, to, from, copy_len, my_async_memcpy_cb, my_semaphore));
// 其他事项
xSemaphoreTake(my_semaphore, portMAX_DELAY); // 等待 buffer 复制完成

卸载驱动

使用 esp_async_memcpy_uninstall() 卸载异步 memcpy 驱动。无需在每次 memcpy 操作后手动卸载。如果你的应用程序不再需要此驱动,此 API 可以帮助回收内存和其他硬件资源。

API 参考

Header File

Functions

esp_err_t esp_async_memcpy_install_cpdma(const async_memcpy_config_t *config, async_memcpy_handle_t *mcp)

Install async memcpy driver, with CPDMA as the backend.

备注

CPDMA is a CPU peripheral, aiming for memory copy.

参数
  • config -- [in] Configuration of async memcpy

  • mcp -- [out] Returned driver handle

返回

  • ESP_OK: Install async memcpy driver successfully

  • ESP_ERR_INVALID_ARG: Install async memcpy driver failed because of invalid argument

  • ESP_ERR_NO_MEM: Install async memcpy driver failed because out of memory

  • ESP_FAIL: Install async memcpy driver failed because of other error

esp_err_t esp_async_memcpy_install(const async_memcpy_config_t *config, async_memcpy_handle_t *mcp)

Install async memcpy driver with the default DMA backend.

备注

On chip with CPDMA support, CPDMA is the default choice. On chip with AHB-GDMA support, AHB-GDMA is the default choice.

参数
  • config -- [in] Configuration of async memcpy

  • mcp -- [out] Returned driver handle

返回

  • ESP_OK: Install async memcpy driver successfully

  • ESP_ERR_INVALID_ARG: Install async memcpy driver failed because of invalid argument

  • ESP_ERR_NO_MEM: Install async memcpy driver failed because out of memory

  • ESP_FAIL: Install async memcpy driver failed because of other error

esp_err_t esp_async_memcpy_uninstall(async_memcpy_handle_t mcp)

Uninstall async memcpy driver.

参数

mcp -- [in] Handle of async memcpy driver that returned from esp_async_memcpy_install

返回

  • ESP_OK: Uninstall async memcpy driver successfully

  • ESP_ERR_INVALID_ARG: Uninstall async memcpy driver failed because of invalid argument

  • ESP_FAIL: Uninstall async memcpy driver failed because of other error

esp_err_t esp_async_memcpy(async_memcpy_handle_t mcp, void *dst, void *src, size_t n, async_memcpy_isr_cb_t cb_isr, void *cb_args)

Send an asynchronous memory copy request.

备注

The callback function is invoked in interrupt context, never do blocking jobs in the callback.

参数
  • mcp -- [in] Handle of async memcpy driver that returned from esp_async_memcpy_install

  • dst -- [in] Destination address (copy to)

  • src -- [in] Source address (copy from)

  • n -- [in] Number of bytes to copy

  • cb_isr -- [in] Callback function, which got invoked in interrupt context. Set to NULL can bypass the callback.

  • cb_args -- [in] User defined argument to be passed to the callback function

返回

  • ESP_OK: Send memory copy request successfully

  • ESP_ERR_INVALID_ARG: Send memory copy request failed because of invalid argument

  • ESP_FAIL: Send memory copy request failed because of other error

Structures

struct async_memcpy_event_t

Async memory copy event data.

Public Members

void *data

Event data

struct async_memcpy_config_t

Type of async memcpy configuration.

Public Members

uint32_t backlog

Maximum number of transactions that can be prepared in the background

size_t sram_trans_align

DMA transfer alignment (both in size and address) for SRAM memory

size_t psram_trans_align

DMA transfer alignment (both in size and address) for PSRAM memory

uint32_t flags

Extra flags to control async memcpy feature

Macros

ASYNC_MEMCPY_DEFAULT_CONFIG()

Default configuration for async memcpy.

Type Definitions

typedef struct async_memcpy_context_t *async_memcpy_handle_t

Async memory copy driver handle.

typedef bool (*async_memcpy_isr_cb_t)(async_memcpy_handle_t mcp_hdl, async_memcpy_event_t *event, void *cb_args)

Type of async memcpy interrupt callback function.

备注

User can call OS primitives (semaphore, mutex, etc) in the callback function. Keep in mind, if any OS primitive wakes high priority task up, the callback should return true.

Param mcp_hdl

Handle of async memcpy

Param event

Event object, which contains related data, reserved for future

Param cb_args

User defined arguments, passed from esp_async_memcpy function

Return

Whether a high priority task is woken up by the callback function