feat(isp): added isp driver framework and isp af driver

pull/13550/head
Armando 2024-01-16 17:14:28 +08:00
rodzic 1ed64afddd
commit bf1275d700
26 zmienionych plików z 1627 dodań i 4 usunięć

Wyświetl plik

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

Wyświetl plik

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

Wyświetl plik

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

Wyświetl plik

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

Wyświetl plik

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

Wyświetl plik

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

Wyświetl plik

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

Wyświetl plik

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

Wyświetl plik

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

Wyświetl plik

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

Wyświetl plik

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

Wyświetl plik

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

Wyświetl plik

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

Wyświetl plik

@ -0,0 +1,94 @@
/*
* 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_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]));
esp_rom_printf("first alloc ok\n");
}
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_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_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));
}

Wyświetl plik

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

Wyświetl plik

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

Wyświetl plik

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

Wyświetl plik

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

Wyświetl plik

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

Wyświetl plik

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

Wyświetl plik

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

Wyświetl plik

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

Wyświetl plik

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

Wyświetl plik

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

Wyświetl plik

@ -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: %d, af_current: %d.%d", 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;
}