diff --git a/examples/peripherals/isp/multi_pipelines/README.md b/examples/peripherals/isp/multi_pipelines/README.md index 4f24b604c8..4e11e4159d 100644 --- a/examples/peripherals/isp/multi_pipelines/README.md +++ b/examples/peripherals/isp/multi_pipelines/README.md @@ -8,6 +8,7 @@ This example demonstrates how to use the ISP (image signal processor) to work with esp_driver_cam component. This example will auto-detect camera sensors via [ESP camera sensor driver](https://components.espressif.com/components/espressif/esp_cam_sensor/versions/0.5.3) and capture camera sensor signals via CSI interface and display it via DSI interface. This example enables following ISP functions: +- ISP AWB (auto white balance) & WBG (white balance gain) feature - ISP AF (auto-focus) feature - ISP BF (bayer denoise) feature - ISP BLC (black level correction) feature @@ -160,23 +161,37 @@ To exit the serial monitor, use `Ctrl` + `]`. If you see the following console output, your example should be running correctly: ``` -I (1425) main_task: Calling app_main() -I (1425) example_dsi_init: Allocating DSI resources with 2 frame buffer(s) -I (1485) example_dsi_init: Frame buffer[0] allocated at: 0x48000a40 -I (1485) example_dsi_init: Frame buffer[1] allocated at: 0x481f4a80 -I (1485) isp_dsi: Original CSI resolution: 800x640 -I (1485) isp_dsi: Display resolution: 800x640, bits per pixel: 16 -I (1495) isp_dsi: frame_buffer_size: 2048000 -I (1495) isp_dsi: Frame buffers: fb0=0x48000a40, fb1=0x481f4a80 -I (1515) ov5647: Detected Camera sensor PID=0x5647 -I (1535) sensor_init: fmt[0].name:MIPI_2lane_24Minput_RAW8_800x1280_50fps -I (1535) sensor_init: fmt[1].name:MIPI_2lane_24Minput_RAW8_800x640_50fps -I (1535) sensor_init: fmt[2].name:MIPI_2lane_24Minput_RAW8_800x800_50fps -I (1535) sensor_init: fmt[3].name:MIPI_2lane_24Minput_RAW10_1920x1080_30fps -I (1545) sensor_init: fmt[4].name:MIPI_2lane_24Minput_RAW10_1280x960_binning_45fps -I (2025) sensor_init: Format in use:MIPI_2lane_24Minput_RAW8_800x640_50fps -I (2045) isp_dsi: ISP Crop not configured -I (2105) ili9881c: ID1: 0x98, ID2: 0x81, ID3: 0x5c +I (1457) main_task: Calling app_main() +I (1457) example_dsi_init: Allocating DSI resources with 2 frame buffer(s) +I (1517) isp_dsi: Original CSI resolution: 800x640 +I (1517) isp_dsi: Display resolution: 800x640, bits per pixel: 16 +I (1517) isp_dsi: Frame buffers: fb0=0x48000a40, fb1=0x481f4a80 +I (1517) ov5647: Detected Camera sensor PID=0x5647 +I (1537) sensor_init: fmt[0].name:MIPI_2lane_24Minput_RAW8_800x1280_50fps +I (1537) sensor_init: fmt[1].name:MIPI_2lane_24Minput_RAW8_800x640_50fps +I (1537) sensor_init: fmt[2].name:MIPI_2lane_24Minput_RAW8_800x800_50fps +I (1547) sensor_init: fmt[3].name:MIPI_2lane_24Minput_RAW10_1920x1080_30fps +I (1547) sensor_init: fmt[4].name:MIPI_2lane_24Minput_RAW10_1280x960_binning_45fps +I (1617) sensor_init: Format in use:MIPI_2lane_24Minput_RAW8_800x640_50fps +I (1627) isp_pipeline: ISP processor initialized +I (1627) isp_pipeline: BLC module configured +I (1627) isp_pipeline: BF module configured +I (1627) isp_pipeline: LSC module configured +I (1627) isp_pipeline: DEMOSAIC module configured +I (1637) isp_pipeline: CCM module configured +I (1637) isp_pipeline: GAMMA module configured +I (1637) isp_pipeline: SHARPEN module configured +I (1647) isp_pipeline: COLOR module configured +I (1647) isp_pipeline: All ISP pipeline modules initialized +W (1657) ISP_AWB: subwindow size (480 x 384) is not divisible by AWB subwindow blocks grid (5 x 5). Resolution will be floored to the nearest divisible value. +I (1667) isp_wb: AWB and WBG module initialized +I (1677) isp_wb: White balance task started +I (1677) isp_wb: WB enabled: AWB statistics started, WBG module enabled, processing task created +I (1687) isp_af: AF module initialized +I (1687) isp_af: AF task started +I (1697) isp_af: AF task created +I (1757) ili9881c: ID1: 0x98, ID2: 0x81, ID3: 0x5c +I (1877) isp_dsi: ISP DSI example started ``` Below picture is from the video stream of OV5647 and ILI9881C. The camera module is auto-focused and calibrated by ESP on-chip ISP hardware. The edge is over-sharpened as example code configured. diff --git a/examples/peripherals/isp/multi_pipelines/main/CMakeLists.txt b/examples/peripherals/isp/multi_pipelines/main/CMakeLists.txt index e0f71b79ad..7a61d3929a 100644 --- a/examples/peripherals/isp/multi_pipelines/main/CMakeLists.txt +++ b/examples/peripherals/isp/multi_pipelines/main/CMakeLists.txt @@ -1,6 +1,7 @@ set(srcs "isp_dsi_main.c" "example_buffer.c" "example_af.c" + "example_awb.c" "example_pipelines.c") if(CONFIG_EXAMPLE_ISP_CROP_ENABLE) diff --git a/examples/peripherals/isp/multi_pipelines/main/example_af.c b/examples/peripherals/isp/multi_pipelines/main/example_af.c index 9cac0e3b26..312a16e804 100644 --- a/examples/peripherals/isp/multi_pipelines/main/example_af.c +++ b/examples/peripherals/isp/multi_pipelines/main/example_af.c @@ -187,8 +187,7 @@ static void example_af_task(void *arg) } esp_err_t example_isp_af_init(isp_proc_handle_t isp_proc, - esp_sccb_io_handle_t dw9714_io_handle, - const example_isp_af_config_t *config) + esp_sccb_io_handle_t dw9714_io_handle) { if (isp_proc == NULL || dw9714_io_handle == NULL) { ESP_LOGE(TAG, "Invalid arguments"); diff --git a/examples/peripherals/isp/multi_pipelines/main/example_af.h b/examples/peripherals/isp/multi_pipelines/main/example_af.h index 4fd52b6d0f..cc878171e4 100644 --- a/examples/peripherals/isp/multi_pipelines/main/example_af.h +++ b/examples/peripherals/isp/multi_pipelines/main/example_af.h @@ -38,20 +38,6 @@ typedef struct { esp_sccb_io_handle_t dw9714_io_handle; // DW9714 VCM SCCB handle } example_isp_af_task_param_t; -/** - * @brief AF configuration structure - */ -typedef struct { - int window_size; // AF window size (centered window) - int edge_thresh; // Edge threshold for definition calculation - int env_interval; // Environment detector interval (frames) - int first_step_val; // First step value for SA scheme - int first_approx_cycles; // First approximation cycles - int second_step_val; // Second step value for SA scheme - int second_approx_cycles; // Second approximation cycles - int focus_val_max; // Maximum focus value -} example_isp_af_config_t; - /** * @brief Initialize AF module * @@ -60,15 +46,13 @@ typedef struct { * * @param[in] isp_proc ISP processor handle * @param[in] dw9714_io_handle DW9714 VCM SCCB handle - * @param[in] config AF configuration (can be NULL for default values) * @return * - ESP_OK: Success * - ESP_ERR_INVALID_ARG: Invalid arguments * - ESP_ERR_NO_MEM: Out of memory */ esp_err_t example_isp_af_init(isp_proc_handle_t isp_proc, - esp_sccb_io_handle_t dw9714_io_handle, - const example_isp_af_config_t *config); + esp_sccb_io_handle_t dw9714_io_handle); /** * @brief Start AF task diff --git a/examples/peripherals/isp/multi_pipelines/main/example_awb.c b/examples/peripherals/isp/multi_pipelines/main/example_awb.c new file mode 100644 index 0000000000..9aae8083e1 --- /dev/null +++ b/examples/peripherals/isp/multi_pipelines/main/example_awb.c @@ -0,0 +1,344 @@ +/* + * SPDX-FileCopyrightText: 2025-2026 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "esp_log.h" +#include "esp_check.h" +#include "driver/isp_awb.h" +#include "driver/isp_wbg.h" +#include "driver/isp_types.h" +#include "example_config.h" +#include "example_awb.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/queue.h" +#include "freertos/idf_additions.h" +#include "esp_heap_caps.h" + +static const char *TAG = "isp_awb"; + +static isp_awb_ctlr_t s_awb_ctlr = NULL; +static isp_proc_handle_t s_isp_proc = NULL; +static QueueHandle_t s_awb_queue = NULL; +static TaskHandle_t s_awb_task_handle = NULL; + +// Configuration for gain printing +#define AWB_GAIN_UPDATE_COUNT 5 + +// White balance gain normalization value (1.0 = neutral gain) +#define AWB_GAIN_NORM 256 // Normalization value for gain calculation + +// PI Controller parameters for smooth gain convergence +#define AWB_P_GAIN 0.5f // Proportional gain (0.0-1.0) + +/** + * @brief Get default AWB configuration based on image resolution + */ +static void s_get_default_awb_config(esp_isp_awb_config_t *config, uint32_t h_res, uint32_t v_res) +{ + // Set sample point: after CCM by default + config->sample_point = ISP_AWB_SAMPLE_POINT_AFTER_CCM; + + // Configure window: middle 80% of image to avoid edge overexposure + config->window.top_left.x = h_res * 0.2f; + config->window.top_left.y = v_res * 0.2f; + config->window.btm_right.x = h_res * 0.8f - 1; + config->window.btm_right.y = v_res * 0.8f - 1; + + // Configure subwindow: same size as main window + config->subwindow = config->window; + + // Configure white patch detection thresholds + // Luminance range: [0, 220*3] to avoid overexposed pixels + config->white_patch.luminance.min = 0; + config->white_patch.luminance.max = 220 * 3; + + // Color ratio ranges: wide range to include all possible white patches + config->white_patch.red_green_ratio.min = 0.5f; + config->white_patch.red_green_ratio.max = 1.999f; + config->white_patch.blue_green_ratio.min = 0.5f; + config->white_patch.blue_green_ratio.max = 1.999f; + + // Interrupt priority: 0 means auto-allocate + config->intr_priority = 0; +} + +/** + * @brief AWB statistics callback (runs in ISR context) + * + * This callback receives AWB statistics and sends them to the processing task via queue. + * It should be lightweight as it runs in ISR context. + */ +static bool IRAM_ATTR s_awb_statistics_callback(isp_awb_ctlr_t awb_ctlr, const esp_isp_awb_evt_data_t *edata, void *user_data) +{ + BaseType_t xHigherPriorityTaskWoken = pdFALSE; + + // Send statistics to queue (from ISR) + if (s_awb_queue != NULL) { + if (xQueueSendFromISR(s_awb_queue, &edata->awb_result, &xHigherPriorityTaskWoken) != pdTRUE) { + ESP_DRAM_LOGE(TAG, "Failed to send AWB statistics to queue (queue full)"); + return false; + } + } + return xHigherPriorityTaskWoken == pdTRUE; +} + +/** + * @brief PI controller for smooth white balance gain update + * + * This function implements a PI (Proportional-Integral) controller to smoothly update white balance gains, preventing oscillation and ensuring convergence. + * + * Currently we just do the P control, the I control is not used and could be extended in the future. + * + * @param[in] target_gain Target gain calculated from statistics + * @param[in] current_gain Currently applied gain + * @param[out] new_gain Calculated new gain to apply + */ +static void s_pi_controller_update(isp_wbg_gain_t target_gain, + isp_wbg_gain_t current_gain, + isp_wbg_gain_t *new_gain) +{ + // Calculate error (target - current) + float error_r = (float)target_gain.gain_r - (float)current_gain.gain_r; + float error_b = (float)target_gain.gain_b - (float)current_gain.gain_b; + + // Calculate new gain: current + P*error + I*integral + float new_gain_r = (float)current_gain.gain_r + AWB_P_GAIN * error_r; + float new_gain_b = (float)current_gain.gain_b + AWB_P_GAIN * error_b; + + // Round and clamp to valid range + new_gain->gain_r = (uint32_t)(new_gain_r + 0.5f); + new_gain->gain_g = AWB_GAIN_NORM; // G channel is reference + new_gain->gain_b = (uint32_t)(new_gain_b + 0.5f); +} + +/** + * @brief Calculate white balance gain from statistics + * + * @param[in] stat_result AWB statistics result + * @param[out] gain Calculated white balance gain + * @return true if calculation successful, false otherwise + */ +static bool s_calculate_awb_gain(const isp_awb_stat_result_t *stat_result, isp_wbg_gain_t *gain) +{ + if (stat_result == NULL || gain == NULL) { + return false; + } + + // Check if we have enough white patches + if (stat_result->white_patch_num == 0) { + ESP_LOGD(TAG, "No white patches detected, keeping current gain"); + return false; + } + + // Check if sum values are valid (avoid division by zero) + if (stat_result->sum_r == 0 || stat_result->sum_b == 0) { + ESP_LOGW(TAG, "Invalid sum values (R=%lu, B=%lu), keeping current gain", + stat_result->sum_r, stat_result->sum_b); + return false; + } + + // Calculate gains: use G channel as reference + float gain_r_float = ((float)stat_result->sum_g / (float)stat_result->sum_r) * (float)AWB_GAIN_NORM; + float gain_b_float = ((float)stat_result->sum_g / (float)stat_result->sum_b) * (float)AWB_GAIN_NORM; + + // Clamp gains to valid range + gain->gain_r = (uint32_t)(gain_r_float + 0.5f); // Round to nearest + gain->gain_g = AWB_GAIN_NORM; + gain->gain_b = (uint32_t)(gain_b_float + 0.5f); + + ESP_LOGD(TAG, "Calculated AWB gain: R=%lu, G=%lu, B=%lu (patches=%lu, sum_r=%lu, sum_g=%lu, sum_b=%lu)", + gain->gain_r, gain->gain_g, gain->gain_b, + stat_result->white_patch_num, stat_result->sum_r, stat_result->sum_g, stat_result->sum_b); + + return true; +} + +/** + * @brief White balance processing task + * + * This task receives AWB statistics from the queue, calculates white balance gains, + * accumulates them over multiple periods, and applies the smoothed gain through PI controller + * to prevent oscillation and ensure convergence. + */ +static void s_awb_task(void *pvParameters) +{ + isp_awb_stat_result_t stat_result; + isp_wbg_gain_t gain; + uint32_t gain_count = 0; + uint64_t sum_r = 0, sum_g = 0, sum_b = 0; + + // Current applied gain (maintained by PI controller) + isp_wbg_gain_t current_gain = { + .gain_r = AWB_GAIN_NORM, + .gain_g = AWB_GAIN_NORM, + .gain_b = AWB_GAIN_NORM, + }; + + ESP_LOGI(TAG, "White balance task started"); + + while (1) { + // Wait for statistics from queue + if (xQueueReceive(s_awb_queue, &stat_result, portMAX_DELAY) == pdTRUE) { + // Calculate white balance gain + if (s_calculate_awb_gain(&stat_result, &gain)) { + // Accumulate gain values for averaging + sum_r += gain.gain_r; + sum_g += gain.gain_g; + sum_b += gain.gain_b; + gain_count++; + + // When we have enough samples, calculate average and apply with PI controller + if (gain_count >= AWB_GAIN_UPDATE_COUNT) { + // Calculate average target gain + isp_wbg_gain_t target_gain = {}; + target_gain.gain_r = (uint32_t)(sum_r / AWB_GAIN_UPDATE_COUNT); + target_gain.gain_g = (uint32_t)(sum_g / AWB_GAIN_UPDATE_COUNT); + target_gain.gain_b = (uint32_t)(sum_b / AWB_GAIN_UPDATE_COUNT); + + // Apply PI controller to calculate new gain + isp_wbg_gain_t new_gain = {}; + s_pi_controller_update(target_gain, current_gain, &new_gain); + + // Apply new gain through WBG module + esp_err_t ret = esp_isp_wbg_set_wb_gain(s_isp_proc, new_gain); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "Failed to set AWB gain: %d", ret); + } else { + // Update current gain + current_gain = new_gain; + + // Print final updated gain values + float gain_r_norm = (float)new_gain.gain_r / (float)AWB_GAIN_NORM; + float gain_g_norm = (float)new_gain.gain_g / (float)AWB_GAIN_NORM; + float gain_b_norm = (float)new_gain.gain_b / (float)AWB_GAIN_NORM; + + ESP_LOGD(TAG, "AWB Gain Updated - R: %lu (%.3f), G: %lu (%.3f), B: %lu (%.3f) [PI controlled]", + new_gain.gain_r, gain_r_norm, + new_gain.gain_g, gain_g_norm, + new_gain.gain_b, gain_b_norm); + } + + // Reset for next cycle + gain_count = 0; + sum_r = 0; + sum_g = 0; + sum_b = 0; + } + } + } + } + + ESP_LOGI(TAG, "White balance task exiting"); + vTaskDelete(NULL); +} + +esp_err_t example_isp_awb_init(isp_proc_handle_t isp_proc) +{ + if (isp_proc == NULL) { + ESP_LOGE(TAG, "Invalid arguments: isp_proc is NULL"); + return ESP_ERR_INVALID_ARG; + } + + if (s_awb_ctlr != NULL) { + ESP_LOGW(TAG, "AWB controller already initialized"); + return ESP_OK; + } + + s_isp_proc = isp_proc; + + esp_isp_awb_config_t awb_config = {}; + s_get_default_awb_config(&awb_config, CONFIG_EXAMPLE_MIPI_CSI_DISP_HRES, CONFIG_EXAMPLE_MIPI_CSI_DISP_VRES); + + // Create AWB controller for statistics + esp_err_t ret = esp_isp_new_awb_controller(isp_proc, &awb_config, &s_awb_ctlr); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "Failed to create AWB controller: %d", ret); + return ret; + } + + // Configure WBG module + esp_isp_wbg_config_t wbg_config = {}; + ret = esp_isp_wbg_configure(isp_proc, &wbg_config); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "Failed to configure WBG: %d", ret); + ESP_ERROR_CHECK(esp_isp_del_awb_controller(s_awb_ctlr)); + s_awb_ctlr = NULL; + return ret; + } + + ESP_LOGI(TAG, "AWB and WBG module initialized"); + + return ESP_OK; +} + +esp_err_t example_isp_awb_start(isp_proc_handle_t isp_proc) +{ + esp_err_t ret = ESP_OK; + + if (isp_proc == NULL) { + ESP_LOGE(TAG, "Invalid arguments: isp_proc is NULL"); + return ESP_ERR_INVALID_ARG; + } + + if (s_awb_ctlr == NULL) { + ESP_LOGE(TAG, "AWB not initialized, call example_isp_awb_init() first"); + return ESP_ERR_INVALID_STATE; + } + + // Create queue for statistics (use internal RAM for ISR compatibility) + // Ignore the results of frames that are not received (if any) + s_awb_queue = xQueueCreateWithCaps(1, sizeof(isp_awb_stat_result_t), MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT); + if (s_awb_queue == NULL) { + ESP_LOGE(TAG, "Failed to create AWB queue"); + return ESP_ERR_NO_MEM; + } + + // Register AWB statistics callback + esp_isp_awb_cbs_t awb_cbs = { + .on_statistics_done = s_awb_statistics_callback, + }; + ESP_GOTO_ON_ERROR(esp_isp_awb_register_event_callbacks(s_awb_ctlr, &awb_cbs, NULL), err, TAG, "Failed to register AWB callbacks"); + + ESP_GOTO_ON_ERROR(esp_isp_wbg_enable(isp_proc), err, TAG, "Failed to enable WBG"); + + // Set initial gain (neutral: all channels equal) + isp_wbg_gain_t initial_gain = { + .gain_r = AWB_GAIN_NORM, + .gain_g = AWB_GAIN_NORM, + .gain_b = AWB_GAIN_NORM, + }; + ret = esp_isp_wbg_set_wb_gain(isp_proc, initial_gain); + if (ret != ESP_OK) { + ESP_LOGW(TAG, "Failed to set initial AWB gain: %d", ret); + } + + // Enable AWB controller + ESP_GOTO_ON_ERROR(esp_isp_awb_controller_enable(s_awb_ctlr), err, TAG, "Failed to enable AWB controller"); + + // Start continuous statistics + ESP_GOTO_ON_ERROR(esp_isp_awb_controller_start_continuous_statistics(s_awb_ctlr), err, TAG, "Failed to start continuous statistics"); + + // Create white balance processing task + ESP_GOTO_ON_FALSE(pdPASS == xTaskCreate(s_awb_task, "awb_task", 4096, NULL, 5, &s_awb_task_handle), ESP_FAIL, err, TAG, "Failed to create AWB task"); + + ESP_LOGI(TAG, "AWB enabled: AWB statistics started, WBG module enabled, processing task created"); + return ESP_OK; + +err: + ESP_LOGE(TAG, "AWB start failed: %s (0x%x)", esp_err_to_name(ret), ret); + esp_isp_awb_controller_stop_continuous_statistics(s_awb_ctlr); + esp_isp_awb_controller_disable(s_awb_ctlr); + esp_isp_wbg_disable(isp_proc); + if (s_awb_queue != NULL) { + vQueueDeleteWithCaps(s_awb_queue); + s_awb_queue = NULL; + } + if (s_awb_task_handle != NULL) { + vTaskDelete(s_awb_task_handle); + s_awb_task_handle = NULL; + } + return ret; +} diff --git a/examples/peripherals/isp/multi_pipelines/main/example_awb.h b/examples/peripherals/isp/multi_pipelines/main/example_awb.h new file mode 100644 index 0000000000..d92a0f77ae --- /dev/null +++ b/examples/peripherals/isp/multi_pipelines/main/example_awb.h @@ -0,0 +1,68 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @file example_awb.h + * @brief Auto White Balance (AWB) Functionality + * + * This module implements ISP white balance functionality using hardware AWB controller + * for statistics and WBG module for gain adjustment. + * + * Please note that there are two methods to achieve auto white balance tuning: + * 1. use AWB module to get white balance statistics + tune camera sensor gain registers + * 2. use AWB module to get white balance statistics + tune the output gain of ISP pipeline by WBG module + * + * This example uses method 2 to achieve auto white balance tuning. + * + * How it works: + * - ISP hardware AWB controller detects white patches in the configured window + * - White patches are identified based on luminance and color ratio thresholds + * - AWB controller provides statistics (sum_r, sum_g, sum_b) through continuous mode + * - Statistics are sent to a FreeRTOS task via queue + * - The task calculates white balance gains based on statistics + * - Gains are applied through the WBG (White Balance Gain) module + * + * @note Please note that `AWB` and `WBG` refers to 2 different modules in the ISP pipeline, while this example uses both of them to achieve auto white balance tuning. + */ + +#pragma once + +#include "esp_err.h" +#include "driver/isp.h" +#include "driver/isp_awb.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Initialize White Balance module + * + * This function creates and configures the AWB controller for statistics and WBG module + * for gain adjustment. It uses default configuration for AWB controller and WBG module. + * + * @param[in] isp_proc ISP processor handle + * @return + * - ESP_OK: Success + * - ESP_ERR_INVALID_ARG: Invalid arguments + * - ESP_ERR_NO_MEM: Out of memory + */ +esp_err_t example_isp_awb_init(isp_proc_handle_t isp_proc); + +/** + * @brief Enable White Balance module + * + * @param[in] isp_proc ISP processor handle + * @return + * - ESP_OK: Success + * - ESP_ERR_INVALID_ARG: Invalid arguments + * - ESP_ERR_INVALID_STATE: Invalid state + */ +esp_err_t example_isp_awb_start(isp_proc_handle_t isp_proc); + +#ifdef __cplusplus +} +#endif diff --git a/examples/peripherals/isp/multi_pipelines/main/isp_dsi_main.c b/examples/peripherals/isp/multi_pipelines/main/isp_dsi_main.c index dde86cf4f7..2e23475108 100644 --- a/examples/peripherals/isp/multi_pipelines/main/isp_dsi_main.c +++ b/examples/peripherals/isp/multi_pipelines/main/isp_dsi_main.c @@ -26,6 +26,7 @@ // Include modular ISP components #include "example_buffer.h" #include "example_af.h" +#include "example_awb.h" #include "example_pipelines.h" #ifdef CONFIG_EXAMPLE_ISP_CROP_ENABLE #include "example_crop.h" @@ -222,8 +223,23 @@ void app_main(void) return; } +#if CONFIG_ESP32P4_REV_MIN_FULL >= 300 + //---------------White Balance Init------------------// + ret = example_isp_awb_init(isp_proc); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "AWB init fail[%d]", ret); + return; + } + + ret = example_isp_awb_start(isp_proc); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "AWB enable fail[%d]", ret); + return; + } +#endif /* CONFIG_ESP32P4_REV_MIN_FULL >= 300 */ + //---------------AF Init and Start------------------// - ret = example_isp_af_init(isp_proc, dw9714_io_handle, NULL); + ret = example_isp_af_init(isp_proc, dw9714_io_handle); if (ret != ESP_OK) { ESP_LOGE(TAG, "AF init fail[%d]", ret); return;