refactor(ana_cmpr): enhanced the driver implementation

This commit is contained in:
morris
2025-04-10 16:07:53 +08:00
parent 771e9db73e
commit 2b5d91ad1c
44 changed files with 609 additions and 416 deletions

View File

@ -1,24 +1,45 @@
menu "ESP-Driver:Analog Comparator Configurations"
depends on SOC_ANA_CMPR_SUPPORTED
config ANA_CMPR_ISR_IRAM_SAFE
bool "Analog comparator ISR IRAM-Safe"
default n
config ANA_CMPR_ISR_HANDLER_IN_IRAM
bool "Place Analog Comparator ISR handler in IRAM to reduce latency"
default y
select ANA_CMPR_OBJ_CACHE_SAFE
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).
Place Analog Comparator ISR handler in IRAM to reduce latency caused by cache miss.
config ANA_CMPR_CTRL_FUNC_IN_IRAM
bool "Place Analog Comparator control functions into IRAM"
default n
select ANA_CMPR_OBJ_CACHE_SAFE
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"
config ANA_CMPR_ISR_CACHE_SAFE
bool "Allow Analog Comparator ISR to execute when cache is disabled"
select ANA_CMPR_ISR_HANDLER_IN_IRAM
default n
help
whether 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.
Enable this option to allow the Analog Comparator Interrupt Service Routine (ISR)
to execute even when the cache is disabled. This can be useful in scenarios where the cache
might be turned off, but the comparator functionality is still required to operate correctly.
config ANA_CMPR_OBJ_CACHE_SAFE
bool
default n
help
This will ensure the driver object will not be allocated from a memory region
where its cache can be disabled.
config ANA_CMPR_ENABLE_DEBUG_LOG
bool "Force enable debug log"
default n
help
If enabled, the driver component will:
1. ignore the global logging settings
2. compile all log messages into the binary
3. set the runtime log level to VERBOSE
Please enable this option by caution, as it will increase the binary size.
endmenu # Analog Comparator Configuration

View File

