Merge branch 'feature/support_isp_ae' into 'master'

feat(isp): Support ISP Auto Exposure (AE)

Closes IDF-9592, IDF-10193, and IDF-10580

See merge request espressif/esp-idf!31671
This commit is contained in:
Gao Xu
2024-08-01 16:21:34 +08:00
21 changed files with 1208 additions and 128 deletions

View File

@@ -12,7 +12,8 @@ if(CONFIG_SOC_ISP_SUPPORTED)
list(APPEND srcs "src/isp_core.c"
"src/isp_af.c"
"src/isp_ccm.c"
"src/isp_awb.c")
"src/isp_awb.c"
"src/isp_ae.c")
endif()
if(CONFIG_SOC_ISP_BF_SUPPORTED)

View File

@@ -16,3 +16,4 @@
#include "driver/isp_awb.h"
#include "driver/isp_bf.h"
#include "driver/isp_ccm.h"
#include "driver/isp_ae.h"

View File

@@ -0,0 +1,221 @@
/*
* 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"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief AE controller config
*/
typedef struct {
isp_ae_sample_point_t sample_point; ///< The input data source, ISP_AE_SAMPLE_POINT_AFTER_DEMOSAIC: AE input data after demosaic, ISP_AE_SAMPLE_POINT_AFTER_GAMMA: AE input data after gamma
isp_window_t window; ///< The sampling windows of AE
int intr_priority; ///< The interrupt priority, range 0~3, if set to 0, the driver will try to allocate an interrupt with a relative low priority (1,2,3)
} esp_isp_ae_config_t;
/**
* @brief New an ISP AE controller
*
* @param[in] isp_proc ISP Processor handle
* @param[in] ae_config Pointer to AE config. Refer to ``esp_isp_ae_config_t``.
* @param[out] ret_hdl AE 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_ae_controller(isp_proc_handle_t isp_proc, const esp_isp_ae_config_t *ae_config, isp_ae_ctlr_t *ret_hdl);
/**
* @brief Delete an ISP AE controller
*
* @param[in] ae_ctlr AE 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_ae_controller(isp_ae_ctlr_t ae_ctlr);
/**
* @brief Enable an ISP AE controller
*
* @param[in] ae_ctlr AE 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_ae_controller_enable(isp_ae_ctlr_t ae_ctlr);
/**
* @brief Disable an ISP AE controller
*
* @param[in] ae_ctlr AE 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_ae_controller_disable(isp_ae_ctlr_t ae_ctlr);
/**
* @brief Trigger AE luminance statistics for one time and get the result
*
* @param[in] ae_ctlr AE controller handle
* @param[in] timeout_ms Timeout in millisecond
* - timeout_ms < 0: Won't return until finished
* - timeout_ms = 0: No timeout, trigger one time statistics and return immediately,
* in this case, the result won't be assigned in this function,
* but you can get the result in the callback `esp_isp_ae_env_detector_evt_cbs_t::on_env_statistics_done`
* - timeout_ms > 0: Wait for specified milliseconds, if not finished, then return timeout error
* @param[out] out_res AE luminance statistics result, can be NULL if `timeout_ms = 0`
*
* @return
* - ESP_OK On success
* - ESP_ERR_TIMEOUT If the waiting time exceeds the specified timeout.
* - ESP_ERR_INVALID_ARG If the combination of arguments is invalid.
* - ESP_ERR_INVALID_STATE Driver state is invalid.
*/
esp_err_t esp_isp_ae_controller_get_oneshot_statistics(isp_ae_ctlr_t ae_ctlr, int timeout_ms, isp_ae_result_t *out_res);
/**
* @brief Start AE continuous statistics of the luminance in the windows
* @note This function is an asynchronous and non-block function,
* it will start the continuous statistics and return immediately.
* You have to register the AE callback and get the result from the callback event data.
* @note When using oneshot statistics, the AE Environment Detector will be temporarily disabled
* and will automatically recover once the oneshot is complete.
* @param[in] ae_ctlr AE controller handle
* @return
* - ESP_OK On success
* - ESP_ERR_INVALID_ARG Null pointer
* - ESP_ERR_INVALID_STATE Driver state is invalid.
*/
esp_err_t esp_isp_ae_controller_start_continuous_statistics(isp_ae_ctlr_t ae_ctlr);
/**
* @brief Stop AE continuous statistics of the luminance in the windows
*
* @param[in] ae_ctlr AE controller handle
* @return
* - ESP_OK On success
* - ESP_ERR_INVALID_ARG Null pointer
* - ESP_ERR_INVALID_STATE Driver state is invalid.
*/
esp_err_t esp_isp_ae_controller_stop_continuous_statistics(isp_ae_ctlr_t ae_ctlr);
/*---------------------------------------------
AE env detector
----------------------------------------------*/
/**
* @brief AE environment detector config
*/
typedef struct {
int interval; /*!< Interval between environment detection, in frames.
* i.e., AE controller will trigger the statistic periodically to detect the environment change.
*/
} esp_isp_ae_env_config_t;
/**
* @brief AE environment detector config
*/
typedef struct {
int low_thresh; /*!< Low threshold for AE environment detector luminance */
int high_thresh; /*!< High threshold for AE environment detector luminance */
} esp_isp_ae_env_thresh_t;
/**
* @brief Set ISP AE environment detector
*
* @param[in] ae_ctlr AE controller handle
* @param[in] env_config AE Env detector configuration
*
* @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_ae_controller_set_env_detector(isp_ae_ctlr_t ae_ctlr, const esp_isp_ae_env_config_t *env_config);
/**
* @brief Set ISP AE environment detector detecting threshold
*
* @param[in] ae_ctlr AE controller handle
* @param[in] env_thresh Luminance thresholds for AE env detector
*
* @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_ae_controller_set_env_detector_threshold(isp_ae_ctlr_t ae_ctlr, const esp_isp_ae_env_thresh_t *env_thresh);
/**
* @brief Event data structure
*/
typedef struct {
isp_ae_result_t ae_result; /*!< The AE statistics result */
} esp_isp_ae_env_detector_evt_data_t;
/**
* @brief Prototype of ISP AE Env detector event callback
*
* @param[in] ae_ctlr ISP AE controller handle
* @param[in] edata ISP AE Env detector event data
* @param[in] user_data User registered context, registered when in `esp_isp_ae_env_detector_register_event_callbacks()`
*
* @return Whether a high priority task is woken up by this function
*/
typedef bool (*esp_isp_ae_env_detector_callback_t)(isp_ae_ctlr_t ae_ctlr, const esp_isp_ae_env_detector_evt_data_t *edata, void *user_data);
/**
* @brief Group of ISP AE env_detector
* @note These callbacks are all running in an ISR environment.
* @note When CONFIG_ISP_ISR_IRAM_SAEE 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_ae_env_detector_callback_t on_env_statistics_done; ///< Event callback, invoked when environment sample done.
esp_isp_ae_env_detector_callback_t on_env_change; ///< Event callback, invoked when environment change happens.
} esp_isp_ae_env_detector_evt_cbs_t;
/**
* @brief Register AE Env 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_SAEE 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] ae_ctlr AE controller 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_ae_env_detector_register_event_callbacks(isp_ae_ctlr_t ae_ctlr, const esp_isp_ae_env_detector_evt_cbs_t *cbs, void *user_data);
#ifdef __cplusplus
}
#endif

View File

@@ -21,7 +21,7 @@ extern "C" {
typedef struct {
isp_window_t window[ISP_AF_WINDOW_NUM]; ///< The sampling windows of AF
int edge_thresh; ///< Edge threshold, definition higher than this value will be counted as a valid pixel for calculating AF result
int intr_priority; ///< The interrupt priority, range 0~7, if set to 0, the driver will try to allocate an interrupt with a relative low priority (1,2,3) otherwise the larger the higher, 7 is NMI
int intr_priority; ///< The interrupt priority, range 0~3, if set to 0, the driver will try to allocate an interrupt with a relative low priority (1,2,3)
} esp_isp_af_config_t;
/**
@@ -78,9 +78,6 @@ esp_err_t esp_isp_af_controller_disable(isp_af_ctlr_t af_ctrlr);
/**
* @brief Trigger AF luminance and definition statistics for one time and get the result
* @note This function is a synchronous and block function,
* it only returns when AF luminance and definition statistics is done or timeout.
* It's a simple method to get the result directly for one time.
*
* @param[in] af_ctrlr AF controller handle
* @param[in] timeout_ms Timeout in millisecond
@@ -109,6 +106,7 @@ esp_err_t esp_isp_af_controller_get_oneshot_statistics(isp_af_ctlr_t af_ctrlr, i
* @note This function is an asynchronous and non-block function,
* it will start the continuous statistics and return immediately.
* You have to register the AF callback and get the result from the callback event data.
* @note When continuous mode start, AF environment detector will be invalid
*
* @param[in] af_ctrlr AF controller handle
* @return
@@ -130,7 +128,7 @@ esp_err_t esp_isp_af_controller_start_continuous_statistics(isp_af_ctlr_t af_ctr
esp_err_t esp_isp_af_controller_stop_continuous_statistics(isp_af_ctlr_t af_ctrlr);
/*---------------------------------------------
AF Env Monitor
AF Env Detector
----------------------------------------------*/
/**
* @brief AF environment detector config
@@ -147,6 +145,7 @@ typedef struct {
* @param[in] af_ctrlr AF controller handle
* @param[in] env_config AF Env detector configuration
*
* @note When continuous mode start, AF environment detector will be invalid
* @return
* - ESP_OK On success
* - ESP_ERR_INVALID_ARG If the combination of arguments is invalid.

View File

@@ -121,9 +121,6 @@ esp_err_t esp_isp_awb_controller_disable(isp_awb_ctlr_t awb_ctlr);
/**
* @brief Trigger AWB white patch statistics for one time and get the result
* @note This function is a synchronous and block function,
* it only returns when AWB white patch statistics is done or timeout.
* It's a simple method to get the result directly for one time.
*
* @param[in] awb_ctlr AWB controller handle
* @param[in] timeout_ms Timeout in millisecond

View File

@@ -28,6 +28,7 @@ typedef struct {
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
int intr_priority; ///< The interrupt priority, range 0~3
} esp_isp_processor_cfg_t;
/**

View File

@@ -48,6 +48,13 @@ typedef struct {
uint32_t sum_b; ///< The sum of B channel of these white patches
} isp_awb_stat_result_t;
/**
* @brief ISP AE result
*/
typedef struct {
int luminance[ISP_AE_BLOCK_X_NUM][ISP_AE_BLOCK_Y_NUM]; ///< Luminance, it refers how luminant an image is
} isp_ae_result_t;
/**
* @brief Type of ISP processor handle
*/
@@ -63,6 +70,11 @@ typedef struct isp_af_controller_t *isp_af_ctlr_t;
*/
typedef struct isp_awb_controller_t *isp_awb_ctlr_t;
/**
* @brief Type of ISP AE controller handle
*/
typedef struct isp_ae_controller_t *isp_ae_ctlr_t;
#ifdef __cplusplus
}
#endif

