mirror of
https://github.com/espressif/esp-idf.git
synced 2024-10-05 20:47:46 -04:00
feat(isp_awb): support isp auto white balance
This commit is contained in:
parent
1933973f99
commit
28a1091643
@ -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()`
|
||||
*
|
||||
|
207
components/esp_driver_isp/include/driver/isp_awb.h
Normal file
207
components/esp_driver_isp/include/driver/isp_awb.h
Normal file
@ -0,0 +1,207 @@
|
||||
/*
|
||||
* 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~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.
|
||||
*/
|
||||
} 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 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,34 @@
|
||||
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 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 +50,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
|
||||
|
280
components/esp_driver_isp/src/isp_awb.c
Normal file
280
components/esp_driver_isp/src/isp_awb.c
Normal file
@ -0,0 +1,280 @@
|
||||
/*
|
||||
* 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 "isp_internal.h"
|
||||
|
||||
typedef struct isp_awb_controller_t {
|
||||
isp_fsm_t fsm;
|
||||
portMUX_TYPE spinlock;
|
||||
intr_handle_t intr_handle;
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
int intr_priority = awb_cfg->intr_priority > 0 && awb_cfg->intr_priority <= 7 ? 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 | 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_set_sample_point(isp_proc->hal.hw, awb_cfg->sample_point);
|
||||
isp_ll_awb_enable_algorithm_mode(isp_proc->hal.hw, true);
|
||||
ESP_GOTO_ON_FALSE(isp_hal_awb_set_window_range(&isp_proc->hal, &awb_cfg->window),
|
||||
ESP_ERR_INVALID_ARG, err2, TAG, "invalid window");
|
||||
isp_u32_range_t lum_range = awb_cfg->white_patch.luminance;
|
||||
ESP_GOTO_ON_FALSE(isp_hal_awb_set_luminance_range(&isp_proc->hal, lum_range.min, lum_range.max),
|
||||
ESP_ERR_INVALID_ARG, err2, TAG, "invalid luminance range");
|
||||
isp_float_range_t rg_range = awb_cfg->white_patch.red_green_ratio;
|
||||
ESP_GOTO_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, err2, TAG, "invalid range of Red Green ratio");
|
||||
isp_float_range_t bg_range = awb_cfg->white_patch.blue_green_ratio;
|
||||
ESP_GOTO_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, err2, TAG, "invalid range of Blue to Green ratio");
|
||||
|
||||
*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_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");
|
||||
|
||||
esp_intr_enable(awb_ctlr->intr_handle);
|
||||
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);
|
||||
awb_ctlr->fsm = ISP_FSM_ENABLE;
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
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");
|
||||
|
||||
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);
|
||||
awb_ctlr->fsm = ISP_FSM_INIT;
|
||||
xSemaphoreTake(awb_ctlr->stat_lock, 0);
|
||||
|
||||
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_ISR(awb_ctlr && (out_res || timeout_ms == 0), ESP_ERR_INVALID_ARG, TAG, "invalid argument: null pointer");
|
||||
ESP_RETURN_ON_FALSE_ISR(awb_ctlr->fsm == ISP_FSM_ENABLE, ESP_ERR_INVALID_STATE, TAG, "controller isn't in enable state");
|
||||
TickType_t ticks = timeout_ms < 0 ? portMAX_DELAY : pdMS_TO_TICKS(timeout_ms);
|
||||
|
||||
xSemaphoreTake(awb_ctlr->stat_lock, ticks);
|
||||
// Update state to avoid race condition
|
||||
awb_ctlr->fsm = ISP_FSM_START;
|
||||
esp_err_t ret = ESP_OK;
|
||||
// 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;
|
||||
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_ISR(awb_ctlr, ESP_ERR_INVALID_ARG, TAG, "invalid argument: null pointer");
|
||||
ESP_RETURN_ON_FALSE_ISR(awb_ctlr->fsm == ISP_FSM_ENABLE, ESP_ERR_INVALID_STATE, TAG, "controller isn't in enable state");
|
||||
|
||||
if (xSemaphoreTake(awb_ctlr->stat_lock, 0) == pdFALSE) {
|
||||
ESP_LOGW(TAG, "statistics lock is not acquired, controller is busy");
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
awb_ctlr->fsm = ISP_FSM_START;
|
||||
isp_ll_awb_enable(awb_ctlr->isp_proc->hal.hw, true);
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t esp_isp_awb_controller_stop_continuous_statistics(isp_awb_ctlr_t awb_ctlr)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE_ISR(awb_ctlr, ESP_ERR_INVALID_ARG, TAG, "invalid argument: null pointer");
|
||||
ESP_RETURN_ON_FALSE_ISR(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_patcherence_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, 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,51 @@ TEST_CASE("ISP AF controller exhausted allocation", "[isp]")
|
||||
}
|
||||
TEST_ESP_OK(esp_isp_del_processor(isp_proc));
|
||||
}
|
||||
|
||||
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));
|
||||
/* 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_patcherence_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
|
||||
|
@ -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_af_window_t *window);
|
||||
|
||||
@ -86,6 +88,69 @@ 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);
|
||||
|
||||
/*---------------------------------------------------------------
|
||||
Color Correction Matrix
|
||||
---------------------------------------------------------------*/
|
||||
/**
|
||||
* @brief Set Color Correction Matrix
|
||||
*
|
||||
* @param[in] hal Context of the HAL layer
|
||||
* @param[in] saturation Whether to enable saturation when float data overflow
|
||||
* @param[in] flt_matrix 3x3 RGB correction matrix
|
||||
* @return
|
||||
* - true Set success
|
||||
* - false Invalid are
|
||||
*/
|
||||
bool isp_hal_ccm_set_matrix(const isp_hal_context_t *hal, bool saturation, const float flt_matrix[3][3]);
|
||||
|
||||
/*---------------------------------------------------------------
|
||||
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
|
||||
|
@ -23,6 +23,24 @@ typedef soc_periph_isp_clk_src_t isp_clk_src_t; ///< Clock source type of
|
||||
typedef int isp_clk_src_t; ///< Default type
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief ISP coordinate type
|
||||
*
|
||||
*/
|
||||
typedef struct {
|
||||
uint32_t x; ///< X coordinate of the point
|
||||
uint32_t y; ///< Y coordinate of the point
|
||||
} isp_coordinate_t;
|
||||
|
||||
/**
|
||||
* @brief ISP window type
|
||||
*
|
||||
*/
|
||||
typedef struct {
|
||||
isp_coordinate_t top_left; ///< The top left point coordinate
|
||||
isp_coordinate_t btm_right; ///< The bottom right point coordinate
|
||||
} isp_window_t;
|
||||
|
||||
/**
|
||||
* @brief ISP Input Source
|
||||
*/
|
||||
@ -100,6 +118,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,9 @@
|
||||
#include "hal/isp_hal.h"
|
||||
#include "hal/isp_ll.h"
|
||||
#include "hal/isp_types.h"
|
||||
#include "hal/hal_utils.h"
|
||||
|
||||
#include "esp_rom_sys.h"
|
||||
|
||||
/**
|
||||
* ISP HAL layer
|
||||
@ -68,3 +72,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;
|
||||
}
|
||||
|
@ -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