refactor(isp): refactor the interrupt and callback solution

- Added async API
- Replaced the polling API
- Supported one more callback and event data
This commit is contained in:
laokaiyao 2024-04-11 11:12:26 +08:00
parent ea010f84ef
commit dd20d1f2b5
12 changed files with 359 additions and 321 deletions

View File

@ -3,7 +3,7 @@ set(srcs)
set(public_include "include")
if(CONFIG_SOC_ISP_SUPPORTED)
list(APPEND srcs "src/isp.c"
list(APPEND srcs "src/isp_core.c"
"src/isp_af.c")
endif()

View File

@ -4,84 +4,12 @@
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @brief ISP peripheral contains many submodules, whose drivers are scattered in different header files.
* This header file serves as a prelude, contains every thing that is needed to work with the ISP peripheral.
*/
#pragma once
#include <stdint.h>
#include <stdbool.h>
#include "esp_err.h"
#include "driver/isp_types.h"
#include "driver/isp_core.h"
#include "driver/isp_af.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief ISP configurations
*/
typedef struct {
isp_clk_src_t clk_src; ///< Clock source
uint32_t clk_hz; ///< Clock frequency in Hz, suggest twice higher than cam sensor speed
isp_input_data_source_t input_data_source; ///< Input data source
isp_color_t input_data_color_type; ///< Input color type
isp_color_t output_data_color_type; ///< Output color type
bool has_line_start_packet; ///< Enable line start packet
bool has_line_end_packet; ///< Enable line end packet
uint32_t h_res; ///< Input horizontal resolution, i.e. the number of pixels in a line
uint32_t v_res; ///< Input vertical resolution, i.e. the number of lines in a frame
} esp_isp_processor_cfg_t;
/**
* @brief New an ISP processor
*
* @param[in] proc_config Pointer to ISP config. Refer to ``esp_isp_processor_cfg_t``.
* @param[out] ret_proc Processor handle
*
* @return
* - ESP_OK On success
* - ESP_ERR_INVALID_ARG If the combination of arguments is invalid.
* - ESP_ERR_NOT_FOUND No free interrupt found with the specified flags
* - ESP_ERR_NOT_SUPPORTED Not supported mode
* - ESP_ERR_NO_MEM If out of memory
*/
esp_err_t esp_isp_new_processor(const esp_isp_processor_cfg_t *proc_config, isp_proc_handle_t *ret_proc);
/**
* @brief Delete an ISP processor
*
* @param[in] proc Processor handle
*
* @return
* - ESP_OK On success
* - ESP_ERR_INVALID_ARG If the combination of arguments is invalid.
* - ESP_ERR_INVALID_STATE Driver state is invalid.
*/
esp_err_t esp_isp_del_processor(isp_proc_handle_t proc);
/**
* @brief Enable an ISP processor
*
* @param[in] proc Processor handle
*
* @return
* - ESP_OK On success
* - ESP_ERR_INVALID_ARG If the combination of arguments is invalid.
* - ESP_ERR_INVALID_STATE Driver state is invalid.
*/
esp_err_t esp_isp_enable(isp_proc_handle_t proc);
/**
* @brief Disable an ISP processor
*
* @param[in] proc Processor handle
*
* @return
* - ESP_OK On success
* - ESP_ERR_INVALID_ARG If the combination of arguments is invalid.
* - ESP_ERR_INVALID_STATE Driver state is invalid.
*/
esp_err_t esp_isp_disable(isp_proc_handle_t proc);
#ifdef __cplusplus
}
#endif

View File

