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:
Armando (Dou Yiwen)
2024-04-01 10:44:48 +08:00
39 changed files with 2010 additions and 11 deletions

View 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}
)

View 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

View 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

View 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

View 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

View 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;
}

View 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;
}

View 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

View File

@@ -0,0 +1,5 @@
components/esp_driver_isp/test_apps/isp:
disable:
- if: SOC_ISP_SUPPORTED != 1
depends_components:
- esp_driver_isp

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

View File

@@ -0,0 +1,2 @@
| Supported Targets | ESP32-P4 |
| ----------------- | -------- |

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

View 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();
}

View 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);
}

View File

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

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

View File

@@ -0,0 +1,2 @@
CONFIG_FREERTOS_HZ=1000
CONFIG_ESP_TASK_WDT_EN=n

View File

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

View 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;
}
}

View File

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

View File

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

View File

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

View 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

View 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

View File

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

View File

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

View File

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

View File

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

View 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

View File

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

View File

@@ -0,0 +1 @@
.. include:: ../../../en/api-reference/peripherals/isp.rst

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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