@ -4,62 +4,36 @@
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdlib.h>
#include <inttypes.h>
#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 "esp_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/gpio.h"
#include "esp_private/io_mux.h"
#include "esp_private/esp_clk.h"
#include "ana_cmpr_private.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 */
_Atomic ana_cmpr_fsm_t fsm; /*!< The state machine of the Analog Comparator unit */
ana_cmpr_event_callbacks_t cbs; /*!< The callback group that set by user */
void *user_data; /*!< User data that passed to the callbacks */
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 */
};
/* 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) >= 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_IRAM)
#else
#define ANA_CMPR_MEM_ALLOC_CAPS MALLOC_CAP_DEFAULT
#define ANA_CMPR_INTR_FLAG (0)
#endif
/* Driver tag */
static const char *TAG = "ana_cmpr";
#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) >= 0 && (unit) < SOC_ANA_CMPR_NUM, ESP_ERR_INVALID_ARG, TAG, "invalid unit number")
/* Global static object of the Analog Comparator unit */
static ana_cmpr_handle_t s_ana_cmpr[SOC_ANA_CMPR_NUM] = {
@ -69,10 +43,10 @@ static ana_cmpr_handle_t s_ana_cmpr[SOC_ANA_CMPR_NUM] = {
/* Global spin lock */
static portMUX_TYPE s_spinlock = portMUX_INITIALIZER_UNLOCKED;
static void IRAM_ATTR s_ana_cmpr_default_intr_handler(void *usr_data)
void 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_handle_t cmpr_handle = (ana_cmpr_handle_t)usr_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);
@ -80,6 +54,7 @@ static void IRAM_ATTR s_ana_cmpr_default_intr_handler(void *usr_data)
/* Call the user callback function if it is specified and the corresponding event triggers*/
if (cmpr_handle->cbs.on_cross && (status & cmpr_handle->intr_mask)) {
// some chip can distinguish the edge of the cross event
#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;
@ -103,61 +78,74 @@ static esp_err_t s_ana_cmpr_init_gpio(ana_cmpr_handle_t cmpr, bool is_external_r
return err;
}
static void ana_cmpr_destroy_unit(ana_cmpr_handle_t cmpr)
{
if (cmpr->pm_lock) {
esp_pm_lock_delete(cmpr->pm_lock);
}
if (cmpr->intr_handle) {
esp_intr_free(cmpr->intr_handle);
}
free(cmpr);
}
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
esp_err_t ret = ESP_OK;
ana_cmpr_handle_t ana_cmpr_hdl = NULL;
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(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;
/* 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(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;
#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
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");
ESP_RETURN_ON_FALSE(!s_ana_cmpr[unit], ESP_ERR_INVALID_STATE, TAG, "unit has been allocated already");
if (config->intr_priority) {
ESP_RETURN_ON_FALSE(1 << (config->intr_priority) & ANA_CMPR_ALLOW_INTR_PRIORITY_MASK, ESP_ERR_INVALID_ARG,
TAG, "invalid interrupt priority:%d", config->intr_priority);
}
/* 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,
&s_ana_cmpr[unit]->src_clk_freq_hz),
// analog comparator unit must be allocated from internal memory because it contains atomic variable
ana_cmpr_hdl = heap_caps_calloc(1, sizeof(struct ana_cmpr_t), MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT);
ESP_RETURN_ON_FALSE(ana_cmpr_hdl, ESP_ERR_NO_MEM, TAG, "no memory for analog comparator object");
/* Assign analog comparator unit */
ana_cmpr_hdl->dev = ANALOG_CMPR_LL_GET_HW(unit);
ana_cmpr_hdl->unit = unit;
ana_cmpr_hdl->intr_priority = config->intr_priority;
atomic_init(&ana_cmpr_hdl->fsm, ANA_CMPR_FSM_INIT);
ana_cmpr_clk_src_t clk_src = config->clk_src ? config->clk_src : ANA_CMPR_CLK_SRC_DEFAULT;
// Analog comparator located in the IO MUX, but IO MUX clock might be shared with other submodules as well, check if there's conflict
ESP_GOTO_ON_ERROR(io_mux_set_clock_source((soc_module_clk_t)clk_src), err, TAG, "clock source conflicts with other IOMUX consumers");
ESP_GOTO_ON_ERROR(esp_clk_tree_src_get_freq_hz((soc_module_clk_t)clk_src, ESP_CLK_TREE_SRC_FREQ_PRECISION_CACHED, &ana_cmpr_hdl->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");
#if CONFIG_PM_ENABLE
// Create PM lock, because the light sleep may disable the clock and power domain used by the analog comparator
// TODO: IDF-12818
ret = esp_pm_lock_create(ESP_PM_NO_LIGHT_SLEEP, 0, ana_cmpr_periph[unit].module_name, &ana_cmpr_hdl->pm_lock);
ESP_GOTO_ON_ERROR(ret, err, TAG, "create NO_LIGHT_SLEEP lock failed");
#endif
/* 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_ref_source(ana_cmpr_hdl->dev, config->ref_src);
ana_cmpr_hdl->ref_src = config->ref_src;
#if !SOC_ANA_CMPR_CAN_DISTINGUISH_EDGE
analog_cmpr_ll_set_cross_type(s_ana_cmpr[unit]->dev, config->cross_type);
// set which cross type can trigger the interrupt
analog_cmpr_ll_set_intr_cross_type(ana_cmpr_hdl->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);
// record the interrupt mask, the interrupt will be lazy installed when register user callbacks
// different cross type means different interrupt mask
ana_cmpr_hdl->intr_mask = analog_cmpr_ll_get_intr_mask_by_type(ana_cmpr_hdl->dev, config->cross_type);
// different unit share the same interrupt register, so using a spin lock to protect it
portENTER_CRITICAL(&s_spinlock);
// disable the interrupt by default, and clear pending status
analog_cmpr_ll_enable_intr(ana_cmpr_hdl->dev, ANALOG_CMPR_LL_ALL_INTR_MASK(unit), false);
analog_cmpr_ll_clear_intr(ana_cmpr_hdl->dev, ANALOG_CMPR_LL_ALL_INTR_MASK(unit));
portEXIT_CRITICAL(&s_spinlock);
// GPIO configuration
ESP_GOTO_ON_ERROR(s_ana_cmpr_init_gpio(ana_cmpr_hdl, config->ref_src == ANA_CMPR_REF_SRC_EXTERNAL), err, TAG, "failed to initialize GPIO");
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_periph[unit].src_gpio);
@ -166,12 +154,15 @@ esp_err_t ana_cmpr_new_unit(const ana_cmpr_config_t *config, ana_cmpr_handle_t *
(int)unit, ana_cmpr_periph[unit].src_gpio, ana_cmpr_periph[unit].ext_ref_gpio);
}
*ret_cmpr = s_ana_cmpr[unit];
// register the analog comparator unit to the global object array
s_ana_cmpr[unit] = ana_cmpr_hdl;
*ret_cmpr = ana_cmpr_hdl;
return ESP_OK;
err:
/* Delete the unit if allocation failed */
ana_cmpr_del_unit(s_ana_cmpr[unit]);
if (ana_cmpr_hdl) {
ana_cmpr_destroy_unit(ana_cmpr_hdl);
}
return ret;
}
@ -186,22 +177,12 @@ esp_err_t ana_cmpr_del_unit(ana_cmpr_handle_t cmpr)
break;
}
}
ESP_RETURN_ON_FALSE(unit != -1, 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");
ESP_RETURN_ON_FALSE(unit != -1, ESP_ERR_INVALID_ARG, TAG, "unregistered unit handle");
ESP_RETURN_ON_FALSE(atomic_load(&cmpr->fsm) == ANA_CMPR_FSM_INIT, ESP_ERR_INVALID_STATE, TAG, "not in init state");
/* 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]);
ana_cmpr_destroy_unit(cmpr);
// unregister it from the global object array
s_ana_cmpr[unit] = NULL;
ESP_LOGD(TAG, "unit %d deleted", (int)unit);
return ESP_OK;
@ -212,15 +193,13 @@ esp_err_t ana_cmpr_set_internal_reference(ana_cmpr_handle_t cmpr, const ana_cmpr
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");
TAG, "the reference voltage does not come from internal");
/* Set internal reference voltage */
// the underlying register may be accessed by different threads at the same time, so use spin lock to protect it
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, (uint32_t)ref_cfg->ref_volt);
return ESP_OK;
}
@ -230,14 +209,12 @@ esp_err_t ana_cmpr_set_debounce(ana_cmpr_handle_t cmpr, const ana_cmpr_debounce_
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 */
uint32_t wait_cycle = dbc_cfg->wait_us * (cmpr->src_clk_freq_hz / 1000000);
// the underlying register may be accessed by different threads at the same time, so use spin lock to protect it
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;
}
@ -255,14 +232,10 @@ esp_err_t ana_cmpr_set_cross_type(ana_cmpr_handle_t cmpr, ana_cmpr_cross_type_t
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
analog_cmpr_ll_set_intr_cross_type(cmpr->dev, cross_type);
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
}
@ -271,82 +244,78 @@ esp_err_t ana_cmpr_register_event_callbacks(ana_cmpr_handle_t cmpr, const ana_cm
{
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
ESP_RETURN_ON_FALSE(atomic_load(&cmpr->fsm) == ANA_CMPR_FSM_INIT, ESP_ERR_INVALID_STATE, TAG, "not in init state");
#if CONFIG_ANA_CMPR_ISR_CACHE_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");
ESP_RETURN_ON_FALSE(esp_ptr_in_iram(cbs->on_cross), ESP_ERR_INVALID_ARG, TAG, "on_cross 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");
ESP_RETURN_ON_FALSE(esp_ptr_internal(user_data), ESP_ERR_INVALID_ARG, TAG, "user_data is not in internal RAM");
}
#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
int intr_flags = ANA_CMPR_INTR_FLAG | ((cmpr->intr_priority > 0) ? BIT(cmpr->intr_priority) : ESP_INTR_FLAG_LOWMED);
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");
cmpr->intr_mask, ana_cmpr_default_intr_handler, cmpr, &cmpr->intr_handle),
TAG, "allocate interrupt failed");
}
/* Save the callback group */
/* Save the callback functions */
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);
ESP_LOGV(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;
ana_cmpr_fsm_t expected_fsm = ANA_CMPR_FSM_INIT;
if (atomic_compare_exchange_strong(&cmpr->fsm, &expected_fsm, ANA_CMPR_FSM_WAIT)) {
if (cmpr->pm_lock) {
esp_pm_lock_acquire(cmpr->pm_lock);
}
/* 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");
// the underlying register may be accessed by different threads at the same time, so use spin lock to protect it
portENTER_CRITICAL(&s_spinlock);
analog_cmpr_ll_enable_intr(cmpr->dev, cmpr->intr_mask, true);
analog_cmpr_ll_enable(cmpr->dev, true);
portEXIT_CRITICAL(&s_spinlock);
// switch the state machine to enable state
atomic_store(&cmpr->fsm, ANA_CMPR_FSM_ENABLE);
ESP_LOGD(TAG, "unit %d enabled", (int)cmpr->unit);
} else {
ESP_RETURN_ON_FALSE(false, ESP_ERR_INVALID_STATE, TAG, "not in init state");
}
/* Enable the Analog Comparator */
portENTER_CRITICAL(&s_spinlock);
analog_cmpr_ll_enable_intr(cmpr->dev, cmpr->intr_mask, true);
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, cmpr->intr_mask, false);
analog_cmpr_ll_enable(cmpr->dev, false);
portEXIT_CRITICAL(&s_spinlock);
ana_cmpr_fsm_t expected_fsm = ANA_CMPR_FSM_ENABLE;
if (atomic_compare_exchange_strong(&cmpr->fsm, &expected_fsm, ANA_CMPR_FSM_WAIT)) {
// the underlying register may be accessed by different threads at the same time, so use spin lock to protect it
portENTER_CRITICAL(&s_spinlock);
analog_cmpr_ll_enable_intr(cmpr->dev, cmpr->intr_mask, 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");
if (cmpr->pm_lock) {
esp_pm_lock_release(cmpr->pm_lock);
}
// switch the state machine to init state
atomic_store(&cmpr->fsm, ANA_CMPR_FSM_INIT);
ESP_LOGD(TAG, "unit %d disabled", (int)cmpr->unit);
} else {
ESP_RETURN_ON_FALSE(false, ESP_ERR_INVALID_STATE, TAG, "not enabled yet");
}
/* Update the driver status */
cmpr->is_enabled = false;
ESP_LOGD(TAG, "unit %d disabled", (int)cmpr->unit);
return ESP_OK;
}
@ -371,10 +340,18 @@ 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)
ana_cmpr_unit_t ana_cmpr_get_unit_id(ana_cmpr_handle_t cmpr)
{
if (!cmpr) {
return -1;
}
return cmpr->unit;
}
#if CONFIG_ANA_CMPR_ENABLE_DEBUG_LOG
__attribute__((constructor))
static void ana_cmpr_override_default_log_level(void)
{
esp_log_level_set(TAG, ESP_LOG_DEBUG);
}
#endif

