feat(isp_awb): support isp auto white balance

This commit is contained in:
laokaiyao 2024-06-01 01:50:25 +08:00
parent 1933973f99
commit 28a1091643
13 changed files with 919 additions and 4 deletions

View File

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

View File

@ -13,4 +13,5 @@
#include "driver/isp_core.h"
#include "driver/isp_af.h"
#include "driver/isp_awb.h"
#include "driver/isp_bf.h"

View File

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

View 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

View File

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

View File

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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