diff --git a/components/esp_adc/CMakeLists.txt b/components/esp_adc/CMakeLists.txt index 5c8c3ed1a3..6d3d4fbaba 100644 --- a/components/esp_adc/CMakeLists.txt +++ b/components/esp_adc/CMakeLists.txt @@ -10,6 +10,9 @@ set(srcs "adc_cali.c" if(CONFIG_SOC_ADC_DMA_SUPPORTED) list(APPEND srcs "adc_continuous.c") + if(CONFIG_SOC_ADC_MONITOR_SUPPORTED) + list(APPEND srcs "adc_monitor.c") + endif() endif() if(CONFIG_SOC_ADC_DIG_IIR_FILTER_SUPPORTED) diff --git a/components/esp_adc/adc_continuous_internal.h b/components/esp_adc/adc_continuous_internal.h index e352c300e7..ab846f1285 100644 --- a/components/esp_adc/adc_continuous_internal.h +++ b/components/esp_adc/adc_continuous_internal.h @@ -24,6 +24,7 @@ #endif #include "esp_adc/adc_filter.h" +#include "esp_adc/adc_monitor.h" #ifdef __cplusplus extern "C" { @@ -34,21 +35,38 @@ typedef enum { ADC_FSM_STARTED, } adc_fsm_t; +typedef enum { + ADC_MONITOR_FSM_INIT, + ADC_MONITOR_FSM_ENABLED, +} adc_monitor_fsm_t; + /*--------------------------------------------------------------- Driver Context ---------------------------------------------------------------*/ typedef struct adc_iir_filter_t adc_iir_filter_t; +typedef struct adc_monitor_t adc_monitor_t; typedef struct adc_continuous_ctx_t adc_continuous_ctx_t; /** * @brief ADC iir filter context */ struct adc_iir_filter_t { - adc_digi_iir_filter_t filter_id; // Filter ID + adc_digi_iir_filter_t filter_id; // Filter ID adc_continuous_iir_filter_config_t cfg; //filter configuration adc_continuous_ctx_t *continuous_ctx; //ADC continuous driver context }; +/** + * @brief ADC digi monitor context + */ +struct adc_monitor_t { + adc_monitor_id_t monitor_id; // monitor unit number + adc_monitor_fsm_t fsm; // monitor status indicator + adc_monitor_config_t config; // monitor configuration + adc_monitor_evt_cbs_t cbs; // monitor thresh callbacks + void *user_data; // user data pointer to use in cb +}; + /** * @brief ADC continuous driver context */ @@ -79,6 +97,9 @@ struct adc_continuous_ctx_t { #if SOC_ADC_DIG_IIR_FILTER_SUPPORTED adc_iir_filter_t *iir_filter[SOC_ADC_DIGI_IIR_FILTER_NUM]; //ADC IIR filter context #endif +#if SOC_ADC_MONITOR_SUPPORTED + adc_monitor_t *adc_monitor[SOC_ADC_DIGI_MONITOR_NUM]; // adc monitor context +#endif }; diff --git a/components/esp_adc/adc_monitor.c b/components/esp_adc/adc_monitor.c new file mode 100644 index 0000000000..75ceaf4674 --- /dev/null +++ b/components/esp_adc/adc_monitor.c @@ -0,0 +1,274 @@ +/* + * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include "esp_check.h" +#include "esp_memory_utils.h" +#include "esp_intr_alloc.h" +#include "esp_heap_caps.h" +#include "freertos/FreeRTOS.h" +#include "adc_continuous_internal.h" +#include "soc/periph_defs.h" +#include "esp_adc/adc_monitor.h" + +static const char *MNTOR_TAG = "adc_monitor"; + +/** + * @brief context for adc continuous driver + */ +typedef struct adc_monitor_platform_t { + adc_continuous_ctx_t *continuous_ctx; // ADC continuous driver context + intr_handle_t monitor_intr_handle; // monitor intr handler + portMUX_TYPE monitor_spinlock; // spinlock +} adc_monitor_platform_t; + +// Global context of adc monitor, other member will be lazy loaded +static adc_monitor_platform_t s_adc_monitor_platform = {.monitor_spinlock = portMUX_INITIALIZER_UNLOCKED}; + + +#if CONFIG_IDF_TARGET_ESP32S2 +// Monitor unit index need equal to ADC unit index on ESP32S2 +static atomic_bool s_adc_monitor_claimed[SOC_ADC_DIGI_MONITOR_NUM] = {}; +static esp_err_t s_adc_monitor_claim(adc_continuous_handle_t handle, adc_monitor_t *monitor_ctx, adc_unit_t unit) +{ + assert(handle && monitor_ctx); + esp_err_t ret = ESP_ERR_NOT_FOUND; + bool false_var = false; + if (atomic_compare_exchange_strong(&s_adc_monitor_claimed[unit], &false_var, true)) { + monitor_ctx->monitor_id = unit; + handle->adc_monitor[unit] = monitor_ctx; + ret = ESP_OK; + } else { + ESP_LOGE(MNTOR_TAG, "monitor %d already in use", (int)unit); + } + return ret; +} + +static esp_err_t s_adc_monitor_release(adc_monitor_t *monitor_ctx) +{ + assert(monitor_ctx); + esp_err_t ret = ESP_ERR_NOT_FOUND; + bool true_var = true; + if (atomic_compare_exchange_strong(&s_adc_monitor_claimed[monitor_ctx->monitor_id], &true_var, false)) { + s_adc_monitor_platform.continuous_ctx->adc_monitor[monitor_ctx->monitor_id] = NULL; + ret = ESP_OK; + } + return ret; +} + +#else +static esp_err_t s_adc_monitor_claim(adc_continuous_handle_t handle, adc_monitor_t *monitor_ctx, adc_unit_t unit) +{ + (void)unit; + assert(handle && monitor_ctx); + portENTER_CRITICAL(&s_adc_monitor_platform.monitor_spinlock); + for (int i = 0; i < SOC_ADC_DIGI_MONITOR_NUM; i++) { + if (!handle->adc_monitor[i]) { + monitor_ctx->monitor_id = i; + handle->adc_monitor[i] = monitor_ctx; + portEXIT_CRITICAL(&s_adc_monitor_platform.monitor_spinlock); + return ESP_OK; + } + } + portEXIT_CRITICAL(&s_adc_monitor_platform.monitor_spinlock); + ESP_LOGE(MNTOR_TAG, "no free monitor"); + return ESP_ERR_NOT_FOUND; +} + +static esp_err_t s_adc_monitor_release(adc_monitor_t *monitor_ctx) +{ + assert(monitor_ctx); + portENTER_CRITICAL(&s_adc_monitor_platform.monitor_spinlock); + s_adc_monitor_platform.continuous_ctx->adc_monitor[monitor_ctx->monitor_id] = NULL; + portEXIT_CRITICAL(&s_adc_monitor_platform.monitor_spinlock); + + return ESP_OK; +} +#endif + +static void IRAM_ATTR s_adc_digi_monitor_isr(void *args) +{ + bool need_yield = false; + uint32_t intr_val = *((uint32_t *)adc_ll_digi_monitor_get_intr_status_addr()); + + // clear all intr flags as have save intr status in `intr_val` + adc_ll_digi_monitor_clear_intr(); + + for (uint8_t i = 0; i < SOC_ADC_DIGI_MONITOR_NUM; i++) { + adc_monitor_handle_t monitor_handle = s_adc_monitor_platform.continuous_ctx->adc_monitor[i]; + + // check if high threshold alert + if (intr_val & ADC_LL_GET_HIGH_THRES_MASK(i)) { + assert(monitor_handle); + assert(monitor_handle->monitor_id == i); + + if (monitor_handle->cbs.on_over_high_thresh) { + adc_monitor_evt_data_t event_data = {}; + need_yield |= monitor_handle->cbs.on_over_high_thresh(monitor_handle, &event_data, monitor_handle->user_data); + } + } + // check if low threshold alert + if (intr_val & ADC_LL_GET_LOW_THRES_MASK(i)) { + assert(monitor_handle); + assert(monitor_handle->monitor_id == i); + + if (monitor_handle->cbs.on_below_low_thresh) { + adc_monitor_evt_data_t event_data = {}; + need_yield |= monitor_handle->cbs.on_below_low_thresh(monitor_handle, &event_data, monitor_handle->user_data); + } + } + } + if (need_yield) { + portYIELD_FROM_ISR(); + } +} + +static esp_err_t adc_monitor_intr_alloc(void) +{ + esp_err_t ret = ESP_OK; + int intr_flags = ESP_INTR_FLAG_LOWMED; +#if CONFIG_ADC_CONTINUOUS_ISR_IRAM_SAFE + intr_flags |= ESP_INTR_FLAG_IRAM; +#endif + +#if SOC_ADC_TEMPERATURE_SHARE_INTR + intr_flags |= ESP_INTR_FLAG_SHARED; + ret = esp_intr_alloc_intrstatus(ETS_APB_ADC_INTR_SOURCE, intr_flags, + (uint32_t)adc_ll_digi_monitor_get_intr_status_addr(), + ADC_LL_THRES_ALL_INTR_ST_M, s_adc_digi_monitor_isr, NULL, &s_adc_monitor_platform.monitor_intr_handle); +#else + + ret = esp_intr_alloc(ETS_APB_ADC_INTR_SOURCE, intr_flags, s_adc_digi_monitor_isr, NULL, &s_adc_monitor_platform.monitor_intr_handle); +#endif //SOC_ADC_TEMPERATURE_SHARE_INTR + return ret; +} + +//-------------------------------------------PUBLIC APIs--------------------------------------------// +esp_err_t adc_new_continuous_monitor(adc_continuous_handle_t handle, const adc_monitor_config_t *monitor_cfg, adc_monitor_handle_t *ret_handle) +{ + esp_err_t ret; + ESP_RETURN_ON_FALSE(handle && monitor_cfg && ret_handle, ESP_ERR_INVALID_ARG, MNTOR_TAG, "invalid argument: null pointer"); + ESP_RETURN_ON_FALSE(monitor_cfg->adc_unit < SOC_ADC_PERIPH_NUM, ESP_ERR_INVALID_ARG, MNTOR_TAG, "invalid adc_unit"); + ESP_RETURN_ON_FALSE(handle->fsm == ADC_FSM_INIT, ESP_ERR_INVALID_STATE, MNTOR_TAG, "ADC continuous driver should be in init state"); +#if CONFIG_IDF_TARGET_ESP32S2 + ESP_RETURN_ON_FALSE(!((monitor_cfg->h_threshold >= 0) && (monitor_cfg->l_threshold >= 0)), ESP_ERR_NOT_SUPPORTED, MNTOR_TAG, "ESP32S2 support only one threshold"); +#endif + + // alloc handler memory + adc_monitor_t *monitor_ctx = heap_caps_calloc(1, sizeof(adc_monitor_t), MALLOC_CAP_INTERNAL); + ESP_RETURN_ON_FALSE(monitor_ctx, ESP_ERR_NO_MEM, MNTOR_TAG, "no mem"); + + // alloc monitor hardware + ESP_GOTO_ON_ERROR(s_adc_monitor_claim(handle, monitor_ctx, monitor_cfg->adc_unit), claim_err, MNTOR_TAG, "ADC monitor claim failed"); + memcpy(&monitor_ctx->config, monitor_cfg, sizeof(adc_monitor_config_t)); + s_adc_monitor_platform.continuous_ctx = handle; + + // alloc cpu intr + portENTER_CRITICAL(&s_adc_monitor_platform.monitor_spinlock); + bool alloc_intr = !s_adc_monitor_platform.monitor_intr_handle; + portEXIT_CRITICAL(&s_adc_monitor_platform.monitor_spinlock); + if (alloc_intr) { + ESP_GOTO_ON_ERROR(adc_monitor_intr_alloc(), intr_err, MNTOR_TAG, "esp intr alloc failed"); + } + + // config hardware + adc_ll_digi_monitor_clear_intr(); + adc_ll_digi_monitor_set_thres(monitor_ctx->monitor_id, monitor_ctx->config.adc_unit, monitor_ctx->config.channel, monitor_ctx->config.h_threshold, monitor_ctx->config.l_threshold); + + *ret_handle = monitor_ctx; + return ESP_OK; + +intr_err: + s_adc_monitor_release(monitor_ctx); +claim_err: + free(monitor_ctx); + return ret; +} + +esp_err_t adc_continuous_monitor_register_event_callbacks(adc_monitor_handle_t monitor_handle, const adc_monitor_evt_cbs_t *cbs, void *user_data) +{ + ESP_RETURN_ON_FALSE(monitor_handle && cbs, ESP_ERR_INVALID_ARG, MNTOR_TAG, "invalid argument: null pointer"); + ESP_RETURN_ON_FALSE(monitor_handle->fsm == ADC_MONITOR_FSM_INIT, ESP_ERR_INVALID_STATE, MNTOR_TAG, "monitor should be in init state"); + ESP_RETURN_ON_FALSE(!(monitor_handle->cbs.on_over_high_thresh || monitor_handle->cbs.on_below_low_thresh), ESP_ERR_INVALID_STATE, MNTOR_TAG, "callbacks had beed registered"); +#if CONFIG_IDF_TARGET_ESP32S2 + ESP_RETURN_ON_FALSE(!(cbs->on_below_low_thresh && cbs->on_over_high_thresh), ESP_ERR_NOT_SUPPORTED, MNTOR_TAG, "ESP32S2 support only one threshold"); +#endif + + // If iram_safe enabled, check if user_data and cbs is iram_safe +#if CONFIG_ADC_CONTINUOUS_ISR_IRAM_SAFE + if (cbs->on_over_high_thresh) { + ESP_RETURN_ON_FALSE(esp_ptr_in_iram(cbs->on_over_high_thresh), ESP_ERR_INVALID_ARG, MNTOR_TAG, "on_over_high_thresh func not in iram"); + } + if (cbs->on_below_low_thresh) { + ESP_RETURN_ON_FALSE(esp_ptr_in_iram(cbs->on_below_low_thresh), ESP_ERR_INVALID_ARG, MNTOR_TAG, "on_below_low_thresh func not in iram"); + } + if (user_data) { + ESP_RETURN_ON_FALSE(esp_ptr_in_dram(user_data) || esp_ptr_in_diram_dram(user_data), ESP_ERR_INVALID_ARG, MNTOR_TAG, "user_data not in iram"); + } +#endif + + memcpy(&monitor_handle->cbs, cbs, sizeof(adc_monitor_evt_cbs_t)); + monitor_handle->user_data = user_data; + return ESP_OK; +} + +esp_err_t adc_continuous_monitor_enable(adc_monitor_handle_t monitor_handle) +{ + ESP_RETURN_ON_FALSE(monitor_handle, ESP_ERR_INVALID_ARG, MNTOR_TAG, "invalid argument: null pointer"); + ESP_RETURN_ON_FALSE(monitor_handle->fsm == ADC_MONITOR_FSM_INIT, ESP_ERR_INVALID_STATE, MNTOR_TAG, "monitor should be in init state"); + + // enable peripheral intr_ena + if ((monitor_handle->config.h_threshold >= 0)) { + adc_ll_digi_monitor_enable_intr(monitor_handle->monitor_id, ADC_MONITOR_MODE_HIGH, true); + } + if ((monitor_handle->config.l_threshold >= 0)) { + adc_ll_digi_monitor_enable_intr(monitor_handle->monitor_id, ADC_MONITOR_MODE_LOW, true); + } + + adc_ll_digi_monitor_user_start(monitor_handle->monitor_id, true); + monitor_handle->fsm = ADC_MONITOR_FSM_ENABLED; + return esp_intr_enable(s_adc_monitor_platform.monitor_intr_handle); +} + +esp_err_t adc_continuous_monitor_disable(adc_monitor_handle_t monitor_handle) +{ + ESP_RETURN_ON_FALSE(monitor_handle, ESP_ERR_INVALID_ARG, MNTOR_TAG, "invalid argument: null pointer"); + ESP_RETURN_ON_FALSE(monitor_handle->fsm == ADC_MONITOR_FSM_ENABLED, ESP_ERR_INVALID_STATE, MNTOR_TAG, "monitor not in running"); + + // disable peripheral intr_ena + if ((monitor_handle->config.h_threshold >= 0)) { + adc_ll_digi_monitor_enable_intr(monitor_handle->monitor_id, ADC_MONITOR_MODE_HIGH, false); + } + if ((monitor_handle->config.l_threshold >= 0)) { + adc_ll_digi_monitor_enable_intr(monitor_handle->monitor_id, ADC_MONITOR_MODE_LOW, false); + } + + adc_ll_digi_monitor_user_start(monitor_handle->monitor_id, false); + monitor_handle->fsm = ADC_MONITOR_FSM_INIT; + return esp_intr_disable(s_adc_monitor_platform.monitor_intr_handle); +} + +esp_err_t adc_del_continuous_monitor(adc_monitor_handle_t monitor_handle) +{ + ESP_RETURN_ON_FALSE(monitor_handle, ESP_ERR_INVALID_ARG, MNTOR_TAG, "invalid argument: null pointer"); + ESP_RETURN_ON_FALSE((monitor_handle->fsm == ADC_MONITOR_FSM_INIT) && (s_adc_monitor_platform.continuous_ctx->fsm == ADC_FSM_INIT), \ + ESP_ERR_INVALID_STATE, MNTOR_TAG, "monitor and ADC continuous driver should all be in init state"); + ESP_RETURN_ON_ERROR(s_adc_monitor_release(monitor_handle), MNTOR_TAG, "monitor not find or isn't in use"); + + for (int i = 0; i < SOC_ADC_DIGI_MONITOR_NUM; i++) { + if (s_adc_monitor_platform.continuous_ctx->adc_monitor[i]) { + // If any other monitor not freed, then delete self and exit now. del_monitor is complete + free(monitor_handle); + return ESP_OK; + } + } + + // If no monitor is using, the release intr handle as well + ESP_RETURN_ON_ERROR(esp_intr_free(s_adc_monitor_platform.monitor_intr_handle), MNTOR_TAG, "esp intr release failed\n"); + s_adc_monitor_platform.monitor_intr_handle = NULL; + free(monitor_handle); + return ESP_OK; +} diff --git a/components/esp_adc/include/esp_adc/adc_monitor.h b/components/esp_adc/include/esp_adc/adc_monitor.h new file mode 100644 index 0000000000..c6a37695c7 --- /dev/null +++ b/components/esp_adc/include/esp_adc/adc_monitor.h @@ -0,0 +1,123 @@ +/* + * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + + +#include "adc_continuous.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Type of adc digi monitor handle + */ +typedef struct adc_monitor_t *adc_monitor_handle_t; + +/** + * @brief ADC digital controller (DMA mode) monitor configuration. + */ +typedef struct { + adc_unit_t adc_unit; /*! #include #include "esp_log.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" #include "soc/adc_periph.h" #include "esp_adc/adc_oneshot.h" +#include "esp_adc/adc_monitor.h" #include "driver/gpio.h" #include "driver/rtc_io.h" #include "test_common_adc.h" @@ -263,3 +266,215 @@ TEST_CASE("test ADC2 Single Read with Light Sleep", "[adc][manul][ignore]") #endif //#if (SOC_ADC_PERIPH_NUM >= 2) && !CONFIG_IDF_TARGET_ESP32C3 #endif //#if SOC_ADC_CALIBRATION_V1_SUPPORTED + + +#if SOC_ADC_MONITOR_SUPPORTED && CONFIG_SOC_ADC_DMA_SUPPORTED +#if CONFIG_IDF_TARGET_ESP32S2 +#define TEST_ADC_FORMATE_TYPE ADC_DIGI_OUTPUT_FORMAT_TYPE1 +#else +#define TEST_ADC_FORMATE_TYPE ADC_DIGI_OUTPUT_FORMAT_TYPE2 +#endif +bool IRAM_ATTR test_high_cb(adc_monitor_handle_t monitor_handle, const adc_monitor_evt_data_t *event_data, void *user_data){ + return false; +} +TEST_CASE("ADC continuous monitor init_deinit", "[adc]") +{ + adc_continuous_handle_t handle = NULL; + adc_continuous_handle_cfg_t adc_config = { + .max_store_buf_size = 1024, + .conv_frame_size = SOC_ADC_DIGI_DATA_BYTES_PER_CONV * 2, + }; + TEST_ESP_OK(adc_continuous_new_handle(&adc_config, &handle)); + + adc_digi_pattern_config_t adc_pattern[SOC_ADC_PATT_LEN_MAX] = {0}; + for (int i = 0; i < 1; i++) { + adc_pattern[i].atten = ADC_ATTEN_DB_11; + adc_pattern[i].channel = i; + adc_pattern[i].unit = ADC_UNIT_1; + adc_pattern[i].bit_width = SOC_ADC_DIGI_MAX_BITWIDTH; + } + adc_continuous_config_t dig_cfg = { + .pattern_num = 1, + .adc_pattern = adc_pattern, + .sample_freq_hz = SOC_ADC_SAMPLE_FREQ_THRES_LOW, + .conv_mode = ADC_CONV_SINGLE_UNIT_1, + .format = TEST_ADC_FORMATE_TYPE, + }; + TEST_ESP_OK(adc_continuous_config(handle, &dig_cfg)); + + //try to enable without installed + adc_monitor_handle_t monitor_handle = NULL; + TEST_ESP_ERR(ESP_ERR_INVALID_ARG, adc_continuous_monitor_enable(monitor_handle)); + + //try to install with invalid argument + adc_monitor_config_t adc_monitor_cfg = { + .adc_unit = 2, + .channel = 2, + .h_threshold = 3000, + .l_threshold = -1, + }; + TEST_ESP_ERR(ESP_ERR_INVALID_ARG, adc_new_continuous_monitor(handle, &adc_monitor_cfg, &monitor_handle)); + + //try to install when adc is running + adc_monitor_cfg.adc_unit = ADC_UNIT_1; + TEST_ESP_OK(adc_continuous_start(handle)); + TEST_ESP_ERR(ESP_ERR_INVALID_STATE, adc_new_continuous_monitor(handle, &adc_monitor_cfg, &monitor_handle)); + TEST_ESP_OK(adc_continuous_stop(handle)); + + //normal install + TEST_ESP_OK(adc_new_continuous_monitor(handle, &adc_monitor_cfg, &monitor_handle)); + + //try register callback funcs when monitor is running + adc_monitor_evt_cbs_t monitor_cb = { + .on_over_high_thresh = test_high_cb, + .on_below_low_thresh = NULL, + }; + TEST_ESP_OK(adc_continuous_monitor_enable(monitor_handle)); + TEST_ESP_ERR(ESP_ERR_INVALID_STATE, adc_continuous_monitor_register_event_callbacks(monitor_handle, &monitor_cb, NULL)); + TEST_ESP_OK(adc_continuous_monitor_disable(monitor_handle)); + + //normal register cbs + TEST_ESP_OK(adc_continuous_monitor_register_event_callbacks(monitor_handle, &monitor_cb, NULL)); + + //try init so many monitor, we totally have 2 monitors actually + adc_monitor_handle_t monitor_handle_2 = NULL, monitor_handle_3 = NULL; +#if CONFIG_IDF_TARGET_ESP32S2 + adc_monitor_cfg.adc_unit = ADC_UNIT_2; //s2 can't use two monitor on same ADC unit +#endif + TEST_ESP_OK(adc_new_continuous_monitor(handle, &adc_monitor_cfg, &monitor_handle_2)); + TEST_ESP_ERR(ESP_ERR_NOT_FOUND, adc_new_continuous_monitor(handle, &adc_monitor_cfg, &monitor_handle_3)); + + //try delete them, as monitor_handle_3 should be NULL because it should init failed + TEST_ESP_OK(adc_del_continuous_monitor(monitor_handle_2)); + TEST_ESP_ERR(ESP_ERR_INVALID_ARG, adc_del_continuous_monitor(monitor_handle_3)); + + //try register cbs again + TEST_ESP_ERR(ESP_ERR_INVALID_STATE, adc_continuous_monitor_register_event_callbacks(monitor_handle, &monitor_cb, &monitor_cb)); + + //try delete it when adc is running but monitor not running + TEST_ESP_OK(adc_continuous_start(handle)); + TEST_ESP_ERR(ESP_ERR_INVALID_STATE, adc_del_continuous_monitor(monitor_handle)); + TEST_ESP_OK(adc_continuous_stop(handle)); + + //normal option + TEST_ESP_OK(adc_continuous_monitor_enable(monitor_handle)); + TEST_ESP_OK(adc_continuous_monitor_disable(monitor_handle)); + + //normal uninstall + TEST_ESP_OK(adc_del_continuous_monitor(monitor_handle)); + + TEST_ESP_OK(adc_continuous_deinit(handle)); +} + + +/** + * NOTE: To run this special feature test case, you need wire ADC channel pin you want to monit + * to a wave output pin defined below. + * + * +---------+ + * | | + * | (adc)|------------+ + * | | | + * | (wave)|------------+ + * | | + * | ESP32 | + * +---------+ + * + * or you can connect your signals from signal generator to ESP32 pin which you monitoring + **/ +#define TEST_ADC_CHANNEL ADC_CHANNEL_0 //GPIO_1 +#define TEST_WAVE_OUT_PIN GPIO_NUM_2 //GPIO_2 +static uint32_t m1h_cnt, m1l_cnt; + +bool IRAM_ATTR m1h_cb(adc_monitor_handle_t monitor_handle, const adc_monitor_evt_data_t *event_data, void *user_data){ + m1h_cnt ++; + return false; +} +bool IRAM_ATTR m1l_cb(adc_monitor_handle_t monitor_handle, const adc_monitor_evt_data_t *event_data, void *user_data){ + m1l_cnt ++; + return false; +} +TEST_CASE("ADC continuous monitor functionary", "[adc][manual][ignore]") +{ + adc_continuous_handle_t handle = NULL; + adc_continuous_handle_cfg_t adc_config = { + .max_store_buf_size = 1024, + .conv_frame_size = SOC_ADC_DIGI_DATA_BYTES_PER_CONV * 2, + }; + TEST_ESP_OK(adc_continuous_new_handle(&adc_config, &handle)); + + adc_digi_pattern_config_t adc_pattern[SOC_ADC_PATT_LEN_MAX] = {0}; + for (int i = 0; i < 2; i++) { + adc_pattern[i].atten = ADC_ATTEN_DB_11; + adc_pattern[i].channel = TEST_ADC_CHANNEL; + adc_pattern[i].unit = ADC_UNIT_1; + adc_pattern[i].bit_width = SOC_ADC_DIGI_MAX_BITWIDTH; + } + adc_continuous_config_t dig_cfg = { + .pattern_num = 2, + .adc_pattern = adc_pattern, + .sample_freq_hz = SOC_ADC_SAMPLE_FREQ_THRES_LOW, + .conv_mode = ADC_CONV_SINGLE_UNIT_1, + .format = TEST_ADC_FORMATE_TYPE, + }; + TEST_ESP_OK(adc_continuous_config(handle, &dig_cfg)); + + //config monitor + adc_monitor_handle_t monitor_handle; + adc_monitor_config_t adc_monitor_cfg = { + .adc_unit = ADC_UNIT_1, + .channel = TEST_ADC_CHANNEL, +#if CONFIG_IDF_TARGET_ESP32S2 + .h_threshold = -1, //S2 support only one threshold for one monitor +#else + .h_threshold = 3000, +#endif + .l_threshold = 1000, + }; + adc_monitor_evt_cbs_t monitor_cb = { +#if !CONFIG_IDF_TARGET_ESP32S2 + .on_over_high_thresh = m1h_cb, +#endif + .on_below_low_thresh = m1l_cb, + }; + TEST_ESP_OK(adc_new_continuous_monitor(handle, &adc_monitor_cfg, &monitor_handle)); + TEST_ESP_OK(adc_continuous_monitor_register_event_callbacks(monitor_handle, &monitor_cb, NULL)); + + //config a pin to generate wave + gpio_config_t gpio_cfg = { + .pin_bit_mask = (1ULL << TEST_WAVE_OUT_PIN), + .mode = GPIO_MODE_INPUT_OUTPUT, + .pull_up_en = GPIO_PULLDOWN_ENABLE, + }; + TEST_ESP_OK(gpio_config(&gpio_cfg)); + + TEST_ESP_OK(adc_continuous_monitor_enable(monitor_handle)); + TEST_ESP_OK(adc_continuous_start(handle)); + + for (uint8_t i=0; i<8; i++) + { + vTaskDelay(1000); + + // check monitor cb + printf("%d\t high_cnt %4ld\tlow_cnt %4ld\n", i, m1h_cnt, m1l_cnt); + if (gpio_get_level(TEST_WAVE_OUT_PIN)) { +#if !CONFIG_IDF_TARGET_ESP32S2 + // TEST_ASSERT_UINT32_WITHIN(SOC_ADC_SAMPLE_FREQ_THRES_LOW*0.1, SOC_ADC_SAMPLE_FREQ_THRES_LOW, m1h_cnt); + // TEST_ASSERT_LESS_THAN_UINT32(5, m1l_cnt); //Actually, it will still encountered 1~2 times because hardware run very quickly +#endif + m1h_cnt = 0; + gpio_set_level(TEST_WAVE_OUT_PIN, 0); + } else { + TEST_ASSERT_UINT32_WITHIN(SOC_ADC_SAMPLE_FREQ_THRES_LOW*0.1, SOC_ADC_SAMPLE_FREQ_THRES_LOW, m1l_cnt); + TEST_ASSERT_LESS_THAN_UINT32(5, m1h_cnt); //Actually, it will still encountered 1~2 times because hardware run very quickly + m1l_cnt = 0; + gpio_set_level(TEST_WAVE_OUT_PIN, 1); + } + } + TEST_ESP_OK(adc_continuous_stop(handle)); + TEST_ESP_OK(adc_continuous_monitor_disable(monitor_handle)); + TEST_ESP_OK(adc_del_continuous_monitor(monitor_handle)); + TEST_ESP_OK(adc_continuous_deinit(handle)); +} + +#endif //SOC_ADC_MONITOR_SUPPORTED && CONFIG_SOC_ADC_DMA_SUPPORTED diff --git a/components/esp_adc/test_apps/adc/main/test_app_main.c b/components/esp_adc/test_apps/adc/main/test_app_main.c index e19a4c2c37..2128f75b7b 100644 --- a/components/esp_adc/test_apps/adc/main/test_app_main.c +++ b/components/esp_adc/test_apps/adc/main/test_app_main.c @@ -1,40 +1,37 @@ /* - * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: CC0-1.0 */ #include "unity.h" -#include "unity_test_runner.h" +#include "unity_test_utils.h" #include "esp_heap_caps.h" -#define TEST_MEMORY_LEAK_THRESHOLD (-600) - -static size_t before_free_8bit; -static size_t before_free_32bit; - -static void check_leak(size_t before_free, size_t after_free, const char *type) -{ - ssize_t delta = after_free - before_free; - printf("MALLOC_CAP_%s: Before %u bytes free, After %u bytes free (delta %d)\n", type, before_free, after_free, delta); - TEST_ASSERT_MESSAGE(delta >= TEST_MEMORY_LEAK_THRESHOLD, "memory leak"); -} +#define TEST_MEMORY_LEAK_THRESHOLD (600) void setUp(void) { - before_free_8bit = heap_caps_get_free_size(MALLOC_CAP_8BIT); - before_free_32bit = heap_caps_get_free_size(MALLOC_CAP_32BIT); + unity_utils_record_free_mem(); } void tearDown(void) { - size_t after_free_8bit = heap_caps_get_free_size(MALLOC_CAP_8BIT); - size_t after_free_32bit = heap_caps_get_free_size(MALLOC_CAP_32BIT); - check_leak(before_free_8bit, after_free_8bit, "8BIT"); - check_leak(before_free_32bit, after_free_32bit, "32BIT"); + unity_utils_evaluate_leaks_direct(TEST_MEMORY_LEAK_THRESHOLD); } void app_main(void) { + // _____ _ _ ____ ____ + // |_ _|__ ___| |_ / \ | _ \ / ___| + // | |/ _ \/ __| __| / _ \ | | | | | + // | | __/\__ \ |_ / ___ \| |_| | |___ + // |_|\___||___/\__| /_/ \_\____/ \____| + + printf(" _____ _ _ ____ ____ \n"); + printf("|_ _|__ ___| |_ / \\ | _ \\ / ___|\n"); + printf(" | |/ _ \\/ __| __| / _ \\ | | | | | \n"); + printf(" | | __/\\__ \\ |_ / ___ \\| |_| | |___ \n"); + printf(" |_|\\___||___/\\__| /_/ \\_\\____/ \\____|\n"); unity_run_menu(); } diff --git a/components/hal/esp32c2/include/hal/adc_ll.h b/components/hal/esp32c2/include/hal/adc_ll.h index a880e49980..f35e391dc4 100644 --- a/components/hal/esp32c2/include/hal/adc_ll.h +++ b/components/hal/esp32c2/include/hal/adc_ll.h @@ -247,42 +247,6 @@ static inline void adc_ll_digi_filter_enable(adc_digi_iir_filter_t idx, adc_unit //nothing to do to enable, after adc_ll_digi_filter_set_factor, it's enabled. } -/** - * Set monitor mode of adc digital controller. - * - * @note If the channel info is not supported, the monitor function will not be enabled. - * @param adc_n ADC unit. - * @param is_larger true: If ADC_OUT > threshold, Generates monitor interrupt. - * false: If ADC_OUT < threshold, Generates monitor interrupt. - */ -static inline void adc_ll_digi_monitor_set_mode(adc_digi_monitor_idx_t idx, adc_digi_monitor_t *cfg) -{ - if (idx == ADC_DIGI_MONITOR_IDX0) { - APB_SARADC.saradc_thres0_ctrl.saradc_thres0_channel = (cfg->adc_unit << 3) | (cfg->channel & 0x7); - APB_SARADC.saradc_thres0_ctrl.saradc_thres0_high = cfg->h_threshold; - APB_SARADC.saradc_thres0_ctrl.saradc_thres0_low = cfg->l_threshold; - } else { // ADC_DIGI_MONITOR_IDX1 - APB_SARADC.saradc_thres1_ctrl.saradc_thres1_channel = (cfg->adc_unit << 3) | (cfg->channel & 0x7); - APB_SARADC.saradc_thres1_ctrl.saradc_thres1_high = cfg->h_threshold; - APB_SARADC.saradc_thres1_ctrl.saradc_thres1_low = cfg->l_threshold; - } -} - -/** - * Enable/disable monitor of adc digital controller. - * - * @note If the channel info is not supported, the monitor function will not be enabled. - * @param adc_n ADC unit. - */ -static inline void adc_ll_digi_monitor_disable(adc_digi_monitor_idx_t idx) -{ - if (idx == ADC_DIGI_MONITOR_IDX0) { - APB_SARADC.saradc_thres0_ctrl.saradc_thres0_channel = 0xF; - } else { // ADC_DIGI_MONITOR_IDX1 - APB_SARADC.saradc_thres1_ctrl.saradc_thres1_channel = 0xF; - } -} - /** * Reset adc digital controller. */ diff --git a/components/hal/esp32c3/include/hal/adc_ll.h b/components/hal/esp32c3/include/hal/adc_ll.h index 4428d19c4b..438a3acec4 100644 --- a/components/hal/esp32c3/include/hal/adc_ll.h +++ b/components/hal/esp32c3/include/hal/adc_ll.h @@ -29,10 +29,13 @@ extern "C" { #define ADC_LL_EVENT_ADC1_ONESHOT_DONE BIT(31) #define ADC_LL_EVENT_ADC2_ONESHOT_DONE BIT(30) -#define ADC_LL_EVENT_THRES0_HIGH BIT(29) -#define ADC_LL_EVENT_THRES1_HIGH BIT(28) -#define ADC_LL_EVENT_THRES0_LOW BIT(27) -#define ADC_LL_EVENT_THRES1_LOW BIT(26) + +#define ADC_LL_THRES_ALL_INTR_ST_M (APB_SARADC_THRES0_HIGH_INT_ST_M | \ + APB_SARADC_THRES1_HIGH_INT_ST_M | \ + APB_SARADC_THRES0_LOW_INT_ST_M | \ + APB_SARADC_THRES1_LOW_INT_ST_M) +#define ADC_LL_GET_HIGH_THRES_MASK(monitor_id) ((monitor_id == 0) ? APB_SARADC_THRES0_HIGH_INT_ST_M : APB_SARADC_THRES1_HIGH_INT_ST_M) +#define ADC_LL_GET_LOW_THRES_MASK(monitor_id) ((monitor_id == 0) ? APB_SARADC_THRES0_LOW_INT_ST_M : APB_SARADC_THRES1_LOW_INT_ST_M) /*--------------------------------------------------------------- Oneshot @@ -391,41 +394,87 @@ static inline void adc_ll_digi_filter_enable(adc_digi_iir_filter_t idx, adc_unit } /** - * Set monitor mode of adc digital controller. + * Set monitor threshold of adc digital controller on specific channel. * - * @note If the channel info is not supported, the monitor function will not be enabled. - * @param adc_n ADC unit. - * @param is_larger true: If ADC_OUT > threshold, Generates monitor interrupt. - * false: If ADC_OUT < threshold, Generates monitor interrupt. + * @param monitor_id ADC digi monitor unit index. + * @param adc_n Which adc unit the channel belong to. + * @param channel Which channel of adc want to be monitored. + * @param h_thresh High threshold of this monitor. + * @param l_thresh Low threshold of this monitor. */ -static inline void adc_ll_digi_monitor_set_mode(adc_digi_monitor_idx_t idx, adc_digi_monitor_t *cfg) +static inline void adc_ll_digi_monitor_set_thres(adc_monitor_id_t monitor_id, adc_unit_t adc_n, uint8_t channel, int32_t h_thresh, int32_t l_thresh) { - if (idx == ADC_DIGI_MONITOR_IDX0) { - APB_SARADC.thres0_ctrl.thres0_channel = (cfg->adc_unit << 3) | (cfg->channel & 0x7); - APB_SARADC.thres0_ctrl.thres0_high = cfg->h_threshold; - APB_SARADC.thres0_ctrl.thres0_low = cfg->l_threshold; - } else { // ADC_DIGI_MONITOR_IDX1 - APB_SARADC.thres1_ctrl.thres1_channel = (cfg->adc_unit << 3) | (cfg->channel & 0x7); - APB_SARADC.thres1_ctrl.thres1_high = cfg->h_threshold; - APB_SARADC.thres1_ctrl.thres1_low = cfg->l_threshold; + if (monitor_id == ADC_MONITOR_0) { + APB_SARADC.thres0_ctrl.thres0_channel = (adc_n << 3) | (channel & 0x7); + APB_SARADC.thres0_ctrl.thres0_high = h_thresh; + APB_SARADC.thres0_ctrl.thres0_low = l_thresh; + } else { // ADC_MONITOR_1 + APB_SARADC.thres1_ctrl.thres1_channel = (adc_n << 3) | (channel & 0x7); + APB_SARADC.thres1_ctrl.thres1_high = h_thresh; + APB_SARADC.thres1_ctrl.thres1_low = l_thresh; } } /** - * Enable/disable monitor of adc digital controller. + * Start/Stop monitor of adc digital controller. * - * @note If the channel info is not supported, the monitor function will not be enabled. - * @param adc_n ADC unit. + * @param monitor_id ADC digi monitor unit index. + * @param start 1 for start, 0 for stop */ -static inline void adc_ll_digi_monitor_disable(adc_digi_monitor_idx_t idx) +static inline void adc_ll_digi_monitor_user_start(adc_monitor_id_t monitor_id, bool start) { - if (idx == ADC_DIGI_MONITOR_IDX0) { - APB_SARADC.thres0_ctrl.thres0_channel = 0xF; - } else { // ADC_DIGI_MONITOR_IDX1 - APB_SARADC.thres1_ctrl.thres1_channel = 0xF; + if (monitor_id == ADC_MONITOR_0) { + APB_SARADC.thres_ctrl.thres0_en = start; + } else { + APB_SARADC.thres_ctrl.thres1_en = start; } } +/** + * Enable/disable a intr of adc digital monitor. + * + * @param monitor_id ADC digi monitor unit index. + * @param mode monit mode to enable/disable intr. + * @param enable enable or disable. + */ +static inline void adc_ll_digi_monitor_enable_intr(adc_monitor_id_t monitor_id, adc_monitor_mode_t mode, bool enable) +{ + if (monitor_id == ADC_MONITOR_0) { + if (mode == ADC_MONITOR_MODE_HIGH) { + APB_SARADC.int_ena.thres0_high = enable; + } else { + APB_SARADC.int_ena.thres0_low = enable; + } + } + if (monitor_id == ADC_MONITOR_1) { + if (mode == ADC_MONITOR_MODE_HIGH) { + APB_SARADC.int_ena.thres1_high = enable; + } else { + APB_SARADC.int_ena.thres1_low = enable; + } + } +} + +/** + * Get the address of digi monitor intr statue register. + * + * @return address of register. + */ +__attribute__((always_inline)) +static inline volatile const void *adc_ll_digi_monitor_get_intr_status_addr(void) +{ + return &APB_SARADC.int_st.val; +} + +/** + * Clear intr raw for adc digi monitors. + */ +__attribute__((always_inline)) +static inline void adc_ll_digi_monitor_clear_intr(void) +{ + APB_SARADC.int_clr.val |= ADC_LL_THRES_ALL_INTR_ST_M; +} + /** * Set DMA eof num of adc digital controller. * If the number of measurements reaches `dma_eof_num`, then `dma_in_suc_eof` signal is generated. diff --git a/components/hal/esp32c6/include/hal/adc_ll.h b/components/hal/esp32c6/include/hal/adc_ll.h index 6f9a675540..e0478f8f87 100644 --- a/components/hal/esp32c6/include/hal/adc_ll.h +++ b/components/hal/esp32c6/include/hal/adc_ll.h @@ -30,10 +30,13 @@ extern "C" { #define ADC_LL_EVENT_ADC1_ONESHOT_DONE BIT(31) #define ADC_LL_EVENT_ADC2_ONESHOT_DONE BIT(30) -#define ADC_LL_EVENT_THRES0_HIGH BIT(29) -#define ADC_LL_EVENT_THRES1_HIGH BIT(28) -#define ADC_LL_EVENT_THRES0_LOW BIT(27) -#define ADC_LL_EVENT_THRES1_LOW BIT(26) + +#define ADC_LL_THRES_ALL_INTR_ST_M (APB_SARADC_APB_SARADC_THRES0_HIGH_INT_ST_M | \ + APB_SARADC_APB_SARADC_THRES1_HIGH_INT_ST_M | \ + APB_SARADC_APB_SARADC_THRES0_LOW_INT_ST_M | \ + APB_SARADC_APB_SARADC_THRES1_LOW_INT_ST_M) +#define ADC_LL_GET_HIGH_THRES_MASK(monitor_id) ((monitor_id == 0) ? APB_SARADC_APB_SARADC_THRES0_HIGH_INT_ST_M : APB_SARADC_APB_SARADC_THRES1_HIGH_INT_ST_M) +#define ADC_LL_GET_LOW_THRES_MASK(monitor_id) ((monitor_id == 0) ? APB_SARADC_APB_SARADC_THRES0_LOW_INT_ST_M : APB_SARADC_APB_SARADC_THRES1_LOW_INT_ST_M) /*--------------------------------------------------------------- Oneshot @@ -50,7 +53,6 @@ extern "C" { #define ADC_LL_FSM_STANDBY_WAIT_DEFAULT (100) #define ADC_LL_SAMPLE_CYCLE_DEFAULT (2) #define ADC_LL_DIGI_SAR_CLK_DIV_DEFAULT (1) - #define ADC_LL_CLKM_DIV_NUM_DEFAULT 15 #define ADC_LL_CLKM_DIV_B_DEFAULT 1 #define ADC_LL_CLKM_DIV_A_DEFAULT 0 @@ -407,41 +409,87 @@ static inline void adc_ll_digi_filter_enable(adc_digi_iir_filter_t idx, adc_unit } /** - * Set monitor mode of adc digital controller. + * Set monitor threshold of adc digital controller on specific channel. * - * @note If the channel info is not supported, the monitor function will not be enabled. - * @param adc_n ADC unit. - * @param is_larger true: If ADC_OUT > threshold, Generates monitor interrupt. - * false: If ADC_OUT < threshold, Generates monitor interrupt. + * @param monitor_id ADC digi monitor unit index. + * @param adc_n Which adc unit the channel belong to. + * @param channel Which channel of adc want to be monitored. + * @param h_thresh High threshold of this monitor. + * @param l_thresh Low threshold of this monitor. */ -static inline void adc_ll_digi_monitor_set_mode(adc_digi_monitor_idx_t idx, adc_digi_monitor_t *cfg) +static inline void adc_ll_digi_monitor_set_thres(adc_monitor_id_t monitor_id, adc_unit_t adc_n, uint8_t channel, int32_t h_thresh, int32_t l_thresh) { - if (idx == ADC_DIGI_MONITOR_IDX0) { - APB_SARADC.saradc_thres0_ctrl.saradc_apb_saradc_thres0_channel = (cfg->adc_unit << 3) | (cfg->channel & 0x7); - APB_SARADC.saradc_thres0_ctrl.saradc_apb_saradc_thres0_high = cfg->h_threshold; - APB_SARADC.saradc_thres0_ctrl.saradc_apb_saradc_thres0_low = cfg->l_threshold; - } else { // ADC_DIGI_MONITOR_IDX1 - APB_SARADC.saradc_thres1_ctrl.saradc_apb_saradc_thres1_channel = (cfg->adc_unit << 3) | (cfg->channel & 0x7); - APB_SARADC.saradc_thres1_ctrl.saradc_apb_saradc_thres1_low = cfg->h_threshold; - APB_SARADC.saradc_thres1_ctrl.saradc_apb_saradc_thres1_low = cfg->l_threshold; + if (monitor_id == ADC_MONITOR_0) { + APB_SARADC.saradc_thres0_ctrl.saradc_apb_saradc_thres0_channel = (adc_n << 3) | (channel & 0x7); + APB_SARADC.saradc_thres0_ctrl.saradc_apb_saradc_thres0_high = h_thresh; + APB_SARADC.saradc_thres0_ctrl.saradc_apb_saradc_thres0_low = l_thresh; + } else { // ADC_MONITOR_1 + APB_SARADC.saradc_thres1_ctrl.saradc_apb_saradc_thres1_channel = (adc_n << 3) | (channel & 0x7); + APB_SARADC.saradc_thres1_ctrl.saradc_apb_saradc_thres1_high = h_thresh; + APB_SARADC.saradc_thres1_ctrl.saradc_apb_saradc_thres1_low = l_thresh; } } /** - * Enable/disable monitor of adc digital controller. + * Start/Stop monitor of adc digital controller. * - * @note If the channel info is not supported, the monitor function will not be enabled. - * @param adc_n ADC unit. + * @param monitor_id ADC digi monitor unit index. + * @param start 1 for start, 0 for stop */ -static inline void adc_ll_digi_monitor_disable(adc_digi_monitor_idx_t idx) +static inline void adc_ll_digi_monitor_user_start(adc_monitor_id_t monitor_id, bool start) { - if (idx == ADC_DIGI_MONITOR_IDX0) { - APB_SARADC.saradc_thres0_ctrl.saradc_apb_saradc_thres0_channel = 0xF; - } else { // ADC_DIGI_MONITOR_IDX1 - APB_SARADC.saradc_thres1_ctrl.saradc_apb_saradc_thres1_channel = 0xF; + if (monitor_id == ADC_MONITOR_0) { + APB_SARADC.saradc_thres_ctrl.saradc_apb_saradc_thres0_en = start; + } else { + APB_SARADC.saradc_thres_ctrl.saradc_apb_saradc_thres1_en = start; } } +/** + * Enable/disable a intr of adc digital monitor. + * + * @param monitor_id ADC digi monitor unit index. + * @param mode monit mode to enable/disable intr. + * @param enable enable or disable. + */ +static inline void adc_ll_digi_monitor_enable_intr(adc_monitor_id_t monitor_id, adc_monitor_mode_t mode, bool enable) +{ + if (monitor_id == ADC_MONITOR_0) { + if (mode == ADC_MONITOR_MODE_HIGH) { + APB_SARADC.saradc_int_ena.saradc_apb_saradc_thres0_high_int_ena = enable; + } else { + APB_SARADC.saradc_int_ena.saradc_apb_saradc_thres0_low_int_ena = enable; + } + } + if (monitor_id == ADC_MONITOR_1) { + if (mode == ADC_MONITOR_MODE_HIGH) { + APB_SARADC.saradc_int_ena.saradc_apb_saradc_thres1_high_int_ena = enable; + } else { + APB_SARADC.saradc_int_ena.saradc_apb_saradc_thres1_low_int_ena = enable; + } + } +} + +/** + * Clear intr raw for adc digi monitors. + */ +__attribute__((always_inline)) +static inline void adc_ll_digi_monitor_clear_intr(void) +{ + APB_SARADC.saradc_int_clr.val |= ADC_LL_THRES_ALL_INTR_ST_M; +} + +/** + * Get the address of digi monitor intr statue register. + * + * @return address of register. + */ +__attribute__((always_inline)) +static inline volatile const void *adc_ll_digi_monitor_get_intr_status_addr(void) +{ + return &APB_SARADC.saradc_int_st.val; +} + /** * Set DMA eof num of adc digital controller. * If the number of measurements reaches `dma_eof_num`, then `dma_in_suc_eof` signal is generated. diff --git a/components/hal/esp32h2/include/hal/adc_ll.h b/components/hal/esp32h2/include/hal/adc_ll.h index 4e67ad81de..8a2dc2e798 100644 --- a/components/hal/esp32h2/include/hal/adc_ll.h +++ b/components/hal/esp32h2/include/hal/adc_ll.h @@ -30,10 +30,13 @@ extern "C" { #define ADC_LL_EVENT_ADC1_ONESHOT_DONE BIT(31) #define ADC_LL_EVENT_ADC2_ONESHOT_DONE BIT(30) -#define ADC_LL_EVENT_THRES0_HIGH BIT(29) -#define ADC_LL_EVENT_THRES1_HIGH BIT(28) -#define ADC_LL_EVENT_THRES0_LOW BIT(27) -#define ADC_LL_EVENT_THRES1_LOW BIT(26) + +#define ADC_LL_THRES_ALL_INTR_ST_M (APB_SARADC_APB_SARADC_THRES0_HIGH_INT_ST_M | \ + APB_SARADC_APB_SARADC_THRES1_HIGH_INT_ST_M | \ + APB_SARADC_APB_SARADC_THRES0_LOW_INT_ST_M | \ + APB_SARADC_APB_SARADC_THRES1_LOW_INT_ST_M) +#define ADC_LL_GET_HIGH_THRES_MASK(monitor_id) ((monitor_id == 0) ? APB_SARADC_APB_SARADC_THRES0_HIGH_INT_ST_M : APB_SARADC_APB_SARADC_THRES1_HIGH_INT_ST_M) +#define ADC_LL_GET_LOW_THRES_MASK(monitor_id) ((monitor_id == 0) ? APB_SARADC_APB_SARADC_THRES0_LOW_INT_ST_M : APB_SARADC_APB_SARADC_THRES1_LOW_INT_ST_M) /*--------------------------------------------------------------- Oneshot @@ -407,41 +410,87 @@ static inline void adc_ll_digi_filter_enable(adc_digi_iir_filter_t idx, adc_unit } /** - * Set monitor mode of adc digital controller. + * Set monitor threshold of adc digital controller on specific channel. * - * @note If the channel info is not supported, the monitor function will not be enabled. - * @param adc_n ADC unit. - * @param is_larger true: If ADC_OUT > threshold, Generates monitor interrupt. - * false: If ADC_OUT < threshold, Generates monitor interrupt. + * @param monitor_id ADC digi monitor unit index. + * @param adc_n Which adc unit the channel belong to. + * @param channel Which channel of adc want to be monitored. + * @param h_thresh High threshold of this monitor. + * @param l_thresh Low threshold of this monitor. */ -static inline void adc_ll_digi_monitor_set_mode(adc_digi_monitor_idx_t idx, adc_digi_monitor_t *cfg) +static inline void adc_ll_digi_monitor_set_thres(adc_monitor_id_t monitor_id, adc_unit_t adc_n, uint8_t channel, int32_t h_thresh, int32_t l_thresh) { - if (idx == ADC_DIGI_MONITOR_IDX0) { - APB_SARADC.saradc_thres0_ctrl.saradc_apb_saradc_thres0_channel = (cfg->adc_unit << 3) | (cfg->channel & 0x7); - APB_SARADC.saradc_thres0_ctrl.saradc_apb_saradc_thres0_high = cfg->h_threshold; - APB_SARADC.saradc_thres0_ctrl.saradc_apb_saradc_thres0_low = cfg->l_threshold; - } else { // ADC_DIGI_MONITOR_IDX1 - APB_SARADC.saradc_thres1_ctrl.saradc_apb_saradc_thres1_channel = (cfg->adc_unit << 3) | (cfg->channel & 0x7); - APB_SARADC.saradc_thres1_ctrl.saradc_apb_saradc_thres1_low = cfg->h_threshold; - APB_SARADC.saradc_thres1_ctrl.saradc_apb_saradc_thres1_low = cfg->l_threshold; + if (monitor_id == ADC_MONITOR_0) { + APB_SARADC.saradc_thres0_ctrl.saradc_apb_saradc_thres0_channel = (adc_n << 3) | (channel & 0x7); + APB_SARADC.saradc_thres0_ctrl.saradc_apb_saradc_thres0_high = h_thresh; + APB_SARADC.saradc_thres0_ctrl.saradc_apb_saradc_thres0_low = l_thresh; + } else { // ADC_MONITOR_1 + APB_SARADC.saradc_thres1_ctrl.saradc_apb_saradc_thres1_channel = (adc_n << 3) | (channel & 0x7); + APB_SARADC.saradc_thres1_ctrl.saradc_apb_saradc_thres1_high = h_thresh; + APB_SARADC.saradc_thres1_ctrl.saradc_apb_saradc_thres1_low = l_thresh; } } /** - * Enable/disable monitor of adc digital controller. + * Start/Stop monitor of adc digital controller. * - * @note If the channel info is not supported, the monitor function will not be enabled. - * @param adc_n ADC unit. + * @param monitor_id ADC digi monitor unit index. + * @param start 1 for start, 0 for stop */ -static inline void adc_ll_digi_monitor_disable(adc_digi_monitor_idx_t idx) +static inline void adc_ll_digi_monitor_user_start(adc_monitor_id_t monitor_id, bool start) { - if (idx == ADC_DIGI_MONITOR_IDX0) { - APB_SARADC.saradc_thres0_ctrl.saradc_apb_saradc_thres0_channel = 0xF; - } else { // ADC_DIGI_MONITOR_IDX1 - APB_SARADC.saradc_thres1_ctrl.saradc_apb_saradc_thres1_channel = 0xF; + if (monitor_id == ADC_MONITOR_0) { + APB_SARADC.saradc_thres_ctrl.saradc_apb_saradc_thres0_en = start; + } else { + APB_SARADC.saradc_thres_ctrl.saradc_apb_saradc_thres1_en = start; } } +/** + * Enable/disable a intr of adc digital monitor. + * + * @param monitor_id ADC digi monitor unit index. + * @param mode monit mode to enable/disable intr. + * @param enable enable or disable. + */ +static inline void adc_ll_digi_monitor_enable_intr(adc_monitor_id_t monitor_id, adc_monitor_mode_t mode, bool enable) +{ + if (monitor_id == ADC_MONITOR_0) { + if (mode == ADC_MONITOR_MODE_HIGH) { + APB_SARADC.saradc_int_ena.saradc_apb_saradc_thres0_high_int_ena = enable; + } else { + APB_SARADC.saradc_int_ena.saradc_apb_saradc_thres0_low_int_ena = enable; + } + } + if (monitor_id == ADC_MONITOR_1) { + if (mode == ADC_MONITOR_MODE_HIGH) { + APB_SARADC.saradc_int_ena.saradc_apb_saradc_thres1_high_int_ena = enable; + } else { + APB_SARADC.saradc_int_ena.saradc_apb_saradc_thres1_low_int_ena = enable; + } + } +} + +/** + * Clear intr raw for adc digi monitors. + */ +__attribute__((always_inline)) +static inline void adc_ll_digi_monitor_clear_intr(void) +{ + APB_SARADC.saradc_int_clr.val |= ADC_LL_THRES_ALL_INTR_ST_M; +} + +/** + * Get the address of digi monitor intr statue register. + * + * @return address of register. + */ +__attribute__((always_inline)) +static inline volatile const void *adc_ll_digi_monitor_get_intr_status_addr(void) +{ + return &APB_SARADC.saradc_int_st.val; +} + /** * Set DMA eof num of adc digital controller. * If the number of measurements reaches `dma_eof_num`, then `dma_in_suc_eof` signal is generated. diff --git a/components/hal/esp32s2/include/hal/adc_ll.h b/components/hal/esp32s2/include/hal/adc_ll.h index 8eb918030c..044271ae49 100644 --- a/components/hal/esp32s2/include/hal/adc_ll.h +++ b/components/hal/esp32s2/include/hal/adc_ll.h @@ -30,6 +30,10 @@ extern "C" { #define ADC_LL_EVENT_ADC1_ONESHOT_DONE (1 << 0) #define ADC_LL_EVENT_ADC2_ONESHOT_DONE (1 << 1) +#define ADC_LL_THRES_ALL_INTR_ST_M (APB_SARADC_ADC1_THRES_INT_ST_M | APB_SARADC_ADC2_THRES_INT_ST_M) +#define ADC_LL_GET_HIGH_THRES_MASK(monitor_id) ((monitor_id == 0) ? APB_SARADC_ADC1_THRES_INT_ST_M : APB_SARADC_ADC2_THRES_INT_ST_M) +#define ADC_LL_GET_LOW_THRES_MASK(monitor_id) ((monitor_id == 0) ? APB_SARADC_ADC1_THRES_INT_ST_M : APB_SARADC_ADC2_THRES_INT_ST_M) + /*--------------------------------------------------------------- Oneshot ---------------------------------------------------------------*/ @@ -460,48 +464,88 @@ static inline uint32_t adc_ll_digi_filter_read_data(adc_unit_t adc_n) * Set monitor mode of adc digital controller. * * @note The monitor will monitor all the enabled channel data of the each ADC unit at the same time. - * @param adc_n ADC unit. + * @param monitor_id ADC digi monitor unit index. * @param is_larger true: If ADC_OUT > threshold, Generates monitor interrupt. * false: If ADC_OUT < threshold, Generates monitor interrupt. */ -static inline void adc_ll_digi_monitor_set_mode(adc_unit_t adc_n, bool is_larger) +static inline void adc_ll_digi_monitor_set_mode(adc_monitor_id_t monitor_id, bool is_larger) { - if (adc_n == ADC_UNIT_1) { + if (monitor_id == ADC_MONITOR_0) { APB_SARADC.thres_ctrl.adc1_thres_mode = is_larger; - } else { // adc_n == ADC_UNIT_2 + } else { // monitor_id == ADC_MONITOR_1 APB_SARADC.thres_ctrl.adc2_thres_mode = is_larger; } } /** - * Set monitor threshold of adc digital controller. + * Set monitor threshold of adc digital controller on specific channel. * - * @note The monitor will monitor all the enabled channel data of the each ADC unit at the same time. - * @param adc_n ADC unit. - * @param threshold Monitor threshold. + * @param monitor_id ADC digi monitor unit index. + * @param adc_n Which adc unit the channel belong to. + * @param channel Which channel of adc want to be monitored. + * @param h_thresh High threshold of this monitor. + * @param l_thresh Low threshold of this monitor. */ -static inline void adc_ll_digi_monitor_set_thres(adc_unit_t adc_n, uint32_t threshold) +static inline void adc_ll_digi_monitor_set_thres(adc_monitor_id_t monitor_id, adc_unit_t adc_n, uint8_t channel, int32_t h_thresh, int32_t l_thresh) { - if (adc_n == ADC_UNIT_1) { - APB_SARADC.thres_ctrl.adc1_thres = threshold; - } else { // adc_n == ADC_UNIT_2 - APB_SARADC.thres_ctrl.adc2_thres = threshold; + if (monitor_id == ADC_MONITOR_0) { + APB_SARADC.thres_ctrl.adc1_thres = (h_thresh == -1)? l_thresh : h_thresh; + } else { // monitor_id == ADC_MONITOR_1 + APB_SARADC.thres_ctrl.adc2_thres = (h_thresh == -1)? l_thresh : h_thresh; + } + adc_ll_digi_monitor_set_mode(monitor_id, l_thresh == -1); +} + +/** + * Start/Stop monitor of adc digital controller. + * + * @param monitor_id ADC digi monitor unit index. + * @param start 1 for start, 0 for stop + */ +static inline void adc_ll_digi_monitor_user_start(adc_monitor_id_t monitor_id, bool start) +{ + if (monitor_id == ADC_MONITOR_0) { + APB_SARADC.thres_ctrl.adc1_thres_en = start; + } else { + APB_SARADC.thres_ctrl.adc2_thres_en = start; } } /** - * Enable/disable monitor of adc digital controller. + * Enable/disable a intr of adc digital monitor. * - * @note The monitor will monitor all the enabled channel data of the each ADC unit at the same time. - * @param adc_n ADC unit. + * @param monitor_id ADC digi monitor unit index. + * @param mode monit mode to enable/disable intr. + * @param enable enable or disable. */ -static inline void adc_ll_digi_monitor_enable(adc_unit_t adc_n, bool enable) +static inline void adc_ll_digi_monitor_enable_intr(adc_monitor_id_t monitor_id, adc_monitor_mode_t mode, bool enable) { - if (adc_n == ADC_UNIT_1) { - APB_SARADC.thres_ctrl.adc1_thres_en = enable; - } else { // adc_n == ADC_UNIT_2 - APB_SARADC.thres_ctrl.adc2_thres_en = enable; + if (monitor_id == ADC_MONITOR_0) { + APB_SARADC.int_ena.adc1_thres = enable; } + if (monitor_id == ADC_MONITOR_1) { + APB_SARADC.int_ena.adc2_thres = enable; + } +} + +/** + * Clear intr raw for adc digi monitors. + */ +__attribute__((always_inline)) +static inline void adc_ll_digi_monitor_clear_intr(void) +{ + APB_SARADC.int_clr.val |= ADC_LL_THRES_ALL_INTR_ST_M; +} + +/** + * Get the address of digi monitor intr statue register. + * + * @return address of register. + */ +__attribute__((always_inline)) +static inline volatile const void *adc_ll_digi_monitor_get_intr_status_addr(void) +{ + return &APB_SARADC.int_st.val; } /** diff --git a/components/hal/esp32s3/include/hal/adc_ll.h b/components/hal/esp32s3/include/hal/adc_ll.h index b138b723ca..8a756e69d3 100644 --- a/components/hal/esp32s3/include/hal/adc_ll.h +++ b/components/hal/esp32s3/include/hal/adc_ll.h @@ -30,6 +30,13 @@ extern "C" { #define ADC_LL_EVENT_ADC1_ONESHOT_DONE (1 << 0) #define ADC_LL_EVENT_ADC2_ONESHOT_DONE (1 << 1) +#define ADC_LL_THRES_ALL_INTR_ST_M (APB_SARADC_THRES0_HIGH_INT_ST_M | \ + APB_SARADC_THRES1_HIGH_INT_ST_M | \ + APB_SARADC_THRES0_LOW_INT_ST_M | \ + APB_SARADC_THRES1_LOW_INT_ST_M) +#define ADC_LL_GET_HIGH_THRES_MASK(monitor_id) ((monitor_id == 0) ? APB_SARADC_THRES0_HIGH_INT_ST_M : APB_SARADC_THRES1_HIGH_INT_ST_M) +#define ADC_LL_GET_LOW_THRES_MASK(monitor_id) ((monitor_id == 0) ? APB_SARADC_THRES0_LOW_INT_ST_M : APB_SARADC_THRES1_LOW_INT_ST_M) + /*--------------------------------------------------------------- Oneshot ---------------------------------------------------------------*/ @@ -456,39 +463,85 @@ static inline void adc_ll_digi_filter_enable(adc_digi_iir_filter_t idx, adc_unit } /** - * Set monitor mode of adc digital controller. + * Set monitor threshold of adc digital controller on specific channel. * - * @note The monitor will monitor all the enabled channel data of the each ADC unit at the same time. - * @param adc_n ADC unit. - * @param is_larger true: If ADC_OUT > threshold, Generates monitor interrupt. - * false: If ADC_OUT < threshold, Generates monitor interrupt. + * @param monitor_id ADC digi monitor unit index. + * @param adc_n Which adc unit the channel belong to. + * @param channel Which channel of adc want to be monitored. + * @param h_thresh High threshold of this monitor. + * @param l_thresh Low threshold of this monitor. */ -static inline void adc_ll_digi_monitor_set_mode(adc_unit_t adc_n, bool is_larger) +static inline void adc_ll_digi_monitor_set_thres(adc_monitor_id_t monitor_id, adc_unit_t adc_n, uint8_t channel, int32_t h_thresh, int32_t l_thresh) { - abort(); + if (monitor_id == ADC_MONITOR_0) { + APB_SARADC.thres0_ctrl.thres0_channel = (adc_n << 3) | (channel & 0x7); + APB_SARADC.thres0_ctrl.thres0_high = h_thresh; + APB_SARADC.thres0_ctrl.thres0_low = l_thresh; + } else { // ADC_MONITOR_1 + APB_SARADC.thres1_ctrl.thres1_channel = (adc_n << 3) | (channel & 0x7); + APB_SARADC.thres1_ctrl.thres1_high = h_thresh; + APB_SARADC.thres1_ctrl.thres1_low = l_thresh; + } } /** - * Set monitor threshold of adc digital controller. + * Start/Stop monitor of adc digital controller. * - * @note The monitor will monitor all the enabled channel data of the each ADC unit at the same time. - * @param adc_n ADC unit. - * @param threshold Monitor threshold. + * @param monitor_id ADC digi monitor unit index. + * @param start 1 for start, 0 for stop */ -static inline void adc_ll_digi_monitor_set_thres(adc_unit_t adc_n, uint32_t threshold) +static inline void adc_ll_digi_monitor_user_start(adc_monitor_id_t monitor_id, bool start) { - abort(); + if (monitor_id == ADC_MONITOR_0) { + APB_SARADC.thres_ctrl.thres0_en = start; + } else { + APB_SARADC.thres_ctrl.thres1_en = start; + } } /** - * Enable/disable monitor of adc digital controller. + * Enable/disable a intr of adc digital monitor. * - * @note The monitor will monitor all the enabled channel data of the each ADC unit at the same time. - * @param adc_n ADC unit. + * @param monitor_id ADC digi monitor unit index. + * @param mode monit mode to enable/disable intr. + * @param enable enable or disable. */ -static inline void adc_ll_digi_monitor_enable(adc_unit_t adc_n, bool enable) +static inline void adc_ll_digi_monitor_enable_intr(adc_monitor_id_t monitor_id, adc_monitor_mode_t mode, bool enable) { - abort(); + if (monitor_id == ADC_MONITOR_0) { + if (mode == ADC_MONITOR_MODE_HIGH) { + APB_SARADC.int_ena.thres0_high = enable; + } else { + APB_SARADC.int_ena.thres0_low = enable; + } + } + if (monitor_id == ADC_MONITOR_1) { + if (mode == ADC_MONITOR_MODE_HIGH) { + APB_SARADC.int_ena.thres1_high = enable; + } else { + APB_SARADC.int_ena.thres1_low = enable; + } + } +} + +/** + * Clear intr raw for adc digi monitors. + */ +__attribute__((always_inline)) +static inline void adc_ll_digi_monitor_clear_intr(void) +{ + APB_SARADC.int_clr.val |= ADC_LL_THRES_ALL_INTR_ST_M; +} + +/** + * Get the address of digi monitor intr statue register. + * + * @return address of register. + */ +__attribute__((always_inline)) +static inline volatile const void *adc_ll_digi_monitor_get_intr_status_addr(void) +{ + return &APB_SARADC.int_st.val; } /** diff --git a/components/hal/include/hal/adc_types.h b/components/hal/include/hal/adc_types.h index 16dfa20d19..51f64927e0 100644 --- a/components/hal/include/hal/adc_types.h +++ b/components/hal/include/hal/adc_types.h @@ -116,6 +116,25 @@ typedef enum { ADC_DIGI_IIR_FILTER_COEFF_64, ///< The filter coefficient is 64 } adc_digi_iir_filter_coeff_t; +/*--------------------------------------------------------------- + ADC Monitor +---------------------------------------------------------------*/ +/** + * @brief ADC monitor (continuous mode) ID + */ +typedef enum { + ADC_MONITOR_0, ///< The monitor index 0. + ADC_MONITOR_1, ///< The monitor index 1. +} adc_monitor_id_t; + +/** + * @brief Monitor config/event mode type + */ +typedef enum { + ADC_MONITOR_MODE_HIGH = 0, ///< ADC raw_result > threshold value, monitor interrupt will be generated. + ADC_MONITOR_MODE_LOW, ///< ADC raw_result < threshold value, monitor interrupt will be generated. +} adc_monitor_mode_t; + /*--------------------------------------------------------------- Output Format ---------------------------------------------------------------*/ @@ -205,6 +224,7 @@ typedef struct { #endif + #if CONFIG_IDF_TARGET_ESP32S2 /** * @brief ADC digital controller (DMA mode) clock system setting. diff --git a/components/hal/include/hal/adc_types_private.h b/components/hal/include/hal/adc_types_private.h index 9a9b0d6b26..e23c9ce9e9 100644 --- a/components/hal/include/hal/adc_types_private.h +++ b/components/hal/include/hal/adc_types_private.h @@ -47,55 +47,6 @@ typedef struct { } #endif //#if SOC_ADC_ARBITER_SUPPORTED -#if SOC_ADC_MONITOR_SUPPORTED -/*--------------------------------------------------------------- - Monitor ----------------------------------------------------------------*/ -/** - * @brief ADC digital controller (DMA mode) monitor index options. - * - * @note For ESP32-S2, The monitor object of the ADC is fixed. - */ -typedef enum { - ADC_DIGI_MONITOR_IDX0 = 0, /*! threshold, Generates monitor interrupt. - * MONITOR_LOW: If ADC_OUT < threshold, Generates monitor interrupt. - */ -typedef enum { -#if CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32C2 - ADC_DIGI_MONITOR_DIS = 0, /*! threshold, Generates monitor interrupt. */ -#else - ADC_DIGI_MONITOR_HIGH = 0, /*! threshold, Generates monitor interrupt. */ - ADC_DIGI_MONITOR_LOW, /*!