From 65f4707d2bda376da7a8988889d99adccb3df496 Mon Sep 17 00:00:00 2001 From: morris Date: Sat, 25 Apr 2026 11:57:43 +0800 Subject: [PATCH] feat(ana_cmpr): add ETM periodic scan example --- .../en/api-reference/peripherals/ana_cmpr.rst | 5 +- .../api-reference/peripherals/ana_cmpr.rst | 5 +- examples/peripherals/.build-test-rules.yml | 10 + .../etm_periodic_scan/CMakeLists.txt | 11 + .../etm_periodic_scan/README.md | 114 ++++++++++ .../etm_periodic_scan/main/CMakeLists.txt | 3 + .../etm_periodic_scan/main/Kconfig.projbuild | 23 ++ .../main/ana_cmpr_etm_periodic_scan_main.c | 200 ++++++++++++++++++ .../pytest_ana_cmpr_etm_periodic_scan.py | 20 ++ .../etm_periodic_scan/sdkconfig.defaults | 1 + .../sdkconfig.defaults.esp32s31 | 1 + .../etm_periodic_scan/wave.png | Bin 0 -> 18049 bytes 12 files changed, 391 insertions(+), 2 deletions(-) create mode 100644 examples/peripherals/analog_comparator/etm_periodic_scan/CMakeLists.txt create mode 100644 examples/peripherals/analog_comparator/etm_periodic_scan/README.md create mode 100644 examples/peripherals/analog_comparator/etm_periodic_scan/main/CMakeLists.txt create mode 100644 examples/peripherals/analog_comparator/etm_periodic_scan/main/Kconfig.projbuild create mode 100644 examples/peripherals/analog_comparator/etm_periodic_scan/main/ana_cmpr_etm_periodic_scan_main.c create mode 100644 examples/peripherals/analog_comparator/etm_periodic_scan/pytest_ana_cmpr_etm_periodic_scan.py create mode 100644 examples/peripherals/analog_comparator/etm_periodic_scan/sdkconfig.defaults create mode 100644 examples/peripherals/analog_comparator/etm_periodic_scan/sdkconfig.defaults.esp32s31 create mode 100644 examples/peripherals/analog_comparator/etm_periodic_scan/wave.png diff --git a/docs/en/api-reference/peripherals/ana_cmpr.rst b/docs/en/api-reference/peripherals/ana_cmpr.rst index 13a1db9c35d..382ae6a529d 100644 --- a/docs/en/api-reference/peripherals/ana_cmpr.rst +++ b/docs/en/api-reference/peripherals/ana_cmpr.rst @@ -222,7 +222,10 @@ Kconfig Options Application Example ------------------- -* :example:`peripherals/analog_comparator` shows the basic usage of the analog comparator, and other potential usages like hysteresis comparator and SPWM generator. +.. list:: + + :SOC_ANA_CMPR_SUPPORT_AUTO_SCAN: - :example:`peripherals/analog_comparator/auto_scan` shows auto scan based threshold detection with internal or external reference. After enabling the comparator, hardware scans continuously and updates output in real time, while the example demonstrates interrupt-based or ETM-based monitor GPIO control depending on target capabilities. + :SOC_ANA_CMPR_SUPPORT_ETM_SCAN: - :example:`peripherals/analog_comparator/etm_periodic_scan` shows how to use GPTimer and ETM to trigger periodic comparator scans and drive a monitor GPIO from comparator crossing events. API Reference ------------- diff --git a/docs/zh_CN/api-reference/peripherals/ana_cmpr.rst b/docs/zh_CN/api-reference/peripherals/ana_cmpr.rst index 8378e403d67..27d5be86df7 100644 --- a/docs/zh_CN/api-reference/peripherals/ana_cmpr.rst +++ b/docs/zh_CN/api-reference/peripherals/ana_cmpr.rst @@ -222,7 +222,10 @@ Kconfig 选项 应用示例 -------- -* :example:`peripherals/analog_comparator` 展示了模拟比较器的基本用法以及其他用途(如迟滞比较器和 SPWM 发生器)。 +.. list:: + + :SOC_ANA_CMPR_SUPPORT_AUTO_SCAN: - :example:`peripherals/analog_comparator/auto_scan` 展示了基于自动扫描功能的阈值检测(支持内部参考或外部参考)。比较器在使能后会持续扫描并实时更新输出,示例根据目标能力演示了基于中断或 ETM 的监控 GPIO 控制。 + :SOC_ANA_CMPR_SUPPORT_ETM_SCAN: - :example:`peripherals/analog_comparator/etm_periodic_scan` 展示了如何使用 GPTimer 和 ETM 周期性触发比较器扫描,并通过比较器跨越事件驱动监控 GPIO。 API 参考 -------- diff --git a/examples/peripherals/.build-test-rules.yml b/examples/peripherals/.build-test-rules.yml index 4a1dcc7d990..e87a959b546 100644 --- a/examples/peripherals/.build-test-rules.yml +++ b/examples/peripherals/.build-test-rules.yml @@ -37,6 +37,16 @@ examples/peripherals/analog_comparator/auto_scan: - esp_hal_ana_cmpr - soc +examples/peripherals/analog_comparator/etm_periodic_scan: + disable: + - if: SOC_ANA_CMPR_SUPPORT_ETM_SCAN != 1 + depends_components: + - esp_driver_gpio + - esp_driver_ana_cmpr + - esp_driver_gptimer + - esp_hal_ana_cmpr + - soc + examples/peripherals/bitscrambler: disable: - if: SOC_BITSCRAMBLER_SUPPORTED != 1 diff --git a/examples/peripherals/analog_comparator/etm_periodic_scan/CMakeLists.txt b/examples/peripherals/analog_comparator/etm_periodic_scan/CMakeLists.txt new file mode 100644 index 00000000000..5127a906e71 --- /dev/null +++ b/examples/peripherals/analog_comparator/etm_periodic_scan/CMakeLists.txt @@ -0,0 +1,11 @@ +# 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.22) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +# "Trim" the build. Include the minimal set of components, main, and anything it depends on. +idf_build_set_property(MINIMAL_BUILD ON) + +project(ana_cmpr_etm_periodic_scan) diff --git a/examples/peripherals/analog_comparator/etm_periodic_scan/README.md b/examples/peripherals/analog_comparator/etm_periodic_scan/README.md new file mode 100644 index 00000000000..df381801710 --- /dev/null +++ b/examples/peripherals/analog_comparator/etm_periodic_scan/README.md @@ -0,0 +1,114 @@ +| Supported Targets | ESP32-S31 | +| ----------------- | --------- | + +# Analog Comparator ETM Periodic Scan Example + +(See the README.md file in the upper level `examples` directory for more information about examples.) + +This example shows how to use a GPTimer periodic ETM event to trigger the analog comparator scan task. The analog comparator uses the internal 50% VDD reference, and the comparator positive and negative crossing events set or clear a monitor GPIO through ETM. With an external sine wave connected to the source channel, the monitor GPIO becomes a square wave representation of the sampled input. + +## Realization + +This example builds the following ETM chain: + +- GPTimer alarm event -> GPTimer enable-alarm task +- GPTimer alarm event -> Analog comparator start task +- Analog comparator positive cross event -> GPIO set task +- Analog comparator negative cross event -> GPIO clear task + +The steady-state signal path runs without CPU intervention. The CPU is only used during one-time initialization. + +## How to Use Example + +### Hardware Requirement + +* A development board with a supported Espressif SOC chip (see `Supported Targets` table above) +* A USB cable for power supply and programming +* A signal generator for generating the source sine wave +* An oscilloscope or logic analyzer to observe the source input and monitor GPIO + +### Example Connection + +The example uses a configurable comparator source input GPIO. The shipped default value matches the comparator pad0 GPIO for each supported target, and the example logs the actual source GPIO number at startup. + +``` + +--------------+ +--------------+ + | ESP Board | | Signal Gen | + | | source signal | | ++----+GPIO Src In|<----+----------+OUT | +| | | | | | +| | GND+-----+----+-----+GND | +| | | | | | | +| +--------------+ | | +--------------+ +| | | +| +--------------+ | | +| | Oscilloscope | | | +| | | | | ++--->|Probe1 Probe2|<----+ | + | | | + | GND+----------+ + | | + +--------------+ +``` + +Probe the source sine wave on the comparator source GPIO and probe the monitor GPIO at the same time. + +### Configure the Project + +Open the project configuration menu: + +```bash +idf.py menuconfig +``` + +Under `Example Configuration`, you can configure: + +- `Source GPIO number` +- `Monitor GPIO number` +- `Comparator scan period (us)` + +The comparator reference voltage is fixed to the internal 50% VDD reference in this example. +The shipped default source GPIO value matches comparator pad0 on each supported target. + +### Build and Flash + +Build the project and flash it to the board, then run the monitor tool to view serial output: + +```bash +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 + +```text +I (252) main_task: Started on CPU0 +I (262) main_task: Calling app_main() +I (262) example: Monitor GPIO 4 +I (262) example: Analog comparator source GPIO 37 +I (262) example: Analog comparator internal reference 50% VDD +I (272) example: GPTimer scan period 50 us +I (282) example: Periodic ETM-driven comparator scan started +I (282) main_task: Returned from app_main() +``` + +The exact source GPIO number depends on the target and package. + +## Expected Result On Hardware + +Feed a sine wave into the comparator source channel. Because the comparator reference is fixed at 50% VDD, the monitor GPIO stays high while the sampled source voltage is above the threshold and low while it is below the threshold. + +On an oscilloscope, the monitor GPIO appears as a square wave derived from the sampled sine wave. + +![example_waveform](wave.png) + +## Troubleshooting + +- This example only works on targets that support analog comparator channel scan and the ETM scan-task path. +- If the monitor GPIO does not change, reduce the input frequency or shorten the scan period. +- If the square wave looks unstable, confirm the input sine wave amplitude crosses the 50% VDD threshold. + +For any technical queries, please open an [issue](https://github.com/espressif/esp-idf/issues) on GitHub. We will get back to you soon. diff --git a/examples/peripherals/analog_comparator/etm_periodic_scan/main/CMakeLists.txt b/examples/peripherals/analog_comparator/etm_periodic_scan/main/CMakeLists.txt new file mode 100644 index 00000000000..61c4ab34cad --- /dev/null +++ b/examples/peripherals/analog_comparator/etm_periodic_scan/main/CMakeLists.txt @@ -0,0 +1,3 @@ +idf_component_register(SRCS "ana_cmpr_etm_periodic_scan_main.c" + PRIV_REQUIRES esp_driver_ana_cmpr esp_driver_gptimer esp_driver_gpio + INCLUDE_DIRS ".") diff --git a/examples/peripherals/analog_comparator/etm_periodic_scan/main/Kconfig.projbuild b/examples/peripherals/analog_comparator/etm_periodic_scan/main/Kconfig.projbuild new file mode 100644 index 00000000000..4b1b3eed8e5 --- /dev/null +++ b/examples/peripherals/analog_comparator/etm_periodic_scan/main/Kconfig.projbuild @@ -0,0 +1,23 @@ +menu "Example Configuration" + + config EXAMPLE_SRC_GPIO_NUM + int "Source GPIO number" + default 0 + help + GPIO connected to analog comparator source channel 0. + + config EXAMPLE_MONITOR_GPIO_NUM + int "Monitor GPIO number" + default 4 + range 0 SOC_GPIO_OUT_RANGE_MAX + help + GPIO that outputs the square wave converted from the analog comparator result. + + config EXAMPLE_SCAN_PERIOD_US + int "Comparator scan period (us)" + default 50 + range 10 1000000 + help + Period of the GPTimer ETM event that triggers the analog comparator scan task. + +endmenu diff --git a/examples/peripherals/analog_comparator/etm_periodic_scan/main/ana_cmpr_etm_periodic_scan_main.c b/examples/peripherals/analog_comparator/etm_periodic_scan/main/ana_cmpr_etm_periodic_scan_main.c new file mode 100644 index 00000000000..cfa797b2bdf --- /dev/null +++ b/examples/peripherals/analog_comparator/etm_periodic_scan/main/ana_cmpr_etm_periodic_scan_main.c @@ -0,0 +1,200 @@ +/* + * SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Unlicense OR CC0-1.0 + */ + +#include +#include "sdkconfig.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "esp_log.h" +#include "esp_etm.h" +#include "driver/gpio.h" +#include "driver/gptimer.h" +#include "driver/ana_cmpr.h" +#include "driver/gpio_etm.h" +#include "driver/gptimer_etm.h" +#include "driver/ana_cmpr_etm.h" + +#define EXAMPLE_ANA_CMPR_UNIT (0) +#define EXAMPLE_MONITOR_GPIO_NUM CONFIG_EXAMPLE_MONITOR_GPIO_NUM +#define EXAMPLE_SCAN_PERIOD_US CONFIG_EXAMPLE_SCAN_PERIOD_US +#define EXAMPLE_ANA_CMPR_SRC_GPIO_NUM CONFIG_EXAMPLE_SRC_GPIO_NUM + +#define TAG "example" + +typedef struct { + /* ETM events are produced by peripherals and can trigger ETM tasks without CPU intervention. */ + esp_etm_event_handle_t gptimer_alarm_evt; + esp_etm_event_handle_t cmpr_pos_evt; + esp_etm_event_handle_t cmpr_neg_evt; + /* ETM tasks are the hardware actions we want to execute when an event happens. */ + esp_etm_task_handle_t gptimer_en_alarm_task; + esp_etm_task_handle_t cmpr_start_task; + esp_etm_task_handle_t gpio_set_task; + esp_etm_task_handle_t gpio_clr_task; + /* One ETM channel connects exactly one event source to one task target. */ + esp_etm_channel_handle_t etm_realarm_handle; + esp_etm_channel_handle_t etm_scan_handle; + esp_etm_channel_handle_t etm_pos_handle; + esp_etm_channel_handle_t etm_neg_handle; +} example_etm_handles_t; + +static void example_init_monitor_gpio(void) +{ + gpio_config_t io_conf = { + .intr_type = GPIO_INTR_DISABLE, + .mode = GPIO_MODE_OUTPUT, + .pull_down_en = GPIO_PULLDOWN_DISABLE, + .pull_up_en = GPIO_PULLUP_DISABLE, + .pin_bit_mask = 1ULL << EXAMPLE_MONITOR_GPIO_NUM, + }; + ESP_ERROR_CHECK(gpio_config(&io_conf)); + ESP_ERROR_CHECK(gpio_set_level(EXAMPLE_MONITOR_GPIO_NUM, 0)); + ESP_LOGI(TAG, "Monitor GPIO %d", EXAMPLE_MONITOR_GPIO_NUM); +} + +static ana_cmpr_handle_t example_init_ana_cmpr(void) +{ + gpio_num_t src_gpio = -1; + ana_cmpr_handle_t cmpr = NULL; + 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, + .src_chan0_gpio = EXAMPLE_ANA_CMPR_SRC_GPIO_NUM, + /* Require several consistent samples before the scan result is updated. + * This makes the output more stable when the input is noisy. */ + .resample_limit = 3, + }; + ESP_ERROR_CHECK(ana_cmpr_new_unit(&config, &cmpr)); + ESP_ERROR_CHECK(ana_cmpr_get_channel_gpio(cmpr, ANA_CMPR_SOURCE_CHAN, 0, &src_gpio)); + + ana_cmpr_internal_ref_config_t ref_cfg = { + .ref_volt = ANA_CMPR_REF_VOLT_50_PCT_VDD, + .ref_hys_level = ANA_CMPR_REF_HYS_LEVEL0, + }; + /* Compare the input signal against an internal reference set to 50% of VDD. */ + ESP_ERROR_CHECK(ana_cmpr_set_internal_reference(cmpr, &ref_cfg)); + + ana_cmpr_scan_config_t scan_cfg = { + .scan_mode = ANA_CMPR_SCAN_MODE_FULL, + /* Stay on each source channel for a short time before moving to the next one. */ + .poll_period_us = 2, + }; + /* Scan parameters control how the hardware walks through the enabled source channels. */ + ESP_ERROR_CHECK(ana_cmpr_set_scan_config(cmpr, &scan_cfg)); + + ESP_LOGI(TAG, "Analog comparator source GPIO %d", src_gpio); + ESP_LOGI(TAG, "Analog comparator internal reference 50%% VDD"); + + return cmpr; +} + +static gptimer_handle_t example_init_gptimer(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, // 1 MHz, which means the timer count value will increase by 1 every microsecond + }; + ESP_ERROR_CHECK(gptimer_new_timer(&timer_config, &gptimer)); + ESP_ERROR_CHECK(gptimer_set_raw_count(gptimer, 0)); + ESP_ERROR_CHECK(gptimer_enable(gptimer)); + + gptimer_alarm_config_t alarm_config = { + .reload_count = 0, + /* The alarm period is the high-level scan period of this example. + * Every alarm will trigger one comparator scan through ETM. */ + .alarm_count = EXAMPLE_SCAN_PERIOD_US, + .flags.auto_reload_on_alarm = true, + }; + ESP_ERROR_CHECK(gptimer_set_alarm_action(gptimer, &alarm_config)); + ESP_LOGI(TAG, "GPTimer scan period %d us", EXAMPLE_SCAN_PERIOD_US); + + return gptimer; +} + +static void example_init_etm(ana_cmpr_handle_t cmpr, gptimer_handle_t gptimer) +{ + example_etm_handles_t handles = {}; + + /* Step 1: create the ETM event generated when the timer alarm fires. */ + gptimer_etm_event_config_t gptimer_evt_cfg = { + .event_type = GPTIMER_ETM_EVENT_ALARM_MATCH, + }; + ESP_ERROR_CHECK(gptimer_new_etm_event(gptimer, &gptimer_evt_cfg, &handles.gptimer_alarm_evt)); + + /* Step 2: create the timer task that rearms the alarm after each trigger. */ + gptimer_etm_task_config_t gptimer_task_cfg = { + .task_type = GPTIMER_ETM_TASK_EN_ALARM, + }; + ESP_ERROR_CHECK(gptimer_new_etm_task(gptimer, &gptimer_task_cfg, &handles.gptimer_en_alarm_task)); + + /* Step 3: create the comparator task that starts one scan sequence. */ + ana_cmpr_etm_task_config_t cmpr_task_cfg = { + .task_type = ANA_CMPR_TASK_START, + }; + ESP_ERROR_CHECK(ana_cmpr_new_etm_task(cmpr, &cmpr_task_cfg, &handles.cmpr_start_task)); + + /* Step 4: create comparator events for positive and negative threshold crossings. */ + ana_cmpr_etm_event_config_t cmpr_evt_cfg = { + .event_type = ANA_CMPR_EVENT_POS_CROSS, + }; + ESP_ERROR_CHECK(ana_cmpr_new_etm_event(cmpr, &cmpr_evt_cfg, &handles.cmpr_pos_evt)); + cmpr_evt_cfg.event_type = ANA_CMPR_EVENT_NEG_CROSS; + ESP_ERROR_CHECK(ana_cmpr_new_etm_event(cmpr, &cmpr_evt_cfg, &handles.cmpr_neg_evt)); + + /* Step 5: create GPIO ETM tasks so the comparator result is visible on a normal output pin. + * Positive crossing drives the monitor GPIO high, negative crossing drives it low. */ + gpio_etm_task_config_t gpio_task_cfg = {}; + gpio_task_cfg.actions[0] = GPIO_ETM_TASK_ACTION_SET; + gpio_task_cfg.actions[1] = GPIO_ETM_TASK_ACTION_CLR; + ESP_ERROR_CHECK(gpio_new_etm_task(&gpio_task_cfg, &handles.gpio_set_task, &handles.gpio_clr_task)); + ESP_ERROR_CHECK(gpio_etm_task_add_gpio(handles.gpio_set_task, EXAMPLE_MONITOR_GPIO_NUM)); + ESP_ERROR_CHECK(gpio_etm_task_add_gpio(handles.gpio_clr_task, EXAMPLE_MONITOR_GPIO_NUM)); + + /* Step 6: allocate ETM channels. Each channel is an event-to-task connection. */ + esp_etm_channel_config_t etm_cfg = {}; + ESP_ERROR_CHECK(esp_etm_new_channel(&etm_cfg, &handles.etm_realarm_handle)); + ESP_ERROR_CHECK(esp_etm_new_channel(&etm_cfg, &handles.etm_scan_handle)); + ESP_ERROR_CHECK(esp_etm_new_channel(&etm_cfg, &handles.etm_pos_handle)); + ESP_ERROR_CHECK(esp_etm_new_channel(&etm_cfg, &handles.etm_neg_handle)); + + /* Step 7: wire the ETM graph: + * - timer alarm event -> timer rearm task + * - timer alarm event -> comparator start task + * - comparator positive event -> GPIO set task + * - comparator negative event -> GPIO clear task */ + ESP_ERROR_CHECK(esp_etm_channel_connect(handles.etm_realarm_handle, handles.gptimer_alarm_evt, handles.gptimer_en_alarm_task)); + ESP_ERROR_CHECK(esp_etm_channel_connect(handles.etm_scan_handle, handles.gptimer_alarm_evt, handles.cmpr_start_task)); + ESP_ERROR_CHECK(esp_etm_channel_connect(handles.etm_pos_handle, handles.cmpr_pos_evt, handles.gpio_set_task)); + ESP_ERROR_CHECK(esp_etm_channel_connect(handles.etm_neg_handle, handles.cmpr_neg_evt, handles.gpio_clr_task)); + + /* Step 8: enable the channels so the hardware pipeline becomes active. */ + ESP_ERROR_CHECK(esp_etm_channel_enable(handles.etm_realarm_handle)); + ESP_ERROR_CHECK(esp_etm_channel_enable(handles.etm_scan_handle)); + ESP_ERROR_CHECK(esp_etm_channel_enable(handles.etm_pos_handle)); + ESP_ERROR_CHECK(esp_etm_channel_enable(handles.etm_neg_handle)); +} + +void app_main(void) +{ + example_init_monitor_gpio(); + ana_cmpr_handle_t cmpr = example_init_ana_cmpr(); + gptimer_handle_t gptimer = example_init_gptimer(); + example_init_etm(cmpr, gptimer); + + ESP_ERROR_CHECK(ana_cmpr_enable(cmpr)); + /* Run one software-triggered scan before the periodic timer starts. + * This gives the comparator an initial result immediately, instead of waiting for the first timer alarm. */ + ESP_ERROR_CHECK(ana_cmpr_trigger_scan(cmpr)); + vTaskDelay(pdMS_TO_TICKS(10)); + + /* After the timer starts, future scans are launched by ETM rather than by CPU code. */ + ESP_ERROR_CHECK(gptimer_start(gptimer)); + ESP_LOGI(TAG, "Periodic ETM-driven comparator scan started"); +} diff --git a/examples/peripherals/analog_comparator/etm_periodic_scan/pytest_ana_cmpr_etm_periodic_scan.py b/examples/peripherals/analog_comparator/etm_periodic_scan/pytest_ana_cmpr_etm_periodic_scan.py new file mode 100644 index 00000000000..217ec6973ac --- /dev/null +++ b/examples/peripherals/analog_comparator/etm_periodic_scan/pytest_ana_cmpr_etm_periodic_scan.py @@ -0,0 +1,20 @@ +# SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD +# SPDX-License-Identifier: CC0-1.0 +import pytest +from pytest_embedded import Dut +from pytest_embedded_idf.utils import idf_parametrize +from pytest_embedded_idf.utils import soc_filtered_targets + + +@pytest.mark.generic +@idf_parametrize( + 'target', + soc_filtered_targets('SOC_ANA_CMPR_SUPPORT_ETM_SCAN == 1'), + indirect=['target'], +) +def test_ana_cmpr_etm_periodic_scan(dut: Dut) -> None: + dut.expect(r'Monitor GPIO \d+') + dut.expect(r'Analog comparator source GPIO \d+') + dut.expect_exact('Analog comparator internal reference 50% VDD') + dut.expect(r'GPTimer scan period \d+ us') + dut.expect_exact('Periodic ETM-driven comparator scan started') diff --git a/examples/peripherals/analog_comparator/etm_periodic_scan/sdkconfig.defaults b/examples/peripherals/analog_comparator/etm_periodic_scan/sdkconfig.defaults new file mode 100644 index 00000000000..a73029ee703 --- /dev/null +++ b/examples/peripherals/analog_comparator/etm_periodic_scan/sdkconfig.defaults @@ -0,0 +1 @@ +CONFIG_EXAMPLE_SRC_GPIO_NUM=0 diff --git a/examples/peripherals/analog_comparator/etm_periodic_scan/sdkconfig.defaults.esp32s31 b/examples/peripherals/analog_comparator/etm_periodic_scan/sdkconfig.defaults.esp32s31 new file mode 100644 index 00000000000..e47a6705285 --- /dev/null +++ b/examples/peripherals/analog_comparator/etm_periodic_scan/sdkconfig.defaults.esp32s31 @@ -0,0 +1 @@ +CONFIG_EXAMPLE_SRC_GPIO_NUM=37 diff --git a/examples/peripherals/analog_comparator/etm_periodic_scan/wave.png b/examples/peripherals/analog_comparator/etm_periodic_scan/wave.png new file mode 100644 index 0000000000000000000000000000000000000000..00749cf61270f17c55330405a5943887386ee02d GIT binary patch literal 18049 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 literal 0 HcmV?d00001