forked from espressif/esp-idf
Merge branch 'feat/isp_af_driver' into 'master'
isp: added isp driver framework and isp af driver See merge request espressif/esp-idf!27937
This commit is contained in:
12
components/esp_driver_isp/CMakeLists.txt
Normal file
12
components/esp_driver_isp/CMakeLists.txt
Normal file
@@ -0,0 +1,12 @@
|
||||
set(srcs)
|
||||
|
||||
set(public_include "include")
|
||||
|
||||
if(CONFIG_SOC_ISP_SUPPORTED)
|
||||
list(APPEND srcs "src/isp.c"
|
||||
"src/isp_af.c")
|
||||
endif()
|
||||
|
||||
idf_component_register(SRCS ${srcs}
|
||||
INCLUDE_DIRS ${public_include}
|
||||
)
|
11
components/esp_driver_isp/Kconfig
Normal file
11
components/esp_driver_isp/Kconfig
Normal file
@@ -0,0 +1,11 @@
|
||||
menu "ESP-Driver:ISP Configurations"
|
||||
depends on SOC_ISP_SUPPORTED
|
||||
|
||||
config ISP_ISR_IRAM_SAFE
|
||||
bool "ISP driver ISR IRAM-Safe"
|
||||
default n
|
||||
help
|
||||
Ensure the ISP driver ISR is IRAM-Safe. When enabled, the ISR handler
|
||||
will be available when the cache is disabled.
|
||||
|
||||
endmenu # ISP Configuration
|
87
components/esp_driver_isp/include/driver/isp.h
Normal file
87
components/esp_driver_isp/include/driver/isp.h
Normal file
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include "esp_err.h"
|
||||
#include "driver/isp_types.h"
|
||||
#include "driver/isp_af.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief ISP configurations
|
||||
*/
|
||||
typedef struct {
|
||||
isp_clk_src_t clk_src; ///< Clock source
|
||||
uint32_t clk_hz; ///< Clock frequency in Hz, suggest twice higher than cam sensor speed
|
||||
isp_input_data_source_t input_data_source; ///< Input data source
|
||||
isp_color_t input_data_color_type; ///< Input color type
|
||||
isp_color_t output_data_color_type; ///< Output color type
|
||||
bool has_line_start_packet; ///< Enable line start packet
|
||||
bool has_line_end_packet; ///< Enable line end packet
|
||||
uint32_t h_res; ///< Input horizontal resolution, i.e. the number of pixels in a line
|
||||
uint32_t v_res; ///< Input vertical resolution, i.e. the number of lines in a frame
|
||||
} esp_isp_processor_cfg_t;
|
||||
|
||||
/**
|
||||
* @brief New an ISP processor
|
||||
*
|
||||
* @param[in] proc_config Pointer to ISP config. Refer to ``esp_isp_processor_cfg_t``.
|
||||
* @param[out] ret_proc Processor handle
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK On success
|
||||
* - ESP_ERR_INVALID_ARG If the combination of arguments is invalid.
|
||||
* - ESP_ERR_NOT_FOUND No free interrupt found with the specified flags
|
||||
* - ESP_ERR_NOT_SUPPORTED Not supported mode
|
||||
* - ESP_ERR_NO_MEM If out of memory
|
||||
*/
|
||||
esp_err_t esp_isp_new_processor(const esp_isp_processor_cfg_t *proc_config, isp_proc_handle_t *ret_proc);
|
||||
|
||||
/**
|
||||
* @brief Delete an ISP processor
|
||||
*
|
||||
* @param[in] 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_del_processor(isp_proc_handle_t proc);
|
||||
|
||||
/**
|
||||
* @brief Enable an ISP processor
|
||||
*
|
||||
* @param[in] 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_enable(isp_proc_handle_t proc);
|
||||
|
||||
/**
|
||||
* @brief Disable an ISP processor
|
||||
*
|
||||
* @param[in] 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_disable(isp_proc_handle_t proc);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
220
components/esp_driver_isp/include/driver/isp_af.h
Normal file
220
components/esp_driver_isp/include/driver/isp_af.h
Normal file
@@ -0,0 +1,220 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include "esp_err.h"
|
||||
#include "driver/isp_types.h"
|
||||
#include "driver/isp.h"
|
||||
#include "soc/soc_caps.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief AF controller config
|
||||
*/
|
||||
typedef struct {
|
||||
#if SOC_ISP_AF_WINDOW_NUMS
|
||||
isp_af_window_t window[SOC_ISP_AF_WINDOW_NUMS]; ///< AF window settings
|
||||
#endif
|
||||
int edge_thresh; ///< Edge threshold, definition higher than this value will be counted as a valid pixel for calculating AF result
|
||||
} esp_isp_af_config_t;
|
||||
|
||||
/**
|
||||
* @brief New an ISP AF controller
|
||||
*
|
||||
* @param[in] isp_proc ISP Processor handle
|
||||
* @param[in] af_config Pointer to AF config. Refer to ``esp_isp_af_config_t``.
|
||||
* @param[out] ret_hdl AF controller handle
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK On success
|
||||
* - ESP_ERR_INVALID_ARG If the combination of arguments is invalid
|
||||
* - ESP_ERR_INVALID_STATE Invalid state
|
||||
* - ESP_ERR_NOT_FOUND No free interrupt found with the specified flags
|
||||
* - ESP_ERR_NO_MEM If out of memory
|
||||
*/
|
||||
esp_err_t esp_isp_new_af_controller(isp_proc_handle_t isp_proc, const esp_isp_af_config_t *af_config, isp_af_ctrlr_t *ret_hdl);
|
||||
|
||||
/**
|
||||
* @brief Delete an ISP AF controller
|
||||
*
|
||||
* @param[in] af_ctrlr AF controller 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_del_af_controller(isp_af_ctrlr_t af_ctrlr);
|
||||
|
||||
/**
|
||||
* @brief Enable an ISP AF controller
|
||||
*
|
||||
* @param[in] af_ctrlr AF controller 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_af_controller_enable(isp_af_ctrlr_t af_ctrlr);
|
||||
|
||||
/**
|
||||
* @brief Disable an ISP AF controller
|
||||
*
|
||||
* @param[in] af_ctrlr AF controller 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_af_controller_disable(isp_af_ctrlr_t af_ctrlr);
|
||||
|
||||
/**
|
||||
* @brief Get AF result
|
||||
*
|
||||
* @param[in] af_ctrlr AF controller handle
|
||||
* @param[out] out_res AF result
|
||||
*
|
||||
* @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_af_controller_get_oneshot_result(isp_af_ctrlr_t af_ctrlr, isp_af_result_t *out_res);
|
||||
|
||||
/*---------------------------------------------
|
||||
AF Env Monitor
|
||||
----------------------------------------------*/
|
||||
/**
|
||||
* @brief AF environment detector config
|
||||
*/
|
||||
typedef struct {
|
||||
int interval; ///< Interval between environment detection, in frames
|
||||
} esp_isp_af_env_config_t;
|
||||
|
||||
/**
|
||||
* @brief Event data structure
|
||||
*/
|
||||
typedef struct {
|
||||
//empty for future proof
|
||||
} esp_isp_af_env_detector_evt_data_t;
|
||||
|
||||
/**
|
||||
* @brief Prototype of ISP AF Env detector event callback
|
||||
*
|
||||
* @param[in] handle ISP Env detector handle
|
||||
* @param[in] edata ISP AF Env detector event data
|
||||
* @param[in] user_data User registered context, registered when in `esp_isp_af_env_detector_register_event_callbacks()`
|
||||
*
|
||||
* @return Whether a high priority task is woken up by this function
|
||||
*/
|
||||
typedef bool (*esp_isp_af_env_detector_callback_t)(isp_af_env_detr_t detector, const esp_isp_af_env_detector_evt_data_t *edata, void *user_data);
|
||||
|
||||
/**
|
||||
* @brief Group of ISP AF Env detector callbacks
|
||||
*
|
||||
* @note These callbacks are all running in an ISR environment.
|
||||
* @note When CONFIG_ISP_ISR_IRAM_SAFE is enabled, the callback itself and functions called by it should be placed in IRAM.
|
||||
* Involved variables should be in internal RAM as well.
|
||||
*/
|
||||
typedef struct {
|
||||
esp_isp_af_env_detector_callback_t on_env_change; ///< Event callback, invoked when environment change happens.
|
||||
} esp_isp_af_env_detector_evt_cbs_t;
|
||||
|
||||
/**
|
||||
* @brief New an ISP AF environment detector
|
||||
*
|
||||
* @param[in] af_ctrlr AF controller handle
|
||||
* @param[in] config Pointer to AF env detector config. Refer to ``esp_isp_af_env_config_t``.
|
||||
* @param[out] ret_hdl AF env detector handle
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK On success
|
||||
* - ESP_ERR_INVALID_ARG If the combination of arguments is invalid
|
||||
* - ESP_ERR_INVALID_STATE Invalid state
|
||||
* - ESP_ERR_NO_MEM If out of memory
|
||||
*/
|
||||
esp_err_t esp_isp_new_af_env_detector(isp_af_ctrlr_t af_ctrlr, const esp_isp_af_env_config_t *config, isp_af_env_detr_t *ret_hdl);
|
||||
|
||||
/**
|
||||
* @brief Delete an ISP AF environment detector
|
||||
*
|
||||
* @param[in] af_env_detector AF env detector 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_del_af_env_detector(isp_af_env_detr_t af_env_detector);
|
||||
|
||||
/**
|
||||
* @brief Enable an ISP AF environment detector
|
||||
*
|
||||
* @param[in] af_env_detector AF env detector 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_af_env_detector_enable(isp_af_env_detr_t af_env_detector);
|
||||
|
||||
/**
|
||||
* @brief Disable an ISP AF environment detector
|
||||
*
|
||||
* @param[in] af_env_detector AF env detector 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_af_env_detector_disable(isp_af_env_detr_t af_env_detector);
|
||||
|
||||
/**
|
||||
* @brief Set ISP AF environment detector detecting threshold
|
||||
*
|
||||
* @param[in] af_env_detector AF env detector handle
|
||||
* @param[in] definition_thresh Threshold for definition
|
||||
* @param[in] luminance_thresh Threshold for luminance
|
||||
*
|
||||
* @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_af_env_detector_set_threshold(isp_af_env_detr_t af_env_detector, int definition_thresh, int luminance_thresh);
|
||||
|
||||
/**
|
||||
* @brief Register AF environment detector event callbacks
|
||||
*
|
||||
* @note User can deregister a previously registered callback by calling this function and setting the to-be-deregistered callback member in
|
||||
* the `cbs` structure to NULL.
|
||||
* @note When CONFIG_ISP_ISR_IRAM_SAFE is enabled, the callback itself and functions called by it should be placed in IRAM.
|
||||
* Involved variables (including `user_data`) should be in internal RAM as well.
|
||||
*
|
||||
* @param[in] af_env_detector AF env detector handle
|
||||
* @param[in] cbs Group of callback functions
|
||||
* @param[in] user_data User data, which will be delivered to the callback functions directly
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK: On success
|
||||
* - ESP_ERR_INVALID_ARG: Invalid arguments
|
||||
* - ESP_ERR_INVALID_STATE: Driver state is invalid, you shouldn't call this API at this moment
|
||||
*/
|
||||
esp_err_t esp_isp_af_env_detector_register_event_callbacks(isp_af_env_detr_t af_env_detector, const esp_isp_af_env_detector_evt_cbs_t *cbs, void *user_data);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
32
components/esp_driver_isp/include/driver/isp_types.h
Normal file
32
components/esp_driver_isp/include/driver/isp_types.h
Normal file
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "hal/isp_types.h"
|
||||
#include "hal/color_types.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Type of ISP processor handle
|
||||
*/
|
||||
typedef struct isp_processor_t *isp_proc_handle_t;
|
||||
|
||||
/**
|
||||
* @brief Type of ISP AF controller handle
|
||||
*/
|
||||
typedef struct isp_af_controller_t *isp_af_ctrlr_t;
|
||||
|
||||
/**
|
||||
* @brief Type of ISP AF Env detector handle
|
||||
*/
|
||||
typedef struct isp_af_env_detector_t *isp_af_env_detr_t;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
301
components/esp_driver_isp/src/isp.c
Normal file
301
components/esp_driver_isp/src/isp.c
Normal file
@@ -0,0 +1,301 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <esp_types.h>
|
||||
#include <sys/lock.h>
|
||||
#include "sdkconfig.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_check.h"
|
||||
#include "esp_heap_caps.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "esp_clk_tree.h"
|
||||
#include "driver/isp.h"
|
||||
#include "esp_private/periph_ctrl.h"
|
||||
#include "esp_private/mipi_csi_share_hw_ctrl.h"
|
||||
#include "hal/hal_utils.h"
|
||||
#include "hal/isp_types.h"
|
||||
#include "hal/isp_hal.h"
|
||||
#include "hal/isp_ll.h"
|
||||
#include "soc/mipi_csi_bridge_struct.h"
|
||||
#include "soc/isp_periph.h"
|
||||
#include "isp_internal.h"
|
||||
|
||||
#if CONFIG_ISP_ISR_IRAM_SAFE
|
||||
#define ISP_INTR_ALLOC_FLAGS (ESP_INTR_FLAG_IRAM)
|
||||
#else
|
||||
#define ISP_INTR_ALLOC_FLAGS 0
|
||||
#endif
|
||||
|
||||
#if CONFIG_ISP_ISR_IRAM_SAFE
|
||||
#define ISP_MEM_ALLOC_CAPS (MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT)
|
||||
#else
|
||||
#define ISP_MEM_ALLOC_CAPS MALLOC_CAP_DEFAULT
|
||||
#endif
|
||||
|
||||
typedef struct isp_platform_t {
|
||||
_lock_t mutex;
|
||||
isp_processor_t *processors[SOC_ISP_NUMS];
|
||||
} isp_platform_t;
|
||||
|
||||
static const char *TAG = "ISP";
|
||||
static isp_platform_t s_platform;
|
||||
|
||||
static esp_err_t s_isp_claim_processor(isp_processor_t *proc)
|
||||
{
|
||||
assert(proc);
|
||||
|
||||
_lock_acquire(&s_platform.mutex);
|
||||
bool found = false;
|
||||
for (int i = 0; i < SOC_ISP_NUMS; i ++) {
|
||||
found = !s_platform.processors[i];
|
||||
if (found) {
|
||||
s_platform.processors[i] = proc;
|
||||
proc->proc_id = i;
|
||||
PERIPH_RCC_ATOMIC() {
|
||||
isp_ll_enable_module_clock(proc->hal.hw, true);
|
||||
isp_ll_reset_module_clock(proc->hal.hw);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
_lock_release(&s_platform.mutex);
|
||||
|
||||
if (!found) {
|
||||
return ESP_ERR_NOT_FOUND;
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t s_isp_declaim_processor(isp_processor_t *proc)
|
||||
{
|
||||
assert(proc);
|
||||
|
||||
_lock_acquire(&s_platform.mutex);
|
||||
s_platform.processors[proc->proc_id] = NULL;
|
||||
PERIPH_RCC_ATOMIC() {
|
||||
isp_ll_enable_module_clock(proc->hal.hw, false);
|
||||
}
|
||||
_lock_release(&s_platform.mutex);
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t esp_isp_new_processor(const esp_isp_processor_cfg_t *proc_config, isp_proc_handle_t *ret_proc)
|
||||
{
|
||||
esp_err_t ret = ESP_FAIL;
|
||||
ESP_RETURN_ON_FALSE(proc_config && ret_proc, ESP_ERR_INVALID_ARG, TAG, "invalid argument: null pointer");
|
||||
ESP_RETURN_ON_FALSE(proc_config->input_data_source == ISP_INPUT_DATA_SOURCE_CSI, ESP_ERR_NOT_SUPPORTED, TAG, "only support CSI as input source at this moment");
|
||||
|
||||
isp_processor_t *proc = heap_caps_calloc(1, sizeof(isp_processor_t), ISP_MEM_ALLOC_CAPS);
|
||||
ESP_RETURN_ON_FALSE(proc, ESP_ERR_NO_MEM, TAG, "no mem");
|
||||
|
||||
//claim a processor, then do assignment
|
||||
ESP_GOTO_ON_ERROR(s_isp_claim_processor(proc), err, TAG, "no available isp processor");
|
||||
#if SOC_ISP_SHARE_CSI_BRG
|
||||
ESP_GOTO_ON_ERROR(mipi_csi_brg_claim(MIPI_CSI_BRG_USER_SHARE, &proc->csi_brg_id), err, TAG, "csi bridge is in use already");
|
||||
#endif
|
||||
|
||||
isp_clk_src_t clk_src = !proc_config->clk_src ? ISP_CLK_SRC_DEFAULT : proc_config->clk_src;
|
||||
uint32_t clk_src_freq_hz = 0;
|
||||
ESP_GOTO_ON_ERROR(esp_clk_tree_src_get_freq_hz(clk_src, ESP_CLK_TREE_SRC_FREQ_PRECISION_CACHED, &clk_src_freq_hz), err, TAG, "clock source setting fail");
|
||||
ESP_GOTO_ON_FALSE((proc_config->clk_hz > 0 && proc_config->clk_hz <= clk_src_freq_hz), ESP_ERR_INVALID_ARG, err, TAG, "clk hz not supported");
|
||||
|
||||
uint32_t out_clk_freq_hz = 0;
|
||||
hal_utils_clk_div_t clk_div = {};
|
||||
hal_utils_clk_info_t clk_info = {
|
||||
.src_freq_hz = clk_src_freq_hz,
|
||||
.exp_freq_hz = proc_config->clk_hz,
|
||||
.max_integ = ISP_LL_TX_MAX_CLK_INT_DIV,
|
||||
.min_integ = 1,
|
||||
.round_opt = HAL_DIV_ROUND,
|
||||
};
|
||||
out_clk_freq_hz = hal_utils_calc_clk_div_integer(&clk_info, &clk_div.integer);
|
||||
if (out_clk_freq_hz != proc_config->clk_hz) {
|
||||
ESP_LOGW(TAG, "precision loss, real output frequency: %"PRIu32"Hz", out_clk_freq_hz);
|
||||
}
|
||||
|
||||
isp_hal_init(&proc->hal, proc->proc_id);
|
||||
//necessary ISP submodules that needs basic initialisation
|
||||
isp_ll_bf_clk_enable(proc->hal.hw, true);
|
||||
isp_ll_bf_enable(proc->hal.hw, true);
|
||||
isp_ll_ccm_clk_enable(proc->hal.hw, true);
|
||||
isp_ll_ccm_enable(proc->hal.hw, true);
|
||||
isp_ll_color_clk_enable(proc->hal.hw, true);
|
||||
isp_ll_color_enable(proc->hal.hw, true);
|
||||
PERIPH_RCC_ATOMIC() {
|
||||
isp_ll_select_clk_source(proc->hal.hw, clk_src);
|
||||
isp_ll_set_clock_div(proc->hal.hw, &clk_div);
|
||||
}
|
||||
|
||||
proc->isp_fsm = ISP_FSM_INIT;
|
||||
proc->spinlock = (portMUX_TYPE)portMUX_INITIALIZER_UNLOCKED;
|
||||
|
||||
//Input color format
|
||||
bool valid_format = false;
|
||||
color_space_pixel_format_t in_color_format = {
|
||||
.color_type_id = proc_config->input_data_color_type,
|
||||
};
|
||||
valid_format = isp_ll_set_input_data_color_format(proc->hal.hw, in_color_format);
|
||||
ESP_GOTO_ON_FALSE(valid_format, ESP_ERR_INVALID_ARG, err, TAG, "invalid input color space config");
|
||||
|
||||
//Output color format
|
||||
valid_format = false;
|
||||
color_space_pixel_format_t out_color_format = {
|
||||
.color_type_id = proc_config->output_data_color_type,
|
||||
};
|
||||
valid_format = isp_ll_set_output_data_color_format(proc->hal.hw, out_color_format);
|
||||
ESP_GOTO_ON_FALSE(valid_format, ESP_ERR_INVALID_ARG, err, TAG, "invalid output color space config");
|
||||
|
||||
isp_ll_clk_enable(proc->hal.hw, true);
|
||||
isp_ll_set_input_data_source(proc->hal.hw, proc_config->input_data_source);
|
||||
isp_ll_enable_line_start_packet_exist(proc->hal.hw, proc_config->has_line_start_packet);
|
||||
isp_ll_enable_line_end_packet_exist(proc->hal.hw, proc_config->has_line_end_packet);
|
||||
isp_ll_set_intput_data_h_pixel_num(proc->hal.hw, proc_config->h_res);
|
||||
isp_ll_set_intput_data_v_row_num(proc->hal.hw, proc_config->v_res);
|
||||
|
||||
*ret_proc = proc;
|
||||
|
||||
return ESP_OK;
|
||||
|
||||
err:
|
||||
free(proc);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t esp_isp_del_processor(isp_proc_handle_t proc)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(proc, ESP_ERR_INVALID_ARG, TAG, "invalid argument: null pointer");
|
||||
ESP_RETURN_ON_FALSE(proc->isp_fsm == ISP_FSM_INIT, ESP_ERR_INVALID_STATE, TAG, "processor isn't in init state");
|
||||
|
||||
//declaim first, then do free
|
||||
ESP_RETURN_ON_ERROR(s_isp_declaim_processor(proc), TAG, "declaim processor fail");
|
||||
#if SOC_ISP_SHARE_CSI_BRG
|
||||
ESP_RETURN_ON_ERROR(mipi_csi_brg_declaim(proc->csi_brg_id), TAG, "declaim csi bridge fail");
|
||||
#endif
|
||||
free(proc);
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t esp_isp_enable(isp_proc_handle_t proc)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(proc, ESP_ERR_INVALID_ARG, TAG, "invalid argument: null pointer");
|
||||
ESP_RETURN_ON_FALSE(proc->isp_fsm == ISP_FSM_INIT, ESP_ERR_INVALID_STATE, TAG, "processor isn't in init state");
|
||||
|
||||
isp_ll_enable(proc->hal.hw, true);
|
||||
proc->isp_fsm = ISP_FSM_ENABLE;
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t esp_isp_disable(isp_proc_handle_t proc)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(proc, ESP_ERR_INVALID_ARG, TAG, "invalid argument: null pointer");
|
||||
ESP_RETURN_ON_FALSE(proc->isp_fsm == ISP_FSM_ENABLE, ESP_ERR_INVALID_STATE, TAG, "processor isn't in enable state");
|
||||
|
||||
isp_ll_enable(proc->hal.hw, false);
|
||||
proc->isp_fsm = ISP_FSM_INIT;
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
/*---------------------------------------------------------------
|
||||
INTR
|
||||
---------------------------------------------------------------*/
|
||||
static void IRAM_ATTR s_isp_isr_dispatcher(void *arg)
|
||||
{
|
||||
isp_processor_t *proc = (isp_processor_t *)arg;
|
||||
bool need_yield = false;
|
||||
|
||||
//Check and clear hw events
|
||||
uint32_t af_events = isp_hal_check_clear_intr_event(&proc->hal, ISP_LL_EVENT_AF_MASK);
|
||||
|
||||
bool do_dispatch = false;
|
||||
//Deal with hw events
|
||||
if (af_events) {
|
||||
portENTER_CRITICAL_ISR(&proc->spinlock);
|
||||
do_dispatch = proc->af_isr_added;
|
||||
portEXIT_CRITICAL_ISR(&proc->spinlock);
|
||||
|
||||
if (do_dispatch) {
|
||||
need_yield |= esp_isp_af_isr(proc, af_events);
|
||||
}
|
||||
do_dispatch = false;
|
||||
}
|
||||
|
||||
if (need_yield) {
|
||||
portYIELD_FROM_ISR();
|
||||
}
|
||||
}
|
||||
|
||||
esp_err_t esp_isp_register_isr(isp_proc_handle_t proc, isp_submodule_t submodule)
|
||||
{
|
||||
esp_err_t ret = ESP_FAIL;
|
||||
ESP_RETURN_ON_FALSE(proc, ESP_ERR_INVALID_ARG, TAG, "invalid argument: null pointer");
|
||||
|
||||
bool do_alloc = false;
|
||||
portENTER_CRITICAL(&proc->spinlock);
|
||||
proc->isr_ref_counts++;
|
||||
if (proc->isr_ref_counts == 1) {
|
||||
assert(!proc->intr_hdl);
|
||||
do_alloc = true;
|
||||
}
|
||||
|
||||
switch (submodule) {
|
||||
case ISP_SUBMODULE_AF:
|
||||
proc->af_isr_added = true;
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
portEXIT_CRITICAL(&proc->spinlock);
|
||||
|
||||
if (do_alloc) {
|
||||
ret = esp_intr_alloc(isp_hw_info.instances[proc->proc_id].irq, ISP_INTR_ALLOC_FLAGS, s_isp_isr_dispatcher, (void *)proc, &proc->intr_hdl);
|
||||
if (ret != ESP_OK) {
|
||||
ESP_LOGE(TAG, "no intr source");
|
||||
return ret;
|
||||
}
|
||||
esp_intr_enable(proc->intr_hdl);
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t esp_isp_deregister_isr(isp_proc_handle_t proc, isp_submodule_t submodule)
|
||||
{
|
||||
esp_err_t ret = ESP_FAIL;
|
||||
ESP_RETURN_ON_FALSE(proc, ESP_ERR_INVALID_ARG, TAG, "invalid argument: null pointer");
|
||||
|
||||
bool do_free = false;
|
||||
portENTER_CRITICAL(&proc->spinlock);
|
||||
proc->isr_ref_counts--;
|
||||
assert(proc->isr_ref_counts >= 0);
|
||||
if (proc->isr_ref_counts == 0) {
|
||||
assert(proc->intr_hdl);
|
||||
do_free = true;
|
||||
}
|
||||
|
||||
switch (submodule) {
|
||||
case ISP_SUBMODULE_AF:
|
||||
proc->af_isr_added = false;
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
portEXIT_CRITICAL(&proc->spinlock);
|
||||
|
||||
if (do_free) {
|
||||
esp_intr_disable(proc->intr_hdl);
|
||||
ret = esp_intr_free(proc->intr_hdl);
|
||||
if (ret != ESP_OK) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
}
|
330
components/esp_driver_isp/src/isp_af.c
Normal file
330
components/esp_driver_isp/src/isp_af.c
Normal file
@@ -0,0 +1,330 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <esp_types.h>
|
||||
#include <sys/lock.h>
|
||||
#include "sdkconfig.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_check.h"
|
||||
#include "esp_heap_caps.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "driver/isp.h"
|
||||
#include "hal/isp_hal.h"
|
||||
#include "hal/isp_ll.h"
|
||||
#include "isp_internal.h"
|
||||
|
||||
#if CONFIG_ISP_ISR_IRAM_SAFE
|
||||
#define ISP_AF_MEM_ALLOC_CAPS (MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT)
|
||||
#else
|
||||
#define ISP_AF_MEM_ALLOC_CAPS MALLOC_CAP_DEFAULT
|
||||
#endif
|
||||
|
||||
static const char *TAG = "ISP_AF";
|
||||
|
||||
/*---------------------------------------------
|
||||
AF
|
||||
----------------------------------------------*/
|
||||
static esp_err_t s_isp_claim_af_controller(isp_proc_handle_t isp_proc, isp_af_controller_t *af_ctlr)
|
||||
{
|
||||
assert(isp_proc && af_ctlr);
|
||||
|
||||
bool found = false;
|
||||
portENTER_CRITICAL(&isp_proc->spinlock);
|
||||
for (int i = 0; i < SOC_ISP_AF_CTLR_NUMS; i++) {
|
||||
found = !isp_proc->af_ctlr[i];
|
||||
if (found) {
|
||||
isp_proc->af_ctlr[i] = af_ctlr;
|
||||
af_ctlr->id = i;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
portEXIT_CRITICAL(&isp_proc->spinlock);
|
||||
|
||||
if (!found) {
|
||||
return ESP_ERR_NOT_FOUND;
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t s_isp_declaim_af_controller(isp_af_controller_t *af_ctlr)
|
||||
{
|
||||
assert(af_ctlr && af_ctlr->isp_proc);
|
||||
|
||||
portENTER_CRITICAL(&af_ctlr->isp_proc->spinlock);
|
||||
af_ctlr->isp_proc->af_ctlr[af_ctlr->id] = NULL;
|
||||
portEXIT_CRITICAL(&af_ctlr->isp_proc->spinlock);
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t esp_isp_new_af_controller(isp_proc_handle_t isp_proc, const esp_isp_af_config_t *af_config, isp_af_ctrlr_t *ret_hdl)
|
||||
{
|
||||
esp_err_t ret = ESP_FAIL;
|
||||
ESP_RETURN_ON_FALSE(isp_proc && af_config && ret_hdl, ESP_ERR_INVALID_ARG, TAG, "invalid argument: null pointer");
|
||||
|
||||
bool rgb2yuv_en = isp_ll_is_rgb2yuv_enabled(isp_proc->hal.hw);
|
||||
bool demosaic_en = isp_ll_is_demosaic_enabled(isp_proc->hal.hw);
|
||||
ESP_RETURN_ON_FALSE(demosaic_en && rgb2yuv_en, ESP_ERR_INVALID_STATE, TAG, "RGB2YUV not enabled, please update the output_data_color_type");
|
||||
|
||||
for (int i = 0; i < SOC_ISP_AF_WINDOW_NUMS; i++) {
|
||||
ESP_LOGV(TAG, "af_config->window[%d].top_left_x: %"PRId32, i, af_config->window[i].top_left_x);
|
||||
ESP_LOGV(TAG, "af_config->window[%d].bottom_right_x: %"PRId32, i, af_config->window[i].bottom_right_x);
|
||||
ESP_LOGV(TAG, "af_config->window[%d].bottom_right_y: %"PRId32, i, af_config->window[i].bottom_right_y);
|
||||
ESP_LOGV(TAG, "af_config->window[%d].top_left_y: %"PRId32, i, af_config->window[i].top_left_y);
|
||||
|
||||
ESP_RETURN_ON_FALSE(((af_config->window[i].top_left_x < ISP_LL_AF_WINDOW_MAX_RANGE) &&
|
||||
(af_config->window[i].bottom_right_x >= af_config->window[i].top_left_x) &&
|
||||
(af_config->window[i].bottom_right_x < ISP_LL_AF_WINDOW_MAX_RANGE) &&
|
||||
(af_config->window[i].top_left_y < ISP_LL_AF_WINDOW_MAX_RANGE) &&
|
||||
(af_config->window[i].bottom_right_y >= af_config->window[i].top_left_y) &&
|
||||
(af_config->window[i].bottom_right_y < ISP_LL_AF_WINDOW_MAX_RANGE)), ESP_ERR_INVALID_ARG, TAG, "invalid window");
|
||||
}
|
||||
ESP_RETURN_ON_FALSE(af_config->edge_thresh > 0, ESP_ERR_INVALID_ARG, TAG, "edge threshold should be larger than 0");
|
||||
|
||||
isp_af_controller_t *af_ctlr = heap_caps_calloc(1, sizeof(isp_af_controller_t), ISP_AF_MEM_ALLOC_CAPS);
|
||||
ESP_RETURN_ON_FALSE(af_ctlr, ESP_ERR_NO_MEM, TAG, "no mem");
|
||||
|
||||
//claim an AF controller
|
||||
ESP_GOTO_ON_ERROR(s_isp_claim_af_controller(isp_proc, af_ctlr), err, TAG, "no available controller");
|
||||
|
||||
af_ctlr->fsm = ISP_FSM_INIT;
|
||||
af_ctlr->spinlock = (portMUX_TYPE)portMUX_INITIALIZER_UNLOCKED;
|
||||
af_ctlr->isp_proc = isp_proc;
|
||||
|
||||
isp_ll_af_enable_auto_update(isp_proc->hal.hw, false);
|
||||
isp_ll_af_enable(isp_proc->hal.hw, false);
|
||||
|
||||
for (int i = 0; i < SOC_ISP_AF_WINDOW_NUMS; i++) {
|
||||
isp_hal_af_window_config(&isp_proc->hal, i, &af_config->window[i]);
|
||||
}
|
||||
|
||||
isp_ll_af_set_edge_thresh_mode(isp_proc->hal.hw, ISP_LL_AF_EDGE_MONITOR_MODE_MANUAL);
|
||||
isp_ll_af_set_edge_thresh(isp_proc->hal.hw, af_config->edge_thresh);
|
||||
isp_ll_clear_intr(isp_proc->hal.hw, ISP_LL_EVENT_AF_MASK);
|
||||
|
||||
*ret_hdl = af_ctlr;
|
||||
|
||||
return ESP_OK;
|
||||
|
||||
err:
|
||||
free(af_ctlr);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t esp_isp_del_af_controller(isp_af_ctrlr_t af_ctlr)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(af_ctlr && af_ctlr->isp_proc, ESP_ERR_INVALID_ARG, TAG, "invalid argument: null pointer");
|
||||
ESP_RETURN_ON_ERROR(s_isp_declaim_af_controller(af_ctlr), TAG, "controller isn't in use");
|
||||
ESP_RETURN_ON_FALSE(af_ctlr->fsm == ISP_FSM_INIT, ESP_ERR_INVALID_STATE, TAG, "controller isn't in init state");
|
||||
|
||||
free(af_ctlr);
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t esp_isp_af_controller_enable(isp_af_ctrlr_t af_ctlr)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(af_ctlr && af_ctlr->isp_proc, ESP_ERR_INVALID_ARG, TAG, "invalid argument: null pointer");
|
||||
ESP_RETURN_ON_FALSE(af_ctlr->fsm == ISP_FSM_INIT, ESP_ERR_INVALID_STATE, TAG, "controller isn't in init state");
|
||||
|
||||
isp_ll_af_clk_enable(af_ctlr->isp_proc->hal.hw, true);
|
||||
isp_ll_af_enable(af_ctlr->isp_proc->hal.hw, true);
|
||||
af_ctlr->fsm = ISP_FSM_ENABLE;
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t esp_isp_af_controller_disable(isp_af_ctrlr_t af_ctlr)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(af_ctlr && af_ctlr->isp_proc, ESP_ERR_INVALID_ARG, TAG, "invalid argument: null pointer");
|
||||
ESP_RETURN_ON_FALSE(af_ctlr->fsm == ISP_FSM_ENABLE, ESP_ERR_INVALID_STATE, TAG, "controller isn't in enable state");
|
||||
|
||||
isp_ll_af_clk_enable(af_ctlr->isp_proc->hal.hw, false);
|
||||
isp_ll_af_enable(af_ctlr->isp_proc->hal.hw, false);
|
||||
af_ctlr->fsm = ISP_FSM_INIT;
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t esp_isp_af_controller_get_oneshot_result(isp_af_ctrlr_t af_ctlr, isp_af_result_t *out_res)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE_ISR(af_ctlr && out_res, ESP_ERR_INVALID_ARG, TAG, "invalid argument: null pointer");
|
||||
ESP_RETURN_ON_FALSE_ISR(af_ctlr->fsm == ISP_FSM_ENABLE, ESP_ERR_INVALID_STATE, TAG, "controller isn't in enable state");
|
||||
|
||||
isp_hal_af_get_oneshot_result(&af_ctlr->isp_proc->hal, out_res);
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
/*---------------------------------------------
|
||||
AF Env Monitor
|
||||
----------------------------------------------*/
|
||||
static esp_err_t s_isp_claim_af_env_detector(isp_af_ctrlr_t af_ctlr, isp_af_env_detector_t *af_env_detector)
|
||||
{
|
||||
assert(af_ctlr && af_env_detector);
|
||||
|
||||
bool found = false;
|
||||
portENTER_CRITICAL(&af_ctlr->spinlock);
|
||||
for (int i = 0; i < SOC_ISP_AF_ENV_DETECTOR_NUMS; i++) {
|
||||
found = !af_ctlr->af_env_detector[i];
|
||||
if (found) {
|
||||
af_ctlr->af_env_detector[i] = af_env_detector;
|
||||
af_env_detector->id = i;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
portEXIT_CRITICAL(&af_ctlr->spinlock);
|
||||
|
||||
if (!found) {
|
||||
return ESP_ERR_NOT_FOUND;
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t s_isp_declaim_af_env_detector(isp_af_env_detector_t *af_env_detector)
|
||||
{
|
||||
assert(af_env_detector && af_env_detector->af_ctlr);
|
||||
|
||||
portENTER_CRITICAL(&af_env_detector->af_ctlr->spinlock);
|
||||
af_env_detector->af_ctlr->af_env_detector[af_env_detector->id] = NULL;
|
||||
portEXIT_CRITICAL(&af_env_detector->af_ctlr->spinlock);
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t esp_isp_new_af_env_detector(isp_af_ctrlr_t af_ctlr, const esp_isp_af_env_config_t *config, isp_af_env_detr_t *ret_hdl)
|
||||
{
|
||||
esp_err_t ret = ESP_FAIL;
|
||||
ESP_RETURN_ON_FALSE(af_ctlr && config && ret_hdl, ESP_ERR_INVALID_ARG, TAG, "invalid argument: null pointer");
|
||||
|
||||
isp_af_env_detector_t *af_env_detector = heap_caps_calloc(1, sizeof(isp_af_env_detector_t), ISP_AF_MEM_ALLOC_CAPS);
|
||||
ESP_RETURN_ON_FALSE(af_env_detector, ESP_ERR_NO_MEM, TAG, "no mem");
|
||||
|
||||
//claim an AF Env detector
|
||||
ESP_GOTO_ON_ERROR(s_isp_claim_af_env_detector(af_ctlr, af_env_detector), err, TAG, "no available env detector");
|
||||
|
||||
af_env_detector->fsm = ISP_FSM_INIT;
|
||||
af_env_detector->config.interval = config->interval;
|
||||
af_env_detector->spinlock = (portMUX_TYPE)portMUX_INITIALIZER_UNLOCKED;
|
||||
af_env_detector->af_ctlr = af_ctlr;
|
||||
|
||||
isp_ll_af_env_monitor_set_period(af_ctlr->isp_proc->hal.hw, 0);
|
||||
isp_ll_clear_intr(af_ctlr->isp_proc->hal.hw, ISP_LL_EVENT_AF_ENV);
|
||||
|
||||
*ret_hdl = af_env_detector;
|
||||
|
||||
return ESP_OK;
|
||||
|
||||
err:
|
||||
free(af_env_detector);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t esp_isp_del_af_env_detector(isp_af_env_detr_t af_env_detector)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(af_env_detector && af_env_detector->af_ctlr, ESP_ERR_INVALID_ARG, TAG, "invalid argument: null pointer");
|
||||
ESP_RETURN_ON_ERROR(s_isp_declaim_af_env_detector(af_env_detector), TAG, "detector isn't in use");
|
||||
ESP_RETURN_ON_FALSE(af_env_detector->fsm == ISP_FSM_INIT, ESP_ERR_INVALID_STATE, TAG, "detector isn't in init state");
|
||||
|
||||
free(af_env_detector);
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t esp_isp_af_env_detector_enable(isp_af_env_detr_t af_env_detector)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(af_env_detector && af_env_detector->af_ctlr, ESP_ERR_INVALID_ARG, TAG, "invalid argument: null pointer");
|
||||
ESP_RETURN_ON_FALSE(af_env_detector->fsm == ISP_FSM_INIT, ESP_ERR_INVALID_STATE, TAG, "detector isn't in init state");
|
||||
|
||||
//try use ratio mode
|
||||
isp_ll_af_env_monitor_set_mode(af_env_detector->af_ctlr->isp_proc->hal.hw, ISP_LL_AF_ENV_MONITOR_MODE_ABS);
|
||||
isp_ll_af_env_monitor_set_period(af_env_detector->af_ctlr->isp_proc->hal.hw, af_env_detector->config.interval);
|
||||
isp_ll_enable_intr(af_env_detector->af_ctlr->isp_proc->hal.hw, ISP_LL_EVENT_AF_ENV, true);
|
||||
af_env_detector->fsm = ISP_FSM_ENABLE;
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t esp_isp_af_env_detector_disable(isp_af_env_detr_t af_env_detector)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(af_env_detector && af_env_detector->af_ctlr, ESP_ERR_INVALID_ARG, TAG, "invalid argument: null pointer");
|
||||
ESP_RETURN_ON_FALSE(af_env_detector->fsm == ISP_FSM_ENABLE, ESP_ERR_INVALID_STATE, TAG, "detector isn't in enable state");
|
||||
|
||||
isp_ll_af_env_monitor_set_period(af_env_detector->af_ctlr->isp_proc->hal.hw, 0);
|
||||
af_env_detector->fsm = ISP_FSM_INIT;
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t esp_isp_af_env_detector_register_event_callbacks(isp_af_env_detr_t af_env_detector, const esp_isp_af_env_detector_evt_cbs_t *cbs, void *user_data)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(af_env_detector && cbs, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
|
||||
ESP_RETURN_ON_FALSE(af_env_detector->fsm == ISP_FSM_INIT, ESP_ERR_INVALID_STATE, TAG, "detector isn't in the init state");
|
||||
|
||||
#if CONFIG_ISP_ISR_IRAM_SAFE
|
||||
if (cbs->on_env_change) {
|
||||
ESP_RETURN_ON_FALSE(esp_ptr_in_iram(cbs->on_env_change), ESP_ERR_INVALID_ARG, TAG, "on_env_change callback not in IRAM");
|
||||
}
|
||||
#endif
|
||||
|
||||
ESP_RETURN_ON_ERROR(esp_isp_register_isr(af_env_detector->af_ctlr->isp_proc, ISP_SUBMODULE_AF), TAG, "fail to register ISR");
|
||||
|
||||
af_env_detector->cbs.on_env_change = cbs->on_env_change;
|
||||
af_env_detector->user_data = user_data;
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t esp_isp_af_env_detector_set_threshold(isp_af_env_detr_t af_env_detector, int definition_thresh, int luminance_thresh)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE_ISR(af_env_detector, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
|
||||
ESP_RETURN_ON_FALSE_ISR(af_env_detector->fsm == ISP_FSM_ENABLE, ESP_ERR_INVALID_STATE, TAG, "detector isn't in enable state");
|
||||
|
||||
isp_ll_af_env_monitor_set_thresh(af_env_detector->af_ctlr->isp_proc->hal.hw, definition_thresh, luminance_thresh);
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
/*---------------------------------------------------------------
|
||||
INTR
|
||||
---------------------------------------------------------------*/
|
||||
static bool IRAM_ATTR s_af_env_isr(isp_af_env_detector_t *detector)
|
||||
{
|
||||
bool need_yield = false;
|
||||
|
||||
esp_isp_af_env_detector_evt_data_t edata = {};
|
||||
if (detector->cbs.on_env_change(detector, &edata, detector->user_data)) {
|
||||
need_yield |= true;
|
||||
}
|
||||
|
||||
return need_yield;
|
||||
}
|
||||
|
||||
bool IRAM_ATTR esp_isp_af_isr(isp_proc_handle_t proc, uint32_t af_events)
|
||||
{
|
||||
/**
|
||||
* HW events are cleared in the ISP ISR dispatcher.
|
||||
* We only deal with HW events
|
||||
*/
|
||||
|
||||
bool need_yield = false;
|
||||
|
||||
if (af_events & ISP_LL_EVENT_AF_ENV) {
|
||||
/**
|
||||
* Now only one detector.
|
||||
* Should decide a detector instance according to the hw event.
|
||||
*/
|
||||
isp_af_env_detector_t *detector = proc->af_ctlr[0]->af_env_detector[0];
|
||||
|
||||
need_yield |= s_af_env_isr(detector);
|
||||
}
|
||||
|
||||
return need_yield;
|
||||
}
|
90
components/esp_driver_isp/src/isp_internal.h
Normal file
90
components/esp_driver_isp/src/isp_internal.h
Normal file
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <esp_types.h>
|
||||
#include "sdkconfig.h"
|
||||
#include "esp_attr.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_check.h"
|
||||
#include "esp_heap_caps.h"
|
||||
#include "esp_intr_alloc.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "hal/isp_hal.h"
|
||||
#include "hal/isp_types.h"
|
||||
#include "soc/isp_periph.h"
|
||||
#include "soc/soc_caps.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef enum {
|
||||
ISP_FSM_INIT,
|
||||
ISP_FSM_ENABLE,
|
||||
} isp_fsm_t;
|
||||
|
||||
/*---------------------------------------------------------------
|
||||
Driver Context
|
||||
---------------------------------------------------------------*/
|
||||
typedef enum {
|
||||
ISP_SUBMODULE_AF,
|
||||
} isp_submodule_t;
|
||||
|
||||
typedef struct isp_af_env_detector_t isp_af_env_detector_t;
|
||||
typedef struct isp_af_controller_t isp_af_controller_t;
|
||||
typedef struct isp_processor_t isp_processor_t;
|
||||
|
||||
struct isp_af_env_detector_t {
|
||||
int id;
|
||||
isp_fsm_t fsm;
|
||||
esp_isp_af_env_config_t config;
|
||||
portMUX_TYPE spinlock;
|
||||
esp_isp_af_env_detector_evt_cbs_t cbs;
|
||||
void *user_data;
|
||||
isp_af_controller_t *af_ctlr;
|
||||
};
|
||||
|
||||
struct isp_af_controller_t {
|
||||
int id;
|
||||
isp_fsm_t fsm;
|
||||
portMUX_TYPE spinlock;
|
||||
isp_processor_t *isp_proc;
|
||||
isp_af_env_detector_t *af_env_detector[SOC_ISP_AF_ENV_DETECTOR_NUMS];
|
||||
};
|
||||
|
||||
struct isp_processor_t {
|
||||
int proc_id;
|
||||
isp_hal_context_t hal;
|
||||
#if SOC_ISP_SHARE_CSI_BRG
|
||||
int csi_brg_id;
|
||||
void *csi_brg_hw;
|
||||
#endif
|
||||
isp_fsm_t isp_fsm;
|
||||
portMUX_TYPE spinlock;
|
||||
intr_handle_t intr_hdl;
|
||||
|
||||
/* sub module contexts */
|
||||
isp_af_controller_t *af_ctlr[SOC_ISP_AF_CTLR_NUMS];
|
||||
|
||||
/* should be accessed within isp_processor_t spinlock */
|
||||
int isr_ref_counts;
|
||||
bool af_isr_added;
|
||||
};
|
||||
|
||||
/*---------------------------------------------------------------
|
||||
INTR
|
||||
---------------------------------------------------------------*/
|
||||
esp_err_t esp_isp_register_isr(isp_proc_handle_t proc, isp_submodule_t submodule);
|
||||
esp_err_t esp_isp_deregister_isr(isp_proc_handle_t proc, isp_submodule_t submodule);
|
||||
bool esp_isp_af_isr(isp_proc_handle_t proc, uint32_t af_events);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
@@ -0,0 +1,5 @@
|
||||
components/esp_driver_isp/test_apps/isp:
|
||||
disable:
|
||||
- if: SOC_ISP_SUPPORTED != 1
|
||||
depends_components:
|
||||
- esp_driver_isp
|
8
components/esp_driver_isp/test_apps/isp/CMakeLists.txt
Normal file
8
components/esp_driver_isp/test_apps/isp/CMakeLists.txt
Normal file
@@ -0,0 +1,8 @@
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
|
||||
list(APPEND EXTRA_COMPONENT_DIRS "$ENV{IDF_PATH}/tools/unit-test-app/components")
|
||||
|
||||
set(COMPONENTS main)
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
project(test_isp)
|
2
components/esp_driver_isp/test_apps/isp/README.md
Normal file
2
components/esp_driver_isp/test_apps/isp/README.md
Normal file
@@ -0,0 +1,2 @@
|
||||
| Supported Targets | ESP32-P4 |
|
||||
| ----------------- | -------- |
|
16
components/esp_driver_isp/test_apps/isp/main/CMakeLists.txt
Normal file
16
components/esp_driver_isp/test_apps/isp/main/CMakeLists.txt
Normal file
@@ -0,0 +1,16 @@
|
||||
set(srcs "test_app_main.c"
|
||||
"test_isp_driver.c")
|
||||
|
||||
if(CONFIG_SOC_ISP_SHARE_CSI_BRG)
|
||||
list(APPEND srcs "test_isp_csi.c")
|
||||
endif()
|
||||
|
||||
set(priv_requires
|
||||
unity
|
||||
esp_driver_isp
|
||||
)
|
||||
|
||||
idf_component_register(SRCS ${srcs}
|
||||
INCLUDE_DIRS "."
|
||||
PRIV_REQUIRES ${priv_requires}
|
||||
WHOLE_ARCHIVE TRUE)
|
40
components/esp_driver_isp/test_apps/isp/main/test_app_main.c
Normal file
40
components/esp_driver_isp/test_apps/isp/main/test_app_main.c
Normal file
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: CC0-1.0
|
||||
*/
|
||||
|
||||
#include "unity.h"
|
||||
#include "unity_test_utils.h"
|
||||
#include "esp_heap_caps.h"
|
||||
#include "sdkconfig.h"
|
||||
|
||||
#define TEST_MEMORY_LEAK_THRESHOLD (400)
|
||||
|
||||
void setUp(void)
|
||||
{
|
||||
unity_utils_record_free_mem();
|
||||
}
|
||||
|
||||
void tearDown(void)
|
||||
{
|
||||
unity_utils_evaluate_leaks_direct(TEST_MEMORY_LEAK_THRESHOLD);
|
||||
}
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
/*
|
||||
____________________ ___________
|
||||
/_ __/ __/ __/_ __/ / _/ __/ _ \
|
||||
/ / / _/_\ \ / / _/ /_\ \/ ___/
|
||||
/_/ /___/___/ /_/ /___/___/_/
|
||||
|
||||
*/
|
||||
|
||||
printf(" ____________________ ___________\n");
|
||||
printf("/_ __/ __/ __/_ __/ / _/ __/ _ \\\n");
|
||||
printf(" / / / _/_\\ \\ / / _/ /_\\ \\/ ___/\n");
|
||||
printf("/_/ /___/___/ /_/ /___/___/_/\n");
|
||||
|
||||
unity_run_menu();
|
||||
}
|
28
components/esp_driver_isp/test_apps/isp/main/test_isp_csi.c
Normal file
28
components/esp_driver_isp/test_apps/isp/main/test_isp_csi.c
Normal file
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include "sdkconfig.h"
|
||||
#include "unity.h"
|
||||
#include "esp_private/mipi_csi_share_hw_ctrl.h"
|
||||
|
||||
TEST_CASE("ISP CSI share usage test", "[isp]")
|
||||
{
|
||||
int csi_brg_id = 0;
|
||||
TEST_ESP_OK(mipi_csi_brg_claim(MIPI_CSI_BRG_USER_CSI, &csi_brg_id));
|
||||
TEST_ESP_OK(mipi_csi_brg_claim(MIPI_CSI_BRG_USER_SHARE, &csi_brg_id));
|
||||
TEST_ESP_OK(mipi_csi_brg_declaim(csi_brg_id));
|
||||
TEST_ESP_OK(mipi_csi_brg_declaim(csi_brg_id));
|
||||
|
||||
TEST_ESP_OK(mipi_csi_brg_claim(MIPI_CSI_BRG_USER_ISP_DVP, &csi_brg_id));
|
||||
TEST_ESP_OK(mipi_csi_brg_claim(MIPI_CSI_BRG_USER_SHARE, &csi_brg_id));
|
||||
TEST_ESP_OK(mipi_csi_brg_declaim(csi_brg_id));
|
||||
TEST_ESP_OK(mipi_csi_brg_declaim(csi_brg_id));
|
||||
|
||||
TEST_ESP_OK(mipi_csi_brg_claim(MIPI_CSI_BRG_USER_ISP_DVP, &csi_brg_id));
|
||||
TEST_ASSERT(mipi_csi_brg_claim(MIPI_CSI_BRG_USER_CSI, &csi_brg_id) == ESP_ERR_NOT_FOUND);
|
||||
TEST_ESP_OK(mipi_csi_brg_declaim(csi_brg_id));
|
||||
TEST_ASSERT(mipi_csi_brg_declaim(csi_brg_id) == ESP_ERR_INVALID_STATE);
|
||||
}
|
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include "sdkconfig.h"
|
||||
#include "unity.h"
|
||||
#include "driver/isp.h"
|
||||
|
||||
#include "esp_rom_sys.h"
|
||||
|
||||
TEST_CASE("ISP processor exhausted allocation", "[isp]")
|
||||
{
|
||||
esp_isp_processor_cfg_t isp_config = {
|
||||
.clk_hz = 80 * 1000 * 1000,
|
||||
.input_data_source = ISP_INPUT_DATA_SOURCE_CSI,
|
||||
.input_data_color_type = ISP_COLOR_RAW8,
|
||||
.output_data_color_type = ISP_COLOR_RGB565,
|
||||
};
|
||||
isp_proc_handle_t isp_proc[SOC_ISP_NUMS + 1] = {};
|
||||
|
||||
for (int i = 0; i < SOC_ISP_NUMS; i++) {
|
||||
TEST_ESP_OK(esp_isp_new_processor(&isp_config, &isp_proc[i]));
|
||||
}
|
||||
|
||||
TEST_ASSERT(esp_isp_new_processor(&isp_config, &isp_proc[SOC_ISP_NUMS]) == ESP_ERR_NOT_FOUND);
|
||||
|
||||
for (int i = 0; i < SOC_ISP_NUMS; i++) {
|
||||
TEST_ESP_OK(esp_isp_del_processor(isp_proc[i]));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("ISP AF controller exhausted allocation", "[isp]")
|
||||
{
|
||||
esp_isp_processor_cfg_t isp_config = {
|
||||
.clk_hz = 80 * 1000 * 1000,
|
||||
.input_data_source = ISP_INPUT_DATA_SOURCE_CSI,
|
||||
.input_data_color_type = ISP_COLOR_RAW8,
|
||||
.output_data_color_type = ISP_COLOR_RGB565,
|
||||
};
|
||||
isp_proc_handle_t isp_proc = NULL;
|
||||
TEST_ESP_OK(esp_isp_new_processor(&isp_config, &isp_proc));
|
||||
|
||||
esp_isp_af_config_t af_config = {
|
||||
.edge_thresh = 128,
|
||||
};
|
||||
isp_af_ctrlr_t af_ctrlr[SOC_ISP_AF_CTLR_NUMS + 1] = {};
|
||||
for (int i = 0; i < SOC_ISP_AF_CTLR_NUMS; i++) {
|
||||
TEST_ESP_OK(esp_isp_new_af_controller(isp_proc, &af_config, &af_ctrlr[i]));
|
||||
}
|
||||
|
||||
TEST_ASSERT(esp_isp_new_af_controller(isp_proc, &af_config, &af_ctrlr[SOC_ISP_AF_CTLR_NUMS]) == ESP_ERR_NOT_FOUND);
|
||||
|
||||
for (int i = 0; i < SOC_ISP_AF_CTLR_NUMS; i++) {
|
||||
TEST_ESP_OK(esp_isp_del_af_controller(af_ctrlr[i]));
|
||||
}
|
||||
TEST_ESP_OK(esp_isp_del_processor(isp_proc));
|
||||
}
|
||||
|
||||
TEST_CASE("ISP AF env detector exhausted allocation", "[isp]")
|
||||
{
|
||||
esp_isp_processor_cfg_t isp_config = {
|
||||
.clk_hz = 80 * 1000 * 1000,
|
||||
.input_data_source = ISP_INPUT_DATA_SOURCE_CSI,
|
||||
.input_data_color_type = ISP_COLOR_RAW8,
|
||||
.output_data_color_type = ISP_COLOR_RGB565,
|
||||
};
|
||||
isp_proc_handle_t isp_proc = NULL;
|
||||
TEST_ESP_OK(esp_isp_new_processor(&isp_config, &isp_proc));
|
||||
|
||||
esp_isp_af_config_t af_config = {
|
||||
.edge_thresh = 128,
|
||||
};
|
||||
isp_af_ctrlr_t af_ctrlr = NULL;
|
||||
TEST_ESP_OK(esp_isp_new_af_controller(isp_proc, &af_config, &af_ctrlr));
|
||||
|
||||
esp_isp_af_env_config_t env_config = {
|
||||
.interval = 10,
|
||||
};
|
||||
isp_af_env_detr_t env_detector[SOC_ISP_AF_ENV_DETECTOR_NUMS + 1] = {};
|
||||
for (int i = 0; i < SOC_ISP_AF_ENV_DETECTOR_NUMS; i++) {
|
||||
TEST_ESP_OK(esp_isp_new_af_env_detector(af_ctrlr, &env_config, &env_detector[i]));
|
||||
}
|
||||
|
||||
TEST_ASSERT(esp_isp_new_af_env_detector(af_ctrlr, &env_config, &env_detector[SOC_ISP_AF_ENV_DETECTOR_NUMS]) == ESP_ERR_NOT_FOUND);
|
||||
|
||||
for (int i = 0; i < SOC_ISP_AF_ENV_DETECTOR_NUMS; i++) {
|
||||
TEST_ESP_OK(esp_isp_del_af_env_detector(env_detector[i]));
|
||||
}
|
||||
TEST_ESP_OK(esp_isp_del_af_controller(af_ctrlr));
|
||||
TEST_ESP_OK(esp_isp_del_processor(isp_proc));
|
||||
}
|
7
components/esp_driver_isp/test_apps/isp/pytest_isp.py
Normal file
7
components/esp_driver_isp/test_apps/isp/pytest_isp.py
Normal file
@@ -0,0 +1,7 @@
|
||||
# SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
# SPDX-License-Identifier: CC0-1.0
|
||||
from pytest_embedded_idf import IdfDut
|
||||
|
||||
|
||||
def test_isp(dut: IdfDut) -> None:
|
||||
dut.run_all_single_board_cases()
|
@@ -0,0 +1,2 @@
|
||||
CONFIG_FREERTOS_HZ=1000
|
||||
CONFIG_ESP_TASK_WDT_EN=n
|
@@ -73,6 +73,7 @@ esp_err_t mipi_csi_brg_declaim(int id)
|
||||
|
||||
s_ctx.ref_cnt[id]--;
|
||||
if (s_ctx.ref_cnt[id] < 0) {
|
||||
s_ctx.ref_cnt[id] = 0;
|
||||
portEXIT_CRITICAL(&s_ctx.spinlock);
|
||||
ESP_LOGE(TAG, "%s called, but s_ctx.ref_cnt[%d] == 0", __func__, id);
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
|
0
components/hal/esp32p4/include/hal/clk_gate_ll.h
Normal file
0
components/hal/esp32p4/include/hal/clk_gate_ll.h
Normal file
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@@ -10,6 +10,7 @@
|
||||
#include "esp_attr.h"
|
||||
#include "hal/misc.h"
|
||||
#include "hal/assert.h"
|
||||
#include "hal/hal_utils.h"
|
||||
#include "hal/isp_types.h"
|
||||
#include "hal/color_types.h"
|
||||
#include "soc/isp_struct.h"
|
||||
@@ -23,6 +24,11 @@ extern "C" {
|
||||
|
||||
#define ISP_LL_GET_HW(num) (((num) == 0) ? (&ISP) : NULL)
|
||||
|
||||
/*---------------------------------------------------------------
|
||||
Clock
|
||||
---------------------------------------------------------------*/
|
||||
#define ISP_LL_TX_MAX_CLK_INT_DIV 0x100
|
||||
|
||||
|
||||
/*---------------------------------------------------------------
|
||||
INTR
|
||||
@@ -151,11 +157,12 @@ static inline void isp_ll_select_clk_source(isp_dev_t *hw, soc_periph_isp_clk_sr
|
||||
* @brief Set ISP clock div
|
||||
*
|
||||
* @param hw Hardware instance address
|
||||
* @param div divider value
|
||||
* @param div Clock division with integral and decimal part
|
||||
*/
|
||||
static inline void isp_ll_set_clock_div(isp_dev_t *hw, uint32_t div)
|
||||
static inline void isp_ll_set_clock_div(isp_dev_t *hw, const hal_utils_clk_div_t *clk_div)
|
||||
{
|
||||
HP_SYS_CLKRST.peri_clk_ctrl26.reg_isp_clk_div_num = div;
|
||||
HAL_ASSERT(clk_div->integer > 0 && clk_div->integer <= ISP_LL_TX_MAX_CLK_INT_DIV);
|
||||
HAL_FORCE_MODIFY_U32_REG_FIELD(HP_SYS_CLKRST.peri_clk_ctrl26, reg_isp_clk_div_num, clk_div->integer - 1);
|
||||
}
|
||||
|
||||
/// use a macro to wrap the function, force the caller to use it in a critical section
|
||||
@@ -518,6 +525,7 @@ static inline void isp_ll_af_set_window_range(isp_dev_t *hw, uint32_t window_id,
|
||||
break;
|
||||
default:
|
||||
HAL_ASSERT(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -540,6 +548,7 @@ static inline uint32_t isp_ll_af_get_window_sum(isp_dev_t *hw, uint32_t window_i
|
||||
return hw->af_sum_c.af_sumc;
|
||||
default:
|
||||
HAL_ASSERT(false);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -562,6 +571,7 @@ static inline uint32_t isp_ll_af_get_window_lum(isp_dev_t *hw, uint32_t window_i
|
||||
return hw->af_lum_c.af_lumc;
|
||||
default:
|
||||
HAL_ASSERT(false);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -67,6 +67,10 @@ if(CONFIG_SOC_SDM_SUPPORTED)
|
||||
list(APPEND srcs "${target_folder}/sdm_periph.c")
|
||||
endif()
|
||||
|
||||
if(CONFIG_SOC_ISP_SUPPORTED)
|
||||
list(APPEND srcs "${target}/isp_periph.c")
|
||||
endif()
|
||||
|
||||
if(CONFIG_SOC_I2S_SUPPORTED)
|
||||
list(APPEND srcs "${target_folder}/i2s_periph.c")
|
||||
endif()
|
||||
|
@@ -127,6 +127,10 @@ config SOC_LEDC_SUPPORTED
|
||||
bool
|
||||
default y
|
||||
|
||||
config SOC_ISP_SUPPORTED
|
||||
bool
|
||||
default y
|
||||
|
||||
config SOC_I2C_SUPPORTED
|
||||
bool
|
||||
default y
|
||||
|
@@ -53,6 +53,7 @@
|
||||
#define SOC_SDM_SUPPORTED 1
|
||||
#define SOC_GPSPI_SUPPORTED 1
|
||||
#define SOC_LEDC_SUPPORTED 1
|
||||
#define SOC_ISP_SUPPORTED 1
|
||||
#define SOC_I2C_SUPPORTED 1
|
||||
#define SOC_SYSTIMER_SUPPORTED 1
|
||||
#define SOC_AES_SUPPORTED 1
|
||||
|
24
components/soc/esp32p4/isp_periph.c
Normal file
24
components/soc/esp32p4/isp_periph.c
Normal file
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include "soc/periph_defs.h"
|
||||
#include "soc/isp_periph.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
const isp_info_t isp_hw_info = {
|
||||
.instances = {
|
||||
[0] = {
|
||||
.irq = ETS_ISP_INTR_SOURCE,
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
29
components/soc/include/soc/isp_periph.h
Normal file
29
components/soc/include/soc/isp_periph.h
Normal file
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include "soc/soc_caps.h"
|
||||
#include "soc/periph_defs.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#if SOC_ISP_SUPPORTED
|
||||
typedef struct {
|
||||
struct {
|
||||
const uint32_t irq;
|
||||
} instances[SOC_ISP_NUMS];
|
||||
} isp_info_t;
|
||||
|
||||
extern const isp_info_t isp_hw_info;
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
@@ -152,6 +152,8 @@ SDM_DOCS = ['api-reference/peripherals/sdm.rst']
|
||||
|
||||
I2S_DOCS = ['api-reference/peripherals/i2s.rst']
|
||||
|
||||
ISP_DOCS = ['api-reference/peripherals/isp.rst']
|
||||
|
||||
RTC_MEM_DOCS = ['api-guides/deep-sleep-stub.rst']
|
||||
|
||||
ADC_DOCS = ['api-reference/peripherals/adc_oneshot.rst',
|
||||
@@ -246,6 +248,7 @@ conditional_include_dict = {'SOC_BT_SUPPORTED':BT_DOCS,
|
||||
'SOC_TEMP_SENSOR_SUPPORTED':TEMP_SENSOR_DOCS,
|
||||
'SOC_TWAI_SUPPORTED':TWAI_DOCS,
|
||||
'SOC_I2S_SUPPORTED':I2S_DOCS,
|
||||
'SOC_ISP_SUPPORTED':ISP_DOCS,
|
||||
'SOC_RTC_MEM_SUPPORTED': RTC_MEM_DOCS,
|
||||
'SOC_ADC_SUPPORTED':ADC_DOCS,
|
||||
'SOC_ADC_DMA_SUPPORTED':ADC_DMA_DOCS,
|
||||
@@ -281,7 +284,7 @@ extensions += ['sphinx_copybutton',
|
||||
'esp_docs.esp_extensions.run_doxygen',
|
||||
]
|
||||
|
||||
# Use wavedrompy as backend, insted of wavedrom-cli
|
||||
# Use wavedrompy as backend, instead of wavedrom-cli
|
||||
render_using_wavedrompy = True
|
||||
|
||||
smartquotes = False
|
||||
|
@@ -15,9 +15,12 @@ INPUT += \
|
||||
$(PROJECT_PATH)/components/esp_driver_cam/include/esp_cam_ctlr.h \
|
||||
$(PROJECT_PATH)/components/esp_driver_cam/include/esp_cam_ctlr_types.h \
|
||||
$(PROJECT_PATH)/components/esp_driver_cam/csi/include/esp_cam_ctlr_csi.h \
|
||||
$(PROJECT_PATH)/components/esp_driver_jpeg/include/driver/jpeg_decode.h \
|
||||
$(PROJECT_PATH)/components/hal/include/hal/jpeg_types.h \
|
||||
$(PROJECT_PATH)/components/esp_driver_jpeg/include/driver/jpeg_types.h \
|
||||
$(PROJECT_PATH)/components/esp_driver_isp/include/driver/isp.h \
|
||||
$(PROJECT_PATH)/components/esp_driver_isp/include/driver/isp_types.h \
|
||||
$(PROJECT_PATH)/components/esp_driver_isp/include/driver/isp_af.h \
|
||||
$(PROJECT_PATH)/components/esp_driver_jpeg/include/driver/jpeg_decode.h \
|
||||
$(PROJECT_PATH)/components/esp_lcd/dsi/include/esp_lcd_mipi_dsi.h \
|
||||
$(PROJECT_PATH)/components/soc/$(IDF_TARGET)/include/soc/adc_channel.h \
|
||||
$(PROJECT_PATH)/components/soc/$(IDF_TARGET)/include/soc/clk_tree_defs.h \
|
||||
|
@@ -21,10 +21,10 @@ Functional Overview
|
||||
- `Resource Allocation <#cam-resource-allocation>`__ - covers how to allocate camera controller instances with properly set of configurations. It also covers how to recycle the resources when they are no longer needed.
|
||||
- `Enable and disable a camera controller <#cam-enable-disable>`__ - covers how to enable and disable a camera controller.
|
||||
- `Start and stop a camera controller <#cam-start-stop>`__ - covers how to start and stop a camera controller.
|
||||
- `Receive from a camera sensor or something else <#cam-receive>`__ - convers how to receive camera signal from a sensor or something else.
|
||||
- `Receive from a camera sensor or something else <#cam-receive>`__ - covers how to receive camera signal from a sensor or something else.
|
||||
- `Register callback <#cam-callback>`__ - covers how to hook user specific code to camera controller driver event callback function.
|
||||
- `Thread Safety <#thread-safety>`__ - lists which APIs are guaranteed to be thread safe by the driver.
|
||||
- `Kconfig Options <#kconfig-options>`__ - lists the supported Kconfig options that can bring different effects to the driver.
|
||||
- `Thread Safety <#cam-thread-safety>`__ - lists which APIs are guaranteed to be thread safe by the driver.
|
||||
- `Kconfig Options <#cam-kconfig-options>`__ - lists the supported Kconfig options that can bring different effects to the driver.
|
||||
- `IRAM SAFE <#cam-iram-safe>`__ - describes tips on how to make the CSI interrupt and control functions work better along with a disabled cache.
|
||||
|
||||
.. _cam-resource-allocation:
|
||||
@@ -122,14 +122,14 @@ After the Camera Controller Driver starts receiving, it can generate a specific
|
||||
|
||||
- :cpp:member:`esp_cam_ctlr_evt_cbs_t::on_trans_finished` sets a callback function when the Camera Controller Driver finishes a transaction. As this function is called within the ISR context, you must ensure that the function does not attempt to block (e.g., by making sure that only FreeRTOS APIs with ``ISR`` suffix are called from within the function).
|
||||
|
||||
.. _thread-safety:
|
||||
.. _cam-thread-safety:
|
||||
|
||||
Thread Safety
|
||||
^^^^^^^^^^^^^
|
||||
|
||||
The factory function :cpp:func:`esp_cam_new_csi_ctlr` and :cpp:func:`esp_cam_del_ctlr` are guaranteed to be thread safe by the driver, which means, user can call them from different RTOS tasks without protection by extra locks.
|
||||
|
||||
.. _kconfig-options:
|
||||
.. _cam-kconfig-options:
|
||||
|
||||
Kconfig Options
|
||||
^^^^^^^^^^^^^^^
|
||||
|
@@ -21,6 +21,7 @@ Peripherals API
|
||||
:SOC_DIG_SIGN_SUPPORTED: ds
|
||||
i2c
|
||||
:SOC_I2S_SUPPORTED: i2s
|
||||
:SOC_ISP_SUPPORTED: isp
|
||||
lcd/index
|
||||
:SOC_GP_LDO_SUPPORTED: ldo_regulator
|
||||
ledc
|
||||
|
218
docs/en/api-reference/peripherals/isp.rst
Normal file
218
docs/en/api-reference/peripherals/isp.rst
Normal file
@@ -0,0 +1,218 @@
|
||||
Image Signal Processor
|
||||
======================
|
||||
|
||||
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. ISP shall work with other modules to read and write data, it can not work alone.
|
||||
|
||||
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
|
||||
|
||||
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 oneshot result <#isp-af-get-oneshot-result>`__ - covers how to get AF oneshot result.
|
||||
- `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.
|
||||
|
||||
.. _isp-resource-allocation:
|
||||
|
||||
Resource Allocation
|
||||
^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Install Image Signal Processor (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.
|
||||
|
||||
.. code:: c
|
||||
|
||||
esp_isp_processor_cfg_t isp_config = {
|
||||
.clk_src = ISP_CLK_SRC_DEFAULT,
|
||||
.clk_hz = 80 * 1000 * 1000,
|
||||
.input_data_source = ISP_INPUT_DATA_SOURCE_CSI,
|
||||
.input_data_color_type = ISP_COLOR_RAW8,
|
||||
.output_data_color_type = ISP_COLOR_RGB565,
|
||||
};
|
||||
|
||||
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.
|
||||
|
||||
|
||||
Install Image Signal Processor (ISP) Auto-Focus (AF) Driver
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
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.
|
||||
|
||||
.. code:: c
|
||||
|
||||
esp_isp_af_config_t af_config = {
|
||||
.edge_thresh = 128,
|
||||
};
|
||||
isp_af_ctrlr_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.
|
||||
|
||||
|
||||
Install Image Signal Processor (ISP) Auto-Focus (AF) Environment Detector Driver
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
ISP auto-focus (AF) driver requires the configuration that specified by :cpp:type:`esp_isp_af_env_config_t`.
|
||||
|
||||
If the configurations in :cpp:type:`esp_isp_af_env_config_t` is specified, users can call :cpp:func:`esp_isp_new_af_env_detector` to allocate and initialize an ISP AF processor. This function will return an ISP AF environment detector handle if it runs correctly. You can take following code as reference.
|
||||
|
||||
.. code:: c
|
||||
|
||||
esp_isp_af_env_config_t env_config = {
|
||||
.interval = 10,
|
||||
};
|
||||
isp_af_env_detr_t env_detector = NULL;
|
||||
ESP_ERROR_CHECK(esp_isp_new_af_env_detector(af_ctrlr, &env_config, &env_detector[i]));
|
||||
|
||||
Uninstall Image Signal Processor (ISP) Driver
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
If a previously installed ISP processor is no longer needed, it's recommended to recycle the resource by calling :cpp:func:`esp_isp_del_processor`, so that to release the underlying hardware.
|
||||
|
||||
UnInstall Image Signal Processor (ISP) Auto-Focus (AF) Driver
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
If a previously installed ISP AF processor is no longer needed, it's recommended to recycle the resource by calling :cpp:func:`esp_isp_del_af_controller`, so that to release the underlying hardware.
|
||||
|
||||
Uninstall Image Signal Processor (ISP) Environment Decetor Driver
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
If a previously installed ISP AF processor is no longer needed, it's recommended to recycle the resource by calling :cpp:func:`esp_isp_del_af_env_detector`, so that to release the underlying hardware.
|
||||
|
||||
|
||||
.. _isp-enable-disable:
|
||||
|
||||
Enable and Disable Image Signal Processor (ISP)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Image Signal Processor (ISP)
|
||||
----------------------------
|
||||
|
||||
Before doing ISP pipeline, you need to enable the ISP processor first, by calling :cpp:func:`esp_isp_enable`. This function:
|
||||
|
||||
* Switches the driver state from **init** to **enable**.
|
||||
|
||||
Calling :cpp:func:`esp_isp_disable` does the opposite, that is, put the driver back to the **init** state.
|
||||
|
||||
Image Signal Processor (ISP) Auto-Focus (AF) Processor
|
||||
------------------------------------------------------
|
||||
|
||||
Before doing ISP AF, you need to enable the ISP AF processor first, by calling :cpp:func:`esp_isp_af_controller_enable`. This function:
|
||||
|
||||
* Switches the driver state from **init** to **enable**.
|
||||
|
||||
Calling :cpp:func:`esp_isp_af_controller_disable` does the opposite, that is, put the driver back to the **init** state.
|
||||
|
||||
Image Signal Processor (ISP) Auto-Focus (AF) Environment Detector
|
||||
-----------------------------------------------------------------
|
||||
|
||||
Before starting ISP environment detector, you need to enable the ISP AF environment detector first, by calling :cpp:func:`esp_isp_af_env_detector_enable`. This function:
|
||||
|
||||
* Switches the driver state from **init** to **enable**.
|
||||
|
||||
Calling :cpp:func:`esp_isp_af_env_detector_disable` does the opposite, that is, put the driver back to the **init** state.
|
||||
|
||||
.. _isp-af-get-oneshot-result:
|
||||
|
||||
Get Auto-Focus (AF) Oneshot Result
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Calling :cpp:func:`esp_isp_af_controller_get_oneshot_result` to get oneshot AF result. You can take following code as reference.
|
||||
|
||||
.. code:: c
|
||||
|
||||
esp_isp_af_config_t af_config = {
|
||||
.edge_thresh = 128,
|
||||
};
|
||||
isp_af_ctrlr_t af_ctrlr = NULL;
|
||||
ESP_ERROR_CHECK(esp_isp_new_af_controller(isp_proc, &af_config, &af_ctrlr));
|
||||
isp_af_result_t result = {};
|
||||
ESP_ERROR_CHECK(esp_isp_af_controller_get_oneshot_result(ctx->af_ctlr, &result));
|
||||
|
||||
Set Auto-Focus (AF) Environment Detector Threshold
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Calling :cpp:func:`esp_isp_af_env_detector_set_threshold` to set the threshold of an ISP AF environment detector.
|
||||
|
||||
.. code:: 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-callback:
|
||||
|
||||
Register Event Callbacks
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Register Image Signal Processor (ISP) Auto-Focus (AF) Environment Detector Event Callbacks
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
After the ISP AF 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_af_env_detector_register_event_callbacks`. All supported event callbacks are listed in :cpp:type:`esp_isp_af_env_detector_evt_cbs_t`:
|
||||
|
||||
- :cpp:member:`esp_isp_af_env_detector_evt_cbs_t::on_env_change` sets a callback function for environment change. As this function is called within the ISR context, you must ensure that the function does not attempt to block (e.g., by making sure that only FreeRTOS APIs with ``ISR`` suffix are called from within the function). The function prototype is declared in :cpp:type:`esp_isp_af_env_detector_callback_t`.
|
||||
|
||||
You can save your own context to :cpp:func:`esp_isp_af_env_detector_register_event_callbacks` as well, via the parameter ``user_data``. The user data will be directly passed to the callback function.
|
||||
|
||||
.. _isp-thread-safety:
|
||||
|
||||
Thread Safety
|
||||
^^^^^^^^^^^^^
|
||||
|
||||
The factory function :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_af_env_detector`, and :cpp:func:`esp_isp_del_af_env_detector` are guaranteed to be thread safe by the driver, which means, user can call them from different RTOS tasks without protection by extra locks.
|
||||
|
||||
.. _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
|
||||
|
||||
.. _isp-iram-safe:
|
||||
|
||||
IRAM Safe
|
||||
^^^^^^^^^
|
||||
|
||||
By default, the ISP interrupt will be deferred when the cache is disabled because of writing or erasing the flash.
|
||||
|
||||
There is a Kconfig option :ref:`CONFIG_ISP_ISR_IRAM_SAFE` that:
|
||||
|
||||
- Enables the interrupt being serviced even when the cache is disabled
|
||||
- Places all functions that used by the ISR into IRAM
|
||||
- Places driver object into DRAM (in case it is mapped to PSRAM by accident)
|
||||
|
||||
This allows the interrupt to run while the cache is disabled, but comes at the cost of increased IRAM consumption.
|
||||
|
||||
API Reference
|
||||
-------------
|
||||
|
||||
.. include-build-file:: inc/isp.inc
|
||||
.. include-build-file:: inc/isp_types.inc
|
||||
.. include-build-file:: inc/isp_af.inc
|
@@ -21,6 +21,7 @@
|
||||
:SOC_DIG_SIGN_SUPPORTED: ds
|
||||
i2c
|
||||
:SOC_I2S_SUPPORTED: i2s
|
||||
:SOC_ISP_SUPPORTED: isp
|
||||
lcd/index
|
||||
:SOC_GP_LDO_SUPPORTED: ldo_regulator
|
||||
ledc
|
||||
|
1
docs/zh_CN/api-reference/peripherals/isp.rst
Normal file
1
docs/zh_CN/api-reference/peripherals/isp.rst
Normal file
@@ -0,0 +1 @@
|
||||
.. include:: ../../../en/api-reference/peripherals/isp.rst
|
@@ -112,6 +112,14 @@ examples/peripherals/i2s/i2s_recorder:
|
||||
- esp_driver_spi
|
||||
- esp_driver_i2s
|
||||
|
||||
examples/peripherals/isp/auto_focus:
|
||||
disable:
|
||||
- if: INCLUDE_DEFAULT == 1
|
||||
temporary: true
|
||||
reason: disable build temporarily # TODO: IDF-8895
|
||||
depends_components:
|
||||
- esp_driver_isp
|
||||
|
||||
examples/peripherals/jpeg/jpeg_decode:
|
||||
disable:
|
||||
- if: SOC_JPEG_CODEC_SUPPORTED != 1
|
||||
|
@@ -0,0 +1,10 @@
|
||||
set(srcs)
|
||||
|
||||
list(APPEND srcs "src/isp_af_scheme.c"
|
||||
"src/isp_af_scheme_sa.c")
|
||||
|
||||
set(priv_requires "esp_driver_isp")
|
||||
|
||||
idf_component_register(SRCS ${srcs}
|
||||
INCLUDE_DIRS "include" "interface"
|
||||
PRIV_REQUIRES ${priv_requires})
|
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2020-2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "sdkconfig.h"
|
||||
#include "esp_err.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Type of ISP AF scheme handle
|
||||
*/
|
||||
typedef struct isp_af_scheme_t *isp_af_scheme_handle_t;
|
||||
|
||||
/**
|
||||
* @brief ISP AF process, which is used to calculate definition threshold and luminance threshold for AF environment detector to detect environment change
|
||||
*
|
||||
* @param[in] scheme AF scheme handle
|
||||
* @param[out] out_definition_thresh Calculated definition threshold
|
||||
* @param[out] out_luminance_thresh Calculated luminance threshold
|
||||
*
|
||||
* @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 isp_af_process(isp_af_scheme_handle_t scheme, int *out_definition_thresh, int *out_luminance_thresh);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2020-2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "sdkconfig.h"
|
||||
#include "isp_af_scheme.h"
|
||||
#include "driver/isp_af.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*---------------------------------------------------------------
|
||||
ISP AF Step Approximation Scheme (SA Scheme)
|
||||
---------------------------------------------------------------*/
|
||||
typedef struct {
|
||||
int first_step_val; ///< Step of the camera sensor focus value for first stage approximation
|
||||
int first_approx_cycles; ///< First stage approximation cycles
|
||||
int second_step_val; ///< Step of the camera sensor focus value for second stage approximation
|
||||
int second_approx_cycles; ///< Second stage approximation cycles
|
||||
} isp_af_sa_scheme_config_t;
|
||||
|
||||
typedef struct {
|
||||
int focus_val_max; ///< Max camera sensor focus value
|
||||
} isp_af_sa_scheme_sensor_info_t;
|
||||
|
||||
typedef struct {
|
||||
/**
|
||||
* @brief Sensor driver API to set sensor focus value
|
||||
*
|
||||
* @param[in] focus_val Camera sensor focus value
|
||||
*/
|
||||
esp_err_t (*af_sensor_set_focus)(int focus_val);
|
||||
|
||||
} isp_af_sa_scheme_sensor_drv_t;
|
||||
|
||||
/**
|
||||
* @brief Create an AF step approximation scheme
|
||||
*
|
||||
* @param[in] af_ctrlr AF controller handle
|
||||
* @param[in] config AF SA scheme configurations, see `isp_af_sa_scheme_config_t`
|
||||
* @param[out] ret_scheme AF scheme handle
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK On success
|
||||
* - ESP_ERR_INVALID_ARG If the combination of arguments is invalid
|
||||
* - ESP_ERR_INVALID_STATE Invalid state
|
||||
* - ESP_ERR_NO_MEM If out of memory
|
||||
*/
|
||||
esp_err_t isp_af_create_sa_scheme(isp_af_ctrlr_t af_ctrlr, const isp_af_sa_scheme_config_t *config, isp_af_scheme_handle_t *ret_scheme);
|
||||
|
||||
/**
|
||||
* @brief Delete an AF step approximation scheme
|
||||
*
|
||||
* @param[in] scheme AF scheme handle
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK On success
|
||||
* - ESP_ERR_INVALID_ARG If the combination of arguments is invalid
|
||||
* - ESP_ERR_INVALID_STATE Invalid state
|
||||
* - ESP_ERR_NO_MEM If out of memory
|
||||
*/
|
||||
esp_err_t isp_af_delete_sa_scheme(isp_af_scheme_handle_t scheme);
|
||||
|
||||
/**
|
||||
* @brief Register camera sensor driver to the SA scheme
|
||||
*
|
||||
* @param[in] scheme AF scheme handle
|
||||
* @param[in] sensor_drv Sensor driver, see `isp_af_sa_scheme_sensor_drv_t`
|
||||
* @param[in] info Sensor info
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK On success
|
||||
* - ESP_ERR_INVALID_ARG If the combination of arguments is invalid
|
||||
* - ESP_ERR_INVALID_STATE Invalid state
|
||||
*/
|
||||
esp_err_t isp_af_sa_scheme_register_sensor_driver(isp_af_scheme_handle_t scheme, const isp_af_sa_scheme_sensor_drv_t *sensor_drv, const isp_af_sa_scheme_sensor_info_t *info);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include "esp_err.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct isp_af_scheme_t isp_af_scheme_t;
|
||||
|
||||
/**
|
||||
* @brief ISP AF Scheme Interface
|
||||
*/
|
||||
struct isp_af_scheme_t {
|
||||
/**
|
||||
* @brief Do AF
|
||||
*
|
||||
* @param[in] arg ISP AF scheme specific context
|
||||
* @param[out] definition_thresh Definition thresh that is updated according to the current definition, this can be used to set to the ISP AF Env detector
|
||||
* @param[out] luminance_thresh Luminance thresh that is updated according to the current luminance, this can be used to set to the ISP AF Env detector
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
esp_err_t (*af_process)(void *arg, int *out_definition_thresh, int *out_luminance_thresh);
|
||||
|
||||
/**
|
||||
* @brief ISP AF scheme specific contexts
|
||||
* Can be customized to difference AF schemes
|
||||
*/
|
||||
void *ctx;
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <sys/cdefs.h>
|
||||
#include "esp_types.h"
|
||||
#include "sdkconfig.h"
|
||||
#include "esp_err.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_check.h"
|
||||
#include "esp_heap_caps.h"
|
||||
#include "isp_af_scheme.h"
|
||||
#include "isp_af_scheme_interface.h"
|
||||
|
||||
const static char *TAG = "AF_SCHEME";
|
||||
|
||||
esp_err_t isp_af_process(isp_af_scheme_handle_t scheme, int *out_definition_thresh, int *out_luminance_thresh)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(scheme, ESP_ERR_INVALID_ARG, TAG, "invalid argument: null pointer");
|
||||
ESP_RETURN_ON_FALSE(scheme->ctx, ESP_ERR_INVALID_STATE, TAG, "no scheme registered, create a scheme first");
|
||||
|
||||
return scheme->af_process(scheme->ctx, out_definition_thresh, out_luminance_thresh);
|
||||
}
|
@@ -0,0 +1,208 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <esp_types.h>
|
||||
#include <string.h>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "esp_err.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_check.h"
|
||||
#include "esp_heap_caps.h"
|
||||
#include "driver/isp_af.h"
|
||||
#include "isp_af_scheme_sa.h"
|
||||
#include "isp_af_scheme_interface.h"
|
||||
|
||||
#define ISP_AF_SCHEME_SA_DEFAULT_WINDOW_NUMS 3
|
||||
#define ISP_AF_SCHEME_SA_ENV_THRESH_SEARCH_NUMS 30
|
||||
|
||||
static const char *TAG = "AF_SCHEME";
|
||||
|
||||
typedef struct {
|
||||
isp_af_ctrlr_t af_ctlr;
|
||||
int first_step_val;
|
||||
int first_approx_cycles;
|
||||
int second_step_val;
|
||||
int second_approx_cycles;
|
||||
|
||||
isp_af_sa_scheme_sensor_info_t sensor_info;
|
||||
isp_af_sa_scheme_sensor_drv_t sensor_drv;
|
||||
} af_scheme_context_t;
|
||||
|
||||
/* ------------------------ Interface Functions --------------------------- */
|
||||
static esp_err_t s_af_process(void *arg, int *out_definition_thresh, int *out_luminance_thresh);
|
||||
|
||||
/* ------------------------- Public API ------------------------------------- */
|
||||
esp_err_t isp_af_create_sa_scheme(isp_af_ctrlr_t af_ctlr, const isp_af_sa_scheme_config_t *config, isp_af_scheme_handle_t *ret_scheme)
|
||||
{
|
||||
esp_err_t ret = ESP_FAIL;
|
||||
ESP_RETURN_ON_FALSE(af_ctlr && config && ret_scheme, ESP_ERR_INVALID_ARG, TAG, "invalid arg: null pointer");
|
||||
|
||||
isp_af_scheme_t *scheme = (isp_af_scheme_t *)heap_caps_calloc(1, sizeof(isp_af_scheme_t), MALLOC_CAP_DEFAULT);
|
||||
ESP_RETURN_ON_FALSE(scheme, ESP_ERR_NO_MEM, TAG, "no mem for scheme");
|
||||
|
||||
af_scheme_context_t *ctx = (af_scheme_context_t *)heap_caps_calloc(1, sizeof(af_scheme_context_t), MALLOC_CAP_DEFAULT);
|
||||
ESP_GOTO_ON_FALSE(ctx, ESP_ERR_NO_MEM, err, TAG, "no mem scheme context");
|
||||
|
||||
scheme->af_process = s_af_process;
|
||||
scheme->ctx = ctx;
|
||||
|
||||
ctx->af_ctlr = af_ctlr;
|
||||
ctx->first_step_val = config->first_step_val;
|
||||
ctx->first_approx_cycles = config->first_approx_cycles;
|
||||
ctx->second_step_val = config->second_step_val;
|
||||
ctx->second_approx_cycles = config->second_approx_cycles;
|
||||
|
||||
*ret_scheme = scheme;
|
||||
|
||||
return ESP_OK;
|
||||
|
||||
err:
|
||||
free(scheme);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t isp_af_delete_sa_scheme(isp_af_scheme_handle_t scheme)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(scheme, ESP_ERR_INVALID_ARG, TAG, "invalid argument: null pointer");
|
||||
|
||||
free(scheme->ctx);
|
||||
scheme->ctx = NULL;
|
||||
|
||||
free(scheme);
|
||||
scheme = NULL;
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t isp_af_sa_scheme_register_sensor_driver(isp_af_scheme_handle_t scheme, const isp_af_sa_scheme_sensor_drv_t *sensor_drv, const isp_af_sa_scheme_sensor_info_t *info)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(scheme, ESP_ERR_INVALID_ARG, TAG, "invalid arg: null pointer");
|
||||
ESP_RETURN_ON_FALSE(scheme->ctx, ESP_ERR_INVALID_STATE, TAG, "no scheme created yet");
|
||||
|
||||
af_scheme_context_t *ctx = scheme->ctx;
|
||||
ctx->sensor_drv.af_sensor_set_focus = sensor_drv->af_sensor_set_focus;
|
||||
ctx->sensor_info.focus_val_max = info->focus_val_max;
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
/* ------------------------ Interface Functions --------------------------- */
|
||||
static esp_err_t s_af_process(void *arg, int *out_definition_thresh, int *out_luminance_thresh)
|
||||
{
|
||||
//arg pointer is checked in the upper layer
|
||||
|
||||
af_scheme_context_t *ctx = arg;
|
||||
ESP_RETURN_ON_FALSE(ctx->af_ctlr, ESP_ERR_INVALID_STATE, TAG, "no AF controller registered");
|
||||
ESP_RETURN_ON_FALSE(ctx->sensor_drv.af_sensor_set_focus, ESP_ERR_INVALID_STATE, TAG, "no sensor driver function `af_sensor_set_focus` registered");
|
||||
|
||||
int af_sum = 0;
|
||||
int af_lum = 0;
|
||||
int af_sum_max = 0;
|
||||
|
||||
int af_current_base = 0;
|
||||
int af_current = 0;
|
||||
int af_current_best = 0;
|
||||
|
||||
int af_sum_env_th = 0;
|
||||
int af_lum_env_th = 0;
|
||||
int af_sum_tmp[ISP_AF_SCHEME_SA_ENV_THRESH_SEARCH_NUMS] = {0};
|
||||
int af_lum_tmp[ISP_AF_SCHEME_SA_ENV_THRESH_SEARCH_NUMS] = {0};
|
||||
|
||||
int ref_x = ISP_AF_SCHEME_SA_ENV_THRESH_SEARCH_NUMS;
|
||||
int ref_x_fallback = ISP_AF_SCHEME_SA_ENV_THRESH_SEARCH_NUMS - 1;
|
||||
|
||||
isp_af_result_t result = {};
|
||||
|
||||
ESP_RETURN_ON_ERROR(ctx->sensor_drv.af_sensor_set_focus(0), TAG, "sensor set focus val fail");
|
||||
|
||||
ESP_LOGV(TAG, "//----------- af start ----------//");
|
||||
|
||||
// first search
|
||||
ESP_LOGV(TAG, "//----------- first search ----------//");
|
||||
af_sum_max = 0;
|
||||
af_current_base = 0;
|
||||
|
||||
for (int x = 0; x <= ctx->first_approx_cycles; x++) {
|
||||
af_current = af_current_base + x * ctx->first_step_val;
|
||||
ESP_RETURN_ON_ERROR(ctx->sensor_drv.af_sensor_set_focus(af_current), TAG, "sensor set focus val fail");
|
||||
ESP_RETURN_ON_ERROR(esp_isp_af_controller_get_oneshot_result(ctx->af_ctlr, &result), TAG, "get AF result fail");
|
||||
af_sum = result.definition[0] + result.definition[1] + result.definition[2];
|
||||
if (af_sum > af_sum_max) {
|
||||
af_sum_max = af_sum;
|
||||
af_current_best = af_current;
|
||||
}
|
||||
|
||||
ESP_LOGV(TAG, "af_sum: %"PRId32", af_current: %"PRId32".%"PRId32, af_sum, (int)af_current, (int)((int)(af_current * 1000) % 1000));
|
||||
}
|
||||
|
||||
// second search
|
||||
ESP_LOGV(TAG, "//----------- second search ----------//");
|
||||
af_sum_max = 0;
|
||||
af_current_base = af_current_best + 10;
|
||||
|
||||
if (af_current_base > ctx->sensor_info.focus_val_max) {
|
||||
af_current_base = ctx->sensor_info.focus_val_max;
|
||||
}
|
||||
|
||||
for (int x = 0; x <= ctx->second_approx_cycles; x++) {
|
||||
af_current = af_current_base - x * ctx->second_step_val;
|
||||
if (af_current < 0) {
|
||||
af_current = 0;
|
||||
}
|
||||
|
||||
ESP_RETURN_ON_ERROR(ctx->sensor_drv.af_sensor_set_focus(af_current), TAG, "sensor set focus val fail");
|
||||
ESP_RETURN_ON_ERROR(esp_isp_af_controller_get_oneshot_result(ctx->af_ctlr, &result), TAG, "get AF result fail");
|
||||
af_sum = result.definition[0] + result.definition[1] + result.definition[2];
|
||||
if (af_sum > af_sum_max) {
|
||||
af_sum_max = af_sum;
|
||||
af_current_best = af_current;
|
||||
}
|
||||
ESP_LOGV(TAG, "af_sum: %d, af_current: %d.%d", af_sum, (int)af_current, (int)((int)(af_current * 1000) % 1000));
|
||||
}
|
||||
|
||||
// af done
|
||||
ESP_LOGV(TAG, "//----------- af done ----------//");
|
||||
ESP_LOGV(TAG, "af_sum_max: %d, af_current_best: %d.%d", af_sum_max, (int)af_current_best, (int)((int)(af_current_best * 1000) % 1000));
|
||||
ESP_RETURN_ON_ERROR(ctx->sensor_drv.af_sensor_set_focus(af_current_best), TAG, "sensor set focus val fail");
|
||||
|
||||
// update env threshold
|
||||
ESP_LOGV(TAG, "//------- update env threshold -------//");
|
||||
|
||||
bool use_fallback_th = true;
|
||||
for (int x = 0; x < ref_x; x++) {
|
||||
ESP_RETURN_ON_ERROR(esp_isp_af_controller_get_oneshot_result(ctx->af_ctlr, &result), TAG, "get AF result fail");
|
||||
af_sum_tmp[x] = result.definition[0] + result.definition[1] + result.definition[2];
|
||||
af_lum_tmp[x] = result.luminance[0] + result.luminance[1] + result.luminance[2];
|
||||
|
||||
if ((x >= 1) && (abs(af_sum_tmp[x] - af_sum_max) < af_sum_max * 0.3) && (abs(af_sum_tmp[x - 1] - af_sum_max) < af_sum_max * 0.3)) {
|
||||
ref_x = x;
|
||||
use_fallback_th = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (use_fallback_th) {
|
||||
ref_x = ref_x_fallback;
|
||||
}
|
||||
|
||||
af_sum = af_sum_tmp[ref_x];
|
||||
af_lum = af_lum_tmp[ref_x];
|
||||
af_sum_env_th = af_sum * 0.5;
|
||||
af_lum_env_th = af_lum * 0.05;
|
||||
*out_definition_thresh = af_sum_env_th;
|
||||
*out_luminance_thresh = af_lum_env_th;
|
||||
|
||||
for (int x = 0; x < ref_x; x++) {
|
||||
ESP_LOGV(TAG, "af_sum[%d]: %d, af_lum[%d]: %d", x, af_sum_tmp[x], x, af_lum_tmp[x]);
|
||||
}
|
||||
ESP_LOGV(TAG, "//------- update af env threshold done -------//");
|
||||
ESP_LOGV(TAG, "af_sum: %d, af_sum_env_th: %d", af_sum, af_sum_env_th);
|
||||
ESP_LOGV(TAG, "af_lum: %d, af_lum_env_th: %d", af_lum, af_lum_env_th);
|
||||
ESP_LOGV(TAG, "//----------- af update done ----------//\n\n");
|
||||
|
||||
return ESP_OK;
|
||||
}
|
Reference in New Issue
Block a user