View File

@ -1,30 +1,13 @@
/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdlib.h>
#include <string.h>
#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 {
@ -35,18 +18,14 @@ 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);
ana_cmpr_unit_t unit = ana_cmpr_get_unit_id(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);

View File

@ -1,19 +1,67 @@
/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include <inttypes.h>
#include <stdatomic.h>
#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 "esp_log.h"
#include "esp_check.h"
#include "esp_heap_caps.h"
#include "esp_intr_alloc.h"
#include "driver/ana_cmpr_types.h"
#include "soc/soc_caps.h"
#include "hal/ana_cmpr_ll.h"
#include "soc/ana_cmpr_periph.h"
#define TAG "ana_cmpr"
#if CONFIG_ANA_CMPR_OBJ_CACHE_SAFE
#define ANA_CMPR_MEM_ALLOC_CAPS (MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT)
#else
#define ANA_CMPR_MEM_ALLOC_CAPS MALLOC_CAP_DEFAULT
#endif
#if CONFIG_ANA_CMPR_ISR_CACHE_SAFE
// the interrupt source of Analog Comparator is marked as shared because
// it may share with other GPIO interrupt events
// it may share with different analog comparator units
#define ANA_CMPR_INTR_FLAG (ESP_INTR_FLAG_IRAM | ESP_INTR_FLAG_SHARED)
#else
#define ANA_CMPR_INTR_FLAG (ESP_INTR_FLAG_SHARED)
#endif
#define ANA_CMPR_ALLOW_INTR_PRIORITY_MASK ESP_INTR_FLAG_LOWMED
#ifdef __cplusplus
extern "C" {
#endif
ana_cmpr_unit_t ana_cmpr_priv_get_unit_by_handle(ana_cmpr_handle_t cmpr);
typedef enum {
ANA_CMPR_FSM_INIT, // Comparator is in the initialization stage, not enabled yet
ANA_CMPR_FSM_ENABLE, // Comparator is enabled
ANA_CMPR_FSM_WAIT, // Comparator is in the middle of state change, so busy, other operations should wait
} ana_cmpr_fsm_t;
/**
* @brief Get the analog comparator unit id from the handle
*
* @param cmpr The handle of the analog comparator unit
* @return The id of the analog comparator unit
*/
ana_cmpr_unit_t ana_cmpr_get_unit_id(ana_cmpr_handle_t cmpr);
#ifdef __cplusplus
}