@ -10,8 +10,6 @@
#include <stdbool.h>
#include "esp_err.h"
#include "driver/isp_types.h"
#include "driver/isp.h"
#include "soc/soc_caps.h"
#ifdef __cplusplus
extern "C" {
@ -21,10 +19,9 @@ extern "C" {
* @brief AF controller config
*/
typedef struct {
#if SOC_ISP_AF_WINDOW_NUMS
isp_af_window_t window[SOC_ISP_AF_WINDOW_NUMS]; ///< AF window settings
#endif
isp_af_window_t window[ISP_AF_WINDOW_NUM]; ///< The sampling windows of AF
int edge_thresh; ///< Edge threshold, definition higher than this value will be counted as a valid pixel for calculating AF result
int intr_priority; ///< The interrupt priority, range 0~7, if set to 0, the driver will try to allocate an interrupt with a relative low priority (1,2,3) otherwise the larger the higher, 7 is NMI
} esp_isp_af_config_t;
/**
@ -80,17 +77,57 @@ esp_err_t esp_isp_af_controller_enable(isp_af_ctrlr_t af_ctrlr);
esp_err_t esp_isp_af_controller_disable(isp_af_ctrlr_t af_ctrlr);
/**
* @brief Get AF result
* @brief Trigger AF luminance and definition statistics for one time and get the result
* @note This function is a synchronous and block function,
* it only returns when AF luminance and definition statistics is done or timeout.
* It's a simple method to get the result directly for one time.
*
* @param[in] af_ctrlr AF controller handle
* @param[out] out_res AF result
* @param[in] af_ctrlr AF 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_af_env_detector_evt_cbs_t::on_env_statistics_done`
* - timeout_ms > 0: Wait for specified milliseconds, if not finished, then return timeout error
* @param[out] out_res AF luminance and definition statistics result, can be NULL if `timeout_ms = 0`
*
* @return
* - ESP_OK On success
* - ESP_ERR_TIMEOUT If the waiting time exceeds the specified timeout.
* - ESP_ERR_INVALID_ARG If the combination of arguments is invalid.
* - ESP_ERR_INVALID_STATE Driver state is invalid.
*/
esp_err_t esp_isp_af_controller_get_oneshot_result(isp_af_ctrlr_t af_ctrlr, isp_af_result_t *out_res);
esp_err_t esp_isp_af_controller_get_oneshot_statistics(isp_af_ctrlr_t af_ctrlr, int timeout_ms, isp_af_result_t *out_res);
/** @cond */
#define esp_isp_af_controller_get_oneshot_result(af_ctrlr, out_res) \
esp_isp_af_controller_get_oneshot_statistics(af_ctrlr, -1, out_res) // Alias
/** @endcond */
/**
* @brief Start AF continuous statistics of the luminance and definition in the windows
* @note This function is an asynchronous and non-block function,
* it will start the continuous statistics and return immediately.
* You have to register the AF callback and get the result from the callback event data.
*
* @param[in] af_ctrlr AF 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_af_controller_start_continuous_statistics(isp_af_ctrlr_t af_ctrlr);
/**
* @brief Stop AF continuous statistics of the luminance and definition in the windows
*
* @param[in] af_ctrlr AF 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_af_controller_stop_continuous_statistics(isp_af_ctrlr_t af_ctrlr);
/*---------------------------------------------
AF Env Monitor
@ -99,7 +136,9 @@ esp_err_t esp_isp_af_controller_get_oneshot_result(isp_af_ctrlr_t af_ctrlr, isp_
* @brief AF environment detector config
*/
typedef struct {
int interval; ///< Interval between environment detection, in frames
int interval; /*!< Interval between environment detection, in frames.
* i.e., AF controller will trigger the statistic periodically to detect the environment change.
*/
} esp_isp_af_env_config_t;
/**
@ -133,7 +172,7 @@ esp_err_t esp_isp_af_controller_set_env_detector_threshold(isp_af_ctrlr_t af_ctr
* @brief Event data structure
*/
typedef struct {
//empty for future proof
isp_af_result_t af_result; /*!< The AF statistics result */
} esp_isp_af_env_detector_evt_data_t;
/**
@ -155,7 +194,8 @@ typedef bool (*esp_isp_af_env_detector_callback_t)(isp_af_ctrlr_t af_ctrlr, cons
* Involved variables should be in internal RAM as well.
*/
typedef struct {
esp_isp_af_env_detector_callback_t on_env_change; ///< Event callback, invoked when environment change happens.
esp_isp_af_env_detector_callback_t on_env_statistics_done; ///< Event callback, invoked when environment sample done.
esp_isp_af_env_detector_callback_t on_env_change; ///< Event callback, invoked when environment change happens.
} esp_isp_af_env_detector_evt_cbs_t;
/**

View File

@ -0,0 +1,86 @@
/*
* 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 ISP configurations
*/
typedef struct {
isp_clk_src_t clk_src; ///< Clock source
uint32_t clk_hz; ///< Clock frequency in Hz, suggest twice higher than cam sensor speed
isp_input_data_source_t input_data_source; ///< Input data source
isp_color_t input_data_color_type; ///< Input color type
isp_color_t output_data_color_type; ///< Output color type
bool has_line_start_packet; ///< Enable line start packet
bool has_line_end_packet; ///< Enable line end packet
uint32_t h_res; ///< Input horizontal resolution, i.e. the number of pixels in a line
uint32_t v_res; ///< Input vertical resolution, i.e. the number of lines in a frame
} esp_isp_processor_cfg_t;
/**
* @brief New an ISP processor
*
* @param[in] proc_config Pointer to ISP config. Refer to ``esp_isp_processor_cfg_t``.
* @param[out] ret_proc Processor handle
*
* @return
* - ESP_OK On success
* - ESP_ERR_INVALID_ARG If the combination of arguments is invalid.
* - ESP_ERR_NOT_FOUND No free interrupt found with the specified flags
* - ESP_ERR_NOT_SUPPORTED Not supported mode
* - ESP_ERR_NO_MEM If out of memory
*/
esp_err_t esp_isp_new_processor(const esp_isp_processor_cfg_t *proc_config, isp_proc_handle_t *ret_proc);
/**
* @brief Delete an ISP processor
*
* @param[in] proc Processor handle
*
* @return
* - ESP_OK On success
* - ESP_ERR_INVALID_ARG If the combination of arguments is invalid.
* - ESP_ERR_INVALID_STATE Driver state is invalid.
*/
esp_err_t esp_isp_del_processor(isp_proc_handle_t proc);
/**
* @brief Enable an ISP processor
*
* @param[in] proc Processor handle
*
* @return
* - ESP_OK On success
* - ESP_ERR_INVALID_ARG If the combination of arguments is invalid.
* - ESP_ERR_INVALID_STATE Driver state is invalid.
*/
esp_err_t esp_isp_enable(isp_proc_handle_t proc);
/**
* @brief Disable an ISP processor
*
* @param[in] proc Processor handle
*
* @return
* - ESP_OK On success
* - ESP_ERR_INVALID_ARG If the combination of arguments is invalid.
* - ESP_ERR_INVALID_STATE Driver state is invalid.
*/
esp_err_t esp_isp_disable(isp_proc_handle_t proc);
#ifdef __cplusplus
}
#endif

View File

@ -11,23 +11,29 @@
#include "esp_check.h"
#include "esp_heap_caps.h"
#include "freertos/FreeRTOS.h"
#include "driver/isp.h"
#include "hal/isp_hal.h"
#include "hal/isp_ll.h"
#include "driver/isp_af.h"
#include "isp_internal.h"
#if CONFIG_ISP_ISR_IRAM_SAFE
#define ISP_AF_MEM_ALLOC_CAPS (MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT)
#else
#define ISP_AF_MEM_ALLOC_CAPS MALLOC_CAP_DEFAULT
#endif
static const char *TAG = "ISP_AF";
typedef struct isp_af_controller_t {
int id;
isp_fsm_t fsm;
portMUX_TYPE spinlock;
intr_handle_t intr_handle;
isp_proc_handle_t isp_proc;
QueueHandle_t evt_que;
esp_isp_af_env_config_t config;
esp_isp_af_env_detector_evt_cbs_t cbs;
void *user_data;
} isp_af_controller_t;
static void s_isp_af_default_isr(void *arg);
/*---------------------------------------------
AF
----------------------------------------------*/
static esp_err_t s_isp_claim_af_controller(isp_proc_handle_t isp_proc, isp_af_controller_t *af_ctlr)
static esp_err_t s_isp_claim_af_controller(isp_proc_handle_t isp_proc, isp_af_ctrlr_t af_ctlr)
{
assert(isp_proc && af_ctlr);
@ -50,15 +56,26 @@ static esp_err_t s_isp_claim_af_controller(isp_proc_handle_t isp_proc, isp_af_co
return ESP_OK;
}
static esp_err_t s_isp_declaim_af_controller(isp_af_controller_t *af_ctlr)
static void s_isp_declaim_af_controller(isp_af_ctrlr_t af_ctlr)
{
assert(af_ctlr && af_ctlr->isp_proc);
portENTER_CRITICAL(&af_ctlr->isp_proc->spinlock);
af_ctlr->isp_proc->af_ctlr[af_ctlr->id] = NULL;
portEXIT_CRITICAL(&af_ctlr->isp_proc->spinlock);
}
return ESP_OK;
static void s_isp_af_free_controller(isp_af_ctrlr_t af_ctlr)
{
if (af_ctlr) {
if (af_ctlr->intr_handle) {
esp_intr_free(af_ctlr->intr_handle);
}
if (af_ctlr->evt_que) {
vQueueDelete(af_ctlr->evt_que);
}
free(af_ctlr);
}
}
esp_err_t esp_isp_new_af_controller(isp_proc_handle_t isp_proc, const esp_isp_af_config_t *af_config, isp_af_ctrlr_t *ret_hdl)
@ -85,16 +102,23 @@ esp_err_t esp_isp_new_af_controller(isp_proc_handle_t isp_proc, const esp_isp_af
}
ESP_RETURN_ON_FALSE(af_config->edge_thresh > 0, ESP_ERR_INVALID_ARG, TAG, "edge threshold should be larger than 0");
isp_af_controller_t *af_ctlr = heap_caps_calloc(1, sizeof(isp_af_controller_t), ISP_AF_MEM_ALLOC_CAPS);
isp_af_ctrlr_t af_ctlr = heap_caps_calloc(1, sizeof(isp_af_controller_t), ISP_MEM_ALLOC_CAPS);
ESP_RETURN_ON_FALSE(af_ctlr, ESP_ERR_NO_MEM, TAG, "no mem");
//claim an AF controller
ESP_GOTO_ON_ERROR(s_isp_claim_af_controller(isp_proc, af_ctlr), err, TAG, "no available controller");
af_ctlr->evt_que = xQueueCreateWithCaps(1, sizeof(isp_af_result_t), ISP_MEM_ALLOC_CAPS);
ESP_GOTO_ON_FALSE(af_ctlr->evt_que, ESP_ERR_NO_MEM, err1, TAG, "no mem for af event queue");
af_ctlr->fsm = ISP_FSM_INIT;
af_ctlr->spinlock = (portMUX_TYPE)portMUX_INITIALIZER_UNLOCKED;
af_ctlr->isp_proc = isp_proc;
//claim an AF controller
ESP_GOTO_ON_ERROR(s_isp_claim_af_controller(isp_proc, af_ctlr), err1, TAG, "no available controller");
// Register the AF ISR
uint32_t intr_st_reg_addr = isp_ll_get_intr_status_reg_addr(isp_proc->hal.hw);
int intr_priority = af_config->intr_priority > 0 && af_config->intr_priority <= 7 ? BIT(af_config->intr_priority) : ESP_INTR_FLAG_LOWMED;
ESP_GOTO_ON_ERROR(esp_intr_alloc_intrstatus(isp_hw_info.instances[isp_proc->proc_id].irq, ISP_INTR_ALLOC_FLAGS | intr_priority, intr_st_reg_addr, ISP_LL_EVENT_AF_MASK,
s_isp_af_default_isr, af_ctlr, &af_ctlr->intr_handle), err2, TAG, "allocate interrupt failed");
isp_ll_af_enable_auto_update(isp_proc->hal.hw, false);
isp_ll_af_enable(isp_proc->hal.hw, false);
@ -109,9 +133,10 @@ esp_err_t esp_isp_new_af_controller(isp_proc_handle_t isp_proc, const esp_isp_af
*ret_hdl = af_ctlr;
return ESP_OK;
err:
free(af_ctlr);
err2:
s_isp_declaim_af_controller(af_ctlr);
err1:
s_isp_af_free_controller(af_ctlr);
return ret;
}
@ -119,10 +144,17 @@ err:
esp_err_t esp_isp_del_af_controller(isp_af_ctrlr_t af_ctlr)
{
ESP_RETURN_ON_FALSE(af_ctlr && af_ctlr->isp_proc, ESP_ERR_INVALID_ARG, TAG, "invalid argument: null pointer");
ESP_RETURN_ON_ERROR(s_isp_declaim_af_controller(af_ctlr), TAG, "controller isn't in use");
ESP_RETURN_ON_FALSE(af_ctlr->fsm == ISP_FSM_INIT, ESP_ERR_INVALID_STATE, TAG, "controller isn't in init state");
free(af_ctlr);
bool exist = false;
for (int i = 0; i < SOC_ISP_AF_CTLR_NUMS; i++) {
if (af_ctlr->isp_proc->af_ctlr[i] == af_ctlr) {
exist = true;
break;
}
}
ESP_RETURN_ON_FALSE(exist, ESP_ERR_INVALID_ARG, TAG, "controller isn't in use");
s_isp_declaim_af_controller(af_ctlr);
s_isp_af_free_controller(af_ctlr);
return ESP_OK;
}
@ -132,7 +164,9 @@ esp_err_t esp_isp_af_controller_enable(isp_af_ctrlr_t af_ctlr)
ESP_RETURN_ON_FALSE(af_ctlr && af_ctlr->isp_proc, ESP_ERR_INVALID_ARG, TAG, "invalid argument: null pointer");
ESP_RETURN_ON_FALSE(af_ctlr->fsm == ISP_FSM_INIT, ESP_ERR_INVALID_STATE, TAG, "controller isn't in init state");
esp_intr_enable(af_ctlr->intr_handle);
isp_ll_af_clk_enable(af_ctlr->isp_proc->hal.hw, true);
isp_ll_enable_intr(af_ctlr->isp_proc->hal.hw, ISP_LL_EVENT_AF_MASK, true);
isp_ll_af_enable(af_ctlr->isp_proc->hal.hw, true);
af_ctlr->fsm = ISP_FSM_ENABLE;
@ -145,18 +179,51 @@ esp_err_t esp_isp_af_controller_disable(isp_af_ctrlr_t af_ctlr)
ESP_RETURN_ON_FALSE(af_ctlr->fsm == ISP_FSM_ENABLE, ESP_ERR_INVALID_STATE, TAG, "controller isn't in enable state");
isp_ll_af_clk_enable(af_ctlr->isp_proc->hal.hw, false);
isp_ll_enable_intr(af_ctlr->isp_proc->hal.hw, ISP_LL_EVENT_AF_MASK, false);
isp_ll_af_enable(af_ctlr->isp_proc->hal.hw, false);
esp_intr_disable(af_ctlr->intr_handle);
af_ctlr->fsm = ISP_FSM_INIT;
return ESP_OK;
}
esp_err_t esp_isp_af_controller_get_oneshot_result(isp_af_ctrlr_t af_ctlr, isp_af_result_t *out_res)
esp_err_t esp_isp_af_controller_get_oneshot_statistics(isp_af_ctrlr_t af_ctrlr, int timeout_ms, isp_af_result_t *out_res)
{
ESP_RETURN_ON_FALSE_ISR(af_ctlr && out_res, ESP_ERR_INVALID_ARG, TAG, "invalid argument: null pointer");
ESP_RETURN_ON_FALSE_ISR(af_ctlr->fsm == ISP_FSM_ENABLE, ESP_ERR_INVALID_STATE, TAG, "controller isn't in enable state");
ESP_RETURN_ON_FALSE_ISR(af_ctrlr && (out_res || timeout_ms == 0), ESP_ERR_INVALID_ARG, TAG, "invalid argument: null pointer");
ESP_RETURN_ON_FALSE_ISR(af_ctrlr->fsm == ISP_FSM_ENABLE, ESP_ERR_INVALID_STATE, TAG, "controller isn't enabled or continuous statistics has started");
isp_hal_af_get_oneshot_result(&af_ctlr->isp_proc->hal, out_res);
esp_err_t ret = ESP_OK;
TickType_t ticks = timeout_ms < 0 ? portMAX_DELAY : pdMS_TO_TICKS(timeout_ms);
// Reset the queue in case receiving the legacy data in the queue
xQueueReset(af_ctrlr->evt_que);
// Trigger the AF statistics manually
isp_ll_af_manual_update(af_ctrlr->isp_proc->hal.hw);
// Wait the statistics to finish and receive the result from the queue
if ((ticks > 0) && xQueueReceive(af_ctrlr->evt_que, out_res, ticks) != pdTRUE) {
ret = ESP_ERR_TIMEOUT;
}
return ret;
}
esp_err_t esp_isp_af_controller_start_continuous_statistics(isp_af_ctrlr_t af_ctrlr)
{
ESP_RETURN_ON_FALSE_ISR(af_ctrlr, ESP_ERR_INVALID_ARG, TAG, "invalid argument: null pointer");
ESP_RETURN_ON_FALSE_ISR(af_ctrlr->fsm == ISP_FSM_ENABLE, ESP_ERR_INVALID_STATE, TAG, "controller isn't in enable state");
af_ctrlr->fsm = ISP_FSM_START;
isp_ll_af_enable_auto_update(af_ctrlr->isp_proc->hal.hw, true);
return ESP_OK;
}
esp_err_t esp_isp_af_controller_stop_continuous_statistics(isp_af_ctrlr_t af_ctrlr)
{
ESP_RETURN_ON_FALSE_ISR(af_ctrlr, ESP_ERR_INVALID_ARG, TAG, "invalid argument: null pointer");
ESP_RETURN_ON_FALSE_ISR(af_ctrlr->fsm == ISP_FSM_START, ESP_ERR_INVALID_STATE, TAG, "controller isn't in continuous state");
isp_ll_af_enable_auto_update(af_ctrlr->isp_proc->hal.hw, false);
af_ctrlr->fsm = ISP_FSM_ENABLE;
return ESP_OK;
}
@ -183,13 +250,17 @@ esp_err_t esp_isp_af_env_detector_register_event_callbacks(isp_af_ctrlr_t af_ctr
ESP_RETURN_ON_FALSE(af_ctrlr->fsm == ISP_FSM_INIT, ESP_ERR_INVALID_STATE, TAG, "detector isn't in the init state");
#if CONFIG_ISP_ISR_IRAM_SAFE
if (cbs->on_env_statistics_done) {
ESP_RETURN_ON_FALSE(esp_ptr_in_iram(cbs->on_env_statistics_done), ESP_ERR_INVALID_ARG, TAG, "on_env_statistics_done callback not in IRAM");
}
if (cbs->on_env_change) {
ESP_RETURN_ON_FALSE(esp_ptr_in_iram(cbs->on_env_change), ESP_ERR_INVALID_ARG, TAG, "on_env_change callback not in IRAM");
}
if (user_data) {
ESP_RETURN_ON_FALSE(esp_ptr_internal(user_data), ESP_ERR_INVALID_ARG, TAG, "user context not in internal RAM");
}
#endif
ESP_RETURN_ON_ERROR(esp_isp_register_isr(af_ctrlr->isp_proc, ISP_SUBMODULE_AF), TAG, "fail to register ISR");
af_ctrlr->cbs.on_env_statistics_done = cbs->on_env_statistics_done;
af_ctrlr->cbs.on_env_change = cbs->on_env_change;
af_ctrlr->user_data = user_data;
@ -209,36 +280,47 @@ esp_err_t esp_isp_af_env_detector_set_threshold(isp_af_ctrlr_t af_ctrlr, int def
/*---------------------------------------------------------------
INTR
---------------------------------------------------------------*/
static bool IRAM_ATTR s_af_env_isr(isp_af_ctrlr_t af_ctrlr)
static void IRAM_ATTR s_isp_af_default_isr(void *arg)
{
bool need_yield = false;
isp_af_ctrlr_t af_ctrlr = (isp_af_ctrlr_t)arg;
isp_proc_handle_t proc = af_ctrlr->isp_proc;
uint32_t af_events = isp_hal_check_clear_intr_event(&proc->hal, ISP_LL_EVENT_AF_MASK);
bool need_yield = false;
esp_isp_af_env_detector_evt_data_t edata = {};
if (af_ctrlr->cbs.on_env_change(af_ctrlr, &edata, af_ctrlr->user_data)) {
need_yield |= true;
if (af_events) {
// Get the statistics result
for (int i = 0; i < SOC_ISP_AF_WINDOW_NUMS; i++) {
edata.af_result.definition[i] = isp_ll_af_get_window_sum(proc->hal.hw, i);
edata.af_result.luminance[i] = isp_ll_af_get_window_lum(proc->hal.hw, i);
}
}
return need_yield;
}
bool IRAM_ATTR esp_isp_af_isr(isp_proc_handle_t proc, uint32_t af_events)
{
/**
* HW events are cleared in the ISP ISR dispatcher.
* We only deal with HW events
* Deal with the interrupts.
* Now only one detector.
* Should decide a detector instance according to the hw event.
*/
bool need_yield = false;
if (af_events & ISP_LL_EVENT_AF_FDONE) {
BaseType_t high_task_awake = false;
// Send the event data to the queue, overwrite the legacy one if exist
xQueueOverwriteFromISR(af_ctrlr->evt_que, &edata.af_result, &high_task_awake);
// Invoke the callback if the callback is registered
need_yield |= high_task_awake == pdTRUE;
if (af_ctrlr->cbs.on_env_statistics_done) {
need_yield |= af_ctrlr->cbs.on_env_statistics_done(af_ctrlr, &edata, af_ctrlr->user_data);
}
}
if (af_events & ISP_LL_EVENT_AF_ENV) {
/**
* Now only one detector.
* Should decide a detector instance according to the hw event.
*/
isp_af_ctrlr_t af_ctrlr = proc->af_ctlr[0];
need_yield |= s_af_env_isr(af_ctrlr);
// Invoke the callback if the callback is registered
if (af_ctrlr->cbs.on_env_change) {
need_yield |= af_ctrlr->cbs.on_env_change(af_ctrlr, &edata, af_ctrlr->user_data);
}
}
return need_yield;
if (need_yield) {
portYIELD_FROM_ISR();
}
}

View File

@ -12,29 +12,14 @@
#include "esp_heap_caps.h"
#include "freertos/FreeRTOS.h"
#include "esp_clk_tree.h"
#include "driver/isp.h"
#include "driver/isp_core.h"
#include "esp_private/periph_ctrl.h"
#include "esp_private/mipi_csi_share_hw_ctrl.h"
#include "hal/hal_utils.h"
#include "hal/isp_types.h"
#include "hal/isp_hal.h"
#include "hal/isp_ll.h"
#include "soc/mipi_csi_bridge_struct.h"
#include "soc/isp_periph.h"
#include "isp_internal.h"
#if CONFIG_ISP_ISR_IRAM_SAFE
#define ISP_INTR_ALLOC_FLAGS (ESP_INTR_FLAG_IRAM)
#else
#define ISP_INTR_ALLOC_FLAGS 0
#endif
#if CONFIG_ISP_ISR_IRAM_SAFE
#define ISP_MEM_ALLOC_CAPS (MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT)
#else
#define ISP_MEM_ALLOC_CAPS MALLOC_CAP_DEFAULT
#endif
typedef struct isp_platform_t {
_lock_t mutex;
isp_processor_t *processors[SOC_ISP_NUMS];
@ -202,100 +187,3 @@ esp_err_t esp_isp_disable(isp_proc_handle_t proc)
return ESP_OK;
}
/*---------------------------------------------------------------
INTR
---------------------------------------------------------------*/
static void IRAM_ATTR s_isp_isr_dispatcher(void *arg)
{
isp_processor_t *proc = (isp_processor_t *)arg;
bool need_yield = false;
//Check and clear hw events
uint32_t af_events = isp_hal_check_clear_intr_event(&proc->hal, ISP_LL_EVENT_AF_MASK);
bool do_dispatch = false;
//Deal with hw events
if (af_events) {
portENTER_CRITICAL_ISR(&proc->spinlock);
do_dispatch = proc->af_isr_added;
portEXIT_CRITICAL_ISR(&proc->spinlock);
if (do_dispatch) {
need_yield |= esp_isp_af_isr(proc, af_events);
}
do_dispatch = false;
}
if (need_yield) {
portYIELD_FROM_ISR();
}
}
esp_err_t esp_isp_register_isr(isp_proc_handle_t proc, isp_submodule_t submodule)
{
esp_err_t ret = ESP_FAIL;
ESP_RETURN_ON_FALSE(proc, ESP_ERR_INVALID_ARG, TAG, "invalid argument: null pointer");
bool do_alloc = false;
portENTER_CRITICAL(&proc->spinlock);
proc->isr_ref_counts++;
if (proc->isr_ref_counts == 1) {
assert(!proc->intr_hdl);
do_alloc = true;
}
switch (submodule) {
case ISP_SUBMODULE_AF:
proc->af_isr_added = true;
break;
default:
assert(false);
}
portEXIT_CRITICAL(&proc->spinlock);
if (do_alloc) {
ret = esp_intr_alloc(isp_hw_info.instances[proc->proc_id].irq, ISP_INTR_ALLOC_FLAGS, s_isp_isr_dispatcher, (void *)proc, &proc->intr_hdl);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "no intr source");
return ret;
}
esp_intr_enable(proc->intr_hdl);
}
return ESP_OK;
}
esp_err_t esp_isp_deregister_isr(isp_proc_handle_t proc, isp_submodule_t submodule)
{
esp_err_t ret = ESP_FAIL;
ESP_RETURN_ON_FALSE(proc, ESP_ERR_INVALID_ARG, TAG, "invalid argument: null pointer");
bool do_free = false;
portENTER_CRITICAL(&proc->spinlock);
proc->isr_ref_counts--;
assert(proc->isr_ref_counts >= 0);
if (proc->isr_ref_counts == 0) {
assert(proc->intr_hdl);
do_free = true;
}
switch (submodule) {
case ISP_SUBMODULE_AF:
proc->af_isr_added = false;
break;
default:
assert(false);
}
portEXIT_CRITICAL(&proc->spinlock);
if (do_free) {
esp_intr_disable(proc->intr_hdl);
ret = esp_intr_free(proc->intr_hdl);
if (ret != ESP_OK) {
return ret;
}
}
return ESP_OK;
}

View File

@ -16,7 +16,11 @@
#include "esp_heap_caps.h"
#include "esp_intr_alloc.h"
#include "freertos/FreeRTOS.h"
#include "freertos/queue.h"
#include "freertos/idf_additions.h"
#include "driver/isp_types.h"
#include "hal/isp_hal.h"
#include "hal/isp_ll.h"
#include "hal/isp_types.h"
#include "soc/isp_periph.h"
#include "soc/soc_caps.h"
@ -25,32 +29,24 @@
extern "C" {
#endif
#if CONFIG_ISP_ISR_IRAM_SAFE
#define ISP_INTR_ALLOC_FLAGS (ESP_INTR_FLAG_SHARED | ESP_INTR_FLAG_INTRDISABLED | ESP_INTR_FLAG_IRAM)
#define ISP_MEM_ALLOC_CAPS (MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT)
#else
#define ISP_INTR_ALLOC_FLAGS (ESP_INTR_FLAG_SHARED | ESP_INTR_FLAG_INTRDISABLED)
#define ISP_MEM_ALLOC_CAPS MALLOC_CAP_DEFAULT
#endif
typedef enum {
ISP_FSM_INIT,
ISP_FSM_ENABLE,
ISP_FSM_START,
} isp_fsm_t;
/*---------------------------------------------------------------
Driver Context
---------------------------------------------------------------*/
typedef enum {
ISP_SUBMODULE_AF,
} isp_submodule_t;
typedef struct isp_af_controller_t isp_af_controller_t;
typedef struct isp_processor_t isp_processor_t;
struct isp_af_controller_t {
int id;
isp_fsm_t fsm;
portMUX_TYPE spinlock;
isp_processor_t *isp_proc;
esp_isp_af_env_config_t config;
esp_isp_af_env_detector_evt_cbs_t cbs;
void *user_data;
};
struct isp_processor_t {
typedef struct isp_processor_t {
int proc_id;
isp_hal_context_t hal;
#if SOC_ISP_SHARE_CSI_BRG
@ -59,22 +55,10 @@ struct isp_processor_t {
#endif
isp_fsm_t isp_fsm;
portMUX_TYPE spinlock;
intr_handle_t intr_hdl;
/* sub module contexts */
isp_af_controller_t *af_ctlr[SOC_ISP_AF_CTLR_NUMS];
/* should be accessed within isp_processor_t spinlock */
int isr_ref_counts;
bool af_isr_added;
};
/*---------------------------------------------------------------
INTR
---------------------------------------------------------------*/
esp_err_t esp_isp_register_isr(isp_proc_handle_t proc, isp_submodule_t submodule);
esp_err_t esp_isp_deregister_isr(isp_proc_handle_t proc, isp_submodule_t submodule);
bool esp_isp_af_isr(isp_proc_handle_t proc, uint32_t af_events);
isp_af_ctrlr_t af_ctlr[SOC_ISP_AF_CTLR_NUMS];
} isp_processor_t;
#ifdef __cplusplus
}

View File

@ -537,6 +537,7 @@ static inline void isp_ll_af_set_window_range(isp_dev_t *hw, uint32_t window_id,
*
* @return Window sum
*/
__attribute__((always_inline))
static inline uint32_t isp_ll_af_get_window_sum(isp_dev_t *hw, uint32_t window_id)
{
switch (window_id) {
@ -560,6 +561,7 @@ static inline uint32_t isp_ll_af_get_window_sum(isp_dev_t *hw, uint32_t window_i
*
* @return Window lum
*/
__attribute__((always_inline))
static inline uint32_t isp_ll_af_get_window_lum(isp_dev_t *hw, uint32_t window_id)
{
switch (window_id) {
@ -742,6 +744,18 @@ static inline uint32_t isp_ll_get_intr_status(isp_dev_t *hw)
return hw->int_st.val;
}
/**
* @brief Get interrupt status reg address
*
* @param[in] hw Hardware instance address
*
* @return Interrupt status reg address
*/
static inline uint32_t isp_ll_get_intr_status_reg_addr(isp_dev_t *hw)
{
return (uint32_t)&(hw->int_st);
}
/**
* @brief Get interrupt raw
*

View File

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@ -50,14 +50,6 @@ void isp_hal_init(isp_hal_context_t *hal, int isp_id);
*/
void isp_hal_af_window_config(const isp_hal_context_t *hal, int window_id, const isp_af_window_t *window);
/**
* @brief Get AF oneshot result
*
* @param[in] hal Context of the HAL layer
* @param[out] out_res AF result
*/
void isp_hal_af_get_oneshot_result(const isp_hal_context_t *hal, isp_af_result_t *out_res);
/*---------------------------------------------------------------
INTR
---------------------------------------------------------------*/

View File

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@ -48,6 +48,12 @@ typedef enum {
/*---------------------------------------------------------------
AF
---------------------------------------------------------------*/
#if SOC_ISP_AF_WINDOW_NUMS
#define ISP_AF_WINDOW_NUM SOC_ISP_AF_WINDOW_NUMS // The AF window number for sampling
#else
#define ISP_AF_WINDOW_NUM 0
#endif
/**
* @brief ISP AF window
*/
@ -62,10 +68,8 @@ typedef struct {
* @brief ISP AF result
*/
typedef struct {
#if SOC_ISP_SUPPORTED
int definition[SOC_ISP_AF_WINDOW_NUMS]; ///< Definition, it refers how clear and sharp an image is
int luminance[SOC_ISP_AF_WINDOW_NUMS]; ///< Luminance, it refers how luminant an image is
#endif
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;
#ifdef __cplusplus

View File

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@ -31,22 +31,6 @@ void isp_hal_af_window_config(const isp_hal_context_t *hal, int window_id, const
isp_ll_af_set_window_range(hal->hw, window_id, window->top_left_x, window->top_left_y, window->bottom_right_x, window->bottom_right_y);
}
void isp_hal_af_get_oneshot_result(const isp_hal_context_t *hal, isp_af_result_t *out_res)
{
isp_ll_clear_intr(hal->hw, ISP_LL_EVENT_AF_FDONE);
isp_ll_af_manual_update(hal->hw);
while (!(isp_ll_get_intr_raw(hal->hw) & ISP_LL_EVENT_AF_FDONE)) {
;
}
for (int i = 0; i < SOC_ISP_AF_WINDOW_NUMS; i++) {
out_res->definition[i] = isp_ll_af_get_window_sum(hal->hw, i);
out_res->luminance[i] = isp_ll_af_get_window_lum(hal->hw, i);
}
}
/*---------------------------------------------------------------
INTR, put in iram
---------------------------------------------------------------*/

View File

@ -25,6 +25,7 @@ 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.
- `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.
@ -114,7 +115,7 @@ Calling :cpp:func:`esp_isp_af_controller_disable` does the opposite, that is, pu
Get Auto-Focus (AF) Oneshot Result
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Calling :cpp:func:`esp_isp_af_controller_get_oneshot_result` to get oneshot AF result. You can take following code as reference.
Calling :cpp:func:`esp_isp_af_controller_get_oneshot_statistics` to get oneshot AF statistics result. You can take following code as reference.
.. code:: c
@ -123,8 +124,42 @@ Calling :cpp:func:`esp_isp_af_controller_get_oneshot_result` to get oneshot AF r
};
isp_af_ctrlr_t af_ctrlr = NULL;
ESP_ERROR_CHECK(esp_isp_new_af_controller(isp_proc, &af_config, &af_ctrlr));
ESP_ERROR_CHECK(esp_isp_af_controller_enable(af_ctrlr));
isp_af_result_t result = {};
ESP_ERROR_CHECK(esp_isp_af_controller_get_oneshot_result(ctx->af_ctlr, &result));
/* 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_ctrlr_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
// ......
// 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
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -161,6 +196,7 @@ Register Image Signal Processor (ISP) Auto-Focus (AF) Environment Detector Event
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`:
- :cpp:member:`esp_isp_af_env_detector_evt_cbs_t::on_env_statistics_done` sets a callback function for environment statistics done. 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_af_env_detector_callback_t`.
- :cpp:member:`esp_isp_af_env_detector_evt_cbs_t::on_env_change` sets a callback function for environment change. 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_af_env_detector_callback_t`.
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.