diff --git a/components/driver/CMakeLists.txt b/components/driver/CMakeLists.txt index 74ac9e75cf57..0a168abb5ad9 100644 --- a/components/driver/CMakeLists.txt +++ b/components/driver/CMakeLists.txt @@ -47,6 +47,9 @@ endif() # Analog comparator related source files if(CONFIG_SOC_ANA_CMPR_SUPPORTED) list(APPEND srcs "analog_comparator/ana_cmpr.c") + if(CONFIG_SOC_ANA_CMPR_SUPPORT_ETM) + list(APPEND srcs "analog_comparator/ana_cmpr_etm.c") + endif() endif() # DAC related source files diff --git a/components/driver/analog_comparator/ana_cmpr.c b/components/driver/analog_comparator/ana_cmpr.c index 7fbdcf268932..f417c2a02a31 100644 --- a/components/driver/analog_comparator/ana_cmpr.c +++ b/components/driver/analog_comparator/ana_cmpr.c @@ -25,6 +25,7 @@ #include "soc/ana_cmpr_periph.h" #include "hal/ana_cmpr_ll.h" #include "driver/ana_cmpr.h" +#include "driver/gpio.h" #include "esp_private/io_mux.h" #include "esp_private/esp_clk.h" @@ -35,6 +36,8 @@ struct ana_cmpr_t { bool is_enabled; /*!< Whether the Analog comparator unit is enabled */ ana_cmpr_event_callbacks_t cbs; /*!< The callback group that set by user */ intr_handle_t intr_handle; /*!< Interrupt handle */ + uint32_t intr_mask; /*!< Interrupt mask */ + int intr_priority; /*!< Interrupt priority */ void *user_data; /*!< User data that passed to the callbacks */ uint32_t src_clk_freq_hz; /*!< Source clock frequency of the Analog Comparator unit */ esp_pm_lock_handle_t pm_lock; /*!< The Power Management lock that used to avoid unexpected power down of the clock domain */ @@ -43,16 +46,16 @@ struct ana_cmpr_t { /* Helper macros */ #define ANA_CMPR_NULL_POINTER_CHECK(p) ESP_RETURN_ON_FALSE((p), ESP_ERR_INVALID_ARG, TAG, "input parameter '"#p"' is NULL") #define ANA_CMPR_NULL_POINTER_CHECK_ISR(p) ESP_RETURN_ON_FALSE_ISR((p), ESP_ERR_INVALID_ARG, TAG, "input parameter '"#p"' is NULL") -#define ANA_CMPR_UNIT_CHECK(unit) ESP_RETURN_ON_FALSE((unit) >= ANA_CMPR_UNIT_0 && (unit) < SOC_ANA_CMPR_NUM, \ +#define ANA_CMPR_UNIT_CHECK(unit) ESP_RETURN_ON_FALSE((unit) >= 0 && (unit) < SOC_ANA_CMPR_NUM, \ ESP_ERR_INVALID_ARG, TAG, "invalid uint number"); /* Memory allocation caps which decide the section that memory supposed to allocate */ #if CONFIG_ANA_CMPR_ISR_IRAM_SAFE #define ANA_CMPR_MEM_ALLOC_CAPS (MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT) -#define ANA_CMPR_INTR_FLAG (ESP_INTR_FLAG_LOWMED | ESP_INTR_FLAG_SHARED | ESP_INTR_FLAG_IRAM) // Shared with GPIO +#define ANA_CMPR_INTR_FLAG (ESP_INTR_FLAG_IRAM) #else #define ANA_CMPR_MEM_ALLOC_CAPS MALLOC_CAP_DEFAULT -#define ANA_CMPR_INTR_FLAG (ESP_INTR_FLAG_LOWMED | ESP_INTR_FLAG_SHARED) // Shared with GPIO +#define ANA_CMPR_INTR_FLAG (0) #endif /* Driver tag */ @@ -70,13 +73,20 @@ static void IRAM_ATTR s_ana_cmpr_default_intr_handler(void *usr_data) { ana_cmpr_handle_t cmpr_handle = (ana_cmpr_handle_t)usr_data; bool need_yield = false; - ana_cmpr_cross_event_data_t evt_data; + ana_cmpr_cross_event_data_t evt_data = {.cross_type = ANA_CMPR_CROSS_ANY}; /* Get and clear the interrupt status */ uint32_t status = analog_cmpr_ll_get_intr_status(cmpr_handle->dev); analog_cmpr_ll_clear_intr(cmpr_handle->dev, status); /* Call the user callback function if it is specified and the corresponding event triggers*/ - if (cmpr_handle->cbs.on_cross && (status & ANALOG_CMPR_LL_EVENT_CROSS)) { + if (cmpr_handle->cbs.on_cross && (status & cmpr_handle->intr_mask)) { +#if SOC_ANA_CMPR_CAN_DISTINGUISH_EDGE + if (status & ANALOG_CMPR_LL_POS_CROSS_MASK(cmpr_handle->unit)) { + evt_data.cross_type = ANA_CMPR_CROSS_POS; + } else if (status & ANALOG_CMPR_LL_NEG_CROSS_MASK(cmpr_handle->unit)) { + evt_data.cross_type = ANA_CMPR_CROSS_NEG; + } +#endif // SOC_ANA_CMPR_CAN_DISTINGUISH_EDGE need_yield = cmpr_handle->cbs.on_cross(cmpr_handle, &evt_data, cmpr_handle->user_data); } if (need_yield) { @@ -84,6 +94,22 @@ static void IRAM_ATTR s_ana_cmpr_default_intr_handler(void *usr_data) } } +static esp_err_t s_ana_cmpr_init_gpio(ana_cmpr_handle_t cmpr, bool is_external_ref) +{ + uint64_t pin_mask = BIT64(ana_cmpr_periph[cmpr->unit].src_gpio); + if (is_external_ref) { + pin_mask |= BIT64(ana_cmpr_periph[cmpr->unit].ext_ref_gpio); + } + gpio_config_t ana_cmpr_gpio_cfg = { + .pin_bit_mask = pin_mask, + .mode = GPIO_MODE_DISABLE, + .pull_up_en = GPIO_PULLUP_DISABLE, + .pull_down_en = GPIO_PULLDOWN_DISABLE, + .intr_type = GPIO_INTR_DISABLE, + }; + return gpio_config(&ana_cmpr_gpio_cfg); +} + esp_err_t ana_cmpr_new_unit(const ana_cmpr_config_t *config, ana_cmpr_handle_t *ret_cmpr) { #if CONFIG_ANA_CMPR_ENABLE_DEBUG_LOG @@ -93,6 +119,7 @@ esp_err_t ana_cmpr_new_unit(const ana_cmpr_config_t *config, ana_cmpr_handle_t * ANA_CMPR_NULL_POINTER_CHECK(ret_cmpr); ana_cmpr_unit_t unit = config->unit; ANA_CMPR_UNIT_CHECK(unit); + ESP_RETURN_ON_FALSE(config->intr_priority >= 0 && config->intr_priority <= 7, ESP_ERR_INVALID_ARG, TAG, "interrupt priority should be within 0~7"); ESP_RETURN_ON_FALSE(!s_ana_cmpr[unit], ESP_ERR_INVALID_STATE, TAG, "unit has been allocated already"); esp_err_t ret = ESP_OK; @@ -102,8 +129,9 @@ esp_err_t ana_cmpr_new_unit(const ana_cmpr_config_t *config, ana_cmpr_handle_t * ESP_RETURN_ON_FALSE(s_ana_cmpr[unit], ESP_ERR_NO_MEM, TAG, "no memory for analog comparator struct"); /* Assign analog comparator unit */ - s_ana_cmpr[unit]->dev = ANALOG_CMPR_LL_GET_HW(); + s_ana_cmpr[unit]->dev = ANALOG_CMPR_LL_GET_HW(unit); s_ana_cmpr[unit]->ref_src = config->ref_src; + s_ana_cmpr[unit]->intr_priority = config->intr_priority; s_ana_cmpr[unit]->is_enabled = false; s_ana_cmpr[unit]->pm_lock = NULL; @@ -115,6 +143,10 @@ esp_err_t ana_cmpr_new_unit(const ana_cmpr_config_t *config, ana_cmpr_handle_t * ESP_GOTO_ON_ERROR(ret, err, TAG, "create NO_LIGHT_SLEEP, lock failed"); #endif + if (!config->flags.io_loop_back) { + ESP_GOTO_ON_ERROR(s_ana_cmpr_init_gpio(s_ana_cmpr[unit], config->ref_src == ANA_CMPR_REF_SRC_EXTERNAL), err, TAG, "failed to initialize GPIO"); + } + /* Analog clock comes from IO MUX, but IO MUX clock might be shared with other submodules as well */ ESP_GOTO_ON_ERROR(esp_clk_tree_src_get_freq_hz((soc_module_clk_t)config->clk_src, ESP_CLK_TREE_SRC_FREQ_PRECISION_CACHED, @@ -126,20 +158,19 @@ esp_err_t ana_cmpr_new_unit(const ana_cmpr_config_t *config, ana_cmpr_handle_t * /* Configure the register */ portENTER_CRITICAL(&s_spinlock); analog_cmpr_ll_set_ref_source(s_ana_cmpr[unit]->dev, config->ref_src); +#if !SOC_ANA_CMPR_CAN_DISTINGUISH_EDGE analog_cmpr_ll_set_cross_type(s_ana_cmpr[unit]->dev, config->cross_type); +#endif // SOC_ANA_CMPR_CAN_DISTINGUISH_EDGE + /* Record the interrupt mask, the interrupt will be lazy installed when register the callbacks */ + s_ana_cmpr[unit]->intr_mask = analog_cmpr_ll_get_intr_mask_by_type(s_ana_cmpr[unit]->dev, config->cross_type); portEXIT_CRITICAL(&s_spinlock); - /* Allocate the interrupt, currently the interrupt source of Analog Comparator is shared with GPIO interrupt source */ - ESP_GOTO_ON_ERROR(esp_intr_alloc_intrstatus(ETS_GPIO_INTR_SOURCE, ANA_CMPR_INTR_FLAG, (uint32_t)analog_cmpr_ll_get_intr_status_reg(s_ana_cmpr[unit]->dev), - ANALOG_CMPR_LL_EVENT_CROSS, s_ana_cmpr_default_intr_handler, s_ana_cmpr[unit], &s_ana_cmpr[unit]->intr_handle), - err, TAG, "allocate interrupt failed"); - if (config->ref_src == ANA_CMPR_REF_SRC_INTERNAL) { ESP_LOGD(TAG, "unit %d allocated, source signal: GPIO %d, reference signal: internal", - (int)unit, ana_cmpr_io_map[unit].src_gpio); + (int)unit, ana_cmpr_periph[unit].src_gpio); } else { ESP_LOGD(TAG, "unit %d allocated, source signal: GPIO %d, reference signal: GPIO %d", - (int)unit, ana_cmpr_io_map[unit].src_gpio, ana_cmpr_io_map[unit].ext_ref_gpio); + (int)unit, ana_cmpr_periph[unit].src_gpio, ana_cmpr_periph[unit].ext_ref_gpio); } *ret_cmpr = s_ana_cmpr[unit]; @@ -219,17 +250,28 @@ esp_err_t ana_cmpr_set_debounce(ana_cmpr_handle_t cmpr, const ana_cmpr_debounce_ esp_err_t ana_cmpr_set_cross_type(ana_cmpr_handle_t cmpr, ana_cmpr_cross_type_t cross_type) { +#if SOC_ANA_CMPR_CAN_DISTINGUISH_EDGE + /* Not support to set the cross type after initialized, because it relies on the interrupt types to distinguish the edge, + * i.e. have to re-allocate the interrupt to change the cross type */ + (void)cmpr; + (void)cross_type; + return ESP_ERR_NOT_SUPPORTED; +#else ANA_CMPR_NULL_POINTER_CHECK_ISR(cmpr); ESP_RETURN_ON_FALSE_ISR(cross_type >= ANA_CMPR_CROSS_DISABLE && cross_type <= ANA_CMPR_CROSS_ANY, ESP_ERR_INVALID_ARG, TAG, "invalid cross type"); portENTER_CRITICAL_SAFE(&s_spinlock); +#if !SOC_ANA_CMPR_CAN_DISTINGUISH_EDGE analog_cmpr_ll_set_cross_type(cmpr->dev, cross_type); +#endif + cmpr->intr_mask = analog_cmpr_ll_get_intr_mask_by_type(cmpr->dev, cross_type); portEXIT_CRITICAL_SAFE(&s_spinlock); ESP_EARLY_LOGD(TAG, "unit %d cross type updated to %d", (int)cmpr->unit, cross_type); return ESP_OK; +#endif } esp_err_t ana_cmpr_register_event_callbacks(ana_cmpr_handle_t cmpr, const ana_cmpr_event_callbacks_t *cbs, void *user_data) @@ -249,6 +291,16 @@ esp_err_t ana_cmpr_register_event_callbacks(ana_cmpr_handle_t cmpr, const ana_cm } #endif + /* Allocate the interrupt, the interrupt source of Analog Comparator is shared with GPIO interrupt source on ESP32H2 */ + if (!cmpr->intr_handle) { + int intr_flags = ANA_CMPR_INTR_FLAG | (cmpr->intr_priority ? BIT(cmpr->intr_priority) : ESP_INTR_FLAG_LOWMED); +#if SOC_ANA_CMPR_INTR_SHARE_WITH_GPIO + intr_flags |= ESP_INTR_FLAG_SHARED; +#endif // SOC_ANA_CMPR_INTR_SHARE_WITH_GPIO + ESP_RETURN_ON_ERROR(esp_intr_alloc_intrstatus(ana_cmpr_periph[cmpr->unit].intr_src, intr_flags, (uint32_t)analog_cmpr_ll_get_intr_status_reg(cmpr->dev), + cmpr->intr_mask, s_ana_cmpr_default_intr_handler, cmpr, &cmpr->intr_handle), TAG, "allocate interrupt failed"); + } + /* Save the callback group */ memcpy(&(cmpr->cbs), cbs, sizeof(ana_cmpr_event_callbacks_t)); cmpr->user_data = user_data; @@ -273,7 +325,7 @@ esp_err_t ana_cmpr_enable(ana_cmpr_handle_t cmpr) /* Enable the Analog Comparator */ portENTER_CRITICAL(&s_spinlock); - analog_cmpr_ll_enable_intr(cmpr->dev, ANALOG_CMPR_LL_EVENT_CROSS, !!(cmpr->cbs.on_cross)); + analog_cmpr_ll_enable_intr(cmpr->dev, cmpr->intr_mask, true); analog_cmpr_ll_enable(cmpr->dev, true); portEXIT_CRITICAL(&s_spinlock); @@ -289,7 +341,7 @@ esp_err_t ana_cmpr_disable(ana_cmpr_handle_t cmpr) "the analog comparator not enabled yet"); /* Disable the Analog Comparator */ portENTER_CRITICAL(&s_spinlock); - analog_cmpr_ll_enable_intr(cmpr->dev, ANALOG_CMPR_LL_EVENT_CROSS, false); + analog_cmpr_ll_enable_intr(cmpr->dev, cmpr->intr_mask, false); analog_cmpr_ll_enable(cmpr->dev, false); portEXIT_CRITICAL(&s_spinlock); @@ -313,10 +365,10 @@ esp_err_t ana_cmpr_get_gpio(ana_cmpr_unit_t unit, ana_cmpr_channel_type_t chan_t /* Get the gpio number according to the channel type */ switch (chan_type) { case ANA_CMPR_SOURCE_CHAN: - *gpio_num = ana_cmpr_io_map[unit].src_gpio; + *gpio_num = ana_cmpr_periph[unit].src_gpio; break; case ANA_CMPR_EXT_REF_CHAN: - *gpio_num = ana_cmpr_io_map[unit].ext_ref_gpio; + *gpio_num = ana_cmpr_periph[unit].ext_ref_gpio; break; default: ESP_LOGE(TAG, "invalid channel type"); @@ -325,3 +377,11 @@ esp_err_t ana_cmpr_get_gpio(ana_cmpr_unit_t unit, ana_cmpr_channel_type_t chan_t return ESP_OK; } + +ana_cmpr_unit_t ana_cmpr_priv_get_unit_by_handle(ana_cmpr_handle_t cmpr) +{ + if (!cmpr) { + return -1; + } + return cmpr->unit; +} diff --git a/components/driver/analog_comparator/ana_cmpr_etm.c b/components/driver/analog_comparator/ana_cmpr_etm.c new file mode 100644 index 000000000000..02dc491269de --- /dev/null +++ b/components/driver/analog_comparator/ana_cmpr_etm.c @@ -0,0 +1,69 @@ +/* + * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include "sdkconfig.h" +#if CONFIG_ETM_ENABLE_DEBUG_LOG +// The local log level must be defined before including esp_log.h +// Set the maximum log level for this source file +#define LOG_LOCAL_LEVEL ESP_LOG_DEBUG +#endif +#include "freertos/FreeRTOS.h" +#include "esp_heap_caps.h" +#include "esp_log.h" +#include "esp_check.h" +#include "soc/soc_caps.h" +#include "hal/ana_cmpr_ll.h" +#include "driver/ana_cmpr_types.h" +#include "driver/ana_cmpr_etm.h" +#include "esp_private/etm_interface.h" +#include "ana_cmpr_private.h" + +static const char *TAG = "ana-cmpr-etm"; + +#define ETM_MEM_ALLOC_CAPS MALLOC_CAP_DEFAULT + +typedef struct { + esp_etm_event_t base; +} ana_cmpr_etm_event_t; + +static esp_err_t ana_cmpr_del_etm_event(esp_etm_event_handle_t base_event) +{ + ana_cmpr_etm_event_t *event = __containerof(base_event, ana_cmpr_etm_event_t, base); + free(event); + event = NULL; + return ESP_OK; +} + +esp_err_t ana_cmpr_new_etm_event(ana_cmpr_handle_t cmpr, const ana_cmpr_etm_event_config_t *config, esp_etm_event_handle_t *ret_event) +{ +#if CONFIG_ETM_ENABLE_DEBUG_LOG + esp_log_level_set(TAG, ESP_LOG_DEBUG); +#endif + esp_err_t ret = ESP_OK; + ana_cmpr_etm_event_t *event = NULL; + ana_cmpr_unit_t unit = ana_cmpr_priv_get_unit_by_handle(cmpr); + ESP_RETURN_ON_FALSE(((int)unit) >= 0, ESP_ERR_INVALID_ARG, TAG, "invalid analog comparator handle"); + ESP_RETURN_ON_FALSE(config && ret_event, ESP_ERR_INVALID_ARG, TAG, "invalid argument"); + event = heap_caps_calloc(1, sizeof(ana_cmpr_etm_event_t), ETM_MEM_ALLOC_CAPS); + ESP_GOTO_ON_FALSE(event, ESP_ERR_NO_MEM, err, TAG, "no mem for analog comparator event"); + + uint32_t event_id = ANALOG_CMPR_LL_ETM_SOURCE(unit, config->event_type); + event->base.del = ana_cmpr_del_etm_event; + event->base.event_id = event_id; + event->base.trig_periph = ETM_TRIG_PERIPH_ANA_CMPR; + ESP_LOGD(TAG, "new event @%p, event_id=%"PRIu32", unit_id=%d", event, event_id, unit); + *ret_event = &event->base; + return ESP_OK; + +err: + if (event) { + free(event); + event = NULL; + } + return ret; +} diff --git a/components/driver/analog_comparator/ana_cmpr_private.h b/components/driver/analog_comparator/ana_cmpr_private.h new file mode 100644 index 000000000000..4bbacb9c0cc2 --- /dev/null +++ b/components/driver/analog_comparator/ana_cmpr_private.h @@ -0,0 +1,20 @@ +/* + * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include +#include "driver/ana_cmpr_types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +ana_cmpr_unit_t ana_cmpr_priv_get_unit_by_handle(ana_cmpr_handle_t cmpr); + +#ifdef __cplusplus +} +#endif diff --git a/components/driver/analog_comparator/include/driver/ana_cmpr.h b/components/driver/analog_comparator/include/driver/ana_cmpr.h index 4cd09852b13b..d10b1e0a74f5 100644 --- a/components/driver/analog_comparator/include/driver/ana_cmpr.h +++ b/components/driver/analog_comparator/include/driver/ana_cmpr.h @@ -28,6 +28,12 @@ typedef struct { * for external reference, the reference signal should be connect to `ANA_CMPRx_EXT_REF_GPIO` */ ana_cmpr_cross_type_t cross_type; /*!< The crossing types that can trigger interrupt */ + 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 */ + struct { + uint32_t io_loop_back:1; /*!< Enable this field when the other signals that output on the comparision pins are supposed to be fed back. + * Normally used for debug/test scenario */ + } flags; /*!< Analog comparator driver flags */ } ana_cmpr_config_t; /** diff --git a/components/driver/analog_comparator/include/driver/ana_cmpr_etm.h b/components/driver/analog_comparator/include/driver/ana_cmpr_etm.h new file mode 100644 index 000000000000..dce83452386d --- /dev/null +++ b/components/driver/analog_comparator/include/driver/ana_cmpr_etm.h @@ -0,0 +1,55 @@ +/* + * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include +#include "esp_err.h" +#include "esp_etm.h" +#include "driver/ana_cmpr_types.h" + +#if SOC_ANA_CMPR_SUPPORT_ETM + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Analog Comparator ETM Events for each unit + * + */ +typedef enum { + ANA_CMPR_EVENT_POS_CROSS, /*!< Positive cross event when the source signal becomes higher than the reference signal */ + ANA_CMPR_EVENT_NEG_CROSS, /*!< Negative cross event when the source signal becomes lower than the reference signal */ +} ana_cmpr_event_type_t; + +/** + * @brief Analog Comparator ETM event configuration + * + */ +typedef struct { + ana_cmpr_event_type_t event_type; /*!< Which kind of cross type can trigger the ETM event module */ +} ana_cmpr_etm_event_config_t; + +/** + * @brief Allocate a new Analog Comparator ETM event + * + * @param[in] cmpr Analog Comparator handle that allocated by `ana_cmpr_new_unit` + * @param[in] config Analog Comparator ETM event configuration + * @param[out] ret_event The returned generic handle of ETM event, which is used to connect to a task in the ETM driver + * @return + * - ESP_OK Success to create the new ETM event handle + * - ESP_ERR_NO_MEM No memory for the ETM event + * - ESP_ERR_INVALID_ARG NULL pointer of the input parameters + * - ESP_ERR_INVALID_STATE The event on the unit has been registered + */ +esp_err_t ana_cmpr_new_etm_event(ana_cmpr_handle_t cmpr, const ana_cmpr_etm_event_config_t *config, esp_etm_event_handle_t *ret_event); + +#ifdef __cplusplus +} +#endif + +#endif // SOC_ANA_CMPR_SUPPORT_ETM diff --git a/components/driver/analog_comparator/include/driver/ana_cmpr_types.h b/components/driver/analog_comparator/include/driver/ana_cmpr_types.h index a04ae0b52f75..978509262097 100644 --- a/components/driver/analog_comparator/include/driver/ana_cmpr_types.h +++ b/components/driver/analog_comparator/include/driver/ana_cmpr_types.h @@ -7,6 +7,7 @@ #pragma once #include +#include "soc/soc_caps.h" #include "soc/clk_tree_defs.h" #ifdef __cplusplus @@ -17,9 +18,9 @@ extern "C" { * @brief Analog comparator unit * */ -typedef enum { - ANA_CMPR_UNIT_0, /*!< Analog Comparator unit */ -} ana_cmpr_unit_t; +typedef int ana_cmpr_unit_t; + +#define ANA_CMPR_UNIT_0 0 /*!< @deprecated Analog comparator unit 0 */ /** * @brief Analog comparator reference source @@ -90,7 +91,9 @@ typedef int ana_cmpr_clk_src_t; * */ typedef struct { - // No data for now + ana_cmpr_cross_type_t cross_type; /*!< The cross type of the target signal to the reference signal. + * Will either be ANA_CMPR_CROSS_POS or ANA_CMPR_CROSS_NEG + * Always be ANA_CMPR_CROSS_ANY if target does not support independent interrupt (like ESP32H2) */ } ana_cmpr_cross_event_data_t; /** diff --git a/components/driver/gpio/gpio.c b/components/driver/gpio/gpio.c index 3cea507013c2..b6f4324607ed 100644 --- a/components/driver/gpio/gpio.c +++ b/components/driver/gpio/gpio.c @@ -594,7 +594,7 @@ esp_err_t gpio_isr_register(void (*fn)(void *), void *arg, int intr_alloc_flags, p.source = ETS_GPIO_INTR0_SOURCE; #endif p.intr_alloc_flags = intr_alloc_flags; -#if SOC_ANA_CMPR_SUPPORTED +#if SOC_ANA_CMPR_INTR_SHARE_WITH_GPIO p.intr_alloc_flags |= ESP_INTR_FLAG_SHARED; #endif p.fn = fn; diff --git a/components/driver/test_apps/.build-test-rules.yml b/components/driver/test_apps/.build-test-rules.yml index d13e5809a912..5af096497f9d 100644 --- a/components/driver/test_apps/.build-test-rules.yml +++ b/components/driver/test_apps/.build-test-rules.yml @@ -3,6 +3,10 @@ components/driver/test_apps/analog_comparator: disable: - if: SOC_ANA_CMPR_SUPPORTED != 1 + disable_test: + - if: IDF_TARGET == "esp32p4" + temporary: true + reason: not supported yet components/driver/test_apps/dac_test_apps/dac: disable: diff --git a/components/driver/test_apps/analog_comparator/README.md b/components/driver/test_apps/analog_comparator/README.md index 8161d00f6d8e..bde3cf8e7385 100644 --- a/components/driver/test_apps/analog_comparator/README.md +++ b/components/driver/test_apps/analog_comparator/README.md @@ -1,2 +1,2 @@ -| Supported Targets | ESP32-H2 | -| ----------------- | -------- | +| Supported Targets | ESP32-H2 | ESP32-P4 | +| ----------------- | -------- | -------- | diff --git a/components/driver/test_apps/analog_comparator/main/test_ana_cmpr.c b/components/driver/test_apps/analog_comparator/main/test_ana_cmpr.c index 53a69ea9c7f0..b24776e455ed 100644 --- a/components/driver/test_apps/analog_comparator/main/test_ana_cmpr.c +++ b/components/driver/test_apps/analog_comparator/main/test_ana_cmpr.c @@ -18,7 +18,7 @@ TEST_CASE("ana_cmpr_unit_install_uninstall", "[ana_cmpr]") /* Allocate a wrong unit */ TEST_ESP_ERR(ESP_ERR_INVALID_ARG, ana_cmpr_new_unit(&config, &cmpr)); /* Allocate a correct unit */ - config.unit = ANA_CMPR_UNIT_0; + config.unit = 0; TEST_ESP_OK(ana_cmpr_new_unit(&config, &cmpr)); /* Try to allocate a existed unit */ TEST_ESP_ERR(ESP_ERR_INVALID_STATE, ana_cmpr_new_unit(&config, &cmpr)); @@ -51,14 +51,15 @@ TEST_CASE("ana_cmpr_unit_install_uninstall", "[ana_cmpr]") TEST_CASE("ana_cmpr_internal_reference", "[ana_cmpr]") { int src_chan = test_init_src_chan_gpio(); - uint32_t cnt = 0; + uint32_t cnt = 0; ana_cmpr_handle_t cmpr = NULL; ana_cmpr_config_t config = { - .unit = ANA_CMPR_UNIT_0, + .unit = 0, .clk_src = ANA_CMPR_CLK_SRC_DEFAULT, .ref_src = ANA_CMPR_REF_SRC_INTERNAL, .cross_type = ANA_CMPR_CROSS_ANY, + .flags.io_loop_back = 1, }; TEST_ESP_OK(ana_cmpr_new_unit(&config, &cmpr)); ana_cmpr_internal_ref_config_t ref_cfg = { diff --git a/components/driver/test_apps/analog_comparator/main/test_ana_cmpr_common.c b/components/driver/test_apps/analog_comparator/main/test_ana_cmpr_common.c index 9db12a0d07fe..0feb218ea666 100644 --- a/components/driver/test_apps/analog_comparator/main/test_ana_cmpr_common.c +++ b/components/driver/test_apps/analog_comparator/main/test_ana_cmpr_common.c @@ -19,7 +19,7 @@ bool IRAM_ATTR test_ana_cmpr_on_cross_callback(ana_cmpr_handle_t cmpr, const ana int test_init_src_chan_gpio(void) { int src_chan_num = -1; - TEST_ESP_OK(ana_cmpr_get_gpio(ANA_CMPR_UNIT_0, ANA_CMPR_SOURCE_CHAN, &src_chan_num)); + TEST_ESP_OK(ana_cmpr_get_gpio(0, ANA_CMPR_SOURCE_CHAN, &src_chan_num)); TEST_ASSERT(src_chan_num > 0); gpio_config_t io_conf = { .intr_type = GPIO_INTR_DISABLE, diff --git a/components/driver/test_apps/analog_comparator/main/test_ana_cmpr_iram.c b/components/driver/test_apps/analog_comparator/main/test_ana_cmpr_iram.c index ce0d5425f995..44da8afacd6e 100644 --- a/components/driver/test_apps/analog_comparator/main/test_ana_cmpr_iram.c +++ b/components/driver/test_apps/analog_comparator/main/test_ana_cmpr_iram.c @@ -44,10 +44,11 @@ TEST_CASE("ana_cmpr_internal_reference_iram_safe", "[ana_cmpr]") ana_cmpr_handle_t cmpr = NULL; ana_cmpr_config_t config = { - .unit = ANA_CMPR_UNIT_0, + .unit = 0, .clk_src = ANA_CMPR_CLK_SRC_DEFAULT, .ref_src = ANA_CMPR_REF_SRC_INTERNAL, .cross_type = ANA_CMPR_CROSS_ANY, + .flags.io_loop_back = 1, }; TEST_ESP_OK(ana_cmpr_new_unit(&config, &cmpr)); test_data.handle = cmpr; diff --git a/components/esp_hw_support/include/esp_private/etm_interface.h b/components/esp_hw_support/include/esp_private/etm_interface.h index 351f89c70246..a7c7e4253886 100644 --- a/components/esp_hw_support/include/esp_private/etm_interface.h +++ b/components/esp_hw_support/include/esp_private/etm_interface.h @@ -25,6 +25,7 @@ typedef enum { ETM_TRIG_PERIPH_GPTIMER, /*!< ETM trigger source: GPTimer */ ETM_TRIG_PERIPH_SYSTIMER, /*!< ETM trigger source: Systimer */ ETM_TRIG_PERIPH_MCPWM, /*!< ETM trigger source: MCPWM */ + ETM_TRIG_PERIPH_ANA_CMPR, /*!< ETM trigger source: Analog Comparator */ } etm_trigger_peripheral_t; /** diff --git a/components/esp_hw_support/port/esp32c6/io_mux.c b/components/esp_hw_support/port/esp32c6/io_mux.c index ea7446b2baaf..e5679e40b4bd 100644 --- a/components/esp_hw_support/port/esp32c6/io_mux.c +++ b/components/esp_hw_support/port/esp32c6/io_mux.c @@ -28,5 +28,6 @@ esp_err_t io_mux_set_clock_source(soc_module_clk_t clk_src) } gpio_ll_iomux_set_clk_src(clk_src); + return ESP_OK; } diff --git a/components/esp_hw_support/port/esp32h2/io_mux.c b/components/esp_hw_support/port/esp32h2/io_mux.c index a621deb8a31c..653c0b298129 100644 --- a/components/esp_hw_support/port/esp32h2/io_mux.c +++ b/components/esp_hw_support/port/esp32h2/io_mux.c @@ -28,5 +28,6 @@ esp_err_t io_mux_set_clock_source(soc_module_clk_t clk_src) } gpio_ll_iomux_set_clk_src(clk_src); + return ESP_OK; } diff --git a/components/esp_hw_support/port/esp32p4/io_mux.c b/components/esp_hw_support/port/esp32p4/io_mux.c index e69de29bb2d1..10e021aba0f8 100644 --- a/components/esp_hw_support/port/esp32p4/io_mux.c +++ b/components/esp_hw_support/port/esp32p4/io_mux.c @@ -0,0 +1,34 @@ +/* + * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "freertos/FreeRTOS.h" +#include "esp_private/io_mux.h" +#include "esp_private/periph_ctrl.h" +#include "hal/gpio_ll.h" + +static portMUX_TYPE s_io_mux_spinlock = portMUX_INITIALIZER_UNLOCKED; +static soc_module_clk_t s_io_mux_clk_src = 0; // by default, the clock source is not set explicitly by any consumer (e.g. SDM, Filter) + +esp_err_t io_mux_set_clock_source(soc_module_clk_t clk_src) +{ + bool clk_conflict = false; + // check is the IO MUX has been set to another clock source + portENTER_CRITICAL(&s_io_mux_spinlock); + if (s_io_mux_clk_src != 0 && s_io_mux_clk_src != clk_src) { + clk_conflict = true; + } else { + s_io_mux_clk_src = clk_src; + } + portEXIT_CRITICAL(&s_io_mux_spinlock); + + if (clk_conflict) { + return ESP_ERR_INVALID_STATE; + } + PERIPH_RCC_ATOMIC() { + gpio_ll_iomux_set_clk_src(clk_src); + } + return ESP_OK; +} diff --git a/components/esp_hw_support/test_apps/etm/main/CMakeLists.txt b/components/esp_hw_support/test_apps/etm/main/CMakeLists.txt index 14c910836c8e..e7a895debdcb 100644 --- a/components/esp_hw_support/test_apps/etm/main/CMakeLists.txt +++ b/components/esp_hw_support/test_apps/etm/main/CMakeLists.txt @@ -21,6 +21,11 @@ if(CONFIG_SOC_MCPWM_SUPPORT_ETM) list(APPEND srcs "test_mcpwm_etm.c") endif() +if(CONFIG_SOC_ANA_CMPR_SUPPORT_ETM AND CONFIG_SOC_TIMER_SUPPORT_ETM) + # Analog Comparator event test relies on GPTIMER task + list(APPEND srcs "test_ana_cmpr_etm.c") +endif() + # In order for the cases defined by `TEST_CASE` to be linked into the final elf, # the component can be registered as WHOLE_ARCHIVE idf_component_register(SRCS ${srcs} diff --git a/components/esp_hw_support/test_apps/etm/main/test_ana_cmpr_etm.c b/components/esp_hw_support/test_apps/etm/main/test_ana_cmpr_etm.c new file mode 100644 index 000000000000..348662862807 --- /dev/null +++ b/components/esp_hw_support/test_apps/etm/main/test_ana_cmpr_etm.c @@ -0,0 +1,167 @@ +/* + * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include "unity.h" +#include "unity_test_utils.h" +#include "esp_attr.h" +#include "esp_etm.h" +#include "driver/gpio.h" +#include "driver/gptimer.h" +#include "driver/ana_cmpr.h" +#include "driver/gptimer_etm.h" +#include "driver/ana_cmpr_etm.h" + +#define TEST_ANA_CMPR_UNIT 0 +#define TEST_TIME_US 5000 + +static gptimer_handle_t s_test_ana_cmpr_gptimer_init(void) +{ + gptimer_handle_t gptimer = NULL; + gptimer_config_t timer_config = { + .clk_src = GPTIMER_CLK_SRC_DEFAULT, + .direction = GPTIMER_COUNT_UP, + .resolution_hz = 1 * 1000 * 1000, // 1MHz, 1 tick = 1us + }; + TEST_ESP_OK(gptimer_new_timer(&timer_config, &gptimer)); + TEST_ESP_OK(gptimer_set_raw_count(gptimer, 0)); + TEST_ESP_OK(gptimer_enable(gptimer)); + return gptimer; +} + +static void s_test_ana_cmpr_gptimer_deinit(gptimer_handle_t gptimer) +{ + TEST_ESP_OK(gptimer_disable(gptimer)); + TEST_ESP_OK(gptimer_del_timer(gptimer)); +} + +static ana_cmpr_handle_t s_test_ana_cmpr_init(void) +{ + ana_cmpr_handle_t cmpr = NULL; + + ana_cmpr_config_t config = { + .unit = TEST_ANA_CMPR_UNIT, + .clk_src = ANA_CMPR_CLK_SRC_DEFAULT, + .ref_src = ANA_CMPR_REF_SRC_INTERNAL, + .cross_type = ANA_CMPR_CROSS_ANY, + }; + TEST_ESP_OK(ana_cmpr_new_unit(&config, &cmpr)); + + ana_cmpr_internal_ref_config_t ref_cfg = { + .ref_volt = ANA_CMPR_REF_VOLT_50_PCT_VDD, + }; + TEST_ESP_OK(ana_cmpr_set_internal_reference(cmpr, &ref_cfg)); + + ana_cmpr_debounce_config_t dbc_cfg = { + .wait_us = 10, + }; + TEST_ESP_OK(ana_cmpr_set_debounce(cmpr, &dbc_cfg)); + TEST_ESP_OK(ana_cmpr_enable(cmpr)); + + return cmpr; +} + +static void s_test_ana_cmpr_deinit(ana_cmpr_handle_t cmpr) +{ + TEST_ESP_OK(ana_cmpr_disable(cmpr)); + TEST_ESP_OK(ana_cmpr_del_unit(cmpr)); +} + +static int s_test_ana_cmpr_src_gpio_init(void) +{ + int gpio_num = -1; + TEST_ESP_OK(ana_cmpr_get_gpio(TEST_ANA_CMPR_UNIT, ANA_CMPR_SOURCE_CHAN, &gpio_num)); + gpio_config_t io_conf = { + .intr_type = GPIO_INTR_DISABLE, + .mode = GPIO_MODE_OUTPUT, + .pin_bit_mask = (1ULL << gpio_num), + .pull_down_en = false, + .pull_up_en = false, + }; + gpio_config(&io_conf); + gpio_set_level(gpio_num, 1); + return gpio_num; +} + +typedef struct { + esp_etm_event_handle_t cmpr_pos_evt; + esp_etm_event_handle_t cmpr_neg_evt; + esp_etm_task_handle_t gptimer_start_task; + esp_etm_task_handle_t gptimer_stop_task; + esp_etm_channel_handle_t etm_pos_handle; + esp_etm_channel_handle_t etm_neg_handle; +} test_ana_cmpr_etm_handles_t; + +static test_ana_cmpr_etm_handles_t s_test_ana_cmpr_init_etm(ana_cmpr_handle_t cmpr, gptimer_handle_t gptimer) +{ + test_ana_cmpr_etm_handles_t etm_handles = {}; + + /* Allocate the analog comparator positive & negative cross events */ + ana_cmpr_etm_event_config_t evt_cfg = { + .event_type = ANA_CMPR_EVENT_POS_CROSS, + }; + TEST_ESP_OK(ana_cmpr_new_etm_event(cmpr, &evt_cfg, &etm_handles.cmpr_pos_evt)); + evt_cfg.event_type = GPIO_ETM_EVENT_EDGE_NEG; + TEST_ESP_OK(ana_cmpr_new_etm_event(cmpr, &evt_cfg, &etm_handles.cmpr_neg_evt)); + + /* Allocate the GPTimer tasks */ + gptimer_etm_task_config_t gptimer_etm_task_conf = { + .task_type = GPTIMER_ETM_TASK_START_COUNT, + }; + TEST_ESP_OK(gptimer_new_etm_task(gptimer, &gptimer_etm_task_conf, &etm_handles.gptimer_start_task)); + gptimer_etm_task_conf.task_type = GPTIMER_ETM_TASK_STOP_COUNT; + TEST_ESP_OK(gptimer_new_etm_task(gptimer, &gptimer_etm_task_conf, &etm_handles.gptimer_stop_task)); + + /* Allocate the Event Task Matrix channels */ + esp_etm_channel_config_t etm_cfg = {}; + TEST_ESP_OK(esp_etm_new_channel(&etm_cfg, &etm_handles.etm_pos_handle)); + TEST_ESP_OK(esp_etm_new_channel(&etm_cfg, &etm_handles.etm_neg_handle)); + /* Bind the events and tasks */ + TEST_ESP_OK(esp_etm_channel_connect(etm_handles.etm_pos_handle, etm_handles.cmpr_pos_evt, etm_handles.gptimer_start_task)); + TEST_ESP_OK(esp_etm_channel_connect(etm_handles.etm_neg_handle, etm_handles.cmpr_neg_evt, etm_handles.gptimer_stop_task)); + /* Enable the ETM channels */ + TEST_ESP_OK(esp_etm_channel_enable(etm_handles.etm_pos_handle)); + TEST_ESP_OK(esp_etm_channel_enable(etm_handles.etm_neg_handle)); + + return etm_handles; +} + +static void s_test_ana_cmpr_deinit_etm(test_ana_cmpr_etm_handles_t handles) +{ + TEST_ESP_OK(esp_etm_channel_disable(handles.etm_pos_handle)); + TEST_ESP_OK(esp_etm_channel_disable(handles.etm_pos_handle)); + + TEST_ESP_OK(esp_etm_del_task(handles.gptimer_start_task)); + TEST_ESP_OK(esp_etm_del_task(handles.gptimer_stop_task)); + + TEST_ESP_OK(esp_etm_del_event(handles.cmpr_pos_evt)); + TEST_ESP_OK(esp_etm_del_event(handles.cmpr_neg_evt)); + + TEST_ESP_OK(esp_etm_del_channel(handles.etm_pos_handle)); + TEST_ESP_OK(esp_etm_del_channel(handles.etm_neg_handle)); +} + +TEST_CASE("analog_comparator_etm_event", "[etm]") +{ + gptimer_handle_t gptimer = s_test_ana_cmpr_gptimer_init(); + ana_cmpr_handle_t cmpr = s_test_ana_cmpr_init(); + int src_gpio = s_test_ana_cmpr_src_gpio_init(); + test_ana_cmpr_etm_handles_t handles = s_test_ana_cmpr_init_etm(cmpr, gptimer); + + gpio_set_level(src_gpio, 0); + esp_rom_delay_us(TEST_TIME_US); + gpio_set_level(src_gpio, 1); + + uint64_t cnt_us = 0; + TEST_ESP_OK(gptimer_get_raw_count(gptimer, &cnt_us)); + + s_test_ana_cmpr_deinit_etm(handles); + s_test_ana_cmpr_deinit(cmpr); + s_test_ana_cmpr_gptimer_deinit(gptimer); + + TEST_ASSERT_UINT64_WITHIN(TEST_TIME_US * 0.1, 5000, cnt_us); +} diff --git a/components/hal/esp32h2/include/hal/ana_cmpr_ll.h b/components/hal/esp32h2/include/hal/ana_cmpr_ll.h index a818ea8b4719..d45b43a5fa8b 100644 --- a/components/hal/esp32h2/include/hal/ana_cmpr_ll.h +++ b/components/hal/esp32h2/include/hal/ana_cmpr_ll.h @@ -9,14 +9,11 @@ #include #include "hal/misc.h" #include "hal/assert.h" -#include "soc/gpio_ext_struct.h" +#include "soc/ana_cmpr_struct.h" -#define ANALOG_CMPR_LL_GET_HW() (&ANALOG_CMPR) +#define ANALOG_CMPR_LL_GET_HW(unit) (&ANALOG_CMPR[unit]) #define ANALOG_CMPR_LL_EVENT_CROSS (1 << 0) -#define ANALOG_CMPR_LL_POS_CROSS_MASK (1 << 1) -#define ANALOG_CMPR_LL_NEG_CROSS_MASK (1 << 2) - #ifdef __cplusplus extern "C" { #endif @@ -29,7 +26,7 @@ extern "C" { */ static inline void analog_cmpr_ll_enable(analog_cmpr_dev_t *hw, bool en) { - hw->pad_comp_config.xpd_comp = en; + hw->pad_comp_config->xpd_comp = en; } /** @@ -41,7 +38,7 @@ static inline void analog_cmpr_ll_enable(analog_cmpr_dev_t *hw, bool en) __attribute__((always_inline)) static inline void analog_cmpr_ll_set_internal_ref_voltage(analog_cmpr_dev_t *hw, uint32_t volt_level) { - hw->pad_comp_config.dref_comp = volt_level; + hw->pad_comp_config->dref_comp = volt_level; } /** @@ -52,7 +49,7 @@ static inline void analog_cmpr_ll_set_internal_ref_voltage(analog_cmpr_dev_t *hw */ static inline float analog_cmpr_ll_get_internal_ref_voltage(analog_cmpr_dev_t *hw) { - return hw->pad_comp_config.dref_comp * 0.1F; + return hw->pad_comp_config->dref_comp * 0.1F; } /** @@ -65,7 +62,7 @@ static inline float analog_cmpr_ll_get_internal_ref_voltage(analog_cmpr_dev_t *h */ static inline void analog_cmpr_ll_set_ref_source(analog_cmpr_dev_t *hw, uint32_t ref_src) { - hw->pad_comp_config.mode_comp = ref_src; + hw->pad_comp_config->mode_comp = ref_src; } /** @@ -81,7 +78,23 @@ static inline void analog_cmpr_ll_set_ref_source(analog_cmpr_dev_t *hw, uint32_t __attribute__((always_inline)) static inline void analog_cmpr_ll_set_cross_type(analog_cmpr_dev_t *hw, uint8_t type) { - hw->pad_comp_config.zero_det_mode = type; + hw->pad_comp_config->zero_det_mode = type; +} + +/** + * @brief Get the interrupt mask by trigger type + * @note Only one interrupt on H2 + * + * @param hw Analog comparator register base address + * @param type Not used on H2, because H2 can't distinguish the edge type + * The parameter here only to be compatible with other targets + * @return interrupt mask + */ +__attribute__((always_inline)) +static inline uint32_t analog_cmpr_ll_get_intr_mask_by_type(analog_cmpr_dev_t *hw, uint8_t type) +{ + (void)type; + return ANALOG_CMPR_LL_EVENT_CROSS; } /** @@ -95,7 +108,7 @@ static inline void analog_cmpr_ll_set_cross_type(analog_cmpr_dev_t *hw, uint8_t __attribute__((always_inline)) static inline void analog_cmpr_ll_set_debounce_cycle(analog_cmpr_dev_t *hw, uint32_t cycle) { - hw->pad_comp_filter.zero_det_filter_cnt = cycle; + hw->pad_comp_filter->zero_det_filter_cnt = cycle; } /** @@ -107,11 +120,13 @@ static inline void analog_cmpr_ll_set_debounce_cycle(analog_cmpr_dev_t *hw, uint */ static inline void analog_cmpr_ll_enable_intr(analog_cmpr_dev_t *hw, uint32_t mask, bool enable) { + uint32_t val = hw->int_ena->val; if (enable) { - hw->int_ena.val |= mask; + val |= mask; } else { - hw->int_ena.val &= ~mask; + val &= ~mask; } + hw->int_ena->val = val; } /** @@ -122,7 +137,7 @@ static inline void analog_cmpr_ll_enable_intr(analog_cmpr_dev_t *hw, uint32_t ma __attribute__((always_inline)) static inline uint32_t analog_cmpr_ll_get_intr_status(analog_cmpr_dev_t *hw) { - return hw->int_st.val; + return hw->int_st->val; } /** @@ -134,7 +149,7 @@ static inline uint32_t analog_cmpr_ll_get_intr_status(analog_cmpr_dev_t *hw) __attribute__((always_inline)) static inline void analog_cmpr_ll_clear_intr(analog_cmpr_dev_t *hw, uint32_t mask) { - hw->int_clr.val = mask; + hw->int_clr->val = mask; } /** @@ -145,7 +160,7 @@ static inline void analog_cmpr_ll_clear_intr(analog_cmpr_dev_t *hw, uint32_t mas */ static inline volatile void *analog_cmpr_ll_get_intr_status_reg(analog_cmpr_dev_t *hw) { - return &hw->int_st; + return hw->int_st; } #ifdef __cplusplus diff --git a/components/hal/esp32p4/include/hal/ana_cmpr_ll.h b/components/hal/esp32p4/include/hal/ana_cmpr_ll.h new file mode 100644 index 000000000000..d1075ed64ac7 --- /dev/null +++ b/components/hal/esp32p4/include/hal/ana_cmpr_ll.h @@ -0,0 +1,170 @@ +/* + * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include +#include "hal/misc.h" +#include "hal/assert.h" +#include "soc/ana_cmpr_struct.h" +#include "soc/soc_etm_source.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define ANALOG_CMPR_LL_GET_HW(unit) (&ANALOG_CMPR[unit]) +#define ANALOG_CMPR_LL_GET_UNIT(hw) ((hw) == (&ANALOG_CMPR[0]) ? 0 : 1) +#define ANALOG_CMPR_LL_EVENT_CROSS (1 << 0) +#define ANALOG_CMPR_LL_ETM_EVENTS_PER_UNIT (2) + +#define ANALOG_CMPR_LL_NEG_CROSS_MASK(unit) (1UL << ((int)unit * 3)) +#define ANALOG_CMPR_LL_POS_CROSS_MASK(unit) (1UL << ((int)unit * 3 + 1)) + +#define ANALOG_CMPR_LL_ETM_SOURCE(unit, type) (GPIO_EVT_ZERO_DET_POS0 + (unit) * 2 + (type)) + + +/** + * @brief Enable analog comparator + * + * @param hw Analog comparator register base address + * @param en True to enable, False to disable + */ +static inline void analog_cmpr_ll_enable(analog_cmpr_dev_t *hw, bool en) +{ + hw->pad_comp_config->xpd_comp = en; +} + +/** + * @brief Set the voltage of the internal reference + * + * @param hw Analog comparator register base address + * @param volt_level The voltage level of the internal reference, range [0.0V, 0.7VDD], step 0.1VDD + */ +__attribute__((always_inline)) +static inline void analog_cmpr_ll_set_internal_ref_voltage(analog_cmpr_dev_t *hw, uint32_t volt_level) +{ + hw->pad_comp_config->dref_comp = volt_level; +} + +/** + * @brief Get the voltage of the internal reference + * + * @param hw Analog comparator register base address + * @return The voltage of the internal reference + */ +static inline float analog_cmpr_ll_get_internal_ref_voltage(analog_cmpr_dev_t *hw) +{ + return hw->pad_comp_config->dref_comp * 0.1F; +} + +/** + * @brief The reference voltage comes from internal or external + * + * @note Also see `analog_cmpr_ll_set_internal_ref_voltage` to use the internal reference voltage + * + * @param hw Analog comparator register base address + * @param ref_src reference source, 0 for internal, 1 for external GPIO pad (GPIO10) + */ +static inline void analog_cmpr_ll_set_ref_source(analog_cmpr_dev_t *hw, uint32_t ref_src) +{ + hw->pad_comp_config->mode_comp = ref_src; +} + +/** + * @brief Get the interrupt mask by trigger type + * + * @param hw Analog comparator register base address + * @param type The type of cross interrupt + * - 0: disable interrupt + * - 1: enable positive cross interrupt (input analog goes from low to high and across the reference voltage) + * - 2: enable negative cross interrupt (input analog goes from high to low and across the reference voltage) + * - 3: enable any positive or negative cross interrupt + * @return interrupt mask + */ +__attribute__((always_inline)) +static inline uint32_t analog_cmpr_ll_get_intr_mask_by_type(analog_cmpr_dev_t *hw, uint8_t type) +{ + uint32_t unit = ANALOG_CMPR_LL_GET_UNIT(hw); + uint32_t mask = 0; + if (type & 0x01) { + mask |= ANALOG_CMPR_LL_POS_CROSS_MASK(unit); + } + if (type & 0x02) { + mask |= ANALOG_CMPR_LL_NEG_CROSS_MASK(unit); + } + return mask; +} + +/** + * @brief Set the debounce cycle for the cross detection + * + * @note When the comparator detects a cross, it will wait for the debounce cycle to make sure the cross is stable. + * + * @param hw Analog comparator register base address + * @param cycle The debounce cycle + */ +__attribute__((always_inline)) +static inline void analog_cmpr_ll_set_debounce_cycle(analog_cmpr_dev_t *hw, uint32_t cycle) +{ + hw->pad_comp_filter->zero_det_filter_cnt = cycle; +} + +/** + * @brief Enable comparator interrupt + * + * @param hw Analog comparator register base address + * @param mask Interrupt mask + * @param enable True to enable, False to disable + */ +static inline void analog_cmpr_ll_enable_intr(analog_cmpr_dev_t *hw, uint32_t mask, bool enable) +{ + uint32_t val = hw->int_ena->val; + if (enable) { + val |= mask; + } else { + val &= ~mask; + } + hw->int_ena->val = val; +} + +/** + * @brief Get comparator interrupt status + * + * @param hw Analog comparator register base address + */ +__attribute__((always_inline)) +static inline uint32_t analog_cmpr_ll_get_intr_status(analog_cmpr_dev_t *hw) +{ + return hw->int_st->val; +} + +/** + * @brief Clear comparator interrupt status + * + * @param hw Analog comparator register base address + * @param mask Interrupt status word + */ +__attribute__((always_inline)) +static inline void analog_cmpr_ll_clear_intr(analog_cmpr_dev_t *hw, uint32_t mask) +{ + hw->int_clr->val = mask; +} + +/** + * @brief Get the interrupt status register address + * + * @param hw Analog comparator register base address + * @return The interrupt status register address + */ +static inline volatile void *analog_cmpr_ll_get_intr_status_reg(analog_cmpr_dev_t *hw) +{ + return hw->int_st; +} + +#ifdef __cplusplus +} +#endif diff --git a/components/hal/esp32p4/include/hal/gpio_ll.h b/components/hal/esp32p4/include/hal/gpio_ll.h index 274d5e68102d..61e241428060 100644 --- a/components/hal/esp32p4/include/hal/gpio_ll.h +++ b/components/hal/esp32p4/include/hal/gpio_ll.h @@ -603,6 +603,10 @@ static inline void gpio_ll_iomux_set_clk_src(soc_module_clk_t src) } } +/// use a macro to wrap the function, force the caller to use it in a critical section +/// the critical section needs to declare the __DECLARE_RCC_ATOMIC_ENV variable in advance +#define gpio_ll_iomux_set_clk_src(...) (void)__DECLARE_RCC_ATOMIC_ENV; gpio_ll_iomux_set_clk_src(__VA_ARGS__) + /** * @brief Force hold digital io pad. * @note GPIO force hold, whether the chip in sleep mode or wakeup mode. diff --git a/components/soc/esp32h2/ana_cmpr_periph.c b/components/soc/esp32h2/ana_cmpr_periph.c index 4d3a67586c8c..e83acba42c9c 100644 --- a/components/soc/esp32h2/ana_cmpr_periph.c +++ b/components/soc/esp32h2/ana_cmpr_periph.c @@ -5,10 +5,22 @@ */ #include "soc/ana_cmpr_periph.h" +#include "soc/ana_cmpr_struct.h" -const ana_cmpr_conn_t ana_cmpr_io_map[SOC_ANA_CMPR_NUM] = { +const ana_cmpr_periph_t ana_cmpr_periph[SOC_ANA_CMPR_NUM] = { [0] = { .src_gpio = ANA_CMPR0_SRC_GPIO, .ext_ref_gpio = ANA_CMPR0_EXT_REF_GPIO, + .intr_src = ETS_GPIO_INTR_SOURCE, + }, +}; + +analog_cmpr_dev_t ANALOG_CMPR[SOC_ANA_CMPR_NUM] = { + [0] = { + .pad_comp_config = &GPIO_EXT.pad_comp_config, + .pad_comp_filter = &GPIO_EXT.pad_comp_filter, + .int_st = &GPIO_EXT.int_st, + .int_ena = &GPIO_EXT.int_ena, + .int_clr = &GPIO_EXT.int_clr, }, }; diff --git a/components/soc/esp32h2/include/soc/Kconfig.soc_caps.in b/components/soc/esp32h2/include/soc/Kconfig.soc_caps.in index e80aec35f666..fa5fd53e6cca 100644 --- a/components/soc/esp32h2/include/soc/Kconfig.soc_caps.in +++ b/components/soc/esp32h2/include/soc/Kconfig.soc_caps.in @@ -483,6 +483,10 @@ config SOC_ANA_CMPR_NUM int default 1 +config SOC_ANA_CMPR_INTR_SHARE_WITH_GPIO + bool + default y + config SOC_I2C_NUM int default 2 diff --git a/components/soc/esp32h2/include/soc/ana_cmpr_struct.h b/components/soc/esp32h2/include/soc/ana_cmpr_struct.h new file mode 100644 index 000000000000..b8444fb0be10 --- /dev/null +++ b/components/soc/esp32h2/include/soc/ana_cmpr_struct.h @@ -0,0 +1,36 @@ +/** + * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/* NOTE: this file is created manually for compatibility */ + +#pragma once + +#include +#include "soc/gpio_ext_struct.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief The Analog Comparator Device struct + * @note The field in it are register pointers, which point to the physical address + * of the corresponding configuration register + * @note see 'ana_cmpr_periph.c' for the device instance + */ +typedef struct { + volatile gpio_pad_comp_config_reg_t *pad_comp_config; + volatile gpio_pad_comp_filter_reg_t *pad_comp_filter; + volatile gpio_ext_int_st_reg_t *int_st; + volatile gpio_ext_int_ena_reg_t *int_ena; + volatile gpio_ext_int_clr_reg_t *int_clr; +} analog_cmpr_dev_t; + +extern analog_cmpr_dev_t ANALOG_CMPR[1]; + +#ifdef __cplusplus +} +#endif diff --git a/components/soc/esp32h2/include/soc/gpio_ext_struct.h b/components/soc/esp32h2/include/soc/gpio_ext_struct.h index 9b211852e1ff..813ee4bbfcde 100644 --- a/components/soc/esp32h2/include/soc/gpio_ext_struct.h +++ b/components/soc/esp32h2/include/soc/gpio_ext_struct.h @@ -305,15 +305,10 @@ typedef struct { volatile gpio_ext_version_reg_t version; } gpio_ext_dev_t; -// analog comparator is a stand alone peripheral, but it is connected to GPIO -// so we rename it to analog_cmpr_dev_t from user's perspective -typedef gpio_ext_dev_t analog_cmpr_dev_t; - extern gpio_sd_dev_t SDM; extern gpio_glitch_filter_dev_t GLITCH_FILTER; extern gpio_etm_dev_t GPIO_ETM; extern gpio_ext_dev_t GPIO_EXT; -extern analog_cmpr_dev_t ANALOG_CMPR; #ifndef __cplusplus _Static_assert(sizeof(gpio_ext_dev_t) == 0x100, "Invalid size of gpio_ext_dev_t structure"); diff --git a/components/soc/esp32h2/include/soc/soc_caps.h b/components/soc/esp32h2/include/soc/soc_caps.h index e2080e6c474b..87112e06131e 100644 --- a/components/soc/esp32h2/include/soc/soc_caps.h +++ b/components/soc/esp32h2/include/soc/soc_caps.h @@ -220,7 +220,8 @@ #define SOC_DEDIC_PERIPH_ALWAYS_ENABLE (1) /*!< The dedicated GPIO (a.k.a. fast GPIO) is featured by some customized CPU instructions, which is always enabled */ /*------------------------- Analog Comparator CAPS ---------------------------*/ -#define SOC_ANA_CMPR_NUM (1U) +#define SOC_ANA_CMPR_NUM (1U) +#define SOC_ANA_CMPR_INTR_SHARE_WITH_GPIO (1) /*-------------------------- I2C CAPS ----------------------------------------*/ // ESP32-H2 has 2 I2C diff --git a/components/soc/esp32h2/ld/esp32h2.peripherals.ld b/components/soc/esp32h2/ld/esp32h2.peripherals.ld index 9b88f4b68f4a..faac00ad3c6c 100644 --- a/components/soc/esp32h2/ld/esp32h2.peripherals.ld +++ b/components/soc/esp32h2/ld/esp32h2.peripherals.ld @@ -44,7 +44,6 @@ PROVIDE ( HMAC = 0x6008D000 ); PROVIDE ( IO_MUX = 0x60090000 ); PROVIDE ( GPIO = 0x60091000 ); PROVIDE ( GPIO_EXT = 0x60091f00 ); -PROVIDE ( ANALOG_CMPR = 0x60091f00 ); PROVIDE ( SDM = 0x60091f00 ); PROVIDE ( GLITCH_FILTER = 0x60091f30 ); PROVIDE ( GPIO_ETM = 0x60091f60 ); diff --git a/components/soc/esp32p4/ana_cmpr_periph.c b/components/soc/esp32p4/ana_cmpr_periph.c new file mode 100644 index 000000000000..df1db2ef6366 --- /dev/null +++ b/components/soc/esp32p4/ana_cmpr_periph.c @@ -0,0 +1,38 @@ +/* + * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "soc/ana_cmpr_periph.h" +#include "soc/ana_cmpr_struct.h" + +const ana_cmpr_periph_t ana_cmpr_periph[SOC_ANA_CMPR_NUM] = { + [0] = { + .src_gpio = ANA_CMPR0_SRC_GPIO, + .ext_ref_gpio = ANA_CMPR0_EXT_REF_GPIO, + .intr_src = ETS_GPIO_PAD_COMP_INTR_SOURCE, + }, + [1] = { + .src_gpio = ANA_CMPR1_SRC_GPIO, + .ext_ref_gpio = ANA_CMPR1_EXT_REF_GPIO, + .intr_src = ETS_GPIO_PAD_COMP_INTR_SOURCE, + }, +}; + +analog_cmpr_dev_t ANALOG_CMPR[SOC_ANA_CMPR_NUM] = { + [0] = { + .pad_comp_config = &LP_SYS.pad_comp[0], + .pad_comp_filter = &GPIO.zero_det_filter_cnt[0], + .int_st = &GPIO.int_st, + .int_ena = &GPIO.int_ena, + .int_clr = &GPIO.int_clr, + }, + [1] = { + .pad_comp_config = &LP_SYS.pad_comp[1], + .pad_comp_filter = &GPIO.zero_det_filter_cnt[1], + .int_st = &GPIO.int_st, + .int_ena = &GPIO.int_ena, + .int_clr = &GPIO.int_clr, + }, +}; diff --git a/components/soc/esp32p4/include/soc/Kconfig.soc_caps.in b/components/soc/esp32p4/include/soc/Kconfig.soc_caps.in index 3a120de79b33..930416a4e834 100644 --- a/components/soc/esp32p4/include/soc/Kconfig.soc_caps.in +++ b/components/soc/esp32p4/include/soc/Kconfig.soc_caps.in @@ -3,6 +3,10 @@ # using gen_soc_caps_kconfig.py, do not edit manually ##################################################### +config SOC_ANA_CMPR_SUPPORTED + bool + default y + config SOC_UART_SUPPORTED bool default y @@ -383,6 +387,18 @@ config SOC_DEDIC_PERIPH_ALWAYS_ENABLE bool default y +config SOC_ANA_CMPR_NUM + int + default 2 + +config SOC_ANA_CMPR_CAN_DISTINGUISH_EDGE + bool + default y + +config SOC_ANA_CMPR_SUPPORT_ETM + bool + default y + config SOC_I2C_NUM int default 2 diff --git a/components/soc/esp32p4/include/soc/ana_cmpr_channel.h b/components/soc/esp32p4/include/soc/ana_cmpr_channel.h new file mode 100644 index 000000000000..e6430604c2a3 --- /dev/null +++ b/components/soc/esp32p4/include/soc/ana_cmpr_channel.h @@ -0,0 +1,12 @@ +/* + * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#define ANA_CMPR0_EXT_REF_GPIO 51 /*!< The GPIO that can be used as external reference voltage */ +#define ANA_CMPR0_SRC_GPIO 52 /*!< The GPIO that used for inputting the source signal to compare */ +#define ANA_CMPR1_EXT_REF_GPIO 53 /*!< The GPIO that can be used as external reference voltage */ +#define ANA_CMPR1_SRC_GPIO 54 /*!< The GPIO that used for inputting the source signal to compare */ diff --git a/components/soc/esp32p4/include/soc/ana_cmpr_struct.h b/components/soc/esp32p4/include/soc/ana_cmpr_struct.h new file mode 100644 index 000000000000..b49d31807d79 --- /dev/null +++ b/components/soc/esp32p4/include/soc/ana_cmpr_struct.h @@ -0,0 +1,37 @@ +/** + * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/* NOTE: this file is created manually for compatibility */ + +#pragma once + +#include +#include "soc/gpio_struct.h" +#include "soc/lp_system_struct.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief The Analog Comparator Device struct + * @note The field in it are register pointers, which point to the physical address + * of the corresponding configuration register + * @note see 'ana_cmpr_periph.c' for the device instance + */ +typedef struct { + volatile lp_system_reg_pad_comp_reg_t *pad_comp_config; + volatile gpio_zero_det_filter_cnt_reg_t *pad_comp_filter; + volatile gpio_int_st_reg_t *int_st; + volatile gpio_int_ena_reg_t *int_ena; + volatile gpio_int_clr_reg_t *int_clr; +} analog_cmpr_dev_t; + +extern analog_cmpr_dev_t ANALOG_CMPR[2]; + +#ifdef __cplusplus +} +#endif diff --git a/components/soc/esp32p4/include/soc/clk_tree_defs.h b/components/soc/esp32p4/include/soc/clk_tree_defs.h index 8e3e34bc9c30..0a50c5483c24 100644 --- a/components/soc/esp32p4/include/soc/clk_tree_defs.h +++ b/components/soc/esp32p4/include/soc/clk_tree_defs.h @@ -405,6 +405,22 @@ typedef enum { //////////////////////////////////////////////////GPIO Glitch Filter//////////////////////////////////////////////////// +///////////////////////////////////////////////////Analog Comparator//////////////////////////////////////////////////// + +/** + * @brief Array initializer for all supported clock sources of Analog Comparator + */ +#define SOC_ANA_CMPR_CLKS {SOC_MOD_CLK_PLL_F80M, SOC_MOD_CLK_XTAL} + +/** + * @brief Analog Comparator clock source + */ +typedef enum { + ANA_CMPR_CLK_SRC_XTAL = SOC_MOD_CLK_XTAL, /*!< Select XTAL clock as the source clock */ + ANA_CMPR_CLK_SRC_PLL_F80M = SOC_MOD_CLK_PLL_F80M, /*!< Select PLL_F80M clock as the source clock */ + ANA_CMPR_CLK_SRC_DEFAULT = SOC_MOD_CLK_PLL_F80M, /*!< Select PLL_F80M as the default clock choice */ +} soc_periph_ana_cmpr_clk_src_t; + //////////////////////////////////////////////////TWAI////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////ADC/////////////////////////////////////////////////////////////////// diff --git a/components/soc/esp32p4/include/soc/gpio_struct.h b/components/soc/esp32p4/include/soc/gpio_struct.h index 7aaade288358..052c0b9a6db6 100644 --- a/components/soc/esp32p4/include/soc/gpio_struct.h +++ b/components/soc/esp32p4/include/soc/gpio_struct.h @@ -553,31 +553,18 @@ typedef union { uint32_t val; } gpio_clock_gate_reg_t; -/** Type of zero_det0_filter_cnt register +/** Type of zero_det_filter_cnt register * GPIO analog comparator zero detect filter count */ typedef union { struct { - /** zero_det0_filter_cnt : R/W; bitpos: [31:0]; default: 4294967295; + /** zero_det_filter_cnt : R/W; bitpos: [31:0]; default: 4294967295; * GPIO analog comparator zero detect filter count */ - uint32_t zero_det0_filter_cnt:32; + uint32_t zero_det_filter_cnt:32; }; uint32_t val; -} gpio_zero_det0_filter_cnt_reg_t; - -/** Type of zero_det1_filter_cnt register - * GPIO analog comparator zero detect filter count - */ -typedef union { - struct { - /** zero_det1_filter_cnt : R/W; bitpos: [31:0]; default: 4294967295; - * GPIO analog comparator zero detect filter count - */ - uint32_t zero_det1_filter_cnt:32; - }; - uint32_t val; -} gpio_zero_det1_filter_cnt_reg_t; +} gpio_zero_det_filter_cnt_reg_t; /** Type of send_seq register * High speed sdio pad bist send sequence @@ -871,8 +858,7 @@ typedef struct gpio_dev_t { volatile gpio_int_st_reg_t int_st; volatile gpio_int_ena_reg_t int_ena; volatile gpio_int_clr_reg_t int_clr; - volatile gpio_zero_det0_filter_cnt_reg_t zero_det0_filter_cnt; - volatile gpio_zero_det1_filter_cnt_reg_t zero_det1_filter_cnt; + volatile gpio_zero_det_filter_cnt_reg_t zero_det_filter_cnt[2]; volatile gpio_send_seq_reg_t send_seq; volatile gpio_recive_seq_reg_t recive_seq; volatile gpio_bistin_sel_reg_t bistin_sel; diff --git a/components/soc/esp32p4/include/soc/lp_system_struct.h b/components/soc/esp32p4/include/soc/lp_system_struct.h index f54c249eec0e..633e5a0aaabf 100644 --- a/components/soc/esp32p4/include/soc/lp_system_struct.h +++ b/components/soc/esp32p4/include/soc/lp_system_struct.h @@ -677,44 +677,22 @@ typedef union { */ typedef union { struct { - /** dref_comp0 : R/W; bitpos: [2:0]; default: 0; + /** dref_comp : R/W; bitpos: [2:0]; default: 0; * pad comp dref */ - uint32_t dref_comp0:3; - /** mode_comp0 : R/W; bitpos: [3]; default: 0; + uint32_t dref_comp:3; + /** mode_comp : R/W; bitpos: [3]; default: 0; * pad comp mode */ - uint32_t mode_comp0:1; - /** xpd_comp0 : R/W; bitpos: [4]; default: 0; + uint32_t mode_comp:1; + /** xpd_comp : R/W; bitpos: [4]; default: 0; * pad comp xpd */ - uint32_t xpd_comp0:1; + uint32_t xpd_comp:1; uint32_t reserved_5:27; }; uint32_t val; -} lp_system_reg_pad_comp0_reg_t; - -/** Type of pad_comp1 register - * need_des - */ -typedef union { - struct { - /** dref_comp1 : R/W; bitpos: [2:0]; default: 0; - * pad comp dref - */ - uint32_t dref_comp1:3; - /** mode_comp1 : R/W; bitpos: [3]; default: 0; - * pad comp mode - */ - uint32_t mode_comp1:1; - /** xpd_comp1 : R/W; bitpos: [4]; default: 0; - * pad comp xpd - */ - uint32_t xpd_comp1:1; - uint32_t reserved_5:27; - }; - uint32_t val; -} lp_system_reg_pad_comp1_reg_t; +} lp_system_reg_pad_comp_reg_t; /** Type of backup_dma_cfg0 register * need_des @@ -1291,8 +1269,7 @@ typedef struct { volatile lp_system_reg_lp_pmu_rdn_eco_low_reg_t lp_pmu_rdn_eco_low; volatile lp_system_reg_lp_pmu_rdn_eco_high_reg_t lp_pmu_rdn_eco_high; uint32_t reserved_140[2]; - volatile lp_system_reg_pad_comp0_reg_t pad_comp0; - volatile lp_system_reg_pad_comp1_reg_t pad_comp1; + volatile lp_system_reg_pad_comp_reg_t pad_comp[2]; uint32_t reserved_150; volatile lp_system_reg_backup_dma_cfg0_reg_t backup_dma_cfg0; volatile lp_system_reg_backup_dma_cfg1_reg_t backup_dma_cfg1; @@ -1323,6 +1300,7 @@ typedef struct { volatile lp_system_reg_rng_cfg_reg_t rng_cfg; } lp_system_reg_dev_t; +extern lp_system_reg_dev_t LP_SYS; #ifndef __cplusplus _Static_assert(sizeof(lp_system_reg_dev_t) == 0x1c4, "Invalid size of lp_system_reg_dev_t structure"); diff --git a/components/soc/esp32p4/include/soc/soc_caps.h b/components/soc/esp32p4/include/soc/soc_caps.h index e4ee0d074d9e..c72c2ba214b4 100644 --- a/components/soc/esp32p4/include/soc/soc_caps.h +++ b/components/soc/esp32p4/include/soc/soc_caps.h @@ -26,7 +26,7 @@ /*-------------------------- COMMON CAPS ---------------------------------------*/ // #define SOC_ADC_SUPPORTED 1 //TODO: IDF-6496 -// #define SOC_ANA_CMPR_SUPPORTED 1 //TODO: IDF-7479 +#define SOC_ANA_CMPR_SUPPORTED 1 // #define SOC_DEDICATED_GPIO_SUPPORTED 1 //TODO: IDF-7552 #define SOC_UART_SUPPORTED 1 #define SOC_GDMA_SUPPORTED 1 @@ -220,6 +220,11 @@ #define SOC_DEDIC_GPIO_IN_CHANNELS_NUM (8) /*!< 8 inward channels on each CPU core */ #define SOC_DEDIC_PERIPH_ALWAYS_ENABLE (1) /*!< The dedicated GPIO (a.k.a. fast GPIO) is featured by some customized CPU instructions, which is always enabled */ +/*------------------------- Analog Comparator CAPS ---------------------------*/ +#define SOC_ANA_CMPR_NUM (2) +#define SOC_ANA_CMPR_CAN_DISTINGUISH_EDGE (1) // Support positive/negative/any cross interrupt +#define SOC_ANA_CMPR_SUPPORT_ETM (1) + /*-------------------------- I2C CAPS ----------------------------------------*/ // ESP32-P4 has 2 I2Cs #define SOC_I2C_NUM (2U) diff --git a/components/soc/esp32p4/ld/esp32p4.peripherals.ld b/components/soc/esp32p4/ld/esp32p4.peripherals.ld index 4085a915e41e..f4223a2f01ce 100644 --- a/components/soc/esp32p4/ld/esp32p4.peripherals.ld +++ b/components/soc/esp32p4/ld/esp32p4.peripherals.ld @@ -65,6 +65,7 @@ PROVIDE ( TEE = 0x60098000 ); /* TODO: IDF-7542 */ PROVIDE ( HP_APM = 0x60099000 ); /* TODO: IDF-7542 */ PROVIDE ( PMU = 0x50115000 ); +PROVIDE ( LP_SYS = 0x50110000 ); PROVIDE ( LP_AON_CLKRST = 0x50111000 ); PROVIDE ( EFUSE = 0x5012D000 ); PROVIDE ( LP_TIMER = 0x50112000 ); diff --git a/components/soc/include/soc/ana_cmpr_periph.h b/components/soc/include/soc/ana_cmpr_periph.h index 9418b15578eb..ce4738fcd672 100644 --- a/components/soc/include/soc/ana_cmpr_periph.h +++ b/components/soc/include/soc/ana_cmpr_periph.h @@ -8,6 +8,7 @@ #include #include "soc/soc_caps.h" +#include "soc/interrupts.h" #if SOC_ANA_CMPR_SUPPORTED #include "soc/ana_cmpr_channel.h" #endif @@ -20,9 +21,10 @@ extern "C" { typedef struct { int src_gpio; int ext_ref_gpio; -} ana_cmpr_conn_t; + int intr_src; +} ana_cmpr_periph_t; -extern const ana_cmpr_conn_t ana_cmpr_io_map[SOC_ANA_CMPR_NUM]; +extern const ana_cmpr_periph_t ana_cmpr_periph[SOC_ANA_CMPR_NUM]; #endif #ifdef __cplusplus diff --git a/docs/doxygen/Doxyfile b/docs/doxygen/Doxyfile index 777ffd4d7b96..60441c822fdf 100644 --- a/docs/doxygen/Doxyfile +++ b/docs/doxygen/Doxyfile @@ -73,6 +73,7 @@ INPUT = \ $(PROJECT_PATH)/components/bt/host/nimble/esp-hci/include/esp_nimble_hci.h \ $(PROJECT_PATH)/components/console/esp_console.h \ $(PROJECT_PATH)/components/driver/analog_comparator/include/driver/ana_cmpr.h \ + $(PROJECT_PATH)/components/driver/analog_comparator/include/driver/ana_cmpr_etm.h \ $(PROJECT_PATH)/components/driver/analog_comparator/include/driver/ana_cmpr_types.h \ $(PROJECT_PATH)/components/driver/dac/include/driver/dac_continuous.h \ $(PROJECT_PATH)/components/driver/dac/include/driver/dac_cosine.h \ diff --git a/docs/en/api-reference/peripherals/ana_cmpr.rst b/docs/en/api-reference/peripherals/ana_cmpr.rst index 0593dd11f722..e9d3809649c6 100644 --- a/docs/en/api-reference/peripherals/ana_cmpr.rst +++ b/docs/en/api-reference/peripherals/ana_cmpr.rst @@ -1,9 +1,10 @@ Analog Comparator ================= -{IDF_TARGET_ANA_CMPR_NUM: default="NOT UPDATED", esp32h2="one"} -{IDF_TARGET_ANA_CMPR_SRC_CHAN: default="NOT UPDATED", esp32h2="GPIO11"} -{IDF_TARGET_ANA_CMPR_EXT_REF_CHAN: default="NOT UPDATED", esp32h2="GPIO10"} +{IDF_TARGET_ANA_CMPR_SRC_CHAN0: default="NOT UPDATED", esp32h2="GPIO11", esp32p4="GPIO52"} +{IDF_TARGET_ANA_CMPR_EXT_REF_CHAN0: default="NOT UPDATED", esp32h2="GPIO10", esp32p4="GPIO51"} +{IDF_TARGET_ANA_CMPR_SRC_CHAN1: default="NOT UPDATED", esp32p4="GPIO54"} +{IDF_TARGET_ANA_CMPR_EXT_REF_CHAN1: default="NOT UPDATED", esp32p4="GPIO53"} Introduction ------------ @@ -12,13 +13,21 @@ Analog Comparator is a peripheral that can be used to compare a source signal wi It is a cost effective way to replace an amplifier comparator in some scenarios. But unlike the continuous comparing of the amplifier comparator, ESP Analog Comparator is driven by a source clock, which decides the sampling frequency. -Analog Comparator on {IDF_TARGET_NAME} has {IDF_TARGET_ANA_CMPR_NUM} unit(s), the channels in the unit(s) are: +Analog Comparator on {IDF_TARGET_NAME} has {IDF_TARGET_SOC_ANA_CMPR_NUM} unit(s), the channels in the unit(s) are: **UNIT0** -- Source Channel: {IDF_TARGET_ANA_CMPR_SRC_CHAN} -- External Reference Channel: {IDF_TARGET_ANA_CMPR_EXT_REF_CHAN} -- Internal Reference Channel: Range 0% ~ 70% VDD, step 10% VDD +- Source Channel: {IDF_TARGET_ANA_CMPR_SRC_CHAN0} +- External Reference Channel: {IDF_TARGET_ANA_CMPR_EXT_REF_CHAN0} +- Internal Reference Channel: Range 0% ~ 70% of the VDD, the step is 10% of the VDD + +.. only:: esp32p4 + + **UNIT1** + + - Source Channel: {IDF_TARGET_ANA_CMPR_SRC_CHAN1} + - External Reference Channel: {IDF_TARGET_ANA_CMPR_EXT_REF_CHAN1} + - Internal Reference Channel: Range 0% ~ 70% of the VDD, the step is 10% of the VDD Functional Overview ------------------- @@ -33,6 +42,10 @@ The following sections of this document cover the typical steps to install and o - `Thread Safety <#thread-safety>`__ - lists which APIs are guaranteed to be thread safe by the driver. - `Kconfig Options <#kconfig-options>`__ - lists the supported Kconfig options that can be used to make a different effect on driver behavior. +.. only:: SOC_ANA_CMPR_SUPPORT_ETM + + - `ETM Events <#etm-events>`__ - + Resource Allocation ^^^^^^^^^^^^^^^^^^^ @@ -42,7 +55,7 @@ To allocate the resource of the Analog Comparator unit, :cpp:func:`ana_cmpr_new_ - :cpp:member:`ana_cmpr_config_t::unit` selects the Analog Comparator unit. - :cpp:member:`ana_cmpr_config_t::clk_src` selects the source clock for Analog Comparator, it can affect the sampling frequency. Note that the clock source of the Analog Comparator comes from the io mux, it is shared with GPIO extension peripherals like SDM (Sigma-Delta Modulation) and Glitch Filter. The configuration will fail if you specific different clock sources for multiple GPIO extension peripherals. The default clock sources of these peripherals are same, typically, we select :cpp:enumerator:`soc_periph_ana_cmpr_clk_src_t::ANA_CMPR_CLK_SRC_DEFAULT` as the clock source. -- :cpp:member:`ana_cmpr_config_t::ref_src` selects the reference source from internal voltage or external signal (from {IDF_TARGET_ANA_CMPR_EXT_REF_CHAN}). +- :cpp:member:`ana_cmpr_config_t::ref_src` selects the reference source from internal voltage or external signal. - :cpp:member:`ana_cmpr_config_t::cross_type` selects which kind of cross type can trigger the interrupt. The function :cpp:func:`ana_cmpr_new_unit` can fail due to various errors such as insufficient memory, invalid arguments, etc. If a previously created Analog Comparator unit is no longer required, you should recycle it by calling :cpp:func:`ana_cmpr_del_unit`. It allows the underlying HW channel to be used for other purposes. Before deleting an Analog Comparator unit handle, you should disable it by :cpp:func:`ana_cmpr_unit_disable` in advance, or make sure it has not enabled yet by :cpp:func:`ana_cmpr_unit_enable`. @@ -53,7 +66,7 @@ The function :cpp:func:`ana_cmpr_new_unit` can fail due to various errors such a ana_cmpr_handle_t cmpr = NULL; ana_cmpr_config_t config = { - .unit = ANA_CMPR_UNIT_0, + .unit = 0, .clk_src = ANA_CMPR_CLK_SRC_DEFAULT, .ref_src = ANA_CMPR_REF_SRC_INTERNAL, .cross_type = ANA_CMPR_CROSS_ANY, @@ -180,6 +193,13 @@ Kconfig Options - :ref:`CONFIG_ANA_CMPR_CTRL_FUNC_IN_IRAM` controls where to place the Analog Comparator control functions (IRAM or Flash), see `IRAM Safe <#iram-safe>`__ for more information. - :ref:`CONFIG_ANA_CMPR_ENABLE_DEBUG_LOG` is used to enabled the debug log output. Enabling this option increases the firmware binary size. +.. only:: SOC_ANA_CMPR_SUPPORT_ETM + + ETM Events + ^^^^^^^^^^ + + To create an analog comparator cross event, you need to include ``driver/ana_cmpr_etm.h`` additionally, and allocate the event by :cpp:func:`ana_cmpr_new_etm_event`. You can refer to :doc:`ETM ` for how to connect an event to a task. + Application Example ------------------- diff --git a/docs/en/api-reference/peripherals/etm.rst b/docs/en/api-reference/peripherals/etm.rst index e8dbcefd8b11..bca698df5dfc 100644 --- a/docs/en/api-reference/peripherals/etm.rst +++ b/docs/en/api-reference/peripherals/etm.rst @@ -70,6 +70,7 @@ Other Peripheral Events :SOC_TIMER_SUPPORT_ETM: - Refer to :doc:`/api-reference/peripherals/gptimer` for how to get the ETM event handle from GPTimer. :SOC_GDMA_SUPPORT_ETM: - Refer to :doc:`/api-reference/system/async_memcpy` for how to get the ETM event handle from async memcpy. :SOC_MCPWM_SUPPORT_ETM: - Refer to :doc:`/api-reference/peripherals/mcpwm` for how to get the ETM event handle from MCPWM. + :SOC_ANA_CMPR_SUPPORT_ETM: - Refer to :doc:`/api-reference/peripherals/ana_cmpr` for how to get the ETM event handle from analog comparator. .. _etm-task: diff --git a/docs/zh_CN/api-reference/peripherals/etm.rst b/docs/zh_CN/api-reference/peripherals/etm.rst index 37c036972dac..8d56935aec34 100644 --- a/docs/zh_CN/api-reference/peripherals/etm.rst +++ b/docs/zh_CN/api-reference/peripherals/etm.rst @@ -70,6 +70,7 @@ GPIO **边沿** 事件是最常见的事件类型,任何 GPIO 管脚均可触 :SOC_TIMER_SUPPORT_ETM: - 要了解如何从 GPTimer 获取 ETM 事件句柄,请参阅 :doc:`/api-reference/peripherals/gptimer`。 :SOC_GDMA_SUPPORT_ETM: - 要了解如何从 async memcpy 获取 ETM 事件句柄,请参阅 :doc:`/api-reference/system/async_memcpy`。 :SOC_MCPWM_SUPPORT_ETM: - 要了解如何从 MCPWM 中获取 ETM 事件句柄,请参阅 :doc:`/api-reference/peripherals/mcpwm`。 + :SOC_ANA_CMPR_SUPPORT_ETM: - 要了解如何从模拟比较器获取 ETM 事件句柄,请参阅 :doc:`/api-reference/peripherals/ana_cmpr`。 .. _etm-task: diff --git a/examples/peripherals/.build-test-rules.yml b/examples/peripherals/.build-test-rules.yml index 02974dcc02ec..c3055b0a6051 100644 --- a/examples/peripherals/.build-test-rules.yml +++ b/examples/peripherals/.build-test-rules.yml @@ -18,6 +18,10 @@ examples/peripherals/adc/oneshot_read: examples/peripherals/analog_comparator: disable: - if: SOC_ANA_CMPR_SUPPORTED != 1 + disable_test: + - if: IDF_TARGET == "esp32p4" + temporary: true + reason: not supported yet examples/peripherals/dac: disable: diff --git a/examples/peripherals/analog_comparator/README.md b/examples/peripherals/analog_comparator/README.md index 8a6adb523c84..330b1dcc0e30 100644 --- a/examples/peripherals/analog_comparator/README.md +++ b/examples/peripherals/analog_comparator/README.md @@ -1,11 +1,17 @@ -| Supported Targets | ESP32-H2 | -| ----------------- | -------- | +| Supported Targets | ESP32-H2 | ESP32-P4 | +| ----------------- | -------- | -------- | # Analog Comparator Example (See the README.md file in the upper level 'examples' directory for more information about examples.) -This example is going to show how to use the Analog Comparator. +This example is going to show how to use the Analog Comparator. The example toggles a GPIO to monitor the positive and negative crosses on the analog comparator unit. + +## Realization + +- If the target supports generating the analog comparator events (like ESP32-P4), the example can toggle the monitoring GPIO via Event Task Matrix (ETM). ETM can bind the analog comparator cross events (positive and negative events) with the GPIO tasks (to set or clear a GPIO), so that every event happens, hardware will execute the corresponding task without CPU involved. + +- If the target does not support to generate the analog comparator events (like ESP32-H2). The example will register an event callback on the analog comparator, and toggle the GPIO in the callback. It requires the CPU to process every event ISR, thus it is not effective as ETM and can't achieve a high resolution. ## How to Use Example diff --git a/examples/peripherals/analog_comparator/main/CMakeLists.txt b/examples/peripherals/analog_comparator/main/CMakeLists.txt index e017eb488976..07070ae02048 100644 --- a/examples/peripherals/analog_comparator/main/CMakeLists.txt +++ b/examples/peripherals/analog_comparator/main/CMakeLists.txt @@ -1,2 +1,10 @@ -idf_component_register(SRCS "ana_cmpr_example_main.c" +set(src "ana_cmpr_example_main.c") + +if(CONFIG_EXAMPLE_USE_ETM) + list(APPEND src "ana_cmpr_example_etm.c") +else() + list(APPEND src "ana_cmpr_example_intr.c") +endif() + +idf_component_register(SRCS ${src} INCLUDE_DIRS ".") diff --git a/examples/peripherals/analog_comparator/main/Kconfig.projbuild b/examples/peripherals/analog_comparator/main/Kconfig.projbuild index 1e2f780b3708..21bd9d1998f7 100644 --- a/examples/peripherals/analog_comparator/main/Kconfig.projbuild +++ b/examples/peripherals/analog_comparator/main/Kconfig.projbuild @@ -1,5 +1,20 @@ menu "Analog Comparator Example Configuration" + choice EXAMPLE_REALIZATION + prompt "Analog Comparator example realization methods" + default EXAMPLE_USE_ETM if SOC_ANA_CMPR_SUPPORT_ETM && SOC_GPIO_SUPPORT_ETM + default EXAMPLE_USE_INTR if !SOC_ANA_CMPR_SUPPORT_ETM || !SOC_GPIO_SUPPORT_ETM + config EXAMPLE_USE_INTR + bool "Use Interrupt" + help + Enable to set the monitor GPIO via interrupt callback + config EXAMPLE_USE_ETM + depends on SOC_ANA_CMPR_SUPPORT_ETM && SOC_GPIO_SUPPORT_ETM + bool "Use ETM" + help + Enable to set the monitor GPIO via Event Task Matrix + endchoice + choice EXAMPLE_REFERENCE_SOURCE prompt "Analog Comparator reference source" default EXAMPLE_INTERNAL_REF @@ -18,7 +33,7 @@ menu "Analog Comparator Example Configuration" endchoice config EXAMPLE_HYSTERESIS_COMPARATOR - depends on EXAMPLE_INTERNAL_REF + depends on EXAMPLE_INTERNAL_REF && !EXAMPLE_USE_ETM bool "Enable hysteresis comparator" default n help diff --git a/examples/peripherals/analog_comparator/main/ana_cmpr_example_etm.c b/examples/peripherals/analog_comparator/main/ana_cmpr_example_etm.c new file mode 100644 index 000000000000..a8dbfce5b109 --- /dev/null +++ b/examples/peripherals/analog_comparator/main/ana_cmpr_example_etm.c @@ -0,0 +1,133 @@ +/* + * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Unlicense OR CC0-1.0 + */ + +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "driver/gpio.h" +#include "driver/ana_cmpr.h" +#include "driver/ana_cmpr_etm.h" +#include "driver/gpio_etm.h" +#include "esp_etm.h" +#include "esp_log.h" +#include "ana_cmpr_example_main.h" + +static const char *TAG = "ana_cmpr_example"; + +static void example_etm_bind_ana_cmpr_event_with_gpio_task(ana_cmpr_handle_t cmpr) +{ + /* Allocate the analog comparator positive & negative cross events */ + esp_etm_event_handle_t cmpr_pos_evt = NULL; + esp_etm_event_handle_t cmpr_neg_evt = NULL; + ana_cmpr_etm_event_config_t evt_cfg = { + .event_type = ANA_CMPR_EVENT_POS_CROSS, + }; + ESP_ERROR_CHECK(ana_cmpr_new_etm_event(cmpr, &evt_cfg, &cmpr_pos_evt)); + evt_cfg.event_type = GPIO_ETM_EVENT_EDGE_NEG; + ESP_ERROR_CHECK(ana_cmpr_new_etm_event(cmpr, &evt_cfg, &cmpr_neg_evt)); + + /* Allocate the GPIO set & clear tasks */ + esp_etm_task_handle_t gpio_pos_task = NULL; + esp_etm_task_handle_t gpio_neg_task = NULL; + gpio_etm_task_config_t task_cfg = { + .action = GPIO_ETM_TASK_ACTION_SET, + }; + ESP_ERROR_CHECK(gpio_new_etm_task(&task_cfg, &gpio_pos_task)); + task_cfg.action = GPIO_ETM_TASK_ACTION_CLR; + ESP_ERROR_CHECK(gpio_new_etm_task(&task_cfg, &gpio_neg_task)); + /* Add task to the monitor GPIO */ + ESP_ERROR_CHECK(gpio_etm_task_add_gpio(gpio_pos_task, EXAMPLE_MONITOR_GPIO_NUM)); + ESP_ERROR_CHECK(gpio_etm_task_add_gpio(gpio_neg_task, EXAMPLE_MONITOR_GPIO_NUM)); + + /* Allocate the Event Task Matrix channels */ + esp_etm_channel_handle_t etm_pos_handle; + esp_etm_channel_handle_t etm_neg_handle; + esp_etm_channel_config_t etm_cfg = {}; + ESP_ERROR_CHECK(esp_etm_new_channel(&etm_cfg, &etm_pos_handle)); + ESP_ERROR_CHECK(esp_etm_new_channel(&etm_cfg, &etm_neg_handle)); + /* Bind the events and tasks */ + ESP_ERROR_CHECK(esp_etm_channel_connect(etm_pos_handle, cmpr_pos_evt, gpio_pos_task)); + ESP_ERROR_CHECK(esp_etm_channel_connect(etm_neg_handle, cmpr_neg_evt, gpio_neg_task)); + /* Enable the ETM channels */ + ESP_ERROR_CHECK(esp_etm_channel_enable(etm_pos_handle)); + ESP_ERROR_CHECK(esp_etm_channel_enable(etm_neg_handle)); +} + +ana_cmpr_handle_t example_init_analog_comparator_etm(void) +{ + /* Step 0: Show the source channel and reference channel GPIO */ + int src_gpio = -1; + int ext_ref_gpio = -1; + ESP_ERROR_CHECK(ana_cmpr_get_gpio(EXAMPLE_ANA_CMPR_UNIT, ANA_CMPR_SOURCE_CHAN, &src_gpio)); + ESP_ERROR_CHECK(ana_cmpr_get_gpio(EXAMPLE_ANA_CMPR_UNIT, ANA_CMPR_EXT_REF_CHAN, &ext_ref_gpio)); + ESP_LOGI(TAG, "Analog Comparator source gpio %d, external reference gpio %d", src_gpio, ext_ref_gpio); + + ana_cmpr_handle_t cmpr = NULL; + +#if CONFIG_EXAMPLE_INTERNAL_REF + /* Step 1: Allocate the new analog comparator unit */ + ana_cmpr_config_t config = { + .unit = EXAMPLE_ANA_CMPR_UNIT, + .clk_src = ANA_CMPR_CLK_SRC_DEFAULT, + .ref_src = ANA_CMPR_REF_SRC_INTERNAL, + .cross_type = ANA_CMPR_CROSS_ANY, + }; + ESP_ERROR_CHECK(ana_cmpr_new_unit(&config, &cmpr)); + ESP_LOGI(TAG, "Allocate Analog Comparator with internal reference"); + + /* Step 1.1: As we are using the internal reference source, we need to configure the internal reference */ + ana_cmpr_internal_ref_config_t ref_cfg = { + .ref_volt = ANA_CMPR_REF_VOLT_50_PCT_VDD, + }; + ESP_ERROR_CHECK(ana_cmpr_set_internal_reference(cmpr, &ref_cfg)); +#else + /* Step 1: Allocate the new analog comparator unit */ + ana_cmpr_config_t config = { + .unit = EXAMPLE_ANA_CMPR_UNIT, + .clk_src = ANA_CMPR_CLK_SRC_DEFAULT, + .ref_src = ANA_CMPR_REF_SRC_EXTERNAL, + .cross_type = ANA_CMPR_CROSS_ANY, + }; + ESP_ERROR_CHECK(ana_cmpr_new_unit(&config, &cmpr)); + ESP_LOGI(TAG, "Allocate Analog Comparator with external reference"); +#endif // CONFIG_EXAMPLE_INTERNAL_REF + + /* Step 2: (Optional) Set the debounce configuration + * It's an optional configuration, if the wait time is set in debounce configuration, + * the cross interrupt will be disabled temporary after it is triggered, and it will be enabled + * automatically enabled after `wait_us`, so that the duplicate interrupts + * can be suppressed while the source signal crossing the reference signal. */ + ana_cmpr_debounce_config_t dbc_cfg = { + /* Normally the `wait_us` is related to the relative frequency between the source and reference signal + * comparing to another one. This example adopts an approximate frequency as the relative signal + * frequency, and set the default wait time to EXAMPLE_WAIT_TIME_PROP of the relative signal period. + * We need to estimate an appropriate `freq_approx` and EXAMPLE_WAIT_TIME_PROP + * to make sure the interrupt neither triggers duplicate interrupts, nor misses the next crossing interrupt. + * Here we take 1 KHz for example */ + .wait_us = EXAMPLE_WAITE_TIME_US(1000), + }; + ESP_ERROR_CHECK(ana_cmpr_set_debounce(cmpr, &dbc_cfg)); + + /* Step 3: Enable the analog comparator unit */ + ESP_ERROR_CHECK(ana_cmpr_enable(cmpr)); + +#if CONFIG_EXAMPLE_INTERNAL_REF + ESP_LOGI(TAG, "Analog comparator enabled, reference voltage: %d%% * VDD", (int)ref_cfg.ref_volt * 10); +#else + ESP_LOGI(TAG, "Analog comparator enabled, external reference selected"); +#endif + return cmpr; +} + +void example_analog_comparator_etm_app(void) +{ + /* Initialize GPIO to monitor the comparator interrupt */ + example_init_monitor_gpio(); + /* Initialize Analog Comparator */ + ana_cmpr_handle_t cmpr = example_init_analog_comparator_etm(); + /* Connect the analog comparator events and gpio tasks via ETM channels */ + example_etm_bind_ana_cmpr_event_with_gpio_task(cmpr); +} diff --git a/examples/peripherals/analog_comparator/main/ana_cmpr_example_intr.c b/examples/peripherals/analog_comparator/main/ana_cmpr_example_intr.c new file mode 100644 index 000000000000..78fec3e9bc37 --- /dev/null +++ b/examples/peripherals/analog_comparator/main/ana_cmpr_example_intr.c @@ -0,0 +1,122 @@ +/* + * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Unlicense OR CC0-1.0 + */ + +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "driver/gpio.h" +#include "driver/ana_cmpr.h" +#include "esp_log.h" +#include "ana_cmpr_example_main.h" + +static const char *TAG = "ana_cmpr_example"; + +static bool example_ana_cmpr_on_cross_callback(ana_cmpr_handle_t cmpr, + const ana_cmpr_cross_event_data_t *edata, + void *user_ctx) +{ +#if CONFIG_EXAMPLE_HYSTERESIS_COMPARATOR + static ana_cmpr_internal_ref_config_t ref_cfg = { + .ref_volt = ANA_CMPR_REF_VOLT_70_PCT_VDD, + }; + bool is_70p = ref_cfg.ref_volt == ANA_CMPR_REF_VOLT_70_PCT_VDD; + /* Toggle the GPIO, monitor the gpio on an oscilloscope. */ + gpio_set_level(EXAMPLE_MONITOR_GPIO_NUM, is_70p); + /* Set the internal reference voltage to 30% VDD and 70 %VDD alternately */ + ana_cmpr_set_internal_reference(cmpr, &ref_cfg); + ref_cfg.ref_volt = is_70p ? ANA_CMPR_REF_VOLT_30_PCT_VDD : ANA_CMPR_REF_VOLT_70_PCT_VDD; +#else + static int lvl = 0; + /* Toggle the GPIO, monitor the gpio on a oscilloscope. */ + gpio_set_level(EXAMPLE_MONITOR_GPIO_NUM, lvl); + lvl = !lvl; +#endif + return false; +} + +void example_init_analog_comparator_intr(void) +{ + /* Step 0: Show the source channel and reference channel GPIO */ + int src_gpio = -1; + int ext_ref_gpio = -1; + ESP_ERROR_CHECK(ana_cmpr_get_gpio(EXAMPLE_ANA_CMPR_UNIT, ANA_CMPR_SOURCE_CHAN, &src_gpio)); + ESP_ERROR_CHECK(ana_cmpr_get_gpio(EXAMPLE_ANA_CMPR_UNIT, ANA_CMPR_EXT_REF_CHAN, &ext_ref_gpio)); + ESP_LOGI(TAG, "Analog Comparator source gpio %d, external reference gpio %d", src_gpio, ext_ref_gpio); + + ana_cmpr_handle_t cmpr = NULL; + +#if CONFIG_EXAMPLE_INTERNAL_REF + /* Step 1: Allocate the new analog comparator unit */ + ana_cmpr_config_t config = { + .unit = EXAMPLE_ANA_CMPR_UNIT, + .clk_src = ANA_CMPR_CLK_SRC_DEFAULT, + .ref_src = ANA_CMPR_REF_SRC_INTERNAL, + .cross_type = ANA_CMPR_CROSS_ANY, + }; + ESP_ERROR_CHECK(ana_cmpr_new_unit(&config, &cmpr)); + ESP_LOGI(TAG, "Allocate Analog Comparator with internal reference"); + + /* Step 1.1: As we are using the internal reference source, we need to configure the internal reference */ + ana_cmpr_internal_ref_config_t ref_cfg = { +#if CONFIG_EXAMPLE_HYSTERESIS_COMPARATOR + /* Set the initial internal reference voltage to 70% VDD, it will be updated in the callback every time the interrupt triggered */ + .ref_volt = ANA_CMPR_REF_VOLT_70_PCT_VDD +#else + .ref_volt = ANA_CMPR_REF_VOLT_50_PCT_VDD, +#endif + }; + ESP_ERROR_CHECK(ana_cmpr_set_internal_reference(cmpr, &ref_cfg)); +#else + /* Step 1: Allocate the new analog comparator unit */ + ana_cmpr_config_t config = { + .unit = EXAMPLE_ANA_CMPR_UNIT, + .clk_src = ANA_CMPR_CLK_SRC_DEFAULT, + .ref_src = ANA_CMPR_REF_SRC_EXTERNAL, + .cross_type = ANA_CMPR_CROSS_ANY, + }; + ESP_ERROR_CHECK(ana_cmpr_new_unit(&config, &cmpr)); + ESP_LOGI(TAG, "Allocate Analog Comparator with external reference"); +#endif + + /* Step 2: (Optional) Set the debounce configuration + * It's an optional configuration, if the wait time is set in debounce configuration, + * the cross interrupt will be disabled temporary after it is triggered, and it will be enabled + * automatically enabled after `wait_us`, so that the duplicate interrupts + * can be suppressed while the source signal crossing the reference signal. */ + ana_cmpr_debounce_config_t dbc_cfg = { + /* Normally the `wait_us` is related to the relative frequency between the source and reference signal + * comparing to another one. This example adopts an approximate frequency as the relative signal + * frequency, and set the default wait time to EXAMPLE_WAIT_TIME_PROP of the relative signal period. + * We need to estimate an appropriate `freq_approx` and EXAMPLE_WAIT_TIME_PROP + * to make sure the interrupt neither triggers duplicate interrupts, nor misses the next crossing interrupt. + * Here we take 1 KHz for example */ + .wait_us = EXAMPLE_WAITE_TIME_US(1000), + }; + ESP_ERROR_CHECK(ana_cmpr_set_debounce(cmpr, &dbc_cfg)); + + /* Step 3: Register the event callbacks */ + ana_cmpr_event_callbacks_t cbs = { + .on_cross = example_ana_cmpr_on_cross_callback, + }; + ESP_ERROR_CHECK(ana_cmpr_register_event_callbacks(cmpr, &cbs, NULL)); + + /* Step 4: Enable the analog comparator unit */ + ESP_ERROR_CHECK(ana_cmpr_enable(cmpr)); + +#if CONFIG_EXAMPLE_INTERNAL_REF + ESP_LOGI(TAG, "Analog comparator enabled, reference voltage: %d%% * VDD", (int)ref_cfg.ref_volt * 10); +#else + ESP_LOGI(TAG, "Analog comparator enabled, external reference selected"); +#endif +} + +void example_analog_comparator_intr_app(void) +{ + /* Initialize GPIO to monitor the comparator interrupt */ + example_init_monitor_gpio(); + /* Initialize Analog Comparator */ + example_init_analog_comparator_intr(); +} diff --git a/examples/peripherals/analog_comparator/main/ana_cmpr_example_main.c b/examples/peripherals/analog_comparator/main/ana_cmpr_example_main.c index dff400372460..49b7d6ca84a2 100644 --- a/examples/peripherals/analog_comparator/main/ana_cmpr_example_main.c +++ b/examples/peripherals/analog_comparator/main/ana_cmpr_example_main.c @@ -5,118 +5,8 @@ */ #include -#include "freertos/FreeRTOS.h" -#include "freertos/task.h" #include "driver/gpio.h" -#include "driver/ana_cmpr.h" -#include "esp_log.h" - -#define EXAMPLE_ANA_CMPR_UNIT ANA_CMPR_UNIT_0 // Analog Comparator unit -#define EXAMPLE_WAIT_TIME_PROP (0.1) // The wait time proportion in one relative signal period -#define EXAMPLE_WAITE_TIME_US(freq_approx) (uint32_t)(1000000 * EXAMPLE_WAIT_TIME_PROP / (freq_approx)) - -#define EXAMPLE_MONITOR_GPIO_NUM (0) // The gpio to monitor the on cross callback - -static const char *TAG = "ana_cmpr_example"; - -static bool example_ana_cmpr_on_cross_callback(ana_cmpr_handle_t cmpr, - const ana_cmpr_cross_event_data_t *edata, - void *user_ctx) -{ -#if CONFIG_EXAMPLE_HYSTERESIS_COMPARATOR - static ana_cmpr_internal_ref_config_t ref_cfg = { - .ref_volt = ANA_CMPR_REF_VOLT_70_PCT_VDD, - }; - bool is_70p = ref_cfg.ref_volt == ANA_CMPR_REF_VOLT_70_PCT_VDD; - /* Toggle the GPIO, monitor the gpio on an oscilloscope. */ - gpio_set_level(EXAMPLE_MONITOR_GPIO_NUM, is_70p); - /* Set the internal reference voltage to 30% VDD and 70 %VDD alternately */ - ana_cmpr_set_internal_reference(cmpr, &ref_cfg); - ref_cfg.ref_volt = is_70p ? ANA_CMPR_REF_VOLT_30_PCT_VDD : ANA_CMPR_REF_VOLT_70_PCT_VDD; -#else - static int lvl = 0; - /* Toggle the GPIO, monitor the gpio on a oscilloscope. */ - gpio_set_level(EXAMPLE_MONITOR_GPIO_NUM, lvl); - lvl = !lvl; -#endif - return false; -} - -void example_init_analog_comparator(void) -{ - /* Step 0: Show the source channel and reference channel GPIO */ - int src_gpio = -1; - int ext_ref_gpio = -1; - ESP_ERROR_CHECK(ana_cmpr_get_gpio(EXAMPLE_ANA_CMPR_UNIT, ANA_CMPR_SOURCE_CHAN, &src_gpio)); - ESP_ERROR_CHECK(ana_cmpr_get_gpio(EXAMPLE_ANA_CMPR_UNIT, ANA_CMPR_EXT_REF_CHAN, &ext_ref_gpio)); - ESP_LOGI(TAG, "Analog Comparator source gpio %d, external reference gpio %d", src_gpio, ext_ref_gpio); - - ana_cmpr_handle_t cmpr = NULL; - -#if CONFIG_EXAMPLE_INTERNAL_REF - /* Step 1: Allocate the new analog comparator unit */ - ana_cmpr_config_t config = { - .unit = EXAMPLE_ANA_CMPR_UNIT, - .clk_src = ANA_CMPR_CLK_SRC_DEFAULT, - .ref_src = ANA_CMPR_REF_SRC_INTERNAL, - .cross_type = ANA_CMPR_CROSS_ANY, - }; - ESP_ERROR_CHECK(ana_cmpr_new_unit(&config, &cmpr)); - ESP_LOGI(TAG, "Allocate Analog Comparator with internal reference"); - - /* Step 1.1: As we are using the internal reference source, we need to configure the internal reference */ - ana_cmpr_internal_ref_config_t ref_cfg = { -#if CONFIG_EXAMPLE_HYSTERESIS_COMPARATOR - /* Set the initial internal reference voltage to 70% VDD, it will be updated in the callback every time the interrupt triggered */ - .ref_volt = ANA_CMPR_REF_VOLT_70_PCT_VDD -#else - .ref_volt = ANA_CMPR_REF_VOLT_50_PCT_VDD, -#endif - }; - ESP_ERROR_CHECK(ana_cmpr_set_internal_reference(cmpr, &ref_cfg)); -#else - /* Step 1: Allocate the new analog comparator unit */ - ana_cmpr_config_t config = { - .unit = EXAMPLE_ANA_CMPR_UNIT, - .clk_src = ANA_CMPR_CLK_SRC_DEFAULT, - .ref_src = ANA_CMPR_REF_SRC_EXTERNAL, - .cross_type = ANA_CMPR_CROSS_ANY, - }; - ESP_ERROR_CHECK(ana_cmpr_new_unit(&config, &cmpr)); - ESP_LOGI(TAG, "Allocate Analog Comparator with external reference"); -#endif - - /* Step 2: (Optional) Set the debounce configuration - * It's an optional configuration, if the wait time is set in debounce configuration, - * the cross interrupt will be disabled temporary after it is triggered, and it will be enabled - * automatically enabled after `wait_us`, so that the duplicate interrupts - * can be suppressed while the source signal crossing the reference signal. */ - ana_cmpr_debounce_config_t dbc_cfg = { - /* Normally the `wait_us` is related to the relative frequency between the source and reference signal - * comparing to another one. This example adopts an approximate frequency as the relative signal - * frequency, and set the default wait time to EXAMPLE_WAIT_TIME_PROP of the relative signal period. - * We need to estimate an appropriate `freq_approx` and EXAMPLE_WAIT_TIME_PROP - * to make sure the interrupt neither triggers duplicate interrupts, nor misses the next crossing interrupt. - * Here we take 1 KHz for example */ - .wait_us = EXAMPLE_WAITE_TIME_US(1000), - }; - ESP_ERROR_CHECK(ana_cmpr_set_debounce(cmpr, &dbc_cfg)); - - /* Step 3: Register the event callbacks */ - ana_cmpr_event_callbacks_t cbs = { - .on_cross = example_ana_cmpr_on_cross_callback, - }; - ESP_ERROR_CHECK(ana_cmpr_register_event_callbacks(cmpr, &cbs, NULL)); - - /* Step 4: Enable the analog comparator unit */ - ESP_ERROR_CHECK(ana_cmpr_enable(cmpr)); - -#if CONFIG_EXAMPLE_INTERNAL_REF - ESP_LOGI(TAG, "Analog comparator enabled, reference voltage: %d%% * VDD", (int)ref_cfg.ref_volt * 10); -#else - ESP_LOGI(TAG, "Analog comparator enabled, external reference selected"); -#endif -} +#include "ana_cmpr_example_main.h" void example_init_monitor_gpio(void) { @@ -133,8 +23,9 @@ void example_init_monitor_gpio(void) void app_main(void) { - /* Initialize GPIO to monitor the comparator interrupt */ - example_init_monitor_gpio(); - /* Initialize Analog Comparator */ - example_init_analog_comparator(); +#if CONFIG_EXAMPLE_USE_ETM + example_analog_comparator_etm_app(); +#else + example_analog_comparator_intr_app(); +#endif } diff --git a/examples/peripherals/analog_comparator/main/ana_cmpr_example_main.h b/examples/peripherals/analog_comparator/main/ana_cmpr_example_main.h new file mode 100644 index 000000000000..48e872b3ae9b --- /dev/null +++ b/examples/peripherals/analog_comparator/main/ana_cmpr_example_main.h @@ -0,0 +1,45 @@ +/* + * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Unlicense OR CC0-1.0 + */ + +#pragma once + +#include +#include "sdkconfig.h" +#include "soc/soc_caps.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define EXAMPLE_ANA_CMPR_UNIT 0 // Analog Comparator unit +#define EXAMPLE_WAIT_TIME_PROP (0.1) // The wait time proportion in one relative signal period +#define EXAMPLE_WAITE_TIME_US(freq_approx) (uint32_t)(1000000 * EXAMPLE_WAIT_TIME_PROP / (freq_approx)) + +#define EXAMPLE_MONITOR_GPIO_NUM (0) // The gpio to monitor the on cross callback + +void example_init_monitor_gpio(void); + +#if CONFIG_EXAMPLE_USE_ETM +/** + * @brief Set or clear the monitor GPIO via Event Task Matrix when cross interrupt triggers. + * @note The interrupt of analog comparator is regarded as Event, + * and the the operation of setting/clearing the GPIO is regarded as the corresponding task of the event. +* CPU won't be involved by using Event Task Matrix, so it can achieve relatively higher interrupt frequency + */ +void example_analog_comparator_etm_app(void); +#endif + +/** + * @brief Set or clear the monitor GPIO in the cross interrupt callback. + * @note The GPIO level is set manually in the callback. + * It's more flexible so that we can realize some operation like hysteresis, + * But as the operations are done in callback, CPU is involved, so it can only achieve a low interrupt frequency + */ +void example_analog_comparator_intr_app(void); + +#ifdef __cplusplus +} +#endif