feat(isp): support Crop driver on p4 rev3

Add support for crop driver on p4eco5 and update example in
`isp/multi_pipelines`
This commit is contained in:
Chen Chen
2025-09-08 17:24:25 +08:00
parent 5defc77ffe
commit 467eddae9f
29 changed files with 2015 additions and 240 deletions

View File

@@ -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()

View File

@@ -25,3 +25,4 @@
#include "driver/isp_lsc.h"
#include "driver/isp_sharpen.h"
#include "driver/isp_wbg.h"
#include "driver/isp_crop.h"

View File

@@ -0,0 +1,64 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include <stdint.h>
#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

View File

@@ -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;

View File

@@ -0,0 +1,80 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdio.h>
#include <stdlib.h>
#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;
}

View File

@@ -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
*/

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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 \

View File

@@ -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

View File

@@ -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 0255 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

View File

@@ -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

View File

@@ -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 <math.h>
// 设置相机 gamma 为 0.7gamma 校正曲线为 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

View File

@@ -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)

View File

@@ -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

View File

@@ -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");

View File

@@ -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

View File

@@ -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");

View File

@@ -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)

View File

@@ -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);

View File

@@ -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)

View File

@@ -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

View File

@@ -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);

View File

@@ -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.

View File

@@ -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

View File

@@ -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

View File

@@ -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
}

View File

@@ -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);