View File

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@ -15,7 +15,6 @@ extern "C" {
/**
* @brief Analog comparator unit configuration
*
*/
typedef struct {
ana_cmpr_unit_t unit; /*!< Analog comparator unit */
@ -28,17 +27,15 @@ 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 */
int intr_priority; /*!< The interrupt priority, range 1~3.
If set to 0, the driver will automatically select a relative low priority (1,2,3) */
struct {
uint32_t io_loop_back: 1; /*!< Enable this field when the other signals that output on the comparison pins are supposed to be fed back.
* Normally used for debug/test scenario */
uint32_t io_loop_back: 1; /*!< Deprecated. For debug/test, a signal output from other peripheral can work as comparator input. */
} flags; /*!< Analog comparator driver flags */
} 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
@ -48,21 +45,18 @@ typedef struct {
/**
* @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)
*/
uint32_t wait_us; /*!< The wait time to prevent frequent interrupts caused by signal noise or bouncing.
During the specified wait_us period, no new interrupts will be triggered.
Set the value according to the signal characteristics. A rapid signal requires a small wait time,
otherwise the next cross event may be missed. */
} 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.
* @note When CONFIG_ANA_CMPR_ISR_CACHE_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 {
@ -95,9 +89,8 @@ 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 only need to be called when `ana_cmpr_config_t::ref_src` is set to `ANA_CMPR_REF_SRC_INTERNAL`.
* @note This function is allowed to run within ISR context including interrupt 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
*
@ -112,7 +105,7 @@ esp_err_t ana_cmpr_set_internal_reference(ana_cmpr_handle_t cmpr, const ana_cmpr
/**
* @brief Set debounce configuration to the analog comparator
* @note This function is allowed to run within ISR context including intr callbacks
* @note This function is allowed to run within ISR context including interrupt 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
*
@ -127,9 +120,8 @@ esp_err_t ana_cmpr_set_debounce(ana_cmpr_handle_t cmpr, const ana_cmpr_debounce_
/**
* @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
* @note This function is allowed to run within ISR context including interrupt callbacks
* @note This function must be called before `ana_cmpr_register_event_callbacks`
*
* @param[in] cmpr The handle of analog comparator unit
* @param[in] cross_type The source signal cross type that can trigger the interrupt

View File

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@ -17,7 +17,6 @@ extern "C" {
/**
* @brief Analog comparator unit
*
*/
typedef int ana_cmpr_unit_t;
@ -25,16 +24,14 @@ typedef int 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_SRC_INTERNAL, /*!< Analog Comparator reference voltage comes from internal, divided from VDD */
ANA_CMPR_REF_SRC_EXTERNAL, /*!< Analog Comparator reference voltage comes from external pin, e.g. `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 */
@ -43,27 +40,23 @@ typedef enum {
/**
* @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 {
ana_cmpr_cross_type_t cross_type; /*!< The cross type of the target signal to the reference signal.

View File

@ -5,3 +5,5 @@ entries:
ana_cmpr: ana_cmpr_set_internal_reference (noflash)
ana_cmpr: ana_cmpr_set_debounce (noflash)
ana_cmpr: ana_cmpr_set_cross_type (noflash)
if ANA_CMPR_ISR_HANDLER_IN_IRAM = y:
ana_cmpr: ana_cmpr_default_intr_handler (noflash)

View File

@ -0,0 +1,4 @@
# sdkconfig replacement configurations for deprecated options formatted as
# CONFIG_DEPRECATED_OPTION CONFIG_NEW_OPTION
CONFIG_ANA_CMPR_ISR_IRAM_SAFE CONFIG_ANA_CMPR_ISR_CACHE_SAFE

View File

@ -1,8 +1,8 @@
set(srcs "test_app_main.c"
"test_ana_cmpr_common.c"
"test_ana_cmpr_utils.c"
"test_ana_cmpr.c")
if(CONFIG_ANA_CMPR_ISR_IRAM_SAFE)
if(CONFIG_ANA_CMPR_ISR_CACHE_SAFE)
list(APPEND srcs "test_ana_cmpr_iram.c")
endif()
@ -13,5 +13,5 @@ endif()
idf_component_register(SRCS ${srcs}
INCLUDE_DIRS "."
PRIV_REQUIRES unity esp_driver_gpio esp_driver_ana_cmpr esp_driver_gptimer
PRIV_REQUIRES unity esp_driver_gpio esp_driver_ana_cmpr esp_driver_gptimer esp_pm
WHOLE_ARCHIVE)

View File

@ -1,12 +1,12 @@
/*
* SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2023-2025 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]")
TEST_CASE("ana_cmpr unit install/uninstall", "[ana_cmpr]")
{
ana_cmpr_handle_t cmpr = NULL;
ana_cmpr_config_t config = {
@ -45,10 +45,11 @@ TEST_CASE("ana_cmpr_unit_install_uninstall", "[ana_cmpr]")
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_ERR(ESP_ERR_INVALID_ARG, ana_cmpr_del_unit(NULL));
TEST_ESP_OK(ana_cmpr_del_unit(cmpr));
}
TEST_CASE("ana_cmpr_internal_reference", "[ana_cmpr]")
TEST_CASE("ana_cmpr internal reference", "[ana_cmpr]")
{
uint32_t cnt = 0;
ana_cmpr_handle_t cmpr = NULL;
@ -57,28 +58,29 @@ TEST_CASE("ana_cmpr_internal_reference", "[ana_cmpr]")
.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));
int src_chan = test_init_src_chan_gpio(TEST_ANA_CMPR_UNIT_ID);
int src_chan_io = test_init_src_chan_gpio(TEST_ANA_CMPR_UNIT_ID, 0);
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,
.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,
};
printf("register ana_cmpr event callbacks\r\n");
TEST_ESP_OK(ana_cmpr_register_event_callbacks(cmpr, &cbs, &cnt));
TEST_ESP_OK(ana_cmpr_enable(cmpr));
cnt = 0;
for (uint32_t i = 1; i <= 10; i++) {
test_simulate_src_signal(src_chan, i % 2);
esp_rom_delay_us(100);
gpio_set_level(src_chan_io, i % 2);
esp_rom_delay_us(100); // must be larger than the debounce time
// we assume the cross event was triggered already, and the value of cnt should be updated
TEST_ASSERT_EQUAL_UINT32(i, cnt);
}
TEST_ESP_OK(ana_cmpr_disable(cmpr));

View File

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@ -15,14 +15,20 @@
#include "esp_rom_sys.h"
#include "soc/soc_caps.h"
#include "driver/ana_cmpr.h"
#include "driver/ana_cmpr_etm.h"
#include "driver/gpio.h"
#if CONFIG_IDF_TARGET_ESP32P4
// The pin of unit 0 is used for other purpose on P4 runner, use unit 1 instead
// The pin of unit 0 is not exposed on some ESP32-P4 runner, so test unit 1 by default
#define TEST_ANA_CMPR_UNIT_ID 1
#else
#define TEST_ANA_CMPR_UNIT_ID 0
#endif
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Test default on cross callback
*
@ -38,15 +44,14 @@ bool test_ana_cmpr_on_cross_callback(ana_cmpr_handle_t cmpr, const ana_cmpr_cros
/**
* @brief Initialize Analog Comparator source channel GPIO
*
* @param unit_id Analog Comparator unit ID
* @param init_level Initial level of the GPIO
*
* @return
* - int Source channel GPIO number
*/
int test_init_src_chan_gpio(int unit_id);
int test_init_src_chan_gpio(int unit_id, int init_level);
/**
* @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);
#ifdef __cplusplus
}
#endif

View File

@ -1,22 +1,18 @@
/*
* SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdio.h>
#include <inttypes.h>
#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"
#include "test_ana_cmpr.h"
#define TEST_ANA_CMPR_UNIT 0
#define TEST_TIME_US 5000
static gptimer_handle_t test_ana_cmpr_gptimer_init(void)
@ -44,7 +40,7 @@ static ana_cmpr_handle_t test_ana_cmpr_init(void)
ana_cmpr_handle_t cmpr = NULL;
ana_cmpr_config_t config = {
.unit = TEST_ANA_CMPR_UNIT,
.unit = TEST_ANA_CMPR_UNIT_ID,
.clk_src = ANA_CMPR_CLK_SRC_DEFAULT,
.ref_src = ANA_CMPR_REF_SRC_INTERNAL,
.cross_type = ANA_CMPR_CROSS_ANY,
@ -71,22 +67,6 @@ static void test_ana_cmpr_deinit(ana_cmpr_handle_t cmpr)
TEST_ESP_OK(ana_cmpr_del_unit(cmpr));
}
static int 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;
@ -145,14 +125,17 @@ static void test_ana_cmpr_deinit_etm(test_ana_cmpr_etm_handles_t handles)
TEST_ESP_OK(esp_etm_del_channel(handles.etm_neg_handle));
}
TEST_CASE("analog_comparator_etm_event", "[etm]")
TEST_CASE("ana_cmpr etm event", "[ana_cmpr][etm]")
{
gptimer_handle_t gptimer = test_ana_cmpr_gptimer_init();
ana_cmpr_handle_t cmpr = test_ana_cmpr_init();
int src_gpio = test_ana_cmpr_src_gpio_init();
int src_gpio = test_init_src_chan_gpio(TEST_ANA_CMPR_UNIT_ID, 1);
test_ana_cmpr_etm_handles_t handles = test_ana_cmpr_init_etm(cmpr, gptimer);
// triggers a negative pulse, whose duration is ~TEST_TIME_US
// negedge triggers the gptimer to start task
// posedge triggers the gptimer to stop task
// gptimer will record the time between the negedge and posedge
gpio_set_level(src_gpio, 0);
esp_rom_delay_us(TEST_TIME_US);
gpio_set_level(src_gpio, 1);

View File

@ -1,17 +1,16 @@
/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2023-2025 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;
int src_chan_io;
} test_ana_cmpr_data_t;
static void IRAM_ATTR test_ana_cmpr_iram_safety(void *args)
@ -26,40 +25,41 @@ static void IRAM_ATTR test_ana_cmpr_iram_safety(void *args)
};
ana_cmpr_set_debounce(data->handle, &dbc_cfg);
data->count = 0;
// generate 10 cross events by toggling the GPIO
for (int i = 1; i <= 10; i++) {
test_simulate_src_signal(data->src_chan, i % 2);
gpio_set_level(data->src_chan_io, 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_CASE("ana_cmpr works with cache disabled", "[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(TEST_ANA_CMPR_UNIT_ID);
ana_cmpr_handle_t cmpr = NULL;
ana_cmpr_config_t config = {
.unit = TEST_ANA_CMPR_UNIT_ID,
.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;
ana_cmpr_internal_ref_config_t ref_cfg = {
.ref_volt = ANA_CMPR_REF_VOLT_50_PCT_VDD,
.ref_volt = ANA_CMPR_REF_VOLT_20_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_ana_cmpr_data_t test_data = {
.handle = cmpr,
.count = 0,
.src_chan_io = test_init_src_chan_gpio(TEST_ANA_CMPR_UNIT_ID, 0),
};
ana_cmpr_event_callbacks_t cbs = {
.on_cross = test_ana_cmpr_on_cross_callback,
};

View File

@ -5,8 +5,6 @@
*/
#include "test_ana_cmpr.h"
#include "hal/gpio_ll.h"
#include "driver/gpio.h"
#include "esp_private/gpio.h"
#include "esp_attr.h"
@ -17,17 +15,12 @@ bool IRAM_ATTR test_ana_cmpr_on_cross_callback(ana_cmpr_handle_t cmpr, const ana
return false;
}
int test_init_src_chan_gpio(int unit_id)
int test_init_src_chan_gpio(int unit_id, int init_level)
{
int src_chan_num = -1;
TEST_ESP_OK(ana_cmpr_get_gpio(unit_id, ANA_CMPR_SOURCE_CHAN, &src_chan_num));
TEST_ASSERT(src_chan_num > 0);
TEST_ESP_OK(gpio_set_level(src_chan_num, 0));
TEST_ASSERT(src_chan_num >= 0);
TEST_ESP_OK(gpio_set_level(src_chan_num, init_level));
TEST_ESP_OK(gpio_output_enable(src_chan_num));
return src_chan_num;
}
void IRAM_ATTR test_simulate_src_signal(int src_chan, uint32_t val)
{
gpio_set_level(src_chan, val);
}

View File

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@ -17,7 +17,7 @@ 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);
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");
}

View File

@ -9,7 +9,7 @@ from pytest_embedded_idf.utils import idf_parametrize
@pytest.mark.parametrize(
'config',
[
'iram_safe',
'cache_safe',
'release',
],
indirect=True,

View File

@ -1,5 +1,5 @@
CONFIG_COMPILER_DUMP_RTL_FILES=y
CONFIG_ANA_CMPR_ISR_IRAM_SAFE=y
CONFIG_ANA_CMPR_ISR_CACHE_SAFE=y
CONFIG_ANA_CMPR_CTRL_FUNC_IN_IRAM=y
CONFIG_GPIO_CTRL_FUNC_IN_IRAM=y
CONFIG_COMPILER_OPTIMIZATION_NONE=y

View File

@ -1,5 +1,6 @@
CONFIG_PM_ENABLE=y
CONFIG_FREERTOS_USE_TICKLESS_IDLE=y
CONFIG_PM_DFS_INIT_AUTO=y
CONFIG_COMPILER_OPTIMIZATION_SIZE=y
CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_SIZE=y
CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_SILENT=y

View File

@ -19,10 +19,12 @@ extern "C" {
#define ANALOG_CMPR_LL_GET_HW(unit) (&ANALOG_CMPR[unit])
#define ANALOG_CMPR_LL_GET_UNIT(hw) (0)
#define ANALOG_CMPR_LL_EVENT_CROSS (1 << 0)
#define ANALOG_CMPR_LL_NEG_CROSS_MASK(unit) (1UL << ((int)unit * 3))
#define ANALOG_CMPR_LL_NEG_CROSS_MASK(unit) (1UL << ((int)unit * 3 + 0))
#define ANALOG_CMPR_LL_POS_CROSS_MASK(unit) (1UL << ((int)unit * 3 + 1))
#define ANALOG_CMPR_LL_ANY_CROSS_MASK(unit) (1UL << ((int)unit * 3 + 2))
#define ANALOG_CMPR_LL_ALL_INTR_MASK(unit) (ANALOG_CMPR_LL_NEG_CROSS_MASK(unit) | ANALOG_CMPR_LL_POS_CROSS_MASK(unit) | ANALOG_CMPR_LL_ANY_CROSS_MASK(unit))
#define ANALOG_CMPR_LL_ETM_SOURCE(unit, type) (GPIO_EVT_ZERO_DET_POS0 + (unit) * 2 + (type))

View File

@ -19,10 +19,12 @@ extern "C" {
#define ANALOG_CMPR_LL_GET_HW(unit) (&ANALOG_CMPR[unit])
#define ANALOG_CMPR_LL_GET_UNIT(hw) (0)
#define ANALOG_CMPR_LL_EVENT_CROSS (1 << 0)
#define ANALOG_CMPR_LL_NEG_CROSS_MASK(unit) (1UL << ((int)unit * 3))
#define ANALOG_CMPR_LL_NEG_CROSS_MASK(unit) (1UL << ((int)unit * 3 + 0))
#define ANALOG_CMPR_LL_POS_CROSS_MASK(unit) (1UL << ((int)unit * 3 + 1))
#define ANALOG_CMPR_LL_ANY_CROSS_MASK(unit) (1UL << ((int)unit * 3 + 2))
#define ANALOG_CMPR_LL_ALL_INTR_MASK(unit) (ANALOG_CMPR_LL_NEG_CROSS_MASK(unit) | ANALOG_CMPR_LL_POS_CROSS_MASK(unit) | ANALOG_CMPR_LL_ANY_CROSS_MASK(unit))
#define ANALOG_CMPR_LL_ETM_SOURCE(unit, type) (GPIO_EVT_ZERO_DET_POS0 + (unit) * 2 + (type))

View File

@ -15,6 +15,8 @@
#define ANALOG_CMPR_LL_GET_HW(unit) (&ANALOG_CMPR[unit])
#define ANALOG_CMPR_LL_EVENT_CROSS (1 << 0)
#define ANALOG_CMPR_LL_ALL_INTR_MASK(unit) (ANALOG_CMPR_LL_EVENT_CROSS)
#ifdef __cplusplus
extern "C" {
#endif
@ -77,7 +79,7 @@ static inline void analog_cmpr_ll_set_ref_source(analog_cmpr_dev_t *hw, ana_cmpr
* - 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)
static inline void analog_cmpr_ll_set_intr_cross_type(analog_cmpr_dev_t *hw, ana_cmpr_cross_type_t type)
{
hw->pad_comp_config->zero_det_mode = type;
}

View File

@ -0,0 +1,168 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include <stdbool.h>
#include "hal/misc.h"
#include "hal/assert.h"
#include "hal/ana_cmpr_types.h"
#include "soc/ana_cmpr_struct.h"
#ifdef __cplusplus
extern "C" {
#endif
#define ANALOG_CMPR_LL_GET_HW(unit) (&ANALOG_CMPR[unit])
#define ANALOG_CMPR_LL_GET_UNIT(hw) (0)
#define ANALOG_CMPR_LL_NEG_CROSS_MASK(unit) (1UL << ((int)unit * 3 + 0))
#define ANALOG_CMPR_LL_POS_CROSS_MASK(unit) (1UL << ((int)unit * 3 + 1))
#define ANALOG_CMPR_LL_ANY_CROSS_MASK(unit) (1UL << ((int)unit * 3 + 2))
#define ANALOG_CMPR_LL_ALL_INTR_MASK(unit) (ANALOG_CMPR_LL_NEG_CROSS_MASK(unit) | ANALOG_CMPR_LL_POS_CROSS_MASK(unit) | ANALOG_CMPR_LL_ANY_CROSS_MASK(unit))
/**
* @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->ext_xpd_comp_0 = 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->ext_dref_comp_0 = 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->ext_dref_comp_0 * 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, ana_cmpr_ref_voltage_t ref_src)
{
hw->pad_comp_config->ext_mode_comp_0 = 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, ana_cmpr_cross_type_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->ext_zero_det_filter_cnt_0 = 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

View File

@ -19,10 +19,12 @@ extern "C" {
#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_NEG_CROSS_MASK(unit) (1UL << ((int)unit * 3))
#define ANALOG_CMPR_LL_NEG_CROSS_MASK(unit) (1UL << ((int)unit * 3 + 0))
#define ANALOG_CMPR_LL_POS_CROSS_MASK(unit) (1UL << ((int)unit * 3 + 1))
#define ANALOG_CMPR_LL_ANY_CROSS_MASK(unit) (1UL << ((int)unit * 3 + 2))
#define ANALOG_CMPR_LL_ALL_INTR_MASK(unit) (ANALOG_CMPR_LL_NEG_CROSS_MASK(unit) | ANALOG_CMPR_LL_POS_CROSS_MASK(unit) | ANALOG_CMPR_LL_ANY_CROSS_MASK(unit))
#define ANALOG_CMPR_LL_ETM_SOURCE(unit, type) (GPIO_EVT_ZERO_DET_POS0 + (unit) * 2 + (type))

View File

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@ -12,6 +12,7 @@ const ana_cmpr_periph_t ana_cmpr_periph[SOC_ANA_CMPR_NUM] = {
.src_gpio = ANA_CMPR0_SRC_GPIO,
.ext_ref_gpio = ANA_CMPR0_EXT_REF_GPIO,
.intr_src = ETS_GPIO_EXT_SOURCE,
.module_name = "ANA_CMPR_U0",
},
};

View File

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@ -12,6 +12,7 @@ const ana_cmpr_periph_t ana_cmpr_periph[SOC_ANA_CMPR_NUM] = {
.src_gpio = ANA_CMPR0_SRC_GPIO,
.ext_ref_gpio = ANA_CMPR0_EXT_REF_GPIO,
.intr_src = ETS_GPIO_INTERRUPT_EXT_SOURCE,
.module_name = "ANA_CMPR_U0",
},
};

View File

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@ -12,6 +12,7 @@ const ana_cmpr_periph_t ana_cmpr_periph[SOC_ANA_CMPR_NUM] = {
.src_gpio = ANA_CMPR0_SRC_GPIO,
.ext_ref_gpio = ANA_CMPR0_EXT_REF_GPIO,
.intr_src = ETS_GPIO_INTR_SOURCE,
.module_name = "ANA_CMPR_U0",
},
};

View File

@ -0,0 +1,36 @@
/**
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
/* NOTE: this file is created manually for compatibility */
#pragma once
#include <stdint.h>
#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_ext_pad_comp_config_0_reg_t *pad_comp_config;
volatile gpio_ext_pad_comp_filter_0_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

View File

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@ -12,11 +12,13 @@ const ana_cmpr_periph_t ana_cmpr_periph[SOC_ANA_CMPR_NUM] = {
.src_gpio = ANA_CMPR0_SRC_GPIO,
.ext_ref_gpio = ANA_CMPR0_EXT_REF_GPIO,
.intr_src = ETS_GPIO_PAD_COMP_INTR_SOURCE,
.module_name = "ANA_CMPR_U0",
},
[1] = {
.src_gpio = ANA_CMPR1_SRC_GPIO,
.ext_ref_gpio = ANA_CMPR1_EXT_REF_GPIO,
.intr_src = ETS_GPIO_PAD_COMP_INTR_SOURCE,
.module_name = "ANA_CMPR_U1",
},
};

View File

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@ -10,7 +10,7 @@
#include "soc/soc_caps.h"
#include "soc/interrupts.h"
#if SOC_ANA_CMPR_SUPPORTED
#include "soc/ana_cmpr_channel.h"
#include "soc/ana_cmpr_pins.h"
#endif
#ifdef __cplusplus
@ -22,6 +22,7 @@ typedef struct {
int src_gpio;
int ext_ref_gpio;
int intr_src;
const char *module_name;
} ana_cmpr_periph_t;
extern const ana_cmpr_periph_t ana_cmpr_periph[SOC_ANA_CMPR_NUM];

View File

@ -141,7 +141,7 @@ Currently it supports :cpp:member:`ana_cmpr_event_callbacks_t::on_cross`, and it
.. note::
When :ref:`CONFIG_ANA_CMPR_ISR_IRAM_SAFE` is enabled, you should guarantee that the callback context and involved data are in internal RAM by adding the attribute ``IRAM_ATTR`` (See more in :ref:`anacmpr-iram-safe`).
When :ref:`CONFIG_ANA_CMPR_ISR_CACHE_SAFE` is enabled, you should guarantee that the callback context and involved data are in internal RAM by adding the attribute ``IRAM_ATTR`` (See more in :ref:`anacmpr-iram-safe`).
.. _anacmpr-enable-and-disable-unit:
@ -171,7 +171,7 @@ IRAM Safe
By default, the analog comparator interrupt will be deferred when the cache is disabled for reasons like programming or erasing the flash. Thus the alarm interrupt will not get executed in time, which is not expected in a real-time application.
There is a Kconfig option :ref:`CONFIG_ANA_CMPR_ISR_IRAM_SAFE` that:
There is a Kconfig option :ref:`CONFIG_ANA_CMPR_ISR_CACHE_SAFE` that:
1. Enables the interrupt being serviced even when cache is disabled.
2. Places all functions that used by the ISR into IRAM. [1]_
@ -205,7 +205,7 @@ Other functions that take :cpp:type:`ana_cmpr_handle_t` as the first positional
Kconfig Options
^^^^^^^^^^^^^^^
- :ref:`CONFIG_ANA_CMPR_ISR_IRAM_SAFE` controls whether the default ISR handler can work when cache is disabled. See :ref:`anacmpr-iram-safe` for more information.
- :ref:`CONFIG_ANA_CMPR_ISR_CACHE_SAFE` controls whether the default ISR handler can work when cache is disabled. See :ref:`anacmpr-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 :ref:`anacmpr-iram-safe` for more information.
- :ref:`CONFIG_ANA_CMPR_ENABLE_DEBUG_LOG` is used to enable the debug log output. Enabling this option increases the firmware binary size.

View File

@ -141,7 +141,7 @@
.. note::
当启用 :ref:`CONFIG_ANA_CMPR_ISR_IRAM_SAFE` 时,应添加属性 ``IRAM_ATTR``,确保回调上下文和涉及的数据位于内部 RAM 中(详情请参阅 :ref:`anacmpr-iram-safe`)。
当启用 :ref:`CONFIG_ANA_CMPR_ISR_CACHE_SAFE` 时,应添加属性 ``IRAM_ATTR``,确保回调上下文和涉及的数据位于内部 RAM 中(详情请参阅 :ref:`anacmpr-iram-safe`)。
.. _anacmpr-enable-and-disable-unit:
@ -171,7 +171,7 @@ IRAM 安全
默认情况下,当 cache 因写入或擦除 flash 等原因而被禁用时,模拟比较器的中断服务将会延迟,造成警报中断无法及时执行。在实时应用程序中通常需要避免这一情况发生。
Kconfig 选项 :ref:`CONFIG_ANA_CMPR_ISR_IRAM_SAFE` 支持:
Kconfig 选项 :ref:`CONFIG_ANA_CMPR_ISR_CACHE_SAFE` 支持:
1. 即使 cache 被禁用也能启用中断服务。
2. 将 ISR 使用的所有函数放入 IRAM。 [1]_
@ -205,7 +205,7 @@ Kconfig 选项 :ref:`CONFIG_ANA_CMPR_ISR_IRAM_SAFE` 支持:
Kconfig 选项
^^^^^^^^^^^^
- :ref:`CONFIG_ANA_CMPR_ISR_IRAM_SAFE` 控制默认的 ISR 句柄在 cache 被禁用时是否可以正常工作,详见 :ref:`anacmpr-iram-safe`
- :ref:`CONFIG_ANA_CMPR_ISR_CACHE_SAFE` 控制默认的 ISR 句柄在 cache 被禁用时是否可以正常工作,详见 :ref:`anacmpr-iram-safe`
- :ref:`CONFIG_ANA_CMPR_CTRL_FUNC_IN_IRAM` 控制模拟比较器控制函数的存放位置IRAM 或 flash详见 :ref:`anacmpr-iram-safe`
- :ref:`CONFIG_ANA_CMPR_ENABLE_DEBUG_LOG` 用于启用调试日志输出。启用此选项将增加固件的二进制文件大小。

View File

@ -1,11 +1,11 @@
set(src "ana_cmpr_example_main.c")
if(CONFIG_EXAMPLE_USE_ETM)
if(CONFIG_EXAMPLE_MONITOR_IO_FROM_ETM)
list(APPEND src "ana_cmpr_example_etm.c")
else()
list(APPEND src "ana_cmpr_example_intr.c")
endif()
idf_component_register(SRCS ${src}
PRIV_REQUIRES esp_driver_ana_cmpr esp_driver_gpio
INCLUDE_DIRS ".")
PRIV_REQUIRES esp_driver_ana_cmpr esp_driver_gpio
INCLUDE_DIRS ".")

View File

@ -1,39 +1,41 @@
menu "Analog Comparator Example Configuration"
menu "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"
choice EXAMPLE_MONITOR_IO_FROM
prompt "Monitor IO signal comes from"
default EXAMPLE_MONITOR_IO_FROM_ETM if SOC_ANA_CMPR_SUPPORT_ETM && SOC_GPIO_SUPPORT_ETM
default EXAMPLE_MONITOR_IO_FROM_INTR
config EXAMPLE_MONITOR_IO_FROM_INTR
bool "Analog Comparator Interrupt"
help
Enable to set the monitor GPIO via interrupt callback
config EXAMPLE_USE_ETM
config EXAMPLE_MONITOR_IO_FROM_ETM
depends on SOC_ANA_CMPR_SUPPORT_ETM && SOC_GPIO_SUPPORT_ETM
bool "Use ETM"
bool "Analog Comparator Event-Task-Matrix"
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
choice EXAMPLE_REF_FROM
prompt "Analog Comparator reference voltage comes from"
default EXAMPLE_REF_FROM_INTERNAL
help
Decide the reference signal comes from internal or external
Decide the reference voltage comes from internal or external
config EXAMPLE_INTERNAL_REF
bool "Internal reference"
config EXAMPLE_REF_FROM_INTERNAL
bool "Internal"
help
The source signal will refer to an internal voltage, which related to VDD.
The source signal will refer to an internal voltage, which is divided from VDD.
config EXAMPLE_EXTERNAL_REF
bool "External reference"
config EXAMPLE_REF_FROM_EXTERNAL
bool "External pin"
help
The source signal will refer to the external signal on a specific GPIO.
endchoice
config EXAMPLE_HYSTERESIS_COMPARATOR
depends on EXAMPLE_INTERNAL_REF && !EXAMPLE_USE_ETM
depends on EXAMPLE_REF_FROM_INTERNAL && EXAMPLE_MONITOR_IO_FROM_INTR
bool "Enable hysteresis comparator"
default n
help

View File

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
@ -7,16 +7,11 @@
#include <stdio.h>
#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 */
@ -36,13 +31,13 @@ static void example_etm_bind_ana_cmpr_event_with_gpio_task(ana_cmpr_handle_t cmp
task_cfg.actions[0] = GPIO_ETM_TASK_ACTION_SET;
task_cfg.actions[1] = GPIO_ETM_TASK_ACTION_CLR;
ESP_ERROR_CHECK(gpio_new_etm_task(&task_cfg, &gpio_set_task, &gpio_clr_task));
/* Add task to the monitor GPIO */
/* Bind different tasks to the same GPIO */
ESP_ERROR_CHECK(gpio_etm_task_add_gpio(gpio_set_task, EXAMPLE_MONITOR_GPIO_NUM));
ESP_ERROR_CHECK(gpio_etm_task_add_gpio(gpio_clr_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_handle_t etm_pos_handle = NULL;
esp_etm_channel_handle_t etm_neg_handle = NULL;
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));
@ -54,18 +49,13 @@ static void example_etm_bind_ana_cmpr_event_with_gpio_task(ana_cmpr_handle_t cmp
ESP_ERROR_CHECK(esp_etm_channel_enable(etm_neg_handle));
}
ana_cmpr_handle_t example_init_analog_comparator_etm(void)
static void 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;
int src_gpio = -1;
ESP_ERROR_CHECK(ana_cmpr_get_gpio(EXAMPLE_ANA_CMPR_UNIT, ANA_CMPR_SOURCE_CHAN, &src_gpio));
#if CONFIG_EXAMPLE_INTERNAL_REF
#if CONFIG_EXAMPLE_REF_FROM_INTERNAL
/* Step 1: Allocate the new analog comparator unit */
ana_cmpr_config_t config = {
.unit = EXAMPLE_ANA_CMPR_UNIT,
@ -74,13 +64,14 @@ ana_cmpr_handle_t example_init_analog_comparator_etm(void)
.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 = {
/* Set the internal reference voltage to 50% VDD */
.ref_volt = ANA_CMPR_REF_VOLT_50_PCT_VDD,
};
ESP_ERROR_CHECK(ana_cmpr_set_internal_reference(cmpr, &ref_cfg));
ESP_LOGI(TAG, "Allocate Analog Comparator with internal reference voltage: %d%% * VDD, source from GPIO %d", (int)ref_cfg.ref_volt * 10, src_gpio);
#else
/* Step 1: Allocate the new analog comparator unit */
ana_cmpr_config_t config = {
@ -90,8 +81,10 @@ ana_cmpr_handle_t example_init_analog_comparator_etm(void)
.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
int ext_ref_gpio = -1;
ESP_ERROR_CHECK(ana_cmpr_get_gpio(EXAMPLE_ANA_CMPR_UNIT, ANA_CMPR_EXT_REF_CHAN, &ext_ref_gpio));
ESP_LOGI(TAG, "Allocate Analog Comparator with external reference from GPIO %d, source from GPIO %d", ext_ref_gpio, src_gpio);
#endif // CONFIG_EXAMPLE_REF_FROM_INTERNAL
/* Step 2: (Optional) Set the debounce configuration
* It's an optional configuration, if the wait time is set in debounce configuration,
@ -109,23 +102,19 @@ ana_cmpr_handle_t example_init_analog_comparator_etm(void)
};
ESP_ERROR_CHECK(ana_cmpr_set_debounce(cmpr, &dbc_cfg));
/* Step 3: Enable the analog comparator unit */
ESP_ERROR_CHECK(ana_cmpr_enable(cmpr));
/* Step 3: Connect the analog comparator events and gpio tasks via ETM channels */
example_etm_bind_ana_cmpr_event_with_gpio_task(cmpr);
ESP_LOGI(TAG, "Connected analog comparator events and GPIO tasks");
#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;
/* Step 4: Enable the analog comparator unit */
ESP_ERROR_CHECK(ana_cmpr_enable(cmpr));
ESP_LOGI(TAG, "Analog comparator enabled");
}
void example_analog_comparator_etm_app(void)
{
/* Initialize GPIO to monitor the comparator interrupt */
/* Initialize GPIO to monitor the comparator cross event */
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);
example_init_analog_comparator_etm();
}

View File

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
@ -7,13 +7,8 @@
#include <stdio.h>
#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)
@ -37,18 +32,13 @@ static bool example_ana_cmpr_on_cross_callback(ana_cmpr_handle_t cmpr,
return false;
}
void example_init_analog_comparator_intr(void)
static 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;
int src_gpio = -1;
ESP_ERROR_CHECK(ana_cmpr_get_gpio(EXAMPLE_ANA_CMPR_UNIT, ANA_CMPR_SOURCE_CHAN, &src_gpio));
#if CONFIG_EXAMPLE_INTERNAL_REF
#if CONFIG_EXAMPLE_REF_FROM_INTERNAL
/* Step 1: Allocate the new analog comparator unit */
ana_cmpr_config_t config = {
.unit = EXAMPLE_ANA_CMPR_UNIT,
@ -57,7 +47,6 @@ void example_init_analog_comparator_intr(void)
.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 = {
@ -65,10 +54,12 @@ void example_init_analog_comparator_intr(void)
/* 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
/* Set the internal reference voltage to 50% VDD */
.ref_volt = ANA_CMPR_REF_VOLT_50_PCT_VDD,
#endif
#endif // CONFIG_EXAMPLE_HYSTERESIS_COMPARATOR
};
ESP_ERROR_CHECK(ana_cmpr_set_internal_reference(cmpr, &ref_cfg));
ESP_LOGI(TAG, "Allocate Analog Comparator with internal reference voltage: %d%% * VDD, source from GPIO %d", (int)ref_cfg.ref_volt * 10, src_gpio);
#else
/* Step 1: Allocate the new analog comparator unit */
ana_cmpr_config_t config = {
@ -78,8 +69,10 @@ void example_init_analog_comparator_intr(void)
.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
int ext_ref_gpio = -1;
ESP_ERROR_CHECK(ana_cmpr_get_gpio(EXAMPLE_ANA_CMPR_UNIT, ANA_CMPR_EXT_REF_CHAN, &ext_ref_gpio));
ESP_LOGI(TAG, "Allocate Analog Comparator with external reference from GPIO %d, source from GPIO %d", ext_ref_gpio, src_gpio);
#endif // CONFIG_EXAMPLE_REF_FROM_INTERNAL
/* Step 2: (Optional) Set the debounce configuration
* It's an optional configuration, if the wait time is set in debounce configuration,
@ -105,17 +98,12 @@ void example_init_analog_comparator_intr(void)
/* 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
ESP_LOGI(TAG, "Analog comparator enabled");
}
void example_analog_comparator_intr_app(void)
{
/* Initialize GPIO to monitor the comparator interrupt */
/* Initialize GPIO to monitor the comparator cross event */
example_init_monitor_gpio();
/* Initialize Analog Comparator */
example_init_analog_comparator_intr();

View File

@ -23,7 +23,7 @@ void example_init_monitor_gpio(void)
void app_main(void)
{
#if CONFIG_EXAMPLE_USE_ETM
#if CONFIG_EXAMPLE_MONITOR_IO_FROM_ETM
example_analog_comparator_etm_app();
#else
example_analog_comparator_intr_app();

View File

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
@ -9,29 +9,29 @@
#include <stdint.h>
#include "sdkconfig.h"
#include "soc/soc_caps.h"
#include "esp_log.h"
#include "driver/gpio.h"
#include "driver/ana_cmpr.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_MONITOR_GPIO_NUM (0) // The gpio to monitor the comparator cross event
#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))
#if CONFIG_IDF_TARGET_ESP32P4
#define EXAMPLE_MONITOR_GPIO_NUM (32) // The gpio to monitor the on cross callback
#else
#define EXAMPLE_MONITOR_GPIO_NUM (0) // The gpio to monitor the on cross callback
#endif
#define TAG "example"
void example_init_monitor_gpio(void);
#if CONFIG_EXAMPLE_USE_ETM
#if CONFIG_EXAMPLE_MONITOR_IO_FROM_ETM
/**
* @brief Set or clear the monitor GPIO via Event Task Matrix when cross interrupt triggers.
* @brief Set or clear the monitor GPIO via Event Task Matrix when cross event fires.
* @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
* 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

View File

@ -17,12 +17,8 @@ from pytest_embedded_idf.utils import idf_parametrize
@idf_parametrize('target', ['esp32h2', 'esp32p4', 'esp32c5', 'esp32c61'], indirect=['target'])
def test_ana_cmpr_example(dut: Dut) -> None:
sdkconfig = dut.app.sdkconfig
dut.expect(
r'ana_cmpr_example: Analog Comparator source gpio ([0-9]+), external reference gpio ([0-9]+)', 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)
if sdkconfig['EXAMPLE_REF_FROM_INTERNAL']:
dut.expect_exact('Allocate Analog Comparator with internal reference voltage')
elif sdkconfig['EXAMPLE_REF_FROM_EXTERNAL']:
dut.expect_exact('Allocate Analog Comparator with external reference from GPIO')
dut.expect_exact('Analog comparator enabled')

View File

@ -1,2 +1,2 @@
CONFIG_EXAMPLE_INTERNAL_REF=n
CONFIG_EXAMPLE_EXTERNAL_REF=y
CONFIG_EXAMPLE_REF_FROM_INTERNAL=n
CONFIG_EXAMPLE_REF_FROM_EXTERNAL=y

View File

@ -1,2 +1,2 @@
CONFIG_EXAMPLE_INTERNAL_REF=y
CONFIG_EXAMPLE_EXTERNAL_REF=n
CONFIG_EXAMPLE_REF_FROM_INTERNAL=y
CONFIG_EXAMPLE_REF_FROM_EXTERNAL=n