View File

@@ -32,17 +32,20 @@ extern "C" {
#endif
#if CONFIG_ISP_ISR_IRAM_SAFE
#define ISP_INTR_ALLOC_FLAGS (ESP_INTR_FLAG_SHARED | ESP_INTR_FLAG_INTRDISABLED | ESP_INTR_FLAG_IRAM)
#define ISP_INTR_ALLOC_FLAGS (ESP_INTR_FLAG_INTRDISABLED | ESP_INTR_FLAG_IRAM)
#define ISP_MEM_ALLOC_CAPS (MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT)
#else
#define ISP_INTR_ALLOC_FLAGS (ESP_INTR_FLAG_SHARED | ESP_INTR_FLAG_INTRDISABLED)
#define ISP_INTR_ALLOC_FLAGS (ESP_INTR_FLAG_INTRDISABLED)
#define ISP_MEM_ALLOC_CAPS MALLOC_CAP_DEFAULT
#endif
typedef enum {
ISP_FSM_INIT,
ISP_FSM_ENABLE,
ISP_FSM_START,
ISP_FSM_INIT, // Controller is initialized, but not enabled
ISP_FSM_ENABLE, // Controller is enabled, but is not running
ISP_FSM_START, // Controller is in running
ISP_FSM_ONESHOT, // Controller is in oneshot sampling
ISP_FSM_CONTINUOUS, // Controller is in continuous sampling
} isp_fsm_t;
#if SOC_ISP_SUPPORTED
@@ -66,9 +69,35 @@ typedef struct isp_processor_t {
isp_af_ctlr_t af_ctlr[SOC_ISP_AF_CTLR_NUMS];
isp_awb_ctlr_t awb_ctlr;
isp_fsm_t bf_fsm;
isp_ae_ctlr_t ae_ctlr;
/* ISR */
intr_handle_t intr_hdl;
int intr_priority;
int isr_ref_counts;
struct {
uint32_t af_isr_added: 1;
uint32_t ae_isr_added: 1;
uint32_t awb_isr_added: 1;
} isr_users;
} isp_processor_t;
#endif
typedef enum {
ISP_SUBMODULE_AF,
ISP_SUBMODULE_AE,
ISP_SUBMODULE_AWB,
} isp_submodule_t;
/*---------------------------------------------------------------
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);
bool esp_isp_ae_isr(isp_proc_handle_t proc, uint32_t ae_events);
bool esp_isp_awb_isr(isp_proc_handle_t proc, uint32_t awb_events);
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,321 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <esp_types.h>
#include <sys/lock.h>
#include <stdatomic.h>
#include "sdkconfig.h"
#include "esp_log.h"
#include "esp_check.h"
#include "esp_heap_caps.h"
#include "freertos/FreeRTOS.h"
#include "driver/isp_ae.h"
#include "esp_private/isp_private.h"
static const char *TAG = "ISP_AE";
typedef struct isp_ae_controller_t {
int id;
_Atomic isp_fsm_t fsm;
portMUX_TYPE spinlock;
intr_handle_t intr_handle;
int intr_priority;
isp_proc_handle_t isp_proc;
QueueHandle_t evt_que;
int low_thresh;
int high_thresh;
esp_isp_ae_env_detector_evt_cbs_t cbs;
void *user_data;
} isp_ae_controller_t;
/*---------------------------------------------
AE
----------------------------------------------*/
static esp_err_t s_isp_claim_ae_controller(isp_proc_handle_t isp_proc, isp_ae_ctlr_t ae_ctlr)
{
assert(isp_proc && ae_ctlr);
esp_err_t ret = ESP_ERR_NOT_FOUND;
portENTER_CRITICAL(&isp_proc->spinlock);
if (!isp_proc->ae_ctlr) {
isp_proc->ae_ctlr = ae_ctlr;
ret = ESP_OK;
}
portEXIT_CRITICAL(&isp_proc->spinlock);
return ret;
}
static void s_isp_declaim_ae_controller(isp_ae_ctlr_t ae_ctlr)
{
if (ae_ctlr && ae_ctlr->isp_proc) {
portENTER_CRITICAL(&ae_ctlr->isp_proc->spinlock);
ae_ctlr->isp_proc->ae_ctlr = NULL;
portEXIT_CRITICAL(&ae_ctlr->isp_proc->spinlock);
}
}
static void s_isp_ae_free_controller(isp_ae_ctlr_t ae_ctlr)
{
if (ae_ctlr) {
if (ae_ctlr->intr_handle) {
esp_intr_free(ae_ctlr->intr_handle);
}
if (ae_ctlr->evt_que) {
vQueueDeleteWithCaps(ae_ctlr->evt_que);
}
free(ae_ctlr);
}
}
esp_err_t esp_isp_new_ae_controller(isp_proc_handle_t isp_proc, const esp_isp_ae_config_t *ae_config, isp_ae_ctlr_t *ret_hdl)
{
esp_err_t ret = ESP_FAIL;
ESP_RETURN_ON_FALSE(isp_proc && ae_config && ret_hdl, ESP_ERR_INVALID_ARG, TAG, "invalid argument: null pointer");
ESP_RETURN_ON_FALSE(((ae_config->window.top_left.x < ISP_LL_AE_WINDOW_MAX_RANGE) &&
(ae_config->window.btm_right.x >= ae_config->window.top_left.x) &&
(ae_config->window.btm_right.x < ISP_LL_AE_WINDOW_MAX_RANGE) &&
(ae_config->window.top_left.y < ISP_LL_AE_WINDOW_MAX_RANGE) &&
(ae_config->window.btm_right.y >= ae_config->window.top_left.y) &&
(ae_config->window.btm_right.y < ISP_LL_AE_WINDOW_MAX_RANGE)), ESP_ERR_INVALID_ARG, TAG, "invalid window");
isp_ae_ctlr_t ae_ctlr = heap_caps_calloc(1, sizeof(isp_ae_controller_t), ISP_MEM_ALLOC_CAPS);
ESP_RETURN_ON_FALSE(ae_ctlr, ESP_ERR_NO_MEM, TAG, "no mem");
ae_ctlr->evt_que = xQueueCreateWithCaps(1, sizeof(isp_ae_result_t), ISP_MEM_ALLOC_CAPS);
ESP_GOTO_ON_FALSE(ae_ctlr->evt_que, ESP_ERR_NO_MEM, err1, TAG, "no mem for ae event queue");
atomic_init(&ae_ctlr->fsm, ISP_FSM_INIT);
ae_ctlr->spinlock = (portMUX_TYPE)portMUX_INITIALIZER_UNLOCKED;
ae_ctlr->isp_proc = isp_proc;
//claim an AE controller
ESP_GOTO_ON_ERROR(s_isp_claim_ae_controller(isp_proc, ae_ctlr), err1, TAG, "no available controller");
// Register the AE ISR
int intr_priority = (ae_config->intr_priority > 0 && ae_config->intr_priority <= 3) ? BIT(ae_config->intr_priority) : ESP_INTR_FLAG_LOWMED;
ESP_GOTO_ON_ERROR(intr_priority != isp_proc->intr_priority, err2, TAG, "intr_priority error");
ESP_GOTO_ON_ERROR(esp_isp_register_isr(ae_ctlr->isp_proc, ISP_SUBMODULE_AE), err2, TAG, "fail to register ISR");
isp_ll_ae_set_sample_point(isp_proc->hal.hw, ae_config->sample_point);
isp_ll_ae_enable(isp_proc->hal.hw, false);
isp_hal_ae_window_config(&isp_proc->hal, &ae_config->window);
isp_ll_clear_intr(isp_proc->hal.hw, ISP_LL_EVENT_AE_MASK);
*ret_hdl = ae_ctlr;
return ESP_OK;
err2:
s_isp_declaim_ae_controller(ae_ctlr);
err1:
s_isp_ae_free_controller(ae_ctlr);
return ret;
}
esp_err_t esp_isp_del_ae_controller(isp_ae_ctlr_t ae_ctlr)
{
ESP_RETURN_ON_FALSE(ae_ctlr && ae_ctlr->isp_proc, ESP_ERR_INVALID_ARG, TAG, "invalid argument: null pointer");
ESP_RETURN_ON_FALSE(ae_ctlr->isp_proc->ae_ctlr == ae_ctlr, ESP_ERR_INVALID_ARG, TAG, "controller isn't in use");
ESP_RETURN_ON_FALSE(atomic_load(&ae_ctlr->fsm) == ISP_FSM_INIT, ESP_ERR_INVALID_STATE, TAG, "controller not in init state");
// Deregister the AE ISR
ESP_RETURN_ON_FALSE(esp_isp_deregister_isr(ae_ctlr->isp_proc, ISP_SUBMODULE_AE) == ESP_OK, ESP_FAIL, TAG, "fail to deregister ISR");
s_isp_declaim_ae_controller(ae_ctlr);
s_isp_ae_free_controller(ae_ctlr);
return ESP_OK;
}
esp_err_t esp_isp_ae_controller_enable(isp_ae_ctlr_t ae_ctlr)
{
ESP_RETURN_ON_FALSE(ae_ctlr && ae_ctlr->isp_proc, ESP_ERR_INVALID_ARG, TAG, "invalid argument: null pointer");
isp_fsm_t expected_fsm = ISP_FSM_INIT;
ESP_RETURN_ON_FALSE(atomic_compare_exchange_strong(&ae_ctlr->fsm, &expected_fsm, ISP_FSM_ENABLE),
ESP_ERR_INVALID_STATE, TAG, "controller not in init state");
isp_ll_ae_clk_enable(ae_ctlr->isp_proc->hal.hw, true);
isp_ll_enable_intr(ae_ctlr->isp_proc->hal.hw, ISP_LL_EVENT_AE_MASK, true);
isp_ll_ae_enable(ae_ctlr->isp_proc->hal.hw, true);
return ESP_OK;
}
esp_err_t esp_isp_ae_controller_disable(isp_ae_ctlr_t ae_ctlr)
{
ESP_RETURN_ON_FALSE(ae_ctlr && ae_ctlr->isp_proc, ESP_ERR_INVALID_ARG, TAG, "invalid argument: null pointer");
isp_fsm_t expected_fsm = ISP_FSM_ENABLE;
ESP_RETURN_ON_FALSE(atomic_compare_exchange_strong(&ae_ctlr->fsm, &expected_fsm, ISP_FSM_INIT),
ESP_ERR_INVALID_STATE, TAG, "controller not in enable state");
isp_ll_ae_clk_enable(ae_ctlr->isp_proc->hal.hw, false);
isp_ll_enable_intr(ae_ctlr->isp_proc->hal.hw, ISP_LL_EVENT_AE_MASK, false);
isp_ll_ae_enable(ae_ctlr->isp_proc->hal.hw, false);
esp_intr_disable(ae_ctlr->intr_handle);
return ESP_OK;
}
esp_err_t esp_isp_ae_controller_get_oneshot_statistics(isp_ae_ctlr_t ae_ctlr, int timeout_ms, isp_ae_result_t *out_res)
{
ESP_RETURN_ON_FALSE_ISR(ae_ctlr && (out_res || timeout_ms == 0), ESP_ERR_INVALID_ARG, TAG, "invalid argument: null pointer");
esp_err_t ret = ESP_OK;
TickType_t ticks = timeout_ms < 0 ? portMAX_DELAY : pdMS_TO_TICKS(timeout_ms);
isp_fsm_t expected_fsm = ISP_FSM_ENABLE;
if (atomic_compare_exchange_strong(&ae_ctlr->fsm, &expected_fsm, ISP_FSM_ONESHOT)) {
// Reset the queue in case receiving the legacy data in the queue
xQueueReset(ae_ctlr->evt_que);
// Disable the env detector when manual statistics.
// Otherwise, the env detector results may overwrite the manual statistics results when the statistics results are not read out in time
isp_ll_ae_env_detector_set_thresh(ae_ctlr->isp_proc->hal.hw, 0, 0);
// Trigger the AE statistics manually
isp_ll_ae_manual_update(ae_ctlr->isp_proc->hal.hw);
// Wait the statistics to finish and receive the result from the queue
if ((ticks > 0) && xQueueReceive(ae_ctlr->evt_que, out_res, ticks) != pdTRUE) {
ret = ESP_ERR_TIMEOUT;
}
// Re-enable the env detector after manual statistics.
isp_ll_ae_env_detector_set_thresh(ae_ctlr->isp_proc->hal.hw, ae_ctlr->low_thresh, ae_ctlr->high_thresh);
} else {
ESP_RETURN_ON_FALSE_ISR(false, ESP_ERR_INVALID_STATE, TAG, "controller is not enabled yet");
}
atomic_store(&ae_ctlr->fsm, ISP_FSM_ENABLE);
return ret;
}
esp_err_t esp_isp_ae_controller_start_continuous_statistics(isp_ae_ctlr_t ae_ctlr)
{
ESP_RETURN_ON_FALSE_ISR(ae_ctlr, ESP_ERR_INVALID_ARG, TAG, "invalid argument: null pointer");
isp_fsm_t expected_fsm = ISP_FSM_ENABLE;
if (atomic_compare_exchange_strong(&ae_ctlr->fsm, &expected_fsm, ISP_FSM_CONTINUOUS)) {
isp_ll_ae_manual_update(ae_ctlr->isp_proc->hal.hw);
} else {
ESP_RETURN_ON_FALSE_ISR(false, ESP_ERR_INVALID_STATE, TAG, "controller is not enabled yet");
}
return ESP_OK;
}
esp_err_t esp_isp_ae_controller_stop_continuous_statistics(isp_ae_ctlr_t ae_ctlr)
{
ESP_RETURN_ON_FALSE_ISR(ae_ctlr, ESP_ERR_INVALID_ARG, TAG, "invalid argument: null pointer");
isp_fsm_t expected_fsm = ISP_FSM_CONTINUOUS;
ESP_RETURN_ON_FALSE_ISR(atomic_compare_exchange_strong(&ae_ctlr->fsm, &expected_fsm, ISP_FSM_ENABLE),
ESP_ERR_INVALID_STATE, TAG, "controller is not running");
return ESP_OK;
}
/*---------------------------------------------
AE Env Detector
----------------------------------------------*/
esp_err_t esp_isp_ae_controller_set_env_detector(isp_ae_ctlr_t ae_ctlr, const esp_isp_ae_env_config_t *env_config)
{
ESP_RETURN_ON_FALSE(ae_ctlr && env_config, ESP_ERR_INVALID_ARG, TAG, "invalid argument: null pointer");
ESP_RETURN_ON_FALSE(env_config->interval > 0, ESP_ERR_INVALID_STATE, TAG, "invalid interval, should be greater than 0");
ESP_RETURN_ON_FALSE(atomic_load(&ae_ctlr->fsm) == ISP_FSM_INIT, ESP_ERR_INVALID_STATE, TAG, "controller not in init state");
isp_ll_clear_intr(ae_ctlr->isp_proc->hal.hw, ISP_LL_EVENT_AE_ENV);
isp_ll_ae_env_detector_set_period(ae_ctlr->isp_proc->hal.hw, env_config->interval);
isp_ll_enable_intr(ae_ctlr->isp_proc->hal.hw, ISP_LL_EVENT_AE_ENV, true);
return ESP_OK;
}
esp_err_t esp_isp_ae_env_detector_register_event_callbacks(isp_ae_ctlr_t ae_ctlr, const esp_isp_ae_env_detector_evt_cbs_t *cbs, void *user_data)
{
ESP_RETURN_ON_FALSE(ae_ctlr && cbs, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
ESP_RETURN_ON_FALSE(atomic_load(&ae_ctlr->fsm) == ISP_FSM_INIT, ESP_ERR_INVALID_STATE, TAG, "controller not in init state");
#if CONFIG_ISP_ISR_IRAM_SAEE
if (cbs->on_env_statistics_done) {
ESP_RETURN_ON_FALSE(esp_ptr_in_iram(cbs->on_env_statistics_done), ESP_ERR_INVALID_ARG, TAG, "on_env_statistics_done callback not in IRAM");
}
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");
}
if (user_data) {
ESP_RETURN_ON_FALSE(esp_ptr_internal(user_data), ESP_ERR_INVALID_ARG, TAG, "user context not in internal RAM");
}
#endif
ae_ctlr->cbs.on_env_statistics_done = cbs->on_env_statistics_done;
ae_ctlr->cbs.on_env_change = cbs->on_env_change;
ae_ctlr->user_data = user_data;
return ESP_OK;
}
esp_err_t esp_isp_ae_controller_set_env_detector_threshold(isp_ae_ctlr_t ae_ctlr, const esp_isp_ae_env_thresh_t *env_thresh)
{
ESP_RETURN_ON_FALSE_ISR(ae_ctlr, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
ESP_RETURN_ON_FALSE_ISR((env_thresh->low_thresh != 0 && env_thresh->high_thresh != 0) && (env_thresh->low_thresh <= env_thresh->high_thresh), ESP_ERR_INVALID_STATE, TAG, "invalid AE env detector thresh");
ESP_RETURN_ON_FALSE(atomic_load(&ae_ctlr->fsm) == ISP_FSM_ENABLE, ESP_ERR_INVALID_STATE, TAG, "controller not in enable state");
ae_ctlr->low_thresh = env_thresh->low_thresh;
ae_ctlr->high_thresh = env_thresh->high_thresh;
isp_ll_ae_env_detector_set_thresh(ae_ctlr->isp_proc->hal.hw, ae_ctlr->low_thresh, ae_ctlr->high_thresh);
return ESP_OK;
}
/*---------------------------------------------------------------
INTR
---------------------------------------------------------------*/
bool IRAM_ATTR esp_isp_ae_isr(isp_proc_handle_t proc, uint32_t ae_events)
{
isp_ae_ctlr_t ae_ctlr = proc->ae_ctlr;
bool need_yield = false;
esp_isp_ae_env_detector_evt_data_t edata = {};
if (ae_events) {
// Get the statistics result
int block_id = 0;
for (int i = 0; i < SOC_ISP_AE_BLOCK_X_NUMS; i++) {
for (int j = 0; j < SOC_ISP_AE_BLOCK_Y_NUMS; j++) {
edata.ae_result.luminance[i][j] = isp_ll_ae_get_block_mean_lum(proc->hal.hw, block_id);
block_id++;
}
}
BaseType_t high_task_awake = false;
// Send the event data to the queue, overwrite the legacy one if exist
xQueueOverwriteFromISR(ae_ctlr->evt_que, &edata.ae_result, &high_task_awake);
// Invoke the callback if the callback is registered
need_yield |= high_task_awake == pdTRUE;
/* If started continuous sampling, then trigger the next AE sample */
if (atomic_load(&ae_ctlr->fsm) == ISP_FSM_START) {
isp_ll_ae_manual_update(ae_ctlr->isp_proc->hal.hw);
}
}
/**
* Deal with the interrupts.
* Now only one detector.
* Should decide a detector instance according to the hw event.
*/
if (ae_events & ISP_LL_EVENT_AE_FDONE) {
if (ae_ctlr->cbs.on_env_statistics_done) {
need_yield |= ae_ctlr->cbs.on_env_statistics_done(ae_ctlr, &edata, ae_ctlr->user_data);
}
}
if (ae_events & ISP_LL_EVENT_AE_ENV) {
if (ae_ctlr->cbs.on_env_change) {
need_yield |= ae_ctlr->cbs.on_env_change(ae_ctlr, &edata, ae_ctlr->user_data);
}
}
return need_yield;
}

View File

@@ -28,8 +28,6 @@ typedef struct isp_af_controller_t {
void *user_data;
} isp_af_controller_t;
static void s_isp_af_default_isr(void *arg);
/*---------------------------------------------
AF
----------------------------------------------*/
@@ -72,7 +70,7 @@ static void s_isp_af_free_controller(isp_af_ctlr_t af_ctlr)
esp_intr_free(af_ctlr->intr_handle);
}
if (af_ctlr->evt_que) {
vQueueDelete(af_ctlr->evt_que);
vQueueDeleteWithCaps(af_ctlr->evt_que);
}
free(af_ctlr);
}
@@ -114,10 +112,9 @@ esp_err_t esp_isp_new_af_controller(isp_proc_handle_t isp_proc, const esp_isp_af
ESP_GOTO_ON_ERROR(s_isp_claim_af_controller(isp_proc, af_ctlr), err1, TAG, "no available controller");
// Register the AF ISR
uint32_t intr_st_reg_addr = isp_ll_get_intr_status_reg_addr(isp_proc->hal.hw);
int intr_priority = af_config->intr_priority > 0 && af_config->intr_priority <= 7 ? BIT(af_config->intr_priority) : ESP_INTR_FLAG_LOWMED;
ESP_GOTO_ON_ERROR(esp_intr_alloc_intrstatus(isp_hw_info.instances[isp_proc->proc_id].irq, ISP_INTR_ALLOC_FLAGS | intr_priority, intr_st_reg_addr, ISP_LL_EVENT_AF_MASK,
s_isp_af_default_isr, af_ctlr, &af_ctlr->intr_handle), err2, TAG, "allocate interrupt failed");
int intr_priority = (af_config->intr_priority > 0 && af_config->intr_priority <= 3) ? BIT(af_config->intr_priority) : ESP_INTR_FLAG_LOWMED;
ESP_GOTO_ON_ERROR(intr_priority != isp_proc->intr_priority, err2, TAG, "intr_priority error");
ESP_GOTO_ON_ERROR(esp_isp_register_isr(af_ctlr->isp_proc, ISP_SUBMODULE_AF), err2, TAG, "fail to register ISR");
isp_ll_af_enable_auto_update(isp_proc->hal.hw, false);
isp_ll_af_enable(isp_proc->hal.hw, false);
@@ -126,7 +123,7 @@ esp_err_t esp_isp_new_af_controller(isp_proc_handle_t isp_proc, const esp_isp_af
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_mode(isp_proc->hal.hw, ISP_LL_AF_EDGE_DETECTOR_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);
@@ -153,6 +150,10 @@ esp_err_t esp_isp_del_af_controller(isp_af_ctlr_t af_ctlr)
}
}
ESP_RETURN_ON_FALSE(exist, ESP_ERR_INVALID_ARG, TAG, "controller isn't in use");
// Deregister the AF ISR
ESP_RETURN_ON_FALSE(esp_isp_deregister_isr(af_ctlr->isp_proc, ISP_SUBMODULE_AF) == ESP_OK, ESP_FAIL, TAG, "fail to deregister ISR");
s_isp_declaim_af_controller(af_ctlr);
s_isp_af_free_controller(af_ctlr);
@@ -164,7 +165,6 @@ esp_err_t esp_isp_af_controller_enable(isp_af_ctlr_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");
esp_intr_enable(af_ctlr->intr_handle);
isp_ll_af_clk_enable(af_ctlr->isp_proc->hal.hw, true);
isp_ll_enable_intr(af_ctlr->isp_proc->hal.hw, ISP_LL_EVENT_AF_MASK, true);
isp_ll_af_enable(af_ctlr->isp_proc->hal.hw, true);
@@ -229,7 +229,7 @@ esp_err_t esp_isp_af_controller_stop_continuous_statistics(isp_af_ctlr_t af_ctrl
}
/*---------------------------------------------
AF Env Monitor
AF Env Detector
----------------------------------------------*/
esp_err_t esp_isp_af_controller_set_env_detector(isp_af_ctlr_t af_ctrlr, const esp_isp_af_env_config_t *env_config)
{
@@ -238,11 +238,11 @@ esp_err_t esp_isp_af_controller_set_env_detector(isp_af_ctlr_t af_ctrlr, const e
af_ctrlr->config.interval = env_config->interval;
isp_ll_af_env_monitor_set_period(af_ctrlr->isp_proc->hal.hw, 0);
isp_ll_af_env_detector_set_period(af_ctrlr->isp_proc->hal.hw, 0);
isp_ll_clear_intr(af_ctrlr->isp_proc->hal.hw, ISP_LL_EVENT_AF_ENV);
isp_ll_af_env_monitor_set_mode(af_ctrlr->isp_proc->hal.hw, ISP_LL_AF_ENV_MONITOR_MODE_ABS);
isp_ll_af_env_monitor_set_period(af_ctrlr->isp_proc->hal.hw, af_ctrlr->config.interval);
isp_ll_af_env_detector_set_mode(af_ctrlr->isp_proc->hal.hw, ISP_LL_AF_ENV_DETECTOR_MODE_ABS);
isp_ll_af_env_detector_set_period(af_ctrlr->isp_proc->hal.hw, af_ctrlr->config.interval);
isp_ll_enable_intr(af_ctrlr->isp_proc->hal.hw, ISP_LL_EVENT_AF_ENV, true);
return ESP_OK;
@@ -276,7 +276,7 @@ esp_err_t esp_isp_af_controller_set_env_detector_threshold(isp_af_ctlr_t af_ctrl
ESP_RETURN_ON_FALSE_ISR(af_ctrlr, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
ESP_RETURN_ON_FALSE_ISR(af_ctrlr->fsm == ISP_FSM_ENABLE, ESP_ERR_INVALID_STATE, TAG, "detector isn't in enable state");
isp_ll_af_env_monitor_set_thresh(af_ctrlr->isp_proc->hal.hw, definition_thresh, luminance_thresh);
isp_ll_af_env_detector_set_thresh(af_ctrlr->isp_proc->hal.hw, definition_thresh, luminance_thresh);
return ESP_OK;
}
@@ -284,12 +284,9 @@ esp_err_t esp_isp_af_controller_set_env_detector_threshold(isp_af_ctlr_t af_ctrl
/*---------------------------------------------------------------
INTR
---------------------------------------------------------------*/
static void IRAM_ATTR s_isp_af_default_isr(void *arg)
bool IRAM_ATTR esp_isp_af_isr(isp_proc_handle_t proc, uint32_t af_events)
{
isp_af_ctlr_t af_ctrlr = (isp_af_ctlr_t)arg;
isp_proc_handle_t proc = af_ctrlr->isp_proc;
uint32_t af_events = isp_hal_check_clear_intr_event(&proc->hal, ISP_LL_EVENT_AF_MASK);
isp_af_ctlr_t af_ctrlr = proc->af_ctlr[0];
bool need_yield = false;
esp_isp_af_env_detector_evt_data_t edata = {};
@@ -323,8 +320,5 @@ static void IRAM_ATTR s_isp_af_default_isr(void *arg)
need_yield |= af_ctrlr->cbs.on_env_change(af_ctrlr, &edata, af_ctrlr->user_data);
}
}
if (need_yield) {
portYIELD_FROM_ISR();
}
return need_yield;
}

View File

@@ -18,7 +18,6 @@ typedef struct isp_awb_controller_t {
isp_fsm_t fsm;
portMUX_TYPE spinlock;
intr_handle_t intr_handle;
int intr_priority;
isp_proc_handle_t isp_proc;
QueueHandle_t evt_que;
SemaphoreHandle_t stat_lock;
@@ -28,8 +27,6 @@ typedef struct isp_awb_controller_t {
static const char *TAG = "ISP_AWB";
static void s_isp_awb_default_isr(void *arg);
/*---------------------------------------------
AWB
----------------------------------------------*/
@@ -64,10 +61,10 @@ static void s_isp_awb_free_controller(isp_awb_ctlr_t awb_ctlr)
esp_intr_free(awb_ctlr->intr_handle);
}
if (awb_ctlr->evt_que) {
vQueueDelete(awb_ctlr->evt_que);
vQueueDeleteWithCaps(awb_ctlr->evt_que);
}
if (awb_ctlr->stat_lock) {
vSemaphoreDelete(awb_ctlr->stat_lock);
vSemaphoreDeleteWithCaps(awb_ctlr->stat_lock);
}
free(awb_ctlr);
}
@@ -109,11 +106,11 @@ esp_err_t esp_isp_new_awb_controller(isp_proc_handle_t isp_proc, const esp_isp_a
// Claim an AWB controller
ESP_GOTO_ON_ERROR(s_isp_claim_awb_controller(isp_proc, awb_ctlr), err1, TAG, "no available controller");
// Register the AWB ISR
uint32_t intr_st_reg_addr = isp_ll_get_intr_status_reg_addr(isp_proc->hal.hw);
awb_ctlr->intr_priority = awb_cfg->intr_priority > 0 && awb_cfg->intr_priority <= 3 ? BIT(awb_cfg->intr_priority) : ESP_INTR_FLAG_LOWMED;
ESP_GOTO_ON_ERROR(esp_intr_alloc_intrstatus(isp_hw_info.instances[isp_proc->proc_id].irq, ISP_INTR_ALLOC_FLAGS | awb_ctlr->intr_priority, intr_st_reg_addr, ISP_LL_EVENT_AWB_MASK,
s_isp_awb_default_isr, awb_ctlr, &awb_ctlr->intr_handle), err2, TAG, "allocate interrupt failed");
int intr_priority = (awb_cfg->intr_priority > 0 && awb_cfg->intr_priority <= 3) ? BIT(awb_cfg->intr_priority) : ESP_INTR_FLAG_LOWMED;
ESP_GOTO_ON_ERROR(intr_priority != isp_proc->intr_priority, err2, TAG, "intr_priority error");
ESP_GOTO_ON_ERROR(esp_isp_register_isr(awb_ctlr->isp_proc, ISP_SUBMODULE_AWB), err2, TAG, "fail to register ISR");
// Configure the hardware
isp_ll_awb_enable(isp_proc->hal.hw, false);
@@ -137,6 +134,8 @@ esp_err_t esp_isp_del_awb_controller(isp_awb_ctlr_t awb_ctlr)
ESP_RETURN_ON_FALSE(awb_ctlr && awb_ctlr->isp_proc, ESP_ERR_INVALID_ARG, TAG, "invalid argument: null pointer");
ESP_RETURN_ON_FALSE(awb_ctlr->isp_proc->awb_ctlr == awb_ctlr, ESP_ERR_INVALID_ARG, TAG, "controller isn't in use");
ESP_RETURN_ON_FALSE(awb_ctlr->fsm == ISP_FSM_INIT, ESP_ERR_INVALID_STATE, TAG, "controller isn't in init state");
ESP_RETURN_ON_FALSE(esp_isp_deregister_isr(awb_ctlr->isp_proc, ISP_SUBMODULE_AWB) == ESP_OK, ESP_FAIL, TAG, "fail to deregister ISR");
s_isp_declaim_awb_controller(awb_ctlr);
isp_ll_awb_enable_algorithm_mode(awb_ctlr->isp_proc->hal.hw, false);
@@ -148,9 +147,6 @@ esp_err_t esp_isp_del_awb_controller(isp_awb_ctlr_t awb_ctlr)
esp_err_t esp_isp_awb_controller_reconfig(isp_awb_ctlr_t awb_ctlr, const esp_isp_awb_config_t *awb_cfg)
{
ESP_RETURN_ON_FALSE(awb_ctlr && awb_cfg, ESP_ERR_INVALID_ARG, TAG, "invalid argument: null pointer");
int intr_priority = awb_cfg->intr_priority > 0 && awb_cfg->intr_priority <= 3 ? BIT(awb_cfg->intr_priority) : ESP_INTR_FLAG_LOWMED;
ESP_RETURN_ON_FALSE(intr_priority == awb_ctlr->intr_priority, ESP_ERR_INVALID_ARG, TAG, "can't change interrupt priority after initialized");
return s_esp_isp_awb_config_hardware(awb_ctlr->isp_proc, awb_cfg);
}
@@ -161,14 +157,10 @@ esp_err_t esp_isp_awb_controller_enable(isp_awb_ctlr_t awb_ctlr)
awb_ctlr->fsm = ISP_FSM_ENABLE;
esp_err_t ret = ESP_OK;
ESP_GOTO_ON_ERROR(esp_intr_enable(awb_ctlr->intr_handle), err, TAG, "failed to enable the AWB interrupt");
isp_ll_awb_clk_enable(awb_ctlr->isp_proc->hal.hw, true);
isp_ll_enable_intr(awb_ctlr->isp_proc->hal.hw, ISP_LL_EVENT_AWB_MASK, true);
xSemaphoreGive(awb_ctlr->stat_lock);
return ret;
err:
awb_ctlr->fsm = ISP_FSM_INIT;
return ret;
}
@@ -245,13 +237,8 @@ esp_err_t esp_isp_awb_controller_stop_continuous_statistics(isp_awb_ctlr_t awb_c
/*---------------------------------------------------------------
INTR
---------------------------------------------------------------*/
static void IRAM_ATTR s_isp_awb_default_isr(void *arg)
bool IRAM_ATTR esp_isp_awb_isr(isp_proc_handle_t proc, uint32_t awb_events)
{
isp_awb_ctlr_t awb_ctlr = (isp_awb_ctlr_t)arg;
isp_proc_handle_t proc = awb_ctlr->isp_proc;
uint32_t awb_events = isp_hal_check_clear_intr_event(&proc->hal, ISP_LL_EVENT_AWB_MASK);
bool need_yield = false;
if (awb_events & ISP_LL_EVENT_AWB_FDONE) {
@@ -279,10 +266,7 @@ static void IRAM_ATTR s_isp_awb_default_isr(void *arg)
isp_ll_awb_enable(awb_ctlr->isp_proc->hal.hw, true);
}
}
if (need_yield) {
portYIELD_FROM_ISR();
}
return need_yield;
}
esp_err_t esp_isp_awb_register_event_callbacks(isp_awb_ctlr_t awb_ctlr, const esp_isp_awb_cbs_t *cbs, void *user_data)

View File

@@ -89,6 +89,8 @@ esp_err_t esp_isp_new_processor(const esp_isp_processor_cfg_t *proc_config, isp_
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");
proc->intr_priority = (proc_config->intr_priority > 0 && proc_config->intr_priority <= 3) ? BIT(proc_config->intr_priority) : ESP_INTR_FLAG_LOWMED;
uint32_t out_clk_freq_hz = 0;
hal_utils_clk_div_t clk_div = {};
hal_utils_clk_info_t clk_info = {
@@ -102,7 +104,7 @@ esp_err_t esp_isp_new_processor(const esp_isp_processor_cfg_t *proc_config, isp_
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);
PERIPH_RCC_ATOMIC() {
isp_ll_select_clk_source(proc->hal.hw, clk_src);
@@ -186,3 +188,137 @@ esp_err_t esp_isp_disable(isp_proc_handle_t proc)
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);
uint32_t awb_events = isp_hal_check_clear_intr_event(&proc->hal, ISP_LL_EVENT_AWB_MASK);
uint32_t ae_events = isp_hal_check_clear_intr_event(&proc->hal, ISP_LL_EVENT_AE_MASK);
bool do_dispatch = false;
//Deal with hw events
if (af_events) {
portENTER_CRITICAL_ISR(&proc->spinlock);
do_dispatch = proc->isr_users.af_isr_added;
portEXIT_CRITICAL_ISR(&proc->spinlock);
if (do_dispatch) {
need_yield |= esp_isp_af_isr(proc, af_events);
}
do_dispatch = false;
}
if (awb_events) {
portENTER_CRITICAL_ISR(&proc->spinlock);
do_dispatch = proc->isr_users.awb_isr_added;
portEXIT_CRITICAL_ISR(&proc->spinlock);
if (do_dispatch) {
need_yield |= esp_isp_awb_isr(proc, awb_events);
}
do_dispatch = false;
}
if (ae_events) {
portENTER_CRITICAL_ISR(&proc->spinlock);
do_dispatch = proc->isr_users.ae_isr_added;
portEXIT_CRITICAL_ISR(&proc->spinlock);
if (do_dispatch) {
need_yield |= esp_isp_ae_isr(proc, ae_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->isr_users.af_isr_added = true;
break;
case ISP_SUBMODULE_AWB:
proc->isr_users.awb_isr_added = true;
break;
case ISP_SUBMODULE_AE:
proc->isr_users.ae_isr_added = true;
break;
default:
assert(false);
}
portEXIT_CRITICAL(&proc->spinlock);
if (do_alloc) {
uint32_t intr_st_reg_addr = isp_ll_get_intr_status_reg_addr(proc->hal.hw);
uint32_t intr_st_mask = ISP_LL_EVENT_AF_MASK | ISP_LL_EVENT_AE_MASK | ISP_LL_EVENT_AWB_MASK;
ret = esp_intr_alloc_intrstatus(isp_hw_info.instances[proc->proc_id].irq, ISP_INTR_ALLOC_FLAGS | proc->intr_priority, intr_st_reg_addr, intr_st_mask,
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->isr_users.af_isr_added = false;
break;
case ISP_SUBMODULE_AWB:
proc->isr_users.awb_isr_added = false;
break;
case ISP_SUBMODULE_AE:
proc->isr_users.ae_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

@@ -153,3 +153,94 @@ TEST_CASE("ISP CCM basic function", "[isp]")
TEST_ESP_OK(esp_isp_disable(isp_proc));
TEST_ESP_OK(esp_isp_del_processor(isp_proc));
}
static bool test_isp_ae_default_on_statistics_done_cb(isp_ae_ctlr_t ae_ctlr, const esp_isp_ae_env_detector_evt_data_t *edata, void *user_data)
{
(void) ae_ctlr;
(void) edata;
(void) user_data;
// Do nothing
return false;
}
static bool test_isp_ae_default_on_continuous_done_cb(isp_ae_ctlr_t ae_ctlr, const esp_isp_ae_env_detector_evt_data_t *edata, void *user_data)
{
(void) ae_ctlr;
(void) edata;
(void) user_data;
// Do nothing
return false;
}
TEST_CASE("ISP AE driver basic function", "[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));
TEST_ESP_OK(esp_isp_enable(isp_proc));
isp_ae_ctlr_t ae_ctlr = NULL;
/* Default parameters from helper macro */
esp_isp_ae_config_t ae_config = {
.sample_point = ISP_AE_SAMPLE_POINT_AFTER_DEMOSAIC,
};
isp_ae_result_t ae_res = {};
/* Create the ae controller */
TEST_ESP_OK(esp_isp_new_ae_controller(isp_proc, &ae_config, &ae_ctlr));
/* Register AE callback */
esp_isp_ae_env_detector_evt_cbs_t ae_cb = {
.on_env_statistics_done = test_isp_ae_default_on_statistics_done_cb,
.on_env_change = test_isp_ae_default_on_continuous_done_cb,
};
TEST_ESP_OK(esp_isp_ae_env_detector_register_event_callbacks(ae_ctlr, &ae_cb, NULL));
/* Enabled the ae controller */
TEST_ESP_OK(esp_isp_ae_controller_enable(ae_ctlr));
/* Start continuous AE statistics */
TEST_ESP_OK(esp_isp_ae_controller_start_continuous_statistics(ae_ctlr));
TEST_ESP_ERR(ESP_ERR_INVALID_STATE, esp_isp_ae_controller_get_oneshot_statistics(ae_ctlr, 0, &ae_res));
/* Stop continuous AE statistics */
TEST_ESP_OK(esp_isp_ae_controller_stop_continuous_statistics(ae_ctlr));
TEST_ESP_ERR(ESP_ERR_TIMEOUT, esp_isp_ae_controller_get_oneshot_statistics(ae_ctlr, 1, &ae_res));
/* Disable the ae controller */
TEST_ESP_OK(esp_isp_ae_controller_disable(ae_ctlr));
/* Delete the ae controller and free the resources */
TEST_ESP_OK(esp_isp_del_ae_controller(ae_ctlr));
TEST_ESP_OK(esp_isp_disable(isp_proc));
TEST_ESP_OK(esp_isp_del_processor(isp_proc));
}
TEST_CASE("ISP AE 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_ae_config_t ae_config = {
.sample_point = ISP_AE_SAMPLE_POINT_AFTER_DEMOSAIC,
};
isp_ae_ctlr_t ae_ctrlr[SOC_ISP_AE_CTLR_NUMS + 1] = {};
for (int i = 0; i < SOC_ISP_AE_CTLR_NUMS; i++) {
TEST_ESP_OK(esp_isp_new_ae_controller(isp_proc, &ae_config, &ae_ctrlr[i]));
}
TEST_ASSERT(esp_isp_new_ae_controller(isp_proc, &ae_config, &ae_ctrlr[SOC_ISP_AE_CTLR_NUMS]) == ESP_ERR_NOT_FOUND);
for (int i = 0; i < SOC_ISP_AE_CTLR_NUMS; i++) {
TEST_ESP_OK(esp_isp_del_ae_controller(ae_ctrlr[i]));
}
TEST_ESP_OK(esp_isp_del_processor(isp_proc));
}

View File

@@ -41,8 +41,8 @@ extern "C" {
#define ISP_LL_EVENT_MIPI_HNUM_UNMATCH (1<<5)
#define ISP_LL_EVENT_DPC_CHECK_DONE (1<<6)
#define ISP_LL_EVENT_GAMMA_XCOORD_ERR (1<<7)
#define ISP_LL_EVENT_AE_MONITOR (1<<8)
#define ISP_LL_EVENT_AE_FRAME_DONE (1<<9)
#define ISP_LL_EVENT_AE_ENV (1<<8)
#define ISP_LL_EVENT_AE_FDONE (1<<9)
#define ISP_LL_EVENT_AF_FDONE (1<<10)
#define ISP_LL_EVENT_AF_ENV (1<<11)
#define ISP_LL_EVENT_AWB_FDONE (1<<12)
@@ -65,6 +65,7 @@ extern "C" {
#define ISP_LL_EVENT_ALL_MASK (0x1FFFFFFF)
#define ISP_LL_EVENT_AF_MASK (ISP_LL_EVENT_AF_FDONE | ISP_LL_EVENT_AF_ENV)
#define ISP_LL_EVENT_AE_MASK (ISP_LL_EVENT_AE_FDONE | ISP_LL_EVENT_AE_ENV)
#define ISP_LL_EVENT_AWB_MASK (ISP_LL_EVENT_AWB_FDONE)
/*---------------------------------------------------------------
@@ -72,6 +73,11 @@ extern "C" {
---------------------------------------------------------------*/
#define ISP_LL_AF_WINDOW_MAX_RANGE ((1<<12) - 1)
/*---------------------------------------------------------------
AE
---------------------------------------------------------------*/
#define ISP_LL_AE_WINDOW_MAX_RANGE ((1<<12) - 1)
/*---------------------------------------------------------------
BF
---------------------------------------------------------------*/
@@ -117,20 +123,20 @@ typedef union {
} isp_ll_ccm_gain_t;
/**
* @brief Env monitor mode
* @brief Env detector mode
*/
typedef enum {
ISP_LL_AF_ENV_MONITOR_MODE_ABS, ///< Use absolute val for threshold
ISP_LL_AF_ENV_MONITOR_MODE_RATIO, ///< Use ratio val for threshold
} isp_ll_af_env_monitor_mode_t;
ISP_LL_AF_ENV_DETECTOR_MODE_ABS, ///< Use absolute val for threshold
ISP_LL_AF_ENV_DETECTOR_MODE_RATIO, ///< Use ratio val for threshold
} isp_ll_af_env_detector_mode_t;
/**
* @brief Edge monitor mode
* @brief Edge detector mode
*/
typedef enum {
ISP_LL_AF_EDGE_MONITOR_MODE_AUTO, ///< Auto set threshold
ISP_LL_AF_EDGE_MONITOR_MODE_MANUAL, ///< Manual set threshold
} isp_ll_af_edge_monitor_mode_t;
ISP_LL_AF_EDGE_DETECTOR_MODE_AUTO, ///< Auto set threshold
ISP_LL_AF_EDGE_DETECTOR_MODE_MANUAL, ///< Manual set threshold
} isp_ll_af_edge_detector_mode_t;
/*---------------------------------------------------------------
@@ -496,11 +502,11 @@ static inline void isp_ll_af_manual_update(isp_dev_t *hw)
* @brief Set edge thresh mode
*
* @param[in] hw Hardware instance address
* @param[in] mode See `isp_ll_af_edge_monitor_mode_t`
* @param[in] mode See `isp_ll_af_edge_detector_mode_t`
*/
static inline void isp_ll_af_set_edge_thresh_mode(isp_dev_t *hw, isp_ll_af_edge_monitor_mode_t mode)
static inline void isp_ll_af_set_edge_thresh_mode(isp_dev_t *hw, isp_ll_af_edge_detector_mode_t mode)
{
if (mode == ISP_LL_AF_EDGE_MONITOR_MODE_AUTO) {
if (mode == ISP_LL_AF_EDGE_DETECTOR_MODE_AUTO) {
hw->af_threshold.af_threshold = 0;
}
}
@@ -622,43 +628,43 @@ static inline uint32_t isp_ll_af_get_window_lum(isp_dev_t *hw, uint32_t window_i
}
/*---------------------------------------------
AF Env Monitor
AF Env detector
----------------------------------------------*/
/**
* @brief Set env monitor period
* @brief Set env detector period
*
* @param[in] hw Hardware instance address
* @param[in] period period of the env monitor, in frames
* @param[in] period period of the env detector, in frames
*/
static inline void isp_ll_af_env_monitor_set_period(isp_dev_t *hw, uint32_t period)
static inline void isp_ll_af_env_detector_set_period(isp_dev_t *hw, uint32_t period)
{
hw->af_ctrl0.af_env_period = period;
}
/**
* @brief Set env monitor mode
* @brief Set env detector mode
*
* @param[in] hw Hardware instance address
* @param[in] mode See `isp_ll_af_env_monitor_mode_t`
* @param[in] mode See `isp_ll_af_env_detector_mode_t`
*/
static inline void isp_ll_af_env_monitor_set_mode(isp_dev_t *hw, isp_ll_af_env_monitor_mode_t mode)
static inline void isp_ll_af_env_detector_set_mode(isp_dev_t *hw, isp_ll_af_env_detector_mode_t mode)
{
if (mode == ISP_LL_AF_ENV_MONITOR_MODE_RATIO) {
if (mode == ISP_LL_AF_ENV_DETECTOR_MODE_RATIO) {
hw->af_env_user_th_sum.af_env_user_threshold_sum = 0x0;
hw->af_env_user_th_lum.af_env_user_threshold_lum = 0x0;
}
//nothing to do to if using abs mode, it'll be enabled after `isp_ll_af_env_monitor_set_thresh()`
//nothing to do to if using abs mode, it'll be enabled after `isp_ll_af_env_detector_set_thresh()`
}
/**
* @brief Set env monitor threshold
* @brief Set env detector threshold
*
* @param[in] hw Hardware instance address
* @param[in] sum_thresh Threshold for definition
* @param[in] lum_thresh Threshold for luminance
*/
static inline void isp_ll_af_env_monitor_set_thresh(isp_dev_t *hw, uint32_t sum_thresh, uint32_t lum_thresh)
static inline void isp_ll_af_env_detector_set_thresh(isp_dev_t *hw, uint32_t sum_thresh, uint32_t lum_thresh)
{
HAL_ASSERT(sum_thresh != 0 || lum_thresh != 0);
@@ -667,12 +673,12 @@ static inline void isp_ll_af_env_monitor_set_thresh(isp_dev_t *hw, uint32_t sum_
}
/**
* @brief Set env monitor ratio
* @brief Set env detector ratio
*
* @param[in] hw Hardware instance address
* @param[in] ratio_val Threshold for ratio
*/
static inline void isp_ll_af_env_monitor_set_ratio(isp_dev_t *hw, uint32_t ratio_val)
static inline void isp_ll_af_env_detector_set_ratio(isp_dev_t *hw, uint32_t ratio_val)
{
HAL_ASSERT(hw->af_env_user_th_sum.af_env_user_threshold_sum == 0 &&
hw->af_env_user_th_lum.af_env_user_threshold_lum == 0);
@@ -974,6 +980,122 @@ static inline void isp_ll_cam_enable(isp_dev_t *hw, bool enable)
hw->cam_cntl.cam_en = 0;
}
}
/*---------------------------------------------------------------
AE
---------------------------------------------------------------*/
/**
* @brief Enable / Disable AE clock
*
* @param[in] hw Hardware instance address
* @param[in] enable Enable / Disable
*/
static inline void isp_ll_ae_clk_enable(isp_dev_t *hw, bool enable)
{
hw->clk_en.clk_ae_force_on = enable;
}
/**
* @brief Enable / Disable AE
*
* @param[in] hw Hardware instance address
* @param[in] enable Enable / Disable
*/
static inline void isp_ll_ae_enable(isp_dev_t *hw, bool enable)
{
hw->cntl.ae_en = enable;
}
/**
* @brief Manual aupdate AF once
*
* @param[in] hw Hardware instance address
*/
static inline void isp_ll_ae_manual_update(isp_dev_t *hw)
{
hw->ae_ctrl.ae_update = 1;
}
/**
* @brief Select AE input data source
*
* @param[in] hw Hardware instance address
* @param[in] sample_point 0: AE input data after demosaic, 1: AE input data after gamma
*/
static inline void isp_ll_ae_set_sample_point(isp_dev_t *hw, isp_ae_sample_point_t sample_point)
{
hw->ae_ctrl.ae_select = sample_point;
}
/**
* @brief Set AE window range
*
* @param[in] hw Hardware instance address
* @param[in] x_start Top left pixel x axis value
* @param[in] x_bsize Block size on x axis
* @param[in] y_start Top left pixel y axis value
* @param[in] y_bsize Block size on y axis
*/
static inline void isp_ll_ae_set_window_range(isp_dev_t *hw, int x_start, int x_bsize, int y_start, int y_bsize)
{
hw->ae_bx.ae_x_start = x_start;
hw->ae_bx.ae_x_bsize = x_bsize;
hw->ae_by.ae_y_start = y_start;
hw->ae_by.ae_y_bsize = y_bsize;
}
/**
* @brief Get block mean luminance
*
* @param[in] hw Hardware instance address
* @param[in] block_id
*
* @return Mean luminance
*/
static inline int isp_ll_ae_get_block_mean_lum(isp_dev_t *hw, int block_id)
{
HAL_ASSERT(block_id >=0 && block_id < (SOC_ISP_AE_BLOCK_X_NUMS * SOC_ISP_AE_BLOCK_Y_NUMS));
return hw->ae_block_mean[block_id / 4].ae_b_mean[3 - (block_id % 4)];
}
/**
* @brief AE set the pixel number of each subwin, and set the reciprocal of each subwin_pixnum, 20bit fraction
*
* @param[in] hw Hardware instance address
* @param[in] subwin_pixnum Pixel number
*/
static inline void isp_ll_ae_set_subwin_pixnum_recip(isp_dev_t *hw, int subwin_pixnum)
{
hw->ae_winpixnum.ae_subwin_pixnum = subwin_pixnum;
int subwin_recip = (1 << 20) / subwin_pixnum;
hw->ae_win_reciprocal.ae_subwin_recip = subwin_recip;
}
/**
* @brief Set AE env detector threshold
*
* @param[in] hw Hardware instance address
* @param[in] low_thresh Lower lum threshold
* @param[in] high_thresh Higher lum threshold
*/
static inline void isp_ll_ae_env_detector_set_thresh(isp_dev_t *hw, uint32_t low_thresh, uint32_t high_thresh)
{
hw->ae_monitor.ae_monitor_tl = low_thresh;
hw->ae_monitor.ae_monitor_th = high_thresh;
}
/**
* @brief Set AE env detector period
*
* @param[in] hw Hardware instance address
* @param[in] period period of the AE env detector, in frames
*/
static inline void isp_ll_ae_env_detector_set_period(isp_dev_t *hw, uint32_t period)
{
hw->ae_monitor.ae_monitor_period = period;
}
/*---------------------------------------------------------------
INTR
---------------------------------------------------------------*/

View File

@@ -64,7 +64,18 @@ void isp_hal_init(isp_hal_context_t *hal, int isp_id);
* @param[in] window_id Window ID
* @param[in] window Window info, see `isp_window_t`
*/
void isp_hal_af_window_config(const isp_hal_context_t *hal, int window_id, const isp_window_t *window);
void isp_hal_af_window_config(isp_hal_context_t *hal, int window_id, const isp_window_t *window);
/*---------------------------------------------------------------
AE
---------------------------------------------------------------*/
/**
* @brief Configure AE window
*
* @param[in] hal Context of the HAL layer
* @param[in] window Window info, see `isp_window_t`
*/
void isp_hal_ae_window_config(isp_hal_context_t *hal, const isp_window_t *window);
/*---------------------------------------------------------------
INTR
@@ -75,7 +86,7 @@ void isp_hal_af_window_config(const isp_hal_context_t *hal, int window_id, const
* @param[in] hal Context of the HAL layer
* @param[in] mask HW event mask
*/
uint32_t isp_hal_check_clear_intr_event(const isp_hal_context_t *hal, uint32_t mask);
uint32_t isp_hal_check_clear_intr_event(isp_hal_context_t *hal, uint32_t mask);
/*---------------------------------------------------------------
BF
@@ -101,7 +112,7 @@ void isp_hal_bf_config(isp_hal_context_t *hal, isp_hal_bf_cfg_t *config);
* - true Set success
* - false Invalid argument
*/
bool isp_hal_ccm_set_matrix(const isp_hal_context_t *hal, bool saturation, const float flt_matrix[ISP_CCM_DIMENSION][ISP_CCM_DIMENSION]);
bool isp_hal_ccm_set_matrix(isp_hal_context_t *hal, bool saturation, const float flt_matrix[ISP_CCM_DIMENSION][ISP_CCM_DIMENSION]);
/*---------------------------------------------------------------
AWB
@@ -115,7 +126,7 @@ bool isp_hal_ccm_set_matrix(const isp_hal_context_t *hal, bool saturation, const
* - true Set success
* - false Invalid arg
*/
bool isp_hal_awb_set_window_range(const isp_hal_context_t *hal, const isp_window_t *win);
bool isp_hal_awb_set_window_range(isp_hal_context_t *hal, const isp_window_t *win);
/**
* @brief Set the luminance range of the white patch
@@ -127,7 +138,7 @@ bool isp_hal_awb_set_window_range(const isp_hal_context_t *hal, const isp_window
* - true Set success
* - false Invalid arg
*/
bool isp_hal_awb_set_luminance_range(const isp_hal_context_t *hal, uint32_t lum_min, uint32_t lum_max);
bool isp_hal_awb_set_luminance_range(isp_hal_context_t *hal, uint32_t lum_min, uint32_t lum_max);
/**
* @brief Set the R/G ratio of the white patch
@@ -138,7 +149,7 @@ bool isp_hal_awb_set_luminance_range(const isp_hal_context_t *hal, uint32_t lum_
* - true Set success
* - false Invalid arg
*/
bool isp_hal_awb_set_rg_ratio_range(const isp_hal_context_t *hal, float rg_min, float rg_max);
bool isp_hal_awb_set_rg_ratio_range(isp_hal_context_t *hal, float rg_min, float rg_max);
/**
* @brief Set the B/R ratio of the white patch
@@ -149,7 +160,7 @@ bool isp_hal_awb_set_rg_ratio_range(const isp_hal_context_t *hal, float rg_min,
* - true Set success
* - false Invalid arg
*/
bool isp_hal_awb_set_bg_ratio_range(const isp_hal_context_t *hal, float bg_min, float bg_max);
bool isp_hal_awb_set_bg_ratio_range(isp_hal_context_t *hal, float bg_min, float bg_max);
#ifdef __cplusplus
}

View File

@@ -122,6 +122,26 @@ typedef enum {
ISP_AWB_SAMPLE_POINT_AFTER_CCM, ///< Sample AWB data after CCM (Color Correction Matrix)
} isp_awb_sample_point_t;
/*---------------------------------------------------------------
AE
---------------------------------------------------------------*/
#if (SOC_ISP_AE_BLOCK_X_NUMS && SOC_ISP_AE_BLOCK_Y_NUMS)
#define ISP_AE_BLOCK_X_NUM SOC_ISP_AE_BLOCK_X_NUMS // The AF window number for sampling
#define ISP_AE_BLOCK_Y_NUM SOC_ISP_AE_BLOCK_Y_NUMS // The AF window number for sampling
#else
#define ISP_AE_BLOCK_X_NUM 0
#define ISP_AE_BLOCK_Y_NUM 0
#endif
/**
* @brief ISP AE input data source
*
*/
typedef enum {
ISP_AE_SAMPLE_POINT_AFTER_DEMOSAIC, ///< AE input data after demosaic
ISP_AE_SAMPLE_POINT_AFTER_GAMMA, ///< AE input data after gamma
} isp_ae_sample_point_t;
#ifdef __cplusplus
}

View File

@@ -29,7 +29,7 @@ void isp_hal_init(isp_hal_context_t *hal, int isp_id)
/*---------------------------------------------------------------
AF
---------------------------------------------------------------*/
void isp_hal_af_window_config(const isp_hal_context_t *hal, int window_id, const isp_window_t *window)
void isp_hal_af_window_config(isp_hal_context_t *hal, int window_id, const isp_window_t *window)
{
isp_ll_af_set_window_range(hal->hw, window_id, window->top_left.x, window->top_left.y, window->btm_right.x, window->btm_right.y);
}
@@ -57,10 +57,28 @@ void isp_hal_bf_config(isp_hal_context_t *hal, isp_hal_bf_cfg_t *config)
isp_ll_bf_set_template(hal->hw, default_template);
}
}
/*---------------------------------------------------------------
AE
---------------------------------------------------------------*/
void isp_hal_ae_window_config(isp_hal_context_t *hal, const isp_window_t *window)
{
uint32_t ae_x_start = window->top_left.x;
uint32_t ae_x_bsize = (window->btm_right.x - window-> top_left.x) / SOC_ISP_AE_BLOCK_X_NUMS;
uint32_t ae_y_start = window->top_left.y;
uint32_t ae_y_bsize = (window->btm_right.y - window->top_left.y) / SOC_ISP_AE_BLOCK_Y_NUMS;
isp_ll_ae_set_window_range(hal->hw, ae_x_start, ae_x_bsize, ae_y_start, ae_y_bsize);
int ae_subwin_pixnum = ae_x_bsize * ae_y_bsize;
isp_ll_ae_set_subwin_pixnum_recip(hal->hw, ae_subwin_pixnum);
}
/*---------------------------------------------------------------
INTR, put in iram
---------------------------------------------------------------*/
uint32_t isp_hal_check_clear_intr_event(const isp_hal_context_t *hal, uint32_t mask)
uint32_t isp_hal_check_clear_intr_event(isp_hal_context_t *hal, uint32_t mask)
{
uint32_t triggered_events = isp_ll_get_intr_status(hal->hw) & mask;
@@ -74,7 +92,7 @@ uint32_t isp_hal_check_clear_intr_event(const isp_hal_context_t *hal, uint32_t m
/*---------------------------------------------------------------
Color Correction Matrix
---------------------------------------------------------------*/
bool isp_hal_ccm_set_matrix(const isp_hal_context_t *hal, bool saturation, const float flt_matrix[ISP_CCM_DIMENSION][ISP_CCM_DIMENSION])
bool isp_hal_ccm_set_matrix(isp_hal_context_t *hal, bool saturation, const float flt_matrix[ISP_CCM_DIMENSION][ISP_CCM_DIMENSION])
{
isp_ll_ccm_gain_t fp_matrix[ISP_CCM_DIMENSION][ISP_CCM_DIMENSION] = {};
hal_utils_fixed_point_t fp_cfg = {
@@ -98,7 +116,7 @@ bool isp_hal_ccm_set_matrix(const isp_hal_context_t *hal, bool saturation, const
/*---------------------------------------------------------------
AWB
---------------------------------------------------------------*/
bool isp_hal_awb_set_window_range(const isp_hal_context_t *hal, const isp_window_t *win)
bool isp_hal_awb_set_window_range(isp_hal_context_t *hal, const isp_window_t *win)
{
if (win->top_left.x > win->btm_right.x ||
win->top_left.y > win->btm_right.y ||
@@ -111,7 +129,7 @@ bool isp_hal_awb_set_window_range(const isp_hal_context_t *hal, const isp_window
return true;
}
bool isp_hal_awb_set_luminance_range(const isp_hal_context_t *hal, uint32_t lum_min, uint32_t lum_max)
bool isp_hal_awb_set_luminance_range(isp_hal_context_t *hal, uint32_t lum_min, uint32_t lum_max)
{
if (lum_min > lum_max || lum_max > ISP_LL_AWB_LUM_MAX_RANGE) {
return false;
@@ -120,7 +138,7 @@ bool isp_hal_awb_set_luminance_range(const isp_hal_context_t *hal, uint32_t lum_
return true;
}
bool isp_hal_awb_set_rg_ratio_range(const isp_hal_context_t *hal, float rg_min, float rg_max)
bool isp_hal_awb_set_rg_ratio_range(isp_hal_context_t *hal, float rg_min, float rg_max)
{
// Convert to fixed point
isp_ll_awb_rgb_ratio_t fp_rg_min = {};
@@ -142,7 +160,7 @@ bool isp_hal_awb_set_rg_ratio_range(const isp_hal_context_t *hal, float rg_min,
return true;
}
bool isp_hal_awb_set_bg_ratio_range(const isp_hal_context_t *hal, float bg_min, float bg_max)
bool isp_hal_awb_set_bg_ratio_range(isp_hal_context_t *hal, float bg_min, float bg_max)
{
// Convert to fixed point
isp_ll_awb_rgb_ratio_t fp_bg_min = {};

View File

@@ -843,6 +843,18 @@ config SOC_ISP_AF_WINDOW_NUMS
int
default 3
config SOC_ISP_AE_CTLR_NUMS
int
default 1
config SOC_ISP_AE_BLOCK_X_NUMS
int
default 5
config SOC_ISP_AE_BLOCK_Y_NUMS
int
default 5
config SOC_ISP_SHARE_CSI_BRG
bool
default y

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
*/
@@ -398,35 +398,35 @@ typedef union {
typedef union {
struct {
/** gau_template21 : R/W; bitpos: [3:0]; default: 15;
* this field configures index 21 of gausian template
* this field configures index 21 of gaussian template
*/
uint32_t gau_template21:4;
/** gau_template20 : R/W; bitpos: [7:4]; default: 15;
* this field configures index 20 of gausian template
* this field configures index 20 of gaussian template
*/
uint32_t gau_template20:4;
/** gau_template12 : R/W; bitpos: [11:8]; default: 15;
* this field configures index 12 of gausian template
* this field configures index 12 of gaussian template
*/
uint32_t gau_template12:4;
/** gau_template11 : R/W; bitpos: [15:12]; default: 15;
* this field configures index 11 of gausian template
* this field configures index 11 of gaussian template
*/
uint32_t gau_template11:4;
/** gau_template10 : R/W; bitpos: [19:16]; default: 15;
* this field configures index 10 of gausian template
* this field configures index 10 of gaussian template
*/
uint32_t gau_template10:4;
/** gau_template02 : R/W; bitpos: [23:20]; default: 15;
* this field configures index 02 of gausian template
* this field configures index 02 of gaussian template
*/
uint32_t gau_template02:4;
/** gau_template01 : R/W; bitpos: [27:24]; default: 15;
* this field configures index 01 of gausian template
* this field configures index 01 of gaussian template
*/
uint32_t gau_template01:4;
/** gau_template00 : R/W; bitpos: [31:28]; default: 15;
* this field configures index 00 of gausian template
* this field configures index 00 of gaussian template
*/
uint32_t gau_template00:4;
};
@@ -439,7 +439,7 @@ typedef union {
typedef union {
struct {
/** gau_template22 : R/W; bitpos: [3:0]; default: 15;
* this field configures index 22 of gausian template
* this field configures index 22 of gaussian template
*/
uint32_t gau_template22:4;
uint32_t reserved_4:28;
@@ -1321,7 +1321,7 @@ typedef union {
*/
uint32_t ae_monitor_th:8;
/** ae_monitor_period : R/W; bitpos: [21:16]; default: 0;
* this field cnfigures ae monitor frame period
* this field configures ae monitor frame period
*/
uint32_t ae_monitor_period:6;
uint32_t reserved_22:10;
@@ -1536,7 +1536,7 @@ typedef union {
typedef union {
struct {
/** dma_en : WT; bitpos: [0]; default: 0;
* write 1 to triger dma to get 1 frame
* write 1 to trigger dma to get 1 frame
*/
uint32_t dma_en:1;
/** dma_update_reg : R/W; bitpos: [1]; default: 0;
@@ -1584,7 +1584,7 @@ typedef union {
typedef union {
struct {
/** cam_en : R/W; bitpos: [0]; default: 0;
* write 1 to start recive camera data, write 0 to disable
* write 1 to start receive camera data, write 0 to disable
*/
uint32_t cam_en:1;
/** cam_update_reg : R/W; bitpos: [1]; default: 0;
@@ -1596,7 +1596,7 @@ typedef union {
*/
uint32_t cam_reset:1;
/** cam_clk_inv : R/W; bitpos: [3]; default: 0;
* this bit configures the invertion of cam clk from pad. 0: not invert cam clk, 1:
* this bit configures the inversion of cam clk from pad. 0: not invert cam clk, 1:
* invert cam clk
*/
uint32_t cam_clk_inv:1;
@@ -1880,7 +1880,7 @@ typedef union {
typedef union {
struct {
/** awb_mode : R/W; bitpos: [1:0]; default: 3;
* this field configures awb algo sel. 00: none sellected. 01: sel algo0. 10: sel
* this field configures awb algo sel. 00: none selected. 01: sel algo0. 10: sel
* algo1. 11: sel both algo0 and algo1
*/
uint32_t awb_mode:2;
@@ -2627,6 +2627,19 @@ typedef union {
uint32_t val;
} isp_lut_rdata_reg_t;
/** Type of ae_block_mean register
* ae statistic result
*/
typedef union {
struct {
/** ae_lum : RO; bitpos: [31:0]; default: 0;
* this field represents the result of AE block
*/
uint8_t ae_b_mean[4];
};
uint32_t val;
} isp_ae_block_mean_reg_t;
/** Type of ae_block_mean_0 register
* ae statistic result register 0
*/
@@ -2790,7 +2803,6 @@ typedef union {
};
uint32_t val;
} isp_ae_block_mean_6_reg_t;
/** Type of af_sum_a register
* result of sum of af window a
*/
@@ -3764,13 +3776,7 @@ typedef struct {
volatile isp_ae_by_reg_t ae_by;
volatile isp_ae_winpixnum_reg_t ae_winpixnum;
volatile isp_ae_win_reciprocal_reg_t ae_win_reciprocal;
volatile isp_ae_block_mean_0_reg_t ae_block_mean_0;
volatile isp_ae_block_mean_1_reg_t ae_block_mean_1;
volatile isp_ae_block_mean_2_reg_t ae_block_mean_2;
volatile isp_ae_block_mean_3_reg_t ae_block_mean_3;
volatile isp_ae_block_mean_4_reg_t ae_block_mean_4;
volatile isp_ae_block_mean_5_reg_t ae_block_mean_5;
volatile isp_ae_block_mean_6_reg_t ae_block_mean_6;
volatile isp_ae_block_mean_reg_t ae_block_mean[7];
volatile isp_sharp_ctrl0_reg_t sharp_ctrl0;
volatile isp_sharp_filter0_reg_t sharp_filter0;
volatile isp_sharp_filter1_reg_t sharp_filter1;

View File

@@ -334,6 +334,9 @@
#define SOC_ISP_DVP_CTLR_NUMS 1U
#define SOC_ISP_AF_CTLR_NUMS 1U
#define SOC_ISP_AF_WINDOW_NUMS 3
#define SOC_ISP_AE_CTLR_NUMS 1U
#define SOC_ISP_AE_BLOCK_X_NUMS 5
#define SOC_ISP_AE_BLOCK_Y_NUMS 5
#define SOC_ISP_SHARE_CSI_BRG 1
#define SOC_ISP_BF_TEMPLATE_X_NUMS 3
#define SOC_ISP_BF_TEMPLATE_Y_NUMS 3

View File

@@ -131,6 +131,24 @@ If an :cpp:type:`esp_isp_awb_config_t` configuration is specified, you can call
The AWB handle created in this step is required by other AWB APIs and AWB scheme.
Install ISP Auto-Exposure (AE) Driver
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
ISP auto-exposure (AE) driver requires the configuration that specified by :cpp:type:`esp_isp_ae_config_t`.
If the configurations in :cpp:type:`esp_isp_ae_config_t` is specified, users can call :cpp:func:`esp_isp_new_ae_controller` to allocate and initialize an ISP AE processor. This function will return an ISP AE processor handle if it runs correctly. You can take following code as reference.
.. code:: c
esp_isp_ae_config_t ae_config = {
.sample_point = ISP_AE_SAMPLE_POINT_AFTER_DEMOSAIC,
...
};
isp_ae_ctlr_t ae_ctlr = NULL;
ESP_ERROR_CHECK(esp_isp_new_ae_controller(isp_proc, &ae_config, &ae_ctlr));
You can use the created handle to do driver enable / disable the ISP AE driver and ISP AE environment detector setup.
Uninstall ISP Driver
~~~~~~~~~~~~~~~~~~~~
@@ -146,6 +164,11 @@ UnInstall ISP AWB Driver
If a previously installed ISP AWB processor is no longer needed, it's recommended to free the resource by calling :cpp:func:`esp_isp_del_awb_controller`, it will also release the underlying hardware.
UnInstall ISP AE Driver
~~~~~~~~~~~~~~~~~~~~~~~~
If a previously installed ISP AE processor is no longer needed, it's recommended to free the resource by calling :cpp:func:`esp_isp_del_ae_controller`, it will also release the underlying hardware.
.. _isp-enable-disable:
@@ -181,6 +204,9 @@ Aside from the above oneshot API, the ISP AF driver also provides a way to start
Note that if you want to use the continuous statistics, you need to register the :cpp:member:`esp_isp_af_env_detector_evt_cbs_t::on_env_statistics_done` or :cpp:member:`esp_isp_af_env_detector_evt_cbs_t::on_env_change` callback to get the statistics result. See how to register in `Register Event Callbacks <#isp-callback>`__
.. note::
When you use the continuous statistics, AF Environment Detector will be invalid.
.. code:: c
esp_isp_af_config_t af_config = {
@@ -231,6 +257,81 @@ Calling :cpp:func:`esp_isp_af_env_detector_set_threshold` to set the threshold o
int luminance_thresh = 0;
ESP_ERROR_CHECK(esp_isp_af_env_detector_set_threshold(env_detector, definition_thresh, luminance_thresh));
ISP AE Processor
----------------
Before doing ISP AE, you need to enable the ISP AE processor first, by calling :cpp:func:`esp_isp_ae_controller_enable`. This function:
* Switches the driver state from **init** to **enable**.
Calling :cpp:func:`esp_isp_ae_controller_disable` does the opposite, that is, put the driver back to the **init** state.
.. _isp-ae-statistics:
AE One-shot and Continuous Statistics
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Calling :cpp:func:`esp_isp_ae_controller_get_oneshot_statistics` to get oneshot AE statistics result. You can take following code as reference.
When you use AE oneshot statistics, the AE continuous mode need to be disabled otherwise the result may be overwritten by the environment detector. After oneshot operation finishes, you need to restart continuous mode again.
Aside from the above oneshot API, the ISP AE driver also provides a way to start AE statistics continuously. Calling :cpp:func:`esp_isp_ae_controller_start_continuous_statistics` to start the continuous statistics and :cpp:func:`esp_isp_ae_controller_stop_continuous_statistics` to stop it.
Note that if you want to use the continuous statistics, you need to register the :cpp:member:`esp_isp_ae_env_detector_evt_cbs_t::on_statistics_done` or :cpp:member:`esp_isp_ae_env_detector_evt_cbs_t::on_change` callback to get the statistics result. See how to register in `Register Event Callbacks <#isp-callback>`__
.. note::
When using oneshot statistics, the AE Environment Detector will be temporarily disabled and will automatically recover once the oneshot is complete.
.. code:: c
esp_isp_ae_config_t ae_config = {
.sample_point = ISP_AE_SAMPLE_POINT_AFTER_DEMOSAIC,
};
isp_ae_ctlr_t ae_ctlr = NULL;
ESP_ERROR_CHECK(esp_isp_new_ae_controller(isp_proc, &ae_config, &ae_ctlr));
ESP_ERROR_CHECK(esp_isp_ae_controller_enable(ae_ctlr));
isp_ae_result_t result = {};
/* Trigger the AE statistics and get its result for one time with timeout value 2000ms. */
ESP_ERROR_CHECK(esp_isp_ae_controller_get_oneshot_statistics(ae_ctlr, 2000, &result));
/* Start continuous AE statistics */
ESP_ERROR_CHECK(esp_isp_ae_controller_start_continuous_statistics(ae_ctlr));
// You can do other stuffs here, the statistics result can be obtained in the callback
// ......
// vTaskDelay(pdMS_TO_TICKS(1000));
/* Stop continuous AE statistics */
ESP_ERROR_CHECK(esp_isp_ae_controller_stop_continuous_statistics(ae_ctlr));
/* Disable the ae controller */
ESP_ERROR_CHECK(esp_isp_ae_controller_disable(ae_ctlr));
/* Delete the ae controller and free the resources */
ESP_ERROR_CHECK(esp_isp_del_ae_controller(ae_ctlr));
Set AE Environment Detector
^^^^^^^^^^^^^^^^^^^^^^^^^^^
Calling :cpp:func:`esp_isp_ae_controller_set_env_detector` to set an ISP AE environment detector. You can take following code as reference.
.. code:: c
esp_isp_ae_env_config_t env_config = {
.interval = 10,
};
ESP_ERROR_CHECK(esp_isp_ae_controller_set_env_detector(ae_ctlr, &env_config));
Set AE Environment Detector Threshold
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Calling :cpp:func:`esp_isp_ae_controller_set_env_detector_threshold` to set the thresholds(1-255) of an ISP AE environment detector.
.. code:: c
esp_isp_ae_env_thresh_t env_thresh = {
.low_thresh = 110,
.high_thresh = 130,
};
ESP_ERROR_CHECK(esp_isp_ae_controller_set_env_detector_threshold(ae_ctlr, env_thresh));
ISP AWB Processor
~~~~~~~~~~~~~~~~~
@@ -385,7 +486,7 @@ You can save your own context via the parameter ``user_data`` of :cpp:func:`esp_
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.
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_ae_controller` and :cpp:func:`esp_isp_del_ae_controller` are guaranteed to be thread safe by the driver, which means, user can call them from different RTOS tasks without protection by extra locks. Other APIs are not guaranteed to be thread-safe
.. _isp-kconfig-options: