diff --git a/components/driver/.build-test-rules.yml b/components/driver/.build-test-rules.yml index 7aefd1f0ab..fc4e3f03bd 100644 --- a/components/driver/.build-test-rules.yml +++ b/components/driver/.build-test-rules.yml @@ -1,5 +1,9 @@ # Documentation: .gitlab/ci/README.md#manifest-file-to-control-the-buildtest-apps +components/driver/test_apps/analog_comparator: + disable: + - if: SOC_ANA_CMPR_SUPPORTED != 1 + components/driver/test_apps/dac_test_apps/dac: disable: - if: SOC_DAC_SUPPORTED != 1 diff --git a/components/driver/CMakeLists.txt b/components/driver/CMakeLists.txt index 3b3a7f28ba..564814091f 100644 --- a/components/driver/CMakeLists.txt +++ b/components/driver/CMakeLists.txt @@ -10,6 +10,7 @@ set(srcs # Always included headers set(includes "include" "deprecated" + "analog_comparator/include" "dac/include" "gpio/include" "gptimer/include" @@ -39,6 +40,11 @@ if(CONFIG_SOC_ADC_DMA_SUPPORTED) list(APPEND srcs "deprecated/adc_dma_legacy.c") endif() +# Analog comparator related source files +if(CONFIG_SOC_ANA_CMPR_SUPPORTED) + list(APPEND srcs "analog_comparator/ana_cmpr.c") +endif() + # DAC related source files if(CONFIG_SOC_DAC_SUPPORTED) list(APPEND srcs "dac/dac_oneshot.c" diff --git a/components/driver/Kconfig b/components/driver/Kconfig index e458b50922..a8ff75a3ae 100644 --- a/components/driver/Kconfig +++ b/components/driver/Kconfig @@ -274,6 +274,31 @@ menu "Driver Configurations" Note that, this option only controls the SDM driver log, won't affect other drivers. endmenu # Sigma Delta Modulator Configuration + menu "Analog Comparator Configuration" + depends on SOC_ANA_CMPR_SUPPORTED + config ANA_CMPR_ISR_IRAM_SAFE + bool "Analog comparator ISR IRAM-Safe" + default n + help + Ensure the Analog Comparator interrupt is IRAM-Safe by allowing the interrupt handler to be + executable when the cache is disabled (e.g. SPI Flash write). + + config ANA_CMPR_CTRL_FUNC_IN_IRAM + bool "Place Analog Comparator control functions into IRAM" + default n + help + Place Analog Comparator control functions (like ana_cmpr_set_internal_reference) into IRAM, + so that these functions can be IRAM-safe and able to be called in an IRAM interrupt context. + Enabling this option can improve driver performance as well. + + config ANA_CMPR_ENABLE_DEBUG_LOG + bool "Enable debug log" + default n + help + Wether to enable the debug log message for Analog Comparator driver. + Note that, this option only controls the Analog Comparator driver log, won't affect other drivers. + endmenu # Analog Comparator Configuration + menu "GPTimer Configuration" config GPTIMER_CTRL_FUNC_IN_IRAM bool "Place GPTimer control functions into IRAM" diff --git a/components/driver/analog_comparator/ana_cmpr.c b/components/driver/analog_comparator/ana_cmpr.c new file mode 100644 index 0000000000..b45391a005 --- /dev/null +++ b/components/driver/analog_comparator/ana_cmpr.c @@ -0,0 +1,327 @@ +/* + * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include "sdkconfig.h" +#if CONFIG_ANA_CMPR_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 "clk_tree.h" +#include "esp_types.h" +#include "esp_attr.h" +#include "esp_check.h" +#include "esp_pm.h" +#include "esp_heap_caps.h" +#include "esp_intr_alloc.h" +#include "esp_memory_utils.h" +#include "soc/periph_defs.h" +#include "soc/ana_cmpr_periph.h" +#include "hal/ana_cmpr_ll.h" +#include "driver/ana_cmpr.h" +#include "esp_private/io_mux.h" +#include "esp_private/esp_clk.h" + +struct ana_cmpr_t { + ana_cmpr_unit_t unit; /*!< Analog comparator unit id */ + analog_cmpr_dev_t *dev; /*!< Analog comparator unit device address */ + ana_cmpr_ref_source_t ref_src; /*!< Analog comparator reference source, internal or external */ + 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 */ + 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 */ +}; + +/* 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, \ + 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 +#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 +#endif + +/* Driver tag */ +static const char *TAG = "ana_cmpr"; + +/* Global static object of the Analog Comparator unit */ +static ana_cmpr_handle_t s_ana_cmpr[SOC_ANA_CMPR_NUM] = { + [0 ... (SOC_ANA_CMPR_NUM - 1)] = NULL, +}; + +/* Global spin lock */ +static portMUX_TYPE s_spinlock = portMUX_INITIALIZER_UNLOCKED; + +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; + /* 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)) { + need_yield = cmpr_handle->cbs.on_cross(cmpr_handle, &evt_data, cmpr_handle->user_data); + } + if (need_yield) { + portYIELD_FROM_ISR(); + } +} + +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 + esp_log_level_set(TAG, ESP_LOG_DEBUG); +#endif + ANA_CMPR_NULL_POINTER_CHECK(config); + ANA_CMPR_NULL_POINTER_CHECK(ret_cmpr); + ana_cmpr_unit_t unit = config->unit; + ANA_CMPR_UNIT_CHECK(unit); + ESP_RETURN_ON_FALSE(!s_ana_cmpr[unit], ESP_ERR_INVALID_STATE, TAG, + "unit has been allocated already"); + esp_err_t ret = ESP_OK; + + /* Allocate analog comparator unit */ + s_ana_cmpr[unit] = (ana_cmpr_handle_t)heap_caps_calloc(1, sizeof(struct ana_cmpr_t), ANA_CMPR_MEM_ALLOC_CAPS); + 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]->ref_src = config->ref_src; + s_ana_cmpr[unit]->is_enabled = false; + s_ana_cmpr[unit]->pm_lock = NULL; + +#if CONFIG_PM_ENABLE + /* Create PM lock */ + char lock_name[10] = "ana_cmpr\0"; + lock_name[8] = '0' + unit; + ret = esp_pm_lock_create(ESP_PM_NO_LIGHT_SLEEP, 0, lock_name, &s_ana_cmpr[unit]->pm_lock); + ESP_GOTO_ON_ERROR(ret, err, TAG, "create NO_LIGHT_SLEEP, lock failed"); +#endif + + /* Analog clock comes from IO MUX, but IO MUX clock might be shared with other submodules as well */ + ESP_GOTO_ON_ERROR(clk_tree_src_get_freq_hz((soc_module_clk_t)config->clk_src, + CLK_TREE_SRC_FREQ_PRECISION_CACHED, + &s_ana_cmpr[unit]->src_clk_freq_hz), + err, TAG, "get source clock frequency failed"); + ESP_GOTO_ON_ERROR(io_mux_set_clock_source((soc_module_clk_t)(config->clk_src)), err, TAG, + "potential clock source conflicts from other IOMUX peripherals"); + + /* Configure the register */ + portENTER_CRITICAL(&s_spinlock); + analog_cmpr_ll_set_ref_source(s_ana_cmpr[unit]->dev, config->ref_src); + analog_cmpr_ll_set_cross_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); + } 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); + } + + *ret_cmpr = s_ana_cmpr[unit]; + return ESP_OK; + +err: + /* Delete the unit if allocation failed */ + ana_cmpr_del_unit(s_ana_cmpr[unit]); + return ret; +} + +esp_err_t ana_cmpr_del_unit(ana_cmpr_handle_t cmpr) +{ + ANA_CMPR_NULL_POINTER_CHECK(cmpr); + /* Search the global object array to check if the input handle is valid */ + ana_cmpr_unit_t unit = -1; + for (int i = 0; i < SOC_ANA_CMPR_NUM; i++) { + if (s_ana_cmpr[i] == cmpr) { + unit = i; + break; + } + } + ESP_RETURN_ON_FALSE(unit >= ANA_CMPR_UNIT_0, ESP_ERR_INVALID_ARG, TAG, "wrong analog comparator handle"); + ESP_RETURN_ON_FALSE(!cmpr->is_enabled, ESP_ERR_INVALID_STATE, TAG, "this analog comparator unit not disabled yet"); + + /* Delete the pm lock if the unit has */ + if (cmpr->pm_lock) { + ESP_RETURN_ON_ERROR(esp_pm_lock_delete(cmpr->pm_lock), TAG, "delete pm lock failed"); + } + + /* Free interrupt and other resources */ + if (cmpr->intr_handle) { + esp_intr_free(cmpr->intr_handle); + } + + free(s_ana_cmpr[unit]); + s_ana_cmpr[unit] = NULL; + + ESP_LOGD(TAG, "unit %d deleted", (int)unit); + + return ESP_OK; +} + +esp_err_t ana_cmpr_set_internal_reference(ana_cmpr_handle_t cmpr, const ana_cmpr_internal_ref_config_t *ref_cfg) +{ + ANA_CMPR_NULL_POINTER_CHECK_ISR(cmpr); + ANA_CMPR_NULL_POINTER_CHECK_ISR(ref_cfg); + ESP_RETURN_ON_FALSE_ISR(cmpr->ref_src == ANA_CMPR_REF_SRC_INTERNAL, ESP_ERR_INVALID_STATE, + TAG, "the reference channel is not internal, no need to configure internal reference"); + + /* Set internal reference voltage */ + portENTER_CRITICAL_SAFE(&s_spinlock); + analog_cmpr_ll_set_internal_ref_voltage(cmpr->dev, ref_cfg->ref_volt); + portEXIT_CRITICAL_SAFE(&s_spinlock); + + ESP_EARLY_LOGD(TAG, "unit %d internal voltage level %"PRIu32, (int)cmpr->unit, ref_cfg->ref_volt); + + return ESP_OK; +} + +esp_err_t ana_cmpr_set_debounce(ana_cmpr_handle_t cmpr, const ana_cmpr_debounce_config_t *dbc_cfg) +{ + ANA_CMPR_NULL_POINTER_CHECK_ISR(cmpr); + ANA_CMPR_NULL_POINTER_CHECK_ISR(dbc_cfg); + + /* Transfer the time to clock cycles */ + uint32_t wait_cycle = (uint32_t)(dbc_cfg->wait_us * (cmpr->src_clk_freq_hz / 1000000)); + /* Set the waiting clock cycles */ + portENTER_CRITICAL_SAFE(&s_spinlock); + analog_cmpr_ll_set_debounce_cycle(cmpr->dev, wait_cycle); + portEXIT_CRITICAL_SAFE(&s_spinlock); + + ESP_EARLY_LOGD(TAG, "unit %d debounce wait cycle %"PRIu32, (int)cmpr->unit, wait_cycle); + + return ESP_OK; +} + +esp_err_t ana_cmpr_set_cross_type(ana_cmpr_handle_t cmpr, ana_cmpr_cross_type_t cross_type) +{ + 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); + analog_cmpr_ll_set_cross_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; +} + +esp_err_t ana_cmpr_register_event_callbacks(ana_cmpr_handle_t cmpr, const ana_cmpr_event_callbacks_t *cbs, void *user_data) +{ + ANA_CMPR_NULL_POINTER_CHECK(cmpr); + ANA_CMPR_NULL_POINTER_CHECK(cbs); + ESP_RETURN_ON_FALSE(!cmpr->is_enabled, ESP_ERR_INVALID_STATE, TAG, + "please disable the analog comparator before registering the callbacks"); +#if CONFIG_ANA_CMPR_ISR_IRAM_SAFE + if (cbs->on_cross) { + ESP_RETURN_ON_FALSE(esp_ptr_in_iram(cbs->on_cross), ESP_ERR_INVALID_ARG, TAG, + "ANA_CMPR_ISR_IRAM_SAFE enabled but the callback function is not in IRAM"); + } + if (user_data) { + ESP_RETURN_ON_FALSE(esp_ptr_in_iram(user_data), ESP_ERR_INVALID_ARG, TAG, + "ANA_CMPR_ISR_IRAM_SAFE enabled but the user_data is not in IRAM"); + } +#endif + + /* Save the callback group */ + memcpy(&(cmpr->cbs), cbs, sizeof(ana_cmpr_event_callbacks_t)); + cmpr->user_data = user_data; + + ESP_LOGD(TAG, "unit %d event callback registered", (int)cmpr->unit); + + return ESP_OK; +} + +esp_err_t ana_cmpr_enable(ana_cmpr_handle_t cmpr) +{ + ANA_CMPR_NULL_POINTER_CHECK(cmpr); + ESP_RETURN_ON_FALSE(!cmpr->is_enabled, ESP_ERR_INVALID_STATE, TAG, + "the analog comparator has enabled already"); + /* Update the driver status */ + cmpr->is_enabled = true; + + /* Acquire the pm lock if the unit has, to avoid the system start light sleep while Analog comparator still working */ + if (cmpr->pm_lock) { + ESP_RETURN_ON_ERROR(esp_pm_lock_acquire(cmpr->pm_lock), TAG, "acquire pm_lock failed"); + } + + /* 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(cmpr->dev, true); + portEXIT_CRITICAL(&s_spinlock); + + ESP_LOGD(TAG, "unit %d enabled", (int)cmpr->unit); + + return ESP_OK; +} + +esp_err_t ana_cmpr_disable(ana_cmpr_handle_t cmpr) +{ + ANA_CMPR_NULL_POINTER_CHECK(cmpr); + ESP_RETURN_ON_FALSE(cmpr->is_enabled, ESP_ERR_INVALID_STATE, TAG, + "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(cmpr->dev, false); + portEXIT_CRITICAL(&s_spinlock); + + /* Release the pm lock, allow light sleep then */ + if (cmpr->pm_lock) { + ESP_RETURN_ON_ERROR(esp_pm_lock_release(cmpr->pm_lock), TAG, "release pm_lock failed"); + } + + /* Update the driver status */ + cmpr->is_enabled = false; + + ESP_LOGD(TAG, "unit %d disabled", (int)cmpr->unit); + return ESP_OK; +} + +esp_err_t ana_cmpr_get_gpio(ana_cmpr_unit_t unit, ana_cmpr_channel_type_t chan_type, int *gpio_num) +{ + ANA_CMPR_NULL_POINTER_CHECK(gpio_num); + ANA_CMPR_UNIT_CHECK(unit); + + /* 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; + break; + case ANA_CMPR_EXT_REF_CHAN: + *gpio_num = ana_cmpr_io_map[unit].ext_ref_gpio; + break; + default: + ESP_LOGE(TAG, "invalid channel type"); + return ESP_ERR_INVALID_ARG; + } + + return ESP_OK; +} diff --git a/components/driver/analog_comparator/include/driver/ana_cmpr.h b/components/driver/analog_comparator/include/driver/ana_cmpr.h new file mode 100644 index 0000000000..4cd09852b1 --- /dev/null +++ b/components/driver/analog_comparator/include/driver/ana_cmpr.h @@ -0,0 +1,186 @@ +/* + * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include "esp_err.h" +#include "driver/ana_cmpr_types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Analog comparator unit configuration + * + */ +typedef struct { + ana_cmpr_unit_t unit; /*!< Analog comparator unit */ + ana_cmpr_clk_src_t clk_src; /*!< The clock source of the analog comparator, + * which decide the resolution of the comparator + */ + ana_cmpr_ref_source_t ref_src; /*!< Reference signal source of the comparator, + * select using ANA_CMPR_REF_SRC_INTERNAL or ANA_CMPR_REF_SRC_EXTERNAL. + * For internal reference, the reference voltage should be set to `internal_ref_volt`, + * 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 */ +} ana_cmpr_config_t; + +/** + * @brief Analog comparator internal reference configuration + * + */ +typedef struct { + ana_cmpr_ref_voltage_t ref_volt; /*!< The internal reference voltage. It can be specified to a certain fixed percentage of + * the VDD power supply, currently supports 0%~70% VDD with a step 10% + */ +} ana_cmpr_internal_ref_config_t; + +/** + * @brief Analog comparator debounce filter configuration + * + */ +typedef struct { + uint32_t wait_us; /*!< The wait time of re-enabling the interrupt after the last triggering, + * it is used to avoid the spurious triggering while the source signal crossing the reference signal. + * The value should regarding how fast the source signal changes, e.g., a rapid signal requires + * a small wait time, otherwise the next crosses may be missed. + * (Unit: micro second) + */ +} ana_cmpr_debounce_config_t; + +/** + * @brief Group of Analog Comparator callbacks + * @note The callbacks are all running under ISR environment + * @note When CONFIG_ANA_CMPR_ISR_IRAM_SAFE is enabled, the callback itself and functions called by it should be placed in IRAM. + * The variables used in the function should be in the SRAM as well. + */ +typedef struct { + ana_cmpr_cross_cb_t on_cross; /*!< The callback function on cross interrupt */ +} ana_cmpr_event_callbacks_t; + +/** + * @brief Allocating a new analog comparator unit handle + * + * @param[in] config The config of the analog comparator unit + * @param[out] ret_cmpr The returned analog comparator unit handle + * @return + * - ESP_OK Allocate analog comparator unit handle success + * - ESP_ERR_NO_MEM No memory for the analog comparator structure + * - ESP_ERR_INVALID_ARG NULL pointer of the parameters or wrong unit number + * - ESP_ERR_INVALID_STATE The unit has been allocated or the clock source has been occupied + */ +esp_err_t ana_cmpr_new_unit(const ana_cmpr_config_t *config, ana_cmpr_handle_t *ret_cmpr); + +/** + * @brief Delete the analog comparator unit handle + * + * @param[in] cmpr The handle of analog comparator unit + * @return + * - ESP_OK Delete analog comparator unit handle success + * - ESP_ERR_INVALID_ARG NULL pointer of the parameters or wrong unit number + * - ESP_ERR_INVALID_STATE The analog comparator is not disabled yet + */ +esp_err_t ana_cmpr_del_unit(ana_cmpr_handle_t cmpr); + +/** + * @brief Set internal reference configuration + * @note This function only need to be called when `ana_cmpr_config_t::ref_src` + * is ANA_CMPR_REF_SRC_INTERNAL. + * @note This function is allowed to run within ISR context including intr callbacks + * @note This function will be placed into IRAM if `CONFIG_ANA_CMPR_CTRL_FUNC_IN_IRAM` is on, + * so that it's allowed to be executed when Cache is disabled + * + * @param[in] cmpr The handle of analog comparator unit + * @param[in] ref_cfg Internal reference configuration + * @return + * - ESP_OK Set denounce configuration success + * - ESP_ERR_INVALID_ARG NULL pointer of the parameters + * - ESP_ERR_INVALID_STATE The reference source is not `ANA_CMPR_REF_SRC_INTERNAL` + */ +esp_err_t ana_cmpr_set_internal_reference(ana_cmpr_handle_t cmpr, const ana_cmpr_internal_ref_config_t *ref_cfg); + +/** + * @brief Set debounce configuration to the analog comparator + * @note This function is allowed to run within ISR context including intr callbacks + * @note This function will be placed into IRAM if `CONFIG_ANA_CMPR_CTRL_FUNC_IN_IRAM` is on, + * so that it's allowed to be executed when Cache is disabled + * + * @param[in] cmpr The handle of analog comparator unit + * @param[in] dbc_cfg Debounce configuration + * @return + * - ESP_OK Set denounce configuration success + * - ESP_ERR_INVALID_ARG NULL pointer of the parameters + */ +esp_err_t ana_cmpr_set_debounce(ana_cmpr_handle_t cmpr, const ana_cmpr_debounce_config_t *dbc_cfg); + +/** + * @brief Set the source signal cross type + * @note The initial cross type is configured in `ana_cmpr_new_unit`, this function can update the cross type + * @note This function is allowed to run within ISR context including intr callbacks + * @note This function will be placed into IRAM if `CONFIG_ANA_CMPR_CTRL_FUNC_IN_IRAM` is on, + * so that it's allowed to be executed when Cache is disabled + * + * @param[in] cmpr The handle of analog comparator unit + * @param[in] cross_type The source signal cross type that can trigger the interrupt + * @return + * - ESP_OK Set denounce configuration success + * - ESP_ERR_INVALID_ARG NULL pointer of the parameters + */ +esp_err_t ana_cmpr_set_cross_type(ana_cmpr_handle_t cmpr, ana_cmpr_cross_type_t cross_type); + +/** + * @brief Register analog comparator interrupt event callbacks + * @note This function can only be called before enabling the unit + * + * @param[in] cmpr The handle of analog comparator unit + * @param[in] cbs Group of callback functions + * @param[in] user_data The user data that will be passed to callback functions directly + * @return + * - ESP_OK Register callbacks success + * - ESP_ERR_INVALID_ARG NULL pointer of the parameters + * - ESP_ERR_INVALID_STATE The analog comparator has been enabled + */ +esp_err_t ana_cmpr_register_event_callbacks(ana_cmpr_handle_t cmpr, const ana_cmpr_event_callbacks_t *cbs, void *user_data); + +/** + * @brief Enable the analog comparator unit + * + * @param[in] cmpr The handle of analog comparator unit + * @return + * - ESP_OK Enable analog comparator unit success + * - ESP_ERR_INVALID_ARG NULL pointer of the parameters + * - ESP_ERR_INVALID_STATE The analog comparator has been enabled + */ +esp_err_t ana_cmpr_enable(ana_cmpr_handle_t cmpr); + +/** + * @brief Disable the analog comparator unit + * + * @param[in] cmpr The handle of analog comparator unit + * @return + * - ESP_OK Disable analog comparator unit success + * - ESP_ERR_INVALID_ARG NULL pointer of the parameters + * - ESP_ERR_INVALID_STATE The analog comparator has disabled already + */ +esp_err_t ana_cmpr_disable(ana_cmpr_handle_t cmpr); + +/** + * @brief Get the specific GPIO number of the analog comparator unit + * + * @param[in] unit The handle of analog comparator unit + * @param[in] chan_type The channel type of analog comparator, like source channel or reference channel + * @param[out] gpio_num The output GPIO number of this channel + * @return + * - ESP_OK Get GPIO success + * - ESP_ERR_INVALID_ARG NULL pointer of the parameters or wrong unit number or wrong channel type + */ +esp_err_t ana_cmpr_get_gpio(ana_cmpr_unit_t unit, ana_cmpr_channel_type_t chan_type, int *gpio_num); + +#ifdef __cplusplus +} +#endif diff --git a/components/driver/analog_comparator/include/driver/ana_cmpr_types.h b/components/driver/analog_comparator/include/driver/ana_cmpr_types.h new file mode 100644 index 0000000000..a04ae0b52f --- /dev/null +++ b/components/driver/analog_comparator/include/driver/ana_cmpr_types.h @@ -0,0 +1,109 @@ +/* + * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include +#include "soc/clk_tree_defs.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Analog comparator unit + * + */ +typedef enum { + ANA_CMPR_UNIT_0, /*!< Analog Comparator unit */ +} ana_cmpr_unit_t; + +/** + * @brief Analog comparator reference source + * + */ +typedef enum { + ANA_CMPR_REF_SRC_INTERNAL, /*!< Analog Comparator internal reference source, related to VDD */ + ANA_CMPR_REF_SRC_EXTERNAL, /*!< Analog Comparator external reference source, from `ANA_CMPR0_EXT_REF_GPIO` */ +} ana_cmpr_ref_source_t; + +/** + * @brief Analog comparator channel type + * + */ +typedef enum { + ANA_CMPR_SOURCE_CHAN, /*!< Analog Comparator source channel, which is used to input the signal that to be compared */ + ANA_CMPR_EXT_REF_CHAN, /*!< Analog Comparator external reference channel, which is used as the reference signal */ +} ana_cmpr_channel_type_t; + +/** + * @brief Analog comparator interrupt type + * + */ +typedef enum { + ANA_CMPR_CROSS_DISABLE, /*!< Disable the cross event interrupt */ + ANA_CMPR_CROSS_POS, /*!< Positive cross can trigger event interrupt */ + ANA_CMPR_CROSS_NEG, /*!< Negative cross can trigger event interrupt */ + ANA_CMPR_CROSS_ANY, /*!< Any cross can trigger event interrupt */ +} ana_cmpr_cross_type_t; + +/** + * @brief Analog comparator internal reference voltage + * + */ +typedef enum { + ANA_CMPR_REF_VOLT_0_PCT_VDD, /*!< Internal reference voltage equals to 0% VDD */ + ANA_CMPR_REF_VOLT_10_PCT_VDD, /*!< Internal reference voltage equals to 10% VDD */ + ANA_CMPR_REF_VOLT_20_PCT_VDD, /*!< Internal reference voltage equals to 20% VDD */ + ANA_CMPR_REF_VOLT_30_PCT_VDD, /*!< Internal reference voltage equals to 30% VDD */ + ANA_CMPR_REF_VOLT_40_PCT_VDD, /*!< Internal reference voltage equals to 40% VDD */ + ANA_CMPR_REF_VOLT_50_PCT_VDD, /*!< Internal reference voltage equals to 50% VDD */ + ANA_CMPR_REF_VOLT_60_PCT_VDD, /*!< Internal reference voltage equals to 60% VDD */ + ANA_CMPR_REF_VOLT_70_PCT_VDD, /*!< Internal reference voltage equals to 70% VDD */ +} ana_cmpr_ref_voltage_t; + +/** + * @brief Analog comparator unit handle + * + */ +typedef struct ana_cmpr_t *ana_cmpr_handle_t; + +#if SOC_ANA_CMPR_SUPPORTED +/** + * @brief Analog comparator clock source + * + */ +typedef soc_periph_ana_cmpr_clk_src_t ana_cmpr_clk_src_t; +#else +/** + * @brief Analog comparator clock source + * + */ +typedef int ana_cmpr_clk_src_t; +#endif + +/** + * @brief Analog comparator cross event data + * + */ +typedef struct { + // No data for now +} ana_cmpr_cross_event_data_t; + +/** + * @brief Prototype of Analog comparator event callback + * @param[in] cmpr Analog Comparator handle, created from `ana_cmpr_new_unit()` + * @param[in] edata Point to Analog Comparator event data. The lifecycle of this pointer memory is inside this function, + * user should copy it into static memory if used outside this function. (Currently not use) + * @param[in] user_ctx User registered context, passed from `ana_cmpr_register_event_callbacks()` + * + * @return Whether a high priority task has been waken up by this callback function + */ +typedef bool (*ana_cmpr_cross_cb_t) (ana_cmpr_handle_t cmpr, const ana_cmpr_cross_event_data_t *edata, void *user_ctx); + +#ifdef __cplusplus +} +#endif diff --git a/components/driver/gpio/gpio.c b/components/driver/gpio/gpio.c index 971b2ac5d7..2a45dc356b 100644 --- a/components/driver/gpio/gpio.c +++ b/components/driver/gpio/gpio.c @@ -582,6 +582,9 @@ esp_err_t gpio_isr_register(void (*fn)(void *), void *arg, int intr_alloc_flags, gpio_isr_alloc_t p; p.source = ETS_GPIO_INTR_SOURCE; p.intr_alloc_flags = intr_alloc_flags; +#if SOC_ANA_CMPR_SUPPORTED + p.intr_alloc_flags |= ESP_INTR_FLAG_SHARED; +#endif p.fn = fn; p.arg = arg; p.handle = handle; diff --git a/components/driver/linker.lf b/components/driver/linker.lf index 54670adf28..1f6f247879 100644 --- a/components/driver/linker.lf +++ b/components/driver/linker.lf @@ -18,6 +18,10 @@ entries: gpio: gpio_intr_disable (noflash) if SDM_CTRL_FUNC_IN_IRAM = y: sdm: sdm_channel_set_pulse_density (noflash) + if ANA_CMPR_CTRL_FUNC_IN_IRAM = y: + ana_cmpr: ana_cmpr_set_internal_reference (noflash) + ana_cmpr: ana_cmpr_set_debounce (noflash) + ana_cmpr: ana_cmpr_set_cross_type (noflash) if DAC_CTRL_FUNC_IN_IRAM = y: dac_oneshot: dac_oneshot_output_voltage (noflash) dac_continuous: dac_continuous_write_asynchronously (noflash) diff --git a/components/driver/test_apps/analog_comparator/CMakeLists.txt b/components/driver/test_apps/analog_comparator/CMakeLists.txt new file mode 100644 index 0000000000..1b4500c607 --- /dev/null +++ b/components/driver/test_apps/analog_comparator/CMakeLists.txt @@ -0,0 +1,18 @@ +# This is the project CMakeLists.txt file for the test subproject +cmake_minimum_required(VERSION 3.16) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +project(test_ana_cmpr) + +if(CONFIG_COMPILER_DUMP_RTL_FILES) + add_custom_target(check_test_app_sections ALL + COMMAND ${PYTHON} $ENV{IDF_PATH}/tools/ci/check_callgraph.py + --rtl-dir ${CMAKE_BINARY_DIR}/esp-idf/driver/ + --elf-file ${CMAKE_BINARY_DIR}/test_ana_cmpr.elf + find-refs + --from-sections=.iram0.text + --to-sections=.flash.text + --exit-code + DEPENDS ${elf} + ) +endif() diff --git a/components/driver/test_apps/analog_comparator/README.md b/components/driver/test_apps/analog_comparator/README.md new file mode 100644 index 0000000000..8161d00f6d --- /dev/null +++ b/components/driver/test_apps/analog_comparator/README.md @@ -0,0 +1,2 @@ +| Supported Targets | ESP32-H2 | +| ----------------- | -------- | diff --git a/components/driver/test_apps/analog_comparator/main/CMakeLists.txt b/components/driver/test_apps/analog_comparator/main/CMakeLists.txt new file mode 100644 index 0000000000..beb627afb1 --- /dev/null +++ b/components/driver/test_apps/analog_comparator/main/CMakeLists.txt @@ -0,0 +1,11 @@ +set(srcs "test_app_main.c" + "test_ana_cmpr_common.c" + "test_ana_cmpr.c") + +if(CONFIG_ANA_CMPR_ISR_IRAM_SAFE) + list(APPEND srcs "test_ana_cmpr_iram.c") +endif() + +idf_component_register(SRCS ${srcs} + INCLUDE_DIRS "." + WHOLE_ARCHIVE) 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 new file mode 100644 index 0000000000..8235113afb --- /dev/null +++ b/components/driver/test_apps/analog_comparator/main/test_ana_cmpr.c @@ -0,0 +1,86 @@ +/* + * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "test_ana_cmpr.h" + +TEST_CASE("ana_cmpr_unit_install_uninstall", "[ana_cmpr]") +{ + ana_cmpr_handle_t cmpr = NULL; + ana_cmpr_config_t config = { + .unit = SOC_ANA_CMPR_NUM, // Set a wrong unit + .clk_src = ANA_CMPR_CLK_SRC_DEFAULT, + .ref_src = ANA_CMPR_REF_SRC_INTERNAL, + .cross_type = ANA_CMPR_CROSS_ANY, + }; + /* 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; + 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)); + /* Set the internal reference before enable */ + 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)); + /* Enable the unit */ + TEST_ESP_OK(ana_cmpr_enable(cmpr)); + /* Set the internal reference after enable */ + ref_cfg.ref_volt = ANA_CMPR_REF_VOLT_30_PCT_VDD; + TEST_ESP_OK(ana_cmpr_set_internal_reference(cmpr, &ref_cfg)); + /* Try tp delete unit after enable */ + TEST_ESP_ERR(ESP_ERR_INVALID_STATE, ana_cmpr_del_unit(cmpr)); + /* Disable the unit */ + TEST_ESP_OK(ana_cmpr_disable(cmpr)); + /* Try to delete the unit with a wrong handle */ + TEST_ESP_ERR(ESP_ERR_INVALID_STATE, ana_cmpr_del_unit((void *)&cmpr)); + /* Delete the unit */ + TEST_ESP_OK(ana_cmpr_del_unit(cmpr)); + + /* Try to set internal reference for a external unit */ + config.ref_src = ANA_CMPR_REF_SRC_EXTERNAL; + TEST_ESP_OK(ana_cmpr_new_unit(&config, &cmpr)); + TEST_ESP_ERR(ESP_ERR_INVALID_STATE, ana_cmpr_set_internal_reference(cmpr, &ref_cfg)); + TEST_ESP_OK(ana_cmpr_del_unit(cmpr)); +} + +TEST_CASE("ana_cmpr_internal_reference", "[ana_cmpr]") +{ + int src_chan = test_init_src_chan_gpio(); + uint32_t cnt = 0; + + ana_cmpr_handle_t cmpr = NULL; + ana_cmpr_config_t config = { + .unit = ANA_CMPR_UNIT_0, + .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.0, + }; + TEST_ESP_OK(ana_cmpr_set_debounce(cmpr, &dbc_cfg)); + ana_cmpr_event_callbacks_t cbs = { + .on_cross = test_ana_cmpr_on_cross_callback, + }; + + TEST_ESP_OK(ana_cmpr_register_event_callbacks(cmpr, &cbs, &cnt)); + TEST_ESP_OK(ana_cmpr_enable(cmpr)); + cnt = 0; + for (int i = 1; i <= 10; i++) { + test_simulate_src_signal(src_chan, i % 2); + esp_rom_delay_us(100); + TEST_ASSERT(cnt == i); + } + TEST_ESP_OK(ana_cmpr_disable(cmpr)); + TEST_ESP_OK(ana_cmpr_del_unit(cmpr)); +} diff --git a/components/driver/test_apps/analog_comparator/main/test_ana_cmpr.h b/components/driver/test_apps/analog_comparator/main/test_ana_cmpr.h new file mode 100644 index 0000000000..13566bfc84 --- /dev/null +++ b/components/driver/test_apps/analog_comparator/main/test_ana_cmpr.h @@ -0,0 +1,43 @@ +/* + * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include "sdkconfig.h" +#include "esp_attr.h" +#include "unity.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "esp_rom_sys.h" +#include "soc/soc_caps.h" +#include "driver/ana_cmpr.h" + +/** + * @brief Test default on cross callback + * + * @param cmpr Analog Comparator handle + * @param edata Event data + * @param user_ctx User context, need to input a unint32_t counter + * @return + * - true Need to yield + * - false Don't need yield + */ +bool test_ana_cmpr_on_cross_callback(ana_cmpr_handle_t cmpr, const ana_cmpr_cross_event_data_t *edata, void *user_ctx); + +/** + * @brief Initialize Analog Comparator source channel GPIO + * + * @return + * - int Source channel GPIO number + */ +int test_init_src_chan_gpio(void); + +/** + * @brief Simulate source channel signal + * + * @param src_chan Source channel GPIO number + * @param val 0 to set low, others to set high + */ +void test_simulate_src_signal(int src_chan, uint32_t val); 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 new file mode 100644 index 0000000000..9db12a0d07 --- /dev/null +++ b/components/driver/test_apps/analog_comparator/main/test_ana_cmpr_common.c @@ -0,0 +1,39 @@ +/* + * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "test_ana_cmpr.h" +#include "hal/gpio_ll.h" +#include "driver/gpio.h" +#include "esp_attr.h" + +bool IRAM_ATTR test_ana_cmpr_on_cross_callback(ana_cmpr_handle_t cmpr, const ana_cmpr_cross_event_data_t *edata, void *user_ctx) +{ + uint32_t *count = (uint32_t *)user_ctx; + (*count)++; + return false; +} + +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_ASSERT(src_chan_num > 0); + gpio_config_t io_conf = { + .intr_type = GPIO_INTR_DISABLE, + .mode = GPIO_MODE_OUTPUT, + .pin_bit_mask = (1ULL << src_chan_num), + .pull_down_en = false, + .pull_up_en = false, + }; + TEST_ESP_OK(gpio_config(&io_conf)); + TEST_ESP_OK(gpio_set_level(src_chan_num, 0)); + return src_chan_num; +} + +void IRAM_ATTR test_simulate_src_signal(int src_chan, uint32_t val) +{ + gpio_set_level(src_chan, val); +} 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 new file mode 100644 index 0000000000..ce0d5425f9 --- /dev/null +++ b/components/driver/test_apps/analog_comparator/main/test_ana_cmpr_iram.c @@ -0,0 +1,73 @@ +/* + * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "test_ana_cmpr.h" +#include "unity_test_utils.h" +#include "unity_test_utils_cache.h" + +typedef struct { + ana_cmpr_handle_t handle; + uint32_t count; + int src_chan; +} test_ana_cmpr_data_t; + +static void IRAM_ATTR test_ana_cmpr_iram_safety(void *args) +{ + test_ana_cmpr_data_t *data = (test_ana_cmpr_data_t *)args; + ana_cmpr_internal_ref_config_t ref_cfg = { + .ref_volt = ANA_CMPR_REF_VOLT_50_PCT_VDD, + }; + ana_cmpr_set_internal_reference(data->handle, &ref_cfg); + ana_cmpr_debounce_config_t dbc_cfg = { + .wait_us = 1, + }; + ana_cmpr_set_debounce(data->handle, &dbc_cfg); + data->count = 0; + for (int i = 1; i <= 10; i++) { + test_simulate_src_signal(data->src_chan, i % 2); + esp_rom_delay_us(100); + } + ana_cmpr_set_cross_type(data->handle, ANA_CMPR_CROSS_POS); +} + +TEST_CASE("ana_cmpr_internal_reference_iram_safe", "[ana_cmpr]") +{ + test_ana_cmpr_data_t test_data = { + .handle = NULL, + .count = 0, + .src_chan = -1, + }; + test_data.src_chan = test_init_src_chan_gpio(); + + ana_cmpr_handle_t cmpr = NULL; + ana_cmpr_config_t config = { + .unit = ANA_CMPR_UNIT_0, + .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)); + test_data.handle = 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)); + ana_cmpr_event_callbacks_t cbs = { + .on_cross = test_ana_cmpr_on_cross_callback, + }; + TEST_ESP_OK(ana_cmpr_register_event_callbacks(cmpr, &cbs, &test_data.count)); + TEST_ESP_OK(ana_cmpr_enable(cmpr)); + + unity_utils_run_cache_disable_stub(test_ana_cmpr_iram_safety, &test_data); + TEST_ASSERT_EQUAL_INT(test_data.count, 10); + + TEST_ESP_OK(ana_cmpr_disable(cmpr)); + TEST_ESP_OK(ana_cmpr_del_unit(cmpr)); +} diff --git a/components/driver/test_apps/analog_comparator/main/test_app_main.c b/components/driver/test_apps/analog_comparator/main/test_app_main.c new file mode 100644 index 0000000000..9b6762fcc8 --- /dev/null +++ b/components/driver/test_apps/analog_comparator/main/test_app_main.c @@ -0,0 +1,54 @@ +/* + * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "unity.h" +#include "unity_test_runner.h" +#include "esp_heap_caps.h" + +// Some resources are lazy allocated in analog comparator driver, the threshold is left for that case +#define TEST_MEMORY_LEAK_THRESHOLD (-300) + +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"); +} + +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); +} + +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"); +} + +void app_main(void) +{ + +// _ _ _ _ ____ __ __ ____ ____ _____ _ +// / \ | \ | | / \ / ___| \/ | _ \| _ \ |_ _|__ ___| |_ +// / _ \ | \| | / _ \ | | | |\/| | |_) | |_) | | |/ _ \/ __| __| +// / ___ \| |\ |/ ___ \ | |___| | | | __/| _ < | | __/\__ \ |_ +// /_/ \_\_| \_/_/ \_\ \____|_| |_|_| |_| \_\ |_|\___||___/\__| + + printf(" _ _ _ _ ____ __ __ ____ ____ _____ _ \n"); + printf(" / \\ | \\ | | / \\ / ___| \\/ | _ \\| _ \\ |_ _|__ ___| |_ \n"); + printf(" / _ \\ | \\| | / _ \\ | | | |\\/| | |_) | |_) | | |/ _ \\/ __| __|\n"); + printf(" / ___ \\| |\\ |/ ___ \\ | |___| | | | __/| _ < | | __/\\__ \\ |_ \n"); + printf(" /_/ \\_\\_| \\_/_/ \\_\\ \\____|_| |_|_| |_| \\_\\ |_|\\___||___/\\__|\n"); + printf("\n"); + unity_run_menu(); +} diff --git a/components/driver/test_apps/analog_comparator/pytest_ana_cmpr.py b/components/driver/test_apps/analog_comparator/pytest_ana_cmpr.py new file mode 100644 index 0000000000..1cc5cf4971 --- /dev/null +++ b/components/driver/test_apps/analog_comparator/pytest_ana_cmpr.py @@ -0,0 +1,19 @@ +# SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD +# SPDX-License-Identifier: CC0-1.0 + +import pytest +from pytest_embedded import Dut + + +@pytest.mark.esp32h2 +@pytest.mark.generic +@pytest.mark.parametrize( + 'config', + [ + 'iram_safe', + 'release', + ], + indirect=True, +) +def test_ana_cmpr(dut: Dut) -> None: + dut.run_all_single_board_cases() diff --git a/components/driver/test_apps/analog_comparator/sdkconfig.ci.iram_safe b/components/driver/test_apps/analog_comparator/sdkconfig.ci.iram_safe new file mode 100644 index 0000000000..9d68c306bf --- /dev/null +++ b/components/driver/test_apps/analog_comparator/sdkconfig.ci.iram_safe @@ -0,0 +1,7 @@ +CONFIG_COMPILER_DUMP_RTL_FILES=y +CONFIG_ANA_CMPR_ISR_IRAM_SAFE=y +CONFIG_ANA_CMPR_CTRL_FUNC_IN_IRAM=y +CONFIG_GPIO_CTRL_FUNC_IN_IRAM=y +CONFIG_COMPILER_OPTIMIZATION_NONE=y +# place non-ISR FreeRTOS functions in Flash +CONFIG_FREERTOS_PLACE_FUNCTIONS_INTO_FLASH=y diff --git a/components/driver/test_apps/analog_comparator/sdkconfig.ci.release b/components/driver/test_apps/analog_comparator/sdkconfig.ci.release new file mode 100644 index 0000000000..91d93f163e --- /dev/null +++ b/components/driver/test_apps/analog_comparator/sdkconfig.ci.release @@ -0,0 +1,5 @@ +CONFIG_PM_ENABLE=y +CONFIG_FREERTOS_USE_TICKLESS_IDLE=y +CONFIG_COMPILER_OPTIMIZATION_SIZE=y +CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_SIZE=y +CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_SILENT=y diff --git a/components/driver/test_apps/analog_comparator/sdkconfig.defaults b/components/driver/test_apps/analog_comparator/sdkconfig.defaults new file mode 100644 index 0000000000..b308cb2ddd --- /dev/null +++ b/components/driver/test_apps/analog_comparator/sdkconfig.defaults @@ -0,0 +1,2 @@ +CONFIG_FREERTOS_HZ=1000 +CONFIG_ESP_TASK_WDT=n diff --git a/components/hal/esp32h2/include/hal/ana_cmpr_ll.h b/components/hal/esp32h2/include/hal/ana_cmpr_ll.h new file mode 100644 index 0000000000..a818ea8b47 --- /dev/null +++ b/components/hal/esp32h2/include/hal/ana_cmpr_ll.h @@ -0,0 +1,153 @@ +/* + * 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/gpio_ext_struct.h" + +#define ANALOG_CMPR_LL_GET_HW() (&ANALOG_CMPR) +#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 + +/** + * @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 Set cross interrupt 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 + */ +__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; +} + +/** + * @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) +{ + if (enable) { + hw->int_ena.val |= mask; + } else { + hw->int_ena.val &= ~mask; + } +} + +/** + * @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/soc/CMakeLists.txt b/components/soc/CMakeLists.txt index 35572de538..9255e70f77 100644 --- a/components/soc/CMakeLists.txt +++ b/components/soc/CMakeLists.txt @@ -30,6 +30,10 @@ if(CONFIG_SOC_ADC_SUPPORTED) list(APPEND srcs "${target}/adc_periph.c") endif() +if(CONFIG_SOC_ANA_CMPR_SUPPORTED) + list(APPEND srcs "${target}/ana_cmpr_periph.c") +endif() + if(CONFIG_SOC_DEDICATED_GPIO_SUPPORTED) list(APPEND srcs "${target}/dedic_gpio_periph.c") endif() diff --git a/components/soc/esp32h2/ana_cmpr_periph.c b/components/soc/esp32h2/ana_cmpr_periph.c new file mode 100644 index 0000000000..4d3a67586c --- /dev/null +++ b/components/soc/esp32h2/ana_cmpr_periph.c @@ -0,0 +1,14 @@ +/* + * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "soc/ana_cmpr_periph.h" + +const ana_cmpr_conn_t ana_cmpr_io_map[SOC_ANA_CMPR_NUM] = { + [0] = { + .src_gpio = ANA_CMPR0_SRC_GPIO, + .ext_ref_gpio = ANA_CMPR0_EXT_REF_GPIO, + }, +}; diff --git a/components/soc/esp32h2/include/soc/Kconfig.soc_caps.in b/components/soc/esp32h2/include/soc/Kconfig.soc_caps.in index fb80504c82..1daccc3f54 100644 --- a/components/soc/esp32h2/include/soc/Kconfig.soc_caps.in +++ b/components/soc/esp32h2/include/soc/Kconfig.soc_caps.in @@ -7,6 +7,10 @@ config SOC_ADC_SUPPORTED bool default y +config SOC_ANA_CMPR_SUPPORTED + bool + default y + config SOC_DEDICATED_GPIO_SUPPORTED bool default y @@ -427,6 +431,10 @@ config SOC_DEDIC_PERIPH_ALWAYS_ENABLE bool default y +config SOC_ANA_CMPR_NUM + int + default 1 + config SOC_I2C_NUM int default 2 diff --git a/components/soc/esp32h2/include/soc/ana_cmpr_channel.h b/components/soc/esp32h2/include/soc/ana_cmpr_channel.h new file mode 100644 index 0000000000..1df89f6723 --- /dev/null +++ b/components/soc/esp32h2/include/soc/ana_cmpr_channel.h @@ -0,0 +1,10 @@ +/* + * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#define ANA_CMPR0_EXT_REF_GPIO 10 /*!< The GPIO that can be used as external reference voltage */ +#define ANA_CMPR0_SRC_GPIO 11 /*!< The GPIO that used for inputting the source signal to compare */ diff --git a/components/soc/esp32h2/include/soc/clk_tree_defs.h b/components/soc/esp32h2/include/soc/clk_tree_defs.h index 959060688c..39a6b9d6db 100644 --- a/components/soc/esp32h2/include/soc/clk_tree_defs.h +++ b/components/soc/esp32h2/include/soc/clk_tree_defs.h @@ -330,6 +330,22 @@ typedef enum { SDM_CLK_SRC_DEFAULT = SOC_MOD_CLK_PLL_F48M, /*!< Select PLL_F48M as the default clock choice */ } soc_periph_sdm_clk_src_t; +///////////////////////////////////////////////////Analog Comparator//////////////////////////////////////////////////// + +/** + * @brief Array initializer for all supported clock sources of Analog Comparator + */ +#define SOC_ANA_CMPR_CLKS {SOC_MOD_CLK_PLL_F48M, SOC_MOD_CLK_XTAL} + +/** + * @brief Sigma Delta Modulator 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_F48M = SOC_MOD_CLK_PLL_F48M, /*!< Select PLL_F48M clock as the source clock */ + ANA_CMPR_CLK_SRC_DEFAULT = SOC_MOD_CLK_PLL_F48M, /*!< Select PLL_F48M as the default clock choice */ +} soc_periph_ana_cmpr_clk_src_t; + //////////////////////////////////////////////////GPIO Glitch Filter//////////////////////////////////////////////////// /** diff --git a/components/soc/esp32h2/include/soc/gpio_ext_struct.h b/components/soc/esp32h2/include/soc/gpio_ext_struct.h index 10786e703c..1c9e6f3985 100644 --- a/components/soc/esp32h2/include/soc/gpio_ext_struct.h +++ b/components/soc/esp32h2/include/soc/gpio_ext_struct.h @@ -75,8 +75,8 @@ typedef union { */ uint32_t xpd_comp:1; /** mode_comp : R/W; bitpos: [1]; default: 0; - * 1 to enable external reference from PAD[0]. 0 to enable internal reference, - * meanwhile PAD[0] can be used as a regular GPIO. + * 1 to enable external reference from PAD[10]. 0 to enable internal reference, + * meanwhile PAD[10] can be used as a regular GPIO. */ uint32_t mode_comp:1; /** dref_comp : R/W; bitpos: [4:2]; default: 0; @@ -305,10 +305,15 @@ 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 089f266636..83d9b780be 100644 --- a/components/soc/esp32h2/include/soc/soc_caps.h +++ b/components/soc/esp32h2/include/soc/soc_caps.h @@ -26,6 +26,7 @@ /*-------------------------- COMMON CAPS ---------------------------------------*/ #define SOC_ADC_SUPPORTED 1 +#define SOC_ANA_CMPR_SUPPORTED 1 #define SOC_DEDICATED_GPIO_SUPPORTED 1 #define SOC_UART_SUPPORTED 1 #define SOC_GDMA_SUPPORTED 1 @@ -198,6 +199,9 @@ #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 (1U) + /*-------------------------- I2C CAPS ----------------------------------------*/ // ESP32-H2 has 2 I2C #define SOC_I2C_NUM (2U) diff --git a/components/soc/esp32h2/ld/esp32h2.peripherals.ld b/components/soc/esp32h2/ld/esp32h2.peripherals.ld index c6927ba776..4d24b46d2c 100644 --- a/components/soc/esp32h2/ld/esp32h2.peripherals.ld +++ b/components/soc/esp32h2/ld/esp32h2.peripherals.ld @@ -44,6 +44,7 @@ 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/include/soc/ana_cmpr_periph.h b/components/soc/include/soc/ana_cmpr_periph.h new file mode 100644 index 0000000000..9f3f3c36c1 --- /dev/null +++ b/components/soc/include/soc/ana_cmpr_periph.h @@ -0,0 +1,26 @@ +/* + * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include +#include "soc/soc_caps.h" +#include "soc/ana_cmpr_channel.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + int src_gpio; + int ext_ref_gpio; +} ana_cmpr_conn_t; + +extern const ana_cmpr_conn_t ana_cmpr_io_map[SOC_ANA_CMPR_NUM]; + +#ifdef __cplusplus +} +#endif diff --git a/docs/conf_common.py b/docs/conf_common.py index 6c379cfc4f..1b6120bacb 100644 --- a/docs/conf_common.py +++ b/docs/conf_common.py @@ -127,6 +127,8 @@ ADC_DOCS = ['api-reference/peripherals/adc_oneshot.rst', 'api-reference/peripherals/adc_calibration.rst'] ADC_DMA_DOCS = ['api-reference/peripherals/adc_continuous.rst'] +ANA_CMPR_DOCS = ['api-reference/peripherals/ana_cmpr.rst'] + ESP32_DOCS = ['api-reference/system/himem.rst', 'api-guides/romconsole.rst', 'api-reference/system/ipc.rst', @@ -194,6 +196,7 @@ conditional_include_dict = {'SOC_BT_SUPPORTED':BT_DOCS, 'SOC_RTC_MEM_SUPPORTED': RTC_MEM_DOCS, 'SOC_ADC_SUPPORTED':ADC_DOCS, 'SOC_ADC_DMA_SUPPORTED':ADC_DMA_DOCS, + 'SOC_ANA_CMPR_SUPPORTED': ANA_CMPR_DOCS, 'SOC_SDM_SUPPORTED':SDM_DOCS, 'SOC_WIFI_MESH_SUPPORT':WIFI_MESH_DOCS, 'esp32':ESP32_DOCS, diff --git a/docs/doxygen/Doxyfile b/docs/doxygen/Doxyfile index 63be6bbe0f..85a656d49d 100644 --- a/docs/doxygen/Doxyfile +++ b/docs/doxygen/Doxyfile @@ -61,6 +61,8 @@ INPUT = \ $(PROJECT_PATH)/components/bt/host/nimble/esp-hci/include/esp_nimble_hci.h \ $(PROJECT_PATH)/components/bt/include/esp32/include/esp_bt.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_types.h \ $(PROJECT_PATH)/components/driver/dac/include/driver/dac_continuous.h \ $(PROJECT_PATH)/components/driver/dac/include/driver/dac_cosine.h \ $(PROJECT_PATH)/components/driver/dac/include/driver/dac_oneshot.h \ diff --git a/docs/en/api-reference/peripherals/ana_cmpr.rst b/docs/en/api-reference/peripherals/ana_cmpr.rst new file mode 100644 index 0000000000..1534f6add5 --- /dev/null +++ b/docs/en/api-reference/peripherals/ana_cmpr.rst @@ -0,0 +1,195 @@ +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"} + +Introduction +------------ + +Analog Comparator is a peripheral that can be used to compare a source signal with the internal reference voltage or an external reference signal. + +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: + +**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 + +Functional Overview +------------------- + +The following sections of this document cover the typical steps to install and operate an Analog Comparator unit: + +- `Resource Allocation <#resource-allocation>`__ - covers which parameters should be set up to get a unit handle and how to recycle the resources when it finishes working. +- `Further Configurations <#further-configurations>`__ - covers the other configurations that might need to specific and what they are used for. +- `Enable and Disable Unit <#enable-and-disable-unit>`__ - covers how to enable and disable the unit. +- `Power Management <#power-management>`__ - describes how different source clock selections can affect power consumption. +- `IRAM Safe <#iram-safe>`__ - lists which functions are supposed to work even when the cache is disabled. +- `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. + +Resource Allocation +^^^^^^^^^^^^^^^^^^^ + +An Analog Comparator unit channel is represented by :cpp:type:`ana_cmpr_handle_t`. Each unit can support either an internal or an external reference. + +To allocate the resource of the Analog Comparator unit, :cpp:func:`ana_cmpr_new_unit` need to be called to get the handle of the unit. Configurations :cpp:type:`ana_cmpr_config_t` need to be specified while allocating the unit: + +- :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::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`. + +.. code:: c + + #include "driver/ana_cmpr.h" + + ana_cmpr_handle_t cmpr = NULL; + ana_cmpr_config_t config = { + .unit = ANA_CMPR_UNIT_0, + .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_ERROR_CHECK(ana_cmpr_del_unit(cmpr)); + +Further Configurations +^^^^^^^^^^^^^^^^^^^^^^ + +- :cpp:func:`ana_cmpr_set_intl_reference` - Specify the internal reference voltage when :cpp:enumerator:`ana_cmpr_ref_source_t::ANA_CMPR_REF_SRC_INTERNAL` is selected as reference source. + +It requires :cpp:member:`ana_cmpr_internal_ref_config_t::ref_volt` to specify the voltage. The voltage related to the VDD power supply, which can only support a certain fixed percentage of VDD. Currently on {IDF_TARGET_NAME}, the internal reference voltage can be range to 0~70% VDD with a step 10%. + +.. code:: c + + #include "driver/ana_cmpr.h" + + 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)); + +- :cpp:func:`ana_cmpr_set_debounce` - Set the debounce configuration. + +It requires :cpp:member:`ana_cmpr_debounce_config_t::wait_us` to set the interrupt waiting time. The interrupt will be disabled temporary for :cpp:member:`ana_cmpr_debounce_config_t::wait_us` micro seconds, so that the frequent triggering can be avoid while the source signal crossing the reference signal. That is, the waiting time is supposed to be inverse ratio to the relative frequency between the source and reference. If the waiting time is set too short, it can't bypass the jitter totally, but if too long, the next crossing interrupt might be missed. + +.. code:: c + + #include "driver/ana_cmpr.h" + + ana_cmpr_debounce_config_t dbc_cfg = { + .wait_us = 1, + }; + ESP_ERROR_CHECK(ana_cmpr_set_debounce(cmpr, &dbc_cfg)); + +- :cpp:func:`ana_cmpr_set_cross_type` - Set the source signal cross type. + +The initial cross type is set int :cpp:func:`ana_cmpr_new_unit`, this function can update the cross type, even in ISR context. + +.. code:: c + + #include "driver/ana_cmpr.h" + + ESP_ERROR_CHECK(ana_cmpr_set_cross_type(cmpr, ANA_CMPR_CROSS_POS)); + +- :cpp:func:`ana_cmpr_register_event_callbacks` - Register the callbacks. + +Currently it supports :cpp:member:`ana_cmpr_event_callbacks_t::on_cross`, it will be called when the crossing event (specified by :cpp:member:`ana_cmpr_config_t::cross_type`) occurs. + +.. code:: c + + #include "driver/ana_cmpr.h" + + static bool IRAM_ATTR example_ana_cmpr_on_cross_callback(ana_cmpr_handle_t cmpr, + const ana_cmpr_cross_event_data_t *edata, + void *user_ctx) + { + // ... + return false; + } + 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)); + +.. note:: + + When :ref:`CONFIG_ANA_CMPR_ISR_IRAM_SAFE` is enabled, you should guarantee the callback context and involved data to be in internal RAM by add the attribute ``IRAM_ATTR``. (See more in `IRAM Safe <#iram-safe>`__) + +Enable and Disable Unit +^^^^^^^^^^^^^^^^^^^^^^^ + +- :cpp:func:`ana_cmpr_enable` - Enable the Analog Comparator unit. +- :cpp:func:`ana_cmpr_disable` - Disable the Analog Comparator unit. + +After the Analog Comparator unit is enabled and the crossing event interrupt is enabled, a power management lock will be acquired if the power management is enabled (see `Power Management <#power-management>`__). Under the **enable** state, only :cpp:func:`ana_cmpr_set_intl_reference` and :cpp:func:`ana_cmpr_set_debounce` can be called, other functions can only be called after the unit is disabled. + +Calling :cpp:func:`ana_cmpr_disable` will do the opposite. + +Power Management +^^^^^^^^^^^^^^^^ + +When power management is enabled (i.e. :ref:`CONFIG_PM_ENABLE` is on), the system will adjust the APB frequency before going into light sleep, thus potentially changing the resolution of the Analog Comparator. + +However, the driver can prevent the system from changing APB frequency by acquiring a power management lock of type :cpp:enumerator:`ESP_PM_NO_LIGHT_SLEEP`. Whenever the driver creates a Analog Comparator unit instance that has selected the clock source like :cpp:enumerator:`ANA_CMPR_CLK_SRC_DEFAULT` or :cpp:enumerator:`ANA_CMPR_CLK_SRC_XTAL` as its clock source, the driver will guarantee that the power management lock is acquired when enable the channel by :cpp:func:`ana_cmpr_enable`. Likewise, the driver releases the lock when :cpp:func:`ana_cmpr_disable` is called for that channel. + +IRAM Safe +^^^^^^^^^ + +By default, the Analog Comparator interrupt will be deferred when the Cache is disabled for reasons like programming/erasing Flash. Thus the alarm interrupt will not get executed in time, which is not expected in a real-time application. + +There's a Kconfig option :ref:`CONFIG_ANA_CMPR_ISR_IRAM_SAFE` that will: + +1. Enable the interrupt being serviced even when cache is disabled +2. Place all functions that used by the ISR into IRAM [1]_ +3. Place driver object into DRAM (in case it's allocated on PSRAM) + +This will allow the interrupt to run while the cache is disabled but will come at the cost of increased IRAM consumption. + +There's a Kconfig option :ref:`CONFIG_ANA_CMPR_CTRL_FUNC_IN_IRAM` that can put commonly used IO control functions into IRAM as well. So that these functions can also be executable when the cache is disabled. These IO control functions are listed as follows: + +- :cpp:func:`ana_cmpr_set_internal_reference` +- :cpp:func:`ana_cmpr_set_debounce` +- :cpp:func:`ana_cmpr_set_cross_type` + +Thread Safety +^^^^^^^^^^^^^ + +The factory function :cpp:func:`ana_cmpr_new_unit` is guaranteed to be thread safe by the driver, which means, user can call it from different RTOS tasks without protection by extra locks. +The following functions are allowed to run under ISR context, the driver uses a critical section to prevent them being called concurrently in both task and ISR. + +- :cpp:func:`ana_cmpr_set_internal_reference` +- :cpp:func:`ana_cmpr_set_debounce` +- :cpp:func:`ana_cmpr_set_cross_type` + +Other functions that take the :cpp:type:`ana_cmpr_handle_t` as the first positional parameter, are not treated as thread safe. Which means the user should avoid calling them from multiple tasks. + +Kconfig Options +^^^^^^^^^^^^^^^ + +- :ref:`CONFIG_ANA_CMPR_ISR_IRAM_SAFE` controls whether the default ISR handler can work when cache is disabled, see `IRAM Safe <#iram-safe>`__ for more information. +- :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 will increase the firmware binary size. + +Application Example +------------------- + +* :example:`peripherals/analog_comparator` shows the basic usage of the analog comparator, and other potential usages like hysteresis comparator and SPWM generator. + +API Reference +------------- + +.. include-build-file:: inc/ana_cmpr.inc +.. include-build-file:: inc/ana_cmpr_types.inc + +.. [1] + :cpp:member:`ana_cmpr_event_callbacks_t::on_cross` callback and the functions invoked by itself should also be placed in IRAM, you need to take care of them by themselves. diff --git a/docs/en/api-reference/peripherals/index.rst b/docs/en/api-reference/peripherals/index.rst index eeedfff718..5fd46d82e1 100644 --- a/docs/en/api-reference/peripherals/index.rst +++ b/docs/en/api-reference/peripherals/index.rst @@ -9,6 +9,7 @@ Peripherals API :SOC_ADC_SUPPORTED: adc_oneshot :SOC_ADC_DMA_SUPPORTED: adc_continuous :SOC_ADC_SUPPORTED: adc_calibration + :SOC_ANA_CMPR_SUPPORTED: ana_cmpr clk_tree :SOC_DAC_SUPPORTED: dac :SOC_ETM_SUPPORTED: etm diff --git a/docs/zh_CN/api-reference/peripherals/ana_cmpr.rst b/docs/zh_CN/api-reference/peripherals/ana_cmpr.rst new file mode 100644 index 0000000000..ab03d327dd --- /dev/null +++ b/docs/zh_CN/api-reference/peripherals/ana_cmpr.rst @@ -0,0 +1 @@ +.. include:: ../../../en/api-reference/peripherals/ana_cmpr.rst diff --git a/docs/zh_CN/api-reference/peripherals/index.rst b/docs/zh_CN/api-reference/peripherals/index.rst index 6accad66da..7b436e0553 100644 --- a/docs/zh_CN/api-reference/peripherals/index.rst +++ b/docs/zh_CN/api-reference/peripherals/index.rst @@ -9,6 +9,7 @@ :SOC_ADC_SUPPORTED: adc_oneshot :SOC_ADC_DMA_SUPPORTED: adc_continuous :SOC_ADC_SUPPORTED: adc_calibration + :SOC_ANA_CMPR_SUPPORTED: ana_cmpr clk_tree :SOC_DAC_SUPPORTED: dac :SOC_ETM_SUPPORTED: etm diff --git a/examples/peripherals/.build-test-rules.yml b/examples/peripherals/.build-test-rules.yml index 6d8c66f57d..28b1931547 100644 --- a/examples/peripherals/.build-test-rules.yml +++ b/examples/peripherals/.build-test-rules.yml @@ -8,6 +8,10 @@ examples/peripherals/adc/oneshot_read: disable: - if: SOC_ADC_SUPPORTED != 1 +examples/peripherals/analog_comparator: + disable: + - if: SOC_ANA_CMPR_SUPPORTED != 1 + examples/peripherals/dac: disable: - if: SOC_DAC_SUPPORTED != 1 diff --git a/examples/peripherals/analog_comparator/CMakeLists.txt b/examples/peripherals/analog_comparator/CMakeLists.txt new file mode 100644 index 0000000000..fa9f99dcd0 --- /dev/null +++ b/examples/peripherals/analog_comparator/CMakeLists.txt @@ -0,0 +1,8 @@ +# For more information about build system see +# https://docs.espressif.com/projects/esp-idf/en/latest/api-guides/build-system.html +# The following five lines of boilerplate have to be in your project's +# CMakeLists in this exact order for cmake to work correctly +cmake_minimum_required(VERSION 3.16) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +project(analog_comparator_example) diff --git a/examples/peripherals/analog_comparator/README.md b/examples/peripherals/analog_comparator/README.md new file mode 100644 index 0000000000..8a6adb523c --- /dev/null +++ b/examples/peripherals/analog_comparator/README.md @@ -0,0 +1,144 @@ +| Supported Targets | ESP32-H2 | +| ----------------- | -------- | + +# 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. + +## How to Use Example + +### Hardware Requirement + +* A development board with any supported Espressif SOC chip (see `Supported Targets` table above) +* A USB cable for power supply and programming +* A device to generate the source signal. For example, you can use a ESP SoC that support DAC peripheral (like ESP32 or ESP32S2) to generate source signal, or just use a signal generator. + +#### Required Additionally for External Reference + +* One more external signal for the reference. You can input a static voltage or a wave, for example, the static voltage can be gotten by the resistor divider, and the wave can be generated by either ESP SoC or a signal generator. + +### Example Connection + +Let's take ESP32-H2 and ESP32 for example, and we use the DAC on ESP32 as the signal generator (you can use a real signal generator instead if you have one). + +#### Internal Reference + +Download this example into ESP32-H2 and any DAC examples in `examples/peripherals/dac` directory into ESP32. + +``` + ┌──────────────┐ ┌──────────────┐ + │ ESP32-H2 │ │ ESP32 │ + │ │ source signal │ │ +┌────┤GPIO0 GPIO11│◄────┬──────────┤GPIO25 │ +│ │ │ │ │ │ +│ │ GND├─────┼────┬─────┤GND │ +│ │ │ │ │ │ │ +│ └──────────────┘ │ │ └──────────────┘ +│ │ │ +│ ┌──────────────┐ │ │ +│ │ Oscilloscope │ │ │ +│ │ │ │ │ +└───►│Probe1 Probe2│◄────┘ │ + │ │ │ + │ GND├──────────┘ + │ │ + └──────────────┘ +``` + +#### External Reference + +For the static external reference, we can use resistor divider to get the static voltage. + +``` + ┌──────────────┐ ┌──────────────┐ + │ ESP32-H2 │ │ ESP32 │ + │ │ source signal │ │ +┌────┤GPIO0 GPIO11│◄────┬──────────┤GPIO25 │ +│ │ │ ref│signal │ │ +│ │ GPIO10│◄────┼──────┐ ┌─┤GND │ +│ │ │ │ │ │ │ │ +│ │ GND├─────┼─┬────┼─┘ └──────────────┘ +│ │ │ │ │ │ VDD +│ └──────────────┘ │ │ │ ─┬─ +│ │ │ │ │ +│ │ │ │ ├┐ +│ ┌──────────────┐ │ │ │ ││R1 +│ │ Oscilloscope │ │ │ │ ├┘ +│ │ │ │ │ └──────────┤ +└───►│Probe1 Probe2│◄────┘ │ │ + │ │ │ ├┐ + │ GND├───────┤ ││R2 + │ │ │ ├┘ + └──────────────┘ │ │ + └───────────────┤ + │ + ─┴─ + GND +``` + +Also, we can generate a different signal on another DAC channel on ESP32, you can customize your DAC wave using `examples/peripherals/dac/dac_continuous/signal_generator` example. + +``` + ┌──────────────┐ ┌──────────────┐ + │ ESP32-H2 │ │ ESP32 │ + │ │ source signal │ │ +┌────┤GPIO0 GPIO11│◄────┬──────────┤GPIO25 │ +│ │ │ ref│signal │ │ +│ │ GPIO10│◄────┼──────────┤GPIO26 │ +│ │ │ │ │ │ +│ │ GND├─────┼───┬──────┤GND │ +│ │ │ │ │ │ │ +│ └──────────────┘ │ │ └──────────────┘ +│ │ │ +│ │ │ +│ ┌──────────────┐ │ │ +│ │ Oscilloscope │ │ │ +│ │ │ │ │ +└───►│Probe1 Probe2│◄────┘ │ + │ │ │ + │ GND├─────────┘ + │ │ + └──────────────┘ +``` + +### Configure the Project + +You can decide to adopt internal reference or external reference in the example menu config, and you can also enable hysteresis comparator for the internal reference in the menu config. + +### Build and Flash + +Build the project and flash it to the board, then run monitor tool to view serial output: + +``` +idf.py -p PORT build flash monitor +``` + +(To exit the serial monitor, type ``Ctrl-]``.) + +See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects. + +## Example Output + +### Static Internal Reference + +The internal voltage is set to 50% VDD, and input a 50 Hz sine wave as source signal (blue line), the output GPIO toggles every time the sine wave crossing the reference voltage (yellow line) + +![static_intl_ref](./static_50p_ref.png) + +### Hysteresis Internal Reference + +The internal voltage is set to 30% VDD and 70% VDD alternately in this case, the source signal is a 100 Hz sine wave (blue line), the output GPIO toggles every time the source signal exceed 70% VDD and lower than 30% VDD. + +![hysteresis_cmpr](./hysteresis_ref.png) + +### External Reference + +Here we input a 100 Hz sine wave (blue line) as the source signal and input a 1 KHz triangle wave as the reference signal, the output wave (yellow line) can be regarded as a SPWM (Sinusoidal PWM) wave. + +![ext_ref](./ext_ref.png) + +## Troubleshooting + +For any technical queries, please open an [issue](https://github.com/espressif/esp-idf/issues) on GitHub. We will get back to you soon. diff --git a/examples/peripherals/analog_comparator/ext_ref.png b/examples/peripherals/analog_comparator/ext_ref.png new file mode 100644 index 0000000000..e73485331e Binary files /dev/null and b/examples/peripherals/analog_comparator/ext_ref.png differ diff --git a/examples/peripherals/analog_comparator/hysteresis_ref.png b/examples/peripherals/analog_comparator/hysteresis_ref.png new file mode 100644 index 0000000000..e2707f6db7 Binary files /dev/null and b/examples/peripherals/analog_comparator/hysteresis_ref.png differ diff --git a/examples/peripherals/analog_comparator/main/CMakeLists.txt b/examples/peripherals/analog_comparator/main/CMakeLists.txt new file mode 100644 index 0000000000..e017eb4889 --- /dev/null +++ b/examples/peripherals/analog_comparator/main/CMakeLists.txt @@ -0,0 +1,2 @@ +idf_component_register(SRCS "ana_cmpr_example_main.c" + INCLUDE_DIRS ".") diff --git a/examples/peripherals/analog_comparator/main/Kconfig.projbuild b/examples/peripherals/analog_comparator/main/Kconfig.projbuild new file mode 100644 index 0000000000..1e2f780b37 --- /dev/null +++ b/examples/peripherals/analog_comparator/main/Kconfig.projbuild @@ -0,0 +1,28 @@ +menu "Analog Comparator Example Configuration" + + choice EXAMPLE_REFERENCE_SOURCE + prompt "Analog Comparator reference source" + default EXAMPLE_INTERNAL_REF + help + Decide the reference signal comes from internal or external + + config EXAMPLE_INTERNAL_REF + bool "Internal reference" + help + The source signal will refer to an internal voltage, which related to VDD. + + config EXAMPLE_EXTERNAL_REF + bool "External reference" + help + The source signal will refer to the external signal on a specific GPIO. + endchoice + + config EXAMPLE_HYSTERESIS_COMPARATOR + depends on EXAMPLE_INTERNAL_REF + bool "Enable hysteresis comparator" + default n + help + The internal reference voltage will be set to 30% VDD and 70% VDD alternately + every time the interrupt triggered. + +endmenu diff --git a/examples/peripherals/analog_comparator/main/ana_cmpr_example_main.c b/examples/peripherals/analog_comparator/main/ana_cmpr_example_main.c new file mode 100644 index 0000000000..dff4003724 --- /dev/null +++ b/examples/peripherals/analog_comparator/main/ana_cmpr_example_main.c @@ -0,0 +1,140 @@ +/* + * 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" + +#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 +} + +void example_init_monitor_gpio(void) +{ + gpio_config_t io_conf = { + .intr_type = GPIO_INTR_DISABLE, + .mode = GPIO_MODE_OUTPUT, + .pin_bit_mask = (1ULL << EXAMPLE_MONITOR_GPIO_NUM), + .pull_down_en = false, + .pull_up_en = false, + }; + gpio_config(&io_conf); + gpio_set_level(EXAMPLE_MONITOR_GPIO_NUM, 0); +} + +void app_main(void) +{ + /* Initialize GPIO to monitor the comparator interrupt */ + example_init_monitor_gpio(); + /* Initialize Analog Comparator */ + example_init_analog_comparator(); +} diff --git a/examples/peripherals/analog_comparator/pytest_ana_cmpr_example.py b/examples/peripherals/analog_comparator/pytest_ana_cmpr_example.py new file mode 100644 index 0000000000..edd9e49067 --- /dev/null +++ b/examples/peripherals/analog_comparator/pytest_ana_cmpr_example.py @@ -0,0 +1,26 @@ +# SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD +# SPDX-License-Identifier: CC0-1.0 + +import pytest +from pytest_embedded import Dut + + +@pytest.mark.esp32h2 +@pytest.mark.generic +@pytest.mark.parametrize( + 'config', + [ + 'intl', + 'ext', + ], + indirect=True, +) +def test_ana_cmpr_example(dut: Dut) -> None: + sdkconfig = dut.app.sdkconfig + dut.expect('ana_cmpr_example: Analog Comparator source gpio 11, external reference gpio 10', timeout=10) + if sdkconfig['EXAMPLE_INTERNAL_REF']: + dut.expect('ana_cmpr_example: Allocate Analog Comparator with internal reference', timeout=10) + dut.expect(r'ana_cmpr_example: Analog comparator enabled, reference voltage: [0-9]+% \* VDD', timeout=10) + elif sdkconfig['EXAMPLE_EXTERNAL_REF']: + dut.expect('ana_cmpr_example: Allocate Analog Comparator with external reference', timeout=10) + dut.expect('ana_cmpr_example: Analog comparator enabled, external reference selected', timeout=10) diff --git a/examples/peripherals/analog_comparator/sdkconfig.ci.ext b/examples/peripherals/analog_comparator/sdkconfig.ci.ext new file mode 100644 index 0000000000..3ec04884ce --- /dev/null +++ b/examples/peripherals/analog_comparator/sdkconfig.ci.ext @@ -0,0 +1,2 @@ +CONFIG_EXAMPLE_INTERNAL_REF=n +CONFIG_EXAMPLE_EXTERNAL_REF=y diff --git a/examples/peripherals/analog_comparator/sdkconfig.ci.intl b/examples/peripherals/analog_comparator/sdkconfig.ci.intl new file mode 100644 index 0000000000..ae9cdfdb82 --- /dev/null +++ b/examples/peripherals/analog_comparator/sdkconfig.ci.intl @@ -0,0 +1,2 @@ +CONFIG_EXAMPLE_INTERNAL_REF=y +CONFIG_EXAMPLE_EXTERNAL_REF=n diff --git a/examples/peripherals/analog_comparator/static_50p_ref.png b/examples/peripherals/analog_comparator/static_50p_ref.png new file mode 100644 index 0000000000..202b0ad680 Binary files /dev/null and b/examples/peripherals/analog_comparator/static_50p_ref.png differ diff --git a/examples/peripherals/dac/dac_cosine_wave/main/dac_cosine_example_main.c b/examples/peripherals/dac/dac_cosine_wave/main/dac_cosine_example_main.c index 99b21d6cd5..5616794465 100644 --- a/examples/peripherals/dac/dac_cosine_wave/main/dac_cosine_example_main.c +++ b/examples/peripherals/dac/dac_cosine_wave/main/dac_cosine_example_main.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: CC0-1.0 */