尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

ESP-IDF 异步内存拷贝(async memcpy)驱动:基于 DMA 的高效内存搬运实战指南

ESP-IDF 异步内存拷贝(async memcpy)驱动:基于 DMA 的高效内存搬运实战指南 ESP-IDF 异步内存拷贝async memcpy驱动基于 DMA 的高效内存搬运实战指南【免费下载链接】esp-idfEspressif IoT Development Framework. Official development framework for Espressif SoCs.项目地址: https://gitcode.com/GitHub_Trending/es/esp-idf本篇技术指南围绕 ESP-IDFEspressif IoT Development Framework中的异步内存拷贝驱动Asynchronous Memory Copy简称 async memcpy展开讲解如何利用芯片内置的 DMA 引擎将内存拷贝任务从 CPU 卸载下来、以异步方式排队执行并通过事件回调获知拷贝完成的精确时刻。读完本文你将掌握 async memcpy 驱动的安装配置、异步/阻塞两种请求发送方式、回调函数在 ISR 上下文中的正确写法以及 ETM 事件联动等完整用法并理解其底层事务队列与状态机实现。该驱动对应的官方文档为 docs/en/api-reference/peripherals/async_memcpy.rst另有 中文版驱动头文件与实现位于 components/esp_driver_dma/include/esp_async_memcpy.h 及 components/esp_driver_dma/src 目录下。概述让 DMA 替你搬内存{IDF_TARGET_NAME}系列芯片内置 DMA 引擎可以异步方式将内部内存拷贝操作从 CPU 上卸载。async memcpy 驱动把所有 DMA 配置与操作封装成一组统一 API其中核心接口esp_async_memcpy的函数签名与标准 libc 的memcpy几乎一致学习成本极低。该方案有两个关键收益排队与流水线化DMA 允许在第一次拷贝尚未完成时就把多个内存拷贝请求排队提交从而让计算与内存拷贝在时间上重叠overlap of computation and memory copyCPU 不必阻塞等待搬移完成。精确完成时刻感知通过注册事件回调仍能获知某个内存拷贝请求精确完成的时刻便于在数据就绪后立即执行后续逻辑。安装驱动根据 DMA 引擎选择安装方式根据底层 DMA 引擎的不同async memcpy 驱动提供了多种安装入口。ESP-IDF 以 SoC 能力宏SOC caps在编译期决定哪些接口可用安装函数底层 DMA 引擎可用条件esp_async_memcpy_install_cpdmaCP DMA 引擎CPU 外设专为内存拷贝设计SOC_CP_DMA_SUPPORTEDesp_async_memcpy_install_gdma_ahbAHB GDMA 引擎SOC_AHB_GDMA_SUPPORTEDesp_async_memcpy_install_gdma_axiAXI GDMA 引擎SOC_AXI_GDMA_SUPPORTEDesp_async_memcpy_install_gdma_lp_ahbLP AHB GDMA 引擎SOC_HAS(LP_AHB_GDMA)头文件注释中声明使用低功耗域 AHB GDMAesp_async_memcpy_install通用安装接口自动选择默认引擎所有平台esp_async_memcpy_install是通用安装 API如果 SoC 支持 CP DMA 引擎则默认使用 CP DMA否则默认使用 AHB GDMA。这一点在源码中有直接印证在 async_memcpy_cp_dma.c 中esp_async_memcpy_install通过 GCCalias属性直接别名到esp_async_memcpy_install_cpdma而在 async_memcpy_gdma.c 中则依据编译期宏分别别名到 AHB GDMA 或 AXI GDMA 版本。驱动配置结构 async_memcpy_config_t驱动配置由async_memcpy_config_t描述定义见 esp_async_memcpy.h各字段含义如下字段含义默认值backlog在第一个请求完成前最多可排队的内存拷贝事务数量。若设为 0则应用默认值 48宏默认值/ 4backlog 为 0 时的回退值weightasync memcpy DMA 通道的权重权重越高平均带宽越高需要 SoC 支持加权仲裁SOC_GDMA_SUPPORT_WEIGHTED_ARBITRATION0dma_burst_size一次 DMA 突发传输burst transfer的大小单位字节16flags用于启用某些特殊驱动特性的标志位0注意两个默认值的差别ASYNC_MEMCPY_DEFAULT_CONFIG()宏的默认backlog 8而若用户在配置中显式把backlog设为 0驱动内部会回退到DEFAULT_TRANSACTION_QUEUE_LENGTH值为 4该常量定义于 esp_async_memcpy_priv.h安装逻辑见 async_memcpy_cp_dma.c 与 async_memcpy_gdma.c。backlog的大小直接决定驱动内部事务对象池的大小源码中会按backlog数量用heap_caps_calloc分配事务池transaction pool每个事务对象包含一组 TX/RX DMA 链表描述符因此backlog越大占用的内部内存MALLOC_CAP_INTERNAL越多但能排队的未完成请求也越多流水线重叠度越高。典型安装流程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 引擎安装驱动若需要指定特定 DMA 引擎例如测试对比不同引擎性能将最后一行替换为esp_async_memcpy_install_gdma_ahb(config, driver)、esp_async_memcpy_install_gdma_axi(config, driver)或esp_async_memcpy_install_cpdma(config, driver)即可。安装函数可能返回的错误码包括ESP_OK安装成功ESP_ERR_INVALID_ARG参数非法例如config或输出句柄指针为空ESP_ERR_NO_MEM内存不足驱动上下文或事务池分配失败ESP_FAIL其他底层错误如 GDMA 通道分配失败、中断注册失败。发送内存拷贝请求esp_async_memcpy是向 DMA 引擎发送内存拷贝请求的 API必须在驱动安装成功之后调用。它是线程安全的可以从不同任务中并发调用适合多任务共享同一驱动句柄的场景。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);与 libc 版本的memcpy不同你可以选择性地向esp_async_memcpy传入一个回调函数以便在内存拷贝完成时收到通知。注意回调在 ISR中断服务例程上下文中执行因此在回调中绝不能调用任何阻塞函数如vTaskDelay、xSemaphoreTake等会阻塞或依赖调度器的 API。回调函数原型 async_memcpy_isr_cb_t回调函数原型为async_memcpy_isr_cb_t定义见 esp_async_memcpy.htypedef bool (*async_memcpy_isr_cb_t)(async_memcpy_handle_t mcp_hdl, async_memcpy_event_t *event, void *cb_args);mcp_hdlasync memcpy 驱动句柄event事件对象内含void *data字段当前保留用于未来扩展cb_args用户在调用esp_async_memcpy时传入的自定义参数。返回值约定回调函数只有在通过 RTOS API如xSemaphoreGiveFromISR唤醒了高优先级任务时才应返回true。驱动的 ISR 处理器会依据该返回值决定是否调用portYIELD_FROM_ISR()进行任务切换见 async_memcpy_cp_dma.c错误地恒返回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; } // 创建一个用于上报 async 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); // 等待缓冲区拷贝完成关于 ISR 上下文回调的实践仓库测试代码 test_async_memcpy.c 提供了一个更贴近基准测试场景的写法回调使用IRAM_ATTR修饰以保证在 flash cache 失效时也能执行内部累加完成计数当计数达到目标值时通过xSemaphoreGiveFromISR唤醒等待任务并返回high_task_wakeup pdTRUE。阻塞版本esp_memcpy_blocking对于任务只需等待某一次拷贝完成的简单场景可以使用esp_memcpy_blocking。该 API 构建在异步请求路径之上内部等待完成回调因此对调用方而言表现为阻塞ESP_ERROR_CHECK(esp_memcpy_blocking(driver_handle, to, from, copy_len, -1));使用限制头文件注释与实现均有明确说明禁止在 ISR 上下文中调用否则返回ESP_ERR_INVALID_STATE当前仅支持timeout_ms -1即无限期等待直到内存拷贝完成传入其他值会因参数非法而失败。其阻塞语义通过内部信号量等待完成回调实现测试代码 test_async_memcpy.c 中大量使用esp_memcpy_blocking(driver, dbuf, sbuf, 256, -1)形式验证 SRAM 内、跨内存等场景的拷贝正确性。卸载驱动esp_async_memcpy_uninstall用于卸载异步 memcpy 驱动并回收资源esp_err_t esp_async_memcpy_uninstall(async_memcpy_handle_t mcp);并非每次 memcpy 操作后都需要卸载驱动驱动可长期复用当确定应用不再使用该驱动时调用它会回收事务对象池、DMA 链表、中断以及底层 DMA 通道等硬件与内存资源销毁路径见 async_memcpy_cp_dma.c 和 async_memcpy_gdma.c若仍有未完成的事务ready 队列非空或驱动不处于 IDLE 状态卸载将返回ESP_ERR_INVALID_STATE拒绝执行见 async_memcpy_gdma.c因此在卸载前需确保所有请求都已结束。ETM 事件联动在支持 ETMEvent Task Matrix且 GDMA 支持 ETM 子模块的 SoC 上条件为SOC_ETM_SUPPORTED SOC_GDMA_SUPPORT_ETMasync memcpy 在一次内存拷贝操作完成时能够产生一个事件该事件可用于与 ETM 模块交互。调用esp_async_memcpy_new_etm_event即可获取 ETM 事件句柄esp_err_t esp_async_memcpy_new_etm_event(async_memcpy_handle_t mcp, async_memcpy_etm_event_t event_type, esp_etm_event_handle_t *out_event);事件类型目前仅有一种ASYNC_MEMCPY_ETM_EVENT_COPY_DONE内存拷贝完成。从 async_memcpy_gdma.c 的实现可以看到该事件在 GDMA 后端下直接复用 RX 通道的 EOFEnd-Of-Frame事件通过gdma_new_etm_event创建。创建的事件对象后续可通过esp_etm_del_event删除。关于如何将事件连接到 ETM 通道task请参阅 ETM 模块文档 docs/en/api-reference/peripherals/etm.rst。源码级原理事务池、队列与状态机深入阅读实现可以更好地理解backlog、回调时序与线程安全背后的机制。两个后端实现async_memcpy_cp_dma.c 与 async_memcpy_gdma.c结构高度一致双队列模型驱动上下文维护两条 STAILQ 单向链表队列——空闲队列idle queue与就绪队列ready queue。所有事务对象最初挂在空闲队列每次提交请求时从空闲队列弹出一个对象装配好 TX/RX DMA 链表描述符后挂入就绪队列。三态状态机驱动状态机包含MCP_FSM_IDLE、MCP_FSM_WAIT、MCP_FSM_RUN三种状态枚举定义见 esp_async_memcpy_priv.h使用 C11 原子变量_Atomic保证状态切换的原子性。核心流程是提交请求后调用try_start_pending_transaction若当前处于 IDLE 则取一个就绪事务启动 DMAISR 中收到 RX EOF 后把事务对象归还空闲队列再尝试启动下一个排队事务。线程安全所有队列操作在自旋锁portMUX_TYPE spin_lock保护下进行且提供了普通上下文与 ISR 上下文两套临界区进入/退出接口这正是esp_async_memcpy可以从多个任务并发调用的底层保障。单链表描述符每个事务按MCP_DMA_DESCRIPTOR_BUFFER_MAX_SIZE4095 字节拆分 DMA 描述符节点TX 侧将最后一个 item 标记为 EOF从而让 RX 侧收到对应的 EOF 列表项来触发完成中断。CP DMA 后端与 GDMA 后端的差异维度CP DMA 后端GDMA 后端内存访问范围仅能访问 SRAM源码用esp_ptr_internal强校验访问外部内存返回ESP_ERR_INVALID_ARG支持外部内存PSRAMtransfer_cfg.access_ext_mem true但具体取决于 SoC 的GDMA_LL_GET(AHB_PSRAM_CAPABLE)等能力位缓冲对齐要求内部内存无对齐要求buffer_alignment 1按gdma_get_alignment_constraints查询内部/外部内存对齐约束地址或长度不满足时返回ESP_ERR_INVALID_ARG见check_buffer_alignmentasync_memcpy_gdma.c缓存一致性处理无需处理拷贝前对源缓冲调用esp_cache_msyncDIR_C2M回写缓存目标缓冲若未按 cache line 对齐则拆分为对齐缓冲并使用暂存缓冲stash buffer合并见 async_memcpy_gdma.cETM 支持—将 RX EOF 映射为ASYNC_MEMCPY_ETM_EVENT_COPY_DONE事件这意味着在 PSRAM 与 SRAM 之间、以及非对齐缓冲区之间做 DMA 拷贝时应优先选用 GDMA 后端而纯 SRAM 内部拷贝则可选用 CP DMA 获得更简化的路径。测试验证与性能参考方法仓库在 components/esp_driver_dma/test_apps/dma/main/test_async_memcpy.c 中提供了完整的测试与基准测试实现可作为验证自身用法的参考正确性测试随机填充源缓冲区后执行拷贝逐字节比对目的缓冲区与源缓冲区数据async_memcpy_verify_and_clear_testbench并覆盖 SRAM→SRAM、SRAM→PSRAM、PSRAM→SRAM、源目的同缓冲等组合性能对比方法同一份数据分别用 CPUmemcpy与 DMA 异步拷贝各执行多轮TEST_ASYNC_MEMCPY_BENCH_COUNTS通过ccomp_timer计时并输出 MB/s 吞吐量对比见 test_async_memcpy.c。测试将缓冲区按 burst size 对齐示例中为 32 字节以获得最佳性能加权仲裁测试在多驱动实例场景下验证weight字段对平均带宽的影响test_weighted_arb_isr_cb。使用注意事项小结esp_async_memcpy提交的请求可排队受backlog限制但若空闲队列耗尽会返回ESP_ERR_INVALID_STATE调用方应据此做好流控或重试回调运行在 ISR 上下文禁止阻塞调用需要唤醒任务时使用xSemaphoreGiveFromISR等 FromISR 版本 API并仅在确实唤醒高优先级任务时返回trueesp_memcpy_blocking不能从 ISR 调用且当前只接受timeout_ms -1CP DMA 后端只能访问 SRAM涉及 PSRAM 的拷贝请使用 GDMA 后端esp_async_memcpy_install的默认选择依据 SoC 能力自动决定使用 GDMA 后端时注意缓冲区地址与长度的对齐要求必要时使用heap_caps_aligned_alloc分配对齐缓冲区卸载驱动前确保所有事务已完成驱动处于 IDLE 状态且就绪队列为空。API 参考async memcpy 驱动的完整 API 一览详细声明与返回码说明见 esp_async_memcpy.h安装esp_async_memcpy_install、esp_async_memcpy_install_cpdma、esp_async_memcpy_install_gdma_ahb、esp_async_memcpy_install_gdma_axi、esp_async_memcpy_install_gdma_lp_ahb卸载esp_async_memcpy_uninstall请求esp_async_memcpy异步、esp_memcpy_blocking阻塞回调类型async_memcpy_isr_cb_t、事件结构async_memcpy_event_t、配置结构async_memcpy_config_tETMesp_async_memcpy_new_etm_event、事件枚举async_memcpy_etm_event_t句柄async_memcpy_handle_t兼容旧类型async_memcpy_t【免费下载链接】esp-idfEspressif IoT Development Framework. Official development framework for Espressif SoCs.项目地址: https://gitcode.com/GitHub_Trending/es/esp-idf创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表