mirror of
https://github.com/espressif/esp-idf.git
synced 2024-10-05 20:47:46 -04:00
Merge branch 'feature/support_isp_awb_v5.3' into 'release/v5.3'
feat(isp): Support ISP Auto White Balance (AWB) (v5.3) See merge request espressif/esp-idf!31605
This commit is contained in:
commit
53fd6cb516
@ -10,7 +10,8 @@ set(requires)
|
||||
|
||||
if(CONFIG_SOC_ISP_SUPPORTED)
|
||||
list(APPEND srcs "src/isp_core.c"
|
||||
"src/isp_af.c")
|
||||
"src/isp_af.c"
|
||||
"src/isp_awb.c")
|
||||
endif()
|
||||
|
||||
if(CONFIG_SOC_ISP_BF_SUPPORTED)
|
||||
|
@ -13,4 +13,5 @@
|
||||
|
||||
#include "driver/isp_core.h"
|
||||
#include "driver/isp_af.h"
|
||||
#include "driver/isp_awb.h"
|
||||
#include "driver/isp_bf.h"
|
||||
|
@ -178,7 +178,7 @@ typedef struct {
|
||||
/**
|
||||
* @brief Prototype of ISP AF Env detector event callback
|
||||
*
|
||||
* @param[in] handle ISP AF controller handle
|
||||
* @param[in] af_ctrlr ISP AF controller 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()`
|
||||
*
|
||||
|
220
components/esp_driver_isp/include/driver/isp_awb.h
Normal file
220
components/esp_driver_isp/include/driver/isp_awb.h
Normal file
@ -0,0 +1,220 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include "esp_err.h"
|
||||
#include "driver/isp_types.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief AWB controller config
|
||||
*/
|
||||
typedef struct {
|
||||
isp_awb_sample_point_t sample_point; /*!< AWB sample point of the ISP pipeline.
|
||||
* ISP_AWB_SAMPLE_POINT_BEFORE_CCM: sample before Color Correction Matrix(CCM).
|
||||
* ISP_AWB_SAMPLE_POINT_AFTER_CCM: sample after Color Correction Matrix(CCM).
|
||||
* If your camera support to set the manual gain to the RGB channels,
|
||||
* then you can choose to sample before CCM, and set the gain to the camera registers.
|
||||
* If your camera doesn't support the manual gain or don't want to change the camera configuration,
|
||||
* then you can choose to sample after CCM, and set the calculated gain to the CCM
|
||||
*/
|
||||
isp_window_t window; /*!< Statistic window of AWB.
|
||||
* Suggest to set it at the middle of the image and a little smaller than the whole image.
|
||||
* It will be more reliable because the edges of image are easily to be overexposure,
|
||||
* the overexposure pixels are almost at maximum luminance,
|
||||
* which are not good references to calculate the gain for white balance.
|
||||
*/
|
||||
struct {
|
||||
isp_u32_range_t luminance; /*!< Luminance range of the white patch. Range [0, 255 * 3]
|
||||
* Not suggest to set the max value to 255 * 3,
|
||||
* because these pixels are too bright, very possible to be overexposure.
|
||||
* So the pixels that too bright should not be the reference of the white balance.
|
||||
* And the minimum value better to be 0 to allow the white balance work under low luminance environment.
|
||||
*/
|
||||
isp_float_range_t red_green_ratio; /*!< Red to green ratio of the white patch. Range [0, 4.0).
|
||||
* The ratio could be as wider as possible,
|
||||
* so that all the distorted pixels will be counted for the reference of white balance.
|
||||
*/
|
||||
isp_float_range_t blue_green_ratio; /*!< Blue to green ratio of the white patch. Range [0, 4.0)
|
||||
* The ratio could be as wider as possible,
|
||||
* so that all the distorted pixels will be counted for the reference of white balance.
|
||||
*/
|
||||
} white_patch; /*!< white patch configuration */
|
||||
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_awb_config_t;
|
||||
|
||||
/**
|
||||
* @brief New an ISP AWB controller
|
||||
*
|
||||
* @param[in] isp_proc ISP Processor handle
|
||||
* @param[in] awb_cfg Pointer to AWB config. Refer to ``esp_isp_awb_config_t``.
|
||||
* @param[out] ret_hdl AWB 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_awb_controller(isp_proc_handle_t isp_proc, const esp_isp_awb_config_t *awb_cfg, isp_awb_ctlr_t *ret_hdl);
|
||||
|
||||
/**
|
||||
* @brief Delete an ISP AWB controller
|
||||
*
|
||||
* @param[in] awb_ctlr AWB 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_awb_controller(isp_awb_ctlr_t awb_ctlr);
|
||||
|
||||
/**
|
||||
* @brief Reconfigure the ISP AWB controller
|
||||
* @note This function is allowed to be called no matter the awb controller is enabled or not.
|
||||
*
|
||||
* @param[in] awb_ctlr AWB controller handle
|
||||
* @param[in] awb_cfg Pointer to AWB config. Refer to ``esp_isp_awb_config_t``
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK On success
|
||||
* - ESP_ERR_INVALID_ARG If the combination of arguments is invalid
|
||||
*/
|
||||
esp_err_t esp_isp_awb_controller_reconfig(isp_awb_ctlr_t awb_ctlr, const esp_isp_awb_config_t *awb_cfg);
|
||||
|
||||
/**
|
||||
* @brief Enable an ISP AWB controller
|
||||
*
|
||||
* @param[in] awb_ctlr AWB 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_awb_controller_enable(isp_awb_ctlr_t awb_ctlr);
|
||||
|
||||
/**
|
||||
* @brief Disable an ISP AWB controller
|
||||
*
|
||||
* @param[in] awb_ctlr AWB 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_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
|
||||
* - 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_awb_cbs_t::on_statistics_done`
|
||||
* - timeout_ms > 0: Wait for specified milliseconds, if not finished, then return timeout error
|
||||
* @param[out] out_res AWB white patch statistics result
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK On success
|
||||
* - ESP_ERR_TIMEOUT Wait for the result 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_awb_controller_get_oneshot_statistics(isp_awb_ctlr_t awb_ctlr, int timeout_ms, isp_awb_stat_result_t *out_res);
|
||||
|
||||
/**
|
||||
* @brief Start AWB continuous statistics of the white patch in the window
|
||||
* @note This function is an asynchronous and non-block function,
|
||||
* it will start the continuous statistics and return immediately.
|
||||
* You have to register the AWB callback and get the result from the callback event data.
|
||||
*
|
||||
* @param[in] awb_ctlr AWB 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_awb_controller_start_continuous_statistics(isp_awb_ctlr_t awb_ctlr);
|
||||
|
||||
/**
|
||||
* @brief Stop AWB continuous statistics of the white patch in the window
|
||||
*
|
||||
* @param[in] awb_ctlr AWB 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_awb_controller_stop_continuous_statistics(isp_awb_ctlr_t awb_ctlr);
|
||||
|
||||
/**
|
||||
* @brief Event data of callbacks
|
||||
*/
|
||||
typedef struct {
|
||||
isp_awb_stat_result_t awb_result; /*!< The AWB white patch statistics result */
|
||||
} esp_isp_awb_evt_data_t;
|
||||
|
||||
/**
|
||||
* @brief Prototype of ISP AWB event callback
|
||||
*
|
||||
* @param[in] handle ISP AWB controller handle
|
||||
* @param[in] edata ISP AWB event data
|
||||
* @param[in] user_data User registered context, registered when in `esp_isp_awb_env_detector_register_event_callbacks()`
|
||||
*
|
||||
* @return Whether a high priority task is woken up by this function
|
||||
*/
|
||||
typedef bool (*esp_isp_awb_callback_t)(isp_awb_ctlr_t awb_ctlr, const esp_isp_awb_evt_data_t *edata, void *user_data);
|
||||
|
||||
/**
|
||||
* @brief Group of ISP AWB 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_awb_callback_t on_statistics_done; ///< Event callback, invoked when white patches statistic done.
|
||||
} esp_isp_awb_cbs_t;
|
||||
|
||||
/**
|
||||
* @brief Register AWB 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] awb_ctlr AWB 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_awb_register_event_callbacks(isp_awb_ctlr_t awb_ctlr, const esp_isp_awb_cbs_t *cbs, void *user_data);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
@ -12,6 +12,42 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief ISP unsigned integer range type
|
||||
* @note Whether the edge value are included depends on the variable itself
|
||||
*/
|
||||
typedef struct {
|
||||
uint32_t min; ///< Minimum unsigned int value
|
||||
uint32_t max; ///< Maximum unsigned int value
|
||||
} isp_u32_range_t;
|
||||
|
||||
/**
|
||||
* @brief ISP float range type
|
||||
* @note Whether the edge value are included depends on the variable itself
|
||||
*/
|
||||
typedef struct {
|
||||
float min; ///< Minimum float value
|
||||
float max; ///< Maximum float value
|
||||
} isp_float_range_t;
|
||||
|
||||
/**
|
||||
* @brief ISP AF result
|
||||
*/
|
||||
typedef struct {
|
||||
int definition[ISP_AF_WINDOW_NUM]; ///< Definition, it refers how clear and sharp an image is
|
||||
int luminance[ISP_AF_WINDOW_NUM]; ///< Luminance, it refers how luminant an image is
|
||||
} isp_af_result_t;
|
||||
|
||||
/**
|
||||
* @brief ISP AWB result
|
||||
*/
|
||||
typedef struct {
|
||||
uint32_t white_patch_num; ///< white patch number that counted by AWB in the window
|
||||
uint32_t sum_r; ///< The sum of R channel of these white patches
|
||||
uint32_t sum_g; ///< The sum of G channel of these white patches
|
||||
uint32_t sum_b; ///< The sum of B channel of these white patches
|
||||
} isp_awb_stat_result_t;
|
||||
|
||||
/**
|
||||
* @brief Type of ISP processor handle
|
||||
*/
|
||||
@ -22,6 +58,11 @@ typedef struct isp_processor_t *isp_proc_handle_t;
|
||||
*/
|
||||
typedef struct isp_af_controller_t *isp_af_ctlr_t;
|
||||
|
||||
/**
|
||||
* @brief Type of ISP AWB controller handle
|
||||
*/
|
||||
typedef struct isp_awb_controller_t *isp_awb_ctlr_t;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
@ -64,6 +64,7 @@ typedef struct isp_processor_t {
|
||||
uint32_t v_res;
|
||||
/* sub module contexts */
|
||||
isp_af_ctlr_t af_ctlr[SOC_ISP_AF_CTLR_NUMS];
|
||||
isp_awb_ctlr_t awb_ctlr;
|
||||
isp_fsm_t bf_fsm;
|
||||
} isp_processor_t;
|
||||
#endif
|
||||
|
304
components/esp_driver_isp/src/isp_awb.c
Normal file
304
components/esp_driver_isp/src/isp_awb.c
Normal file
@ -0,0 +1,304 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <esp_types.h>
|
||||
#include <sys/lock.h>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "sdkconfig.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_check.h"
|
||||
#include "esp_heap_caps.h"
|
||||
#include "driver/isp_awb.h"
|
||||
#include "esp_private/isp_private.h"
|
||||
|
||||
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;
|
||||
esp_isp_awb_cbs_t cbs;
|
||||
void *user_data;
|
||||
} isp_awb_controller_t;
|
||||
|
||||
static const char *TAG = "ISP_AWB";
|
||||
|
||||
static void s_isp_awb_default_isr(void *arg);
|
||||
|
||||
/*---------------------------------------------
|
||||
AWB
|
||||
----------------------------------------------*/
|
||||
static esp_err_t s_isp_claim_awb_controller(isp_proc_handle_t isp_proc, isp_awb_ctlr_t awb_ctlr)
|
||||
{
|
||||
assert(isp_proc && awb_ctlr);
|
||||
|
||||
esp_err_t ret = ESP_ERR_NOT_FOUND;
|
||||
portENTER_CRITICAL(&isp_proc->spinlock);
|
||||
if (!isp_proc->awb_ctlr) {
|
||||
isp_proc->awb_ctlr = awb_ctlr;
|
||||
ret = ESP_OK;
|
||||
}
|
||||
portEXIT_CRITICAL(&isp_proc->spinlock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void s_isp_declaim_awb_controller(isp_awb_ctlr_t awb_ctlr)
|
||||
{
|
||||
if (awb_ctlr && awb_ctlr->isp_proc) {
|
||||
portENTER_CRITICAL(&awb_ctlr->isp_proc->spinlock);
|
||||
awb_ctlr->isp_proc->awb_ctlr = NULL;
|
||||
portEXIT_CRITICAL(&awb_ctlr->isp_proc->spinlock);
|
||||
}
|
||||
}
|
||||
|
||||
static void s_isp_awb_free_controller(isp_awb_ctlr_t awb_ctlr)
|
||||
{
|
||||
if (awb_ctlr) {
|
||||
if (awb_ctlr->intr_handle) {
|
||||
esp_intr_free(awb_ctlr->intr_handle);
|
||||
}
|
||||
if (awb_ctlr->evt_que) {
|
||||
vQueueDelete(awb_ctlr->evt_que);
|
||||
}
|
||||
if (awb_ctlr->stat_lock) {
|
||||
vSemaphoreDelete(awb_ctlr->stat_lock);
|
||||
}
|
||||
free(awb_ctlr);
|
||||
}
|
||||
}
|
||||
|
||||
static esp_err_t s_esp_isp_awb_config_hardware(isp_proc_handle_t isp_proc, const esp_isp_awb_config_t *awb_cfg)
|
||||
{
|
||||
isp_ll_awb_set_sample_point(isp_proc->hal.hw, awb_cfg->sample_point);
|
||||
ESP_RETURN_ON_FALSE(isp_hal_awb_set_window_range(&isp_proc->hal, &awb_cfg->window),
|
||||
ESP_ERR_INVALID_ARG, TAG, "invalid window");
|
||||
isp_u32_range_t lum_range = awb_cfg->white_patch.luminance;
|
||||
ESP_RETURN_ON_FALSE(isp_hal_awb_set_luminance_range(&isp_proc->hal, lum_range.min, lum_range.max),
|
||||
ESP_ERR_INVALID_ARG, TAG, "invalid luminance range");
|
||||
isp_float_range_t rg_range = awb_cfg->white_patch.red_green_ratio;
|
||||
ESP_RETURN_ON_FALSE(rg_range.min < rg_range.max && rg_range.min >= 0 &&
|
||||
isp_hal_awb_set_rg_ratio_range(&isp_proc->hal, rg_range.min, rg_range.max),
|
||||
ESP_ERR_INVALID_ARG, TAG, "invalid range of Red Green ratio");
|
||||
isp_float_range_t bg_range = awb_cfg->white_patch.blue_green_ratio;
|
||||
ESP_RETURN_ON_FALSE(bg_range.min < bg_range.max && bg_range.min >= 0 &&
|
||||
isp_hal_awb_set_bg_ratio_range(&isp_proc->hal, bg_range.min, bg_range.max),
|
||||
ESP_ERR_INVALID_ARG, TAG, "invalid range of Blue to Green ratio");
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t esp_isp_new_awb_controller(isp_proc_handle_t isp_proc, const esp_isp_awb_config_t *awb_cfg, isp_awb_ctlr_t *ret_hdl)
|
||||
{
|
||||
esp_err_t ret = ESP_FAIL;
|
||||
ESP_RETURN_ON_FALSE(isp_proc && awb_cfg && ret_hdl, ESP_ERR_INVALID_ARG, TAG, "invalid argument: null pointer");
|
||||
|
||||
isp_awb_ctlr_t awb_ctlr = heap_caps_calloc(1, sizeof(isp_awb_controller_t), ISP_MEM_ALLOC_CAPS);
|
||||
ESP_RETURN_ON_FALSE(awb_ctlr, ESP_ERR_NO_MEM, TAG, "no mem for awb controller");
|
||||
awb_ctlr->evt_que = xQueueCreateWithCaps(1, sizeof(isp_awb_stat_result_t), ISP_MEM_ALLOC_CAPS);
|
||||
ESP_GOTO_ON_FALSE(awb_ctlr->evt_que, ESP_ERR_NO_MEM, err1, TAG, "no mem for awb event queue");
|
||||
awb_ctlr->stat_lock = xSemaphoreCreateBinaryWithCaps(ISP_MEM_ALLOC_CAPS);
|
||||
ESP_GOTO_ON_FALSE(awb_ctlr->stat_lock, ESP_ERR_NO_MEM, err1, TAG, "no mem for awb semaphore");
|
||||
awb_ctlr->fsm = ISP_FSM_INIT;
|
||||
awb_ctlr->spinlock = (portMUX_TYPE)portMUX_INITIALIZER_UNLOCKED;
|
||||
awb_ctlr->isp_proc = isp_proc;
|
||||
|
||||
// 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");
|
||||
|
||||
// Configure the hardware
|
||||
isp_ll_awb_enable(isp_proc->hal.hw, false);
|
||||
isp_ll_awb_enable_algorithm_mode(isp_proc->hal.hw, true);
|
||||
ESP_GOTO_ON_ERROR(s_esp_isp_awb_config_hardware(isp_proc, awb_cfg), err2, TAG, "configure awb hardware failed");
|
||||
|
||||
*ret_hdl = awb_ctlr;
|
||||
|
||||
return ESP_OK;
|
||||
|
||||
err2:
|
||||
s_isp_declaim_awb_controller(awb_ctlr);
|
||||
err1:
|
||||
s_isp_awb_free_controller(awb_ctlr);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
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");
|
||||
s_isp_declaim_awb_controller(awb_ctlr);
|
||||
|
||||
isp_ll_awb_enable_algorithm_mode(awb_ctlr->isp_proc->hal.hw, false);
|
||||
s_isp_awb_free_controller(awb_ctlr);
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
esp_err_t esp_isp_awb_controller_enable(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->fsm == ISP_FSM_INIT, ESP_ERR_INVALID_STATE, TAG, "controller isn't in init state");
|
||||
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;
|
||||
}
|
||||
|
||||
esp_err_t esp_isp_awb_controller_disable(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->fsm == ISP_FSM_ENABLE, ESP_ERR_INVALID_STATE, TAG, "controller isn't in enable state");
|
||||
xSemaphoreTake(awb_ctlr->stat_lock, 0);
|
||||
awb_ctlr->fsm = ISP_FSM_INIT;
|
||||
|
||||
isp_ll_enable_intr(awb_ctlr->isp_proc->hal.hw, ISP_LL_EVENT_AWB_MASK, false);
|
||||
isp_ll_awb_clk_enable(awb_ctlr->isp_proc->hal.hw, false);
|
||||
esp_intr_disable(awb_ctlr->intr_handle);
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t esp_isp_awb_controller_get_oneshot_statistics(isp_awb_ctlr_t awb_ctlr, int timeout_ms, isp_awb_stat_result_t *out_res)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(awb_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);
|
||||
xSemaphoreTake(awb_ctlr->stat_lock, ticks);
|
||||
ESP_GOTO_ON_FALSE(awb_ctlr->fsm == ISP_FSM_ENABLE, ESP_ERR_INVALID_STATE, err, TAG, "controller isn't in enable state");
|
||||
// Update state to avoid race condition
|
||||
awb_ctlr->fsm = ISP_FSM_START;
|
||||
// Reset the queue in case receiving the legacy data in the queue
|
||||
xQueueReset(awb_ctlr->evt_que);
|
||||
// Start the AWB white patch statistics and waiting it done
|
||||
isp_ll_awb_enable(awb_ctlr->isp_proc->hal.hw, true);
|
||||
// Wait the statistics to finish and receive the result from the queue
|
||||
if ((ticks > 0) && xQueueReceive(awb_ctlr->evt_que, out_res, ticks) != pdTRUE) {
|
||||
ret = ESP_ERR_TIMEOUT;
|
||||
}
|
||||
// Stop the AWB white patch statistics
|
||||
isp_ll_awb_enable(awb_ctlr->isp_proc->hal.hw, false);
|
||||
awb_ctlr->fsm = ISP_FSM_ENABLE;
|
||||
err:
|
||||
xSemaphoreGive(awb_ctlr->stat_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t esp_isp_awb_controller_start_continuous_statistics(isp_awb_ctlr_t awb_ctlr)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(awb_ctlr, ESP_ERR_INVALID_ARG, TAG, "invalid argument: null pointer");
|
||||
ESP_RETURN_ON_FALSE(awb_ctlr->cbs.on_statistics_done, ESP_ERR_INVALID_STATE, TAG, "invalid state: on_statistics_done callback not registered");
|
||||
esp_err_t ret = ESP_OK;
|
||||
if (xSemaphoreTake(awb_ctlr->stat_lock, 0) == pdFALSE) {
|
||||
ESP_LOGW(TAG, "statistics lock is not acquired, controller is busy");
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
ESP_GOTO_ON_FALSE(awb_ctlr->fsm == ISP_FSM_ENABLE, ESP_ERR_INVALID_STATE, err, TAG, "controller isn't in enable state");
|
||||
awb_ctlr->fsm = ISP_FSM_START;
|
||||
isp_ll_awb_enable(awb_ctlr->isp_proc->hal.hw, true);
|
||||
|
||||
return ret;
|
||||
err:
|
||||
xSemaphoreGive(awb_ctlr->stat_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t esp_isp_awb_controller_stop_continuous_statistics(isp_awb_ctlr_t awb_ctlr)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(awb_ctlr, ESP_ERR_INVALID_ARG, TAG, "invalid argument: null pointer");
|
||||
ESP_RETURN_ON_FALSE(awb_ctlr->fsm == ISP_FSM_START, ESP_ERR_INVALID_STATE, TAG, "controller isn't in continuous state");
|
||||
|
||||
isp_ll_awb_enable(awb_ctlr->isp_proc->hal.hw, false);
|
||||
awb_ctlr->fsm = ISP_FSM_ENABLE;
|
||||
xSemaphoreGive(awb_ctlr->stat_lock);
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
/*---------------------------------------------------------------
|
||||
INTR
|
||||
---------------------------------------------------------------*/
|
||||
static void IRAM_ATTR s_isp_awb_default_isr(void *arg)
|
||||
{
|
||||
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) {
|
||||
isp_awb_ctlr_t awb_ctlr = proc->awb_ctlr;
|
||||
// Get the statistics result
|
||||
esp_isp_awb_evt_data_t edata = {
|
||||
.awb_result = {
|
||||
.white_patch_num = isp_ll_awb_get_white_patch_cnt(proc->hal.hw),
|
||||
.sum_r = isp_ll_awb_get_accumulated_r_value(proc->hal.hw),
|
||||
.sum_g = isp_ll_awb_get_accumulated_g_value(proc->hal.hw),
|
||||
.sum_b = isp_ll_awb_get_accumulated_b_value(proc->hal.hw),
|
||||
},
|
||||
};
|
||||
// Invoke the callback if the callback is registered
|
||||
if (awb_ctlr->cbs.on_statistics_done) {
|
||||
need_yield |= awb_ctlr->cbs.on_statistics_done(awb_ctlr, &edata, awb_ctlr->user_data);
|
||||
}
|
||||
BaseType_t high_task_awake = false;
|
||||
// Send the event data to the queue, overwrite the legacy one if exist
|
||||
xQueueOverwriteFromISR(awb_ctlr->evt_que, &edata.awb_result, &high_task_awake);
|
||||
need_yield |= high_task_awake == pdTRUE;
|
||||
/* If started continuous sampling, then trigger the next AWB sample */
|
||||
if (awb_ctlr->fsm == ISP_FSM_START) {
|
||||
isp_ll_awb_enable(awb_ctlr->isp_proc->hal.hw, false);
|
||||
isp_ll_awb_enable(awb_ctlr->isp_proc->hal.hw, true);
|
||||
}
|
||||
}
|
||||
|
||||
if (need_yield) {
|
||||
portYIELD_FROM_ISR();
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(awb_ctlr && cbs, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
|
||||
ESP_RETURN_ON_FALSE(awb_ctlr->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_statistics_done) {
|
||||
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
|
||||
awb_ctlr->cbs.on_statistics_done = cbs->on_statistics_done;
|
||||
awb_ctlr->user_data = user_data;
|
||||
|
||||
return ESP_OK;
|
||||
}
|
@ -56,3 +56,65 @@ TEST_CASE("ISP AF controller exhausted allocation", "[isp]")
|
||||
}
|
||||
TEST_ESP_OK(esp_isp_del_processor(isp_proc));
|
||||
}
|
||||
|
||||
static bool test_isp_awb_default_on_statistics_done_cb(isp_awb_ctlr_t awb_ctlr, const esp_isp_awb_evt_data_t *edata, void *user_data)
|
||||
{
|
||||
(void) awb_ctlr;
|
||||
(void) edata;
|
||||
(void) user_data;
|
||||
// Do nothing
|
||||
return false;
|
||||
}
|
||||
|
||||
TEST_CASE("ISP AWB 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_awb_ctlr_t awb_ctlr = NULL;
|
||||
uint32_t image_width = 800;
|
||||
uint32_t image_height = 600;
|
||||
/* Default parameters from helper macro */
|
||||
esp_isp_awb_config_t awb_config = {
|
||||
.sample_point = ISP_AWB_SAMPLE_POINT_AFTER_CCM,
|
||||
.window = {
|
||||
.top_left = {.x = image_width * 0.2, .y = image_height * 0.2},
|
||||
.btm_right = {.x = image_width * 0.8, .y = image_height * 0.8},
|
||||
},
|
||||
.white_patch = {
|
||||
.luminance = {.min = 0, .max = 220 * 3},
|
||||
.red_green_ratio = {.min = 0.0f, .max = 3.999f},
|
||||
.blue_green_ratio = {.min = 0.0f, .max = 3.999f},
|
||||
},
|
||||
};
|
||||
isp_awb_stat_result_t stat_res = {};
|
||||
/* Create the awb controller */
|
||||
TEST_ESP_OK(esp_isp_new_awb_controller(isp_proc, &awb_config, &awb_ctlr));
|
||||
/* Register AWB callback */
|
||||
esp_isp_awb_cbs_t awb_cb = {
|
||||
.on_statistics_done = test_isp_awb_default_on_statistics_done_cb,
|
||||
};
|
||||
TEST_ESP_OK(esp_isp_awb_register_event_callbacks(awb_ctlr, &awb_cb, NULL));
|
||||
/* Enabled the awb controller */
|
||||
TEST_ESP_OK(esp_isp_awb_controller_enable(awb_ctlr));
|
||||
/* Start continuous AWB statistics */
|
||||
TEST_ESP_OK(esp_isp_awb_controller_start_continuous_statistics(awb_ctlr));
|
||||
TEST_ESP_ERR(ESP_ERR_INVALID_STATE, esp_isp_awb_controller_get_oneshot_statistics(awb_ctlr, 0, &stat_res));
|
||||
/* Stop continuous AWB statistics */
|
||||
TEST_ESP_OK(esp_isp_awb_controller_stop_continuous_statistics(awb_ctlr));
|
||||
TEST_ESP_ERR(ESP_ERR_TIMEOUT, esp_isp_awb_controller_get_oneshot_statistics(awb_ctlr, 1, &stat_res));
|
||||
/* Disable the awb controller */
|
||||
TEST_ESP_OK(esp_isp_awb_controller_disable(awb_ctlr));
|
||||
/* Delete the awb controller and free the resources */
|
||||
TEST_ESP_OK(esp_isp_del_awb_controller(awb_ctlr));
|
||||
|
||||
TEST_ESP_OK(esp_isp_disable(isp_proc));
|
||||
TEST_ESP_OK(esp_isp_del_processor(isp_proc));
|
||||
}
|
||||
|
@ -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_AWB_MASK (ISP_LL_EVENT_AWB_FDONE)
|
||||
|
||||
/*---------------------------------------------------------------
|
||||
AF
|
||||
@ -83,6 +84,22 @@ extern "C" {
|
||||
#define ISP_LL_DVP_DATA_TYPE_RAW10 0x2B
|
||||
#define ISP_LL_DVP_DATA_TYPE_RAW12 0x2C
|
||||
|
||||
/*---------------------------------------------------------------
|
||||
AWB
|
||||
---------------------------------------------------------------*/
|
||||
#define ISP_LL_AWB_WINDOW_MAX_RANGE ((1<<12) - 1)
|
||||
#define ISP_LL_AWB_LUM_MAX_RANGE ((1<<10) - 1)
|
||||
#define ISP_LL_AWB_RGB_RATIO_INT_BITS (2)
|
||||
#define ISP_LL_AWB_RGB_RATIO_FRAC_BITS (8)
|
||||
|
||||
typedef union {
|
||||
struct {
|
||||
uint32_t fraction: ISP_LL_AWB_RGB_RATIO_FRAC_BITS;
|
||||
uint32_t integer: ISP_LL_AWB_RGB_RATIO_INT_BITS;
|
||||
};
|
||||
uint32_t val;
|
||||
} isp_ll_awb_rgb_ratio_t;
|
||||
|
||||
/**
|
||||
* @brief Env monitor mode
|
||||
*/
|
||||
@ -992,6 +1009,164 @@ static inline void isp_ll_clear_intr(isp_dev_t *hw, uint32_t mask)
|
||||
hw->int_clr.val = mask;
|
||||
}
|
||||
|
||||
/*---------------------------------------------------------------
|
||||
AWB
|
||||
---------------------------------------------------------------*/
|
||||
/**
|
||||
* @brief Enable / Disable AWB clock
|
||||
*
|
||||
* @param[in] hw Hardware instance address
|
||||
* @param[in] enable Enable / Disable
|
||||
*/
|
||||
static inline void isp_ll_awb_clk_enable(isp_dev_t *hw, bool enable)
|
||||
{
|
||||
hw->clk_en.clk_awb_force_on = enable;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Enable AWB statistics
|
||||
*
|
||||
* @param[in] hw Hardware instance address
|
||||
* @param[in] enable Enable / Disable
|
||||
*/
|
||||
__attribute__((always_inline))
|
||||
static inline void isp_ll_awb_enable(isp_dev_t *hw, bool enable)
|
||||
{
|
||||
hw->cntl.awb_en = enable;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set AWB sample point
|
||||
*
|
||||
* @param[in] hw Hardware instance address
|
||||
* @param[in] point Sample point
|
||||
* - 0: Before CCM
|
||||
* - 1: After CCM
|
||||
*/
|
||||
static inline void isp_ll_awb_set_sample_point(isp_dev_t *hw, isp_awb_sample_point_t point)
|
||||
{
|
||||
hw->awb_mode.awb_sample = point;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set AWB algorithm mode
|
||||
*
|
||||
* @param[in] hw Hardware instance address
|
||||
* @param[in] enable Enable algorithm mode 1
|
||||
*/
|
||||
static inline void isp_ll_awb_enable_algorithm_mode(isp_dev_t *hw, bool enable)
|
||||
{
|
||||
hw->awb_mode.awb_mode = enable;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set AWB window range
|
||||
*
|
||||
* @param[in] hw Hardware instance address
|
||||
* @param[in] top_left_x Top left pixel x axis value
|
||||
* @param[in] top_left_y Top left pixel y axis value
|
||||
* @param[in] bottom_right_x Bottom right pixel x axis value
|
||||
* @param[in] bottom_right_y Bottom right pixel y axis value
|
||||
*/
|
||||
static inline void isp_ll_awb_set_window_range(isp_dev_t *hw, uint32_t top_left_x, uint32_t top_left_y, uint32_t bottom_right_x, uint32_t bottom_right_y)
|
||||
{
|
||||
hw->awb_hscale.awb_lpoint = top_left_x;
|
||||
hw->awb_vscale.awb_tpoint = top_left_y;
|
||||
hw->awb_hscale.awb_rpoint = bottom_right_x;
|
||||
hw->awb_vscale.awb_bpoint = bottom_right_y;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set AWB luminance range
|
||||
*
|
||||
* @param[in] hw Hardware instance address
|
||||
* @param[in] min Minimum luminance
|
||||
* @param[in] max Maximum luminance
|
||||
*/
|
||||
static inline void isp_ll_awb_set_luminance_range(isp_dev_t *hw, uint32_t min, uint32_t max)
|
||||
{
|
||||
hw->awb_th_lum.awb_min_lum = min;
|
||||
hw->awb_th_lum.awb_max_lum = max;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set AWB R/G ratio range
|
||||
*
|
||||
* @param[in] hw Hardware instance address
|
||||
* @param[in] min Minimum R/G ratio in fixed-point data type
|
||||
* @param[in] max Maximum R/G ratio in fixed-point data type
|
||||
*/
|
||||
static inline void isp_ll_awb_set_rg_ratio_range(isp_dev_t *hw, isp_ll_awb_rgb_ratio_t min, isp_ll_awb_rgb_ratio_t max)
|
||||
{
|
||||
hw->awb_th_rg.awb_min_rg = min.val;
|
||||
hw->awb_th_rg.awb_max_rg = max.val;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set AWB B/G ratio range
|
||||
*
|
||||
* @param[in] hw Hardware instance address
|
||||
* @param[in] min Minimum B/G ratio in fixed-point data type
|
||||
* @param[in] max Maximum B/G ratio in fixed-point data type
|
||||
*/
|
||||
static inline void isp_ll_awb_set_bg_ratio_range(isp_dev_t *hw, isp_ll_awb_rgb_ratio_t min, isp_ll_awb_rgb_ratio_t max)
|
||||
{
|
||||
hw->awb_th_bg.awb_min_bg = min.val;
|
||||
hw->awb_th_bg.awb_max_bg = max.val;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get AWB white patch count
|
||||
*
|
||||
* @param[in] hw Hardware instance address
|
||||
* @return
|
||||
* - white patch count
|
||||
*/
|
||||
__attribute__((always_inline))
|
||||
static inline uint32_t isp_ll_awb_get_white_patch_cnt(isp_dev_t *hw)
|
||||
{
|
||||
return hw->awb0_white_cnt.awb0_white_cnt;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get AWB accumulated R value of white patches
|
||||
*
|
||||
* @param[in] hw Hardware instance address
|
||||
* @return
|
||||
* - Accumulated R value of white patches
|
||||
*/
|
||||
__attribute__((always_inline))
|
||||
static inline uint32_t isp_ll_awb_get_accumulated_r_value(isp_dev_t *hw)
|
||||
{
|
||||
return hw->awb0_acc_r.awb0_acc_r;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get AWB accumulated G value of white patches
|
||||
*
|
||||
* @param[in] hw Hardware instance address
|
||||
* @return
|
||||
* - Accumulated G value of white patches
|
||||
*/
|
||||
__attribute__((always_inline))
|
||||
static inline uint32_t isp_ll_awb_get_accumulated_g_value(isp_dev_t *hw)
|
||||
{
|
||||
return hw->awb0_acc_g.awb0_acc_g;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get AWB accumulated B value of white patches
|
||||
*
|
||||
* @param[in] hw Hardware instance address
|
||||
* @return
|
||||
* - Accumulated B value of white patches
|
||||
*/
|
||||
__attribute__((always_inline))
|
||||
static inline uint32_t isp_ll_awb_get_accumulated_b_value(isp_dev_t *hw)
|
||||
{
|
||||
return hw->awb0_acc_b.awb0_acc_b;
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
@ -7,6 +7,14 @@
|
||||
#include "hal/hal_utils.h"
|
||||
#include "hal/assert.h"
|
||||
|
||||
#ifndef BIT
|
||||
#define BIT(n) (1UL << (n))
|
||||
#endif
|
||||
|
||||
#ifndef BIT_MASK
|
||||
#define BIT_MASK(n) (BIT(n) - 1)
|
||||
#endif
|
||||
|
||||
__attribute__((always_inline))
|
||||
static inline uint32_t _sub_abs(uint32_t a, uint32_t b)
|
||||
{
|
||||
@ -131,3 +139,56 @@ uint32_t hal_utils_calc_clk_div_integer(const hal_utils_clk_info_t *clk_info, ui
|
||||
// Return the actual frequency
|
||||
return clk_info->src_freq_hz / div_integ;
|
||||
}
|
||||
|
||||
typedef union {
|
||||
struct {
|
||||
uint32_t mantissa: 23;
|
||||
uint32_t exponent: 8;
|
||||
uint32_t sign: 1;
|
||||
};
|
||||
uint32_t val;
|
||||
} hal_utils_ieee754_float_t;
|
||||
|
||||
int hal_utils_float_to_fixed_point_32b(float flt, const hal_utils_fixed_point_t *fp_cfg, uint32_t *fp_out)
|
||||
{
|
||||
int ret = 0;
|
||||
uint32_t output = 0;
|
||||
const hal_utils_ieee754_float_t *f = (const hal_utils_ieee754_float_t *)&flt;
|
||||
if (fp_cfg->int_bit + fp_cfg->frac_bit > 31) {
|
||||
// Not supported
|
||||
return -3;
|
||||
}
|
||||
|
||||
if (f->val == 0) { // Zero case
|
||||
*fp_out = 0;
|
||||
return 0;
|
||||
}
|
||||
if (f->exponent != 0xFF) { // Normal case
|
||||
int real_exp = (int)f->exponent - 127;
|
||||
uint32_t real_mant = f->mantissa | BIT(23); // Add the hidden bit
|
||||
// Overflow check
|
||||
if (real_exp >= (int)fp_cfg->int_bit) {
|
||||
ret = -1;
|
||||
}
|
||||
// Determine sign
|
||||
output |= f->sign << (fp_cfg->int_bit + fp_cfg->frac_bit);
|
||||
// Determine integer and fraction part
|
||||
int shift = 23 - fp_cfg->frac_bit - real_exp;
|
||||
output |= shift >= 0 ? real_mant >> shift : real_mant << -shift;
|
||||
} else {
|
||||
if (f->mantissa && f->mantissa < BIT(23) - 1) { // NaN (Not-a-Number) case
|
||||
return -2;
|
||||
} else { // Infinity or Largest Number case
|
||||
output = f->sign ? ~(uint32_t)0 : BIT(31) - 1;
|
||||
ret = -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (ret != 0 && fp_cfg->saturation) {
|
||||
*fp_out = (f->sign << (fp_cfg->int_bit + fp_cfg->frac_bit)) |
|
||||
(BIT_MASK(fp_cfg->int_bit + fp_cfg->frac_bit));
|
||||
} else {
|
||||
*fp_out = output;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
@ -7,6 +7,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
@ -148,6 +149,38 @@ static inline uint32_t hal_utils_calc_lcm(uint32_t a, uint32_t b)
|
||||
return (a * b / hal_utils_gcd(a, b));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Fixed-point data configuration
|
||||
*
|
||||
*/
|
||||
typedef struct {
|
||||
uint32_t int_bit; /*!< Integer bit of the fixed point */
|
||||
uint32_t frac_bit; /*!< Fractional bit of the fixed point */
|
||||
bool saturation; /*!< Whether to limit the value to the maximum when fixed-point data overflow.
|
||||
* When set true, the value will be limited to the maximum when the float type data is out of range.
|
||||
* When set false, the function will return false when the float type data is out of range.
|
||||
*/
|
||||
} hal_utils_fixed_point_t;
|
||||
|
||||
/**
|
||||
* @brief Convert the float type to fixed point type
|
||||
* @note The supported data format:
|
||||
* - [input] float (IEEE 754):
|
||||
* sign(1bit) + exponent(8bit) + mantissa(23bit) (32 bit in total)
|
||||
* - [output] fixed-point:
|
||||
* sign(1bit) + integer(int_bit) + fraction(frac_bit) (less or equal to 32 bit)
|
||||
*
|
||||
* @param[in] flt IEEE 754 float type data
|
||||
* @param[in] fp_cfg Fixed-point data configuration
|
||||
* @param[out] fp_out The output fixed-point data
|
||||
* @return
|
||||
* 0: Success
|
||||
* -1: Fixed point data overflow, `fp_out` will still be assigned
|
||||
* -2: Float is NaN
|
||||
* -3: Invalid configuration
|
||||
*/
|
||||
int hal_utils_float_to_fixed_point_32b(float flt, const hal_utils_fixed_point_t *fp_cfg, uint32_t *fp_out);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
@ -13,7 +13,9 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include "hal/isp_types.h"
|
||||
#include "hal/hal_utils.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
@ -60,7 +62,7 @@ void isp_hal_init(isp_hal_context_t *hal, int isp_id);
|
||||
*
|
||||
* @param[in] hal Context of the HAL layer
|
||||
* @param[in] window_id Window ID
|
||||
* @param[in] window Window info, see `isp_af_window_t`
|
||||
* @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);
|
||||
|
||||
@ -86,6 +88,54 @@ uint32_t isp_hal_check_clear_intr_event(const isp_hal_context_t *hal, uint32_t m
|
||||
*/
|
||||
void isp_hal_bf_config(isp_hal_context_t *hal, isp_hal_bf_cfg_t *config);
|
||||
|
||||
/*---------------------------------------------------------------
|
||||
AWB
|
||||
---------------------------------------------------------------*/
|
||||
/**
|
||||
* @brief Set the window of the AWB
|
||||
*
|
||||
* @param[in] hal Context of the HAL layer
|
||||
* @param[in] win Pointer to the window of the AWB
|
||||
* @return
|
||||
* - true Set success
|
||||
* - false Invalid arg
|
||||
*/
|
||||
bool isp_hal_awb_set_window_range(const isp_hal_context_t *hal, const isp_window_t *win);
|
||||
|
||||
/**
|
||||
* @brief Set the luminance range of the white patch
|
||||
*
|
||||
* @param[in] hal Context of the HAL layer
|
||||
* @param[in] lum_min Minimum luminance
|
||||
* @param[in] lum_max Maximum luminance
|
||||
* @return
|
||||
* - 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);
|
||||
|
||||
/**
|
||||
* @brief Set the R/G ratio of the white patch
|
||||
*
|
||||
* @param[in] hal Context of the HAL layer
|
||||
* @param[in] rg_ratio_range Range of Red to Green ratio
|
||||
* @return
|
||||
* - 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);
|
||||
|
||||
/**
|
||||
* @brief Set the B/R ratio of the white patch
|
||||
*
|
||||
* @param[in] hal Context of the HAL layer
|
||||
* @param[in] bg_ratio_range Range of Blue to Green ratio
|
||||
* @return
|
||||
* - 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);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
@ -81,14 +81,6 @@ typedef enum {
|
||||
#define ISP_AF_WINDOW_NUM 0
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief ISP AF result
|
||||
*/
|
||||
typedef struct {
|
||||
int definition[ISP_AF_WINDOW_NUM]; ///< Definition, it refers how clear and sharp an image is
|
||||
int luminance[ISP_AF_WINDOW_NUM]; ///< Luminance, it refers how luminant an image is
|
||||
} isp_af_result_t;
|
||||
|
||||
/*---------------------------------------------------------------
|
||||
BF
|
||||
---------------------------------------------------------------*/
|
||||
@ -108,6 +100,20 @@ typedef enum {
|
||||
ISP_BF_EDGE_PADDING_MODE_CUSTOM_DATA, ///< Fill BF edge padding data with custom pixel data
|
||||
} isp_bf_edge_padding_mode_t;
|
||||
|
||||
/*---------------------------------------------------------------
|
||||
AWB
|
||||
---------------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
* @brief ISP AWB sample point in the ISP pipeline
|
||||
*
|
||||
*/
|
||||
typedef enum {
|
||||
ISP_AWB_SAMPLE_POINT_BEFORE_CCM, ///< Sample AWB data before CCM (Color Correction Matrix)
|
||||
ISP_AWB_SAMPLE_POINT_AFTER_CCM, ///< Sample AWB data after CCM (Color Correction Matrix)
|
||||
} isp_awb_sample_point_t;
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
@ -4,6 +4,7 @@
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <sys/param.h>
|
||||
#include <string.h>
|
||||
#include "sdkconfig.h"
|
||||
@ -13,6 +14,7 @@
|
||||
#include "hal/isp_hal.h"
|
||||
#include "hal/isp_ll.h"
|
||||
#include "hal/isp_types.h"
|
||||
#include "hal/hal_utils.h"
|
||||
|
||||
/**
|
||||
* ISP HAL layer
|
||||
@ -68,3 +70,71 @@ uint32_t isp_hal_check_clear_intr_event(const isp_hal_context_t *hal, uint32_t m
|
||||
|
||||
return triggered_events;
|
||||
}
|
||||
/*---------------------------------------------------------------
|
||||
AWB
|
||||
---------------------------------------------------------------*/
|
||||
bool isp_hal_awb_set_window_range(const 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 ||
|
||||
win->btm_right.x > ISP_LL_AWB_WINDOW_MAX_RANGE ||
|
||||
win->btm_right.y > ISP_LL_AWB_WINDOW_MAX_RANGE) {
|
||||
return false;
|
||||
}
|
||||
isp_ll_awb_set_window_range(hal->hw, win->top_left.x, win->top_left.y,
|
||||
win->btm_right.x, win->btm_right.y);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool isp_hal_awb_set_luminance_range(const 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;
|
||||
}
|
||||
isp_ll_awb_set_luminance_range(hal->hw, lum_min, lum_max);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool isp_hal_awb_set_rg_ratio_range(const isp_hal_context_t *hal, float rg_min, float rg_max)
|
||||
{
|
||||
// Convert to fixed point
|
||||
isp_ll_awb_rgb_ratio_t fp_rg_min = {};
|
||||
isp_ll_awb_rgb_ratio_t fp_rg_max = {};
|
||||
hal_utils_fixed_point_t fp_cfg = {
|
||||
.int_bit = ISP_LL_AWB_RGB_RATIO_INT_BITS,
|
||||
.frac_bit = ISP_LL_AWB_RGB_RATIO_FRAC_BITS,
|
||||
.saturation = false,
|
||||
};
|
||||
if (hal_utils_float_to_fixed_point_32b(rg_min, &fp_cfg, &fp_rg_min.val) != 0) {
|
||||
return false;
|
||||
}
|
||||
if (hal_utils_float_to_fixed_point_32b(rg_max, &fp_cfg, &fp_rg_max.val) != 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Set AWB white patch R/G ratio range
|
||||
isp_ll_awb_set_rg_ratio_range(hal->hw, fp_rg_min, fp_rg_max);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool isp_hal_awb_set_bg_ratio_range(const isp_hal_context_t *hal, float bg_min, float bg_max)
|
||||
{
|
||||
// Convert to fixed point
|
||||
isp_ll_awb_rgb_ratio_t fp_bg_min = {};
|
||||
isp_ll_awb_rgb_ratio_t fp_bg_max = {};
|
||||
hal_utils_fixed_point_t fp_cfg = {
|
||||
.int_bit = ISP_LL_AWB_RGB_RATIO_INT_BITS,
|
||||
.frac_bit = ISP_LL_AWB_RGB_RATIO_FRAC_BITS,
|
||||
.saturation = false,
|
||||
};
|
||||
if (hal_utils_float_to_fixed_point_32b(bg_min, &fp_cfg, &fp_bg_min.val) != 0) {
|
||||
return false;
|
||||
}
|
||||
if (hal_utils_float_to_fixed_point_32b(bg_max, &fp_cfg, &fp_bg_max.val) != 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Set AWB white patch B/G ratio range
|
||||
isp_ll_awb_set_bg_ratio_range(hal->hw, fp_bg_min, fp_bg_max);
|
||||
return true;
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
idf_component_register(SRCS "test_app_main.c"
|
||||
"test_fmt_convert.c"
|
||||
"test_calc_clk_div.c"
|
||||
"test_hal_utils_misc.c"
|
||||
INCLUDE_DIRS "."
|
||||
|
94
components/hal/test_apps/hal_utils/main/test_fmt_convert.c
Normal file
94
components/hal/test_apps/hal_utils/main/test_fmt_convert.c
Normal file
@ -0,0 +1,94 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <math.h>
|
||||
#include "unity.h"
|
||||
#include "hal/hal_utils.h"
|
||||
|
||||
#ifndef BIT
|
||||
#define BIT(n) (1UL << (n))
|
||||
#endif
|
||||
|
||||
TEST_CASE("test_float_to_fixed_point", "[fmt_convert]")
|
||||
{
|
||||
float f_nan = NAN;
|
||||
float f_inf = INFINITY;
|
||||
float f0 = 100.9f;
|
||||
float f_zero = 0;
|
||||
float f_precise = 0.5f;
|
||||
float f_int = 2.0f;
|
||||
float f_frac = 1.0f / 3.0f;
|
||||
float f_dec = 1.453f;
|
||||
float f_neg = -1.25f;
|
||||
uint32_t out = 0;
|
||||
hal_utils_fixed_point_t fp_cfg = {
|
||||
.int_bit = 24,
|
||||
.frac_bit = 8,
|
||||
.saturation = false,
|
||||
};
|
||||
|
||||
// Invalid arguments case
|
||||
TEST_ASSERT_EQUAL_INT(-3, hal_utils_float_to_fixed_point_32b(f_dec, &fp_cfg, &out));
|
||||
printf("Invalid arguments case passed!\n");
|
||||
fp_cfg.int_bit = 2;
|
||||
|
||||
// Overflow case
|
||||
TEST_ASSERT_EQUAL_INT(-1, hal_utils_float_to_fixed_point_32b(f0, &fp_cfg, &out));
|
||||
TEST_ASSERT_EQUAL_UINT32(0x64E6, out); // integ: 0x64 = 100, frac: 0xE6 / 0x100 = 0.8984375
|
||||
printf("Overflow case passed!\n");
|
||||
|
||||
// Not-a-Number case
|
||||
TEST_ASSERT_EQUAL_INT(-2, hal_utils_float_to_fixed_point_32b(f_nan, &fp_cfg, &out));
|
||||
printf("Not-a-Number case passed!\n");
|
||||
|
||||
// Infinity case
|
||||
TEST_ASSERT_EQUAL_INT(-1, hal_utils_float_to_fixed_point_32b(f_inf, &fp_cfg, &out));
|
||||
TEST_ASSERT_EQUAL_UINT32(BIT(31) - 1, out);
|
||||
printf("Infinity case passed!\n");
|
||||
|
||||
fp_cfg.saturation = true;
|
||||
// Limit overflow case
|
||||
TEST_ASSERT_EQUAL_INT(-1, hal_utils_float_to_fixed_point_32b(f0, &fp_cfg, &out));
|
||||
TEST_ASSERT_EQUAL_UINT32(BIT(10) - 1, out); // Limit to the maximum value, integ: 0x03 = 3 | frac: 0xff / 0x100 = 0.99609375
|
||||
printf("Limit overflow case passed!\n");
|
||||
|
||||
// Zero case
|
||||
TEST_ASSERT_EQUAL_INT(0, hal_utils_float_to_fixed_point_32b(f_zero, &fp_cfg, &out));
|
||||
TEST_ASSERT_EQUAL_UINT32(0, out); // Special case, 0 = 0
|
||||
printf("Zero case passed!\n");
|
||||
|
||||
// Precision case
|
||||
TEST_ASSERT_EQUAL_INT(0, hal_utils_float_to_fixed_point_32b(f_precise, &fp_cfg, &out));
|
||||
TEST_ASSERT_EQUAL_UINT32(BIT(7), out); // frac: 0x80 / 0x100 = 0.5
|
||||
printf("Precision case passed!\n");
|
||||
|
||||
// Integer case
|
||||
TEST_ASSERT_EQUAL_INT(0, hal_utils_float_to_fixed_point_32b(f_int, &fp_cfg, &out));
|
||||
TEST_ASSERT_EQUAL_UINT32(BIT(9), out); // integ: 2 | frac: 0x00 / 0x100 = 0
|
||||
printf("Integer case passed!\n");
|
||||
|
||||
// Fraction case
|
||||
TEST_ASSERT_EQUAL_INT(0, hal_utils_float_to_fixed_point_32b(f_frac, &fp_cfg, &out));
|
||||
TEST_ASSERT_EQUAL_UINT32(0x055, out); // 0x55 / 0x100 = 0.33203125
|
||||
printf("Fraction case passed!\n");
|
||||
|
||||
// Decimal case
|
||||
TEST_ASSERT_EQUAL_INT(0, hal_utils_float_to_fixed_point_32b(f_dec, &fp_cfg, &out));
|
||||
TEST_ASSERT_EQUAL_UINT32(0x173, out); // integ: 0x01 = 1, frac: 0x73 / 0x100 = 0.44921875
|
||||
printf("Decimal case passed!\n");
|
||||
|
||||
// Negative case
|
||||
TEST_ASSERT_EQUAL_INT(0, hal_utils_float_to_fixed_point_32b(f_neg, &fp_cfg, &out));
|
||||
TEST_ASSERT_EQUAL_UINT32(BIT(10) | BIT(8) | (BIT(6)), out); // sign: 1 | integ: 1 | frac: 0x40 / 0x100 = 0.25
|
||||
printf("Negative case passed!\n");
|
||||
|
||||
fp_cfg.int_bit = 8;
|
||||
// Integer bits case
|
||||
TEST_ASSERT_EQUAL_INT(0, hal_utils_float_to_fixed_point_32b(f0, &fp_cfg, &out));
|
||||
TEST_ASSERT_EQUAL_UINT32(0x64E6, out); // integ: 0x64 = 100, frac: 0xE6 / 0x100 = 0.8984375
|
||||
printf("Integer bits case passed!\n");
|
||||
}
|
@ -4,7 +4,7 @@ Image Signal Processor
|
||||
Introduction
|
||||
------------
|
||||
|
||||
{IDF_TARGET_NAME} includes an image signal processor (ISP), which is a feature pipeline that consists of many image processing algorithms. ISP receives image data from the DVP camera or MIPI-CSI camera, or system memory, and writes the processed image data to the system memory through DMA. ISP shall work with other modules to read and write data, it can not work alone.
|
||||
{IDF_TARGET_NAME} includes an Image Signal Processor (ISP), which is a feature pipeline that consists of many image processing algorithms. ISP receives image data from the DVP camera or MIPI-CSI camera, or system memory, and writes the processed image data to the system memory through DMA. ISP shall work with other modules to read and write data, it can not work alone.
|
||||
|
||||
Terminology
|
||||
-----------
|
||||
@ -16,6 +16,39 @@ Terminology
|
||||
- RGB: Colored image format composed of red, green, and blue colors classified into RGB888, RGB565, etc., based on the bit width of each color
|
||||
- YUV: Colored image format composed of luminance and chrominance classified into YUV444, YUV422, YUV420, etc., based on the data arrangement
|
||||
- AF: Auto-focus
|
||||
- AWB: Auto-white balance
|
||||
|
||||
ISP Pipeline
|
||||
------------
|
||||
|
||||
.. blockdiag::
|
||||
:scale: 100%
|
||||
:caption: ISP Pipeline
|
||||
:align: center
|
||||
|
||||
blockdiag isp_pipeline {
|
||||
orientation = portrait;
|
||||
node_height = 30;
|
||||
node_width = 120;
|
||||
span_width = 100;
|
||||
default_fontsize = 16;
|
||||
|
||||
isp_header [label = "ISP Header"];
|
||||
isp_tail [label = "ISP Tail"];
|
||||
isp_chs [label = "Contrast &\n Hue & Saturation", width = 150, height = 70];
|
||||
isp_yuv [label = "YUV Limit\nYUB2RGB", width = 120, height = 70];
|
||||
|
||||
isp_header -> BF -> Demosaic -> CCM -> RGB2YUV -> isp_chs -> isp_yuv -> isp_tail;
|
||||
|
||||
BF -> HIST
|
||||
Demosaic -> AWB
|
||||
Demosaic -> AE
|
||||
Demosaic -> HIST
|
||||
CCM -> AWB
|
||||
CCM -> AE
|
||||
RGB2YUV -> HIST
|
||||
RGB2YUV -> AF
|
||||
}
|
||||
|
||||
Functional Overview
|
||||
-------------------
|
||||
@ -24,8 +57,8 @@ The ISP driver offers following services:
|
||||
|
||||
- `Resource Allocation <#isp-resource-allocation>`__ - covers how to allocate ISP resources with properly set of configurations. It also covers how to recycle the resources when they finished working.
|
||||
- `Enable and disable ISP processor <#isp-enable-disable>`__ - covers how to enable and disable an ISP processor.
|
||||
- `Get AF oneshot result <#isp-af-get-oneshot-result>`__ - covers how to get AF oneshot result.
|
||||
- `Start AF statistics continuously <#isp-af-start-conti-stat>`__ - covers how to start the continuous AF statistics.
|
||||
- `Get AF statistics in one shot or continuous way <#isp-af-statistics>`__ - covers how to get AF statistics one-shot or continuously.
|
||||
- `Get AWB statistics in one shot or continuous way <#isp-awb-statistics>`__ - covers how to get AWB white patches statistics one-shot or continuously.
|
||||
- `Register callback <#isp-callback>`__ - covers how to hook user specific code to ISP driver event callback function.
|
||||
- `Thread Safety <#isp-thread-safety>`__ - lists which APIs are guaranteed to be thread safe by the driver.
|
||||
- `Kconfig Options <#isp-kconfig-options>`__ - lists the supported Kconfig options that can bring different effects to the driver.
|
||||
@ -36,8 +69,8 @@ The ISP driver offers following services:
|
||||
Resource Allocation
|
||||
^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Install Image Signal Processor (ISP) Driver
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Install ISP Driver
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
ISP driver requires the configuration that specified by :cpp:type:`esp_isp_processor_cfg_t`.
|
||||
|
||||
@ -59,8 +92,8 @@ If the configurations in :cpp:type:`esp_isp_processor_cfg_t` is specified, users
|
||||
You can use the created handle to do driver enable / disable the ISP driver and do other ISP module installation.
|
||||
|
||||
|
||||
Install Image Signal Processor (ISP) Auto-Focus (AF) Driver
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Install ISP Auto-Focus (AF) Driver
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
ISP auto-focus (AF) driver requires the configuration that specified by :cpp:type:`esp_isp_af_config_t`.
|
||||
|
||||
@ -76,33 +109,66 @@ If the configurations in :cpp:type:`esp_isp_af_config_t` is specified, users can
|
||||
|
||||
You can use the created handle to do driver enable / disable the ISP AF driver and ISP AF Env module installation.
|
||||
|
||||
Uninstall Image Signal Processor (ISP) Driver
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Install ISP Auto-White-Balance (AWB) Driver
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
ISP auto-white-balance (AWB) driver requires the configuration specified by :cpp:type:`esp_isp_awb_config_t`.
|
||||
|
||||
If an :cpp:type:`esp_isp_awb_config_t` configuration is specified, you can call :cpp:func:`esp_isp_new_awb_controller` to allocate and initialize an ISP AWB processor. This function will return an ISP AWB processor handle on success. You can take following code as reference.
|
||||
|
||||
.. code:: c
|
||||
|
||||
isp_awb_ctlr_t awb_ctlr = NULL;
|
||||
uint32_t image_width = 800;
|
||||
uint32_t image_height = 600;
|
||||
/* The AWB configuration, please refer to the API comment for how to tune these parameters */
|
||||
esp_isp_awb_config_t awb_config = {
|
||||
.sample_point = ISP_AWB_SAMPLE_POINT_AFTER_CCM,
|
||||
.window = {
|
||||
.top_left = {.x = image_width * 0.2, .y = image_height * 0.2},
|
||||
.btm_right = {.x = image_width * 0.8, .y = image_height * 0.8},
|
||||
},
|
||||
.white_patch = {
|
||||
.luminance = {.min = 0, .max = 220 * 3},
|
||||
.red_green_ratio = {.min = 0.0f, .max = 3.999f},
|
||||
.blue_green_ratio = {.min = 0.0f, .max = 3.999f},
|
||||
},
|
||||
};
|
||||
ESP_ERROR_CHECK(esp_isp_new_awb_controller(isp_proc, &awb_config, &awb_ctlr));
|
||||
|
||||
The AWB handle created in this step is required by other AWB APIs and AWB scheme.
|
||||
|
||||
Uninstall ISP Driver
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
If a previously installed ISP processor is no longer needed, it's recommended to recycle the resource by calling :cpp:func:`esp_isp_del_processor`, so that to release the underlying hardware.
|
||||
|
||||
UnInstall Image Signal Processor (ISP) Auto-Focus (AF) Driver
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
UnInstall ISP AF Driver
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
If a previously installed ISP AF processor is no longer needed, it's recommended to recycle the resource by calling :cpp:func:`esp_isp_del_af_controller`, so that to release the underlying hardware.
|
||||
|
||||
UnInstall 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.
|
||||
|
||||
|
||||
.. _isp-enable-disable:
|
||||
|
||||
Enable and Disable Image Signal Processor (ISP)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Image Signal Processor (ISP)
|
||||
----------------------------
|
||||
Enable and Disable ISP
|
||||
^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
ISP
|
||||
---------
|
||||
Before doing ISP pipeline, you need to enable the ISP processor first, by calling :cpp:func:`esp_isp_enable`. This function:
|
||||
|
||||
* Switches the driver state from **init** to **enable**.
|
||||
|
||||
Calling :cpp:func:`esp_isp_disable` does the opposite, that is, put the driver back to the **init** state.
|
||||
|
||||
Image Signal Processor (ISP) Auto-Focus (AF) Processor
|
||||
------------------------------------------------------
|
||||
ISP AF Processor
|
||||
----------------
|
||||
|
||||
Before doing ISP AF, you need to enable the ISP AF processor first, by calling :cpp:func:`esp_isp_af_controller_enable`. This function:
|
||||
|
||||
@ -110,13 +176,17 @@ Before doing ISP AF, you need to enable the ISP AF processor first, by calling :
|
||||
|
||||
Calling :cpp:func:`esp_isp_af_controller_disable` does the opposite, that is, put the driver back to the **init** state.
|
||||
|
||||
.. _isp-af-get-oneshot-result:
|
||||
.. _isp-af-statistics:
|
||||
|
||||
Get Auto-Focus (AF) Oneshot Result
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
AF One-shot and Continuous Statistics
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Calling :cpp:func:`esp_isp_af_controller_get_oneshot_statistics` to get oneshot AF statistics result. You can take following code as reference.
|
||||
|
||||
Aside from the above oneshot API, the ISP AF driver also provides a way to start AF statistics continuously. Calling :cpp:func:`esp_isp_af_controller_start_continuous_statistics` to start the continuous statistics and :cpp:func:`esp_isp_af_controller_stop_continuous_statistics` to stop it.
|
||||
|
||||
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>`__
|
||||
|
||||
.. code:: c
|
||||
|
||||
esp_isp_af_config_t af_config = {
|
||||
@ -129,26 +199,6 @@ Calling :cpp:func:`esp_isp_af_controller_get_oneshot_statistics` to get oneshot
|
||||
/* Trigger the AF statistics and get its result for one time with timeout value 2000ms. */
|
||||
ESP_ERROR_CHECK(esp_isp_af_controller_get_oneshot_statistics(af_ctrlr, 2000, &result));
|
||||
|
||||
.. _isp-af-start-conti-stat:
|
||||
|
||||
Start Auto-Focus (AF) Statistics Continuously
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Aside from the above oneshot API, the ISP AF driver also provides a way to start AF statistics continuously. Calling :cpp:func:`esp_isp_af_controller_start_continuous_statistics` to start the continuous statistics and :cpp:func:`esp_isp_af_controller_stop_continuous_statistics` to stop it.
|
||||
|
||||
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>`__
|
||||
|
||||
.. code:: c
|
||||
|
||||
isp_af_ctlr_t af_ctrlr = NULL;
|
||||
esp_isp_af_config_t af_config = {
|
||||
.edge_thresh = 128,
|
||||
};
|
||||
isp_af_result_t stat_res = {};
|
||||
/* Create the af controller */
|
||||
ESP_ERROR_CHECK(esp_isp_new_af_controller(isp_proc, &af_config, &af_ctrlr));
|
||||
/* Enabled the af controller */
|
||||
ESP_ERROR_CHECK(esp_isp_af_controller_enable(af_ctrlr));
|
||||
/* Start continuous AF statistics */
|
||||
ESP_ERROR_CHECK(esp_isp_af_controller_start_continuous_statistics(af_ctrlr));
|
||||
// You can do other stuffs here, the statistics result can be obtained in the callback
|
||||
@ -156,13 +206,14 @@ Note that if you want to use the continuous statistics, you need to register the
|
||||
// vTaskDelay(pdMS_TO_TICKS(1000));
|
||||
/* Stop continuous AF statistics */
|
||||
ESP_ERROR_CHECK(esp_isp_af_controller_stop_continuous_statistics(af_ctrlr));
|
||||
|
||||
/* Disable the af controller */
|
||||
ESP_ERROR_CHECK(esp_isp_af_controller_disable(af_ctrlr));
|
||||
/* Delete the af controller and free the resources */
|
||||
ESP_ERROR_CHECK(esp_isp_del_af_controller(af_ctrlr));
|
||||
|
||||
Set Auto-Focus (AF) Environment Detector
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
Set AF Environment Detector
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Calling :cpp:func:`esp_isp_af_controller_set_env_detector` to set an ISP AF environment detector. You can take following code as reference.
|
||||
|
||||
@ -175,8 +226,8 @@ Calling :cpp:func:`esp_isp_af_controller_set_env_detector` to set an ISP AF envi
|
||||
ESP_ERROR_CHECK(esp_isp_new_af_controller(isp_proc, &af_config, &af_ctrlr));
|
||||
ESP_ERROR_CHECK(esp_isp_af_controller_set_env_detector(af_ctrlr, &env_config));
|
||||
|
||||
Set Auto-Focus (AF) Environment Detector Threshold
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
Set AF Environment Detector Threshold
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Calling :cpp:func:`esp_isp_af_env_detector_set_threshold` to set the threshold of an ISP AF environment detector.
|
||||
|
||||
@ -186,13 +237,80 @@ 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 AWB Processor
|
||||
-----------------
|
||||
|
||||
Before doing ISP AWB, you need to enable the ISP AWB processor first, by calling :cpp:func:`esp_isp_awb_controller_enable`. This function:
|
||||
|
||||
* Switches the driver state from **init** to **enable**.
|
||||
|
||||
Calling :cpp:func:`esp_isp_awb_controller_disable` does the opposite, that is, put the driver back to the **init** state.
|
||||
|
||||
.. _isp-awb-statistics:
|
||||
|
||||
AWB One-shot and Continuous Statistics
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Calling :cpp:func:`esp_isp_awb_controller_get_oneshot_result` to get oneshot AWB statistics result of white patches. You can take following code as reference.
|
||||
|
||||
Aside from the above oneshot API, the ISP AWB driver also provides a way to start AWB statistics continuously. Calling :cpp:func:`esp_isp_awb_controller_start_continuous_statistics` starts the continuous statistics and :cpp:func:`esp_isp_awb_controller_stop_continuous_statistics` stops it.
|
||||
|
||||
Note that if you want to use the continuous statistics, you need to register the :cpp:member:`esp_isp_awb_cbs_t::on_statistics_done` callback to get the statistics result. See how to register it in `Register Event Callbacks <#isp-callback>`__
|
||||
|
||||
.. code:: c
|
||||
|
||||
bool example_isp_awb_on_statistics_done_cb(isp_awb_ctlr_t awb_ctlr, const esp_isp_awb_evt_data_t *edata, void *user_data);
|
||||
// ...
|
||||
isp_awb_ctlr_t awb_ctlr = NULL;
|
||||
uint32_t image_width = 800;
|
||||
uint32_t image_height = 600;
|
||||
/* The AWB configuration, please refer to the API comment for how to tune these parameters */
|
||||
esp_isp_awb_config_t awb_config = {
|
||||
.sample_point = ISP_AWB_SAMPLE_POINT_AFTER_CCM,
|
||||
.window = {
|
||||
.top_left = {.x = image_width * 0.2, .y = image_height * 0.2},
|
||||
.btm_right = {.x = image_width * 0.8, .y = image_height * 0.8},
|
||||
},
|
||||
.white_patch = {
|
||||
.luminance = {.min = 0, .max = 220 * 3},
|
||||
.red_green_ratio = {.min = 0.0f, .max = 3.999f},
|
||||
.blue_green_ratio = {.min = 0.0f, .max = 3.999f},
|
||||
},
|
||||
};
|
||||
isp_awb_stat_result_t stat_res = {};
|
||||
/* Create the awb controller */
|
||||
ESP_ERROR_CHECK(esp_isp_new_awb_controller(isp_proc, &awb_config, &awb_ctlr));
|
||||
/* Register AWB callback */
|
||||
esp_isp_awb_cbs_t awb_cb = {
|
||||
.on_statistics_done = example_isp_awb_on_statistics_done_cb,
|
||||
};
|
||||
ESP_ERROR_CHECK(esp_isp_awb_register_event_callbacks(awb_ctlr, &awb_cb, NULL));
|
||||
/* Enabled the awb controller */
|
||||
ESP_ERROR_CHECK(esp_isp_awb_controller_enable(awb_ctlr));
|
||||
|
||||
/* Get oneshot AWB statistics result */
|
||||
ESP_ERROR_CHECK(esp_isp_awb_controller_get_oneshot_statistics(awb_ctlr, -1, &stat_res));
|
||||
|
||||
/* Start continuous AWB statistics, note that continuous statistics requires `on_statistics_done` callback */
|
||||
ESP_ERROR_CHECK(esp_isp_awb_controller_start_continuous_statistics(awb_ctlr));
|
||||
// You can do other stuffs here, the statistics result can be obtained in the callback
|
||||
// ......
|
||||
// vTaskDelay(pdMS_TO_TICKS(1000));
|
||||
/* Stop continuous AWB statistics */
|
||||
ESP_ERROR_CHECK(esp_isp_awb_controller_stop_continuous_statistics(awb_ctlr));
|
||||
|
||||
/* Disable the awb controller */
|
||||
ESP_ERROR_CHECK(esp_isp_awb_controller_disable(awb_ctlr));
|
||||
/* Delete the awb controller and free the resources */
|
||||
ESP_ERROR_CHECK(esp_isp_del_awb_controller(awb_ctlr));
|
||||
|
||||
.. _isp-callback:
|
||||
|
||||
Register Event Callbacks
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Register Image Signal Processor (ISP) Auto-Focus (AF) Environment Detector Event Callbacks
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Register ISP AF Environment Detector Event Callbacks
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
After the ISP AF environment detector starts up, it can generate a specific event dynamically. If you have some functions that should be called when the event happens, please hook your function to the interrupt service routine by calling :cpp:func:`esp_isp_af_env_detector_register_event_callbacks`. All supported event callbacks are listed in :cpp:type:`esp_isp_af_env_detector_evt_cbs_t`:
|
||||
|
||||
@ -201,6 +319,15 @@ After the ISP AF environment detector starts up, it can generate a specific even
|
||||
|
||||
You can save your own context to :cpp:func:`esp_isp_af_env_detector_register_event_callbacks` as well, via the parameter ``user_data``. The user data will be directly passed to the callback function.
|
||||
|
||||
Register ISP AWB Statistics Done Event Callbacks
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
After the ISP AWB controller finished statistics of white patches, it can generate a specific event dynamically. If you want to be informed when the statistics done event takes place, please hook your function to the interrupt service routine by calling :cpp:func:`esp_isp_awb_register_event_callbacks`. All supported event callbacks are listed in :cpp:type:`esp_isp_awb_cbs_t`:
|
||||
|
||||
- :cpp:member:`esp_isp_awb_cbs_t::on_statistics_done` sets a callback function when finished statistics of the white patches. As this function is called within the ISR context, you must ensure that the function does not attempt to block (e.g., by making sure that only FreeRTOS APIs with ``ISR`` suffix are called from within the function). The function prototype is declared in :cpp:type:`esp_isp_awb_callback_t`.
|
||||
|
||||
You can save your own context via the parameter ``user_data`` of :cpp:func:`esp_isp_awb_register_event_callbacks`. The user data will be directly passed to the callback function.
|
||||
|
||||
.. _isp-thread-safety:
|
||||
|
||||
Thread Safety
|
||||
|
@ -92,7 +92,7 @@ static void af_task(void *arg)
|
||||
|
||||
/**
|
||||
* AF window, windows for ISP hardware to record the
|
||||
* - lunimance
|
||||
* - luminance
|
||||
* - definition
|
||||
* of the current windows
|
||||
*/
|
||||
|
Loading…
x
Reference in New Issue
Block a user