From 16c8013af47125c31d9fb13f69ad58e427f83716 Mon Sep 17 00:00:00 2001 From: morris Date: Mon, 20 Jul 2026 12:44:18 +0800 Subject: [PATCH 1/2] feat(build): support aligned embedded binary data Allow callers to align embedded binary start symbols for DMA-capable assets. --- docs/en/api-guides/build-system.rst | 4 ++++ docs/zh_CN/api-guides/build-system.rst | 4 ++++ .../cmakev2/get-started/hello_world/README.md | 2 +- tools/cmake/scripts/data_file_embed_asm.cmake | 3 +++ tools/cmake/utilities.cmake | 18 +++++++++++++-- tools/cmakev2/utilities.cmake | 23 +++++++++++++++++-- 6 files changed, 49 insertions(+), 5 deletions(-) diff --git a/docs/en/api-guides/build-system.rst b/docs/en/api-guides/build-system.rst index 3ec4b034881..e7e74b99486 100644 --- a/docs/en/api-guides/build-system.rst +++ b/docs/en/api-guides/build-system.rst @@ -996,6 +996,10 @@ To embed a file into a project, rather than a component, you can call the functi Place this line after the ``project()`` line in your project CMakeLists.txt file. Replace ``myproject.elf`` with your project name. The final argument can be ``TEXT`` to embed a null-terminated string, or ``BINARY`` to embed the content as-is. +Use the optional ``ALIGN`` argument to align the embedded data's start symbol to a positive power of two. For example, to align binary data to 16 bytes:: + + target_add_binary_data(myproject.elf "main/data.bin" BINARY ALIGN 16) + For an example of using this technique, see the "main" component of the file_serving example :example_file:`protocols/http_server/file_serving/main/CMakeLists.txt` - two files are loaded at build time and linked into the firmware. .. highlight:: cmake diff --git a/docs/zh_CN/api-guides/build-system.rst b/docs/zh_CN/api-guides/build-system.rst index c51a3e34980..c125a97b49c 100644 --- a/docs/zh_CN/api-guides/build-system.rst +++ b/docs/zh_CN/api-guides/build-system.rst @@ -996,6 +996,10 @@ CMake 文件可以使用 ``IDF_TARGET`` 变量来获取当前的硬件目标。 并将这行代码放在项目 CMakeLists.txt 的 ``project()`` 命令之后,修改 ``myproject.elf`` 为你自己的项目名。如果最后一个参数是 ``TEXT``,那么构建系统会嵌入以 null 结尾的字符串,如果最后一个参数被设置为 ``BINARY``,则将文件内容按照原样嵌入。 +可选的 ``ALIGN`` 参数用于将嵌入数据的起始符号对齐到指定的正整数 2 的幂。例如,将二进制数据按 16 字节对齐:: + + target_add_binary_data(myproject.elf "main/data.bin" BINARY ALIGN 16) + 有关使用此技术的示例,请查看 file_serving 示例 :example_file:`protocols/http_server/file_serving/main/CMakeLists.txt` 中的 main 组件,两个文件会在编译时加载并链接到固件中。 .. highlight:: cmake diff --git a/examples/build_system/cmakev2/get-started/hello_world/README.md b/examples/build_system/cmakev2/get-started/hello_world/README.md index 61af59cc831..eb17e3e2cba 100644 --- a/examples/build_system/cmakev2/get-started/hello_world/README.md +++ b/examples/build_system/cmakev2/get-started/hello_world/README.md @@ -34,7 +34,7 @@ Below is short explanation of remaining files in the project folder. └── README.md This is the file you are currently reading ``` -For more information on structure and contents of ESP-IDF projects, please refer to Section [Build System v2](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/build-system-v2.html) of the ESP-IDF Programming Guide. +For more information on structure and contents of ESP-IDF projects, please refer to Section [Build System v2](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/build-system-v2/index.html) of the ESP-IDF Programming Guide. ## Troubleshooting diff --git a/tools/cmake/scripts/data_file_embed_asm.cmake b/tools/cmake/scripts/data_file_embed_asm.cmake index 5d19e7a1ba9..b8612976894 100644 --- a/tools/cmake/scripts/data_file_embed_asm.cmake +++ b/tools/cmake/scripts/data_file_embed_asm.cmake @@ -74,6 +74,9 @@ append_line(".data") append_line("#if !defined (__APPLE__) && !defined (__linux__)") append_line(".section .rodata.embedded") append_line("#endif") +if(DEFINED DATA_ALIGNMENT) + append_line(".balign ${DATA_ALIGNMENT}") +endif() make_and_append_identifier("${varname}") make_and_append_identifier("_binary_${varname}_start" "for objcopy compatibility") append("${data}") diff --git a/tools/cmake/utilities.cmake b/tools/cmake/utilities.cmake index ed0e9002d98..2d0ed38c5de 100644 --- a/tools/cmake/utilities.cmake +++ b/tools/cmake/utilities.cmake @@ -77,9 +77,10 @@ endfunction() # target_add_binary_data adds binary data into the built target, # by converting it to a generated source file which is then compiled -# to a binary object as part of the build +# to a binary object as part of the build. ALIGN optionally sets the +# alignment of the embedded data's start symbol. function(target_add_binary_data target embed_file embed_type) - cmake_parse_arguments(_ "" "RENAME_TO" "DEPENDS" ${ARGN}) + cmake_parse_arguments(_ "" "RENAME_TO;ALIGN" "DEPENDS" ${ARGN}) idf_build_get_property(build_dir BUILD_DIR) idf_build_get_property(idf_path IDF_PATH) @@ -93,11 +94,24 @@ function(target_add_binary_data target embed_file embed_type) set(rename_to_arg -D "VARIABLE_BASENAME=${__RENAME_TO}") endif() + set(align_arg) + if(DEFINED __ALIGN) + if(NOT __ALIGN MATCHES "^[1-9][0-9]*$") + message(FATAL_ERROR "ALIGN must be a positive integer") + endif() + math(EXPR alignment_mask "${__ALIGN} & (${__ALIGN} - 1)") + if(NOT alignment_mask EQUAL 0) + message(FATAL_ERROR "ALIGN must be a power of two") + endif() + set(align_arg -D "DATA_ALIGNMENT=${__ALIGN}") + endif() + add_custom_command(OUTPUT "${embed_srcfile}" COMMAND "${CMAKE_COMMAND}" -D "DATA_FILE=${embed_file}" -D "SOURCE_FILE=${embed_srcfile}" ${rename_to_arg} + ${align_arg} -D "FILE_TYPE=${embed_type}" -P "${idf_path}/tools/cmake/scripts/data_file_embed_asm.cmake" MAIN_DEPENDENCY "${embed_file}" diff --git a/tools/cmakev2/utilities.cmake b/tools/cmakev2/utilities.cmake index a2804964184..1e5bc432853 100644 --- a/tools/cmakev2/utilities.cmake +++ b/tools/cmakev2/utilities.cmake @@ -803,7 +803,8 @@ endfunction() #[[ target_add_binary_data( - [RENAME_TO ]) + [RENAME_TO ] + [ALIGN ] [DEPENDS ...]) *target[in]* @@ -823,6 +824,11 @@ endfunction() Use the given symbol name for the embedded data. If no symbol name is provided, the embed_file file name will be used instead. + *ALIGN[in,opt]* + + Align the embedded data's start symbol to the given positive power + of two. + *DEPENDS[in,opt]* List of additional dependencies for the generated file containing @@ -833,7 +839,7 @@ endfunction() build process. #]] function(target_add_binary_data target embed_file embed_type) - cmake_parse_arguments(_ "" "RENAME_TO" "DEPENDS" ${ARGN}) + cmake_parse_arguments(_ "" "RENAME_TO;ALIGN" "DEPENDS" ${ARGN}) idf_build_get_property(build_dir BUILD_DIR) idf_build_get_property(idf_path IDF_PATH) @@ -847,11 +853,24 @@ function(target_add_binary_data target embed_file embed_type) set(rename_to_arg -D "VARIABLE_BASENAME=${__RENAME_TO}") endif() + set(align_arg) + if(DEFINED __ALIGN) + if(NOT __ALIGN MATCHES "^[1-9][0-9]*$") + message(FATAL_ERROR "ALIGN must be a positive integer") + endif() + math(EXPR alignment_mask "${__ALIGN} & (${__ALIGN} - 1)") + if(NOT alignment_mask EQUAL 0) + message(FATAL_ERROR "ALIGN must be a power of two") + endif() + set(align_arg -D "DATA_ALIGNMENT=${__ALIGN}") + endif() + add_custom_command(OUTPUT "${embed_srcfile}" COMMAND "${CMAKE_COMMAND}" -D "DATA_FILE=${embed_file}" -D "SOURCE_FILE=${embed_srcfile}" ${rename_to_arg} + ${align_arg} -D "FILE_TYPE=${embed_type}" -P "${idf_path}/tools/cmake/scripts/data_file_embed_asm.cmake" MAIN_DEPENDENCY "${embed_file}" From 91a87cfb360c208be7c4d4e4a23638ea1cea12ba Mon Sep 17 00:00:00 2001 From: morris Date: Mon, 20 Jul 2026 12:44:18 +0800 Subject: [PATCH 2/2] refactor(isp): read DMA input directly from flash Avoid the PSRAM copy for unencrypted flash --- .../esp_driver_isp/include/driver/isp_dma.h | 13 ++--- components/esp_driver_isp/src/isp_dma.c | 27 +++++++++++ docs/en/api-reference/peripherals/isp.rst | 2 +- docs/zh_CN/api-reference/peripherals/isp.rst | 2 +- .../dma/async_color_convert/README.md | 10 ++++ examples/peripherals/isp/dma_input/README.md | 17 +++---- .../isp/dma_input/main/CMakeLists.txt | 4 +- ...{isp_dma_main.c => isp_dma_example_main.c} | 48 ++++++++----------- .../peripherals/jpeg/jpeg_decode/README.md | 3 +- .../peripherals/jpeg/jpeg_encode/README.md | 3 +- 10 files changed, 77 insertions(+), 52 deletions(-) rename examples/peripherals/isp/dma_input/main/{isp_dma_main.c => isp_dma_example_main.c} (75%) diff --git a/components/esp_driver_isp/include/driver/isp_dma.h b/components/esp_driver_isp/include/driver/isp_dma.h index 7bb2cc8585d..adaeb52bd6a 100644 --- a/components/esp_driver_isp/include/driver/isp_dma.h +++ b/components/esp_driver_isp/include/driver/isp_dma.h @@ -17,14 +17,15 @@ extern "C" { /** * @brief Process one ISP DMA frame: feed the input buffer through the ISP and wait for completion * - * @note Input buffer content should be ready before calling this function. If the buffers are in - * cacheable memory, the caller should synchronize them around DMA access. Buffer sizes are - * derived from the ISP processor resolution and pixel formats. Both buffers must be 8-byte - * aligned. This function blocks until both input and output DMA channels finish. + * @note The driver synchronizes cacheable buffers before and after DMA access. Input buffers use + * an unaligned cache write-back, while cacheable output buffers and their derived frame + * sizes must be aligned to the cache line size. + * @note This function blocks until both input and output DMA channels finish. On timeout, + * the output buffer may still be owned by DMA and must not be accessed. * * @param[in] proc Processor handle - * @param[in] output_buffer Destination buffer for ISP output (8-byte aligned) - * @param[in] input_buffer Source input buffer for RAW frame data (8-byte aligned) + * @param[in] output_buffer Destination buffer for ISP output + * @param[in] input_buffer Source input buffer for RAW frame data * @param[in] timeout_ms Timeout in milliseconds for waiting transfer completion * * @return diff --git a/components/esp_driver_isp/src/isp_dma.c b/components/esp_driver_isp/src/isp_dma.c index a22bed0863a..83cecbf2743 100644 --- a/components/esp_driver_isp/src/isp_dma.c +++ b/components/esp_driver_isp/src/isp_dma.c @@ -9,6 +9,7 @@ #include "sdkconfig.h" #include "esp_log.h" #include "esp_check.h" +#include "esp_cache.h" #include "esp_heap_caps.h" #include "freertos/FreeRTOS.h" #include "freertos/semphr.h" @@ -56,6 +57,17 @@ static dw_gdma_burst_items_t s_isp_dma_burst_len_to_items(uint32_t burst_len) } } +static esp_err_t s_isp_dma_sync_cacheable_buffer(void *buffer, size_t size, int flags, const char *buffer_name) +{ + size_t cache_line_size = esp_cache_get_line_size_by_addr(buffer); + if (cache_line_size == 0) { + return ESP_OK; + } + + ESP_RETURN_ON_ERROR(esp_cache_msync(buffer, size, flags), TAG, "sync %s buffer cache failed", buffer_name); + return ESP_OK; +} + static void s_isp_dma_frame_ctx_destroy(struct esp_isp_dma_frame_ctx_t *ctx) { if (!ctx) { @@ -243,6 +255,21 @@ esp_err_t esp_isp_dma_process_frame(isp_proc_handle_t proc, void *output_buffer, ESP_RETURN_ON_FALSE((((uintptr_t)input_buffer) % 8) == 0, ESP_ERR_INVALID_ARG, TAG, "input buffer not 8-byte aligned"); esp_isp_dma_frame_ctx_t *ctx = proc->dma_frame_ctx; + size_t input_frame_size = ctx->input_frame_size_64bit * sizeof(uint64_t); + size_t output_frame_size = ctx->output_frame_size_64bit * sizeof(uint64_t); + + ESP_RETURN_ON_ERROR(s_isp_dma_sync_cacheable_buffer((void *)input_buffer, input_frame_size, + ESP_CACHE_MSYNC_FLAG_DIR_C2M | ESP_CACHE_MSYNC_FLAG_UNALIGNED, + "input"), + TAG, "sync input buffer cache failed"); + /* + * Discard dirty CPU cache lines before DMA writes the frame. Otherwise a + * later cache write-back could overwrite data produced by the ISP. + */ + ESP_RETURN_ON_ERROR(s_isp_dma_sync_cacheable_buffer(output_buffer, output_frame_size, + ESP_CACHE_MSYNC_FLAG_DIR_M2C, "output"), + TAG, "sync output buffer cache failed"); + ctx->dma_out_trans.dst.addr = (uint32_t)output_buffer; ctx->dma_in_trans.src.addr = (uint32_t)input_buffer; diff --git a/docs/en/api-reference/peripherals/isp.rst b/docs/en/api-reference/peripherals/isp.rst index d9f6336f365..85d3f953fc4 100644 --- a/docs/en/api-reference/peripherals/isp.rst +++ b/docs/en/api-reference/peripherals/isp.rst @@ -248,7 +248,7 @@ ISP DMA Input Besides image streams from camera controllers, the ISP can also read image frames from system memory through DW-GDMA. To use DMA input, set :cpp:member:`esp_isp_processor_cfg_t::input_data_source` in :cpp:type:`esp_isp_processor_cfg_t` to :cpp:enumerator:`ISP_INPUT_DATA_SOURCE_DWGDMA`, and configure the input format, output format, and resolution according to the image frame. -DMA input is useful for feeding software-generated data, offline RAW images, or other test images in memory into the ISP. It can be used to validate an ISP pipeline without a camera sensor, reproduce issues with a specific input image. Call :cpp:func:`esp_isp_dma_process_frame` to send one input buffer to the ISP and write the processed image into an output buffer. The input and output buffers must be accessible by DMA; if cacheable memory is used, perform the required cache synchronization before and after the DMA transfer. +DMA input is useful for feeding software-generated data, offline RAW images, or other test images in memory into the ISP. It can be used to validate an ISP pipeline without a camera sensor, reproduce issues with a specific input image. Call :cpp:func:`esp_isp_dma_process_frame` to send one input buffer to the ISP and write the processed image into an output buffer. The input and output buffers must be accessible by DMA. The driver synchronizes cacheable buffers automatically. Input buffers use an unaligned cache write-back; cacheable output buffer addresses and their derived frame sizes must be aligned to the cache line size. ISP AF Controller ~~~~~~~~~~~~~~~~~ diff --git a/docs/zh_CN/api-reference/peripherals/isp.rst b/docs/zh_CN/api-reference/peripherals/isp.rst index 96ec09a8e8e..4ac16f306bc 100644 --- a/docs/zh_CN/api-reference/peripherals/isp.rst +++ b/docs/zh_CN/api-reference/peripherals/isp.rst @@ -248,7 +248,7 @@ ISP DMA 输入 除来自摄像头控制器的数据流外,ISP 还可以通过 DW-GDMA 从系统存储中读取图像帧作为输入。使用 DMA 输入时,应在 :cpp:type:`esp_isp_processor_cfg_t` 中将 :cpp:member:`esp_isp_processor_cfg_t::input_data_source` 配置为 :cpp:enumerator:`ISP_INPUT_DATA_SOURCE_DWGDMA`,并根据输入图像格式设置输入、输出格式及分辨率。 -DMA 输入适用于将软件生成的数据、离线保存的 RAW 图像或其他内存中的测试图像送入 ISP 进行处理。它可用于无摄像头传感器参与时验证 ISP 流水线、复现特定输入图像的问题。调用 :cpp:func:`esp_isp_dma_process_frame` 可以将一帧输入缓冲区送入 ISP,并将处理后的图像写入输出缓冲区。输入和输出缓冲区需要满足 DMA 访问要求;若使用带 cache 的内存,请在 DMA 传输前后执行必要的 cache 同步。 +DMA 输入适用于将软件生成的数据、离线保存的 RAW 图像或其他内存中的测试图像送入 ISP 进行处理。它可用于无摄像头传感器参与时验证 ISP 流水线、复现特定输入图像的问题。调用 :cpp:func:`esp_isp_dma_process_frame` 可以将一帧输入缓冲区送入 ISP,并将处理后的图像写入输出缓冲区。输入和输出缓冲区需要满足 DMA 访问要求。驱动会自动同步带 cache 的缓冲区:输入使用允许未对齐的 cache writeback;带 cache 的输出地址及其派生帧大小必须按 cache line 大小对齐。 ISP AF 控制器 ~~~~~~~~~~~~~ diff --git a/examples/peripherals/dma/async_color_convert/README.md b/examples/peripherals/dma/async_color_convert/README.md index defc28ed31d..1e4c06102c1 100644 --- a/examples/peripherals/dma/async_color_convert/README.md +++ b/examples/peripherals/dma/async_color_convert/README.md @@ -52,6 +52,16 @@ The accompanying pytest script captures the `IMAGE_META` and `IMAGE_BASE64` outp It also compares the generated result with `golden_result.ppm` by hashing the decoded RGB pixel content. This turns the example into a regression test as well as a visual demo: the image must both render correctly for a human and match the stored golden output for CI. +### Viewing The Result Locally + +Build and flash the example for your target, then run the pytest script from this example's directory: + +```bash +pytest pytest_async_color_convert.py --target esp32p4 --port PORT +``` + +Replace `esp32p4` with another supported target and `PORT` with the board's serial device. Pytest prints the artifact directory after the test completes; open `async_color_convert_result.ppm` from that directory with a PPM-compatible image viewer. + ## Replacing The Embedded UYVY Asset The example embeds `main/assets/sample_96x64_uyvy.yuv`. diff --git a/examples/peripherals/isp/dma_input/README.md b/examples/peripherals/isp/dma_input/README.md index b554725300c..149af62f193 100644 --- a/examples/peripherals/isp/dma_input/README.md +++ b/examples/peripherals/isp/dma_input/README.md @@ -5,19 +5,18 @@ ## Overview -This example embeds a 240 x 280 RAW8 Bayer image of a real scene in flash, copies it into a DMA-capable PSRAM input buffer, feeds it into the ISP through DW-GDMA, and prints the RGB888 output as base64. The pytest script decodes the output into a PPM image and compares it with the checked-in golden image. +This example embeds a 240 x 280 RAW8 Bayer image of a real scene in flash, feeds it directly into the ISP through DW-GDMA, and prints the RGB888 output as base64. The ISP driver synchronizes the DMA buffers' cache automatically. The pytest script decodes the output into a PPM image and compares it with the checked-in golden image. The data flow is: -1. The embedded BGGR RAW8 image is copied from flash into the ISP DMA input buffer. -2. The image is transferred into the ISP via `DW-GDMA → ISP DMA input`. -3. The ISP processes the data (demosaic, color adjustment) and outputs RGB888 (BGR24 byte layout). -4. The RGB888 frame is base64-encoded and printed with machine-parseable markers. -5. pytest decodes the payload, swaps BGR→RGB, saves one PPM file per frame, and compares it with the golden image. +1. The embedded 16-byte-aligned BGGR RAW8 image is transferred from mapped flash into the ISP via `DW-GDMA → ISP DMA input`. +2. The ISP processes the data (demosaic, color adjustment) and outputs RGB888 (BGR24 byte layout). +3. The RGB888 frame is base64-encoded and printed with machine-parseable markers. +4. pytest decodes the payload, swaps BGR→RGB, saves one PPM file per frame, and compares it with the golden image. ## Hardware Required -- An ESP32-P4 devkit with PSRAM (this example allocates the ISP DMA input/output buffers from PSRAM). +- An ESP32-P4 devkit with PSRAM (this example allocates the ISP output buffer from PSRAM). ## How to Use @@ -43,7 +42,3 @@ IMAGE_BASE64_END Frame 0 done ISP DMA visual demo done. ``` - -## Reference - -- [ESP-IDF: Image Signal Processor](https://docs.espressif.com/projects/esp-idf/en/latest/esp32p4/api-reference/peripherals/isp.html) diff --git a/examples/peripherals/isp/dma_input/main/CMakeLists.txt b/examples/peripherals/isp/dma_input/main/CMakeLists.txt index 8d7c2262783..4bebe72a62c 100644 --- a/examples/peripherals/isp/dma_input/main/CMakeLists.txt +++ b/examples/peripherals/isp/dma_input/main/CMakeLists.txt @@ -1,7 +1,7 @@ -idf_component_register(SRCS "isp_dma_main.c" +idf_component_register(SRCS "isp_dma_example_main.c" PRIV_REQUIRES esp_driver_isp esp_mm esp_psram mbedtls INCLUDE_DIRS ".") target_add_binary_data(${COMPONENT_LIB} "${CMAKE_CURRENT_LIST_DIR}/assets/sensor_240x280_bggr.raw" - BINARY RENAME_TO "sensor_raw") + BINARY RENAME_TO "sensor_raw" ALIGN 16) diff --git a/examples/peripherals/isp/dma_input/main/isp_dma_main.c b/examples/peripherals/isp/dma_input/main/isp_dma_example_main.c similarity index 75% rename from examples/peripherals/isp/dma_input/main/isp_dma_main.c rename to examples/peripherals/isp/dma_input/main/isp_dma_example_main.c index 4460e7502ac..11e2e8c72bf 100644 --- a/examples/peripherals/isp/dma_input/main/isp_dma_main.c +++ b/examples/peripherals/isp/dma_input/main/isp_dma_example_main.c @@ -7,13 +7,11 @@ #include #include #include -#include #include #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "mbedtls/base64.h" #include "esp_check.h" -#include "esp_cache.h" #include "esp_heap_caps.h" #include "driver/isp_dma.h" #include "driver/isp_core.h" @@ -22,20 +20,14 @@ #define EXAMPLE_WIDTH 240 #define EXAMPLE_HEIGHT 280 #define EXAMPLE_BASE64_CHUNK_LEN 384 -#define EXAMPLE_BASE64_DELAY_MS 10 #define EXAMPLE_DMA_ALIGN 64 #define EXAMPLE_FRAME_COUNT 1 +/* CMake embeds this RAW asset in mapped flash with 16-byte alignment. */ extern const uint8_t sensor_raw_start[] asm("_binary_sensor_raw_start"); extern const uint8_t sensor_raw_end[] asm("_binary_sensor_raw_end"); -static void *s_alloc_dma_buffer(size_t size) -{ - return heap_caps_aligned_calloc(EXAMPLE_DMA_ALIGN, 1, size, - MALLOC_CAP_SPIRAM | MALLOC_CAP_DMA | MALLOC_CAP_8BIT); -} - -static void s_configure_neutral_color(isp_proc_handle_t isp_proc) +static void example_configure_neutral_color(isp_proc_handle_t isp_proc) { esp_isp_color_config_t color_cfg = { .color_contrast = { .integer = 1, .decimal = 0 }, @@ -48,21 +40,22 @@ static void s_configure_neutral_color(isp_proc_handle_t isp_proc) ESP_ERROR_CHECK(esp_isp_color_enable(isp_proc)); } -static void s_print_base64_payload(const unsigned char *encoded, size_t encoded_len) +static void example_print_base64_payload(const unsigned char *encoded, size_t encoded_len) { printf("IMAGE_BASE64_BEGIN\n"); - fflush(stdout); + size_t chunk_count = 0; for (size_t offset = 0; offset < encoded_len; offset += EXAMPLE_BASE64_CHUNK_LEN) { size_t chunk_len = encoded_len - offset; if (chunk_len > EXAMPLE_BASE64_CHUNK_LEN) { chunk_len = EXAMPLE_BASE64_CHUNK_LEN; } printf("IMAGE_BASE64 %.*s\n", (int)chunk_len, (const char *)&encoded[offset]); - fflush(stdout); - vTaskDelay(pdMS_TO_TICKS(EXAMPLE_BASE64_DELAY_MS)); + if ((++chunk_count % 16) == 0) { + /* Let the test host drain the UART without delaying every chunk. */ + vTaskDelay(1); + } } printf("IMAGE_BASE64_END\n"); - fflush(stdout); } void app_main(void) @@ -87,15 +80,15 @@ void app_main(void) }; ESP_ERROR_CHECK(esp_isp_new_processor(&isp_cfg, &isp_proc)); ESP_ERROR_CHECK(esp_isp_enable(isp_proc)); - s_configure_neutral_color(isp_proc); + example_configure_neutral_color(isp_proc); - uint8_t *isp_in_buf = s_alloc_dma_buffer(in_size); - uint8_t *isp_out_buf = s_alloc_dma_buffer(out_size); - assert(isp_in_buf && isp_out_buf); + /* ISP writes this RGB frame through DMA, so PSRAM must be DMA-capable. */ + uint8_t *isp_out_buf = heap_caps_aligned_calloc(EXAMPLE_DMA_ALIGN, 1, out_size, + MALLOC_CAP_SPIRAM | MALLOC_CAP_DMA | MALLOC_CAP_8BIT); + assert(isp_out_buf); size_t embedded_raw_size = sensor_raw_end - sensor_raw_start; assert(embedded_raw_size == in_size); - memcpy(isp_in_buf, sensor_raw_start, embedded_raw_size); size_t encoded_len = 0; int ret = mbedtls_base64_encode(NULL, 0, &encoded_len, isp_out_buf, out_size); @@ -105,24 +98,25 @@ void app_main(void) printf("Feeding %d frames through ISP DMA input...\n", EXAMPLE_FRAME_COUNT); for (int frame = 0; frame < EXAMPLE_FRAME_COUNT; frame++) { - ESP_ERROR_CHECK(esp_cache_msync(isp_in_buf, in_size, ESP_CACHE_MSYNC_FLAG_DIR_C2M)); - ESP_ERROR_CHECK(esp_isp_dma_process_frame(isp_proc, isp_out_buf, isp_in_buf, 1000)); - ESP_ERROR_CHECK(esp_cache_msync(isp_out_buf, out_size, ESP_CACHE_MSYNC_FLAG_DIR_M2C)); + /* + * The RAW image is immutable mapped flash. Its aligned address can be + * read by DMA directly, so no PSRAM copy is needed. + */ + ESP_ERROR_CHECK(esp_isp_dma_process_frame(isp_proc, isp_out_buf, sensor_raw_start, 1000)); size_t out_len = 0; ESP_ERROR_CHECK(mbedtls_base64_encode(encoded, encoded_len + 1, &out_len, isp_out_buf, out_size) == 0 ? ESP_OK : ESP_FAIL); printf("IMAGE_META frame=%d width=%u height=%u format=BGR24 encoding=base64\n", frame, (unsigned)h_res, (unsigned)v_res); - s_print_base64_payload(encoded, out_len); + example_print_base64_payload(encoded, out_len); printf("Frame %d done\n", frame); } printf("ISP DMA visual demo done.\n"); - free(encoded); ESP_ERROR_CHECK(esp_isp_color_disable(isp_proc)); ESP_ERROR_CHECK(esp_isp_disable(isp_proc)); ESP_ERROR_CHECK(esp_isp_del_processor(isp_proc)); - heap_caps_free(isp_in_buf); - heap_caps_free(isp_out_buf); + free(encoded); + free(isp_out_buf); } diff --git a/examples/peripherals/jpeg/jpeg_decode/README.md b/examples/peripherals/jpeg/jpeg_decode/README.md index 05201409354..a0b72c24f56 100644 --- a/examples/peripherals/jpeg/jpeg_decode/README.md +++ b/examples/peripherals/jpeg/jpeg_decode/README.md @@ -60,8 +60,7 @@ The test writes the `PPM` file and compares it with `golden_output.ppm`. This ma To run the pytest helper locally on hardware, build the example for your target first, then invoke the test script with the target and serial port: ```bash -idf.py set-target esp32p4 build -pytest --target esp32p4 --port PORT pytest_jpeg_decode.py +pytest pytest_jpeg_decode.py --target esp32p4 --port PORT ``` Replace `esp32p4` with another supported target such as `esp32s31` when needed. diff --git a/examples/peripherals/jpeg/jpeg_encode/README.md b/examples/peripherals/jpeg/jpeg_encode/README.md index f622976940f..0e3032d651e 100644 --- a/examples/peripherals/jpeg/jpeg_encode/README.md +++ b/examples/peripherals/jpeg/jpeg_encode/README.md @@ -55,8 +55,7 @@ It also compares the generated JPEG with `golden_output.jpeg`. This turns the ex To run the pytest helper locally on hardware, build the example for your target first, then invoke the test script with the target and serial port: ```bash -idf.py set-target esp32p4 build -pytest --target esp32p4 --port PORT pytest_jpeg_encode.py +pytest pytest_jpeg_encode.py --target esp32p4 --port PORT ``` Replace `esp32p4` with another supported target such as `esp32s31` when needed.