mirror of
https://github.com/espressif/esp-idf.git
synced 2025-08-16 02:51:03 +02:00
ana_cmpr: add an example and test cases
This commit is contained in:
@@ -1,5 +1,9 @@
|
|||||||
# Documentation: .gitlab/ci/README.md#manifest-file-to-control-the-buildtest-apps
|
# Documentation: .gitlab/ci/README.md#manifest-file-to-control-the-buildtest-apps
|
||||||
|
|
||||||
|
components/driver/test_apps/analog_comparator:
|
||||||
|
disable:
|
||||||
|
- if: SOC_ANA_CMPR_SUPPORTED != 1
|
||||||
|
|
||||||
components/driver/test_apps/dac_test_apps/dac:
|
components/driver/test_apps/dac_test_apps/dac:
|
||||||
disable:
|
disable:
|
||||||
- if: SOC_DAC_SUPPORTED != 1
|
- if: SOC_DAC_SUPPORTED != 1
|
||||||
|
@@ -20,6 +20,7 @@
|
|||||||
#include "esp_pm.h"
|
#include "esp_pm.h"
|
||||||
#include "esp_heap_caps.h"
|
#include "esp_heap_caps.h"
|
||||||
#include "esp_intr_alloc.h"
|
#include "esp_intr_alloc.h"
|
||||||
|
#include "esp_memory_utils.h"
|
||||||
#include "soc/periph_defs.h"
|
#include "soc/periph_defs.h"
|
||||||
#include "soc/ana_cmpr_periph.h"
|
#include "soc/ana_cmpr_periph.h"
|
||||||
#include "hal/ana_cmpr_ll.h"
|
#include "hal/ana_cmpr_ll.h"
|
||||||
@@ -30,6 +31,7 @@
|
|||||||
struct ana_cmpr_t {
|
struct ana_cmpr_t {
|
||||||
ana_cmpr_unit_t unit; /*!< Analog comparator unit id */
|
ana_cmpr_unit_t unit; /*!< Analog comparator unit id */
|
||||||
analog_cmpr_dev_t *dev; /*!< Analog comparator unit device address */
|
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 */
|
bool is_enabled; /*!< Whether the Analog comparator unit is enabled */
|
||||||
ana_cmpr_event_callbacks_t cbs; /*!< The callback group that set by user */
|
ana_cmpr_event_callbacks_t cbs; /*!< The callback group that set by user */
|
||||||
intr_handle_t intr_handle; /*!< Interrupt handle */
|
intr_handle_t intr_handle; /*!< Interrupt handle */
|
||||||
@@ -46,9 +48,11 @@ struct ana_cmpr_t {
|
|||||||
|
|
||||||
/* Memory allocation caps which decide the section that memory supposed to allocate */
|
/* Memory allocation caps which decide the section that memory supposed to allocate */
|
||||||
#if CONFIG_ANA_CMPR_ISR_IRAM_SAFE
|
#if CONFIG_ANA_CMPR_ISR_IRAM_SAFE
|
||||||
#define ANA_CMPR_MEM_ALLOC_CAPS (MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT)
|
#define ANA_CMPR_MEM_ALLOC_CAPS (MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT)
|
||||||
|
#define ANA_CMPR_INTR_FLAG (ESP_INTR_FLAG_LEVEL1 | ESP_INTR_FLAG_IRAM)
|
||||||
#else
|
#else
|
||||||
#define ANA_CMPR_MEM_ALLOC_CAPS MALLOC_CAP_DEFAULT
|
#define ANA_CMPR_MEM_ALLOC_CAPS MALLOC_CAP_DEFAULT
|
||||||
|
#define ANA_CMPR_INTR_FLAG ESP_INTR_FLAG_LEVEL1
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* Driver tag */
|
/* Driver tag */
|
||||||
@@ -99,6 +103,7 @@ esp_err_t ana_cmpr_new_unit(const ana_cmpr_config_t *config, ana_cmpr_handle_t *
|
|||||||
|
|
||||||
/* Assign analog comparator unit */
|
/* Assign analog comparator unit */
|
||||||
s_ana_cmpr[unit]->dev = ANALOG_CMPR_LL_GET_HW();
|
s_ana_cmpr[unit]->dev = ANALOG_CMPR_LL_GET_HW();
|
||||||
|
s_ana_cmpr[unit]->ref_src = config->ref_src;
|
||||||
s_ana_cmpr[unit]->is_enabled = false;
|
s_ana_cmpr[unit]->is_enabled = false;
|
||||||
s_ana_cmpr[unit]->pm_lock = NULL;
|
s_ana_cmpr[unit]->pm_lock = NULL;
|
||||||
|
|
||||||
@@ -124,7 +129,7 @@ esp_err_t ana_cmpr_new_unit(const ana_cmpr_config_t *config, ana_cmpr_handle_t *
|
|||||||
portEXIT_CRITICAL(&s_spinlock);
|
portEXIT_CRITICAL(&s_spinlock);
|
||||||
|
|
||||||
/* Allocate the interrupt, currently the interrupt source of Analog Comparator is shared with GPIO interrupt source */
|
/* Allocate the interrupt, currently the interrupt source of Analog Comparator is shared with GPIO interrupt source */
|
||||||
esp_intr_alloc(ETS_GPIO_INTR_SOURCE, ESP_INTR_FLAG_LEVEL1,
|
esp_intr_alloc(ETS_GPIO_INTR_SOURCE, ANA_CMPR_INTR_FLAG,
|
||||||
s_ana_cmpr_default_intr_handler, s_ana_cmpr[unit], &s_ana_cmpr[unit]->intr_handle);
|
s_ana_cmpr_default_intr_handler, s_ana_cmpr[unit], &s_ana_cmpr[unit]->intr_handle);
|
||||||
|
|
||||||
if (config->ref_src == ANA_CMPR_REF_SRC_INTERNAL) {
|
if (config->ref_src == ANA_CMPR_REF_SRC_INTERNAL) {
|
||||||
@@ -183,11 +188,13 @@ esp_err_t ana_cmpr_set_intl_reference(ana_cmpr_handle_t cmpr, const ana_cmpr_int
|
|||||||
{
|
{
|
||||||
ANA_CMPR_NULL_POINTER_CHECK_ISR(cmpr);
|
ANA_CMPR_NULL_POINTER_CHECK_ISR(cmpr);
|
||||||
ANA_CMPR_NULL_POINTER_CHECK_ISR(ref_cfg);
|
ANA_CMPR_NULL_POINTER_CHECK_ISR(ref_cfg);
|
||||||
|
ESP_RETURN_ON_FALSE_ISR(cmpr->ref_src == ANA_CMPR_REF_SRC_INTERNAL, ESP_ERR_INVALID_STATE,
|
||||||
|
TAG, "the reference channel is not internal, no need to configure internal reference");
|
||||||
|
|
||||||
/* Set internal reference voltage */
|
/* Set internal reference voltage */
|
||||||
portENTER_CRITICAL_ISR(&s_spinlock);
|
portENTER_CRITICAL_SAFE(&s_spinlock);
|
||||||
analog_cmpr_ll_set_internal_ref_voltage(cmpr->dev, ref_cfg->ref_volt);
|
analog_cmpr_ll_set_internal_ref_voltage(cmpr->dev, ref_cfg->ref_volt);
|
||||||
portEXIT_CRITICAL_ISR(&s_spinlock);
|
portEXIT_CRITICAL_SAFE(&s_spinlock);
|
||||||
|
|
||||||
ESP_EARLY_LOGD(TAG, "unit %d internal voltage level %"PRIu32, (int)cmpr->unit, ref_cfg->ref_volt);
|
ESP_EARLY_LOGD(TAG, "unit %d internal voltage level %"PRIu32, (int)cmpr->unit, ref_cfg->ref_volt);
|
||||||
|
|
||||||
@@ -202,9 +209,9 @@ esp_err_t ana_cmpr_set_debounce(ana_cmpr_handle_t cmpr, const ana_cmpr_debounce_
|
|||||||
/* Transfer the time to clock cycles */
|
/* Transfer the time to clock cycles */
|
||||||
uint32_t wait_cycle = (uint32_t)(dbc_cfg->wait_us * cmpr->src_clk_freq_hz) / 1000000;
|
uint32_t wait_cycle = (uint32_t)(dbc_cfg->wait_us * cmpr->src_clk_freq_hz) / 1000000;
|
||||||
/* Set the waiting clock cycles */
|
/* Set the waiting clock cycles */
|
||||||
portENTER_CRITICAL_ISR(&s_spinlock);
|
portENTER_CRITICAL_SAFE(&s_spinlock);
|
||||||
analog_cmpr_ll_set_debounce_cycle(cmpr->dev, wait_cycle);
|
analog_cmpr_ll_set_debounce_cycle(cmpr->dev, wait_cycle);
|
||||||
portEXIT_CRITICAL_ISR(&s_spinlock);
|
portEXIT_CRITICAL_SAFE(&s_spinlock);
|
||||||
|
|
||||||
ESP_EARLY_LOGD(TAG, "unit %d debounce wait cycle %"PRIu32, (int)cmpr->unit, wait_cycle);
|
ESP_EARLY_LOGD(TAG, "unit %d debounce wait cycle %"PRIu32, (int)cmpr->unit, wait_cycle);
|
||||||
|
|
||||||
@@ -217,6 +224,12 @@ esp_err_t ana_cmpr_register_event_callbacks(ana_cmpr_handle_t cmpr, const ana_cm
|
|||||||
ANA_CMPR_NULL_POINTER_CHECK(cbs);
|
ANA_CMPR_NULL_POINTER_CHECK(cbs);
|
||||||
ESP_RETURN_ON_FALSE(!cmpr->is_enabled, ESP_ERR_INVALID_STATE, TAG,
|
ESP_RETURN_ON_FALSE(!cmpr->is_enabled, ESP_ERR_INVALID_STATE, TAG,
|
||||||
"please disable the analog comparator before registering the callbacks");
|
"please disable the analog comparator before registering the callbacks");
|
||||||
|
#if CONFIG_ANA_CMPR_ISR_IRAM_SAFE
|
||||||
|
if (cbs->on_cross) {
|
||||||
|
ESP_RETURN_ON_FALSE(esp_ptr_in_iram(cbs->on_cross), ESP_ERR_INVALID_ARG, TAG,
|
||||||
|
"ANA_CMPR_ISR_IRAM_SAFE enabled but the callback function not in IRAM");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
/* Save the callback group */
|
/* Save the callback group */
|
||||||
memcpy(&(cmpr->cbs), cbs, sizeof(ana_cmpr_event_callbacks_t));
|
memcpy(&(cmpr->cbs), cbs, sizeof(ana_cmpr_event_callbacks_t));
|
||||||
|
@@ -90,7 +90,7 @@ typedef int ana_cmpr_clk_src_t;
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
typedef struct {
|
typedef struct {
|
||||||
// Currently no data
|
// No data for now
|
||||||
} ana_cmpr_cross_event_data_t;
|
} ana_cmpr_cross_event_data_t;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
18
components/driver/test_apps/analog_comparator/CMakeLists.txt
Normal file
18
components/driver/test_apps/analog_comparator/CMakeLists.txt
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
# This is the project CMakeLists.txt file for the test subproject
|
||||||
|
cmake_minimum_required(VERSION 3.16)
|
||||||
|
|
||||||
|
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||||
|
project(test_ana_cmpr)
|
||||||
|
|
||||||
|
if(CONFIG_COMPILER_DUMP_RTL_FILES)
|
||||||
|
add_custom_target(check_test_app_sections ALL
|
||||||
|
COMMAND ${PYTHON} $ENV{IDF_PATH}/tools/ci/check_callgraph.py
|
||||||
|
--rtl-dir ${CMAKE_BINARY_DIR}/esp-idf/driver/
|
||||||
|
--elf-file ${CMAKE_BINARY_DIR}/test_ana_cmpr.elf
|
||||||
|
find-refs
|
||||||
|
--from-sections=.iram0.text
|
||||||
|
--to-sections=.flash.text
|
||||||
|
--exit-code
|
||||||
|
DEPENDS ${elf}
|
||||||
|
)
|
||||||
|
endif()
|
2
components/driver/test_apps/analog_comparator/README.md
Normal file
2
components/driver/test_apps/analog_comparator/README.md
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
| Supported Targets | ESP32-H2 |
|
||||||
|
| ----------------- | -------- |
|
@@ -0,0 +1,11 @@
|
|||||||
|
set(srcs "test_app_main.c"
|
||||||
|
"test_ana_cmpr_common.c"
|
||||||
|
"test_ana_cmpr.c")
|
||||||
|
|
||||||
|
if(CONFIG_ANA_CMPR_ISR_IRAM_SAFE)
|
||||||
|
list(APPEND srcs "test_ana_cmpr_iram.c")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
idf_component_register(SRCS ${srcs}
|
||||||
|
INCLUDE_DIRS "."
|
||||||
|
WHOLE_ARCHIVE)
|
@@ -0,0 +1,86 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "test_ana_cmpr.h"
|
||||||
|
|
||||||
|
TEST_CASE("ana_cmpr_unit_install_uninstall", "[ana_cmpr]")
|
||||||
|
{
|
||||||
|
ana_cmpr_handle_t cmpr = NULL;
|
||||||
|
ana_cmpr_config_t config = {
|
||||||
|
.unit = SOC_ANA_CMPR_NUM, // Set a wrong unit
|
||||||
|
.clk_src = ANA_CMPR_CLK_SRC_DEFAULT,
|
||||||
|
.ref_src = ANA_CMPR_REF_SRC_INTERNAL,
|
||||||
|
.intr_type = ANA_CMPR_INTR_ANY_CROSS,
|
||||||
|
};
|
||||||
|
/* Allocate a wrong unit */
|
||||||
|
TEST_ESP_ERR(ESP_ERR_INVALID_ARG, ana_cmpr_new_unit(&config, &cmpr));
|
||||||
|
/* Allocate a correct unit */
|
||||||
|
config.unit = ANA_CMPR_UNIT_0;
|
||||||
|
TEST_ESP_OK(ana_cmpr_new_unit(&config, &cmpr));
|
||||||
|
/* Try to allocate a existed unit */
|
||||||
|
TEST_ESP_ERR(ESP_ERR_INVALID_STATE, ana_cmpr_new_unit(&config, &cmpr));
|
||||||
|
/* Set the internal reference before enable */
|
||||||
|
ana_cmpr_intl_ref_config_t ref_cfg = {
|
||||||
|
.ref_volt = ANA_CMPR_REF_VOLT_50_PCT_VDD,
|
||||||
|
};
|
||||||
|
TEST_ESP_OK(ana_cmpr_set_intl_reference(cmpr, &ref_cfg));
|
||||||
|
/* Enable the unit */
|
||||||
|
TEST_ESP_OK(ana_cmpr_enable(cmpr));
|
||||||
|
/* Set the internal reference after enable */
|
||||||
|
ref_cfg.ref_volt = ANA_CMPR_REF_VOLT_30_PCT_VDD;
|
||||||
|
TEST_ESP_OK(ana_cmpr_set_intl_reference(cmpr, &ref_cfg));
|
||||||
|
/* Try tp delete unit after enable */
|
||||||
|
TEST_ESP_ERR(ESP_ERR_INVALID_STATE, ana_cmpr_del_unit(cmpr));
|
||||||
|
/* Disable the unit */
|
||||||
|
TEST_ESP_OK(ana_cmpr_disable(cmpr));
|
||||||
|
/* Try to delete the unit with a wrong handle */
|
||||||
|
TEST_ESP_ERR(ESP_ERR_INVALID_STATE, ana_cmpr_del_unit((void *)&cmpr));
|
||||||
|
/* Delete the unit */
|
||||||
|
TEST_ESP_OK(ana_cmpr_del_unit(cmpr));
|
||||||
|
|
||||||
|
/* Try to set internal reference for a external unit */
|
||||||
|
config.ref_src = ANA_CMPR_REF_SRC_EXTERNAL;
|
||||||
|
TEST_ESP_OK(ana_cmpr_new_unit(&config, &cmpr));
|
||||||
|
TEST_ESP_ERR(ESP_ERR_INVALID_STATE, ana_cmpr_set_intl_reference(cmpr, &ref_cfg));
|
||||||
|
TEST_ESP_OK(ana_cmpr_del_unit(cmpr));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("ana_cmpr_internal_reference", "[ana_cmpr]")
|
||||||
|
{
|
||||||
|
int src_chan = test_init_src_chan_gpio();
|
||||||
|
uint32_t cnt = 0;
|
||||||
|
|
||||||
|
ana_cmpr_handle_t cmpr = NULL;
|
||||||
|
ana_cmpr_config_t config = {
|
||||||
|
.unit = ANA_CMPR_UNIT_0,
|
||||||
|
.clk_src = ANA_CMPR_CLK_SRC_DEFAULT,
|
||||||
|
.ref_src = ANA_CMPR_REF_SRC_INTERNAL,
|
||||||
|
.intr_type = ANA_CMPR_INTR_ANY_CROSS,
|
||||||
|
};
|
||||||
|
TEST_ESP_OK(ana_cmpr_new_unit(&config, &cmpr));
|
||||||
|
ana_cmpr_intl_ref_config_t ref_cfg = {
|
||||||
|
.ref_volt = ANA_CMPR_REF_VOLT_50_PCT_VDD,
|
||||||
|
};
|
||||||
|
TEST_ESP_OK(ana_cmpr_set_intl_reference(cmpr, &ref_cfg));
|
||||||
|
ana_cmpr_debounce_config_t dbc_cfg = {
|
||||||
|
.wait_us = 10.0,
|
||||||
|
};
|
||||||
|
TEST_ESP_OK(ana_cmpr_set_debounce(cmpr, &dbc_cfg));
|
||||||
|
ana_cmpr_event_callbacks_t cbs = {
|
||||||
|
.on_cross = test_ana_cmpr_on_cross_callback,
|
||||||
|
};
|
||||||
|
|
||||||
|
TEST_ESP_OK(ana_cmpr_register_event_callbacks(cmpr, &cbs, &cnt));
|
||||||
|
TEST_ESP_OK(ana_cmpr_enable(cmpr));
|
||||||
|
cnt = 0;
|
||||||
|
for (int i = 1; i <= 10; i++) {
|
||||||
|
test_simulate_src_signal(src_chan, i % 2);
|
||||||
|
esp_rom_delay_us(100);
|
||||||
|
TEST_ASSERT(cnt == i);
|
||||||
|
}
|
||||||
|
TEST_ESP_OK(ana_cmpr_disable(cmpr));
|
||||||
|
TEST_ESP_OK(ana_cmpr_del_unit(cmpr));
|
||||||
|
}
|
@@ -0,0 +1,43 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include "sdkconfig.h"
|
||||||
|
#include "esp_attr.h"
|
||||||
|
#include "unity.h"
|
||||||
|
#include "freertos/FreeRTOS.h"
|
||||||
|
#include "freertos/task.h"
|
||||||
|
#include "esp_rom_sys.h"
|
||||||
|
#include "soc/soc_caps.h"
|
||||||
|
#include "driver/ana_cmpr.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Test default on cross callback
|
||||||
|
*
|
||||||
|
* @param cmpr Analog Comparator handle
|
||||||
|
* @param edata Event data
|
||||||
|
* @param user_ctx User context, need to input a unint32_t counter
|
||||||
|
* @return
|
||||||
|
* - true Need to yield
|
||||||
|
* - false Don't need yield
|
||||||
|
*/
|
||||||
|
bool test_ana_cmpr_on_cross_callback(ana_cmpr_handle_t cmpr, const ana_cmpr_cross_event_data_t *edata, void *user_ctx);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Initialize Analog Comparator source channel GPIO
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* - int Source channel GPIO number
|
||||||
|
*/
|
||||||
|
int test_init_src_chan_gpio(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Simulate source channel signal
|
||||||
|
*
|
||||||
|
* @param src_chan Source channel GPIO number
|
||||||
|
* @param val 0 to set low, others to set high
|
||||||
|
*/
|
||||||
|
void test_simulate_src_signal(int src_chan, uint32_t val);
|
@@ -0,0 +1,39 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "test_ana_cmpr.h"
|
||||||
|
#include "hal/gpio_ll.h"
|
||||||
|
#include "driver/gpio.h"
|
||||||
|
#include "esp_attr.h"
|
||||||
|
|
||||||
|
bool IRAM_ATTR test_ana_cmpr_on_cross_callback(ana_cmpr_handle_t cmpr, const ana_cmpr_cross_event_data_t *edata, void *user_ctx)
|
||||||
|
{
|
||||||
|
uint32_t *count = (uint32_t *)user_ctx;
|
||||||
|
(*count)++;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int test_init_src_chan_gpio(void)
|
||||||
|
{
|
||||||
|
int src_chan_num = -1;
|
||||||
|
TEST_ESP_OK(ana_cmpr_get_gpio(ANA_CMPR_UNIT_0, ANA_CMPR_SOURCE_CHAN, &src_chan_num));
|
||||||
|
TEST_ASSERT(src_chan_num > 0);
|
||||||
|
gpio_config_t io_conf = {
|
||||||
|
.intr_type = GPIO_INTR_DISABLE,
|
||||||
|
.mode = GPIO_MODE_OUTPUT,
|
||||||
|
.pin_bit_mask = (1ULL << src_chan_num),
|
||||||
|
.pull_down_en = false,
|
||||||
|
.pull_up_en = false,
|
||||||
|
};
|
||||||
|
TEST_ESP_OK(gpio_config(&io_conf));
|
||||||
|
TEST_ESP_OK(gpio_set_level(src_chan_num, 0));
|
||||||
|
return src_chan_num;
|
||||||
|
}
|
||||||
|
|
||||||
|
void IRAM_ATTR test_simulate_src_signal(int src_chan, uint32_t val)
|
||||||
|
{
|
||||||
|
gpio_set_level(src_chan, val);
|
||||||
|
}
|
@@ -0,0 +1,76 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "test_ana_cmpr.h"
|
||||||
|
#include "unity_test_utils.h"
|
||||||
|
#include "unity_test_utils_cache.h"
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
ana_cmpr_handle_t handle;
|
||||||
|
uint32_t count;
|
||||||
|
int src_chan;
|
||||||
|
} test_ana_cmpr_data_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Global debounce configuration
|
||||||
|
* @note Why it is global?
|
||||||
|
* If we declare a local variable in 'float' type when cache disabled,
|
||||||
|
* it may be allocated in the flash, which can lead crash when trying to access it.
|
||||||
|
*/
|
||||||
|
static ana_cmpr_debounce_config_t IRAM_ATTR s_dbc_cfg = {
|
||||||
|
.wait_us = 1.0,
|
||||||
|
};
|
||||||
|
|
||||||
|
static void IRAM_ATTR test_ana_cmpr_iram_safety(void *args)
|
||||||
|
{
|
||||||
|
test_ana_cmpr_data_t *data = (test_ana_cmpr_data_t *)args;
|
||||||
|
ana_cmpr_intl_ref_config_t ref_cfg = {
|
||||||
|
.ref_volt = ANA_CMPR_REF_VOLT_50_PCT_VDD,
|
||||||
|
};
|
||||||
|
ana_cmpr_set_intl_reference(data->handle, &ref_cfg);
|
||||||
|
ana_cmpr_set_debounce(data->handle, &s_dbc_cfg);
|
||||||
|
data->count = 0;
|
||||||
|
for (int i = 1; i <= 10; i++) {
|
||||||
|
test_simulate_src_signal(data->src_chan, i % 2);
|
||||||
|
esp_rom_delay_us(100);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("ana_cmpr_internal_reference_iram_safe", "[ana_cmpr]")
|
||||||
|
{
|
||||||
|
test_ana_cmpr_data_t test_data = {
|
||||||
|
.handle = NULL,
|
||||||
|
.count = 0,
|
||||||
|
.src_chan = -1,
|
||||||
|
};
|
||||||
|
test_data.src_chan = test_init_src_chan_gpio();
|
||||||
|
|
||||||
|
ana_cmpr_handle_t cmpr = NULL;
|
||||||
|
ana_cmpr_config_t config = {
|
||||||
|
.unit = ANA_CMPR_UNIT_0,
|
||||||
|
.clk_src = ANA_CMPR_CLK_SRC_DEFAULT,
|
||||||
|
.ref_src = ANA_CMPR_REF_SRC_INTERNAL,
|
||||||
|
.intr_type = ANA_CMPR_INTR_ANY_CROSS,
|
||||||
|
};
|
||||||
|
TEST_ESP_OK(ana_cmpr_new_unit(&config, &cmpr));
|
||||||
|
test_data.handle = cmpr;
|
||||||
|
ana_cmpr_intl_ref_config_t ref_cfg = {
|
||||||
|
.ref_volt = ANA_CMPR_REF_VOLT_50_PCT_VDD,
|
||||||
|
};
|
||||||
|
TEST_ESP_OK(ana_cmpr_set_intl_reference(cmpr, &ref_cfg));
|
||||||
|
TEST_ESP_OK(ana_cmpr_set_debounce(cmpr, &s_dbc_cfg));
|
||||||
|
ana_cmpr_event_callbacks_t cbs = {
|
||||||
|
.on_cross = test_ana_cmpr_on_cross_callback,
|
||||||
|
};
|
||||||
|
TEST_ESP_OK(ana_cmpr_register_event_callbacks(cmpr, &cbs, &test_data.count));
|
||||||
|
TEST_ESP_OK(ana_cmpr_enable(cmpr));
|
||||||
|
|
||||||
|
unity_utils_run_cache_disable_stub(test_ana_cmpr_iram_safety, &test_data);
|
||||||
|
TEST_ASSERT_EQUAL_INT(test_data.count, 10);
|
||||||
|
|
||||||
|
TEST_ESP_OK(ana_cmpr_disable(cmpr));
|
||||||
|
TEST_ESP_OK(ana_cmpr_del_unit(cmpr));
|
||||||
|
}
|
@@ -0,0 +1,54 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "unity.h"
|
||||||
|
#include "unity_test_runner.h"
|
||||||
|
#include "esp_heap_caps.h"
|
||||||
|
|
||||||
|
// Some resources are lazy allocated in analog comparator driver, the threshold is left for that case
|
||||||
|
#define TEST_MEMORY_LEAK_THRESHOLD (-300)
|
||||||
|
|
||||||
|
static size_t before_free_8bit;
|
||||||
|
static size_t before_free_32bit;
|
||||||
|
|
||||||
|
static void check_leak(size_t before_free, size_t after_free, const char *type)
|
||||||
|
{
|
||||||
|
ssize_t delta = after_free - before_free;
|
||||||
|
printf("MALLOC_CAP_%s: Before %u bytes free, After %u bytes free (delta %d)\\n", type, before_free, after_free, delta);
|
||||||
|
TEST_ASSERT_MESSAGE(delta >= TEST_MEMORY_LEAK_THRESHOLD, "memory leak");
|
||||||
|
}
|
||||||
|
|
||||||
|
void setUp(void)
|
||||||
|
{
|
||||||
|
before_free_8bit = heap_caps_get_free_size(MALLOC_CAP_8BIT);
|
||||||
|
before_free_32bit = heap_caps_get_free_size(MALLOC_CAP_32BIT);
|
||||||
|
}
|
||||||
|
|
||||||
|
void tearDown(void)
|
||||||
|
{
|
||||||
|
size_t after_free_8bit = heap_caps_get_free_size(MALLOC_CAP_8BIT);
|
||||||
|
size_t after_free_32bit = heap_caps_get_free_size(MALLOC_CAP_32BIT);
|
||||||
|
check_leak(before_free_8bit, after_free_8bit, "8BIT");
|
||||||
|
check_leak(before_free_32bit, after_free_32bit, "32BIT");
|
||||||
|
}
|
||||||
|
|
||||||
|
void app_main(void)
|
||||||
|
{
|
||||||
|
|
||||||
|
// _ _ _ _ ____ __ __ ____ ____ _____ _
|
||||||
|
// / \ | \ | | / \ / ___| \/ | _ \| _ \ |_ _|__ ___| |_
|
||||||
|
// / _ \ | \| | / _ \ | | | |\/| | |_) | |_) | | |/ _ \/ __| __|
|
||||||
|
// / ___ \| |\ |/ ___ \ | |___| | | | __/| _ < | | __/\__ \ |_
|
||||||
|
// /_/ \_\_| \_/_/ \_\ \____|_| |_|_| |_| \_\ |_|\___||___/\__|
|
||||||
|
|
||||||
|
printf(" _ _ _ _ ____ __ __ ____ ____ _____ _ \n");
|
||||||
|
printf(" / \\ | \\ | | / \\ / ___| \\/ | _ \\| _ \\ |_ _|__ ___| |_ \n");
|
||||||
|
printf(" / _ \\ | \\| | / _ \\ | | | |\\/| | |_) | |_) | | |/ _ \\/ __| __|\n");
|
||||||
|
printf(" / ___ \\| |\\ |/ ___ \\ | |___| | | | __/| _ < | | __/\\__ \\ |_ \n");
|
||||||
|
printf(" /_/ \\_\\_| \\_/_/ \\_\\ \\____|_| |_|_| |_| \\_\\ |_|\\___||___/\\__|\n");
|
||||||
|
printf("\n");
|
||||||
|
unity_run_menu();
|
||||||
|
}
|
@@ -0,0 +1,19 @@
|
|||||||
|
# SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||||
|
# SPDX-License-Identifier: CC0-1.0
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
from pytest_embedded import Dut
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.esp32h2
|
||||||
|
@pytest.mark.generic
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
'config',
|
||||||
|
[
|
||||||
|
'iram_safe',
|
||||||
|
'release',
|
||||||
|
],
|
||||||
|
indirect=True,
|
||||||
|
)
|
||||||
|
def test_ana_cmpr(dut: Dut) -> None:
|
||||||
|
dut.run_all_single_board_cases()
|
@@ -0,0 +1,7 @@
|
|||||||
|
CONFIG_COMPILER_DUMP_RTL_FILES=y
|
||||||
|
CONFIG_ANA_CMPR_ISR_IRAM_SAFE=y
|
||||||
|
CONFIG_ANA_CMPR_CTRL_FUNC_IN_IRAM=y
|
||||||
|
CONFIG_GPIO_CTRL_FUNC_IN_IRAM=y
|
||||||
|
CONFIG_COMPILER_OPTIMIZATION_NONE=y
|
||||||
|
# place non-ISR FreeRTOS functions in Flash
|
||||||
|
CONFIG_FREERTOS_PLACE_FUNCTIONS_INTO_FLASH=y
|
@@ -0,0 +1,5 @@
|
|||||||
|
CONFIG_PM_ENABLE=y
|
||||||
|
CONFIG_FREERTOS_USE_TICKLESS_IDLE=y
|
||||||
|
CONFIG_COMPILER_OPTIMIZATION_SIZE=y
|
||||||
|
CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_SIZE=y
|
||||||
|
CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_SILENT=y
|
@@ -0,0 +1,2 @@
|
|||||||
|
CONFIG_FREERTOS_HZ=1000
|
||||||
|
CONFIG_ESP_TASK_WDT=n
|
@@ -38,6 +38,7 @@ static inline void analog_cmpr_ll_enable(analog_cmpr_dev_t *hw, bool en)
|
|||||||
* @param hw Analog comparator register base address
|
* @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
|
* @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)
|
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;
|
||||||
@@ -90,6 +91,7 @@ static inline void analog_cmpr_ll_set_cross_intr_type(analog_cmpr_dev_t *hw, uin
|
|||||||
* @param hw Analog comparator register base address
|
* @param hw Analog comparator register base address
|
||||||
* @param cycle The debounce cycle
|
* @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)
|
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;
|
||||||
@@ -116,6 +118,7 @@ static inline void analog_cmpr_ll_enable_intr(analog_cmpr_dev_t *hw, uint32_t ma
|
|||||||
*
|
*
|
||||||
* @param hw Analog comparator register base address
|
* @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)
|
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;
|
||||||
@@ -127,6 +130,7 @@ static inline uint32_t analog_cmpr_ll_get_intr_status(analog_cmpr_dev_t *hw)
|
|||||||
* @param hw Analog comparator register base address
|
* @param hw Analog comparator register base address
|
||||||
* @param mask Interrupt status word
|
* @param mask Interrupt status word
|
||||||
*/
|
*/
|
||||||
|
__attribute__((always_inline))
|
||||||
static inline void analog_cmpr_ll_clear_intr(analog_cmpr_dev_t *hw, uint32_t mask)
|
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;
|
||||||
|
@@ -8,6 +8,10 @@ examples/peripherals/adc/oneshot_read:
|
|||||||
disable:
|
disable:
|
||||||
- if: SOC_ADC_SUPPORTED != 1
|
- if: SOC_ADC_SUPPORTED != 1
|
||||||
|
|
||||||
|
examples/peripherals/analog_comparator:
|
||||||
|
disable:
|
||||||
|
- if: SOC_ANA_CMPR_SUPPORTED != 1
|
||||||
|
|
||||||
examples/peripherals/dac:
|
examples/peripherals/dac:
|
||||||
disable:
|
disable:
|
||||||
- if: SOC_DAC_SUPPORTED != 1
|
- if: SOC_DAC_SUPPORTED != 1
|
||||||
|
8
examples/peripherals/analog_comparator/CMakeLists.txt
Normal file
8
examples/peripherals/analog_comparator/CMakeLists.txt
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
# For more information about build system see
|
||||||
|
# https://docs.espressif.com/projects/esp-idf/en/latest/api-guides/build-system.html
|
||||||
|
# The following five lines of boilerplate have to be in your project's
|
||||||
|
# CMakeLists in this exact order for cmake to work correctly
|
||||||
|
cmake_minimum_required(VERSION 3.16)
|
||||||
|
|
||||||
|
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||||
|
project(analog_comparator_example)
|
144
examples/peripherals/analog_comparator/README.md
Normal file
144
examples/peripherals/analog_comparator/README.md
Normal file
@@ -0,0 +1,144 @@
|
|||||||
|
| Supported Targets | ESP32-H2 |
|
||||||
|
| ----------------- | -------- |
|
||||||
|
|
||||||
|
# Analog Comparator Example
|
||||||
|
|
||||||
|
(See the README.md file in the upper level 'examples' directory for more information about examples.)
|
||||||
|
|
||||||
|
This example is going to show how to use the Analog Comparator.
|
||||||
|
|
||||||
|
## How to Use Example
|
||||||
|
|
||||||
|
### Hardware Requirement
|
||||||
|
|
||||||
|
* A development board with any supported Espressif SOC chip (see `Supported Targets` table above)
|
||||||
|
* A USB cable for power supply and programming
|
||||||
|
* A device to generate the source signal. For example, you can use a ESP SoC that support DAC peripheral (like ESP32 or ESP32S2) to generate source signal, or just use a signal generator.
|
||||||
|
|
||||||
|
#### Required Additionally for External Reference
|
||||||
|
|
||||||
|
* One more external signal for the reference. You can input a static voltage or a wave, for example, the static voltage can be gotten by the resistor divider, and the wave can be generated by either ESP SoC or a signal generator.
|
||||||
|
|
||||||
|
### Example Connection
|
||||||
|
|
||||||
|
Let's take ESP32-H2 and ESP32 for example, and we use the DAC on ESP32 as the signal generator (you can use a real signal generator instead if you have one).
|
||||||
|
|
||||||
|
#### Internal Reference
|
||||||
|
|
||||||
|
Download this example into ESP32-H2 and any DAC examples in `examples/peripherals/dac` directory into ESP32.
|
||||||
|
|
||||||
|
```
|
||||||
|
┌──────────────┐ ┌──────────────┐
|
||||||
|
│ ESP32-H2 │ │ ESP32 │
|
||||||
|
│ │ source signal │ │
|
||||||
|
┌────┤GPIO0 GPIO11│◄────┬──────────┤GPIO25 │
|
||||||
|
│ │ │ │ │ │
|
||||||
|
│ │ GND├─────┼────┬─────┤GND │
|
||||||
|
│ │ │ │ │ │ │
|
||||||
|
│ └──────────────┘ │ │ └──────────────┘
|
||||||
|
│ │ │
|
||||||
|
│ ┌──────────────┐ │ │
|
||||||
|
│ │ Oscilloscope │ │ │
|
||||||
|
│ │ │ │ │
|
||||||
|
└───►│Probe1 Probe2│◄────┘ │
|
||||||
|
│ │ │
|
||||||
|
│ GND├──────────┘
|
||||||
|
│ │
|
||||||
|
└──────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
#### External Reference
|
||||||
|
|
||||||
|
For the static external reference, we can use resistor divider to get the static voltage.
|
||||||
|
|
||||||
|
```
|
||||||
|
┌──────────────┐ ┌──────────────┐
|
||||||
|
│ ESP32-H2 │ │ ESP32 │
|
||||||
|
│ │ source signal │ │
|
||||||
|
┌────┤GPIO0 GPIO11│◄────┬──────────┤GPIO25 │
|
||||||
|
│ │ │ ref│signal │ │
|
||||||
|
│ │ GPIO10│◄────┼──────┐ ┌─┤GND │
|
||||||
|
│ │ │ │ │ │ │ │
|
||||||
|
│ │ GND├─────┼─┬────┼─┘ └──────────────┘
|
||||||
|
│ │ │ │ │ │ VDD
|
||||||
|
│ └──────────────┘ │ │ │ ─┬─
|
||||||
|
│ │ │ │ │
|
||||||
|
│ │ │ │ ├┐
|
||||||
|
│ ┌──────────────┐ │ │ │ ││R1
|
||||||
|
│ │ Oscilloscope │ │ │ │ ├┘
|
||||||
|
│ │ │ │ │ └──────────┤
|
||||||
|
└───►│Probe1 Probe2│◄────┘ │ │
|
||||||
|
│ │ │ ├┐
|
||||||
|
│ GND├───────┤ ││R2
|
||||||
|
│ │ │ ├┘
|
||||||
|
└──────────────┘ │ │
|
||||||
|
└───────────────┤
|
||||||
|
│
|
||||||
|
─┴─
|
||||||
|
GND
|
||||||
|
```
|
||||||
|
|
||||||
|
Also, we can generate a different signal on another DAC channel on ESP32, you can customize your DAC wave using `examples/peripherals/dac/dac_continuous/signal_generator` example.
|
||||||
|
|
||||||
|
```
|
||||||
|
┌──────────────┐ ┌──────────────┐
|
||||||
|
│ ESP32-H2 │ │ ESP32 │
|
||||||
|
│ │ source signal │ │
|
||||||
|
┌────┤GPIO0 GPIO11│◄────┬──────────┤GPIO25 │
|
||||||
|
│ │ │ ref│signal │ │
|
||||||
|
│ │ GPIO10│◄────┼──────────┤GPIO26 │
|
||||||
|
│ │ │ │ │ │
|
||||||
|
│ │ GND├─────┼───┬──────┤GND │
|
||||||
|
│ │ │ │ │ │ │
|
||||||
|
│ └──────────────┘ │ │ └──────────────┘
|
||||||
|
│ │ │
|
||||||
|
│ │ │
|
||||||
|
│ ┌──────────────┐ │ │
|
||||||
|
│ │ Oscilloscope │ │ │
|
||||||
|
│ │ │ │ │
|
||||||
|
└───►│Probe1 Probe2│◄────┘ │
|
||||||
|
│ │ │
|
||||||
|
│ GND├─────────┘
|
||||||
|
│ │
|
||||||
|
└──────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
### Configure the Project
|
||||||
|
|
||||||
|
You can decide to adopt internal reference or external reference in the example menu config, and you can also enable hysteresis comparator for the internal reference in the menu config.
|
||||||
|
|
||||||
|
### Build and Flash
|
||||||
|
|
||||||
|
Build the project and flash it to the board, then run monitor tool to view serial output:
|
||||||
|
|
||||||
|
```
|
||||||
|
idf.py -p PORT build flash monitor
|
||||||
|
```
|
||||||
|
|
||||||
|
(To exit the serial monitor, type ``Ctrl-]``.)
|
||||||
|
|
||||||
|
See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects.
|
||||||
|
|
||||||
|
## Example Output
|
||||||
|
|
||||||
|
### Static Internal Reference
|
||||||
|
|
||||||
|
The internal voltage is set to 50% VDD, and input a 50 Hz sine wave as source signal (blue line), the output GPIO toggles every time the sine wave crossing the reference voltage (yellow line)
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
### Hysteresis Internal Reference
|
||||||
|
|
||||||
|
The internal voltage is set to 30% VDD and 70% VDD alternately in this case, the source signal is a 100 Hz sine wave (blue line), the output GPIO toggles every time the source signal exceed 70% VDD and lower than 30% VDD.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
### External Reference
|
||||||
|
|
||||||
|
Here we input a 100 Hz sine wave (blue line) as the source signal and input a 1 KHz triangle wave as the reference signal, the output wave (yellow line) can be regarded as a SPWM (Sinusoidal PWM) wave.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
For any technical queries, please open an [issue](https://github.com/espressif/esp-idf/issues) on GitHub. We will get back to you soon.
|
BIN
examples/peripherals/analog_comparator/ext_ref.png
Normal file
BIN
examples/peripherals/analog_comparator/ext_ref.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 9.0 KiB |
BIN
examples/peripherals/analog_comparator/hysteresis_ref.png
Normal file
BIN
examples/peripherals/analog_comparator/hysteresis_ref.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 7.9 KiB |
@@ -0,0 +1,2 @@
|
|||||||
|
idf_component_register(SRCS "ana_cmpr_example_main.c"
|
||||||
|
INCLUDE_DIRS ".")
|
@@ -0,0 +1,28 @@
|
|||||||
|
menu "Analog Comparator Example Configuration"
|
||||||
|
|
||||||
|
choice EXAMPLE_REFERENCE_SOURCE
|
||||||
|
prompt "Analog Comparator reference source"
|
||||||
|
default EXAMPLE_INTERNAL_REF
|
||||||
|
help
|
||||||
|
Decide the reference signal comes from internal or external
|
||||||
|
|
||||||
|
config EXAMPLE_INTERNAL_REF
|
||||||
|
bool "Internal reference"
|
||||||
|
help
|
||||||
|
The source signal will refer to an internal voltage, which related to VDD.
|
||||||
|
|
||||||
|
config EXAMPLE_EXTERNAL_REF
|
||||||
|
bool "External reference"
|
||||||
|
help
|
||||||
|
The source signal will refer to the external signal on a specific GPIO.
|
||||||
|
endchoice
|
||||||
|
|
||||||
|
config EXAMPLE_HYSTERESIS_COMPARATOR
|
||||||
|
depends on EXAMPLE_INTERNAL_REF
|
||||||
|
bool "Enable hysteresis comparator"
|
||||||
|
default n
|
||||||
|
help
|
||||||
|
The internal reference voltage will be set to 30% VDD and 70% VDD alternately
|
||||||
|
every time the interrupt triggered.
|
||||||
|
|
||||||
|
endmenu
|
@@ -0,0 +1,140 @@
|
|||||||
|
/*
|
||||||
|
* 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"
|
||||||
|
|
||||||
|
#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) (1000000.0 * 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 IRAM_ATTR 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_intl_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 a 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_intl_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,
|
||||||
|
.intr_type = ANA_CMPR_INTR_ANY_CROSS,
|
||||||
|
};
|
||||||
|
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_intl_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_intl_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,
|
||||||
|
.intr_type = ANA_CMPR_INTR_ANY_CROSS,
|
||||||
|
};
|
||||||
|
ESP_ERROR_CHECK(ana_cmpr_new_unit(&config, &cmpr));
|
||||||
|
ESP_LOGI(TAG, "Allocate Analog Comparator with internal 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 how fast the source signal or reference signal changes
|
||||||
|
* comparing to another one. This example adopts an approximate frequency as the relative signal
|
||||||
|
* frequency, and set the default wait time to EXAMPLE_WAIT_TIME_PROP of the relative signal period.
|
||||||
|
* We need to estimate an appropriate `freq_approx` and EXAMPLE_WAIT_TIME_PROP
|
||||||
|
* to make sure the interrupt neither triggers duplicate interrupts, nor misses the next crossing interrupt.
|
||||||
|
* Here we take 1 KHz for example */
|
||||||
|
.wait_us = EXAMPLE_WAITE_TIME_US(1000),
|
||||||
|
};
|
||||||
|
ESP_ERROR_CHECK(ana_cmpr_set_debounce(cmpr, &dbc_cfg));
|
||||||
|
|
||||||
|
/* Step 3: Register the event callbacks */
|
||||||
|
ana_cmpr_event_callbacks_t cbs = {
|
||||||
|
.on_cross = example_ana_cmpr_on_cross_callback,
|
||||||
|
};
|
||||||
|
ESP_ERROR_CHECK(ana_cmpr_register_event_callbacks(cmpr, &cbs, NULL));
|
||||||
|
|
||||||
|
/* Step 4: Enable the analog comparator unit */
|
||||||
|
ESP_ERROR_CHECK(ana_cmpr_enable(cmpr));
|
||||||
|
|
||||||
|
#if CONFIG_EXAMPLE_INTERNAL_REF
|
||||||
|
ESP_LOGI(TAG, "Analog comparator enabled, reference voltage: %d%% * VDD", (int)ref_cfg.ref_volt * 10);
|
||||||
|
#else
|
||||||
|
ESP_LOGI(TAG, "Analog comparator enabled, external reference selected");
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void example_init_monitor_gpio(void)
|
||||||
|
{
|
||||||
|
gpio_config_t io_conf = {
|
||||||
|
.intr_type = GPIO_INTR_DISABLE,
|
||||||
|
.mode = GPIO_MODE_OUTPUT,
|
||||||
|
.pin_bit_mask = (1ULL << EXAMPLE_MONITOR_GPIO_NUM),
|
||||||
|
.pull_down_en = false,
|
||||||
|
.pull_up_en = false,
|
||||||
|
};
|
||||||
|
gpio_config(&io_conf);
|
||||||
|
gpio_set_level(EXAMPLE_MONITOR_GPIO_NUM, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void app_main(void)
|
||||||
|
{
|
||||||
|
/* Initialize GPIO to monitor the comparator interrupt */
|
||||||
|
example_init_monitor_gpio();
|
||||||
|
/* Initialize Analog Comparator */
|
||||||
|
example_init_analog_comparator();
|
||||||
|
}
|
@@ -0,0 +1,26 @@
|
|||||||
|
# SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||||
|
# SPDX-License-Identifier: CC0-1.0
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
from pytest_embedded import Dut
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.esp32h2
|
||||||
|
@pytest.mark.generic
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
'config',
|
||||||
|
[
|
||||||
|
'intl',
|
||||||
|
'ext',
|
||||||
|
],
|
||||||
|
indirect=True,
|
||||||
|
)
|
||||||
|
def test_ana_cmpr_example(dut: Dut) -> None:
|
||||||
|
sdkconfig = dut.app.sdkconfig
|
||||||
|
dut.expect('ana_cmpr_example: Analog Comparator source gpio 11, external reference gpio 10', timeout=10)
|
||||||
|
if sdkconfig['EXAMPLE_INTERNAL_REF']:
|
||||||
|
dut.expect('ana_cmpr_example: Allocate Analog Comparator with internal reference', timeout=10)
|
||||||
|
dut.expect(r'ana_cmpr_example: Analog comparator enabled, reference voltage: [0-9]+% \* VDD', timeout=10)
|
||||||
|
elif sdkconfig['EXAMPLE_EXTERNAL_REF']:
|
||||||
|
dut.expect('ana_cmpr_example: Allocate Analog Comparator with internal reference', timeout=10)
|
||||||
|
dut.expect('ana_cmpr_example: Analog comparator enabled, external reference selected', timeout=10)
|
2
examples/peripherals/analog_comparator/sdkconfig.ci.ext
Normal file
2
examples/peripherals/analog_comparator/sdkconfig.ci.ext
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
CONFIG_EXAMPLE_INTERNAL_REF=n
|
||||||
|
CONFIG_EXAMPLE_EXTERNAL_REF=y
|
2
examples/peripherals/analog_comparator/sdkconfig.ci.intl
Normal file
2
examples/peripherals/analog_comparator/sdkconfig.ci.intl
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
CONFIG_EXAMPLE_INTERNAL_REF=y
|
||||||
|
CONFIG_EXAMPLE_EXTERNAL_REF=n
|
BIN
examples/peripherals/analog_comparator/static_50p_ref.png
Normal file
BIN
examples/peripherals/analog_comparator/static_50p_ref.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 9.4 KiB |
Reference in New Issue
Block a user