mirror of
https://github.com/espressif/esp-idf.git
synced 2026-09-22 13:01:16 +03:00
Merge branch 'feature/isp-flash-dma-input' into 'master'
feat(isp): add aligned flash DMA input example See merge request espressif/esp-idf!50959
This commit is contained in:
@@ -17,14 +17,15 @@ extern "C" {
|
|||||||
/**
|
/**
|
||||||
* @brief Process one ISP DMA frame: feed the input buffer through the ISP and wait for completion
|
* @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
|
* @note The driver synchronizes cacheable buffers before and after DMA access. Input buffers use
|
||||||
* cacheable memory, the caller should synchronize them around DMA access. Buffer sizes are
|
* an unaligned cache write-back, while cacheable output buffers and their derived frame
|
||||||
* derived from the ISP processor resolution and pixel formats. Both buffers must be 8-byte
|
* sizes must be aligned to the cache line size.
|
||||||
* aligned. This function blocks until both input and output DMA channels finish.
|
* @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] proc Processor handle
|
||||||
* @param[in] output_buffer Destination buffer for ISP output (8-byte aligned)
|
* @param[in] output_buffer Destination buffer for ISP output
|
||||||
* @param[in] input_buffer Source input buffer for RAW frame data (8-byte aligned)
|
* @param[in] input_buffer Source input buffer for RAW frame data
|
||||||
* @param[in] timeout_ms Timeout in milliseconds for waiting transfer completion
|
* @param[in] timeout_ms Timeout in milliseconds for waiting transfer completion
|
||||||
*
|
*
|
||||||
* @return
|
* @return
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
#include "sdkconfig.h"
|
#include "sdkconfig.h"
|
||||||
#include "esp_log.h"
|
#include "esp_log.h"
|
||||||
#include "esp_check.h"
|
#include "esp_check.h"
|
||||||
|
#include "esp_cache.h"
|
||||||
#include "esp_heap_caps.h"
|
#include "esp_heap_caps.h"
|
||||||
#include "freertos/FreeRTOS.h"
|
#include "freertos/FreeRTOS.h"
|
||||||
#include "freertos/semphr.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)
|
static void s_isp_dma_frame_ctx_destroy(struct esp_isp_dma_frame_ctx_t *ctx)
|
||||||
{
|
{
|
||||||
if (!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_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;
|
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_out_trans.dst.addr = (uint32_t)output_buffer;
|
||||||
ctx->dma_in_trans.src.addr = (uint32_t)input_buffer;
|
ctx->dma_in_trans.src.addr = (uint32_t)input_buffer;
|
||||||
|
|
||||||
|
|||||||
@@ -1033,6 +1033,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.
|
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.
|
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
|
.. highlight:: cmake
|
||||||
|
|||||||
@@ -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.
|
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
|
ISP AF Controller
|
||||||
~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~
|
||||||
|
|||||||
@@ -1033,6 +1033,10 @@ CMake 文件可以使用 ``IDF_TARGET`` 变量来获取当前的硬件目标。
|
|||||||
|
|
||||||
并将这行代码放在项目 CMakeLists.txt 的 ``project()`` 命令之后,修改 ``myproject.elf`` 为你自己的项目名。如果最后一个参数是 ``TEXT``,那么构建系统会嵌入以 null 结尾的字符串,如果最后一个参数被设置为 ``BINARY``,则将文件内容按照原样嵌入。
|
并将这行代码放在项目 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 组件,两个文件会在编译时加载并链接到固件中。
|
有关使用此技术的示例,请查看 file_serving 示例 :example_file:`protocols/http_server/file_serving/main/CMakeLists.txt` 中的 main 组件,两个文件会在编译时加载并链接到固件中。
|
||||||
|
|
||||||
.. highlight:: cmake
|
.. highlight:: cmake
|
||||||
|
|||||||
@@ -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`,并根据输入图像格式设置输入、输出格式及分辨率。
|
除来自摄像头控制器的数据流外,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 控制器
|
ISP AF 控制器
|
||||||
~~~~~~~~~~~~~
|
~~~~~~~~~~~~~
|
||||||
|
|||||||
@@ -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
|
└── 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
|
## Troubleshooting
|
||||||
|
|
||||||
|
|||||||
@@ -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.
|
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
|
## Replacing The Embedded UYVY Asset
|
||||||
|
|
||||||
The example embeds `main/assets/sample_96x64_uyvy.yuv`.
|
The example embeds `main/assets/sample_96x64_uyvy.yuv`.
|
||||||
|
|||||||
@@ -5,19 +5,18 @@
|
|||||||
|
|
||||||
## Overview
|
## 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:
|
The data flow is:
|
||||||
|
|
||||||
1. The embedded BGGR RAW8 image is copied from flash into the ISP DMA input buffer.
|
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 image is transferred 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 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. 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.
|
||||||
5. pytest decodes the payload, swaps BGR→RGB, saves one PPM file per frame, and compares it with the golden image.
|
|
||||||
|
|
||||||
## Hardware Required
|
## 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
|
## How to Use
|
||||||
|
|
||||||
@@ -43,7 +42,3 @@ IMAGE_BASE64_END
|
|||||||
Frame 0 done
|
Frame 0 done
|
||||||
ISP DMA visual demo 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)
|
|
||||||
|
|||||||
@@ -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
|
PRIV_REQUIRES esp_driver_isp esp_mm esp_psram mbedtls
|
||||||
INCLUDE_DIRS ".")
|
INCLUDE_DIRS ".")
|
||||||
|
|
||||||
target_add_binary_data(${COMPONENT_LIB}
|
target_add_binary_data(${COMPONENT_LIB}
|
||||||
"${CMAKE_CURRENT_LIST_DIR}/assets/sensor_240x280_bggr.raw"
|
"${CMAKE_CURRENT_LIST_DIR}/assets/sensor_240x280_bggr.raw"
|
||||||
BINARY RENAME_TO "sensor_raw")
|
BINARY RENAME_TO "sensor_raw" ALIGN 16)
|
||||||
|
|||||||
@@ -7,13 +7,11 @@
|
|||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include "freertos/FreeRTOS.h"
|
#include "freertos/FreeRTOS.h"
|
||||||
#include "freertos/task.h"
|
#include "freertos/task.h"
|
||||||
#include "mbedtls/base64.h"
|
#include "mbedtls/base64.h"
|
||||||
#include "esp_check.h"
|
#include "esp_check.h"
|
||||||
#include "esp_cache.h"
|
|
||||||
#include "esp_heap_caps.h"
|
#include "esp_heap_caps.h"
|
||||||
#include "driver/isp_dma.h"
|
#include "driver/isp_dma.h"
|
||||||
#include "driver/isp_core.h"
|
#include "driver/isp_core.h"
|
||||||
@@ -22,20 +20,14 @@
|
|||||||
#define EXAMPLE_WIDTH 240
|
#define EXAMPLE_WIDTH 240
|
||||||
#define EXAMPLE_HEIGHT 280
|
#define EXAMPLE_HEIGHT 280
|
||||||
#define EXAMPLE_BASE64_CHUNK_LEN 384
|
#define EXAMPLE_BASE64_CHUNK_LEN 384
|
||||||
#define EXAMPLE_BASE64_DELAY_MS 10
|
|
||||||
#define EXAMPLE_DMA_ALIGN 64
|
#define EXAMPLE_DMA_ALIGN 64
|
||||||
#define EXAMPLE_FRAME_COUNT 1
|
#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_start[] asm("_binary_sensor_raw_start");
|
||||||
extern const uint8_t sensor_raw_end[] asm("_binary_sensor_raw_end");
|
extern const uint8_t sensor_raw_end[] asm("_binary_sensor_raw_end");
|
||||||
|
|
||||||
static void *s_alloc_dma_buffer(size_t size)
|
static void example_configure_neutral_color(isp_proc_handle_t isp_proc)
|
||||||
{
|
|
||||||
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)
|
|
||||||
{
|
{
|
||||||
esp_isp_color_config_t color_cfg = {
|
esp_isp_color_config_t color_cfg = {
|
||||||
.color_contrast = { .integer = 1, .decimal = 0 },
|
.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));
|
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");
|
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) {
|
for (size_t offset = 0; offset < encoded_len; offset += EXAMPLE_BASE64_CHUNK_LEN) {
|
||||||
size_t chunk_len = encoded_len - offset;
|
size_t chunk_len = encoded_len - offset;
|
||||||
if (chunk_len > EXAMPLE_BASE64_CHUNK_LEN) {
|
if (chunk_len > EXAMPLE_BASE64_CHUNK_LEN) {
|
||||||
chunk_len = EXAMPLE_BASE64_CHUNK_LEN;
|
chunk_len = EXAMPLE_BASE64_CHUNK_LEN;
|
||||||
}
|
}
|
||||||
printf("IMAGE_BASE64 %.*s\n", (int)chunk_len, (const char *)&encoded[offset]);
|
printf("IMAGE_BASE64 %.*s\n", (int)chunk_len, (const char *)&encoded[offset]);
|
||||||
fflush(stdout);
|
if ((++chunk_count % 16) == 0) {
|
||||||
vTaskDelay(pdMS_TO_TICKS(EXAMPLE_BASE64_DELAY_MS));
|
/* Let the test host drain the UART without delaying every chunk. */
|
||||||
|
vTaskDelay(1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
printf("IMAGE_BASE64_END\n");
|
printf("IMAGE_BASE64_END\n");
|
||||||
fflush(stdout);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void app_main(void)
|
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_new_processor(&isp_cfg, &isp_proc));
|
||||||
ESP_ERROR_CHECK(esp_isp_enable(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);
|
/* ISP writes this RGB frame through DMA, so PSRAM must be DMA-capable. */
|
||||||
uint8_t *isp_out_buf = s_alloc_dma_buffer(out_size);
|
uint8_t *isp_out_buf = heap_caps_aligned_calloc(EXAMPLE_DMA_ALIGN, 1, out_size,
|
||||||
assert(isp_in_buf && isp_out_buf);
|
MALLOC_CAP_SPIRAM | MALLOC_CAP_DMA | MALLOC_CAP_8BIT);
|
||||||
|
assert(isp_out_buf);
|
||||||
|
|
||||||
size_t embedded_raw_size = sensor_raw_end - sensor_raw_start;
|
size_t embedded_raw_size = sensor_raw_end - sensor_raw_start;
|
||||||
assert(embedded_raw_size == in_size);
|
assert(embedded_raw_size == in_size);
|
||||||
memcpy(isp_in_buf, sensor_raw_start, embedded_raw_size);
|
|
||||||
|
|
||||||
size_t encoded_len = 0;
|
size_t encoded_len = 0;
|
||||||
int ret = mbedtls_base64_encode(NULL, 0, &encoded_len, isp_out_buf, out_size);
|
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);
|
printf("Feeding %d frames through ISP DMA input...\n", EXAMPLE_FRAME_COUNT);
|
||||||
for (int frame = 0; frame < EXAMPLE_FRAME_COUNT; frame++) {
|
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));
|
* The RAW image is immutable mapped flash. Its aligned address can be
|
||||||
ESP_ERROR_CHECK(esp_cache_msync(isp_out_buf, out_size, ESP_CACHE_MSYNC_FLAG_DIR_M2C));
|
* 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;
|
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);
|
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",
|
printf("IMAGE_META frame=%d width=%u height=%u format=BGR24 encoding=base64\n",
|
||||||
frame, (unsigned)h_res, (unsigned)v_res);
|
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("Frame %d done\n", frame);
|
||||||
}
|
}
|
||||||
printf("ISP DMA visual demo done.\n");
|
printf("ISP DMA visual demo done.\n");
|
||||||
|
|
||||||
free(encoded);
|
|
||||||
ESP_ERROR_CHECK(esp_isp_color_disable(isp_proc));
|
ESP_ERROR_CHECK(esp_isp_color_disable(isp_proc));
|
||||||
ESP_ERROR_CHECK(esp_isp_disable(isp_proc));
|
ESP_ERROR_CHECK(esp_isp_disable(isp_proc));
|
||||||
ESP_ERROR_CHECK(esp_isp_del_processor(isp_proc));
|
ESP_ERROR_CHECK(esp_isp_del_processor(isp_proc));
|
||||||
heap_caps_free(isp_in_buf);
|
free(encoded);
|
||||||
heap_caps_free(isp_out_buf);
|
free(isp_out_buf);
|
||||||
}
|
}
|
||||||
@@ -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:
|
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
|
```bash
|
||||||
idf.py set-target esp32p4 build
|
pytest pytest_jpeg_decode.py --target esp32p4 --port PORT
|
||||||
pytest --target esp32p4 --port PORT pytest_jpeg_decode.py
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Replace `esp32p4` with another supported target such as `esp32s31` when needed.
|
Replace `esp32p4` with another supported target such as `esp32s31` when needed.
|
||||||
|
|||||||
@@ -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:
|
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
|
```bash
|
||||||
idf.py set-target esp32p4 build
|
pytest pytest_jpeg_encode.py --target esp32p4 --port PORT
|
||||||
pytest --target esp32p4 --port PORT pytest_jpeg_encode.py
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Replace `esp32p4` with another supported target such as `esp32s31` when needed.
|
Replace `esp32p4` with another supported target such as `esp32s31` when needed.
|
||||||
|
|||||||
@@ -65,7 +65,7 @@ It also compares the generated images with `golden_replace_keyed_text_with_glow.
|
|||||||
If you want to inspect the processed image on your computer, first build the example for your target, then run pytest from the ESP-IDF root directory with the matching target and serial port:
|
If you want to inspect the processed image on your computer, first build the example for your target, then run pytest from the ESP-IDF root directory with the matching target and serial port:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
pytest -k test_ppa_color_key --target esp32p4 --port PORT
|
pytest pytest_ppa_color_key.py --target esp32p4 --port PORT
|
||||||
```
|
```
|
||||||
|
|
||||||
Replace `esp32p4` with another supported target such as `esp32s31`, and set `PORT` to your board's serial device.
|
Replace `esp32p4` with another supported target such as `esp32s31`, and set `PORT` to your board's serial device.
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ It also compares the generated image with `golden_result.ppm` by hashing the dec
|
|||||||
If you want to inspect the processed image on your computer, first build the example for your target, then run pytest from the ESP-IDF root directory with the matching target and serial port:
|
If you want to inspect the processed image on your computer, first build the example for your target, then run pytest from the ESP-IDF root directory with the matching target and serial port:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
pytest -k test_ppa_transform --target esp32p4 --port PORT
|
pytest pytest_ppa_transform.py --target esp32p4 --port PORT
|
||||||
```
|
```
|
||||||
|
|
||||||
Replace `esp32p4` with another supported target such as `esp32s31`, and set `PORT` to your board's serial device.
|
Replace `esp32p4` with another supported target such as `esp32s31`, and set `PORT` to your board's serial device.
|
||||||
|
|||||||
@@ -74,6 +74,9 @@ append_line(".data")
|
|||||||
append_line("#if !defined (__APPLE__) && !defined (__linux__)")
|
append_line("#if !defined (__APPLE__) && !defined (__linux__)")
|
||||||
append_line(".section .rodata.embedded")
|
append_line(".section .rodata.embedded")
|
||||||
append_line("#endif")
|
append_line("#endif")
|
||||||
|
if(DEFINED DATA_ALIGNMENT)
|
||||||
|
append_line(".balign ${DATA_ALIGNMENT}")
|
||||||
|
endif()
|
||||||
make_and_append_identifier("${varname}")
|
make_and_append_identifier("${varname}")
|
||||||
make_and_append_identifier("_binary_${varname}_start" "for objcopy compatibility")
|
make_and_append_identifier("_binary_${varname}_start" "for objcopy compatibility")
|
||||||
append("${data}")
|
append("${data}")
|
||||||
|
|||||||
@@ -103,9 +103,10 @@ endfunction()
|
|||||||
|
|
||||||
# target_add_binary_data adds binary data into the built target,
|
# target_add_binary_data adds binary data into the built target,
|
||||||
# by converting it to a generated source file which is then compiled
|
# 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)
|
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(build_dir BUILD_DIR)
|
||||||
idf_build_get_property(idf_path IDF_PATH)
|
idf_build_get_property(idf_path IDF_PATH)
|
||||||
|
|
||||||
@@ -119,11 +120,24 @@ function(target_add_binary_data target embed_file embed_type)
|
|||||||
set(rename_to_arg -D "VARIABLE_BASENAME=${__RENAME_TO}")
|
set(rename_to_arg -D "VARIABLE_BASENAME=${__RENAME_TO}")
|
||||||
endif()
|
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}"
|
add_custom_command(OUTPUT "${embed_srcfile}"
|
||||||
COMMAND "${CMAKE_COMMAND}"
|
COMMAND "${CMAKE_COMMAND}"
|
||||||
-D "DATA_FILE=${embed_file}"
|
-D "DATA_FILE=${embed_file}"
|
||||||
-D "SOURCE_FILE=${embed_srcfile}"
|
-D "SOURCE_FILE=${embed_srcfile}"
|
||||||
${rename_to_arg}
|
${rename_to_arg}
|
||||||
|
${align_arg}
|
||||||
-D "FILE_TYPE=${embed_type}"
|
-D "FILE_TYPE=${embed_type}"
|
||||||
-P "${idf_path}/tools/cmake/scripts/data_file_embed_asm.cmake"
|
-P "${idf_path}/tools/cmake/scripts/data_file_embed_asm.cmake"
|
||||||
MAIN_DEPENDENCY "${embed_file}"
|
MAIN_DEPENDENCY "${embed_file}"
|
||||||
|
|||||||
@@ -833,7 +833,8 @@ endfunction()
|
|||||||
|
|
||||||
#[[
|
#[[
|
||||||
target_add_binary_data(<target> <embed_file> <embed_type>
|
target_add_binary_data(<target> <embed_file> <embed_type>
|
||||||
[RENAME_TO <symbol>])
|
[RENAME_TO <symbol>]
|
||||||
|
[ALIGN <alignment>]
|
||||||
[DEPENDS <dependency>...])
|
[DEPENDS <dependency>...])
|
||||||
|
|
||||||
*target[in]*
|
*target[in]*
|
||||||
@@ -853,6 +854,11 @@ endfunction()
|
|||||||
Use the given symbol name for the embedded data. If no symbol name is
|
Use the given symbol name for the embedded data. If no symbol name is
|
||||||
provided, the embed_file file name will be used instead.
|
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]*
|
*DEPENDS[in,opt]*
|
||||||
|
|
||||||
List of additional dependencies for the generated file containing
|
List of additional dependencies for the generated file containing
|
||||||
@@ -863,7 +869,7 @@ endfunction()
|
|||||||
build process.
|
build process.
|
||||||
#]]
|
#]]
|
||||||
function(target_add_binary_data target embed_file embed_type)
|
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(build_dir BUILD_DIR)
|
||||||
|
|
||||||
# In cmakev1, the executable target was named "${project}.elf".
|
# In cmakev1, the executable target was named "${project}.elf".
|
||||||
@@ -888,11 +894,24 @@ function(target_add_binary_data target embed_file embed_type)
|
|||||||
set(rename_to_arg -D "VARIABLE_BASENAME=${__RENAME_TO}")
|
set(rename_to_arg -D "VARIABLE_BASENAME=${__RENAME_TO}")
|
||||||
endif()
|
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}"
|
add_custom_command(OUTPUT "${embed_srcfile}"
|
||||||
COMMAND "${CMAKE_COMMAND}"
|
COMMAND "${CMAKE_COMMAND}"
|
||||||
-D "DATA_FILE=${embed_file}"
|
-D "DATA_FILE=${embed_file}"
|
||||||
-D "SOURCE_FILE=${embed_srcfile}"
|
-D "SOURCE_FILE=${embed_srcfile}"
|
||||||
${rename_to_arg}
|
${rename_to_arg}
|
||||||
|
${align_arg}
|
||||||
-D "FILE_TYPE=${embed_type}"
|
-D "FILE_TYPE=${embed_type}"
|
||||||
-P "${idf_path}/tools/cmake/scripts/data_file_embed_asm.cmake"
|
-P "${idf_path}/tools/cmake/scripts/data_file_embed_asm.cmake"
|
||||||
MAIN_DEPENDENCY "${embed_file}"
|
MAIN_DEPENDENCY "${embed_file}"
|
||||||
|
|||||||
Reference in New Issue
Block a user