mirror of
https://github.com/espressif/esp-idf.git
synced 2026-09-22 13:01:16 +03:00
refactor(isp): read DMA input directly from flash
Avoid the PSRAM copy for unencrypted flash
This commit is contained in:
@@ -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`.
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -7,13 +7,11 @@
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#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);
|
||||
}
|
||||
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user