feat(ana_cmpr): supported etm in analog comparator example

This commit is contained in:
laokaiyao 2023-07-31 10:43:54 +08:00
parent bf2a5653a9
commit ff7a11e539
44 changed files with 796 additions and 267 deletions

View File

@ -25,6 +25,7 @@
#include "soc/ana_cmpr_periph.h"
#include "hal/ana_cmpr_ll.h"
#include "driver/ana_cmpr.h"
#include "driver/gpio.h"
#include "esp_private/io_mux.h"
#include "esp_private/esp_clk.h"
@ -36,6 +37,7 @@ struct ana_cmpr_t {
ana_cmpr_event_callbacks_t cbs; /*!< The callback group that set by user */
intr_handle_t intr_handle; /*!< Interrupt handle */
uint32_t intr_mask; /*!< Interrupt mask */
int intr_priority; /*!< Interrupt priority */
void *user_data; /*!< User data that passed to the callbacks */
uint32_t src_clk_freq_hz; /*!< Source clock frequency of the Analog Comparator unit */
esp_pm_lock_handle_t pm_lock; /*!< The Power Management lock that used to avoid unexpected power down of the clock domain */
@ -44,16 +46,16 @@ struct ana_cmpr_t {
/* Helper macros */
#define ANA_CMPR_NULL_POINTER_CHECK(p) ESP_RETURN_ON_FALSE((p), ESP_ERR_INVALID_ARG, TAG, "input parameter '"#p"' is NULL")
#define ANA_CMPR_NULL_POINTER_CHECK_ISR(p) ESP_RETURN_ON_FALSE_ISR((p), ESP_ERR_INVALID_ARG, TAG, "input parameter '"#p"' is NULL")
#define ANA_CMPR_UNIT_CHECK(unit) ESP_RETURN_ON_FALSE((unit) >= ANA_CMPR_UNIT_0 && (unit) < SOC_ANA_CMPR_NUM, \
#define ANA_CMPR_UNIT_CHECK(unit) ESP_RETURN_ON_FALSE((unit) >= 0 && (unit) < SOC_ANA_CMPR_NUM, \
ESP_ERR_INVALID_ARG, TAG, "invalid uint number");
/* Memory allocation caps which decide the section that memory supposed to allocate */
#if CONFIG_ANA_CMPR_ISR_IRAM_SAFE
#define ANA_CMPR_MEM_ALLOC_CAPS (MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT)
#define ANA_CMPR_INTR_FLAG (ESP_INTR_FLAG_LOWMED | ESP_INTR_FLAG_SHARED | ESP_INTR_FLAG_IRAM) // Shared with GPIO
#define ANA_CMPR_INTR_FLAG (ESP_INTR_FLAG_IRAM)
#else
#define ANA_CMPR_MEM_ALLOC_CAPS MALLOC_CAP_DEFAULT
#define ANA_CMPR_INTR_FLAG (ESP_INTR_FLAG_LOWMED | ESP_INTR_FLAG_SHARED) // Shared with GPIO
#define ANA_CMPR_INTR_FLAG (0)
#endif
/* Driver tag */
@ -71,20 +73,20 @@ static void IRAM_ATTR s_ana_cmpr_default_intr_handler(void *usr_data)
{
ana_cmpr_handle_t cmpr_handle = (ana_cmpr_handle_t)usr_data;
bool need_yield = false;
ana_cmpr_cross_event_data_t evt_data;
ana_cmpr_cross_event_data_t evt_data = {.cross_type = ANA_CMPR_CROSS_ANY};
/* Get and clear the interrupt status */
uint32_t status = analog_cmpr_ll_get_intr_status(cmpr_handle->dev);
analog_cmpr_ll_clear_intr(cmpr_handle->dev, status);
/* Call the user callback function if it is specified and the corresponding event triggers*/
if (cmpr_handle->cbs.on_cross && (status & cmpr_handle->intr_mask)) {
#if SOC_ANA_CMPR_SUPPORT_MULTI_INTR
#if SOC_ANA_CMPR_CAN_DISTINGUISH_EDGE
if (status & ANALOG_CMPR_LL_POS_CROSS_MASK(cmpr_handle->unit)) {
evt_data.cross_type = ANA_CMPR_CROSS_POS;
} else if (status & ANALOG_CMPR_LL_NEG_CROSS_MASK(cmpr_handle->unit)) {
evt_data.cross_type = ANA_CMPR_CROSS_NEG;
}
#endif // SOC_ANA_CMPR_SUPPORT_MULTI_INTR
#endif // SOC_ANA_CMPR_CAN_DISTINGUISH_EDGE
need_yield = cmpr_handle->cbs.on_cross(cmpr_handle, &evt_data, cmpr_handle->user_data);
}
if (need_yield) {
@ -92,6 +94,22 @@ static void IRAM_ATTR s_ana_cmpr_default_intr_handler(void *usr_data)
}
}
static esp_err_t s_ana_cmpr_init_gpio(ana_cmpr_handle_t cmpr, bool is_external_ref)
{
uint64_t pin_mask = BIT64(ana_cmpr_periph[cmpr->unit].src_gpio);
if (is_external_ref) {
pin_mask |= BIT64(ana_cmpr_periph[cmpr->unit].ext_ref_gpio);
}
gpio_config_t ana_cmpr_gpio_cfg = {
.pin_bit_mask = pin_mask,
.mode = GPIO_MODE_DISABLE,
.pull_up_en = GPIO_PULLUP_DISABLE,
.pull_down_en = GPIO_PULLDOWN_DISABLE,
.intr_type = GPIO_INTR_DISABLE,
};
return gpio_config(&ana_cmpr_gpio_cfg);
}
esp_err_t ana_cmpr_new_unit(const ana_cmpr_config_t *config, ana_cmpr_handle_t *ret_cmpr)
{
#if CONFIG_ANA_CMPR_ENABLE_DEBUG_LOG
@ -101,6 +119,7 @@ esp_err_t ana_cmpr_new_unit(const ana_cmpr_config_t *config, ana_cmpr_handle_t *
ANA_CMPR_NULL_POINTER_CHECK(ret_cmpr);
ana_cmpr_unit_t unit = config->unit;
ANA_CMPR_UNIT_CHECK(unit);
ESP_RETURN_ON_FALSE(config->intr_priority >= 0 && config->intr_priority <= 7, ESP_ERR_INVALID_ARG, TAG, "interrupt priority should be within 0~7");
ESP_RETURN_ON_FALSE(!s_ana_cmpr[unit], ESP_ERR_INVALID_STATE, TAG,
"unit has been allocated already");
esp_err_t ret = ESP_OK;
@ -112,6 +131,7 @@ esp_err_t ana_cmpr_new_unit(const ana_cmpr_config_t *config, ana_cmpr_handle_t *
/* 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;
@ -123,6 +143,10 @@ esp_err_t ana_cmpr_new_unit(const ana_cmpr_config_t *config, ana_cmpr_handle_t *
ESP_GOTO_ON_ERROR(ret, err, TAG, "create NO_LIGHT_SLEEP, lock failed");
#endif
if (!config->flags.io_loop_back) {
ESP_GOTO_ON_ERROR(s_ana_cmpr_init_gpio(s_ana_cmpr[unit], config->ref_src == ANA_CMPR_REF_SRC_EXTERNAL), err, TAG, "failed to initialize GPIO");
}
/* Analog clock comes from IO MUX, but IO MUX clock might be shared with other submodules as well */
ESP_GOTO_ON_ERROR(esp_clk_tree_src_get_freq_hz((soc_module_clk_t)config->clk_src,
ESP_CLK_TREE_SRC_FREQ_PRECISION_CACHED,
@ -134,23 +158,19 @@ esp_err_t ana_cmpr_new_unit(const ana_cmpr_config_t *config, ana_cmpr_handle_t *
/* Configure the register */
portENTER_CRITICAL(&s_spinlock);
analog_cmpr_ll_set_ref_source(s_ana_cmpr[unit]->dev, config->ref_src);
#if !SOC_ANA_CMPR_SUPPORT_MULTI_INTR
#if !SOC_ANA_CMPR_CAN_DISTINGUISH_EDGE
analog_cmpr_ll_set_cross_type(s_ana_cmpr[unit]->dev, config->cross_type);
#endif // SOC_ANA_CMPR_SUPPORT_MULTI_INTR
portEXIT_CRITICAL(&s_spinlock);
/* Allocate the interrupt, currently the interrupt source of Analog Comparator is shared with GPIO interrupt source */
#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);
ESP_GOTO_ON_ERROR(esp_intr_alloc_intrstatus(ana_cmpr_io_map[unit].intr_src, ANA_CMPR_INTR_FLAG, (uint32_t)analog_cmpr_ll_get_intr_status_reg(s_ana_cmpr[unit]->dev),
s_ana_cmpr[unit]->intr_mask, s_ana_cmpr_default_intr_handler, s_ana_cmpr[unit], &s_ana_cmpr[unit]->intr_handle),
err, TAG, "allocate interrupt failed");
portEXIT_CRITICAL(&s_spinlock);
if (config->ref_src == ANA_CMPR_REF_SRC_INTERNAL) {
ESP_LOGD(TAG, "unit %d allocated, source signal: GPIO %d, reference signal: internal",
(int)unit, ana_cmpr_io_map[unit].src_gpio);
(int)unit, ana_cmpr_periph[unit].src_gpio);
} else {
ESP_LOGD(TAG, "unit %d allocated, source signal: GPIO %d, reference signal: GPIO %d",
(int)unit, ana_cmpr_io_map[unit].src_gpio, ana_cmpr_io_map[unit].ext_ref_gpio);
(int)unit, ana_cmpr_periph[unit].src_gpio, ana_cmpr_periph[unit].ext_ref_gpio);
}
*ret_cmpr = s_ana_cmpr[unit];
@ -230,12 +250,19 @@ esp_err_t ana_cmpr_set_debounce(ana_cmpr_handle_t cmpr, const ana_cmpr_debounce_
esp_err_t ana_cmpr_set_cross_type(ana_cmpr_handle_t cmpr, ana_cmpr_cross_type_t cross_type)
{
#if SOC_ANA_CMPR_CAN_DISTINGUISH_EDGE
/* Not support to set the cross type after initialized, because it relies on the interrupt types to distinguish the edge,
* i.e. have to re-allocate the interrupt to change the cross type */
(void)cmpr;
(void)cross_type;
return ESP_ERR_NOT_SUPPORTED;
#else
ANA_CMPR_NULL_POINTER_CHECK_ISR(cmpr);
ESP_RETURN_ON_FALSE_ISR(cross_type >= ANA_CMPR_CROSS_DISABLE && cross_type <= ANA_CMPR_CROSS_ANY,
ESP_ERR_INVALID_ARG, TAG, "invalid cross type");
portENTER_CRITICAL_SAFE(&s_spinlock);
#if !SOC_ANA_CMPR_SUPPORT_MULTI_INTR
#if !SOC_ANA_CMPR_CAN_DISTINGUISH_EDGE
analog_cmpr_ll_set_cross_type(cmpr->dev, cross_type);
#endif
cmpr->intr_mask = analog_cmpr_ll_get_intr_mask_by_type(cmpr->dev, cross_type);
@ -244,6 +271,7 @@ esp_err_t ana_cmpr_set_cross_type(ana_cmpr_handle_t cmpr, ana_cmpr_cross_type_t
ESP_EARLY_LOGD(TAG, "unit %d cross type updated to %d", (int)cmpr->unit, cross_type);
return ESP_OK;
#endif
}
esp_err_t ana_cmpr_register_event_callbacks(ana_cmpr_handle_t cmpr, const ana_cmpr_event_callbacks_t *cbs, void *user_data)
@ -263,6 +291,16 @@ esp_err_t ana_cmpr_register_event_callbacks(ana_cmpr_handle_t cmpr, const ana_cm
}
#endif
/* Allocate the interrupt, the interrupt source of Analog Comparator is shared with GPIO interrupt source on ESP32H2 */
if (!cmpr->intr_handle) {
int intr_flags = ANA_CMPR_INTR_FLAG | (cmpr->intr_priority ? BIT(cmpr->intr_priority) : ESP_INTR_FLAG_LOWMED);
#if SOC_ANA_CMPR_INTR_SHARE_WITH_GPIO
intr_flags |= ESP_INTR_FLAG_SHARED;
#endif // SOC_ANA_CMPR_INTR_SHARE_WITH_GPIO
ESP_RETURN_ON_ERROR(esp_intr_alloc_intrstatus(ana_cmpr_periph[cmpr->unit].intr_src, intr_flags, (uint32_t)analog_cmpr_ll_get_intr_status_reg(cmpr->dev),
cmpr->intr_mask, s_ana_cmpr_default_intr_handler, cmpr, &cmpr->intr_handle), TAG, "allocate interrupt failed");
}
/* Save the callback group */
memcpy(&(cmpr->cbs), cbs, sizeof(ana_cmpr_event_callbacks_t));
cmpr->user_data = user_data;
@ -327,10 +365,10 @@ esp_err_t ana_cmpr_get_gpio(ana_cmpr_unit_t unit, ana_cmpr_channel_type_t chan_t
/* Get the gpio number according to the channel type */
switch (chan_type) {
case ANA_CMPR_SOURCE_CHAN:
*gpio_num = ana_cmpr_io_map[unit].src_gpio;
*gpio_num = ana_cmpr_periph[unit].src_gpio;
break;
case ANA_CMPR_EXT_REF_CHAN:
*gpio_num = ana_cmpr_io_map[unit].ext_ref_gpio;
*gpio_num = ana_cmpr_periph[unit].ext_ref_gpio;
break;
default:
ESP_LOGE(TAG, "invalid channel type");
@ -339,3 +377,11 @@ esp_err_t ana_cmpr_get_gpio(ana_cmpr_unit_t unit, ana_cmpr_channel_type_t chan_t
return ESP_OK;
}
ana_cmpr_unit_t ana_cmpr_priv_get_unit_by_handle(ana_cmpr_handle_t cmpr)
{
if (!cmpr) {
return -1;
}
return cmpr->unit;
}

View File

@ -21,81 +21,42 @@
#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 ana_cmpr_etm_event_t ana_cmpr_etm_event_t;
typedef struct {
portMUX_TYPE spinlock;
ana_cmpr_etm_event_t *events[SOC_ANA_CMPR_ETM_EVENTS_PER_UNIT];
} ana_cmpr_etm_unit_t;
struct ana_cmpr_etm_event_t {
esp_etm_event_t base;
ana_cmpr_unit_t unit;
ana_cmpr_event_type_t type;
};
static ana_cmpr_etm_unit_t s_ana_cmpr_etm_unit[SOC_ANA_CMPR_NUM] = {
[0 ... SOC_ANA_CMPR_NUM - 1] = {
.spinlock = portMUX_INITIALIZER_UNLOCKED,
.events[0 ... SOC_ANA_CMPR_ETM_EVENTS_PER_UNIT - 1] = NULL,
},
};
static esp_err_t ana_cmpr_etm_event_register_to_unit(ana_cmpr_etm_event_t *evt)
{
ESP_RETURN_ON_FALSE(!s_ana_cmpr_etm_unit[evt->unit].events[evt->type],
ESP_ERR_INVALID_STATE, TAG, "this event has been registered on the unit");
portENTER_CRITICAL(&s_ana_cmpr_etm_unit[evt->unit].spinlock);
s_ana_cmpr_etm_unit[evt->unit].events[evt->type] = evt;
portEXIT_CRITICAL(&s_ana_cmpr_etm_unit[evt->unit].spinlock);
return ESP_OK;
}
static esp_err_t ana_cmpr_etm_event_unregister_from_unit(ana_cmpr_etm_event_t *evt)
{
ESP_RETURN_ON_FALSE(s_ana_cmpr_etm_unit[evt->unit].events[evt->type] == evt,
ESP_ERR_INVALID_ARG, TAG, "the event handle is not match to registered handle");
portENTER_CRITICAL(&s_ana_cmpr_etm_unit[evt->unit].spinlock);
s_ana_cmpr_etm_unit[evt->unit].events[evt->type] = NULL;
portEXIT_CRITICAL(&s_ana_cmpr_etm_unit[evt->unit].spinlock);
return ESP_OK;
}
} ana_cmpr_etm_event_t;
static esp_err_t ana_cmpr_del_etm_event(esp_etm_event_handle_t base_event)
{
ana_cmpr_etm_event_t *event = __containerof(base_event, ana_cmpr_etm_event_t, base);
ESP_RETURN_ON_ERROR(ana_cmpr_etm_event_unregister_from_unit(event), TAG, "unregister the event from the unit");
free(event);
event = NULL;
return ESP_OK;
}
esp_err_t ana_cmpr_new_etm_event(const ana_cmpr_etm_event_config_t *config, esp_etm_event_handle_t *ret_event)
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;
ESP_GOTO_ON_FALSE(config && ret_event, ESP_ERR_INVALID_ARG, err, TAG, "invalid argument");
ana_cmpr_unit_t unit = ana_cmpr_priv_get_unit_by_handle(cmpr);
ESP_RETURN_ON_FALSE(((int)unit) >= 0, ESP_ERR_INVALID_ARG, TAG, "invalid analog comparator handle");
ESP_RETURN_ON_FALSE(config && ret_event, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
event = heap_caps_calloc(1, sizeof(ana_cmpr_etm_event_t), ETM_MEM_ALLOC_CAPS);
ESP_GOTO_ON_FALSE(event, ESP_ERR_NO_MEM, err, TAG, "no mem for analog comparator event");
event->unit = config->unit;
event->type = config->event_type;
// register the event channel to the group
ESP_GOTO_ON_ERROR(ana_cmpr_etm_event_register_to_unit(event), err, TAG, "register event channel to group failed");
uint32_t event_id = ANALOG_CMPR_LL_ETM_SOURCE(config->unit, config->event_type);
uint32_t event_id = ANALOG_CMPR_LL_ETM_SOURCE(unit, config->event_type);
event->base.del = ana_cmpr_del_etm_event;
event->base.event_id = event_id;
event->base.trig_periph = ETM_TRIG_PERIPH_ANA_CMPR;
ESP_LOGD(TAG, "new event @%p, event_id=%"PRIu32", unit_id=%d", event, event_id, config->unit);
ESP_LOGD(TAG, "new event @%p, event_id=%"PRIu32", unit_id=%d", event, event_id, unit);
*ret_event = &event->base;
return ESP_OK;

View File

@ -0,0 +1,20 @@
/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include <stdbool.h>
#include "driver/ana_cmpr_types.h"
#ifdef __cplusplus
extern "C" {
#endif
ana_cmpr_unit_t ana_cmpr_priv_get_unit_by_handle(ana_cmpr_handle_t cmpr);
#ifdef __cplusplus
}
#endif

View File

@ -28,6 +28,12 @@ typedef struct {
* for external reference, the reference signal should be connect to `ANA_CMPRx_EXT_REF_GPIO`
*/
ana_cmpr_cross_type_t cross_type; /*!< The crossing types that can trigger interrupt */
int intr_priority; /*!< The interrupt priority, range 0~7, if set to 0, the driver will try to allocate an interrupt with a relative low priority (1,2,3)
* otherwise the larger the higher, 7 is NMI */
struct {
uint32_t io_loop_back:1; /*!< Enable this field when the other signals that output on the comparision pins are supposed to be fed back.
* Normally used for debug/test scenario */
} flags; /*!< Analog comparator driver flags */
} ana_cmpr_config_t;
/**

View File

@ -17,17 +17,36 @@
extern "C" {
#endif
/**
* @brief Analog Comparator ETM Events for each unit
*
*/
typedef enum {
ANA_CMPR_EVENT_POS_CROSS,
ANA_CMPR_EVENT_NEG_CROSS,
ANA_CMPR_EVENT_POS_CROSS, /*!< Positive cross event when the source signal becomes higher than the reference signal */
ANA_CMPR_EVENT_NEG_CROSS, /*!< Negative cross event when the source signal becomes lower than the reference signal */
} ana_cmpr_event_type_t;
/**
* @brief Analog Comparator ETM event configuration
*
*/
typedef struct {
ana_cmpr_unit_t unit;
ana_cmpr_event_type_t event_type; /*!< Which kind of cross type can trigger the ETM event module */
} ana_cmpr_etm_event_config_t;
esp_err_t ana_cmpr_new_etm_event(const ana_cmpr_etm_event_config_t *config, esp_etm_event_handle_t *ret_event);
/**
* @brief Allocate a new Analog Comparator ETM event
*
* @param[in] cmpr Analog Comparator handle that allocated by `ana_cmpr_new_unit`
* @param[in] config Analog Comparator ETM event configuration
* @param[out] ret_event The returned generic handle of ETM event, which is used to connect to a task in the ETM driver
* @return
* - ESP_OK Success to create the new ETM event handle
* - ESP_ERR_NO_MEM No memory for the ETM event
* - ESP_ERR_INVALID_ARG NULL pointer of the input parameters
* - ESP_ERR_INVALID_STATE The event on the unit has been registered
*/
esp_err_t ana_cmpr_new_etm_event(ana_cmpr_handle_t cmpr, const ana_cmpr_etm_event_config_t *config, esp_etm_event_handle_t *ret_event);
#ifdef __cplusplus
}

View File

@ -18,12 +18,9 @@ extern "C" {
* @brief Analog comparator unit
*
*/
typedef enum {
ANA_CMPR_UNIT_0, /*!< Analog Comparator unit 0 */
#if SOC_ANA_CMPR_NUM == 2
ANA_CMPR_UNIT_1, /*!< Analog Comparator unit 1 */
#endif
} ana_cmpr_unit_t;
typedef int ana_cmpr_unit_t;
#define ANA_CMPR_UNIT_0 0 /*!< @deprecated Analog comparator unit 0 */
/**
* @brief Analog comparator reference source
@ -94,10 +91,9 @@ typedef int ana_cmpr_clk_src_t;
*
*/
typedef struct {
#if SOC_ANA_CMPR_SUPPORT_MULTI_INTR
ana_cmpr_cross_type_t cross_type; /*!< The cross type of the target signal to the reference signal.
* Will either be ANA_CMPR_CROSS_POS or ANA_CMPR_CROSS_NEG */
#endif
* Will either be ANA_CMPR_CROSS_POS or ANA_CMPR_CROSS_NEG
* Always be ANA_CMPR_CROSS_ANY if target does not support independent interrupt (like ESP32H2) */
} ana_cmpr_cross_event_data_t;
/**

View File

@ -594,7 +594,7 @@ esp_err_t gpio_isr_register(void (*fn)(void *), void *arg, int intr_alloc_flags,
p.source = ETS_GPIO_INTR0_SOURCE;
#endif
p.intr_alloc_flags = intr_alloc_flags;
#if SOC_ANA_CMPR_SUPPORTED
#if SOC_ANA_CMPR_INTR_SHARE_WITH_GPIO
p.intr_alloc_flags |= ESP_INTR_FLAG_SHARED;
#endif
p.fn = fn;

View File

@ -3,6 +3,10 @@
components/driver/test_apps/analog_comparator:
disable:
- if: SOC_ANA_CMPR_SUPPORTED != 1
disable_test:
- if: IDF_TARGET == "esp32p4"
temporary: true
reason: not supported yet
components/driver/test_apps/dac_test_apps/dac:
disable:

View File

@ -1,2 +1,2 @@
| Supported Targets | ESP32-H2 |
| ----------------- | -------- |
| Supported Targets | ESP32-H2 | ESP32-P4 |
| ----------------- | -------- | -------- |

View File

@ -18,7 +18,7 @@ TEST_CASE("ana_cmpr_unit_install_uninstall", "[ana_cmpr]")
/* Allocate a wrong unit */
TEST_ESP_ERR(ESP_ERR_INVALID_ARG, ana_cmpr_new_unit(&config, &cmpr));
/* Allocate a correct unit */
config.unit = ANA_CMPR_UNIT_0;
config.unit = 0;
TEST_ESP_OK(ana_cmpr_new_unit(&config, &cmpr));
/* Try to allocate a existed unit */
TEST_ESP_ERR(ESP_ERR_INVALID_STATE, ana_cmpr_new_unit(&config, &cmpr));
@ -51,14 +51,15 @@ TEST_CASE("ana_cmpr_unit_install_uninstall", "[ana_cmpr]")
TEST_CASE("ana_cmpr_internal_reference", "[ana_cmpr]")
{
int src_chan = test_init_src_chan_gpio();
uint32_t cnt = 0;
uint32_t cnt = 0;
ana_cmpr_handle_t cmpr = NULL;
ana_cmpr_config_t config = {
.unit = ANA_CMPR_UNIT_0,
.unit = 0,
.clk_src = ANA_CMPR_CLK_SRC_DEFAULT,
.ref_src = ANA_CMPR_REF_SRC_INTERNAL,
.cross_type = ANA_CMPR_CROSS_ANY,
.flags.io_loop_back = 1,
};
TEST_ESP_OK(ana_cmpr_new_unit(&config, &cmpr));
ana_cmpr_internal_ref_config_t ref_cfg = {

View File

@ -19,7 +19,7 @@ bool IRAM_ATTR test_ana_cmpr_on_cross_callback(ana_cmpr_handle_t cmpr, const ana
int test_init_src_chan_gpio(void)
{
int src_chan_num = -1;
TEST_ESP_OK(ana_cmpr_get_gpio(ANA_CMPR_UNIT_0, ANA_CMPR_SOURCE_CHAN, &src_chan_num));
TEST_ESP_OK(ana_cmpr_get_gpio(0, ANA_CMPR_SOURCE_CHAN, &src_chan_num));
TEST_ASSERT(src_chan_num > 0);
gpio_config_t io_conf = {
.intr_type = GPIO_INTR_DISABLE,

View File

@ -44,10 +44,11 @@ TEST_CASE("ana_cmpr_internal_reference_iram_safe", "[ana_cmpr]")
ana_cmpr_handle_t cmpr = NULL;
ana_cmpr_config_t config = {
.unit = ANA_CMPR_UNIT_0,
.unit = 0,
.clk_src = ANA_CMPR_CLK_SRC_DEFAULT,
.ref_src = ANA_CMPR_REF_SRC_INTERNAL,
.cross_type = ANA_CMPR_CROSS_ANY,
.flags.io_loop_back = 1,
};
TEST_ESP_OK(ana_cmpr_new_unit(&config, &cmpr));
test_data.handle = cmpr;

View File

@ -28,5 +28,6 @@ esp_err_t io_mux_set_clock_source(soc_module_clk_t clk_src)
}
gpio_ll_iomux_set_clk_src(clk_src);
return ESP_OK;
}

View File

@ -28,5 +28,6 @@ esp_err_t io_mux_set_clock_source(soc_module_clk_t clk_src)
}
gpio_ll_iomux_set_clk_src(clk_src);
return ESP_OK;
}

View File

@ -0,0 +1,34 @@
/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "freertos/FreeRTOS.h"
#include "esp_private/io_mux.h"
#include "esp_private/periph_ctrl.h"
#include "hal/gpio_ll.h"
static portMUX_TYPE s_io_mux_spinlock = portMUX_INITIALIZER_UNLOCKED;
static soc_module_clk_t s_io_mux_clk_src = 0; // by default, the clock source is not set explicitly by any consumer (e.g. SDM, Filter)
esp_err_t io_mux_set_clock_source(soc_module_clk_t clk_src)
{
bool clk_conflict = false;
// check is the IO MUX has been set to another clock source
portENTER_CRITICAL(&s_io_mux_spinlock);
if (s_io_mux_clk_src != 0 && s_io_mux_clk_src != clk_src) {
clk_conflict = true;
} else {
s_io_mux_clk_src = clk_src;
}
portEXIT_CRITICAL(&s_io_mux_spinlock);
if (clk_conflict) {
return ESP_ERR_INVALID_STATE;
}
PERIPH_RCC_ATOMIC() {
gpio_ll_iomux_set_clk_src(clk_src);
}
return ESP_OK;
}

View File

@ -21,6 +21,11 @@ if(CONFIG_SOC_MCPWM_SUPPORT_ETM)
list(APPEND srcs "test_mcpwm_etm.c")
endif()
if(CONFIG_SOC_ANA_CMPR_SUPPORT_ETM AND CONFIG_SOC_TIMER_SUPPORT_ETM)
# Analog Comparator event test relies on GPTIMER task
list(APPEND srcs "test_ana_cmpr_etm.c")
endif()
# In order for the cases defined by `TEST_CASE` to be linked into the final elf,
# the component can be registered as WHOLE_ARCHIVE
idf_component_register(SRCS ${srcs}

View File

@ -0,0 +1,167 @@
/*
* SPDX-FileCopyrightText: 2023 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"
#define TEST_ANA_CMPR_UNIT 0
#define TEST_TIME_US 5000
static gptimer_handle_t s_test_ana_cmpr_gptimer_init(void)
{
gptimer_handle_t gptimer = NULL;
gptimer_config_t timer_config = {
.clk_src = GPTIMER_CLK_SRC_DEFAULT,
.direction = GPTIMER_COUNT_UP,
.resolution_hz = 1 * 1000 * 1000, // 1MHz, 1 tick = 1us
};
TEST_ESP_OK(gptimer_new_timer(&timer_config, &gptimer));
TEST_ESP_OK(gptimer_set_raw_count(gptimer, 0));
TEST_ESP_OK(gptimer_enable(gptimer));
return gptimer;
}
static void s_test_ana_cmpr_gptimer_deinit(gptimer_handle_t gptimer)
{
TEST_ESP_OK(gptimer_disable(gptimer));
TEST_ESP_OK(gptimer_del_timer(gptimer));
}
static ana_cmpr_handle_t s_test_ana_cmpr_init(void)
{
ana_cmpr_handle_t cmpr = NULL;
ana_cmpr_config_t config = {
.unit = TEST_ANA_CMPR_UNIT,
.clk_src = ANA_CMPR_CLK_SRC_DEFAULT,
.ref_src = ANA_CMPR_REF_SRC_INTERNAL,
.cross_type = ANA_CMPR_CROSS_ANY,
};
TEST_ESP_OK(ana_cmpr_new_unit(&config, &cmpr));
ana_cmpr_internal_ref_config_t ref_cfg = {
.ref_volt = ANA_CMPR_REF_VOLT_50_PCT_VDD,
};
TEST_ESP_OK(ana_cmpr_set_internal_reference(cmpr, &ref_cfg));
ana_cmpr_debounce_config_t dbc_cfg = {
.wait_us = 10,
};
TEST_ESP_OK(ana_cmpr_set_debounce(cmpr, &dbc_cfg));
TEST_ESP_OK(ana_cmpr_enable(cmpr));
return cmpr;
}
static void s_test_ana_cmpr_deinit(ana_cmpr_handle_t cmpr)
{
TEST_ESP_OK(ana_cmpr_disable(cmpr));
TEST_ESP_OK(ana_cmpr_del_unit(cmpr));
}
static int s_test_ana_cmpr_src_gpio_init(void)
{
int gpio_num = -1;
TEST_ESP_OK(ana_cmpr_get_gpio(TEST_ANA_CMPR_UNIT, ANA_CMPR_SOURCE_CHAN, &gpio_num));
gpio_config_t io_conf = {
.intr_type = GPIO_INTR_DISABLE,
.mode = GPIO_MODE_OUTPUT,
.pin_bit_mask = (1ULL << gpio_num),
.pull_down_en = false,
.pull_up_en = false,
};
gpio_config(&io_conf);
gpio_set_level(gpio_num, 1);
return gpio_num;
}
typedef struct {
esp_etm_event_handle_t cmpr_pos_evt;
esp_etm_event_handle_t cmpr_neg_evt;
esp_etm_task_handle_t gptimer_start_task;
esp_etm_task_handle_t gptimer_stop_task;
esp_etm_channel_handle_t etm_pos_handle;
esp_etm_channel_handle_t etm_neg_handle;
} test_ana_cmpr_etm_handles_t;
static test_ana_cmpr_etm_handles_t s_test_ana_cmpr_init_etm(ana_cmpr_handle_t cmpr, gptimer_handle_t gptimer)
{
test_ana_cmpr_etm_handles_t etm_handles = {};
/* Allocate the analog comparator positive & negative cross events */
ana_cmpr_etm_event_config_t evt_cfg = {
.event_type = ANA_CMPR_EVENT_POS_CROSS,
};
TEST_ESP_OK(ana_cmpr_new_etm_event(cmpr, &evt_cfg, &etm_handles.cmpr_pos_evt));
evt_cfg.event_type = GPIO_ETM_EVENT_EDGE_NEG;
TEST_ESP_OK(ana_cmpr_new_etm_event(cmpr, &evt_cfg, &etm_handles.cmpr_neg_evt));
/* Allocate the GPTimer tasks */
gptimer_etm_task_config_t gptimer_etm_task_conf = {
.task_type = GPTIMER_ETM_TASK_START_COUNT,
};
TEST_ESP_OK(gptimer_new_etm_task(gptimer, &gptimer_etm_task_conf, &etm_handles.gptimer_start_task));
gptimer_etm_task_conf.task_type = GPTIMER_ETM_TASK_STOP_COUNT;
TEST_ESP_OK(gptimer_new_etm_task(gptimer, &gptimer_etm_task_conf, &etm_handles.gptimer_stop_task));
/* Allocate the Event Task Matrix channels */
esp_etm_channel_config_t etm_cfg = {};
TEST_ESP_OK(esp_etm_new_channel(&etm_cfg, &etm_handles.etm_pos_handle));
TEST_ESP_OK(esp_etm_new_channel(&etm_cfg, &etm_handles.etm_neg_handle));
/* Bind the events and tasks */
TEST_ESP_OK(esp_etm_channel_connect(etm_handles.etm_pos_handle, etm_handles.cmpr_pos_evt, etm_handles.gptimer_start_task));
TEST_ESP_OK(esp_etm_channel_connect(etm_handles.etm_neg_handle, etm_handles.cmpr_neg_evt, etm_handles.gptimer_stop_task));
/* Enable the ETM channels */
TEST_ESP_OK(esp_etm_channel_enable(etm_handles.etm_pos_handle));
TEST_ESP_OK(esp_etm_channel_enable(etm_handles.etm_neg_handle));
return etm_handles;
}
static void s_test_ana_cmpr_deinit_etm(test_ana_cmpr_etm_handles_t handles)
{
TEST_ESP_OK(esp_etm_channel_disable(handles.etm_pos_handle));
TEST_ESP_OK(esp_etm_channel_disable(handles.etm_pos_handle));
TEST_ESP_OK(esp_etm_del_task(handles.gptimer_start_task));
TEST_ESP_OK(esp_etm_del_task(handles.gptimer_stop_task));
TEST_ESP_OK(esp_etm_del_event(handles.cmpr_pos_evt));
TEST_ESP_OK(esp_etm_del_event(handles.cmpr_neg_evt));
TEST_ESP_OK(esp_etm_del_channel(handles.etm_pos_handle));
TEST_ESP_OK(esp_etm_del_channel(handles.etm_neg_handle));
}
TEST_CASE("analog_comparator_etm_event", "[etm]")
{
gptimer_handle_t gptimer = s_test_ana_cmpr_gptimer_init();
ana_cmpr_handle_t cmpr = s_test_ana_cmpr_init();
int src_gpio = s_test_ana_cmpr_src_gpio_init();
test_ana_cmpr_etm_handles_t handles = s_test_ana_cmpr_init_etm(cmpr, gptimer);
gpio_set_level(src_gpio, 0);
esp_rom_delay_us(TEST_TIME_US);
gpio_set_level(src_gpio, 1);
uint64_t cnt_us = 0;
TEST_ESP_OK(gptimer_get_raw_count(gptimer, &cnt_us));
s_test_ana_cmpr_deinit_etm(handles);
s_test_ana_cmpr_deinit(cmpr);
s_test_ana_cmpr_gptimer_deinit(gptimer);
TEST_ASSERT_UINT64_WITHIN(TEST_TIME_US * 0.1, 5000, cnt_us);
}

View File

@ -9,9 +9,9 @@
#include <stdbool.h>
#include "hal/misc.h"
#include "hal/assert.h"
#include "soc/gpio_ext_struct.h"
#include "soc/ana_cmpr_struct.h"
#define ANALOG_CMPR_LL_GET_HW(unit) (&ANALOG_CMPR)
#define ANALOG_CMPR_LL_GET_HW(unit) (&ANALOG_CMPR[unit])
#define ANALOG_CMPR_LL_EVENT_CROSS (1 << 0)
#ifdef __cplusplus
@ -26,7 +26,7 @@ extern "C" {
*/
static inline void analog_cmpr_ll_enable(analog_cmpr_dev_t *hw, bool en)
{
hw->pad_comp_config.xpd_comp = en;
hw->pad_comp_config->xpd_comp = en;
}
/**
@ -38,7 +38,7 @@ static inline void analog_cmpr_ll_enable(analog_cmpr_dev_t *hw, bool en)
__attribute__((always_inline))
static inline void analog_cmpr_ll_set_internal_ref_voltage(analog_cmpr_dev_t *hw, uint32_t volt_level)
{
hw->pad_comp_config.dref_comp = volt_level;
hw->pad_comp_config->dref_comp = volt_level;
}
/**
@ -49,7 +49,7 @@ static inline void analog_cmpr_ll_set_internal_ref_voltage(analog_cmpr_dev_t *hw
*/
static inline float analog_cmpr_ll_get_internal_ref_voltage(analog_cmpr_dev_t *hw)
{
return hw->pad_comp_config.dref_comp * 0.1F;
return hw->pad_comp_config->dref_comp * 0.1F;
}
/**
@ -62,7 +62,7 @@ static inline float analog_cmpr_ll_get_internal_ref_voltage(analog_cmpr_dev_t *h
*/
static inline void analog_cmpr_ll_set_ref_source(analog_cmpr_dev_t *hw, uint32_t ref_src)
{
hw->pad_comp_config.mode_comp = ref_src;
hw->pad_comp_config->mode_comp = ref_src;
}
/**
@ -78,7 +78,7 @@ static inline void analog_cmpr_ll_set_ref_source(analog_cmpr_dev_t *hw, uint32_t
__attribute__((always_inline))
static inline void analog_cmpr_ll_set_cross_type(analog_cmpr_dev_t *hw, uint8_t type)
{
hw->pad_comp_config.zero_det_mode = type;
hw->pad_comp_config->zero_det_mode = type;
}
/**
@ -86,16 +86,14 @@ static inline void analog_cmpr_ll_set_cross_type(analog_cmpr_dev_t *hw, uint8_t
* @note Only one interrupt on H2
*
* @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
* @param type Not used on H2, because H2 can't distinguish the edge type
* The parameter here only to be compatible with other targets
* @return interrupt mask
*/
__attribute__((always_inline))
static inline uint32_t analog_cmpr_ll_get_intr_mask_by_type(analog_cmpr_dev_t *hw, uint8_t type)
{
(void)type;
return ANALOG_CMPR_LL_EVENT_CROSS;
}
@ -110,7 +108,7 @@ static inline uint32_t analog_cmpr_ll_get_intr_mask_by_type(analog_cmpr_dev_t *h
__attribute__((always_inline))
static inline void analog_cmpr_ll_set_debounce_cycle(analog_cmpr_dev_t *hw, uint32_t cycle)
{
hw->pad_comp_filter.zero_det_filter_cnt = cycle;
hw->pad_comp_filter->zero_det_filter_cnt = cycle;
}
/**
@ -122,11 +120,13 @@ static inline void analog_cmpr_ll_set_debounce_cycle(analog_cmpr_dev_t *hw, uint
*/
static inline void analog_cmpr_ll_enable_intr(analog_cmpr_dev_t *hw, uint32_t mask, bool enable)
{
uint32_t val = hw->int_ena->val;
if (enable) {
hw->int_ena.val |= mask;
val |= mask;
} else {
hw->int_ena.val &= ~mask;
val &= ~mask;
}
hw->int_ena->val = val;
}
/**
@ -137,7 +137,7 @@ static inline void analog_cmpr_ll_enable_intr(analog_cmpr_dev_t *hw, uint32_t ma
__attribute__((always_inline))
static inline uint32_t analog_cmpr_ll_get_intr_status(analog_cmpr_dev_t *hw)
{
return hw->int_st.val;
return hw->int_st->val;
}
/**
@ -149,7 +149,7 @@ static inline uint32_t analog_cmpr_ll_get_intr_status(analog_cmpr_dev_t *hw)
__attribute__((always_inline))
static inline void analog_cmpr_ll_clear_intr(analog_cmpr_dev_t *hw, uint32_t mask)
{
hw->int_clr.val = mask;
hw->int_clr->val = mask;
}
/**
@ -160,7 +160,7 @@ static inline void analog_cmpr_ll_clear_intr(analog_cmpr_dev_t *hw, uint32_t mas
*/
static inline volatile void *analog_cmpr_ll_get_intr_status_reg(analog_cmpr_dev_t *hw)
{
return &hw->int_st;
return hw->int_st;
}
#ifdef __cplusplus

View File

@ -19,12 +19,14 @@ 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_ETM_EVENTS_PER_UNIT (2)
#define ANALOG_CMPR_LL_NEG_CROSS_MASK(unit) (1UL << ((int)unit * 3))
#define ANALOG_CMPR_LL_POS_CROSS_MASK(unit) (1UL << ((int)unit * 3 + 1))
#define ANALOG_CMPR_LL_ETM_SOURCE(unit, type) (GPIO_EVT_ZERO_DET_POS0 + (unit) * 2 + (type))
/**
* @brief Enable analog comparator
*
@ -92,7 +94,7 @@ static inline uint32_t analog_cmpr_ll_get_intr_mask_by_type(analog_cmpr_dev_t *h
mask |= ANALOG_CMPR_LL_POS_CROSS_MASK(unit);
}
if (type & 0x02) {
mask |= ANALOG_CMPR_LL_POS_CROSS_MASK(unit);
mask |= ANALOG_CMPR_LL_NEG_CROSS_MASK(unit);
}
return mask;
}
@ -120,11 +122,13 @@ static inline void analog_cmpr_ll_set_debounce_cycle(analog_cmpr_dev_t *hw, uint
*/
static inline void analog_cmpr_ll_enable_intr(analog_cmpr_dev_t *hw, uint32_t mask, bool enable)
{
uint32_t val = hw->int_ena->val;
if (enable) {
hw->int_ena->val |= mask;
val |= mask;
} else {
hw->int_ena->val &= ~mask;
val &= ~mask;
}
hw->int_ena->val = val;
}
/**

View File

@ -603,6 +603,10 @@ static inline void gpio_ll_iomux_set_clk_src(soc_module_clk_t src)
}
}
/// use a macro to wrap the function, force the caller to use it in a critical section
/// the critical section needs to declare the __DECLARE_RCC_ATOMIC_ENV variable in advance
#define gpio_ll_iomux_set_clk_src(...) (void)__DECLARE_RCC_ATOMIC_ENV; gpio_ll_iomux_set_clk_src(__VA_ARGS__)
/**
* @brief Force hold digital io pad.
* @note GPIO force hold, whether the chip in sleep mode or wakeup mode.

View File

@ -5,11 +5,22 @@
*/
#include "soc/ana_cmpr_periph.h"
#include "soc/ana_cmpr_struct.h"
const ana_cmpr_conn_t ana_cmpr_io_map[SOC_ANA_CMPR_NUM] = {
const ana_cmpr_periph_t ana_cmpr_periph[SOC_ANA_CMPR_NUM] = {
[0] = {
.src_gpio = ANA_CMPR0_SRC_GPIO,
.ext_ref_gpio = ANA_CMPR0_EXT_REF_GPIO,
.intr_src = ETS_GPIO_INTR_SOURCE,
},
};
analog_cmpr_dev_t ANALOG_CMPR[SOC_ANA_CMPR_NUM] = {
[0] = {
.pad_comp_config = &GPIO_EXT.pad_comp_config,
.pad_comp_filter = &GPIO_EXT.pad_comp_filter,
.int_st = &GPIO_EXT.int_st,
.int_ena = &GPIO_EXT.int_ena,
.int_clr = &GPIO_EXT.int_clr,
},
};

View File

@ -483,6 +483,10 @@ config SOC_ANA_CMPR_NUM
int
default 1
config SOC_ANA_CMPR_INTR_SHARE_WITH_GPIO
bool
default y
config SOC_I2C_NUM
int
default 2

View File

@ -0,0 +1,36 @@
/**
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
/* NOTE: this file is created manually for compatibility */
#pragma once
#include <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_pad_comp_config_reg_t *pad_comp_config;
volatile gpio_pad_comp_filter_reg_t *pad_comp_filter;
volatile gpio_ext_int_st_reg_t *int_st;
volatile gpio_ext_int_ena_reg_t *int_ena;
volatile gpio_ext_int_clr_reg_t *int_clr;
} analog_cmpr_dev_t;
extern analog_cmpr_dev_t ANALOG_CMPR[1];
#ifdef __cplusplus
}
#endif

View File

@ -305,15 +305,10 @@ typedef struct {
volatile gpio_ext_version_reg_t version;
} gpio_ext_dev_t;
// analog comparator is a stand alone peripheral, but it is connected to GPIO
// so we rename it to analog_cmpr_dev_t from user's perspective
typedef gpio_ext_dev_t analog_cmpr_dev_t;
extern gpio_sd_dev_t SDM;
extern gpio_glitch_filter_dev_t GLITCH_FILTER;
extern gpio_etm_dev_t GPIO_ETM;
extern gpio_ext_dev_t GPIO_EXT;
extern analog_cmpr_dev_t ANALOG_CMPR;
#ifndef __cplusplus
_Static_assert(sizeof(gpio_ext_dev_t) == 0x100, "Invalid size of gpio_ext_dev_t structure");

View File

@ -221,6 +221,7 @@
/*------------------------- Analog Comparator CAPS ---------------------------*/
#define SOC_ANA_CMPR_NUM (1U)
#define SOC_ANA_CMPR_INTR_SHARE_WITH_GPIO (1)
/*-------------------------- I2C CAPS ----------------------------------------*/
// ESP32-H2 has 2 I2C

View File

@ -44,7 +44,6 @@ PROVIDE ( HMAC = 0x6008D000 );
PROVIDE ( IO_MUX = 0x60090000 );
PROVIDE ( GPIO = 0x60091000 );
PROVIDE ( GPIO_EXT = 0x60091f00 );
PROVIDE ( ANALOG_CMPR = 0x60091f00 );
PROVIDE ( SDM = 0x60091f00 );
PROVIDE ( GLITCH_FILTER = 0x60091f30 );
PROVIDE ( GPIO_ETM = 0x60091f60 );

View File

@ -7,7 +7,7 @@
#include "soc/ana_cmpr_periph.h"
#include "soc/ana_cmpr_struct.h"
const ana_cmpr_conn_t ana_cmpr_io_map[SOC_ANA_CMPR_NUM] = {
const ana_cmpr_periph_t ana_cmpr_periph[SOC_ANA_CMPR_NUM] = {
[0] = {
.src_gpio = ANA_CMPR0_SRC_GPIO,
.ext_ref_gpio = ANA_CMPR0_EXT_REF_GPIO,

View File

@ -391,7 +391,7 @@ config SOC_ANA_CMPR_NUM
int
default 2
config SOC_ANA_CMPR_SUPPORT_MULTI_INTR
config SOC_ANA_CMPR_CAN_DISTINGUISH_EDGE
bool
default y
@ -399,10 +399,6 @@ config SOC_ANA_CMPR_SUPPORT_ETM
bool
default y
config SOC_ANA_CMPR_ETM_EVENTS_PER_UNIT
int
default 2
config SOC_I2C_NUM
int
default 2

View File

@ -413,7 +413,7 @@ typedef enum {
#define SOC_ANA_CMPR_CLKS {SOC_MOD_CLK_PLL_F80M, SOC_MOD_CLK_XTAL}
/**
* @brief Sigma Delta Modulator clock source
* @brief Analog Comparator clock source
*/
typedef enum {
ANA_CMPR_CLK_SRC_XTAL = SOC_MOD_CLK_XTAL, /*!< Select XTAL clock as the source clock */

View File

@ -221,10 +221,9 @@
#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 (2U)
#define SOC_ANA_CMPR_SUPPORT_MULTI_INTR (1)
#define SOC_ANA_CMPR_NUM (2)
#define SOC_ANA_CMPR_CAN_DISTINGUISH_EDGE (1) // Support positive/negative/any cross interrupt
#define SOC_ANA_CMPR_SUPPORT_ETM (1)
#define SOC_ANA_CMPR_ETM_EVENTS_PER_UNIT (2)
/*-------------------------- I2C CAPS ----------------------------------------*/
// ESP32-P4 has 2 I2Cs

View File

@ -8,7 +8,7 @@
#include <stdint.h>
#include "soc/soc_caps.h"
#include "soc/periph_defs.h"
#include "soc/interrupts.h"
#if SOC_ANA_CMPR_SUPPORTED
#include "soc/ana_cmpr_channel.h"
#endif
@ -22,9 +22,9 @@ typedef struct {
int src_gpio;
int ext_ref_gpio;
int intr_src;
} ana_cmpr_conn_t;
} ana_cmpr_periph_t;
extern const ana_cmpr_conn_t ana_cmpr_io_map[SOC_ANA_CMPR_NUM];
extern const ana_cmpr_periph_t ana_cmpr_periph[SOC_ANA_CMPR_NUM];
#endif
#ifdef __cplusplus

View File

@ -73,6 +73,7 @@ INPUT = \
$(PROJECT_PATH)/components/bt/host/nimble/esp-hci/include/esp_nimble_hci.h \
$(PROJECT_PATH)/components/console/esp_console.h \
$(PROJECT_PATH)/components/driver/analog_comparator/include/driver/ana_cmpr.h \
$(PROJECT_PATH)/components/driver/analog_comparator/include/driver/ana_cmpr_etm.h \
$(PROJECT_PATH)/components/driver/analog_comparator/include/driver/ana_cmpr_types.h \
$(PROJECT_PATH)/components/driver/dac/include/driver/dac_continuous.h \
$(PROJECT_PATH)/components/driver/dac/include/driver/dac_cosine.h \

View File

@ -1,7 +1,6 @@
Analog Comparator
=================
{IDF_TARGET_ANA_CMPR_NUM: default="NOT UPDATED", esp32h2="one", esp32p4="two"}
{IDF_TARGET_ANA_CMPR_SRC_CHAN0: default="NOT UPDATED", esp32h2="GPIO11", esp32p4="GPIO52"}
{IDF_TARGET_ANA_CMPR_EXT_REF_CHAN0: default="NOT UPDATED", esp32h2="GPIO10", esp32p4="GPIO51"}
{IDF_TARGET_ANA_CMPR_SRC_CHAN1: default="NOT UPDATED", esp32p4="GPIO54"}
@ -14,13 +13,13 @@ Analog Comparator is a peripheral that can be used to compare a source signal wi
It is a cost effective way to replace an amplifier comparator in some scenarios. But unlike the continuous comparing of the amplifier comparator, ESP Analog Comparator is driven by a source clock, which decides the sampling frequency.
Analog Comparator on {IDF_TARGET_NAME} has {IDF_TARGET_ANA_CMPR_NUM} unit(s), the channels in the unit(s) are:
Analog Comparator on {IDF_TARGET_NAME} has {IDF_TARGET_SOC_ANA_CMPR_NUM} unit(s), the channels in the unit(s) are:
**UNIT0**
- Source Channel: {IDF_TARGET_ANA_CMPR_SRC_CHAN0}
- External Reference Channel: {IDF_TARGET_ANA_CMPR_EXT_REF_CHAN0}
- Internal Reference Channel: Range 0% ~ 70% VDD, step 10% VDD
- Internal Reference Channel: Range 0% ~ 70% of the VDD, the step is 10% of the VDD
.. only:: esp32p4
@ -28,7 +27,7 @@ Analog Comparator on {IDF_TARGET_NAME} has {IDF_TARGET_ANA_CMPR_NUM} unit(s), th
- Source Channel: {IDF_TARGET_ANA_CMPR_SRC_CHAN1}
- External Reference Channel: {IDF_TARGET_ANA_CMPR_EXT_REF_CHAN1}
- Internal Reference Channel: Range 0% ~ 70% VDD, step 10% VDD
- Internal Reference Channel: Range 0% ~ 70% of the VDD, the step is 10% of the VDD
Functional Overview
-------------------
@ -56,7 +55,7 @@ To allocate the resource of the Analog Comparator unit, :cpp:func:`ana_cmpr_new_
- :cpp:member:`ana_cmpr_config_t::unit` selects the Analog Comparator unit.
- :cpp:member:`ana_cmpr_config_t::clk_src` selects the source clock for Analog Comparator, it can affect the sampling frequency. Note that the clock source of the Analog Comparator comes from the io mux, it is shared with GPIO extension peripherals like SDM (Sigma-Delta Modulation) and Glitch Filter. The configuration will fail if you specific different clock sources for multiple GPIO extension peripherals. The default clock sources of these peripherals are same, typically, we select :cpp:enumerator:`soc_periph_ana_cmpr_clk_src_t::ANA_CMPR_CLK_SRC_DEFAULT` as the clock source.
- :cpp:member:`ana_cmpr_config_t::ref_src` selects the reference source from internal voltage or external signal (from {IDF_TARGET_ANA_CMPR_EXT_REF_CHAN}).
- :cpp:member:`ana_cmpr_config_t::ref_src` selects the reference source from internal voltage or external signal.
- :cpp:member:`ana_cmpr_config_t::cross_type` selects which kind of cross type can trigger the interrupt.
The function :cpp:func:`ana_cmpr_new_unit` can fail due to various errors such as insufficient memory, invalid arguments, etc. If a previously created Analog Comparator unit is no longer required, you should recycle it by calling :cpp:func:`ana_cmpr_del_unit`. It allows the underlying HW channel to be used for other purposes. Before deleting an Analog Comparator unit handle, you should disable it by :cpp:func:`ana_cmpr_unit_disable` in advance, or make sure it has not enabled yet by :cpp:func:`ana_cmpr_unit_enable`.
@ -67,7 +66,7 @@ The function :cpp:func:`ana_cmpr_new_unit` can fail due to various errors such a
ana_cmpr_handle_t cmpr = NULL;
ana_cmpr_config_t config = {
.unit = ANA_CMPR_UNIT_0,
.unit = 0,
.clk_src = ANA_CMPR_CLK_SRC_DEFAULT,
.ref_src = ANA_CMPR_REF_SRC_INTERNAL,
.cross_type = ANA_CMPR_CROSS_ANY,
@ -199,7 +198,7 @@ Kconfig Options
ETM Events
^^^^^^^^^^
To create an analog comparator cross event, you need to include ``driver/ana_cmpr_etm.h`` additionally, and allocate the event by :cpp:func:`ana_cmpr_new_etm_event`. You can refer to :doc:`GPTimer </api-reference/peripherals/etm>` for how to connect an event to a task.
To create an analog comparator cross event, you need to include ``driver/ana_cmpr_etm.h`` additionally, and allocate the event by :cpp:func:`ana_cmpr_new_etm_event`. You can refer to :doc:`ETM </api-reference/peripherals/etm>` for how to connect an event to a task.
Application Example
-------------------

View File

@ -70,9 +70,6 @@ Other Peripheral Events
:SOC_TIMER_SUPPORT_ETM: - Refer to :doc:`/api-reference/peripherals/gptimer` for how to get the ETM event handle from GPTimer.
:SOC_GDMA_SUPPORT_ETM: - Refer to :doc:`/api-reference/system/async_memcpy` for how to get the ETM event handle from async memcpy.
:SOC_MCPWM_SUPPORT_ETM: - Refer to :doc:`/api-reference/peripherals/mcpwm` for how to get the ETM event handle from MCPWM.
.. only:: esp32p4
:SOC_ANA_CMPR_SUPPORT_ETM: - Refer to :doc:`/api-reference/peripherals/ana_cmpr` for how to get the ETM event handle from analog comparator.
.. _etm-task:

View File

@ -70,9 +70,6 @@ GPIO **边沿** 事件是最常见的事件类型,任何 GPIO 管脚均可触
:SOC_TIMER_SUPPORT_ETM: - 要了解如何从 GPTimer 获取 ETM 事件句柄,请参阅 :doc:`/api-reference/peripherals/gptimer`。
:SOC_GDMA_SUPPORT_ETM: - 要了解如何从 async memcpy 获取 ETM 事件句柄,请参阅 :doc:`/api-reference/system/async_memcpy`。
:SOC_MCPWM_SUPPORT_ETM: - 要了解如何从 MCPWM 中获取 ETM 事件句柄,请参阅 :doc:`/api-reference/peripherals/mcpwm`。
.. only:: esp32p4
:SOC_ANA_CMPR_SUPPORT_ETM: - 要了解如何从模拟比较器获取 ETM 事件句柄,请参阅 :doc:`/api-reference/peripherals/ana_cmpr`。
.. _etm-task:

View File

@ -18,6 +18,10 @@ examples/peripherals/adc/oneshot_read:
examples/peripherals/analog_comparator:
disable:
- if: SOC_ANA_CMPR_SUPPORTED != 1
disable_test:
- if: IDF_TARGET == "esp32p4"
temporary: true
reason: not supported yet
examples/peripherals/dac:
disable:

View File

@ -1,11 +1,17 @@
| Supported Targets | ESP32-H2 |
| ----------------- | -------- |
| Supported Targets | ESP32-H2 | ESP32-P4 |
| ----------------- | -------- | -------- |
# Analog Comparator Example
(See the README.md file in the upper level 'examples' directory for more information about examples.)
This example is going to show how to use the Analog Comparator.
This example is going to show how to use the Analog Comparator. The example toggles a GPIO to monitor the positive and negative crosses on the analog comparator unit.
## Realization
- If the target supports generating the analog comparator events (like ESP32-P4), the example can toggle the monitoring GPIO via Event Task Matrix (ETM). ETM can bind the analog comparator cross events (positive and negative events) with the GPIO tasks (to set or clear a GPIO), so that every event happens, hardware will execute the corresponding task without CPU involved.
- If the target does not support to generate the analog comparator events (like ESP32-H2). The example will register an event callback on the analog comparator, and toggle the GPIO in the callback. It requires the CPU to process every event ISR, thus it is not effective as ETM and can't achieve a high resolution.
## How to Use Example

View File

@ -1,2 +1,10 @@
idf_component_register(SRCS "ana_cmpr_example_main.c"
set(src "ana_cmpr_example_main.c")
if(CONFIG_EXAMPLE_USE_ETM)
list(APPEND src "ana_cmpr_example_etm.c")
else()
list(APPEND src "ana_cmpr_example_intr.c")
endif()
idf_component_register(SRCS ${src}
INCLUDE_DIRS ".")

View File

@ -1,5 +1,20 @@
menu "Analog Comparator Example Configuration"
choice EXAMPLE_REALIZATION
prompt "Analog Comparator example realization methods"
default EXAMPLE_USE_ETM if SOC_ANA_CMPR_SUPPORT_ETM && SOC_GPIO_SUPPORT_ETM
default EXAMPLE_USE_INTR if !SOC_ANA_CMPR_SUPPORT_ETM || !SOC_GPIO_SUPPORT_ETM
config EXAMPLE_USE_INTR
bool "Use Interrupt"
help
Enable to set the monitor GPIO via interrupt callback
config EXAMPLE_USE_ETM
depends on SOC_ANA_CMPR_SUPPORT_ETM && SOC_GPIO_SUPPORT_ETM
bool "Use ETM"
help
Enable to set the monitor GPIO via Event Task Matrix
endchoice
choice EXAMPLE_REFERENCE_SOURCE
prompt "Analog Comparator reference source"
default EXAMPLE_INTERNAL_REF
@ -18,7 +33,7 @@ menu "Analog Comparator Example Configuration"
endchoice
config EXAMPLE_HYSTERESIS_COMPARATOR
depends on EXAMPLE_INTERNAL_REF
depends on EXAMPLE_INTERNAL_REF && !EXAMPLE_USE_ETM
bool "Enable hysteresis comparator"
default n
help

View File

@ -0,0 +1,133 @@
/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
#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 */
esp_etm_event_handle_t cmpr_pos_evt = NULL;
esp_etm_event_handle_t cmpr_neg_evt = NULL;
ana_cmpr_etm_event_config_t evt_cfg = {
.event_type = ANA_CMPR_EVENT_POS_CROSS,
};
ESP_ERROR_CHECK(ana_cmpr_new_etm_event(cmpr, &evt_cfg, &cmpr_pos_evt));
evt_cfg.event_type = GPIO_ETM_EVENT_EDGE_NEG;
ESP_ERROR_CHECK(ana_cmpr_new_etm_event(cmpr, &evt_cfg, &cmpr_neg_evt));
/* Allocate the GPIO set & clear tasks */
esp_etm_task_handle_t gpio_pos_task = NULL;
esp_etm_task_handle_t gpio_neg_task = NULL;
gpio_etm_task_config_t task_cfg = {
.action = GPIO_ETM_TASK_ACTION_SET,
};
ESP_ERROR_CHECK(gpio_new_etm_task(&task_cfg, &gpio_pos_task));
task_cfg.action = GPIO_ETM_TASK_ACTION_CLR;
ESP_ERROR_CHECK(gpio_new_etm_task(&task_cfg, &gpio_neg_task));
/* Add task to the monitor GPIO */
ESP_ERROR_CHECK(gpio_etm_task_add_gpio(gpio_pos_task, EXAMPLE_MONITOR_GPIO_NUM));
ESP_ERROR_CHECK(gpio_etm_task_add_gpio(gpio_neg_task, EXAMPLE_MONITOR_GPIO_NUM));
/* Allocate the Event Task Matrix channels */
esp_etm_channel_handle_t etm_pos_handle;
esp_etm_channel_handle_t etm_neg_handle;
esp_etm_channel_config_t etm_cfg = {};
ESP_ERROR_CHECK(esp_etm_new_channel(&etm_cfg, &etm_pos_handle));
ESP_ERROR_CHECK(esp_etm_new_channel(&etm_cfg, &etm_neg_handle));
/* Bind the events and tasks */
ESP_ERROR_CHECK(esp_etm_channel_connect(etm_pos_handle, cmpr_pos_evt, gpio_pos_task));
ESP_ERROR_CHECK(esp_etm_channel_connect(etm_neg_handle, cmpr_neg_evt, gpio_neg_task));
/* Enable the ETM channels */
ESP_ERROR_CHECK(esp_etm_channel_enable(etm_pos_handle));
ESP_ERROR_CHECK(esp_etm_channel_enable(etm_neg_handle));
}
ana_cmpr_handle_t example_init_analog_comparator_etm(void)
{
/* Step 0: Show the source channel and reference channel GPIO */
int src_gpio = -1;
int ext_ref_gpio = -1;
ESP_ERROR_CHECK(ana_cmpr_get_gpio(EXAMPLE_ANA_CMPR_UNIT, ANA_CMPR_SOURCE_CHAN, &src_gpio));
ESP_ERROR_CHECK(ana_cmpr_get_gpio(EXAMPLE_ANA_CMPR_UNIT, ANA_CMPR_EXT_REF_CHAN, &ext_ref_gpio));
ESP_LOGI(TAG, "Analog Comparator source gpio %d, external reference gpio %d", src_gpio, ext_ref_gpio);
ana_cmpr_handle_t cmpr = NULL;
#if CONFIG_EXAMPLE_INTERNAL_REF
/* Step 1: Allocate the new analog comparator unit */
ana_cmpr_config_t config = {
.unit = EXAMPLE_ANA_CMPR_UNIT,
.clk_src = ANA_CMPR_CLK_SRC_DEFAULT,
.ref_src = ANA_CMPR_REF_SRC_INTERNAL,
.cross_type = ANA_CMPR_CROSS_ANY,
};
ESP_ERROR_CHECK(ana_cmpr_new_unit(&config, &cmpr));
ESP_LOGI(TAG, "Allocate Analog Comparator with internal reference");
/* Step 1.1: As we are using the internal reference source, we need to configure the internal reference */
ana_cmpr_internal_ref_config_t ref_cfg = {
.ref_volt = ANA_CMPR_REF_VOLT_50_PCT_VDD,
};
ESP_ERROR_CHECK(ana_cmpr_set_internal_reference(cmpr, &ref_cfg));
#else
/* Step 1: Allocate the new analog comparator unit */
ana_cmpr_config_t config = {
.unit = EXAMPLE_ANA_CMPR_UNIT,
.clk_src = ANA_CMPR_CLK_SRC_DEFAULT,
.ref_src = ANA_CMPR_REF_SRC_EXTERNAL,
.cross_type = ANA_CMPR_CROSS_ANY,
};
ESP_ERROR_CHECK(ana_cmpr_new_unit(&config, &cmpr));
ESP_LOGI(TAG, "Allocate Analog Comparator with external reference");
#endif // CONFIG_EXAMPLE_INTERNAL_REF
/* Step 2: (Optional) Set the debounce configuration
* It's an optional configuration, if the wait time is set in debounce configuration,
* the cross interrupt will be disabled temporary after it is triggered, and it will be enabled
* automatically enabled after `wait_us`, so that the duplicate interrupts
* can be suppressed while the source signal crossing the reference signal. */
ana_cmpr_debounce_config_t dbc_cfg = {
/* Normally the `wait_us` is related to the relative frequency between the source and reference signal
* comparing to another one. This example adopts an approximate frequency as the relative signal
* frequency, and set the default wait time to EXAMPLE_WAIT_TIME_PROP of the relative signal period.
* We need to estimate an appropriate `freq_approx` and EXAMPLE_WAIT_TIME_PROP
* to make sure the interrupt neither triggers duplicate interrupts, nor misses the next crossing interrupt.
* Here we take 1 KHz for example */
.wait_us = EXAMPLE_WAITE_TIME_US(1000),
};
ESP_ERROR_CHECK(ana_cmpr_set_debounce(cmpr, &dbc_cfg));
/* Step 3: Enable the analog comparator unit */
ESP_ERROR_CHECK(ana_cmpr_enable(cmpr));
#if CONFIG_EXAMPLE_INTERNAL_REF
ESP_LOGI(TAG, "Analog comparator enabled, reference voltage: %d%% * VDD", (int)ref_cfg.ref_volt * 10);
#else
ESP_LOGI(TAG, "Analog comparator enabled, external reference selected");
#endif
return cmpr;
}
void example_analog_comparator_etm_app(void)
{
/* Initialize GPIO to monitor the comparator interrupt */
example_init_monitor_gpio();
/* Initialize Analog Comparator */
ana_cmpr_handle_t cmpr = example_init_analog_comparator_etm();
/* Connect the analog comparator events and gpio tasks via ETM channels */
example_etm_bind_ana_cmpr_event_with_gpio_task(cmpr);
}

View File

@ -0,0 +1,122 @@
/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
#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)
{
#if CONFIG_EXAMPLE_HYSTERESIS_COMPARATOR
static ana_cmpr_internal_ref_config_t ref_cfg = {
.ref_volt = ANA_CMPR_REF_VOLT_70_PCT_VDD,
};
bool is_70p = ref_cfg.ref_volt == ANA_CMPR_REF_VOLT_70_PCT_VDD;
/* Toggle the GPIO, monitor the gpio on an oscilloscope. */
gpio_set_level(EXAMPLE_MONITOR_GPIO_NUM, is_70p);
/* Set the internal reference voltage to 30% VDD and 70 %VDD alternately */
ana_cmpr_set_internal_reference(cmpr, &ref_cfg);
ref_cfg.ref_volt = is_70p ? ANA_CMPR_REF_VOLT_30_PCT_VDD : ANA_CMPR_REF_VOLT_70_PCT_VDD;
#else
static int lvl = 0;
/* Toggle the GPIO, monitor the gpio on a oscilloscope. */
gpio_set_level(EXAMPLE_MONITOR_GPIO_NUM, lvl);
lvl = !lvl;
#endif
return false;
}
void example_init_analog_comparator_intr(void)
{
/* Step 0: Show the source channel and reference channel GPIO */
int src_gpio = -1;
int ext_ref_gpio = -1;
ESP_ERROR_CHECK(ana_cmpr_get_gpio(EXAMPLE_ANA_CMPR_UNIT, ANA_CMPR_SOURCE_CHAN, &src_gpio));
ESP_ERROR_CHECK(ana_cmpr_get_gpio(EXAMPLE_ANA_CMPR_UNIT, ANA_CMPR_EXT_REF_CHAN, &ext_ref_gpio));
ESP_LOGI(TAG, "Analog Comparator source gpio %d, external reference gpio %d", src_gpio, ext_ref_gpio);
ana_cmpr_handle_t cmpr = NULL;
#if CONFIG_EXAMPLE_INTERNAL_REF
/* Step 1: Allocate the new analog comparator unit */
ana_cmpr_config_t config = {
.unit = EXAMPLE_ANA_CMPR_UNIT,
.clk_src = ANA_CMPR_CLK_SRC_DEFAULT,
.ref_src = ANA_CMPR_REF_SRC_INTERNAL,
.cross_type = ANA_CMPR_CROSS_ANY,
};
ESP_ERROR_CHECK(ana_cmpr_new_unit(&config, &cmpr));
ESP_LOGI(TAG, "Allocate Analog Comparator with internal reference");
/* Step 1.1: As we are using the internal reference source, we need to configure the internal reference */
ana_cmpr_internal_ref_config_t ref_cfg = {
#if CONFIG_EXAMPLE_HYSTERESIS_COMPARATOR
/* Set the initial internal reference voltage to 70% VDD, it will be updated in the callback every time the interrupt triggered */
.ref_volt = ANA_CMPR_REF_VOLT_70_PCT_VDD
#else
.ref_volt = ANA_CMPR_REF_VOLT_50_PCT_VDD,
#endif
};
ESP_ERROR_CHECK(ana_cmpr_set_internal_reference(cmpr, &ref_cfg));
#else
/* Step 1: Allocate the new analog comparator unit */
ana_cmpr_config_t config = {
.unit = EXAMPLE_ANA_CMPR_UNIT,
.clk_src = ANA_CMPR_CLK_SRC_DEFAULT,
.ref_src = ANA_CMPR_REF_SRC_EXTERNAL,
.cross_type = ANA_CMPR_CROSS_ANY,
};
ESP_ERROR_CHECK(ana_cmpr_new_unit(&config, &cmpr));
ESP_LOGI(TAG, "Allocate Analog Comparator with external reference");
#endif
/* Step 2: (Optional) Set the debounce configuration
* It's an optional configuration, if the wait time is set in debounce configuration,
* the cross interrupt will be disabled temporary after it is triggered, and it will be enabled
* automatically enabled after `wait_us`, so that the duplicate interrupts
* can be suppressed while the source signal crossing the reference signal. */
ana_cmpr_debounce_config_t dbc_cfg = {
/* Normally the `wait_us` is related to the relative frequency between the source and reference signal
* comparing to another one. This example adopts an approximate frequency as the relative signal
* frequency, and set the default wait time to EXAMPLE_WAIT_TIME_PROP of the relative signal period.
* We need to estimate an appropriate `freq_approx` and EXAMPLE_WAIT_TIME_PROP
* to make sure the interrupt neither triggers duplicate interrupts, nor misses the next crossing interrupt.
* Here we take 1 KHz for example */
.wait_us = EXAMPLE_WAITE_TIME_US(1000),
};
ESP_ERROR_CHECK(ana_cmpr_set_debounce(cmpr, &dbc_cfg));
/* Step 3: Register the event callbacks */
ana_cmpr_event_callbacks_t cbs = {
.on_cross = example_ana_cmpr_on_cross_callback,
};
ESP_ERROR_CHECK(ana_cmpr_register_event_callbacks(cmpr, &cbs, NULL));
/* Step 4: Enable the analog comparator unit */
ESP_ERROR_CHECK(ana_cmpr_enable(cmpr));
#if CONFIG_EXAMPLE_INTERNAL_REF
ESP_LOGI(TAG, "Analog comparator enabled, reference voltage: %d%% * VDD", (int)ref_cfg.ref_volt * 10);
#else
ESP_LOGI(TAG, "Analog comparator enabled, external reference selected");
#endif
}
void example_analog_comparator_intr_app(void)
{
/* Initialize GPIO to monitor the comparator interrupt */
example_init_monitor_gpio();
/* Initialize Analog Comparator */
example_init_analog_comparator_intr();
}

View File

@ -5,118 +5,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"
#define EXAMPLE_ANA_CMPR_UNIT ANA_CMPR_UNIT_0 // Analog Comparator unit
#define EXAMPLE_WAIT_TIME_PROP (0.1) // The wait time proportion in one relative signal period
#define EXAMPLE_WAITE_TIME_US(freq_approx) (uint32_t)(1000000 * EXAMPLE_WAIT_TIME_PROP / (freq_approx))
#define EXAMPLE_MONITOR_GPIO_NUM (0) // The gpio to monitor the on cross callback
static const char *TAG = "ana_cmpr_example";
static bool example_ana_cmpr_on_cross_callback(ana_cmpr_handle_t cmpr,
const ana_cmpr_cross_event_data_t *edata,
void *user_ctx)
{
#if CONFIG_EXAMPLE_HYSTERESIS_COMPARATOR
static ana_cmpr_internal_ref_config_t ref_cfg = {
.ref_volt = ANA_CMPR_REF_VOLT_70_PCT_VDD,
};
bool is_70p = ref_cfg.ref_volt == ANA_CMPR_REF_VOLT_70_PCT_VDD;
/* Toggle the GPIO, monitor the gpio on an oscilloscope. */
gpio_set_level(EXAMPLE_MONITOR_GPIO_NUM, is_70p);
/* Set the internal reference voltage to 30% VDD and 70 %VDD alternately */
ana_cmpr_set_internal_reference(cmpr, &ref_cfg);
ref_cfg.ref_volt = is_70p ? ANA_CMPR_REF_VOLT_30_PCT_VDD : ANA_CMPR_REF_VOLT_70_PCT_VDD;
#else
static int lvl = 0;
/* Toggle the GPIO, monitor the gpio on a oscilloscope. */
gpio_set_level(EXAMPLE_MONITOR_GPIO_NUM, lvl);
lvl = !lvl;
#endif
return false;
}
void example_init_analog_comparator(void)
{
/* Step 0: Show the source channel and reference channel GPIO */
int src_gpio = -1;
int ext_ref_gpio = -1;
ESP_ERROR_CHECK(ana_cmpr_get_gpio(EXAMPLE_ANA_CMPR_UNIT, ANA_CMPR_SOURCE_CHAN, &src_gpio));
ESP_ERROR_CHECK(ana_cmpr_get_gpio(EXAMPLE_ANA_CMPR_UNIT, ANA_CMPR_EXT_REF_CHAN, &ext_ref_gpio));
ESP_LOGI(TAG, "Analog Comparator source gpio %d, external reference gpio %d", src_gpio, ext_ref_gpio);
ana_cmpr_handle_t cmpr = NULL;
#if CONFIG_EXAMPLE_INTERNAL_REF
/* Step 1: Allocate the new analog comparator unit */
ana_cmpr_config_t config = {
.unit = EXAMPLE_ANA_CMPR_UNIT,
.clk_src = ANA_CMPR_CLK_SRC_DEFAULT,
.ref_src = ANA_CMPR_REF_SRC_INTERNAL,
.cross_type = ANA_CMPR_CROSS_ANY,
};
ESP_ERROR_CHECK(ana_cmpr_new_unit(&config, &cmpr));
ESP_LOGI(TAG, "Allocate Analog Comparator with internal reference");
/* Step 1.1: As we are using the internal reference source, we need to configure the internal reference */
ana_cmpr_internal_ref_config_t ref_cfg = {
#if CONFIG_EXAMPLE_HYSTERESIS_COMPARATOR
/* Set the initial internal reference voltage to 70% VDD, it will be updated in the callback every time the interrupt triggered */
.ref_volt = ANA_CMPR_REF_VOLT_70_PCT_VDD
#else
.ref_volt = ANA_CMPR_REF_VOLT_50_PCT_VDD,
#endif
};
ESP_ERROR_CHECK(ana_cmpr_set_internal_reference(cmpr, &ref_cfg));
#else
/* Step 1: Allocate the new analog comparator unit */
ana_cmpr_config_t config = {
.unit = EXAMPLE_ANA_CMPR_UNIT,
.clk_src = ANA_CMPR_CLK_SRC_DEFAULT,
.ref_src = ANA_CMPR_REF_SRC_EXTERNAL,
.cross_type = ANA_CMPR_CROSS_ANY,
};
ESP_ERROR_CHECK(ana_cmpr_new_unit(&config, &cmpr));
ESP_LOGI(TAG, "Allocate Analog Comparator with external reference");
#endif
/* Step 2: (Optional) Set the debounce configuration
* It's an optional configuration, if the wait time is set in debounce configuration,
* the cross interrupt will be disabled temporary after it is triggered, and it will be enabled
* automatically enabled after `wait_us`, so that the duplicate interrupts
* can be suppressed while the source signal crossing the reference signal. */
ana_cmpr_debounce_config_t dbc_cfg = {
/* Normally the `wait_us` is related to the relative frequency between the source and reference signal
* comparing to another one. This example adopts an approximate frequency as the relative signal
* frequency, and set the default wait time to EXAMPLE_WAIT_TIME_PROP of the relative signal period.
* We need to estimate an appropriate `freq_approx` and EXAMPLE_WAIT_TIME_PROP
* to make sure the interrupt neither triggers duplicate interrupts, nor misses the next crossing interrupt.
* Here we take 1 KHz for example */
.wait_us = EXAMPLE_WAITE_TIME_US(1000),
};
ESP_ERROR_CHECK(ana_cmpr_set_debounce(cmpr, &dbc_cfg));
/* Step 3: Register the event callbacks */
ana_cmpr_event_callbacks_t cbs = {
.on_cross = example_ana_cmpr_on_cross_callback,
};
ESP_ERROR_CHECK(ana_cmpr_register_event_callbacks(cmpr, &cbs, NULL));
/* Step 4: Enable the analog comparator unit */
ESP_ERROR_CHECK(ana_cmpr_enable(cmpr));
#if CONFIG_EXAMPLE_INTERNAL_REF
ESP_LOGI(TAG, "Analog comparator enabled, reference voltage: %d%% * VDD", (int)ref_cfg.ref_volt * 10);
#else
ESP_LOGI(TAG, "Analog comparator enabled, external reference selected");
#endif
}
#include "ana_cmpr_example_main.h"
void example_init_monitor_gpio(void)
{
@ -133,8 +23,9 @@ void example_init_monitor_gpio(void)
void app_main(void)
{
/* Initialize GPIO to monitor the comparator interrupt */
example_init_monitor_gpio();
/* Initialize Analog Comparator */
example_init_analog_comparator();
#if CONFIG_EXAMPLE_USE_ETM
example_analog_comparator_etm_app();
#else
example_analog_comparator_intr_app();
#endif
}

View File

@ -0,0 +1,45 @@
/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
#pragma once
#include <stdint.h>
#include "sdkconfig.h"
#include "soc/soc_caps.h"
#ifdef __cplusplus
extern "C" {
#endif
#define EXAMPLE_ANA_CMPR_UNIT 0 // Analog Comparator unit
#define EXAMPLE_WAIT_TIME_PROP (0.1) // The wait time proportion in one relative signal period
#define EXAMPLE_WAITE_TIME_US(freq_approx) (uint32_t)(1000000 * EXAMPLE_WAIT_TIME_PROP / (freq_approx))
#define EXAMPLE_MONITOR_GPIO_NUM (0) // The gpio to monitor the on cross callback
void example_init_monitor_gpio(void);
#if CONFIG_EXAMPLE_USE_ETM
/**
* @brief Set or clear the monitor GPIO via Event Task Matrix when cross interrupt triggers.
* @note The interrupt of analog comparator is regarded as Event,
* and the the operation of setting/clearing the GPIO is regarded as the corresponding task of the event.
* CPU won't be involved by using Event Task Matrix, so it can achieve relatively higher interrupt frequency
*/
void example_analog_comparator_etm_app(void);
#endif
/**
* @brief Set or clear the monitor GPIO in the cross interrupt callback.
* @note The GPIO level is set manually in the callback.
* It's more flexible so that we can realize some operation like hysteresis,
* But as the operations are done in callback, CPU is involved, so it can only achieve a low interrupt frequency
*/
void example_analog_comparator_intr_app(void);
#ifdef __cplusplus
}
#endif