From 467eddae9fdfb80af9e8141b8798f4e378f93063 Mon Sep 17 00:00:00 2001 From: Chen Chen Date: Mon, 8 Sep 2025 17:24:25 +0800 Subject: [PATCH] feat(isp): support Crop driver on p4 rev3 Add support for crop driver on p4eco5 and update example in `isp/multi_pipelines` --- components/esp_driver_isp/CMakeLists.txt | 4 + .../esp_driver_isp/include/driver/isp.h | 1 + .../esp_driver_isp/include/driver/isp_crop.h | 64 ++ .../include/esp_private/isp_private.h | 1 + components/esp_driver_isp/src/isp_crop.c | 80 ++ .../esp_lcd/dsi/include/esp_lcd_mipi_dsi.h | 2 +- components/hal/esp32p4/include/hal/isp_ll.h | 143 +++ .../esp32p4/include/soc/Kconfig.soc_caps.in | 4 + components/soc/esp32p4/include/soc/soc_caps.h | 1 + docs/doxygen/Doxyfile_esp32p4 | 1 + .../peripherals/camera_driver.rst | 1 - docs/en/api-reference/peripherals/isp.rst | 491 +++++---- .../peripherals/camera_driver.rst | 1 - docs/zh_CN/api-reference/peripherals/isp.rst | 979 +++++++++++++++++- .../dsi_init/example_dsi_init.c | 42 +- .../dsi_init/include/example_dsi_init.h | 39 +- .../camera/dvp_dsi/main/dvp_dsi_main.c | 6 +- .../camera/dvp_dsi/main/example_config.h | 30 +- .../dvp_isp_dsi/main/dvp_isp_dsi_main.c | 6 +- .../camera/dvp_isp_dsi/main/example_config.h | 1 + .../dvp_spi_lcd/main/dvp_spi_lcd_main.c | 4 +- .../camera/dvp_spi_lcd/main/example_config.h | 1 + .../camera/mipi_isp_dsi/main/example_config.h | 1 + .../mipi_isp_dsi/main/mipi_isp_dsi_main.c | 4 +- .../peripherals/isp/multi_pipelines/README.md | 44 +- .../multi_pipelines/main/Kconfig.projbuild | 53 + .../isp/multi_pipelines/main/example_config.h | 1 + .../isp/multi_pipelines/main/isp_dsi_main.c | 248 ++++- .../ppa/ppa_dsi/main/ppa_dsi_main.c | 2 +- 29 files changed, 2015 insertions(+), 240 deletions(-) create mode 100644 components/esp_driver_isp/include/driver/isp_crop.h create mode 100644 components/esp_driver_isp/src/isp_crop.c diff --git a/components/esp_driver_isp/CMakeLists.txt b/components/esp_driver_isp/CMakeLists.txt index f20d5847b7..476fb925a2 100644 --- a/components/esp_driver_isp/CMakeLists.txt +++ b/components/esp_driver_isp/CMakeLists.txt @@ -46,6 +46,10 @@ if(CONFIG_SOC_ISP_WBG_SUPPORTED) list(APPEND srcs "src/isp_wbg.c") endif() +if(CONFIG_SOC_ISP_CROP_SUPPORTED) + list(APPEND srcs "src/isp_crop.c") +endif() + if(NOT ${target} STREQUAL "linux") list(APPEND requires esp_mm) endif() diff --git a/components/esp_driver_isp/include/driver/isp.h b/components/esp_driver_isp/include/driver/isp.h index 0bdd11d8db..7fcd99650a 100644 --- a/components/esp_driver_isp/include/driver/isp.h +++ b/components/esp_driver_isp/include/driver/isp.h @@ -25,3 +25,4 @@ #include "driver/isp_lsc.h" #include "driver/isp_sharpen.h" #include "driver/isp_wbg.h" +#include "driver/isp_crop.h" diff --git a/components/esp_driver_isp/include/driver/isp_crop.h b/components/esp_driver_isp/include/driver/isp_crop.h new file mode 100644 index 0000000000..fa6986d3eb --- /dev/null +++ b/components/esp_driver_isp/include/driver/isp_crop.h @@ -0,0 +1,64 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include +#include "esp_err.h" +#include "driver/isp_types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief ISP crop configurations + */ +typedef struct { + isp_window_t window; /*!< Crop window coordinates */ +} esp_isp_crop_config_t; + +/** + * @brief ISP Crop configuration + * + * @note After calling this API, crop doesn't take into effect until `esp_isp_crop_enable` is called + * + * @param[in] isp_proc Processor handle + * @param[in] config Crop configurations, set NULL to de-configure the ISP Crop + * + * @return + * - ESP_OK On success + * - ESP_ERR_INVALID_ARG If the combination of arguments is invalid + */ +esp_err_t esp_isp_crop_configure(isp_proc_handle_t isp_proc, const esp_isp_crop_config_t *config); + +/** + * @brief Enable ISP crop function + * + * @param[in] isp_proc Processor handle + * + * @return + * - ESP_OK On success + * - ESP_ERR_INVALID_ARG If the combination of arguments is invalid. + * - ESP_ERR_INVALID_STATE Driver state is invalid. + */ +esp_err_t esp_isp_crop_enable(isp_proc_handle_t isp_proc); + +/** + * @brief Disable ISP crop function + * + * @param[in] isp_proc Processor handle + * + * @return + * - ESP_OK On success + * - ESP_ERR_INVALID_ARG If the combination of arguments is invalid. + * - ESP_ERR_INVALID_STATE Driver state is invalid. + */ +esp_err_t esp_isp_crop_disable(isp_proc_handle_t isp_proc); + +#ifdef __cplusplus +} +#endif diff --git a/components/esp_driver_isp/include/esp_private/isp_private.h b/components/esp_driver_isp/include/esp_private/isp_private.h index 4576e3bd4d..a2c5bfe73e 100644 --- a/components/esp_driver_isp/include/esp_private/isp_private.h +++ b/components/esp_driver_isp/include/esp_private/isp_private.h @@ -83,6 +83,7 @@ typedef struct isp_processor_t { ISP_ATOMIC_TYPE(isp_fsm_t) blc_fsm; ISP_ATOMIC_TYPE(isp_fsm_t) ccm_fsm; ISP_ATOMIC_TYPE(isp_fsm_t) color_fsm; + ISP_ATOMIC_TYPE(isp_fsm_t) crop_fsm; ISP_ATOMIC_TYPE(isp_fsm_t) demosaic_fsm; ISP_ATOMIC_TYPE(isp_fsm_t) gamma_fsm; ISP_ATOMIC_TYPE(isp_fsm_t) lsc_fsm; diff --git a/components/esp_driver_isp/src/isp_crop.c b/components/esp_driver_isp/src/isp_crop.c new file mode 100644 index 0000000000..dd879eec72 --- /dev/null +++ b/components/esp_driver_isp/src/isp_crop.c @@ -0,0 +1,80 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include +#include +#include "sdkconfig.h" +#include "esp_log.h" +#include "esp_check.h" +#include "driver/isp_core.h" +#include "driver/isp_crop.h" +#include "esp_private/isp_private.h" +#include "hal/efuse_hal.h" +#include "soc/chip_revision.h" + +/*--------------------------------------------------------------- + Crop +---------------------------------------------------------------*/ + +#define IS_EVEN(x) ((x) % 2 == 0) +#define IS_ODD(x) ((x) % 2 != 0) + +static const char *TAG = "ISP_CROP"; + +esp_err_t esp_isp_crop_configure(isp_proc_handle_t isp_proc, const esp_isp_crop_config_t *config) +{ +#if CONFIG_IDF_TARGET_ESP32P4 + unsigned chip_version = efuse_hal_chip_revision(); + if (!ESP_CHIP_REV_ABOVE(chip_version, 300)) { + ESP_RETURN_ON_FALSE(false, ESP_ERR_NOT_SUPPORTED, TAG, "Crop is not supported on ESP32P4 chips prior than v3.0"); + } +#endif + uint32_t x_start = config->window.top_left.x; + uint32_t x_end = config->window.btm_right.x; + uint32_t y_start = config->window.top_left.y; + uint32_t y_end = config->window.btm_right.y; + + ESP_RETURN_ON_FALSE(isp_proc && config, ESP_ERR_INVALID_ARG, TAG, "invalid argument: null pointer"); + ESP_RETURN_ON_FALSE(isp_proc->crop_fsm == ISP_FSM_INIT, ESP_ERR_INVALID_STATE, TAG, "crop is enabled already"); + ESP_RETURN_ON_FALSE(x_start < x_end, ESP_ERR_INVALID_ARG, TAG, "invalid window x coordinates"); + ESP_RETURN_ON_FALSE(y_start < y_end, ESP_ERR_INVALID_ARG, TAG, "invalid window y coordinates"); + ESP_RETURN_ON_FALSE(x_end <= isp_proc->h_res, ESP_ERR_INVALID_ARG, TAG, "window exceeds horizontal resolution"); + ESP_RETURN_ON_FALSE(y_end <= isp_proc->v_res, ESP_ERR_INVALID_ARG, TAG, "window exceeds vertical resolution"); + + // Check the validity of x/y coordinates + ESP_RETURN_ON_FALSE(IS_EVEN(x_start), ESP_ERR_INVALID_ARG, TAG, "x_start must be even"); + ESP_RETURN_ON_FALSE(IS_ODD(x_end), ESP_ERR_INVALID_ARG, TAG, "x_end must be odd"); + ESP_RETURN_ON_FALSE(IS_EVEN(y_start), ESP_ERR_INVALID_ARG, TAG, "y_start must be even"); + ESP_RETURN_ON_FALSE(IS_ODD(y_end), ESP_ERR_INVALID_ARG, TAG, "y_end must be odd"); + + isp_ll_crop_set_clk_ctrl_mode(isp_proc->hal.hw, ISP_LL_PIPELINE_CLK_CTRL_AUTO); + isp_ll_crop_set_window(isp_proc->hal.hw, x_start, x_end, y_start, y_end); + + return ESP_OK; +} + +esp_err_t esp_isp_crop_enable(isp_proc_handle_t isp_proc) +{ + ESP_RETURN_ON_FALSE(isp_proc, ESP_ERR_INVALID_ARG, TAG, "invalid argument: null pointer"); + ESP_RETURN_ON_FALSE(isp_proc->crop_fsm == ISP_FSM_INIT, ESP_ERR_INVALID_STATE, TAG, "crop is enabled already"); + + isp_ll_crop_enable(isp_proc->hal.hw, true); + isp_proc->crop_fsm = ISP_FSM_ENABLE; + + ESP_LOGD(TAG, "Crop enabled"); + return ESP_OK; +} + +esp_err_t esp_isp_crop_disable(isp_proc_handle_t isp_proc) +{ + ESP_RETURN_ON_FALSE(isp_proc, ESP_ERR_INVALID_ARG, TAG, "invalid argument: null pointer"); + ESP_RETURN_ON_FALSE(isp_proc->crop_fsm == ISP_FSM_ENABLE, ESP_ERR_INVALID_STATE, TAG, "crop isn't enabled yet"); + + isp_ll_crop_enable(isp_proc->hal.hw, false); + isp_proc->crop_fsm = ISP_FSM_INIT; + + ESP_LOGD(TAG, "Crop disabled"); + return ESP_OK; +} diff --git a/components/esp_lcd/dsi/include/esp_lcd_mipi_dsi.h b/components/esp_lcd/dsi/include/esp_lcd_mipi_dsi.h index 1a78873968..99917c7742 100644 --- a/components/esp_lcd/dsi/include/esp_lcd_mipi_dsi.h +++ b/components/esp_lcd/dsi/include/esp_lcd_mipi_dsi.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ diff --git a/components/hal/esp32p4/include/hal/isp_ll.h b/components/hal/esp32p4/include/hal/isp_ll.h index 253c7a2114..e3e99b7368 100644 --- a/components/hal/esp32p4/include/hal/isp_ll.h +++ b/components/hal/esp32p4/include/hal/isp_ll.h @@ -70,7 +70,11 @@ extern "C" { #define ISP_LL_EVENT_WBG_FRAME (1<<30) #define ISP_LL_EVENT_CROP_ERR (1<<31) +#if HAL_CONFIG(CHIP_SUPPORT_MIN_REV) >= 300 +#define ISP_LL_EVENT_ALL_MASK (0xFFFFFFFF) +#else #define ISP_LL_EVENT_ALL_MASK (0x1FFFFFFF) +#endif #define ISP_LL_EVENT_AF_MASK (ISP_LL_EVENT_AF_FDONE | ISP_LL_EVENT_AF_ENV) #define ISP_LL_EVENT_AE_MASK (ISP_LL_EVENT_AE_FDONE | ISP_LL_EVENT_AE_ENV) #define ISP_LL_EVENT_AWB_MASK (ISP_LL_EVENT_AWB_FDONE | ISP_LL_EVENT_WBG_FRAME) @@ -199,6 +203,23 @@ typedef enum { ISP_SHADOW_MODE_UPDATE_ONLY_NEXT_VSYNC, } isp_ll_shadow_mode_t; +/*--------------------------------------------------------------- + Crop +---------------------------------------------------------------*/ + +/** + * @brief ISP crop error types + */ +typedef enum { + ISP_LL_CROP_ERR_X_MISMATCH = (1 << 0), /*!< X end coordinate exceeds image size */ + ISP_LL_CROP_ERR_Y_MISMATCH = (1 << 1), /*!< Y end coordinate exceeds image size */ + ISP_LL_CROP_ERR_X_END_EVEN = (1 << 2), /*!< X end coordinate is even (should be odd) */ + ISP_LL_CROP_ERR_Y_END_EVEN = (1 << 3), /*!< Y end coordinate is even (should be odd) */ + ISP_LL_CROP_ERR_X_START_ODD = (1 << 4), /*!< X start coordinate is odd (should be even) */ + ISP_LL_CROP_ERR_Y_START_ODD = (1 << 5), /*!< Y start coordinate is odd (should be even) */ +} isp_ll_crop_error_t; + + /*--------------------------------------------------------------- Clock ---------------------------------------------------------------*/ @@ -2468,6 +2489,128 @@ static inline void isp_ll_hist_set_rgb_coefficient(isp_dev_t *hw, const isp_hist HAL_FORCE_MODIFY_U32_REG_FIELD(hw->hist_coeff, hist_coeff_b, rgb_coeff->coeff_b.decimal); } +/*--------------------------------------------------------------- + CROP +---------------------------------------------------------------*/ + +#if HAL_CONFIG(CHIP_SUPPORT_MIN_REV) >= 300 +/** + * @brief Set crop clock control mode + * + * @param[in] hw Hardware instance address + * @param[in] mode 'isp_ll_pipeline_clk_ctrl_t` + */ +static inline void isp_ll_crop_set_clk_ctrl_mode(isp_dev_t *hw, isp_ll_pipeline_clk_ctrl_t mode) +{ + hw->clk_en.clk_crop_force_on = mode; +} + +/** + * @brief Enable/Disable ISP crop + * + * @param[in] hw Hardware instance address + * @param[in] en enable / disable + */ +static inline void isp_ll_crop_enable(isp_dev_t *hw, bool enable) +{ + hw->cntl.crop_en = enable; +} + +/** + * @brief Set ISP crop window coordinates + * + * @param[in] hw Hardware instance address + * @param[in] x_start Crop start x coordinate (0 to image_width-1) + * @param[in] x_end Crop end x coordinate (x_start+1 to image_width-1) + * @param[in] y_start Crop start y coordinate (0 to image_height-1) + * @param[in] y_end Crop end y coordinate (y_start+1 to image_height-1) + */ +static inline void isp_ll_crop_set_window(isp_dev_t *hw, + uint32_t x_start, uint32_t x_end, + uint32_t y_start, uint32_t y_end) +{ + hw->crop_x_capture.crop_x_start = x_start; + hw->crop_x_capture.crop_x_end = x_end; + hw->crop_y_capture.crop_y_start = y_start; + hw->crop_y_capture.crop_y_end = y_end; +} + +/** + * @brief Get crop window coordinates + * + * @param[in] hw Hardware instance address + * @param[out] x_start Crop start x coordinate (0 to image_width-1) + * @param[out] x_end Crop end x coordinate (x_start+1 to image_width-1) + * @param[out] y_start Crop start y coordinate (0 to image_height-1) + * @param[out] y_end Crop end y coordinate (y_start+1 to image_height-1) + */ +static inline void isp_ll_crop_get_window(isp_dev_t *hw, + uint32_t *x_start, uint32_t *x_end, + uint32_t *y_start, uint32_t *y_end) +{ + *x_start = hw->crop_x_capture.crop_x_start; + *x_end = hw->crop_x_capture.crop_x_end; + *y_start = hw->crop_y_capture.crop_y_start; + *y_end = hw->crop_y_capture.crop_y_end; +} + +/** + * @brief Get crop error status + * + * @param[in] hw Hardware instance address + * @param[out] error_bits Error status bits + */ +static inline void isp_ll_crop_get_error_status(isp_dev_t *hw, uint32_t *error_bits) +{ + *error_bits = hw->crop_err_st.val & 0x3F; // Retrieve lower 6 bits +} + +/** + * @brief Clear crop error + * + * @param[in] hw Hardware instance address + */ +static inline void isp_ll_crop_clear_error(isp_dev_t *hw) +{ + hw->crop_ctrl.crop_sft_rst = 1; +} + +#else +static inline void isp_ll_crop_set_clk_ctrl_mode(isp_dev_t *hw, isp_ll_pipeline_clk_ctrl_t mode) +{ + // for compatibility +} + +static inline void isp_ll_crop_enable(isp_dev_t *hw, bool enable) +{ + // for compatibility +} + +static inline void isp_ll_crop_set_window(isp_dev_t *hw, + uint32_t x_start, uint32_t x_end, + uint32_t y_start, uint32_t y_end) +{ + // for compatibility +} + +static inline void isp_ll_crop_get_window(isp_dev_t *hw, + uint32_t *x_start, uint32_t *x_end, + uint32_t *y_start, uint32_t *y_end) +{ + // for compatibility +} + +static inline void isp_ll_crop_get_error_status(isp_dev_t *hw, uint32_t *error_bits) +{ + // for compatibility +} + +static inline void isp_ll_crop_clear_error(isp_dev_t *hw) +{ + // for compatibility +} +#endif //#if HAL_CONFIG(CHIP_SUPPORT_MIN_REV) >= 300 + #ifdef __cplusplus } #endif diff --git a/components/soc/esp32p4/include/soc/Kconfig.soc_caps.in b/components/soc/esp32p4/include/soc/Kconfig.soc_caps.in index 18562f8660..d7fd3d68cd 100644 --- a/components/soc/esp32p4/include/soc/Kconfig.soc_caps.in +++ b/components/soc/esp32p4/include/soc/Kconfig.soc_caps.in @@ -927,6 +927,10 @@ config SOC_ISP_COLOR_SUPPORTED bool default y +config SOC_ISP_CROP_SUPPORTED + bool + default y + config SOC_ISP_DEMOSAIC_SUPPORTED bool default y diff --git a/components/soc/esp32p4/include/soc/soc_caps.h b/components/soc/esp32p4/include/soc/soc_caps.h index c905669cb0..5f137d6a47 100644 --- a/components/soc/esp32p4/include/soc/soc_caps.h +++ b/components/soc/esp32p4/include/soc/soc_caps.h @@ -352,6 +352,7 @@ #define SOC_ISP_BLC_SUPPORTED 1 #define SOC_ISP_CCM_SUPPORTED 1 #define SOC_ISP_COLOR_SUPPORTED 1 +#define SOC_ISP_CROP_SUPPORTED 1 #define SOC_ISP_DEMOSAIC_SUPPORTED 1 #define SOC_ISP_DVP_SUPPORTED 1 #define SOC_ISP_LSC_SUPPORTED 1 diff --git a/docs/doxygen/Doxyfile_esp32p4 b/docs/doxygen/Doxyfile_esp32p4 index d366e6ca0a..b015bc4bb3 100644 --- a/docs/doxygen/Doxyfile_esp32p4 +++ b/docs/doxygen/Doxyfile_esp32p4 @@ -37,6 +37,7 @@ INPUT += \ $(PROJECT_PATH)/components/esp_driver_isp/include/driver/isp_bf.h \ $(PROJECT_PATH)/components/esp_driver_isp/include/driver/isp_blc.h \ $(PROJECT_PATH)/components/esp_driver_isp/include/driver/isp_lsc.h \ + $(PROJECT_PATH)/components/esp_driver_isp/include/driver/isp_crop.h \ $(PROJECT_PATH)/components/esp_driver_isp/include/driver/isp_demosaic.h \ $(PROJECT_PATH)/components/esp_driver_isp/include/driver/isp_sharpen.h \ $(PROJECT_PATH)/components/esp_driver_isp/include/driver/isp_core.h \ diff --git a/docs/en/api-reference/peripherals/camera_driver.rst b/docs/en/api-reference/peripherals/camera_driver.rst index 51e7c5fc6a..3c2eb9db71 100644 --- a/docs/en/api-reference/peripherals/camera_driver.rst +++ b/docs/en/api-reference/peripherals/camera_driver.rst @@ -295,4 +295,3 @@ API Reference .. include-build-file:: inc/esp_cam_ctlr_types.inc .. include-build-file:: inc/esp_cam_ctlr_csi.inc .. include-build-file:: inc/esp_cam_ctlr_isp_dvp.inc -.. include-build-file:: inc/isp_core.inc diff --git a/docs/en/api-reference/peripherals/isp.rst b/docs/en/api-reference/peripherals/isp.rst index 5d787b42a7..a652460d20 100644 --- a/docs/en/api-reference/peripherals/isp.rst +++ b/docs/en/api-reference/peripherals/isp.rst @@ -1,25 +1,31 @@ -Image Signal Processor -====================== +Image Signal Processor (ISP) +============================ + +:link_to_translation:`zh_CN:[中文]` Introduction ------------ -{IDF_TARGET_NAME} includes an Image Signal Processor (ISP), which is a feature pipeline that consists of many image processing algorithms. ISP receives image data from the DVP camera or MIPI-CSI camera, or system memory, and writes the processed image data to the system memory through DMA. The ISP is designed to work with other camera controller modules and cannot operate independently. +{IDF_TARGET_NAME} includes an Image Signal Processor (ISP), which is a feature pipeline that consists of many image processing algorithms. ISP receives image data from the DVP camera or MIPI-CSI camera, or system memory, and writes the processed image data to the system memory through DMA. The ISP is designed to work with other camera controller modules and can not operate independently. Terminology ----------- .. list:: - - MIPI-CSI: Camera serial interface, a high-speed serial interface for cameras compliant with MIPI specifications - - DVP: Digital video parallel interface, generally composed of vsync, hsync, de, and data signals - - RAW: Unprocessed data directly output from an image sensor, typically divided into R, Gr, Gb, and B four channels classified into RAW8, RAW10, RAW12, etc., based on bit width - - RGB: Colored image format composed of red, green, and blue colors classified into RGB888, RGB565, etc., based on the bit width of each color - - YUV: Colored image format composed of luminance and chrominance classified into YUV444, YUV422, YUV420, etc., based on the data arrangement - - AF: Auto-focus - - AWB: Auto-white balance - - BF: Bayer noise filter - - BLC: Black Level Correction - - CCM: Color correction matrix + + - MIPI-CSI: Camera serial interface, a high-speed serial interface for cameras compliant with MIPI specifications + - DVP: Digital video parallel interface, generally composed of vsync, hsync, de, and data signals + - RAW: Unprocessed data directly output from an image sensor, typically divided into R, Gr, Gb, and B four channels classified into RAW8, RAW10, RAW12, etc., based on bit width + - RGB: Colored image format composed of red, green, and blue colors classified into RGB888, RGB565, etc., based on the bit width of each color + - YUV: Colored image format composed of luminance and chrominance classified into YUV444, YUV422, YUV420, etc., based on the data arrangement + - AF: Auto focus + - AWB: Auto white balance + - AE: Auto exposure + - HIST: Histogram + - BF: Bayer noise filter + - BLC: Black Level Correction + - LSC: Lens Shading Correction + - CCM: Color correction matrix ISP Pipeline ------------ @@ -39,9 +45,9 @@ ISP Pipeline isp_header [label = "ISP Header"]; isp_tail [label = "ISP Tail"]; isp_chs [label = "Contrast &\n Hue & Saturation", width = 150, height = 70]; - isp_yuv [label = "YUV Limit\nYUB2RGB", width = 120, height = 70]; + isp_yuv [label = "YUV Limit\n YUB2RGB", width = 120, height = 70]; - isp_header -> BF -> LSC -> Demosaic -> CCM -> Gamma -> RGB2YUV -> SHARP -> isp_chs -> isp_yuv -> isp_tail; + isp_header -> BLC -> BF -> LSC -> Demosaic -> CCM -> Gamma -> RGB2YUV -> SHARP -> isp_chs -> isp_yuv -> CROP -> isp_tail; LSC -> HIST Demosaic -> AWB @@ -50,7 +56,7 @@ ISP Pipeline CCM -> AWB Gamma -> AE RGB2YUV -> HIST - SHARP -> AF + RGB2YUV -> AF } Functional Overview @@ -58,22 +64,24 @@ Functional Overview The ISP driver offers following services: -- `Resource Allocation <#isp-resource-allocation>`__ - covers how to allocate ISP resources with properly set of configurations. It also covers how to recycle the resources when they finished working. -- `Enable and disable ISP processor <#isp-enable-disable>`__ - covers how to enable and disable an ISP processor. -- `Get AF statistics in one shot or continuous way <#isp-af-statistics>`__ - covers how to get AF statistics one-shot or continuously. -- `Get AE statistics in one shot or continuous way <#isp-ae-statistics>`__ - covers how to get AE statistics one-shot or continuously. -- `Get AWB statistics in one shot or continuous way <#isp-awb-statistics>`__ - covers how to get AWB white patches statistics one-shot or continuously. -- `Get histogram statistics in one shot or continuous way <#isp-hist-statistics>`__ - covers how to get histogram statistics one-shot or continuously. -- `Enable BF function <#isp_bf>`__ - covers how to enable and configure BF function. -- `Enable LSC function <#isp_lsc>`__ - covers how to enable and configure LSC function. -- `Configure CCM <#isp-ccm-config>`__ - covers how to configure the Color Correction Matrix. -- `Configure Demosaic <#isp-demosaic>`__ - covers how to config the Demosaic function. -- `Enable Gamma Correction <#isp-gamma-correction>`__ - covers how to enable and configure gamma correction. -- `Configure Sharpen <#isp-sharpen>`__ - covers how to config the Sharpen function. -- `Register callback <#isp-callback>`__ - covers how to hook user specific code to ISP driver event callback function. -- `Thread Safety <#isp-thread-safety>`__ - lists which APIs are guaranteed to be thread safe by the driver. -- `Kconfig Options <#isp-kconfig-options>`__ - lists the supported Kconfig options that can bring different effects to the driver. -- `IRAM SAFE <#isp-iram-safe>`__ - describes tips on how to make the ISP interrupt and control functions work better along with a disabled cache. +- :ref:`isp-resource-allocation` - covers how to allocate ISP resources with properly set of configurations. It also covers how to recycle the resources when they finished working. +- :ref:`isp-enable-disable` - covers how to enable and disable an ISP processor. +- :ref:`isp-af-statistics` - covers how to get AF statistics one-shot or continuously. +- :ref:`isp-awb-statistics` - covers how to get AWB white patches statistics one-shot or continuously. +- :ref:`isp-ae-statistics` - covers how to get AE statistics one-shot or continuously. +- :ref:`isp-hist-statistics` - covers how to get histogram statistics one-shot or continuously. +- :ref:`isp-bf` - covers how to enable and configure BF function. +- :ref:`isp-blc` - covers how to enable and configure BLC function. +- :ref:`isp-lsc` - covers how to enable and configure LSC function. +- :ref:`isp-ccm-config` - covers how to configure the CCM. +- :ref:`isp-demosaic` - covers how to configure the Demosaic function. +- :ref:`isp-gamma-correction` - covers how to enable and configure gamma correction. +- :ref:`isp-sharpen` - covers how to configure the sharpening function. +- :ref:`isp-crop` - covers how to enable and configure image cropping function. +- :ref:`isp-callback` - covers how to hook user specific code to ISP driver event callback function. +- :ref:`isp-thread-safety` - lists which APIs are guaranteed to be thread safe by the driver. +- :ref:`isp-kconfig-options` - lists the supported Kconfig options that can bring different effects to the driver. +- :ref:`isp-iram-safe` - describes tips on how to make the ISP interrupt and control functions work better along with a disabled cache. .. _isp-resource-allocation: @@ -85,9 +93,9 @@ Install ISP Driver ISP driver requires the configuration that specified by :cpp:type:`esp_isp_processor_cfg_t`. -If the configurations in :cpp:type:`esp_isp_processor_cfg_t` is specified, users can call :cpp:func:`esp_isp_new_processor` to allocate and initialize an ISP processor. This function will return an ISP processor handle if it runs correctly. You can take following code as reference. +If the configurations in :cpp:type:`esp_isp_processor_cfg_t` is specified, users can call :cpp:func:`esp_isp_new_processor` to allocate and initialize an ISP processor. This function will return an ISP processor handle if it runs correctly. You can take following code as reference: -.. code:: c +.. code-block:: c esp_isp_processor_cfg_t isp_config = { .clk_src = ISP_CLK_SRC_DEFAULT, @@ -97,17 +105,22 @@ If the configurations in :cpp:type:`esp_isp_processor_cfg_t` is specified, users isp_proc_handle_t isp_proc = NULL; ESP_ERROR_CHECK(esp_isp_new_processor(&isp_config, &isp_proc)); -You can use the created handle to do driver enable / disable the ISP driver and do other ISP module installation. +You can use the created handle to enable/disable the ISP driver and do other ISP module installation. +.. note:: -Install ISP Auto-Focus (AF) Driver + ISP peripheral is necessary if MIPI CSI or ISP_DVP is used as camera controller. This means that even if ISP functions are not needed, you still need to install the ISP driver by calling :cpp:func:`esp_isp_new_processor`. + + If ISP functions are not needed, ISP driver supports bypassing ISP pipelines and enabling only the necessary functions. This can be achieved by setting :cpp:member:`esp_isp_processor_cfg_t::bypass_isp`. + +Install ISP Auto Focus (AF) Driver ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -ISP auto-focus (AF) driver requires the configuration that specified by :cpp:type:`esp_isp_af_config_t`. +ISP auto focus (AF) driver requires the configuration that specified by :cpp:type:`esp_isp_af_config_t`. -If the configurations in :cpp:type:`esp_isp_af_config_t` is specified, users can call :cpp:func:`esp_isp_new_af_controller` to allocate and initialize an ISP AF processor. This function will return an ISP AF processor handle if it runs correctly. You can take following code as reference. +If the configurations in :cpp:type:`esp_isp_af_config_t` is specified, users can call :cpp:func:`esp_isp_new_af_controller` to allocate and initialize an ISP AF controller. This function will return an ISP AF controller handle if it runs correctly. You can take following code as reference: -.. code:: c +.. code-block:: c esp_isp_af_config_t af_config = { .edge_thresh = 128, @@ -115,16 +128,16 @@ If the configurations in :cpp:type:`esp_isp_af_config_t` is specified, users can isp_af_ctlr_t af_ctrlr = NULL; ESP_ERROR_CHECK(esp_isp_new_af_controller(isp_proc, &af_config, &af_ctrlr)); -You can use the created handle to do driver enable / disable the ISP AF driver and ISP AF Env module installation. +You can use the created handle to enable/disable the ISP AF driver and install ISP AF environment detector module. -Install ISP Auto-White-Balance (AWB) Driver +Install ISP Auto White Balance (AWB) Driver ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -ISP auto-white-balance (AWB) driver requires the configuration specified by :cpp:type:`esp_isp_awb_config_t`. +ISP auto white balance (AWB) driver requires the configuration specified by :cpp:type:`esp_isp_awb_config_t`. -If an :cpp:type:`esp_isp_awb_config_t` configuration is specified, you can call :cpp:func:`esp_isp_new_awb_controller` to allocate and initialize an ISP AWB processor. This function will return an ISP AWB processor handle on success. You can take following code as reference. +If an :cpp:type:`esp_isp_awb_config_t` configuration is specified, you can call :cpp:func:`esp_isp_new_awb_controller` to allocate and initialize an ISP AWB controller. This function will return an ISP AWB controller handle on success. You can take following code as reference: -.. code:: c +.. code-block:: c isp_awb_ctlr_t awb_ctlr = NULL; uint32_t image_width = 800; @@ -138,14 +151,14 @@ If an :cpp:type:`esp_isp_awb_config_t` configuration is specified, you can call The AWB handle created in this step is required by other AWB APIs and AWB scheme. -Install ISP Auto-Exposure (AE) Driver +Install ISP Auto Exposure (AE) Driver ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -ISP auto-exposure (AE) driver requires the configuration that specified by :cpp:type:`esp_isp_ae_config_t`. +ISP auto exposure (AE) driver requires the configuration that specified by :cpp:type:`esp_isp_ae_config_t`. -If the configurations in :cpp:type:`esp_isp_ae_config_t` is specified, users can call :cpp:func:`esp_isp_new_ae_controller` to allocate and initialize an ISP AE processor. This function will return an ISP AE processor handle if it runs correctly. You can take following code as reference. +If the configurations in :cpp:type:`esp_isp_ae_config_t` is specified, call :cpp:func:`esp_isp_new_ae_controller` to allocate and initialize an ISP AE controller. This function will return an ISP AE controller handle if it runs correctly. You can take following code as reference. -.. code:: c +.. code-block:: c esp_isp_ae_config_t ae_config = { .sample_point = ISP_AE_SAMPLE_POINT_AFTER_DEMOSAIC, @@ -154,20 +167,20 @@ If the configurations in :cpp:type:`esp_isp_ae_config_t` is specified, users can isp_ae_ctlr_t ae_ctlr = NULL; ESP_ERROR_CHECK(esp_isp_new_ae_controller(isp_proc, &ae_config, &ae_ctlr)); -You can use the created handle to do driver enable / disable the ISP AE driver and ISP AE environment detector setup. +You can use the created handle to enable/disable the ISP AE driver and do ISP AE environment detector setup. -Install ISP histogram (HIST) Driver +Install ISP Histogram (HIST) Driver ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ISP histogram (HIST) driver requires the configuration that specified by :cpp:type:`esp_isp_hist_config_t`. -If the configurations in :cpp:type:`esp_isp_hist_config_t` is specified, users can call :cpp:func:`esp_isp_new_hist_controller` to allocate and initialize an ISP Histogram processor. This function will return an ISP HIST processor handle if it runs correctly. You can take following code as reference. +If the configurations in :cpp:type:`esp_isp_hist_config_t` is specified, users can call :cpp:func:`esp_isp_new_hist_controller` to allocate and initialize an ISP Histogram controller. This function will return an ISP HIST controller handle if it runs correctly. You can take following code as reference. .. list:: - - The sum of all subwindows weight's decimal value should be 256 or the statistics will be small, and integer value should be 0. - - The sum of all RGB coefficients' decimal value should be 256 or the statistics will be small, and integer value should be 0. - - The segment_threshold must be 0 ~ 255 and in order + - The sum of all subwindow weights' decimal values should be 256; otherwise, the statistics will be small. The integer value should be 0. + - The sum of all RGB coefficients' decimal values should be 256; otherwise, the statistics will be small. The integer value should be 0. + - The segment_threshold must be 0–255 and in order. .. code:: c @@ -197,20 +210,20 @@ If the configurations in :cpp:type:`esp_isp_hist_config_t` is specified, users c isp_hist_ctlr_t hist_ctlr_ctlr = NULL; ESP_ERROR_CHECK(esp_isp_new_hist_controller(isp_proc, &hist_config, &hist_ctlr)); -You can use the created handle to do driver enable / disable the ISP HIST driver setup. +You can use the created handle to enable/disable the ISP HIST driver setup. -Uninstall ISP Driver(s) +Uninstall ISP Drivers ~~~~~~~~~~~~~~~~~~~~~~~ -If a previously installed ISP driver(s) are not needed, it's recommended to recycle the resource by following APIs to release the underlying hardware: +If previously installed ISP drivers are no longer needed, it's recommended to recycle the resource by following APIs to release the underlying hardware: .. list:: - :cpp:func:`esp_isp_del_processor`, for ISP processor. - - :cpp:func:`esp_isp_del_af_controller`, for ISP AF processor. - - :cpp:func:`esp_isp_del_awb_controller`, for ISP AWB processor. - - :cpp:func:`esp_isp_del_ae_controller`, for ISP AE processor. - - :cpp:func:`esp_isp_del_hist_controller`, for ISP Histogram processor. + - :cpp:func:`esp_isp_del_af_controller`, for ISP AF controller. + - :cpp:func:`esp_isp_del_awb_controller`, for ISP AWB controller. + - :cpp:func:`esp_isp_del_ae_controller`, for ISP AE controller. + - :cpp:func:`esp_isp_del_hist_controller`, for ISP Histogram controller. .. _isp-enable-disable: @@ -226,10 +239,10 @@ Before doing ISP pipeline, you need to enable the ISP processor first, by callin Calling :cpp:func:`esp_isp_disable` does the opposite, that is, put the driver back to the **init** state. -ISP AF Processor -~~~~~~~~~~~~~~~~ +ISP AF Controller +~~~~~~~~~~~~~~~~~ -Before doing ISP AF, you need to enable the ISP AF processor first, by calling :cpp:func:`esp_isp_af_controller_enable`. This function: +Before doing ISP AF, you need to enable the ISP AF controller first, by calling :cpp:func:`esp_isp_af_controller_enable`. This function: * Switches the driver state from **init** to **enable**. @@ -240,13 +253,14 @@ Calling :cpp:func:`esp_isp_af_controller_disable` does the opposite, that is, pu AF One-shot and Continuous Statistics ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Calling :cpp:func:`esp_isp_af_controller_get_oneshot_statistics` to get oneshot AF statistics result. You can take following code as reference. +Calling :cpp:func:`esp_isp_af_controller_get_oneshot_statistics` to get one-shot AF statistics result. You can take following code as reference. -Aside from the above oneshot API, the ISP AF driver also provides a way to start AF statistics continuously. Calling :cpp:func:`esp_isp_af_controller_start_continuous_statistics` to start the continuous statistics and :cpp:func:`esp_isp_af_controller_stop_continuous_statistics` to stop it. +Aside from the above one-shot API, the ISP AF driver also provides a way to start AF statistics continuously. Calling :cpp:func:`esp_isp_af_controller_start_continuous_statistics` to start the continuous statistics and :cpp:func:`esp_isp_af_controller_stop_continuous_statistics` to stop it. -Note that if you want to use the continuous statistics, you need to register the :cpp:member:`esp_isp_af_env_detector_evt_cbs_t::on_env_statistics_done` or :cpp:member:`esp_isp_af_env_detector_evt_cbs_t::on_env_change` callback to get the statistics result. See how to register in `Register Event Callbacks <#isp-callback>`__ +Note that if you want to use the continuous statistics, you need to register the :cpp:member:`esp_isp_af_env_detector_evt_cbs_t::on_env_statistics_done` or :cpp:member:`esp_isp_af_env_detector_evt_cbs_t::on_env_change` callbacks to get the statistics result. See how to register in :ref:`isp-callback`. .. note:: + When you use the continuous statistics, AF Environment Detector will be invalid. .. code:: c @@ -258,7 +272,7 @@ Note that if you want to use the continuous statistics, you need to register the ESP_ERROR_CHECK(esp_isp_new_af_controller(isp_proc, &af_config, &af_ctrlr)); ESP_ERROR_CHECK(esp_isp_af_controller_enable(af_ctrlr)); isp_af_result_t result = {}; - /* Trigger the AF statistics and get its result for one time with timeout value 2000ms. */ + /* Trigger the AF statistics and get its result for one time with timeout value 2000 ms */ ESP_ERROR_CHECK(esp_isp_af_controller_get_oneshot_statistics(af_ctrlr, 2000, &result)); /* Start continuous AF statistics */ @@ -269,17 +283,17 @@ Note that if you want to use the continuous statistics, you need to register the /* Stop continuous AF statistics */ ESP_ERROR_CHECK(esp_isp_af_controller_stop_continuous_statistics(af_ctrlr)); - /* Disable the af controller */ + /* Disable the AF controller */ ESP_ERROR_CHECK(esp_isp_af_controller_disable(af_ctrlr)); - /* Delete the af controller and free the resources */ + /* Delete the AF controller and free the resources */ ESP_ERROR_CHECK(esp_isp_del_af_controller(af_ctrlr)); Set AF Environment Detector ^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Calling :cpp:func:`esp_isp_af_controller_set_env_detector` to set an ISP AF environment detector. You can take following code as reference. +Calling :cpp:func:`esp_isp_af_controller_set_env_detector` to set an ISP AF environment detector. You can take following code as reference: -.. code:: c +.. code-block:: c esp_isp_af_env_config_t env_config = { .interval = 10, @@ -291,18 +305,77 @@ Calling :cpp:func:`esp_isp_af_controller_set_env_detector` to set an ISP AF envi Set AF Environment Detector Threshold ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Calling :cpp:func:`esp_isp_af_env_detector_set_threshold` to set the threshold of an ISP AF environment detector. +Calling :cpp:func:`esp_isp_af_controller_set_env_detector_threshold` to set the threshold of an ISP AF environment detector. -.. code:: c +.. code-block:: c int definition_thresh = 0; int luminance_thresh = 0; ESP_ERROR_CHECK(esp_isp_af_env_detector_set_threshold(env_detector, definition_thresh, luminance_thresh)); -ISP AE Processor ----------------- +ISP AWB Controller +~~~~~~~~~~~~~~~~~~ -Before doing ISP AE, you need to enable the ISP AE processor first, by calling :cpp:func:`esp_isp_ae_controller_enable`. This function: +Before doing ISP AWB, you need to enable the ISP AWB controller first, by calling :cpp:func:`esp_isp_awb_controller_enable`. This function: + +* Switches the driver state from **init** to **enable**. + +Calling :cpp:func:`esp_isp_awb_controller_disable` does the opposite, that is, put the driver back to the **init** state. + +.. _isp-awb-statistics: + +AWB One-shot and Continuous Statistics +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Calling :cpp:func:`esp_isp_awb_controller_get_oneshot_statistics` to get oneshot AWB statistics result of white patches. You can take following code as reference. + +Aside from the above one-shot API, the ISP AWB driver also provides a way to start AWB statistics continuously. Calling :cpp:func:`esp_isp_awb_controller_start_continuous_statistics` starts the continuous statistics and :cpp:func:`esp_isp_awb_controller_stop_continuous_statistics` stops it. + +Note that if you want to use the continuous statistics, you need to register the :cpp:member:`esp_isp_awb_cbs_t::on_statistics_done` callback to get the statistics result. See how to register it in :ref:`isp-callback`. + +.. code-block:: c + + bool example_isp_awb_on_statistics_done_cb(isp_awb_ctlr_t awb_ctlr, const esp_isp_awb_evt_data_t *edata, void *user_data); + // ... + isp_awb_ctlr_t awb_ctlr = NULL; + uint32_t image_width = 800; + uint32_t image_height = 600; + /* The AWB configuration, please refer to the API comment for how to tune these parameters */ + esp_isp_awb_config_t awb_config = { + .sample_point = ISP_AWB_SAMPLE_POINT_AFTER_CCM, + ... + }; + isp_awb_stat_result_t stat_res = {}; + /* Create the AWB controller */ + ESP_ERROR_CHECK(esp_isp_new_awb_controller(isp_proc, &awb_config, &awb_ctlr)); + /* Register the AWB callback */ + esp_isp_awb_cbs_t awb_cb = { + .on_statistics_done = example_isp_awb_on_statistics_done_cb, + }; + ESP_ERROR_CHECK(esp_isp_awb_register_event_callbacks(awb_ctlr, &awb_cb, NULL)); + /* Enable the AWB controller */ + ESP_ERROR_CHECK(esp_isp_awb_controller_enable(awb_ctlr)); + + /* Get one-shot AWB statistics result */ + ESP_ERROR_CHECK(esp_isp_awb_controller_get_oneshot_statistics(awb_ctlr, -1, &stat_res)); + + /* Start continuous AWB statistics, note that continuous statistics requires `on_statistics_done` callback */ + ESP_ERROR_CHECK(esp_isp_awb_controller_start_continuous_statistics(awb_ctlr)); + // You can do other stuffs here, the statistics result can be obtained in the callback + // ...... + // vTaskDelay(pdMS_TO_TICKS(1000)); + /* Stop continuous AWB statistics */ + ESP_ERROR_CHECK(esp_isp_awb_controller_stop_continuous_statistics(awb_ctlr)); + + /* Disable the AWB controller */ + ESP_ERROR_CHECK(esp_isp_awb_controller_disable(awb_ctlr)); + /* Delete the AWB controller and free the resources */ + ESP_ERROR_CHECK(esp_isp_del_awb_controller(awb_ctlr)); + +ISP AE Controller +~~~~~~~~~~~~~~~~~ + +Before doing ISP AE, you need to enable the ISP AE controller first, by calling :cpp:func:`esp_isp_ae_controller_enable`. This function: * Switches the driver state from **init** to **enable**. @@ -315,16 +388,17 @@ AE One-shot and Continuous Statistics Calling :cpp:func:`esp_isp_ae_controller_get_oneshot_statistics` to get oneshot AE statistics result. You can take following code as reference. -When you use AE oneshot statistics, the AE continuous mode need to be disabled otherwise the result may be overwritten by the environment detector. After oneshot operation finishes, you need to restart continuous mode again. +When using AE oneshot statistics, the AE continuous mode need to be disabled otherwise the result may be overwritten by the environment detector. After oneshot operation finishes, you need to restart continuous mode again. Aside from the above oneshot API, the ISP AE driver also provides a way to start AE statistics continuously. Calling :cpp:func:`esp_isp_ae_controller_start_continuous_statistics` to start the continuous statistics and :cpp:func:`esp_isp_ae_controller_stop_continuous_statistics` to stop it. -Note that if you want to use the continuous statistics, you need to register the :cpp:member:`esp_isp_ae_env_detector_evt_cbs_t::on_statistics_done` or :cpp:member:`esp_isp_ae_env_detector_evt_cbs_t::on_change` callback to get the statistics result. See how to register in `Register Event Callbacks <#isp-callback>`__ +Note that if you want to use the continuous statistics, you need to register the :cpp:member:`esp_isp_ae_env_detector_evt_cbs_t::on_env_statistics_done` or :cpp:member:`esp_isp_ae_env_detector_evt_cbs_t::on_env_change` callback to get the statistics result. See how to register in :ref:`isp-callback`. .. note:: - When using oneshot statistics, the AE Environment Detector will be temporarily disabled and will automatically recover once the oneshot is complete. -.. code:: c + When using oneshot statistics, the AE environment detector will be temporarily disabled and will automatically recover once the oneshot is completed. + +.. code-block:: c esp_isp_ae_config_t ae_config = { .sample_point = ISP_AE_SAMPLE_POINT_AFTER_DEMOSAIC, @@ -333,7 +407,7 @@ Note that if you want to use the continuous statistics, you need to register the ESP_ERROR_CHECK(esp_isp_new_ae_controller(isp_proc, &ae_config, &ae_ctlr)); ESP_ERROR_CHECK(esp_isp_ae_controller_enable(ae_ctlr)); isp_ae_result_t result = {}; - /* Trigger the AE statistics and get its result for one time with timeout value 2000ms. */ + /* Trigger the AE statistics and get its result for one time with timeout value 2000 ms. */ ESP_ERROR_CHECK(esp_isp_ae_controller_get_oneshot_statistics(ae_ctlr, 2000, &result)); /* Start continuous AE statistics */ @@ -344,9 +418,9 @@ Note that if you want to use the continuous statistics, you need to register the /* Stop continuous AE statistics */ ESP_ERROR_CHECK(esp_isp_ae_controller_stop_continuous_statistics(ae_ctlr)); - /* Disable the ae controller */ + /* Disable the AE controller */ ESP_ERROR_CHECK(esp_isp_ae_controller_disable(ae_ctlr)); - /* Delete the ae controller and free the resources */ + /* Delete the AE controller and free the resources */ ESP_ERROR_CHECK(esp_isp_del_ae_controller(ae_ctlr)); Set AE Environment Detector @@ -364,7 +438,7 @@ Calling :cpp:func:`esp_isp_ae_controller_set_env_detector` to set an ISP AE envi Set AE Environment Detector Threshold ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Calling :cpp:func:`esp_isp_ae_controller_set_env_detector_threshold` to set the thresholds(1-255) of an ISP AE environment detector. +Calling :cpp:func:`esp_isp_ae_controller_set_env_detector_threshold` to set the thresholds (1-255) of an ISP AE environment detector. .. code:: c @@ -374,71 +448,12 @@ Calling :cpp:func:`esp_isp_ae_controller_set_env_detector_threshold` to set the }; ESP_ERROR_CHECK(esp_isp_ae_controller_set_env_detector_threshold(ae_ctlr, env_thresh)); -ISP AWB Processor -~~~~~~~~~~~~~~~~~ - -Before doing ISP AWB, you need to enable the ISP AWB processor first, by calling :cpp:func:`esp_isp_awb_controller_enable`. This function: - -* Switches the driver state from **init** to **enable**. - -Calling :cpp:func:`esp_isp_awb_controller_disable` does the opposite, that is, put the driver back to the **init** state. - -.. _isp-awb-statistics: - -AWB One-shot and Continuous Statistics -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Calling :cpp:func:`esp_isp_awb_controller_get_oneshot_statistics` to get oneshot AWB statistics result of white patches. You can take following code as reference. - -Aside from the above oneshot API, the ISP AWB driver also provides a way to start AWB statistics continuously. Calling :cpp:func:`esp_isp_awb_controller_start_continuous_statistics` starts the continuous statistics and :cpp:func:`esp_isp_awb_controller_stop_continuous_statistics` stops it. - -Note that if you want to use the continuous statistics, you need to register the :cpp:member:`esp_isp_awb_cbs_t::on_statistics_done` callback to get the statistics result. See how to register it in `Register Event Callbacks <#isp-callback>`__ - -.. code:: c - - bool example_isp_awb_on_statistics_done_cb(isp_awb_ctlr_t awb_ctlr, const esp_isp_awb_evt_data_t *edata, void *user_data); - // ... - isp_awb_ctlr_t awb_ctlr = NULL; - uint32_t image_width = 800; - uint32_t image_height = 600; - /* The AWB configuration, please refer to the API comment for how to tune these parameters */ - esp_isp_awb_config_t awb_config = { - .sample_point = ISP_AWB_SAMPLE_POINT_AFTER_CCM, - ... - }; - isp_awb_stat_result_t stat_res = {}; - /* Create the awb controller */ - ESP_ERROR_CHECK(esp_isp_new_awb_controller(isp_proc, &awb_config, &awb_ctlr)); - /* Register AWB callback */ - esp_isp_awb_cbs_t awb_cb = { - .on_statistics_done = example_isp_awb_on_statistics_done_cb, - }; - ESP_ERROR_CHECK(esp_isp_awb_register_event_callbacks(awb_ctlr, &awb_cb, NULL)); - /* Enabled the awb controller */ - ESP_ERROR_CHECK(esp_isp_awb_controller_enable(awb_ctlr)); - - /* Get oneshot AWB statistics result */ - ESP_ERROR_CHECK(esp_isp_awb_controller_get_oneshot_statistics(awb_ctlr, -1, &stat_res)); - - /* Start continuous AWB statistics, note that continuous statistics requires `on_statistics_done` callback */ - ESP_ERROR_CHECK(esp_isp_awb_controller_start_continuous_statistics(awb_ctlr)); - // You can do other stuffs here, the statistics result can be obtained in the callback - // ...... - // vTaskDelay(pdMS_TO_TICKS(1000)); - /* Stop continuous AWB statistics */ - ESP_ERROR_CHECK(esp_isp_awb_controller_stop_continuous_statistics(awb_ctlr)); - - /* Disable the awb controller */ - ESP_ERROR_CHECK(esp_isp_awb_controller_disable(awb_ctlr)); - /* Delete the awb controller and free the resources */ - ESP_ERROR_CHECK(esp_isp_del_awb_controller(awb_ctlr)); - .. _isp-hist: -ISP histogram Processor ------------------------ +ISP Histogram Controller +~~~~~~~~~~~~~~~~~~~~~~~~ -Before doing ISP histogram statistics, you need to enable the ISP histogram processor first, by calling :cpp:func:`esp_isp_hist_controller_enable`. This function: +Before doing ISP histogram statistics, you need to enable the ISP histogram controller first, by calling :cpp:func:`esp_isp_hist_controller_enable`. This function: * Switches the driver state from **init** to **enable**. @@ -453,7 +468,7 @@ Calling :cpp:func:`esp_isp_hist_controller_get_oneshot_statistics` to get onesho Aside from the above oneshot API, the ISP histogram driver also provides a way to start histogram statistics continuously. Calling :cpp:func:`esp_isp_hist_controller_start_continuous_statistics` starts the continuous statistics and :cpp:func:`esp_isp_hist_controller_stop_continuous_statistics` stops it. -Note that if you want to use the continuous statistics, you need to register the :cpp:member:`esp_isp_hist_cbs_t::on_statistics_done` callback to get the statistics result. See how to register it in `Register Event Callbacks <#isp-callback>`__ +Note that if you want to use the continuous statistics, you need to register the :cpp:member:`esp_isp_hist_cbs_t::on_statistics_done` callback to get the statistics result. See how to register it in :ref:`isp-callback`. .. code:: c @@ -473,16 +488,16 @@ Note that if you want to use the continuous statistics, you need to register the esp_isp_hist_controller_enable(hist_ctlr); -.. _isp_bf: +.. _isp-bf: -ISP BF Processor -~~~~~~~~~~~~~~~~ +ISP BF Controller +~~~~~~~~~~~~~~~~~ This pipeline is used for doing image input denoising under bayer mode. Calling :cpp:func:`esp_isp_bf_configure` to configure BF function, you can take following code as reference. -.. code:: c +.. code-block:: c esp_isp_bf_config_t bf_config = { .denoising_level = 5, @@ -498,14 +513,71 @@ Calling :cpp:func:`esp_isp_bf_configure` to configure BF function, you can take :cpp:member:`esp_isp_bf_config_t::bf_template` is used for bayer denoise. You can set the :cpp:member:`esp_isp_bf_config_t::bf_template` with a Gaussian filter template or an average filter template. -After calling :cpp:func:`esp_isp_bf_configure`, you need to enable the ISP BF processor, by calling :cpp:func:`esp_isp_bf_enable`. This function: +After calling :cpp:func:`esp_isp_bf_configure`, you need to enable the ISP BF controller, by calling :cpp:func:`esp_isp_bf_enable`. This function: * Switches the driver state from **init** to **enable**. Calling :cpp:func:`esp_isp_bf_disable` does the opposite, that is, put the driver back to the **init** state. +.. _isp-blc: -.. _isp_lsc: +ISP BLC Controller +^^^^^^^^^^^^^^^^^^ + +Black Level Correction (BLC) aims for the issues caused by the uneven black level of the image. + +Calling :cpp:func:`esp_isp_blc_configure` to configure the BLC module to do the correction. + +.. code-block:: c + + esp_isp_blc_config_t blc_config = { + .window = { + .top_left = { + .x = 0, + .y = 0, + }, + .btm_right = { + .x = CONFIG_EXAMPLE_MIPI_CSI_DISP_HRES, + .y = CONFIG_EXAMPLE_MIPI_CSI_DISP_VRES, + }, + }, + .filter_enable = true, + .filter_threshold = { + .top_left_chan_thresh = 128, + .top_right_chan_thresh = 128, + .bottom_left_chan_thresh = 128, + .bottom_right_chan_thresh = 128, + }, + .stretch = { + .top_left_chan_stretch_en = true, + .top_right_chan_stretch_en = true, + .bottom_left_chan_stretch_en = true, + .bottom_right_chan_stretch_en = true, + }, + }; + ESP_ERROR_CHECK(esp_isp_blc_configure(isp_proc, &blc_config)); + ESP_ERROR_CHECK(esp_isp_blc_enable(isp_proc)); + +After calling :cpp:func:`esp_isp_blc_configure`, you need to enable the ISP BLC controller by calling :cpp:func:`esp_isp_blc_enable`. This function: + +* Switches the driver state from **init** to **enable**. + +Calling :cpp:func:`esp_isp_blc_disable` does the opposite, that is, put the driver back to the **init** state. + +Calling :cpp:func:`esp_isp_blc_set_correction_offset` to set the BLC correction offset. + +.. code-block:: c + + esp_isp_blc_offset_t blc_offset = { + .top_left_chan_offset = 20, + .top_right_chan_offset = 20, + .bottom_left_chan_offset = 20, + .bottom_right_chan_offset = 20, + }; + ESP_ERROR_CHECK(esp_isp_blc_set_correction_offset(isp_proc, &blc_offset)); + + +.. _isp-lsc: ISP LSC Controller ~~~~~~~~~~~~~~~~~~ @@ -535,13 +607,13 @@ Calling :cpp:func:`esp_isp_lsc_configure` to configure the LSC module to do the } ESP_ERROR_CHECK(esp_isp_lsc_configure(isp_proc, &lsc_config)); -After calling :cpp:func:`esp_isp_lsc_configure`, you need to enable the ISP LSC controller, by calling :cpp:func:`esp_isp_lsc_enable`. The LSC can be disabled by calling :cpp:func:`esp_isp_lsc_disable`. It's allowed to call :cpp:func:`esp_isp_lsc_configure` when the LSC isn't enabled, but the LSC function will only take effect when it's enabled. +After calling :cpp:func:`esp_isp_lsc_configure`, you need to enable the ISP LSC controller by calling :cpp:func:`esp_isp_lsc_enable`. The LSC can be disabled by calling :cpp:func:`esp_isp_lsc_disable`. It is allowed to call :cpp:func:`esp_isp_lsc_configure` when the LSC is not enabled, but the LSC function will only take effect when it is enabled. .. _isp-color: -ISP Color Processor -~~~~~~~~~~~~~~~~~~~ +ISP Color Controller +~~~~~~~~~~~~~~~~~~~~ This pipeline is used to adjust the image contrast, saturation, hue and brightness. @@ -565,7 +637,7 @@ Calling :cpp:func:`esp_isp_color_configure` to configure color function, you can - Contrast value should be 0 ~ {IDF_TARGET_SOC_ISP_COLOR_CONTRAST_MAX}, default {IDF_TARGET_SOC_ISP_COLOR_CONTRAST_DEFAULT} - Saturation value should be 0 ~ {IDF_TARGET_SOC_ISP_COLOR_SATURATION_MAX}, default {IDF_TARGET_SOC_ISP_COLOR_SATURATION_DEFAULT} - Hue value should be 0 ~ {IDF_TARGET_SOC_ISP_COLOR_HUE_MAX}, default {IDF_TARGET_SOC_ISP_COLOR_HUE_DEFAULT} - - Brightness value should be -{IDF_TARGET_SOC_ISP_COLOR_BRIGHTNESS_MIN} ~ {IDF_TARGET_SOC_ISP_COLOR_BRIGHTNESS_MAX}, default {IDF_TARGET_SOC_ISP_COLOR_BRIGHTNESS_DEFAULT} + - Brightness value should be {IDF_TARGET_SOC_ISP_COLOR_BRIGHTNESS_MIN} ~ {IDF_TARGET_SOC_ISP_COLOR_BRIGHTNESS_MAX}, default {IDF_TARGET_SOC_ISP_COLOR_BRIGHTNESS_DEFAULT} .. code:: c @@ -584,7 +656,7 @@ Calling :cpp:func:`esp_isp_color_configure` to configure color function, you can ESP_ERROR_CHECK(esp_isp_color_configure(isp_proc, &color_config)); ESP_ERROR_CHECK(esp_isp_color_enable(isp_proc)); -After calling :cpp:func:`esp_isp_color_configure`, you need to enable the ISP color processor, by calling :cpp:func:`esp_isp_color_enable`. This function: +After calling :cpp:func:`esp_isp_color_configure`, you need to enable the ISP color controller, by calling :cpp:func:`esp_isp_color_enable`. This function: * Switches the driver state from **init** to **enable**. @@ -599,13 +671,11 @@ Calling :cpp:func:`esp_isp_color_disable` does the opposite, that is, put the dr Configure CCM ^^^^^^^^^^^^^ -Color Correction Matrix can scale the color ratio of RGB888 pixels. It can be used for adjusting the image color via some algorithms, for example, used for white balance by inputting the AWB computed result, or used as a Filter with some filter algorithms. +Color correction matrix can scale the color ratio of RGB888 pixels. It can be used for adjusting the image color via some algorithms, for example, used for white balance by inputting the AWB computed result, or used as a filter with some filter algorithms. To adjust the color correction matrix, here is the formula: - - -:: +.. code-block:: none [ R' ] [ RR RG RB ] [ R ] [ G' ] = [ GR GG GB ] * [ G ] @@ -637,8 +707,8 @@ To adjust the color correction matrix, here is the formula: .. _isp-demosaic: -ISP Demosaic Processor -~~~~~~~~~~~~~~~~~~~~~~ +ISP Demosaic Controller +~~~~~~~~~~~~~~~~~~~~~~~ This pipeline is used for doing image demosaic algorithm to convert RAW image to RGB mode. @@ -657,7 +727,7 @@ Calling :cpp:func:`esp_isp_demosaic_configure` to configure Demosaic function, y ESP_ERROR_CHECK(esp_isp_demosaic_configure(isp_proc, &demosaic_config)); ESP_ERROR_CHECK(esp_isp_demosaic_enable(isp_proc)); -After calling :cpp:func:`esp_isp_demosaic_configure`, you need to enable the ISP Sharpen processor, by calling :cpp:func:`esp_isp_demosaic_enable`. This function: +After calling :cpp:func:`esp_isp_demosaic_configure`, you need to enable the ISP Demosaic controller, by calling :cpp:func:`esp_isp_demosaic_enable`. This function: * Switches the driver state from **init** to **enable**. @@ -670,9 +740,9 @@ Calling :cpp:func:`esp_isp_demosaic_disable` does the opposite, that is, put the Enable Gamma Correction ^^^^^^^^^^^^^^^^^^^^^^^ -The human visual system is non-linearly sensitive to the physical luminance. Adding gamma correction to the ISP pipeline to transforms RGB coordinates into a space in which coordinates are proportional to subjective brightness. +The human visual system is non-linearly sensitive to the physical luminance. Adding gamma correction to the ISP pipeline to transform RGB coordinates into a space in which coordinates are proportional to subjective brightness. -The driver provides a helper API :cpp:func:`esp_isp_gamma_fill_curve_points` to fill :cpp:type:`isp_gamma_curve_points_t`, which is a group of points used to describe the gamma correction curve. Or you can manually declare the points as your desired 'gamma' correction curve. Each R / G / B component can have its own gamma correction curve, you can set the configuration by calling :cpp:func:`esp_isp_gamma_configure`. +The driver provides a helper API :cpp:func:`esp_isp_gamma_fill_curve_points` to fill :cpp:type:`isp_gamma_curve_points_t`, which is a group of points used to describe the gamma correction curve. Or you can manually declare the points as your desired gamma correction curve. Each R/G/B component can have its own gamma correction curve, you can set the configuration by calling :cpp:func:`esp_isp_gamma_configure`. A typical code example is: @@ -700,8 +770,8 @@ A typical code example is: .. _isp-sharpen: -ISP Sharpen Processor -~~~~~~~~~~~~~~~~~~~~~ +ISP Sharpen Controller +~~~~~~~~~~~~~~~~~~~~~~ This pipeline is used for doing image input sharpening under YUV mode. @@ -723,7 +793,7 @@ Calling :cpp:func:`esp_isp_sharpen_configure` to configure Sharpen function, you :cpp:member:`esp_isp_sharpen_config_t::sharpen_template` is used for sharpening. You can set the :cpp:member:`esp_isp_sharpen_config_t::sharpen_template` with a Gaussian filter template or an average filter template. -After calling :cpp:func:`esp_isp_sharpen_configure`, you need to enable the ISP Sharpen processor, by calling :cpp:func:`esp_isp_sharpen_enable`. This function: +After calling :cpp:func:`esp_isp_sharpen_configure`, you need to enable the ISP Sharpen controller, by calling :cpp:func:`esp_isp_sharpen_enable`. This function: * Switches the driver state from **init** to **enable**. @@ -731,24 +801,70 @@ Calling :cpp:func:`esp_isp_sharpen_disable` does the opposite, that is, put the :cpp:func:`esp_isp_sharpen_configure` is allowed to be called even if the driver is in **init** state, but the sharpen configurations will only be taken into effect when in **enable** state. +.. _isp-crop: + +ISP Image Crop Controller +~~~~~~~~~~~~~~~~~~~~~~~~~ + +The ISP image crop function can extract a specified region from the original image, reducing the amount of data for subsequent processing and improving processing efficiency. The crop function is executed at the end of the ISP pipeline and can output a smaller region than the input image. + +.. note:: + + The ISP image crop function is only available on ESP32-P4 revision 3.0 and above. + +Calling :cpp:func:`esp_isp_crop_configure` to configure the image crop function, you can take the following code as reference: + +.. code-block:: c + + esp_isp_crop_config_t crop_config = { + .window = { + .top_left = { + .x = 100, // Top-left X coordinate of crop region + .y = 100, // Top-left Y coordinate of crop region + }, + .btm_right = { + .x = 699, // Bottom-right X coordinate of crop region + .y = 499, // Bottom-right Y coordinate of crop region + } + } + }; + ESP_ERROR_CHECK(esp_isp_crop_configure(isp_proc, &crop_config)); + ESP_ERROR_CHECK(esp_isp_crop_enable(isp_proc)); + +After calling :cpp:func:`esp_isp_crop_configure`, you need to enable the ISP image crop controller by calling :cpp:func:`esp_isp_crop_enable`. This function: + +* Switches the driver state from **init** to **enable**. + +Calling :cpp:func:`esp_isp_crop_disable` does the opposite, that is, put the driver back to the **init** state. + +:cpp:func:`esp_isp_crop_configure` is allowed to be called even if the driver is in **init** state, but the crop configurations will only be taken into effect when in **enable** state. + +.. note:: + + - The top-left coordinates (top_left) of the crop region must be smaller than the bottom-right coordinates (btm_right) + - The top-left coordinates (top_left) of the crop region must be even, and the bottom-right coordinates (btm_right) must be odd + - The crop region cannot exceed the boundaries of the original image + - Adjust the display medium (such as LCD) size according to the cropped resolution to ensure complete display and avoid black borders or stretching. .. _isp-callback: Register Event Callbacks ^^^^^^^^^^^^^^^^^^^^^^^^ + After an ISP module starts up, it can generate a specific event dynamically. + You can save your own context to callback function as well, via the parameter ``user_data``. The user data will be directly passed to the callback function. .. note:: - The below mentioned callback functions are called within an ISR context, you must ensure that the functions do not attempt to block (e.g., by making sure that only FreeRTOS APIs with ``ISR`` suffix are called from within the function). + The below-mentioned callback functions are called within an ISR context. You must ensure that the functions do not attempt to block (e.g., by making sure that only FreeRTOS APIs with ``ISR`` suffix are called from within the function). Register ISP Processor Event Callbacks ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ After the ISP processor is enabled, it can generate multiple events of multiple ISP submodules dynamically. You can hook your functions to the interrupt service routine by calling :cpp:func:`esp_isp_register_event_callbacks`. All supported event callbacks are listed in :cpp:type:`esp_isp_evt_cbs_t`: -- :cpp:member:`esp_isp_evt_cbs_t::on_sharpen_frame_done`. sets a callback function for sharpen frame done. It will be called after the ISP sharpen submodule finishes its operation for one frame. The function prototype is declared in :cpp:type:`esp_isp_sharpen_callback_t`. +- :cpp:member:`esp_isp_evt_cbs_t::on_sharpen_frame_done` sets a callback function for sharpen frame done. It will be called after the ISP sharpen submodule finishes its operation for one frame. The function prototype is declared in :cpp:type:`esp_isp_sharpen_callback_t`. Register ISP AF Environment Detector Event Callbacks ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -763,7 +879,7 @@ Register ISP AWB Statistics Done Event Callbacks After the ISP AWB controller finished statistics of white patches, it can generate a specific event dynamically. If you want to be informed when the statistics done event takes place, please hook your function to the interrupt service routine by calling :cpp:func:`esp_isp_awb_register_event_callbacks`. All supported event callbacks are listed in :cpp:type:`esp_isp_awb_cbs_t`: -- :cpp:member:`esp_isp_awb_cbs_t::on_statistics_done` sets a callback function when finished statistics of the white patches. The function prototype is declared in :cpp:type:`esp_isp_awb_callback_t`. +- :cpp:member:`esp_isp_awb_cbs_t::on_statistics_done` sets a callback function when finishing statistics of the white patches. The function prototype is declared in :cpp:type:`esp_isp_awb_callback_t`. Register ISP AE Environment Detector Event Callbacks @@ -771,8 +887,8 @@ Register ISP AE Environment Detector Event Callbacks After the ISP AE environment detector starts up, it can generate a specific event dynamically. If you have some functions that should be called when the event happens, please hook your function to the interrupt service routine by calling :cpp:func:`esp_isp_ae_env_detector_register_event_callbacks`. All supported event callbacks are listed in :cpp:type:`esp_isp_ae_env_detector_evt_cbs_t`: -- :cpp:member:`esp_isp_ae_env_detector_evt_cbs_t::on_env_statistics_done` sets a callback function for environment statistics done. . The function prototype is declared in :cpp:type:`esp_isp_ae_env_detector_callback_t`. -- :cpp:member:`esp_isp_ae_env_detector_evt_cbs_t::on_env_change` sets a callback function for environment change. . The function prototype is declared in :cpp:type:`esp_isp_ae_env_detector_callback_t`. +- :cpp:member:`esp_isp_ae_env_detector_evt_cbs_t::on_env_statistics_done` sets a callback function for environment statistics done. The function prototype is declared in :cpp:type:`esp_isp_ae_env_detector_callback_t`. +- :cpp:member:`esp_isp_ae_env_detector_evt_cbs_t::on_env_change` sets a callback function for environment change. The function prototype is declared in :cpp:type:`esp_isp_ae_env_detector_callback_t`. Register ISP HIST Statistics Done Event Callbacks @@ -780,14 +896,14 @@ Register ISP HIST Statistics Done Event Callbacks After the ISP HIST controller finished statistics of brightness, it can generate a specific event dynamically. If you want to be informed when the statistics done event takes place, please hook your function to the interrupt service routine by calling :cpp:func:`esp_isp_hist_register_event_callbacks`. All supported event callbacks are listed in :cpp:type:`esp_isp_hist_cbs_t`: -- :cpp:member:`esp_isp_hist_cbs_t::on_statistics_done` sets a callback function when finished statistics of the brightness. . The function prototype is declared in :cpp:type:`esp_isp_hist_callback_t`. +- :cpp:member:`esp_isp_hist_cbs_t::on_statistics_done` sets a callback function when finishing statistics of the brightness. The function prototype is declared in :cpp:type:`esp_isp_hist_callback_t`. .. _isp-thread-safety: Thread Safety ^^^^^^^^^^^^^ -The factory function +The following factory function are guaranteed to be thread safe by the driver: .. list:: @@ -802,14 +918,14 @@ The factory function - :cpp:func:`esp_isp_new_hist_controller` - :cpp:func:`esp_isp_del_hist_controller` -are guaranteed to be thread safe by the driver, which means, user can call them from different RTOS tasks without protection by extra locks. Other APIs are not guaranteed to be thread-safe. +These functions can be called from different RTOS tasks without protection by extra locks. Other APIs are not guaranteed to be thread-safe. .. _isp-kconfig-options: Kconfig Options ^^^^^^^^^^^^^^^ -- :ref:`CONFIG_ISP_ISR_IRAM_SAFE` controls whether the default ISR handler should be masked when the cache is disabled +- :ref:`CONFIG_ISP_ISR_IRAM_SAFE` controls whether the default ISR handler should be masked when the cache is disabled. .. _isp-iram-safe: @@ -828,12 +944,12 @@ This allows the interrupt to run while the cache is disabled, but comes at the c Kconfig option :ref:`CONFIG_ISP_CTRL_FUNC_IN_IRAM` will: -- Place some of ISP control functions into IRAM, function list: +- Place some of the ISP control functions into IRAM, including: -.. list:: + .. list:: - - :cpp:func:`esp_isp_sharpen_configure` - - :cpp:func:`esp_isp_demosaic_configure` + - :cpp:func:`esp_isp_sharpen_configure` + - :cpp:func:`esp_isp_demosaic_configure` Application Examples -------------------- @@ -844,12 +960,11 @@ API Reference ------------- .. include-build-file:: inc/isp.inc -.. include-build-file:: inc/components/hal/include/hal/isp_types.inc -.. include-build-file:: inc/components/esp_driver_isp/include/driver/isp_types.inc .. include-build-file:: inc/isp_af.inc .. include-build-file:: inc/isp_ae.inc .. include-build-file:: inc/isp_awb.inc .. include-build-file:: inc/isp_bf.inc +.. include-build-file:: inc/isp_blc.inc .. include-build-file:: inc/isp_lsc.inc .. include-build-file:: inc/isp_ccm.inc .. include-build-file:: inc/isp_demosaic.inc @@ -857,3 +972,7 @@ API Reference .. include-build-file:: inc/isp_gamma.inc .. include-build-file:: inc/isp_hist.inc .. include-build-file:: inc/isp_color.inc +.. include-build-file:: inc/isp_crop.inc +.. include-build-file:: inc/isp_core.inc +.. include-build-file:: inc/components/esp_driver_isp/include/driver/isp_types.inc +.. include-build-file:: inc/components/hal/include/hal/isp_types.inc diff --git a/docs/zh_CN/api-reference/peripherals/camera_driver.rst b/docs/zh_CN/api-reference/peripherals/camera_driver.rst index 8fa2ccfd07..6b7fe1fbc4 100644 --- a/docs/zh_CN/api-reference/peripherals/camera_driver.rst +++ b/docs/zh_CN/api-reference/peripherals/camera_driver.rst @@ -295,4 +295,3 @@ API 参考 .. include-build-file:: inc/esp_cam_ctlr_types.inc .. include-build-file:: inc/esp_cam_ctlr_csi.inc .. include-build-file:: inc/esp_cam_ctlr_isp_dvp.inc -.. include-build-file:: inc/isp_core.inc diff --git a/docs/zh_CN/api-reference/peripherals/isp.rst b/docs/zh_CN/api-reference/peripherals/isp.rst index 3770b5e0b2..908d13fe78 100644 --- a/docs/zh_CN/api-reference/peripherals/isp.rst +++ b/docs/zh_CN/api-reference/peripherals/isp.rst @@ -1 +1,978 @@ -.. include:: ../../../en/api-reference/peripherals/isp.rst +图像信号处理器 (ISP) +==================== + +:link_to_translation:`en:[English]` + +简介 +---- + +{IDF_TARGET_NAME} 内含图像信号处理器 (ISP),是由众多图像处理算法组成的流水线。ISP 从 DVP 摄像头、MIPI-CSI 摄像头或系统存储处接收图像数据,并通过 DMA 将处理后的图像数据写入系统存储。ISP 需要与其他摄像头控制器模块协同工作,无法独立工作。 + +术语表 +------ + +.. list:: + + - MIPI-CSI:符合 MIPI 规范的高速串行摄像头接口 + - DVP:数字视频并行接口,通常由 vsync、hsync、de 和 data 信号组成 + - RAW:直接从图像传感器输出的未处理数据,通常分为 R、Gr、Gb 和 B 四个通道,按位宽分为 RAW8、RAW10、RAW12 等不同格式 + - RGB:由红、绿、蓝三种颜色组成的彩色图像格式,按每种颜色的位宽分为 RGB888、RGB565 等格式 + - YUV:由亮度和色度组成的彩色图像格式,按数据排列方式分为 YUV444、YUV422、YUV420 等格式 + - AF:自动对焦 + - AWB:自动白平衡 + - AE:自动曝光 + - HIST:直方图 + - BF:拜耳域降噪 + - BLC:黑电平校正 + - LSC:镜头阴影校正 + - CCM:色彩校正矩阵 + +ISP 流水线 +---------- + +.. blockdiag:: + :scale: 100% + :caption: ISP 流水线 + :align: center + + blockdiag isp_pipeline { + orientation = portrait; + node_height = 30; + node_width = 120; + span_width = 100; + default_fontsize = 16; + + isp_header [label = "ISP Header"]; + isp_tail [label = "ISP Tail"]; + isp_chs [label = "对比度 &\n 色调 & 饱和度", width = 150, height = 70]; + isp_yuv [label = "YUV 限制\n YUB2RGB", width = 120, height = 70]; + + isp_header -> BLC -> BF -> LSC -> 去马赛克 -> CCM -> gamma 校正 -> RGB 转 YUV -> 锐化 -> isp_chs -> isp_yuv -> 裁剪 -> isp_tail; + + LSC -> HIST + 去马赛克 -> AWB + 去马赛克 -> AE + 去马赛克 -> HIST + CCM -> AWB + gamma 校正 -> AE + RGB 转 YUV -> HIST + RGB 转 YUV -> AF + } + +功能概述 +-------- + +ISP 驱动程序提供以下服务: + +- :ref:`isp-resource-allocation` - 涵盖如何通过正确的配置来分配 ISP 资源,以及完成工作后如何回收资源。 +- :ref:`isp-enable-disable` - 涵盖如何启用和禁用 ISP 处理器。 +- :ref:`isp-af-statistics` - 涵盖如何单次或连续获取 AF 统计信息。 +- :ref:`isp-awb-statistics` - 涵盖如何单次或连续获取 AWB 白块统计信息。 +- :ref:`isp-ae-statistics` - 涵盖如何单次或连续获取 AE 统计信息。 +- :ref:`isp-hist-statistics` - 涵盖如何单次或连续获取直方图统计信息。 +- :ref:`isp-bf` - 涵盖如何启用和配置 BF 功能。 +- :ref:`isp-blc` - 涵盖如何启用和配置 BLC 功能。 +- :ref:`isp-lsc` - 涵盖如何启用和配置 LSC 功能。 +- :ref:`isp-ccm-config` - 涵盖如何配置 CCM。 +- :ref:`isp-demosaic` - 涵盖如何配置去马赛克功能。 +- :ref:`isp-gamma-correction` - 涵盖如何启用和配置 gamma 校正。 +- :ref:`isp-sharpen` - 涵盖如何配置锐化功能。 +- :ref:`isp-crop` - 涵盖如何启用和配置图像裁剪功能。 +- :ref:`isp-callback` - 涵盖如何将用户特定代码挂接到 ISP 驱动事件回调。 +- :ref:`isp-thread-safety` - 列出了驱动程序中线程安全的 API。 +- :ref:`isp-kconfig-options` - 列出了支持的 Kconfig 选项,这些选项可以对驱动程序产生不同影响。 +- :ref:`isp-iram-safe` - 描述了当 cache 被禁用时,如何使 ISP 中断和控制功能正常工作。 + +.. _isp-resource-allocation: + +资源分配 +^^^^^^^^ + +安装 ISP 驱动程序 +~~~~~~~~~~~~~~~~~ + +ISP 驱动程序需要由 :cpp:type:`esp_isp_processor_cfg_t` 指定配置。 + +指定 :cpp:type:`esp_isp_processor_cfg_t` 中的配置后,可以调用 :cpp:func:`esp_isp_new_processor` 来分配和初始化 ISP 处理器。如果函数运行正常,将返回一个 ISP 处理器句柄。请参考以下代码: + +.. code-block:: c + + esp_isp_processor_cfg_t isp_config = { + .clk_src = ISP_CLK_SRC_DEFAULT, + ... + }; + + isp_proc_handle_t isp_proc = NULL; + ESP_ERROR_CHECK(esp_isp_new_processor(&isp_config, &isp_proc)); + +使用上述句柄,可以启用/禁用 ISP 驱动程序,也可以安装其他 ISP 模块。 + +.. note:: + + 如果将 MIPI CSI 或 ISP_DVP 用作摄像头控制器,则必须使用 ISP 外设。因此即便无需使用 ISP 功能,也要调用 :cpp:func:`esp_isp_new_processor` 函数安装 ISP 驱动程序。 + + 如果无需使用 ISP 功能,也可以设置 :cpp:member:`esp_isp_processor_cfg_t::bypass_isp`,使 ISP 驱动程序绕过 ISP 流水线,仅启用必要的功能。 + +安装 ISP 自动对焦 (AF) 驱动程序 +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +ISP 自动对焦 (AF) 驱动程序需要由 :cpp:type:`esp_isp_af_config_t` 指定配置。 + +指定 :cpp:type:`esp_isp_af_config_t` 中的配置后,可以调用 :cpp:func:`esp_isp_new_af_controller` 来分配和初始化 ISP AF 控制器。如果函数运行正常,将返回一个 ISP AF 控制器句柄。请参考以下代码: + +.. code-block:: c + + esp_isp_af_config_t af_config = { + .edge_thresh = 128, + }; + isp_af_ctlr_t af_ctrlr = NULL; + ESP_ERROR_CHECK(esp_isp_new_af_controller(isp_proc, &af_config, &af_ctrlr)); + +使用上述句柄,可以启用/禁用 ISP AF 驱动程序,也可以安装 ISP AF 环境检测模块。 + +安装 ISP 自动白平衡 (AWB) 驱动程序 +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +ISP 自动白平衡 (AWB) 驱动程序需要由 :cpp:type:`esp_isp_awb_config_t` 指定配置。 + +指定 :cpp:type:`esp_isp_awb_config_t` 中的配置后,可以调用 :cpp:func:`esp_isp_new_awb_controller` 来分配和初始化 ISP AWB 控制器。如果函数运行正常,将返回一个 ISP AWB 控制器句柄。请参考以下代码: + +.. code-block:: c + + isp_awb_ctlr_t awb_ctlr = NULL; + uint32_t image_width = 800; + uint32_t image_height = 600; + /* AWB 配置,请参考 API 注释来调整参数 */ + esp_isp_awb_config_t awb_config = { + .sample_point = ISP_AWB_SAMPLE_POINT_AFTER_CCM, + ... + }; + ESP_ERROR_CHECK(esp_isp_new_awb_controller(isp_proc, &awb_config, &awb_ctlr)); + +其他 AWB API 和 AWB 方案也需要此步骤中创建的 AWB 句柄。 + +安装 ISP 自动曝光 (AE) 驱动程序 +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +ISP 自动曝光 (AE) 驱动程序需要由 :cpp:type:`esp_isp_ae_config_t` 指定配置。 + +指定 :cpp:type:`esp_isp_ae_config_t` 中的配置后,可以调用 :cpp:func:`esp_isp_new_ae_controller` 来分配和初始化 ISP AE 控制器。如果函数运行正常,将返回一个 ISP AE 控制器句柄。请参考以下代码: + +.. code-block:: c + + esp_isp_ae_config_t ae_config = { + .sample_point = ISP_AE_SAMPLE_POINT_AFTER_DEMOSAIC, + ... + }; + isp_ae_ctlr_t ae_ctlr = NULL; + ESP_ERROR_CHECK(esp_isp_new_ae_controller(isp_proc, &ae_config, &ae_ctlr)); + +使用上述句柄,可以启用/禁用 ISP AE 驱动程序,也可以设置 ISP AE 环境检测器。 + +安装 ISP 直方图 (HIST) 驱动程序 +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +ISP 直方图 (HIST) 驱动程序需要由 :cpp:type:`esp_isp_hist_config_t` 指定配置。 + +指定 :cpp:type:`esp_isp_hist_config_t` 中的配置后,可以调用 :cpp:func:`esp_isp_new_hist_controller` 来分配和初始化 ISP 直方图控制器。如果此函数运行正常,将返回一个 ISP HIST 控制器句柄。请参考以下代码。 + +.. list:: + + - 所有子窗口权重的十进制值之和应为 256,否则统计数据将较小,并且整数值应为 0。 + - 所有 RGB 系数的十进制值之和应为 256,否则统计数据将较小,并且整数值应为 0。 + - segment_threshold 必须在 0~255 之间且按顺序排列。 + +.. code:: c + + esp_isp_hist_config_t hist_cfg = { + .segment_threshold = {16, 32, 48, 64, 80, 96, 112, 128, 144, 160, 176, 192, 208, 224, 240}, + .hist_mode = ISP_HIST_SAMPLING_RGB, + .rgb_coefficient.coeff_r = { + .integer = 0, + .decimal = 86, + }, + .rgb_coefficient.coeff_g = { + .integer = 0, + .decimal = 85, + }, + .rgb_coefficient.coeff_b = { + .integer = 0, + .decimal = 85, + }, + .window_weight = { + {{16, 0}}, {{10, 0}}, {{10, 0}}, {{10, 0}}, {{10, 0}}, + {{10, 0}}, {{10, 0}}, {{10, 0}}, {{10, 0}}, {{10, 0}}, + {{10, 0}}, {{10, 0}}, {{10, 0}}, {{10, 0}}, {{10, 0}}, + {{10, 0}}, {{10, 0}}, {{10, 0}}, {{10, 0}}, {{10, 0}}, + {{10, 0}}, {{10, 0}}, {{10, 0}}, {{10, 0}}, {{10, 0}}, + }, + }; + isp_hist_ctlr_t hist_ctlr_ctlr = NULL; + ESP_ERROR_CHECK(esp_isp_new_hist_controller(isp_proc, &hist_config, &hist_ctlr)); + +使用上述句柄,可以启用/禁用 ISP HIST 驱动程序的设置。 + +卸载 ISP 驱动程序 +~~~~~~~~~~~~~~~~~ + +如果不再需要先前安装的 ISP 驱动程序,建议通过调用 API 来回收资源,并释放底层硬件: + +.. list:: + + - :cpp:func:`esp_isp_del_processor`,用于 ISP 核心处理器。 + - :cpp:func:`esp_isp_del_af_controller`,用于 ISP AF 控制器。 + - :cpp:func:`esp_isp_del_awb_controller`,用于 ISP AWB 控制器。 + - :cpp:func:`esp_isp_del_ae_controller`,用于 ISP AE 控制器。 + - :cpp:func:`esp_isp_del_hist_controller`,用于 ISP 直方图控制器。 + +.. _isp-enable-disable: + +启用和禁用 ISP +^^^^^^^^^^^^^^ + +ISP +~~~ + +在进行 ISP 流水线操作之前,需要先调用 :cpp:func:`esp_isp_enable` 函数来启用 ISP 处理器。此函数: + +* 将驱动程序状态从 **init** 切换到 **enable**。 + +调用 :cpp:func:`esp_isp_disable` 函数会执行相反的操作,即将驱动程序恢复到 **init** 状态。 + +ISP AF 控制器 +~~~~~~~~~~~~~ + +在进行 ISP AF 操作之前,需要先调用 :cpp:func:`esp_isp_af_controller_enable` 函数来启用 ISP AF 控制器。此函数: + +* 将驱动程序状态从 **init** 切换到 **enable**。 + +调用 :cpp:func:`esp_isp_af_controller_disable` 函数会执行相反的操作,即将驱动程序恢复到 **init** 状态。 + +.. _isp-af-statistics: + +单次与连续 AF 数据统计 +^^^^^^^^^^^^^^^^^^^^^^ + +调用 :cpp:func:`esp_isp_af_controller_get_oneshot_statistics` 可获取单次 AF 统计结果,请参考以下代码。 + +除此之外,ISP AF 驱动程序还可以连续获取 AF 统计信息。调用 :cpp:func:`esp_isp_af_controller_start_continuous_statistics` 可启动连续统计,调用 :cpp:func:`esp_isp_af_controller_stop_continuous_statistics` 可停止统计。 + +若想启用连续统计,需要先注册回调函数 :cpp:member:`esp_isp_af_env_detector_evt_cbs_t::on_env_statistics_done` 或 :cpp:member:`esp_isp_af_env_detector_evt_cbs_t::on_env_change` 以获取统计数据。有关如何注册回调函数,请参见 :ref:`isp-callback`。 + +.. note:: + + 使用连续统计时,AF 环境检测器将失效。 + +.. code-block:: c + + esp_isp_af_config_t af_config = { + .edge_thresh = 128, + }; + isp_af_ctlr_t af_ctrlr = NULL; + ESP_ERROR_CHECK(esp_isp_new_af_controller(isp_proc, &af_config, &af_ctrlr)); + ESP_ERROR_CHECK(esp_isp_af_controller_enable(af_ctrlr)); + isp_af_result_t result = {}; + /* 触发单次 AF 统计并获取结果,超时时长为 2000 ms */ + ESP_ERROR_CHECK(esp_isp_af_controller_get_oneshot_statistics(af_ctrlr, 2000, &result)); + + /* 启动连续 AF 数据统计 */ + ESP_ERROR_CHECK(esp_isp_af_controller_start_continuous_statistics(af_ctrlr)); + // 可在此进行其他操作,统计结果可从回调函数中获取 + // ...... + // vTaskDelay(pdMS_TO_TICKS(1000)); + /* 停止连续 AF 数据统计 */ + ESP_ERROR_CHECK(esp_isp_af_controller_stop_continuous_statistics(af_ctrlr)); + + /* 禁用 AF 控制器 */ + ESP_ERROR_CHECK(esp_isp_af_controller_disable(af_ctrlr)); + /* 删除 AF 控制器并释放资源 */ + ESP_ERROR_CHECK(esp_isp_del_af_controller(af_ctrlr)); + +设置 AF 环境检测器 +^^^^^^^^^^^^^^^^^^ + +调用 :cpp:func:`esp_isp_af_controller_set_env_detector` 来设置 ISP AF 环境检测器,请参考以下代码: + +.. code-block:: c + + esp_isp_af_env_config_t env_config = { + .interval = 10, + }; + isp_af_ctlr_t af_ctrlr = NULL; + ESP_ERROR_CHECK(esp_isp_new_af_controller(isp_proc, &af_config, &af_ctrlr)); + ESP_ERROR_CHECK(esp_isp_af_controller_set_env_detector(af_ctrlr, &env_config)); + +设置 AF 环境检测器阈值 +^^^^^^^^^^^^^^^^^^^^^^ + +调用 :cpp:func:`esp_isp_af_controller_set_env_detector_threshold` 来设置 ISP AF 环境检测器的阈值。 + +.. code-block:: c + + int definition_thresh = 0; + int luminance_thresh = 0; + ESP_ERROR_CHECK(esp_isp_af_env_detector_set_threshold(env_detector, definition_thresh, luminance_thresh)); + +ISP AWB 控制器 +~~~~~~~~~~~~~~ + +在进行 ISP AWB 操作之前,需要先调用 :cpp:func:`esp_isp_awb_controller_enable` 以启用 ISP AWB 控制器。此函数: + +* 将驱动程序状态从 **init** 切换到 **enable**。 + +调用 :cpp:func:`esp_isp_awb_controller_disable` 函数会执行相反的操作,即将驱动程序恢复到 **init** 状态。 + +.. _isp-awb-statistics: + +单次与连续 AWB 数据统计 +^^^^^^^^^^^^^^^^^^^^^^^ + +调用 :cpp:func:`esp_isp_awb_controller_get_oneshot_statistics` 可获取单次 AWB 白块统计结果,请参考以下代码。 + +除此之外,ISP AWB 驱动程序还可以连续获取 AWB 统计信息。调用 :cpp:func:`esp_isp_awb_controller_start_continuous_statistics` 可启动连续统计,调用 :cpp:func:`esp_isp_awb_controller_stop_continuous_statistics` 可停止统计。 + +若想启用连续统计,需要先注册回调函数 :cpp:member:`esp_isp_awb_cbs_t::on_statistics_done` 以获取统计结果。有关如何注册回调函数,请参见 :ref:`isp-callback`。 + +.. code-block:: c + + bool example_isp_awb_on_statistics_done_cb(isp_awb_ctlr_t awb_ctlr, const esp_isp_awb_evt_data_t *edata, void *user_data); + // ... + isp_awb_ctlr_t awb_ctlr = NULL; + uint32_t image_width = 800; + uint32_t image_height = 600; + /* AWB 配置,请参考 API 注释来调整参数 */ + esp_isp_awb_config_t awb_config = { + .sample_point = ISP_AWB_SAMPLE_POINT_AFTER_CCM, + ... + }; + isp_awb_stat_result_t stat_res = {}; + /* 创建 AWB 控制器 */ + ESP_ERROR_CHECK(esp_isp_new_awb_controller(isp_proc, &awb_config, &awb_ctlr)); + /* 注册 AWB 回调函数 */ + esp_isp_awb_cbs_t awb_cb = { + .on_statistics_done = example_isp_awb_on_statistics_done_cb, + }; + ESP_ERROR_CHECK(esp_isp_awb_register_event_callbacks(awb_ctlr, &awb_cb, NULL)); + /* 启用 AWB 控制器 */ + ESP_ERROR_CHECK(esp_isp_awb_controller_enable(awb_ctlr)); + + /* 获取单次 AWB 统计结果 */ + ESP_ERROR_CHECK(esp_isp_awb_controller_get_oneshot_statistics(awb_ctlr, -1, &stat_res)); + + /* 启动连续 AWB 数据统计,注意在此之前需要先注册 `on_statistics_done` 回调函数 */ + ESP_ERROR_CHECK(esp_isp_awb_controller_start_continuous_statistics(awb_ctlr)); + // 可在此进行其他操作,统计结果可从回调函数中获取 + // ...... + // vTaskDelay(pdMS_TO_TICKS(1000)); + /* 停止连续 AWB 数据统计 */ + ESP_ERROR_CHECK(esp_isp_awb_controller_stop_continuous_statistics(awb_ctlr)); + + /* 禁用 AWB 控制器 */ + ESP_ERROR_CHECK(esp_isp_awb_controller_disable(awb_ctlr)); + /* 删除 AWB 控制器并释放资源 */ + ESP_ERROR_CHECK(esp_isp_del_awb_controller(awb_ctlr)); + +ISP AE 控制器 +~~~~~~~~~~~~~ + +在进行 ISP AE 操作之前,需要先调用 :cpp:func:`esp_isp_ae_controller_enable` 来启用 ISP AE 控制器。此函数: + +* 将驱动程序状态从 **init** 切换到 **enable**。 + +调用 :cpp:func:`esp_isp_ae_controller_disable` 函数会执行相反的操作,即将驱动程序恢复到 **init** 状态。 + +.. _isp-ae-statistics: + +单次与连续 AE 数据统计 +^^^^^^^^^^^^^^^^^^^^^^ + +调用 :cpp:func:`esp_isp_ae_controller_get_oneshot_statistics` 可获取单次 AE 统计结果,请参考以下代码。 + +使用单次 AE 数据统计时,需要禁用连续 AE 模式,否则结果可能会被环境检测器覆盖。完成单次操作后,请重新启动连续模式。 + +除了上述单次统计 API 外,ISP AE 驱动程序还可以连续获取 AE 统计信息。调用 :cpp:func:`esp_isp_ae_controller_start_continuous_statistics` 可启动连续统计,调用 :cpp:func:`esp_isp_ae_controller_stop_continuous_statistics` 可停止统计。 + +若想启用连续统计,需要先注册回调函数 :cpp:member:`esp_isp_ae_env_detector_evt_cbs_t::on_env_statistics_done` 或 :cpp:member:`esp_isp_ae_env_detector_evt_cbs_t::on_env_change` 以获取统计数据。有关如何注册回调函数,请参见 :ref:`isp-callback`。 + +.. note:: + + 使用单次统计时,AE 环境检测器将暂时失效,并在完成单次操作后自动恢复。 + +.. code-block:: c + + esp_isp_ae_config_t ae_config = { + .sample_point = ISP_AE_SAMPLE_POINT_AFTER_DEMOSAIC, + }; + isp_ae_ctlr_t ae_ctlr = NULL; + ESP_ERROR_CHECK(esp_isp_new_ae_controller(isp_proc, &ae_config, &ae_ctlr)); + ESP_ERROR_CHECK(esp_isp_ae_controller_enable(ae_ctlr)); + isp_ae_result_t result = {}; + /* 触发单次 AE 统计并获取结果,超时时长为 2000 ms */ + ESP_ERROR_CHECK(esp_isp_ae_controller_get_oneshot_statistics(ae_ctlr, 2000, &result)); + + /* 启动连续 AE 数据统计 */ + ESP_ERROR_CHECK(esp_isp_ae_controller_start_continuous_statistics(ae_ctlr)); + // 可在此进行其他操作,统计结果可从回调函数中获取 + // ...... + // vTaskDelay(pdMS_TO_TICKS(1000)); + /* 停止连续 AE 数据统计 */ + ESP_ERROR_CHECK(esp_isp_ae_controller_stop_continuous_statistics(ae_ctlr)); + + /* 禁用 AE 控制器 */ + ESP_ERROR_CHECK(esp_isp_ae_controller_disable(ae_ctlr)); + /* 删除 AE 控制器并释放资源 */ + ESP_ERROR_CHECK(esp_isp_del_ae_controller(ae_ctlr)); + +设置 AE 环境检测器 +^^^^^^^^^^^^^^^^^^ + +调用 :cpp:func:`esp_isp_ae_controller_set_env_detector` 来设置 ISP AE 环境检测器,请参考以下代码: + +.. code:: c + + esp_isp_ae_env_config_t env_config = { + .interval = 10, + }; + ESP_ERROR_CHECK(esp_isp_ae_controller_set_env_detector(ae_ctlr, &env_config)); + +设置 AE 环境检测器阈值 +^^^^^^^^^^^^^^^^^^^^^^ + +调用 :cpp:func:`esp_isp_ae_controller_set_env_detector_threshold` 来设置 ISP AE 环境检测器的阈值 (1-255)。 + +.. code:: c + + esp_isp_ae_env_thresh_t env_thresh = { + .low_thresh = 110, + .high_thresh = 130, + }; + ESP_ERROR_CHECK(esp_isp_ae_controller_set_env_detector_threshold(ae_ctlr, env_thresh)); + +.. _isp-hist: + +ISP 直方图控制器 +~~~~~~~~~~~~~~~~ + +在进行 ISP 直方图统计之前,需要先调用 :cpp:func:`esp_isp_hist_controller_enable` 以启用 ISP 直方图控制器。此函数: + +* 将驱动程序状态从 **init** 切换到 **enable**。 + +调用 :cpp:func:`esp_isp_hist_controller_disable` 函数会执行相反的操作,即将驱动程序恢复到 **init** 状态。 + +.. _isp-hist-statistics: + +单次与连续直方图数据统计 +^^^^^^^^^^^^^^^^^^^^^^^^ + +调用 :cpp:func:`esp_isp_hist_controller_get_oneshot_statistics` 可获取单次直方图统计结果,请参考以下代码。 + +除此之外,ISP 直方图驱动程序还可以连续获取直方图统计信息。调用 :cpp:func:`esp_isp_hist_controller_start_continuous_statistics` 可启动连续统计,调用 :cpp:func:`esp_isp_hist_controller_stop_continuous_statistics` 可停止连续统计。 + +若想启用连续统计,需要先注册回调函数 :cpp:member:`esp_isp_hist_cbs_t::on_statistics_done` 以获取统计结果。有关如何注册回调函数,请参见 :ref:`isp-callback`。 + +.. code:: c + + static bool s_hist_scheme_on_statistics_done_callback(isp_hist_ctlr_t awb_ctrlr, const esp_isp_hist_evt_data_t *edata, void *user_data) + { + for(int i = 0; i < 16; i++) { + esp_rom_printf(DRAM_STR("val %d is %x\n"), i, edata->hist_result.hist_value[i]); // 获取直方图统计值 + } + return true; + } + + esp_isp_hist_cbs_t hist_cbs = { + .on_statistics_done = s_hist_scheme_on_statistics_done_callback, + }; + + esp_isp_hist_register_event_callbacks(hist_ctlr, &hist_cbs, hist_ctlr); + esp_isp_hist_controller_enable(hist_ctlr); + + +.. _isp-bf: + +ISP BF 控制器 +~~~~~~~~~~~~~ + +此流水线用于在拜耳模式下进行图像输入降噪。 + +可调用 :cpp:func:`esp_isp_bf_configure` 函数配置 BF 功能,请参考以下代码: + +.. code-block:: c + + esp_isp_bf_config_t bf_config = { + .denoising_level = 5, + .bf_template = { + {1, 2, 1}, + {2, 4, 2}, + {1, 2, 1}, + }, + ... + }; + ESP_ERROR_CHECK(esp_isp_bf_configure(isp_proc, &bf_config)); + ESP_ERROR_CHECK(esp_isp_bf_enable(isp_proc)); + +:cpp:member:`esp_isp_bf_config_t::bf_template` 用于拜耳域降噪。可以通过高斯滤波器模板或均值滤波器模板来设置 :cpp:member:`esp_isp_bf_config_t::bf_template`。 + +调用 :cpp:func:`esp_isp_bf_configure` 后,需要通过调用 :cpp:func:`esp_isp_bf_enable` 来启用 ISP BF 控制器。此函数: + +* 将驱动程序状态从 **init** 切换到 **enable**。 + +调用 :cpp:func:`esp_isp_bf_disable` 函数会执行相反的操作,即将驱动程序恢复到 **init** 状态。 + +.. _isp-blc: + +ISP BLC 控制器 +~~~~~~~~~~~~~~ + +黑电平校正 (BLC) 旨在解决因相机传感器中光线折射不均而引起的问题。 + +可调用 :cpp:func:`esp_isp_blc_configure` 函数配置 BLC 模块以进行校正。 + +.. code-block:: c + + esp_isp_blc_config_t blc_config = { + .window = { + .top_left = { + .x = 0, + .y = 0, + }, + .btm_right = { + .x = CONFIG_EXAMPLE_MIPI_CSI_DISP_HRES, + .y = CONFIG_EXAMPLE_MIPI_CSI_DISP_VRES, + }, + }, + .filter_enable = true, + .filter_threshold = { + .top_left_chan_thresh = 128, + .top_right_chan_thresh = 128, + .bottom_left_chan_thresh = 128, + .bottom_right_chan_thresh = 128, + }, + .stretch = { + .top_left_chan_stretch_en = true, + .top_right_chan_stretch_en = true, + .bottom_left_chan_stretch_en = true, + .bottom_right_chan_stretch_en = true, + }, + }; + ESP_ERROR_CHECK(esp_isp_blc_configure(isp_proc, &blc_config)); + ESP_ERROR_CHECK(esp_isp_blc_enable(isp_proc)); + +调用 :cpp:func:`esp_isp_blc_configure` 后,需要通过调用 :cpp:func:`esp_isp_blc_enable` 来启用 ISP BLC 控制器。此函数: + +* 将驱动程序状态从 **init** 切换到 **enable**。 + +调用 :cpp:func:`esp_isp_blc_disable` 函数会执行相反的操作,即将驱动程序恢复到 **init** 状态。 + +调用 :cpp:func:`esp_isp_blc_set_correction_offset` 函数来设置 BLC 校正偏移量。 + +.. code-block:: c + + esp_isp_blc_offset_t blc_offset = { + .top_left_chan_offset = 20, + .top_right_chan_offset = 20, + .bottom_left_chan_offset = 20, + .bottom_right_chan_offset = 20, + }; + ESP_ERROR_CHECK(esp_isp_blc_set_correction_offset(isp_proc, &blc_offset)); + + +.. _isp-lsc: + +ISP LSC 控制器 +~~~~~~~~~~~~~~ + +镜头阴影校正 (LSC) 旨在解决因相机镜头中光线折射不均而引起的问题。 + +可调用 :cpp:func:`esp_isp_lsc_configure` 函数配置 LSC 模块以进行校正。硬件进行校正相关计算时需要用到 :cpp:type:`esp_isp_lsc_gain_array_t` 类型的数据结构。:cpp:func:`esp_isp_lsc_allocate_gain_array` 是一个辅助函数,为增益值分配大小合适的系统存储。 + +.. code-block:: c + + esp_isp_lsc_gain_array_t gain_array = {}; + size_t gain_size = 0; + ESP_ERROR_CHECK(esp_isp_lsc_allocate_gain_array(isp_proc, &gain_array, &gain_size)); + + esp_isp_lsc_config_t lsc_config = { + .gain_array = &gain_array, + }; + isp_lsc_gain_t gain_val = { + .decimal = 204, + .integer = 0, + }; + for (int i = 0; i < gain_size; i++) { + gain_array.gain_r[i].val = gain_val.val; + gain_array.gain_gr[i].val = gain_val.val; + gain_array.gain_gb[i].val = gain_val.val; + gain_array.gain_b[i].val = gain_val.val; + } + ESP_ERROR_CHECK(esp_isp_lsc_configure(isp_proc, &lsc_config)); + +调用 :cpp:func:`esp_isp_lsc_configure` 后,需要通过调用 :cpp:func:`esp_isp_lsc_enable` 来启用 ISP LSC 控制器。可以通过调用 :cpp:func:`esp_isp_lsc_disable` 来禁用 LSC。此外,即使未启用 LSC 控制器,也可以调用 :cpp:func:`esp_isp_lsc_configure`,但 LSC 功能仅在启用后才会生效。 + + +.. _isp-color: + +ISP 色彩控制器 +~~~~~~~~~~~~~~ + +该流水线用于调整图像的对比度、饱和度、色调和亮度。 + +可调用 :cpp:func:`esp_isp_color_configure` 函数配置色彩功能,请参考以下代码。 + +{IDF_TARGET_SOC_ISP_COLOR_CONTRAST_MAX:default="1.0", esp32p4="1.0"} +{IDF_TARGET_SOC_ISP_COLOR_CONTRAST_DEFAULT:default="1.0", esp32p4="1.0"} + +{IDF_TARGET_SOC_ISP_COLOR_SATURATION_MAX:default="1.0", esp32p4="1.0"} +{IDF_TARGET_SOC_ISP_COLOR_SATURATION_DEFAULT:default="1.0", esp32p4="1.0"} + +{IDF_TARGET_SOC_ISP_COLOR_HUE_MAX:default="360", esp32p4="360"} +{IDF_TARGET_SOC_ISP_COLOR_HUE_DEFAULT:default="0", esp32p4="0"} + +{IDF_TARGET_SOC_ISP_COLOR_BRIGHTNESS_MIN:default="-127", esp32p4="-127"} +{IDF_TARGET_SOC_ISP_COLOR_BRIGHTNESS_MAX:default="128", esp32p4="128"} +{IDF_TARGET_SOC_ISP_COLOR_BRIGHTNESS_DEFAULT:default="0", esp32p4="0"} + +.. list:: + + - 对比度应为 0 ~ {IDF_TARGET_SOC_ISP_COLOR_CONTRAST_MAX},默认值为 {IDF_TARGET_SOC_ISP_COLOR_CONTRAST_DEFAULT} + - 饱和度应为 0 ~ {IDF_TARGET_SOC_ISP_COLOR_SATURATION_MAX},默认值为 {IDF_TARGET_SOC_ISP_COLOR_SATURATION_DEFAULT} + - 色调应为 0 ~ {IDF_TARGET_SOC_ISP_COLOR_HUE_MAX},默认值为 {IDF_TARGET_SOC_ISP_COLOR_HUE_DEFAULT} + - 亮度应为 {IDF_TARGET_SOC_ISP_COLOR_BRIGHTNESS_MIN} ~ {IDF_TARGET_SOC_ISP_COLOR_BRIGHTNESS_MAX},默认值为 {IDF_TARGET_SOC_ISP_COLOR_BRIGHTNESS_DEFAULT} + +.. code:: c + + esp_isp_color_config_t color_config = { + .color_contrast = { + .integer = 1, + .decimal = 0, + }, + .color_saturation = { + .integer = 1, + .decimal = 0, + }, + .color_hue = 0, + .color_brightness = 0, + }; + ESP_ERROR_CHECK(esp_isp_color_configure(isp_proc, &color_config)); + ESP_ERROR_CHECK(esp_isp_color_enable(isp_proc)); + +调用 :cpp:func:`esp_isp_color_configure` 后,需要通过调用 :cpp:func:`esp_isp_color_enable` 来启用 ISP 色彩控制器。此函数: + +* 将驱动程序状态从 **init** 切换为 **enable**。 + +调用 :cpp:func:`esp_isp_color_disable` 函数会执行相反的操作,即将驱动程序恢复到 **init** 状态。 + +.. note:: + + 当 ISP DVP 外设在使用且输出颜色格式设置为 RGB 色彩空间时,摄像头驱动程序会自动启用 :ref:`isp-color` 以确保数据输出正确。在这种情况下,禁止调用 :cpp:func:`esp_isp_color_disable` 函数,否则可能导致摄像头数据混乱。 + +.. _isp-ccm-config: + +配置 CCM +^^^^^^^^ + +色彩校正矩阵可以调整 RGB888 像素格式的颜色比例,可用于通过算法调整图像颜色(例如,使用 AWB 计算结果进行白平衡),或者通过滤波算法用作过滤器。 + +调整色彩校正矩阵的公式如下: + +.. code-block:: none + + [ R' ] [ RR RG RB ] [ R ] + [ G' ] = [ GR GG GB ] * [ G ] + [ B' ] [ BR BG BB ] [ B ] + +可以参考以下代码进行配置: + +.. code-block:: c + + // ... + // 配置 CCM + esp_isp_ccm_config_t ccm_cfg = { + .matrix = { + 1.0, 0.0, 0.0, + 0.0, 1.0, 0.0, + 0.0, 0.0, 1.0 + }, + .saturation = false, + ... + }; + ESP_ERROR_CHECK(esp_isp_ccm_configure(isp_proc, &ccm_cfg)); + // 启用 CCM 模块后,配置好的 CCM 将应用到图像上 + ESP_ERROR_CHECK(esp_isp_ccm_enable(isp_proc)); + // CCM 也可以在启用后进行配置 + ccm_cfg.matrix[0][0] = 2.0; + ESP_ERROR_CHECK(esp_isp_ccm_configure(isp_proc, &ccm_cfg)); + // 如果不再需要 CCM,则禁用它 + ESP_ERROR_CHECK(esp_isp_ccm_disable(isp_proc)); + +.. _isp-demosaic: + +ISP 去马赛克控制器 +~~~~~~~~~~~~~~~~~~~~~~ + +此流水线用于执行图像去马赛克算法,将 RAW 图像转换为 RGB 模式。 + +可调用 :cpp:func:`esp_isp_demosaic_configure` 来配置去马赛克功能,请参考以下代码: + +.. code:: c + + esp_isp_demosaic_config_t demosaic_config = { + .grad_ratio = { + .integer = 2, + .decimal = 5, + }, + ... + }; + + ESP_ERROR_CHECK(esp_isp_demosaic_configure(isp_proc, &demosaic_config)); + ESP_ERROR_CHECK(esp_isp_demosaic_enable(isp_proc)); + +调用 :cpp:func:`esp_isp_demosaic_configure` 后,需要通过调用 :cpp:func:`esp_isp_demosaic_enable` 来启用 ISP 去马赛克控制器。此函数: + +* 将驱动程序状态从 **init** 切换到 **enable**。 + +调用 :cpp:func:`esp_isp_demosaic_disable` 会执行相反的操作,即将驱动程序恢复到 **init** 状态。 + +即使驱动程序处于 **init** 状态,也可以调用 :cpp:func:`esp_isp_demosaic_configure`,但去马赛克配置只有在 **enable** 状态下才会生效。 + +.. _isp-gamma-correction: + +启用 gamma 校正 +^^^^^^^^^^^^^^^ + +人眼的视觉系统对物理亮度的感知是非线性的。将 gamma 校正添加到 ISP 流水线中,可以将 RGB 坐标转换为坐标与主观亮度成正比的空间。 + +驱动程序提供了帮助函数 :cpp:func:`esp_isp_gamma_fill_curve_points`,用于填充 :cpp:type:`isp_gamma_curve_points_t`,这是描述 gamma 校正曲线的点集合。也可以通过手动声明点来获得期望的 gamma 校正曲线。每个 R/G/B 分量有自己的 gamma 校正曲线,可以通过调用 :cpp:func:`esp_isp_gamma_configure` 来配置。 + +以下是一个典型的代码示例: + +.. code:: c + + #include + + // 设置相机 gamma 为 0.7,gamma 校正曲线为 y = 256 * (x / 256) ^ 0.7 + static uint32_t s_gamma_curve(uint32_t x) + { + return pow((double)x / 256, 0.7) * 256; + } + + isp_gamma_curve_points_t pts = {}; + ESP_ERROR_CHECK(esp_isp_gamma_fill_curve_points(s_gamma_curve, &pts)); + ESP_ERROR_CHECK(esp_isp_gamma_configure(isp_proc, COLOR_COMPONENT_R, &pts)); + ESP_ERROR_CHECK(esp_isp_gamma_configure(isp_proc, COLOR_COMPONENT_G, &pts)); + ESP_ERROR_CHECK(esp_isp_gamma_configure(isp_proc, COLOR_COMPONENT_B, &pts)); + + // 配置完曲线参数后启用 gamma 模块 + ESP_ERROR_CHECK(esp_isp_gamma_enable(isp_proc)); + + // 如果不再需要,则禁用 gamma + ESP_ERROR_CHECK(esp_isp_gamma_disable(isp_proc)); + +.. _isp-sharpen: + +ISP 锐化控制器 +~~~~~~~~~~~~~~ + +此流水线用于在 YUV 模式下锐化输入图像。 + +调用 :cpp:func:`esp_isp_sharpen_configure` 来配置锐化功能,请参考以下代码。 + +.. code:: c + + esp_isp_sharpen_config_t sharpen_config = { + .h_thresh = 255, + .sharpen_template = { + {1, 2, 1}, + {2, 4, 2}, + {1, 2, 1}, + }, + ... + }; + ESP_ERROR_CHECK(esp_isp_sharpen_configure(isp_proc, &sharpen_config)); + ESP_ERROR_CHECK(esp_isp_sharpen_enable(isp_proc)); + +调用 :cpp:member:`esp_isp_sharpen_config_t::sharpen_template` 进行锐化。可以通过高斯滤波器模板或均值滤波器模板来设置 :cpp:member:`esp_isp_sharpen_config_t::sharpen_template`。 + +调用 :cpp:func:`esp_isp_sharpen_configure` 后,需要通过调用 :cpp:func:`esp_isp_sharpen_enable` 以启用 ISP 锐化控制器。此函数: + +* 将驱动程序状态从 **init** 切换到 **enable**。 + +调用 :cpp:func:`esp_isp_sharpen_disable` 函数会执行相反的操作,即将驱动程序恢复到 **init** 状态。 + +即使驱动程序处于 **init** 状态,也可以调用 :cpp:func:`esp_isp_sharpen_configure`,但锐化配置只有在 **enable** 状态下才会生效。 + +.. _isp-crop: + +ISP 图像裁剪控制器 +~~~~~~~~~~~~~~~~~~ + +ISP 图像裁剪功能可以从原始图像中提取指定区域,减少后续处理的数据量,提高处理效率。裁剪功能在 ISP 流水线的末端执行,可以输出比输入图像更小的区域。 + +.. note:: + + ISP 图像裁剪功能仅在 ESP32-P4 revision 3.0 及以上版本中可用。 + +可调用 :cpp:func:`esp_isp_crop_configure` 函数配置图像裁剪功能,请参考以下代码: + +.. code-block:: c + + esp_isp_crop_config_t crop_config = { + .window = { + .top_left = { + .x = 100, // 裁剪区域左上角 X 坐标 + .y = 100, // 裁剪区域左上角 Y 坐标 + }, + .btm_right = { + .x = 699, // 裁剪区域右下角 X 坐标 + .y = 499, // 裁剪区域右下角 Y 坐标 + } + } + }; + ESP_ERROR_CHECK(esp_isp_crop_configure(isp_proc, &crop_config)); + ESP_ERROR_CHECK(esp_isp_crop_enable(isp_proc)); + +调用 :cpp:func:`esp_isp_crop_configure` 后,需要通过调用 :cpp:func:`esp_isp_crop_enable` 来启用 ISP 图像裁剪控制器。此函数: + +* 将驱动程序状态从 **init** 切换到 **enable**。 + +调用 :cpp:func:`esp_isp_crop_disable` 函数会执行相反的操作,即将驱动程序恢复到 **init** 状态。 + +即使驱动程序处于 **init** 状态,也可以调用 :cpp:func:`esp_isp_crop_configure`,但裁剪配置只有在 **enable** 状态下才会生效。 + +.. note:: + + - 裁剪区域的左上角坐标 (top_left) 必须小于右下角坐标 (btm_right) + - 裁剪区域的左上角坐标必须为偶数,右下角坐标必须为奇数 + - 裁剪区域不能超出原始图像的边界 + - 需根据裁剪后分辨率调整显示介质(如LCD)的尺寸,确保显示完整,避免出现黑边或拉伸现象。 + +.. _isp-callback: + +注册事件回调函数 +^^^^^^^^^^^^^^^^ + +ISP 模块启动后,会动态生成特定事件。 + +你也可以通过参数 ``user_data`` 将自己的上下文保存到回调函数中,用户数据将直接传递给回调函数。 + +.. note:: + + 下文中提到的回调函数在 ISR 上下文中被调用,必须确保这些函数不会尝试阻塞(例如,确保只从函数中调用带有 ``ISR`` 后缀的 FreeRTOS API)。 + +注册 ISP 处理器事件回调函数 +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +启用 ISP 处理器后,会动态生成多个 ISP 子模块的事件。可以通过调用 :cpp:func:`esp_isp_register_event_callbacks` 将函数挂接到中断服务例程。所有支持的事件回调函数可参见 :cpp:type:`esp_isp_evt_cbs_t`: + +- :cpp:member:`esp_isp_evt_cbs_t::on_sharpen_frame_done` 在完成锐化帧后设置回调函数。ISP 锐化子模块完成一帧的操作后会调用此函数。函数原型在 :cpp:type:`esp_isp_sharpen_callback_t` 中声明。 + +注册 ISP AF 环境检测器事件回调函数 +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +ISP AF 环境检测器启动后,将动态生成特定事件。若想在事件发生时调用某些函数,请通过调用 :cpp:func:`esp_isp_af_env_detector_register_event_callbacks` 将目标函数挂接到中断服务程序中。所有支持的事件回调函数可参见 :cpp:type:`esp_isp_af_env_detector_evt_cbs_t`: + +- :cpp:member:`esp_isp_af_env_detector_evt_cbs_t::on_env_statistics_done` 为环境统计完成事件设置回调函数。该函数原型在 :cpp:type:`esp_isp_af_env_detector_callback_t` 中声明。 +- :cpp:member:`esp_isp_af_env_detector_evt_cbs_t::on_env_change` 为环境变化事件设置回调函数。该函数原型在 :cpp:type:`esp_isp_af_env_detector_callback_t` 中声明。 + +注册 ISP AWB 统计完成事件回调函数 +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +ISP AWB 控制器完成白块数据统计后,将动态生成特定事件。若想在统计完成时收到通知,请通过调用 :cpp:func:`esp_isp_awb_register_event_callbacks` 将目标函数挂接到中断服务程序中。所有支持的事件回调函数可参见 :cpp:type:`esp_isp_awb_cbs_t`: + +- :cpp:member:`esp_isp_awb_cbs_t::on_statistics_done` 在白块数据统计完成后设置回调函数。该函数原型在 :cpp:type:`esp_isp_awb_callback_t` 中声明。 + + +注册 ISP AE 环境检测器事件回调函数 +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +ISP AE 环境检测器启动后,将动态生成特定事件。若想在事件发生时调用某些函数,请通过调用 :cpp:func:`esp_isp_ae_env_detector_register_event_callbacks` 将目标函数挂接到中断服务程序中。所有支持的事件回调函数可参见 :cpp:type:`esp_isp_ae_env_detector_evt_cbs_t`: + +- :cpp:member:`esp_isp_ae_env_detector_evt_cbs_t::on_env_statistics_done` 为环境统计完成事件设置回调函数。该函数原型在 :cpp:type:`esp_isp_ae_env_detector_callback_t` 中声明。 +- :cpp:member:`esp_isp_ae_env_detector_evt_cbs_t::on_env_change` 为环境变化事件设置回调函数。该函数原型在 :cpp:type:`esp_isp_ae_env_detector_callback_t` 中声明。 + + +注册 ISP HIST 统计完成事件回调函数 +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +ISP HIST 控制器完成亮度统计后,将动态生成特定事件。若想在统计完成时收到通知,请通过调用 :cpp:func:`esp_isp_hist_register_event_callbacks` 将目标函数挂接到中断服务程序。所有支持的事件回调函数可参见 :cpp:type:`esp_isp_hist_cbs_t`: + +- :cpp:member:`esp_isp_hist_cbs_t::on_statistics_done` 在完成亮度统计时设置回调函数。该函数原型在 :cpp:type:`esp_isp_hist_callback_t` 中声明。 + +.. _isp-thread-safety: + +线程安全 +^^^^^^^^ + +驱动程序会确保以下工厂函数的线程安全: + +.. list:: + + - :cpp:func:`esp_isp_new_processor` + - :cpp:func:`esp_isp_del_processor` + - :cpp:func:`esp_isp_new_af_controller` + - :cpp:func:`esp_isp_del_af_controller` + - :cpp:func:`esp_isp_new_awb_controller` + - :cpp:func:`esp_isp_del_awb_controller` + - :cpp:func:`esp_isp_new_ae_controller` + - :cpp:func:`esp_isp_del_ae_controller` + - :cpp:func:`esp_isp_new_hist_controller` + - :cpp:func:`esp_isp_del_hist_controller` + +使用时,可以直接从不同的 RTOS 任务中调用此类函数,无需额外锁保护。其他 API 无法确保线程安全。 + +.. _isp-kconfig-options: + +Kconfig 选项 +^^^^^^^^^^^^ + +- :ref:`CONFIG_ISP_ISR_IRAM_SAFE` 控制默认的 ISR 句柄在 cache 被禁用时是否可以正常工作。 + +.. _isp-iram-safe: + +IRAM 安全 +^^^^^^^^^ + +默认情况下,当 cache 因写入或擦除 flash 等原因而被禁用时,ISP 的中断将会延迟。 + +Kconfig 选项 :ref:`CONFIG_ISP_ISR_IRAM_SAFE` 支持: + +- 即使 cache 被禁用也能启用中断 +- 将 ISR 使用的所有函数放入 IRAM +- 将驱动程序对象放入 DRAM(以防意外映射到 PSRAM) + +启用上述 Kconfig 选项,保证 cache 被禁用时中断可以正常运行,但这会增加 IRAM 使用量。启用此选项后,当 cache 被禁用时,ISR 回调函数将继续运行。因此,必须确保回调函数及其上下文也是 IRAM 安全的。 + +Kconfig 选项 :ref:`CONFIG_ISP_CTRL_FUNC_IN_IRAM` 支持: + +- 将一些 ISP 控制函数放入 IRAM,函数列表请参见: + + .. list:: + + - :cpp:func:`esp_isp_sharpen_configure` + - :cpp:func:`esp_isp_demosaic_configure` + +应用示例 +-------- + +* :example:`peripherals/isp/multi_pipelines` 演示了如何使用 ISP 流水线处理来自摄像头传感器的图像信号,并通过 DSI 外设在 LCD 屏幕上显示视频。 + +API 参考 +-------- + +.. include-build-file:: inc/isp.inc +.. include-build-file:: inc/isp_af.inc +.. include-build-file:: inc/isp_ae.inc +.. include-build-file:: inc/isp_awb.inc +.. include-build-file:: inc/isp_bf.inc +.. include-build-file:: inc/isp_blc.inc +.. include-build-file:: inc/isp_lsc.inc +.. include-build-file:: inc/isp_ccm.inc +.. include-build-file:: inc/isp_demosaic.inc +.. include-build-file:: inc/isp_sharpen.inc +.. include-build-file:: inc/isp_gamma.inc +.. include-build-file:: inc/isp_hist.inc +.. include-build-file:: inc/isp_color.inc +.. include-build-file:: inc/isp_crop.inc +.. include-build-file:: inc/isp_core.inc +.. include-build-file:: inc/components/esp_driver_isp/include/driver/isp_types.inc +.. include-build-file:: inc/components/hal/include/hal/isp_types.inc diff --git a/examples/peripherals/camera/common_components/dsi_init/example_dsi_init.c b/examples/peripherals/camera/common_components/dsi_init/example_dsi_init.c index 3b2413b988..f9bc0c8cde 100644 --- a/examples/peripherals/camera/common_components/dsi_init/example_dsi_init.c +++ b/examples/peripherals/camera/common_components/dsi_init/example_dsi_init.c @@ -15,7 +15,14 @@ #include "example_dsi_init_config.h" #include "sdkconfig.h" -void example_dsi_resource_alloc(esp_lcd_dsi_bus_handle_t *mipi_dsi_bus, esp_lcd_panel_io_handle_t *mipi_dbi_io, esp_lcd_panel_handle_t *mipi_dpi_panel, void **frame_buffer) +static const char *TAG = "example_dsi_init"; + +void example_dsi_resource_alloc(const example_dsi_alloc_config_t *config, + esp_lcd_dsi_bus_handle_t *mipi_dsi_bus, + esp_lcd_panel_io_handle_t *mipi_dbi_io, + esp_lcd_panel_handle_t *mipi_dpi_panel, + void** fb0, + void** fb1) { //---------------DSI resource allocation------------------// esp_lcd_dsi_bus_config_t bus_config = { @@ -32,7 +39,22 @@ void example_dsi_resource_alloc(esp_lcd_dsi_bus_handle_t *mipi_dsi_bus, esp_lcd_ }; ESP_ERROR_CHECK(esp_lcd_new_panel_io_dbi(*mipi_dsi_bus, &dbi_config, mipi_dbi_io)); + // Use default config if not provided + example_dsi_alloc_config_t default_config = EXAMPLE_DSI_ALLOC_CONFIG_DEFAULT(); + if (config == NULL) { + config = &default_config; + } + + if (config->num_fbs < 1 || config->num_fbs > 2) { + ESP_LOGE(TAG, "Invalid num_fbs: %d, must be 1 or 2", config->num_fbs); + return; + } + + uint8_t num_fbs = config->num_fbs; + ESP_LOGI(TAG, "Allocating DSI resources with %d frame buffer(s)", num_fbs); + esp_lcd_dpi_panel_config_t dpi_config = { + .num_fbs = num_fbs, .dpi_clk_src = MIPI_DSI_DPI_CLK_SRC_DEFAULT, .dpi_clock_freq_mhz = EXAMPLE_MIPI_DSI_DPI_CLK_MHZ, .virtual_channel = 0, @@ -83,7 +105,23 @@ void example_dsi_resource_alloc(esp_lcd_dsi_bus_handle_t *mipi_dsi_bus, esp_lcd_ ESP_ERROR_CHECK(esp_lcd_new_panel_ek79007(*mipi_dbi_io, &lcd_dev_config, mipi_dpi_panel)); #endif - ESP_ERROR_CHECK(esp_lcd_dpi_panel_get_frame_buffer(*mipi_dpi_panel, 1, frame_buffer)); + // Get frame buffer addresses + if (fb0 != NULL) { + if (num_fbs == 2) { + if (fb1 != NULL) { + ESP_ERROR_CHECK(esp_lcd_dpi_panel_get_frame_buffer(*mipi_dpi_panel, 2, fb0, fb1)); + ESP_LOGD(TAG, "Frame buffer[0] allocated at: %p", *fb0); + ESP_LOGD(TAG, "Frame buffer[1] allocated at: %p", *fb1); + } else { + ESP_LOGW(TAG, "num_fbs is 2 but fb1 is NULL, only getting fb0"); + ESP_ERROR_CHECK(esp_lcd_dpi_panel_get_frame_buffer(*mipi_dpi_panel, 1, fb0)); + ESP_LOGD(TAG, "Frame buffer[0] allocated at: %p", *fb0); + } + } else { + ESP_ERROR_CHECK(esp_lcd_dpi_panel_get_frame_buffer(*mipi_dpi_panel, 1, fb0)); + ESP_LOGD(TAG, "Frame buffer[0] allocated at: %p", *fb0); + } + } } void example_dpi_panel_reset(esp_lcd_panel_handle_t mipi_dpi_panel) diff --git a/examples/peripherals/camera/common_components/dsi_init/include/example_dsi_init.h b/examples/peripherals/camera/common_components/dsi_init/include/example_dsi_init.h index 433c3d0881..bcf1f1073c 100644 --- a/examples/peripherals/camera/common_components/dsi_init/include/example_dsi_init.h +++ b/examples/peripherals/camera/common_components/dsi_init/include/example_dsi_init.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -14,14 +14,37 @@ extern "C" { #endif /** - * @brief DSI init function - * - * @param[out] mipi_dsi_bus MIPI DSI bus handle - * @param[out] mipi_dbi_io MIPI DBI io handle - * @param[out] mipi_dpi_panel MIPI DPI panel handle - * @param[out] frame_buffer frame buffer + * @brief DSI allocation configuration */ -void example_dsi_resource_alloc(esp_lcd_dsi_bus_handle_t *mipi_dsi_bus, esp_lcd_panel_io_handle_t *mipi_dbi_io, esp_lcd_panel_handle_t *mipi_dpi_panel, void **frame_buffer); +typedef struct { + uint8_t num_fbs; /*!< Number of frame buffers (1-2). */ +} example_dsi_alloc_config_t; + +/** + * @brief Default DSI allocation configuration + */ +#define EXAMPLE_DSI_ALLOC_CONFIG_DEFAULT() { \ + .num_fbs = 1, \ +} + +/** + * @brief DSI init function with configurable frame buffers + * + * @param[in] config DSI allocation configuration. If NULL, uses default (single buffer) + * @param[out] mipi_dsi_bus MIPI DSI bus handle + * @param[out] mipi_dbi_io MIPI DBI io handle + * @param[out] mipi_dpi_panel MIPI DPI panel handle + * @param[out] fb0 Pointer to receive the address of frame buffer 0 (first frame buffer) + * @param[out] fb1 Pointer to receive the address of frame buffer 1 (second frame buffer). If NULL, single buffer mode is used. + * + * @note The number of frame buffers allocated is determined by config->num_fbs. If config is NULL, single buffer mode is used. + */ +void example_dsi_resource_alloc(const example_dsi_alloc_config_t *config, + esp_lcd_dsi_bus_handle_t *mipi_dsi_bus, + esp_lcd_panel_io_handle_t *mipi_dbi_io, + esp_lcd_panel_handle_t *mipi_dpi_panel, + void **fb0, + void **fb1); /** * @brief DPI panel reset function diff --git a/examples/peripherals/camera/dvp_dsi/main/dvp_dsi_main.c b/examples/peripherals/camera/dvp_dsi/main/dvp_dsi_main.c index a3392ca965..1b18668faf 100644 --- a/examples/peripherals/camera/dvp_dsi/main/dvp_dsi_main.c +++ b/examples/peripherals/camera/dvp_dsi/main/dvp_dsi_main.c @@ -57,10 +57,10 @@ void app_main(void) ESP_ERROR_CHECK(esp_ldo_acquire_channel(&ldo_mipi_phy_config, &ldo_mipi_phy)); //---------------DSI Init------------------// - example_dsi_resource_alloc(&mipi_dsi_bus, &mipi_dbi_io, &mipi_dpi_panel, &frame_buffer); + example_dsi_resource_alloc(NULL, &mipi_dsi_bus, &mipi_dbi_io, &mipi_dpi_panel, &frame_buffer, NULL); //---------------Necessary variable config------------------// - frame_buffer_size = CONFIG_EXAMPLE_MIPI_DSI_DISP_HRES * CONFIG_EXAMPLE_MIPI_DSI_DISP_VRES * EXAMPLE_RGB565_BITS_PER_PIXEL / 8; + frame_buffer_size = CONFIG_EXAMPLE_MIPI_DSI_DISP_HRES * CONFIG_EXAMPLE_MIPI_DSI_DISP_VRES * EXAMPLE_RGB565_BYTES_PER_PIXEL; ESP_LOGD(TAG, "CONFIG_EXAMPLE_MIPI_DSI_DISP_HRES: %d, CONFIG_EXAMPLE_MIPI_DSI_DISP_VRES: %d, bits per pixel: %d", CONFIG_EXAMPLE_MIPI_DSI_DISP_HRES, CONFIG_EXAMPLE_MIPI_DSI_DISP_VRES, EXAMPLE_RGB565_BITS_PER_PIXEL); ESP_LOGD(TAG, "frame_buffer_size: %zu", frame_buffer_size); @@ -105,7 +105,7 @@ void app_main(void) } //--------Allocate Camera Buffer----------// - size_t cam_buffer_size = CONFIG_EXAMPLE_CAM_HRES * CONFIG_EXAMPLE_CAM_VRES * EXAMPLE_RGB565_BITS_PER_PIXEL / 8; + size_t cam_buffer_size = CONFIG_EXAMPLE_CAM_HRES * CONFIG_EXAMPLE_CAM_VRES * EXAMPLE_RGB565_BYTES_PER_PIXEL; void *cam_buffer = esp_cam_ctlr_alloc_buffer(cam_handle, cam_buffer_size, MALLOC_CAP_DMA | MALLOC_CAP_SPIRAM); if (!cam_buffer) { ESP_LOGE(TAG, "no mem for cam_buffer"); diff --git a/examples/peripherals/camera/dvp_dsi/main/example_config.h b/examples/peripherals/camera/dvp_dsi/main/example_config.h index b297650636..eb76ca211f 100644 --- a/examples/peripherals/camera/dvp_dsi/main/example_config.h +++ b/examples/peripherals/camera/dvp_dsi/main/example_config.h @@ -13,13 +13,35 @@ extern "C" { #endif #define EXAMPLE_RGB565_BITS_PER_PIXEL (16) +#define EXAMPLE_RGB565_BYTES_PER_PIXEL (EXAMPLE_RGB565_BITS_PER_PIXEL / 8) + +#define EXAMPLE_DVP_CAM_XCLK_FREQ_HZ (20000000) +#define EXAMPLE_DVP_CAM_DATA_WIDTH (8) + +#if !CONFIG_ESP32P4_SELECTS_REV_LESS_V3 +#define EXAMPLE_DVP_CAM_SCCB_SCL_IO (8) +#define EXAMPLE_DVP_CAM_SCCB_SDA_IO (7) + +#define EXAMPLE_DVP_CAM_D0_IO (2) +#define EXAMPLE_DVP_CAM_D1_IO (32) +#define EXAMPLE_DVP_CAM_D2_IO (33) +#define EXAMPLE_DVP_CAM_D3_IO (23) +#define EXAMPLE_DVP_CAM_D4_IO (3) +#define EXAMPLE_DVP_CAM_D5_IO (6) +#define EXAMPLE_DVP_CAM_D6_IO (5) +#define EXAMPLE_DVP_CAM_D7_IO (21) + +#define EXAMPLE_DVP_CAM_XCLK_IO (20) +#define EXAMPLE_DVP_CAM_PCLK_IO (4) +#define EXAMPLE_DVP_CAM_DE_IO (22) +#define EXAMPLE_DVP_CAM_VSYNC_IO (37) +#define EXAMPLE_DVP_CAM_HSYNC_IO (-1) + +#else #define EXAMPLE_DVP_CAM_SCCB_SCL_IO (33) #define EXAMPLE_DVP_CAM_SCCB_SDA_IO (32) -#define EXAMPLE_DVP_CAM_XCLK_FREQ_HZ (20000000) - -#define EXAMPLE_DVP_CAM_DATA_WIDTH (8) #define EXAMPLE_DVP_CAM_D0_IO (53) #define EXAMPLE_DVP_CAM_D1_IO (54) #define EXAMPLE_DVP_CAM_D2_IO (26) @@ -28,11 +50,13 @@ extern "C" { #define EXAMPLE_DVP_CAM_D5_IO (45) #define EXAMPLE_DVP_CAM_D6_IO (46) #define EXAMPLE_DVP_CAM_D7_IO (47) + #define EXAMPLE_DVP_CAM_XCLK_IO (20) #define EXAMPLE_DVP_CAM_PCLK_IO (21) #define EXAMPLE_DVP_CAM_DE_IO (22) #define EXAMPLE_DVP_CAM_VSYNC_IO (23) #define EXAMPLE_DVP_CAM_HSYNC_IO (-1) +#endif #if CONFIG_EXAMPLE_CAM_HRES_640 diff --git a/examples/peripherals/camera/dvp_isp_dsi/main/dvp_isp_dsi_main.c b/examples/peripherals/camera/dvp_isp_dsi/main/dvp_isp_dsi_main.c index 9b6c049dfb..6bd754312e 100644 --- a/examples/peripherals/camera/dvp_isp_dsi/main/dvp_isp_dsi_main.c +++ b/examples/peripherals/camera/dvp_isp_dsi/main/dvp_isp_dsi_main.c @@ -89,10 +89,10 @@ void app_main(void) * ISP convert to RGB565 */ //---------------DSI Init------------------// - example_dsi_resource_alloc(&mipi_dsi_bus, &mipi_dbi_io, &mipi_dpi_panel, &frame_buffer); + example_dsi_resource_alloc(NULL, &mipi_dsi_bus, &mipi_dbi_io, &mipi_dpi_panel, &frame_buffer, NULL); //---------------Necessary variable config------------------// - frame_buffer_size = CONFIG_EXAMPLE_MIPI_DSI_DISP_HRES * CONFIG_EXAMPLE_MIPI_DSI_DISP_VRES * EXAMPLE_RGB565_BITS_PER_PIXEL / 8; + frame_buffer_size = CONFIG_EXAMPLE_MIPI_DSI_DISP_HRES * CONFIG_EXAMPLE_MIPI_DSI_DISP_VRES * EXAMPLE_RGB565_BYTES_PER_PIXEL; ESP_LOGD(TAG, "CONFIG_EXAMPLE_MIPI_DSI_DISP_HRES: %d, CONFIG_EXAMPLE_MIPI_DSI_DISP_VRES: %d, bits per pixel: %d", CONFIG_EXAMPLE_MIPI_DSI_DISP_HRES, CONFIG_EXAMPLE_MIPI_DSI_DISP_VRES, EXAMPLE_RGB565_BITS_PER_PIXEL); ESP_LOGD(TAG, "frame_buffer_size: %zu", frame_buffer_size); @@ -157,7 +157,7 @@ void app_main(void) } //--------Allocate Camera Buffer----------// - size_t cam_buffer_size = CONFIG_EXAMPLE_CAM_HRES * CONFIG_EXAMPLE_CAM_VRES * EXAMPLE_RGB565_BITS_PER_PIXEL / 8; + size_t cam_buffer_size = CONFIG_EXAMPLE_CAM_HRES * CONFIG_EXAMPLE_CAM_VRES * EXAMPLE_RGB565_BYTES_PER_PIXEL; void *cam_buffer = esp_cam_ctlr_alloc_buffer(cam_handle, cam_buffer_size, MALLOC_CAP_DMA | MALLOC_CAP_SPIRAM); if (!cam_buffer) { ESP_LOGE(TAG, "no mem for cam_buffer"); diff --git a/examples/peripherals/camera/dvp_isp_dsi/main/example_config.h b/examples/peripherals/camera/dvp_isp_dsi/main/example_config.h index 81b3188c62..c2eb00ffce 100644 --- a/examples/peripherals/camera/dvp_isp_dsi/main/example_config.h +++ b/examples/peripherals/camera/dvp_isp_dsi/main/example_config.h @@ -13,6 +13,7 @@ extern "C" { #endif #define EXAMPLE_RGB565_BITS_PER_PIXEL 16 +#define EXAMPLE_RGB565_BYTES_PER_PIXEL (EXAMPLE_RGB565_BITS_PER_PIXEL / 8) #define EXAMPLE_ISP_DVP_CAM_SCCB_SCL_IO (33) #define EXAMPLE_ISP_DVP_CAM_SCCB_SDA_IO (32) diff --git a/examples/peripherals/camera/dvp_spi_lcd/main/dvp_spi_lcd_main.c b/examples/peripherals/camera/dvp_spi_lcd/main/dvp_spi_lcd_main.c index eacaf2dff7..d9032a57c5 100644 --- a/examples/peripherals/camera/dvp_spi_lcd/main/dvp_spi_lcd_main.c +++ b/examples/peripherals/camera/dvp_spi_lcd/main/dvp_spi_lcd_main.c @@ -24,7 +24,7 @@ static const char *TAG = "dvp_spi_lcd"; -#define BUFFER_SIZE (CONFIG_EXAMPLE_CAM_HRES * CONFIG_EXAMPLE_CAM_VRES * EXAMPLE_RGB565_BITS_PER_PIXEL / 8) +#define BUFFER_SIZE (CONFIG_EXAMPLE_CAM_HRES * CONFIG_EXAMPLE_CAM_VRES * EXAMPLE_RGB565_BYTES_PER_PIXEL) typedef struct { esp_lcd_panel_handle_t panel_hdl; @@ -164,7 +164,7 @@ void app_main(void) } //--------Allocate Camera Buffer----------// - size_t cam_buffer_size = CONFIG_EXAMPLE_CAM_HRES * CONFIG_EXAMPLE_CAM_VRES * EXAMPLE_RGB565_BITS_PER_PIXEL / 8; + size_t cam_buffer_size = CONFIG_EXAMPLE_CAM_HRES * CONFIG_EXAMPLE_CAM_VRES * EXAMPLE_RGB565_BYTES_PER_PIXEL; void *cam_buffer = NULL; cam_buffer = esp_cam_ctlr_alloc_buffer(cam_handle, cam_buffer_size, EXAMPLE_DVP_CAM_BUF_ALLOC_CAPS); diff --git a/examples/peripherals/camera/dvp_spi_lcd/main/example_config.h b/examples/peripherals/camera/dvp_spi_lcd/main/example_config.h index 5609ef560c..95d178d468 100644 --- a/examples/peripherals/camera/dvp_spi_lcd/main/example_config.h +++ b/examples/peripherals/camera/dvp_spi_lcd/main/example_config.h @@ -14,6 +14,7 @@ extern "C" { //----------CAM Config------------// #define EXAMPLE_RGB565_BITS_PER_PIXEL 16 +#define EXAMPLE_RGB565_BYTES_PER_PIXEL (EXAMPLE_RGB565_BITS_PER_PIXEL / 8) #define EXAMPLE_DVP_CAM_SCCB_SCL_IO (5) #define EXAMPLE_DVP_CAM_SCCB_SDA_IO (4) diff --git a/examples/peripherals/camera/mipi_isp_dsi/main/example_config.h b/examples/peripherals/camera/mipi_isp_dsi/main/example_config.h index 570ebdc866..dc2725abad 100644 --- a/examples/peripherals/camera/mipi_isp_dsi/main/example_config.h +++ b/examples/peripherals/camera/mipi_isp_dsi/main/example_config.h @@ -13,6 +13,7 @@ extern "C" { #endif #define EXAMPLE_RGB565_BITS_PER_PIXEL 16 +#define EXAMPLE_RGB565_BYTES_PER_PIXEL (EXAMPLE_RGB565_BITS_PER_PIXEL / 8) #define EXAMPLE_MIPI_IDI_CLOCK_RATE (50000000) #define EXAMPLE_MIPI_CSI_LANE_BITRATE_MBPS 200 //line_rate = pclk * 4 diff --git a/examples/peripherals/camera/mipi_isp_dsi/main/mipi_isp_dsi_main.c b/examples/peripherals/camera/mipi_isp_dsi/main/mipi_isp_dsi_main.c index d7f3645da6..da5329845c 100644 --- a/examples/peripherals/camera/mipi_isp_dsi/main/mipi_isp_dsi_main.c +++ b/examples/peripherals/camera/mipi_isp_dsi/main/mipi_isp_dsi_main.c @@ -51,10 +51,10 @@ void app_main(void) * ISP convert to RGB565 */ //---------------DSI Init------------------// - example_dsi_resource_alloc(&mipi_dsi_bus, &mipi_dbi_io, &mipi_dpi_panel, &frame_buffer); + example_dsi_resource_alloc(NULL, &mipi_dsi_bus, &mipi_dbi_io, &mipi_dpi_panel, &frame_buffer, NULL); //---------------Necessary variable config------------------// - frame_buffer_size = CONFIG_EXAMPLE_MIPI_CSI_DISP_HRES * CONFIG_EXAMPLE_MIPI_DSI_DISP_VRES * EXAMPLE_RGB565_BITS_PER_PIXEL / 8; + frame_buffer_size = CONFIG_EXAMPLE_MIPI_CSI_DISP_HRES * CONFIG_EXAMPLE_MIPI_DSI_DISP_VRES * EXAMPLE_RGB565_BYTES_PER_PIXEL; ESP_LOGD(TAG, "CONFIG_EXAMPLE_MIPI_CSI_DISP_HRES: %d, CONFIG_EXAMPLE_MIPI_DSI_DISP_VRES: %d, bits per pixel: %d", CONFIG_EXAMPLE_MIPI_CSI_DISP_HRES, CONFIG_EXAMPLE_MIPI_DSI_DISP_VRES, EXAMPLE_RGB565_BITS_PER_PIXEL); ESP_LOGD(TAG, "frame_buffer_size: %zu", frame_buffer_size); diff --git a/examples/peripherals/isp/multi_pipelines/README.md b/examples/peripherals/isp/multi_pipelines/README.md index e8fa72ba54..23bd88a704 100644 --- a/examples/peripherals/isp/multi_pipelines/README.md +++ b/examples/peripherals/isp/multi_pipelines/README.md @@ -16,6 +16,9 @@ This example demonstrates how to use the ISP (image signal processor) to work wi - ISP GAMMA feature - ISP Color feature - ISP LSC feature +- ISP Crop feature (need to enable in `idf.py menuconfig`) + +Additionally, this example also implements **Dual Frame Buffer (Ping-Pong Buffering)**, which eliminates screen tearing for smooth video display ## Usage @@ -121,6 +124,21 @@ Remember to select the LCD screen model and set corresponding correct horizontal Available options for the camera sensor output horizontal/vertical resolution can be seen in ``menuconfig`` > ``Example Configuration``. Note that the horizontal resolution for the camera should be the same as the LCD screen horizontal resolution. +#### Optional: Image Cropping Configuration + +This example supports optional image cropping, which allows you to capture and display a specific region of the camera output. To enable this feature: + +1. Navigate to `menuconfig` > `Example Configuration` +2. Enable `Enable ISP Image Cropping` +3. Configure the crop region: + - `ISP Crop Top-Left Horizontal`: X coordinate of top-left corner + - `ISP Crop Top-Left Vertical`: Y coordinate of top-left corner + - `ISP Crop Bottom-Right Horizontal`: X coordinate of bottom-right corner + - `ISP Crop Bottom-Right Vertical`: Y coordinate of bottom-right corner + +**Note**: When cropping is enabled: +- The cropped image maintains its relative position on the screen +- A dedicated frame processing task handles the image transformation ### Build and Flash @@ -142,15 +160,23 @@ To exit the serial monitor, use `Ctrl` + `]`. If you see the following console output, your example should be running correctly: ``` -I (1085) main_task: Calling app_main() -I (1095) ili9881c: ID1: 0x98, ID2: 0x81, ID3: 0x5c -I (1125) gpio: GPIO[31]| InputEn: 1| OutputEn: 1| OpenDrain: 1| Pullup: 1| Pulldown: 0| Intr:0 -I (1125) gpio: GPIO[34]| InputEn: 1| OutputEn: 1| OpenDrain: 1| Pullup: 1| Pulldown: 0| Intr:0 -I (1295) ov5647: Detected Camera sensor PID=0x5647 with index 0 -I (1305) cam_dsi: fmt[0].name:MIPI_2lane_24Minput_RAW8_800x1280_50fps -I (1305) cam_dsi: fmt[1].name:MIPI_2lane_24Minput_RAW8_800x640_50fps -I (1315) cam_dsi: fmt[2].name:MIPI_2lane_24Minput_RAW8_800x800_50fps -I (1355) cam_dsi: Format in use:MIPI_2lane_24Minput_RAW8_800x640_50fps +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 ``` You will also see the screen auto-focus when the screen image changes. diff --git a/examples/peripherals/isp/multi_pipelines/main/Kconfig.projbuild b/examples/peripherals/isp/multi_pipelines/main/Kconfig.projbuild index 98a42e128a..5e4244b886 100644 --- a/examples/peripherals/isp/multi_pipelines/main/Kconfig.projbuild +++ b/examples/peripherals/isp/multi_pipelines/main/Kconfig.projbuild @@ -44,4 +44,57 @@ menu "Example Configuration" default 600 if EXAMPLE_MIPI_CSI_VRES_600 default 640 if EXAMPLE_MIPI_CSI_VRES_640 default 1280 if EXAMPLE_MIPI_CSI_VRES_1280 + + config EXAMPLE_ISP_CROP_ENABLE + depends on (ESP32P4_REV_MIN_FULL >= 300) + bool "Enable ISP crop functionality" + default n + help + Enable ISP crop functionality. When enabled, you can configure + the crop area for the ISP pipeline. + + menu "ISP Crop Configuration" + visible if EXAMPLE_ISP_CROP_ENABLE + + config EXAMPLE_ISP_CROP_TOP_LEFT_H + int "ISP crop top-left horizontal coordinate" + default 0 + range 0 799 if EXAMPLE_MIPI_CSI_HRES_800 + range 0 1023 if EXAMPLE_MIPI_CSI_HRES_1024 + help + Horizontal coordinate of the top-left corner for ISP crop. + Must be an even number and less than bottom-right horizontal coordinate. + + config EXAMPLE_ISP_CROP_TOP_LEFT_V + int "ISP crop top-left vertical coordinate" + default 0 + range 0 599 if EXAMPLE_MIPI_CSI_VRES_600 + range 0 639 if EXAMPLE_MIPI_CSI_VRES_640 + range 0 1279 if EXAMPLE_MIPI_CSI_VRES_1280 + help + Vertical coordinate of the top-left corner for ISP crop. + Must be an even number and less than bottom-right vertical coordinate. + + config EXAMPLE_ISP_CROP_BOTTOM_RIGHT_H + int "ISP crop bottom-right horizontal coordinate" + default 799 if EXAMPLE_MIPI_CSI_HRES_800 + default 1023 if EXAMPLE_MIPI_CSI_HRES_1024 + range 1 799 if EXAMPLE_MIPI_CSI_HRES_800 + range 1 1023 if EXAMPLE_MIPI_CSI_HRES_1024 + help + Horizontal coordinate of the bottom-right corner for ISP crop. + Must be an odd number, greater than top-left horizontal coordinate, and not exceed display width. + + config EXAMPLE_ISP_CROP_BOTTOM_RIGHT_V + int "ISP crop bottom-right vertical coordinate" + default 599 if EXAMPLE_MIPI_CSI_VRES_600 + default 639 if EXAMPLE_MIPI_CSI_VRES_640 + default 1279 if EXAMPLE_MIPI_CSI_VRES_1280 + range 1 599 if EXAMPLE_MIPI_CSI_VRES_600 + range 1 639 if EXAMPLE_MIPI_CSI_VRES_640 + range 1 1279 if EXAMPLE_MIPI_CSI_VRES_1280 + help + Vertical coordinate of the bottom-right corner for ISP crop. + Must be an odd number, greater than top-left vertical coordinate, and not exceed display height. + endmenu endmenu diff --git a/examples/peripherals/isp/multi_pipelines/main/example_config.h b/examples/peripherals/isp/multi_pipelines/main/example_config.h index 9c5482fe39..f4ca7636f0 100644 --- a/examples/peripherals/isp/multi_pipelines/main/example_config.h +++ b/examples/peripherals/isp/multi_pipelines/main/example_config.h @@ -11,6 +11,7 @@ extern "C" { #endif #define EXAMPLE_RGB565_BITS_PER_PIXEL 16 +#define EXAMPLE_RGB565_BYTES_PER_PIXEL (EXAMPLE_RGB565_BITS_PER_PIXEL / 8) #define EXAMPLE_MIPI_SCCB_FREQ (100000) #define EXAMPLE_MIPI_CSI_LANE_BITRATE_MBPS 200 //line_rate = pclk * 4 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 79ac257074..b84532617d 100644 --- a/examples/peripherals/isp/multi_pipelines/main/isp_dsi_main.c +++ b/examples/peripherals/isp/multi_pipelines/main/isp_dsi_main.c @@ -33,6 +33,103 @@ static const char *TAG = "isp_dsi"; static bool s_camera_get_new_vb(esp_cam_ctlr_handle_t handle, esp_cam_ctlr_trans_t *trans, void *user_data); static bool s_camera_get_finished_trans(esp_cam_ctlr_handle_t handle, esp_cam_ctlr_trans_t *trans, void *user_data); +/*--------------------------------------------------------------- + Ping-Pong Buffer Management +---------------------------------------------------------------*/ +typedef struct { + void *fb0; // Frame buffer 0 + void *fb1; // Frame buffer 1 + void *csi_buffer; // Current buffer for CSI to write + void *dsi_buffer; // Current buffer for DSI to display + esp_lcd_panel_handle_t panel;// DPI panel handle + int h_res; // Horizontal resolution + int v_res; // Vertical resolution (full screen) +#ifdef CONFIG_EXAMPLE_ISP_CROP_ENABLE + int crop_h_res; // Cropped horizontal resolution + int crop_v_res; // Cropped vertical resolution + void *pending_buffer; // Buffer pending to be displayed + SemaphoreHandle_t frame_ready_sem; // Semaphore to signal frame ready +#endif +} pingpong_buffer_ctx_t; + +#ifdef CONFIG_EXAMPLE_ISP_CROP_ENABLE +/** + * @brief Process frame: Add blank areas to fill full screen resolution + * + * Algorithm: Fill from bottom to top to avoid overwriting crop data + * - Cropped image is placed at original position (relative to full frame) + * - Other areas are filled with white (0xFFFF for RGB565) + * + * Example: Original 100x100, crop (50,50) to (100,100) → shows at bottom-right + * + * @param buffer Frame buffer to process (contains cropped image at start) + * @param ctx Ping-pong buffer context + */ +static void process_frame_with_blanks(void *buffer, pingpong_buffer_ctx_t *ctx) +{ + if (ctx->crop_v_res == ctx->v_res) { + // No cropping, no need to process + return; + } + + uint16_t *fb = (uint16_t *)buffer; + + const int crop_left = CONFIG_EXAMPLE_ISP_CROP_TOP_LEFT_H; + const int crop_top = CONFIG_EXAMPLE_ISP_CROP_TOP_LEFT_V; + const int crop_right = CONFIG_EXAMPLE_ISP_CROP_BOTTOM_RIGHT_H; + const int crop_bottom = CONFIG_EXAMPLE_ISP_CROP_BOTTOM_RIGHT_V; + const int crop_width = crop_right - crop_left + 1; + + const int full_width = CONFIG_EXAMPLE_MIPI_CSI_DISP_HRES; + const int full_height = CONFIG_EXAMPLE_MIPI_CSI_DISP_VRES; + + // Helper macros for pixel indexing +#define SRC_PIXEL(x, y) fb[(y) * crop_width + (x)] +#define DST_PIXEL(x, y) fb[(y) * full_width + (x)] + + // ========== Step 1: Fill bottom blank region [crop_bottom+1, full_height) ========== + if (crop_bottom + 1 < full_height) { + memset(&DST_PIXEL(0, crop_bottom + 1), + 0xFF, + full_width * (full_height - crop_bottom - 1) * EXAMPLE_RGB565_BYTES_PER_PIXEL); + } + + // ========== Step 2: Process crop region [crop_top, crop_bottom] ========== + for (int y = crop_bottom; y >= crop_top; y--) { + int src_y = y - crop_top; // Corresponding row in cropped data (0-based) + + // Fill right blank region first (crop_right+1, full_width) + if (crop_right + 1 < full_width) { + memset(&DST_PIXEL(crop_right + 1, y), + 0xFF, + (full_width - crop_right - 1) * EXAMPLE_RGB565_BYTES_PER_PIXEL); + } + + // Copy crop data from source to destination + memcpy(&DST_PIXEL(crop_left, y), + &SRC_PIXEL(0, src_y), + crop_width * EXAMPLE_RGB565_BYTES_PER_PIXEL); + + // Fill left blank region [0, crop_left) + if (crop_left > 0) { + memset(&DST_PIXEL(0, y), + 0xFF, + crop_left * EXAMPLE_RGB565_BYTES_PER_PIXEL); + } + } + + // ========== Step 3: Fill top blank region [0, crop_top) ========== + if (crop_top > 0) { + memset(&DST_PIXEL(0, 0), + 0xFF, + full_width * crop_top * EXAMPLE_RGB565_BYTES_PER_PIXEL); + } + +#undef SRC_PIXEL +#undef DST_PIXEL +} +#endif + /*--------------------------------------------------------------- AF ---------------------------------------------------------------*/ @@ -185,13 +282,47 @@ static uint32_t s_gamma_correction_curve(uint32_t x) return pow((double)x / 256, 0.7) * 256; } +#ifdef CONFIG_EXAMPLE_ISP_CROP_ENABLE +/*--------------------------------------------------------------- + Frame Processing Task +---------------------------------------------------------------*/ +static void frame_processing_task(void *arg) +{ + pingpong_buffer_ctx_t *ctx = (pingpong_buffer_ctx_t *)arg; + + while (1) { + // Wait for frame ready signal from ISR + if (xSemaphoreTake(ctx->frame_ready_sem, portMAX_DELAY) == pdTRUE) { + // Process the frame: add blank areas if needed + process_frame_with_blanks(ctx->pending_buffer, ctx); + + // Ping-Pong switch: swap CSI write buffer and DSI display buffer + void *temp = ctx->csi_buffer; + ctx->csi_buffer = ctx->dsi_buffer; + ctx->dsi_buffer = temp; + + // Trigger buffer switch by calling draw_bitmap + // DPI driver will detect which buffer we're using and switch to it + ESP_ERROR_CHECK(esp_lcd_panel_draw_bitmap(ctx->panel, + 0, 0, + ctx->h_res, + ctx->crop_v_res, + ctx->pending_buffer)); + + ESP_LOGD(TAG, "Frame displayed: %p", ctx->pending_buffer); + } + } +} +#endif // CONFIG_EXAMPLE_ISP_CROP_ENABLE + void app_main(void) { esp_err_t ret = ESP_FAIL; esp_lcd_dsi_bus_handle_t mipi_dsi_bus = NULL; esp_lcd_panel_io_handle_t mipi_dbi_io = NULL; esp_lcd_panel_handle_t mipi_dpi_panel = NULL; - void *frame_buffer = NULL; + void *fb0 = NULL; + void *fb1 = NULL; size_t frame_buffer_size = 0; //mipi ldo @@ -207,18 +338,54 @@ void app_main(void) * Sensor use RAW8 * ISP convert to RGB565 */ - //---------------DSI Init------------------// - example_dsi_resource_alloc(&mipi_dsi_bus, &mipi_dbi_io, &mipi_dpi_panel, &frame_buffer); + //---------------DSI Init with Dual Frame Buffers------------------// + example_dsi_alloc_config_t dsi_alloc_config = { + .num_fbs = 2, // Enable dual frame buffers + }; + example_dsi_resource_alloc(&dsi_alloc_config, &mipi_dsi_bus, &mipi_dbi_io, &mipi_dpi_panel, &fb0, &fb1); //---------------Necessary variable config------------------// - frame_buffer_size = CONFIG_EXAMPLE_MIPI_CSI_DISP_HRES * CONFIG_EXAMPLE_MIPI_DSI_DISP_VRES * EXAMPLE_RGB565_BITS_PER_PIXEL / 8; + int display_h_res = CONFIG_EXAMPLE_MIPI_CSI_DISP_HRES; + int display_v_res = CONFIG_EXAMPLE_MIPI_CSI_DISP_VRES; - ESP_LOGD(TAG, "CONFIG_EXAMPLE_MIPI_CSI_DISP_HRES: %d, CONFIG_EXAMPLE_MIPI_DSI_DISP_VRES: %d, bits per pixel: %d", CONFIG_EXAMPLE_MIPI_CSI_DISP_HRES, CONFIG_EXAMPLE_MIPI_DSI_DISP_VRES, 8); - ESP_LOGD(TAG, "frame_buffer_size: %zu", frame_buffer_size); - ESP_LOGD(TAG, "frame_buffer: %p", frame_buffer); +#ifdef CONFIG_EXAMPLE_ISP_CROP_ENABLE + // Use cropped resolution for frame buffer + display_h_res = CONFIG_EXAMPLE_ISP_CROP_BOTTOM_RIGHT_H - CONFIG_EXAMPLE_ISP_CROP_TOP_LEFT_H + 1; + display_v_res = CONFIG_EXAMPLE_ISP_CROP_BOTTOM_RIGHT_V - CONFIG_EXAMPLE_ISP_CROP_TOP_LEFT_V + 1; +#endif + + frame_buffer_size = CONFIG_EXAMPLE_MIPI_DSI_DISP_HRES * CONFIG_EXAMPLE_MIPI_DSI_DISP_VRES * EXAMPLE_RGB565_BYTES_PER_PIXEL; + + ESP_LOGI(TAG, "Original CSI resolution: %dx%d", CONFIG_EXAMPLE_MIPI_CSI_DISP_HRES, CONFIG_EXAMPLE_MIPI_CSI_DISP_VRES); + ESP_LOGI(TAG, "Display resolution: %dx%d, bits per pixel: %d", display_h_res, display_v_res, EXAMPLE_RGB565_BITS_PER_PIXEL); + ESP_LOGI(TAG, "Frame buffers: fb0=%p, fb1=%p", fb0, fb1); + + //---------------Ping-Pong Buffer Context------------------// + pingpong_buffer_ctx_t pp_ctx = { + .fb0 = fb0, + .fb1 = fb1, + .csi_buffer = fb0, // CSI starts writing to fb0 + .dsi_buffer = fb1, // DSI starts displaying fb1 + .panel = mipi_dpi_panel, + .h_res = CONFIG_EXAMPLE_MIPI_DSI_DISP_HRES, + .v_res = CONFIG_EXAMPLE_MIPI_DSI_DISP_VRES, +#ifdef CONFIG_EXAMPLE_ISP_CROP_ENABLE + .crop_h_res = display_h_res, + .crop_v_res = display_v_res, + .pending_buffer = NULL, + .frame_ready_sem = xSemaphoreCreateBinary(), +#endif + }; + +#ifdef CONFIG_EXAMPLE_ISP_CROP_ENABLE + if (pp_ctx.frame_ready_sem == NULL) { + ESP_LOGE(TAG, "Failed to create frame ready semaphore"); + return; + } +#endif esp_cam_ctlr_trans_t new_trans = { - .buffer = frame_buffer, + .buffer = pp_ctx.csi_buffer, .buflen = frame_buffer_size, }; @@ -248,8 +415,8 @@ void app_main(void) //---------------CSI Init------------------// esp_cam_ctlr_csi_config_t csi_config = { .ctlr_id = 0, - .h_res = CONFIG_EXAMPLE_MIPI_CSI_DISP_HRES, - .v_res = CONFIG_EXAMPLE_MIPI_CSI_DISP_VRES, + .h_res = display_h_res, + .v_res = display_v_res, .lane_bit_rate_mbps = EXAMPLE_MIPI_CSI_LANE_BITRATE_MBPS, .input_data_color_type = CAM_CTLR_COLOR_RAW8, .output_data_color_type = CAM_CTLR_COLOR_RGB565, @@ -268,7 +435,7 @@ void app_main(void) .on_get_new_trans = s_camera_get_new_vb, .on_trans_finished = s_camera_get_finished_trans, }; - if (esp_cam_ctlr_register_event_callbacks(handle, &cbs, &new_trans) != ESP_OK) { + if (esp_cam_ctlr_register_event_callbacks(handle, &cbs, &pp_ctx) != ESP_OK) { ESP_LOGE(TAG, "ops register fail"); return; } @@ -422,6 +589,29 @@ void app_main(void) ESP_ERROR_CHECK(esp_isp_lsc_enable(isp_proc)); #endif +#ifdef CONFIG_EXAMPLE_ISP_CROP_ENABLE + /*--------------------------------------------------------------- + CROP + ---------------------------------------------------------------*/ + esp_isp_crop_config_t crop_config = { + .window = { + .top_left = { + .x = CONFIG_EXAMPLE_ISP_CROP_TOP_LEFT_H, + .y = CONFIG_EXAMPLE_ISP_CROP_TOP_LEFT_V + }, + .btm_right = { + .x = CONFIG_EXAMPLE_ISP_CROP_BOTTOM_RIGHT_H, + .y = CONFIG_EXAMPLE_ISP_CROP_BOTTOM_RIGHT_V + } + } + }; + ESP_ERROR_CHECK(esp_isp_crop_configure(isp_proc, &crop_config)); + ESP_ERROR_CHECK(esp_isp_crop_enable(isp_proc)); + ESP_LOGI(TAG, "ISP Crop enabled: (%d,%d) to (%d,%d)", + CONFIG_EXAMPLE_ISP_CROP_TOP_LEFT_H, CONFIG_EXAMPLE_ISP_CROP_TOP_LEFT_V, + CONFIG_EXAMPLE_ISP_CROP_BOTTOM_RIGHT_H, CONFIG_EXAMPLE_ISP_CROP_BOTTOM_RIGHT_V); +#endif + typedef struct af_task_param_t { isp_proc_handle_t isp_proc; esp_sccb_io_handle_t dw9714_io_handle; @@ -433,12 +623,20 @@ void app_main(void) }; xTaskCreatePinnedToCore(af_task, "af_task", 8192, &af_task_param, 5, NULL, 0); +#ifdef CONFIG_EXAMPLE_ISP_CROP_ENABLE + //---------------Frame Processing Task------------------// + xTaskCreatePinnedToCore(frame_processing_task, "frame_proc", 4096, &pp_ctx, 6, NULL, 0); + ESP_LOGI(TAG, "Frame processing task created"); +#endif + //---------------DPI Reset------------------// example_dpi_panel_reset(mipi_dpi_panel); - //init to all white - memset(frame_buffer, 0xFF, frame_buffer_size); - esp_cache_msync((void *)frame_buffer, frame_buffer_size, ESP_CACHE_MSYNC_FLAG_DIR_C2M); + //init both frame buffers to white + memset(fb0, 0xFF, frame_buffer_size); + memset(fb1, 0xFF, frame_buffer_size); + esp_cache_msync((void *)fb0, frame_buffer_size, ESP_CACHE_MSYNC_FLAG_DIR_C2M); + esp_cache_msync((void *)fb1, frame_buffer_size, ESP_CACHE_MSYNC_FLAG_DIR_C2M); if (esp_cam_ctlr_start(handle) != ESP_OK) { ESP_LOGE(TAG, "Driver start fail"); @@ -454,14 +652,30 @@ void app_main(void) static bool s_camera_get_new_vb(esp_cam_ctlr_handle_t handle, esp_cam_ctlr_trans_t *trans, void *user_data) { - esp_cam_ctlr_trans_t new_trans = *(esp_cam_ctlr_trans_t *)user_data; - trans->buffer = new_trans.buffer; - trans->buflen = new_trans.buflen; + pingpong_buffer_ctx_t *ctx = (pingpong_buffer_ctx_t *)user_data; + + // Provide the current CSI buffer for the next frame + trans->buffer = ctx->csi_buffer; + trans->buflen = CONFIG_EXAMPLE_MIPI_CSI_DISP_HRES * CONFIG_EXAMPLE_MIPI_DSI_DISP_VRES * EXAMPLE_RGB565_BYTES_PER_PIXEL; return false; } bool s_camera_get_finished_trans(esp_cam_ctlr_handle_t handle, esp_cam_ctlr_trans_t *trans, void *user_data) { + pingpong_buffer_ctx_t *ctx = (pingpong_buffer_ctx_t *)user_data; + +#ifdef CONFIG_EXAMPLE_ISP_CROP_ENABLE + BaseType_t high_task_wakeup = pdFALSE; + ctx->pending_buffer = trans->buffer; + xSemaphoreGiveFromISR(ctx->frame_ready_sem, &high_task_wakeup); + return (high_task_wakeup == pdTRUE); +#else + void *temp = ctx->csi_buffer; + ctx->csi_buffer = ctx->dsi_buffer; + ctx->dsi_buffer = temp; + + ESP_ERROR_CHECK(esp_lcd_panel_draw_bitmap(ctx->panel, 0, 0, ctx->h_res, ctx->v_res, trans->buffer)); return false; +#endif } diff --git a/examples/peripherals/ppa/ppa_dsi/main/ppa_dsi_main.c b/examples/peripherals/ppa/ppa_dsi/main/ppa_dsi_main.c index 02d46d7fb7..04839b51a3 100644 --- a/examples/peripherals/ppa/ppa_dsi/main/ppa_dsi_main.c +++ b/examples/peripherals/ppa/ppa_dsi/main/ppa_dsi_main.c @@ -329,7 +329,7 @@ void app_main(void) ESP_ERROR_CHECK(esp_ldo_acquire_channel(&ldo_mipi_phy_config, &ldo_mipi_phy)); //---------------DSI Init------------------// - example_dsi_resource_alloc(&mipi_dsi_bus, &mipi_dbi_io, &mipi_dpi_panel, NULL); + example_dsi_resource_alloc(NULL, &mipi_dsi_bus, &mipi_dbi_io, &mipi_dpi_panel, NULL, NULL); example_dpi_panel_reset(mipi_dpi_panel); example_dpi_panel_init(mipi_dpi_panel);