diff --git a/components/esp_driver_dma/include/esp_async_memcpy.h b/components/esp_driver_dma/include/esp_async_memcpy.h index 392c80c5372..eba0763ac58 100644 --- a/components/esp_driver_dma/include/esp_async_memcpy.h +++ b/components/esp_driver_dma/include/esp_async_memcpy.h @@ -174,6 +174,25 @@ esp_err_t esp_async_memcpy_uninstall(async_memcpy_handle_t mcp); */ 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); +/** + * @brief Blocking memory copy function with timeout + * + * @note This function is blocking and should not be called from interrupt context. + * @note Only `timeout_ms=-1` is supported, which means waiting indefinitely. + * + * @param[in] mcp Handle of async memcpy driver that returned from `esp_async_memcpy_install` + * @param[in] dst Destination address (copy to) + * @param[in] src Source address (copy from) + * @param[in] n Number of bytes to copy + * @param[in] timeout_ms Timeout in milliseconds. Only -1 is supported. + * @return + * - ESP_OK: Copy memory successfully + * - ESP_ERR_INVALID_ARG: Copy memory failed because of invalid argument + * - ESP_ERR_INVALID_STATE: Function called from ISR context or driver in invalid state + * - ESP_FAIL: Copy memory failed because of other error + */ +esp_err_t esp_memcpy_blocking(async_memcpy_handle_t mcp, void *dst, void *src, size_t n, int32_t timeout_ms); + #if SOC_ETM_SUPPORTED /** * @brief Async memory copy specific events that supported by the ETM module diff --git a/components/esp_driver_dma/src/esp_async_memcpy.c b/components/esp_driver_dma/src/esp_async_memcpy.c index 47d48404633..536d6d44268 100644 --- a/components/esp_driver_dma/src/esp_async_memcpy.c +++ b/components/esp_driver_dma/src/esp_async_memcpy.c @@ -4,6 +4,9 @@ * SPDX-License-Identifier: Apache-2.0 */ +#include "freertos/FreeRTOS.h" +#include "freertos/semphr.h" +#include "freertos/task.h" #include "esp_check.h" #include "esp_async_memcpy.h" #include "esp_async_memcpy_priv.h" @@ -22,6 +25,38 @@ esp_err_t esp_async_memcpy(async_memcpy_handle_t asmcp, void *dst, void *src, si return asmcp->memcpy(asmcp, dst, src, n, cb_isr, cb_args); } +typedef struct { + SemaphoreHandle_t semaphore; + StaticSemaphore_t semaphore_buffer; +} memcpy_blocking_context_t; + +static bool memcpy_blocking_callback(async_memcpy_handle_t mcp_hdl, async_memcpy_event_t *event, void *user_data) +{ + BaseType_t task_woken = pdFALSE; + memcpy_blocking_context_t *ctx = (memcpy_blocking_context_t *)user_data; + (void)mcp_hdl; + (void)event; + + xSemaphoreGiveFromISR(ctx->semaphore, &task_woken); + + return task_woken == pdTRUE; +} + +esp_err_t esp_memcpy_blocking(async_memcpy_handle_t asmcp, void *dst, void *src, size_t n, int32_t timeout_ms) +{ + ESP_RETURN_ON_FALSE(asmcp && dst && src && n, ESP_ERR_INVALID_ARG, TAG, "invalid argument"); + ESP_RETURN_ON_FALSE(!xPortInIsrContext(), ESP_ERR_INVALID_STATE, TAG, "called from ISR context is not allowed"); + ESP_RETURN_ON_FALSE(timeout_ms == -1, ESP_ERR_INVALID_ARG, TAG, "only timeout_ms=-1 is supported"); + + memcpy_blocking_context_t ctx = {}; + ctx.semaphore = xSemaphoreCreateBinaryStatic(&ctx.semaphore_buffer); + + ESP_RETURN_ON_ERROR(esp_async_memcpy(asmcp, dst, src, n, memcpy_blocking_callback, &ctx), TAG, "failed to start memory copy"); + + xSemaphoreTake(ctx.semaphore, portMAX_DELAY); + return ESP_OK; +} + #if SOC_ETM_SUPPORTED esp_err_t esp_async_memcpy_new_etm_event(async_memcpy_handle_t asmcp, async_memcpy_etm_event_t event_type, esp_etm_event_handle_t *out_event) { diff --git a/components/esp_driver_dma/test_apps/dma/main/test_async_memcpy.c b/components/esp_driver_dma/test_apps/dma/main/test_async_memcpy.c index 810cdb4ba05..dafb8841f66 100644 --- a/components/esp_driver_dma/test_apps/dma/main/test_async_memcpy.c +++ b/components/esp_driver_dma/test_apps/dma/main/test_async_memcpy.c @@ -12,7 +12,6 @@ #include "soc/soc_caps.h" #include "esp_heap_caps.h" #include "freertos/FreeRTOS.h" -#include "freertos/task.h" #include "freertos/semphr.h" #include "ccomp_timer.h" #include "esp_async_memcpy.h" @@ -102,8 +101,7 @@ static void test_memory_copy_with_same_buffer(async_memcpy_handle_t driver, asyn TEST_ASSERT_NOT_NULL(dbuf); for (int j = 0; j < 20; j++) { - TEST_ESP_OK(esp_async_memcpy(driver, dbuf, sbuf, 256, NULL, NULL)); - vTaskDelay(pdMS_TO_TICKS(10)); + TEST_ESP_OK(esp_memcpy_blocking(driver, dbuf, sbuf, 256, -1)); for (int i = 0; i < 256; i++) { if (sbuf[i] != dbuf[i]) { printf("location[%d]:s=%d,d=%d\r\n", i, sbuf[i], dbuf[i]); @@ -151,17 +149,8 @@ TEST_CASE("memory copy the same buffer with different content", "[async mcp]") #endif // SOC_HAS(LP_AHB_GDMA) } -static bool test_async_memcpy_cb_v1(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(sem, &high_task_wakeup); - return high_task_wakeup == pdTRUE; -} - static void test_memory_copy_blocking(async_memcpy_handle_t driver) { - SemaphoreHandle_t sem = xSemaphoreCreateBinary(); const uint32_t test_buffer_size[] = {256, 512, 1024, 2048, 4096, 5008}; memcpy_testbench_context_t test_context = { .align = 16, @@ -177,13 +166,11 @@ static void test_memory_copy_blocking(async_memcpy_handle_t driver) } async_memcpy_setup_testbench(&test_context); - TEST_ESP_OK(esp_async_memcpy(driver, test_context.to_addr, test_context.from_addr, test_context.copy_size, test_async_memcpy_cb_v1, sem)); - TEST_ASSERT_EQUAL(pdTRUE, xSemaphoreTake(sem, pdMS_TO_TICKS(10))); + TEST_ESP_OK(esp_memcpy_blocking(driver, test_context.to_addr, test_context.from_addr, test_context.copy_size, -1)); async_memcpy_verify_and_clear_testbench(test_context.copy_size, test_context.src_buf, test_context.dst_buf, test_context.from_addr, test_context.to_addr); } } - vSemaphoreDelete(sem); } TEST_CASE("memory copy by DMA (blocking)", "[async mcp]") @@ -225,7 +212,6 @@ TEST_CASE("memory copy by DMA (blocking)", "[async mcp]") [[maybe_unused]] static void test_memcpy_with_dest_addr_unaligned(async_memcpy_handle_t driver, bool src_in_psram, bool dst_in_psram) { - SemaphoreHandle_t sem = xSemaphoreCreateBinary(); const uint32_t test_buffer_size[] = {256, 512, 1024, 2048, 4096, 5012}; memcpy_testbench_context_t test_context = { .align = 4, @@ -241,13 +227,11 @@ TEST_CASE("memory copy by DMA (blocking)", "[async mcp]") test_context.dst_offset = off + 1; async_memcpy_setup_testbench(&test_context); - TEST_ESP_OK(esp_async_memcpy(driver, test_context.to_addr, test_context.from_addr, test_context.copy_size, test_async_memcpy_cb_v1, sem)); - TEST_ASSERT_EQUAL(pdTRUE, xSemaphoreTake(sem, pdMS_TO_TICKS(10))); + TEST_ESP_OK(esp_memcpy_blocking(driver, test_context.to_addr, test_context.from_addr, test_context.copy_size, -1)); async_memcpy_verify_and_clear_testbench(test_context.copy_size, test_context.src_buf, test_context.dst_buf, test_context.from_addr, test_context.to_addr); } } - vSemaphoreDelete(sem); } TEST_CASE("memory copy with dest address unaligned", "[async mcp]") diff --git a/docs/en/api-reference/peripherals/async_memcpy.rst b/docs/en/api-reference/peripherals/async_memcpy.rst index b40e5d3e710..41ff872af65 100644 --- a/docs/en/api-reference/peripherals/async_memcpy.rst +++ b/docs/en/api-reference/peripherals/async_memcpy.rst @@ -67,6 +67,14 @@ The prototype of the callback function is :cpp:type:`async_memcpy_isr_cb_t`. The // Do something else here xSemaphoreTake(my_semaphore, portMAX_DELAY); // Wait until the buffer copy is done +For simpler use cases where the task only needs to wait until one copy finishes, use :cpp:func:`esp_memcpy_blocking`. This API is built on top of the async request path and waits internally for the completion callback. + +.. code-block:: c + + ESP_ERROR_CHECK(esp_memcpy_blocking(driver_handle, to, from, copy_len, -1)); + +The blocking API must not be called from ISR context. Currently, it only supports ``timeout_ms = -1``, which means waiting indefinitely until the memory copy completes. + Uninstall Driver ---------------- diff --git a/docs/zh_CN/api-reference/peripherals/async_memcpy.rst b/docs/zh_CN/api-reference/peripherals/async_memcpy.rst index a713a8182ea..21c96b4ca46 100644 --- a/docs/zh_CN/api-reference/peripherals/async_memcpy.rst +++ b/docs/zh_CN/api-reference/peripherals/async_memcpy.rst @@ -67,6 +67,14 @@ DMA 允许多个内存复制请求在首个请求完成之前排队,即允许 // 其他事项 xSemaphoreTake(my_semaphore, portMAX_DELAY); // 等待 buffer 复制完成 +对于只需等待单次复制完成的简单场景,可以使用 :cpp:func:`esp_memcpy_blocking`。该 API 基于异步请求路径实现,并在内部等待完成回调。 + +.. code-block:: c + + ESP_ERROR_CHECK(esp_memcpy_blocking(driver_handle, to, from, copy_len, -1)); + +阻塞 API 不能在 ISR 上下文中调用。目前仅支持 ``timeout_ms = -1``,表示无限期等待直到内存复制完成。 + 卸载驱动 ----------------