test(ana_cmpr): Add edge-specific interrupt direction test case

Add a Unity test case that arms only ANA_CMPR_CROSS_POS (resp. only
ANA_CMPR_CROSS_NEG) on a unit and asserts that a real transition of
the matching direction fires the callback exactly once, while a
transition of the opposite (never-armed) direction does not fire at
all.

This closes a gap in the existing test_apps: none of the current
cases isolate cross direction, so a swapped POS/NEG interrupt mask in
the LL layer (fixed in the previous commit) previously went
undetected.

On the scan-based comparator IP (ESP32-H4/S31), a crossing is only
sampled/latched when a scan is explicitly triggered, so the new test
case also triggers a scan after each level change on that IP, plus
one extra priming scan right after enabling the unit so the internal
compare state starts in sync with the already-set initial GPIO level.

Signed-off-by: Tiago Medicci <tiago.medicci@espressif.com>
This commit is contained in:
Tiago Medicci
2026-07-15 14:05:15 -03:00
committed by morris
parent 5cc17ee75a
commit e9e6a3dcf0
3 changed files with 115 additions and 1 deletions

View File

@@ -86,3 +86,86 @@ TEST_CASE("ana_cmpr internal reference", "[ana_cmpr]")
TEST_ESP_OK(ana_cmpr_disable(cmpr));
TEST_ESP_OK(ana_cmpr_del_unit(cmpr));
}
TEST_CASE("ana_cmpr edge-specific interrupt reports correct cross direction", "[ana_cmpr]")
{
#if !SOC_ANA_CMPR_CAN_DISTINGUISH_EDGE
TEST_IGNORE_MESSAGE("target cannot distinguish cross direction");
#else
test_ana_cmpr_edge_cnt_t cnt = {};
ana_cmpr_event_callbacks_t cbs = {
.on_cross = test_ana_cmpr_edge_cnt_callback,
};
ana_cmpr_internal_ref_config_t ref_cfg = {};
ref_cfg.ref_volt = ANA_CMPR_REF_VOLT_50_PCT_VDD;
ana_cmpr_debounce_config_t dbc_cfg = {
.wait_us = 10,
};
/* Arm only the rising (POS) interrupt: a real rising transition must be
* reported as POS, and a following falling transition must not fire at
* all, since NEG was never armed. */
{
ana_cmpr_handle_t cmpr = NULL;
ana_cmpr_config_t config = {};
config.unit = 0;
config.clk_src = ANA_CMPR_CLK_SRC_DEFAULT;
config.ref_src = ANA_CMPR_REF_SRC_INTERNAL;
config.cross_type = ANA_CMPR_CROSS_POS;
TEST_ESP_OK(ana_cmpr_new_unit(&config, &cmpr));
int src_chan_io = test_init_src_chan_gpio();
TEST_ESP_OK(ana_cmpr_set_internal_reference(cmpr, &ref_cfg));
TEST_ESP_OK(ana_cmpr_set_debounce(cmpr, &dbc_cfg));
TEST_ESP_OK(ana_cmpr_register_event_callbacks(cmpr, &cbs, &cnt));
TEST_ESP_OK(ana_cmpr_enable(cmpr));
esp_rom_delay_us(1000); // allow the comparator analog block to settle after power-up
gpio_set_level(src_chan_io, 1); // rising: armed as POS, must fire as POS
esp_rom_delay_us(1000);
TEST_ASSERT_EQUAL_UINT32(1, cnt.pos_cnt);
TEST_ASSERT_EQUAL_UINT32(0, cnt.neg_cnt);
gpio_set_level(src_chan_io, 0); // falling: NEG never armed, must not fire
esp_rom_delay_us(1000);
TEST_ASSERT_EQUAL_UINT32(1, cnt.pos_cnt);
TEST_ASSERT_EQUAL_UINT32(0, cnt.neg_cnt);
TEST_ESP_OK(ana_cmpr_disable(cmpr));
TEST_ESP_OK(ana_cmpr_del_unit(cmpr));
}
/* Same as above, mirrored: arm only the falling (NEG) interrupt. */
{
cnt.pos_cnt = 0;
cnt.neg_cnt = 0;
ana_cmpr_handle_t cmpr = NULL;
ana_cmpr_config_t config = {};
config.unit = 0;
config.clk_src = ANA_CMPR_CLK_SRC_DEFAULT;
config.ref_src = ANA_CMPR_REF_SRC_INTERNAL;
config.cross_type = ANA_CMPR_CROSS_NEG;
TEST_ESP_OK(ana_cmpr_new_unit(&config, &cmpr));
int src_chan_io = test_init_src_chan_gpio();
TEST_ESP_OK(ana_cmpr_set_internal_reference(cmpr, &ref_cfg));
TEST_ESP_OK(ana_cmpr_set_debounce(cmpr, &dbc_cfg));
TEST_ESP_OK(ana_cmpr_register_event_callbacks(cmpr, &cbs, &cnt));
TEST_ESP_OK(ana_cmpr_enable(cmpr));
esp_rom_delay_us(1000); // allow the comparator analog block to settle after power-up
gpio_set_level(src_chan_io, 0); // falling: armed as NEG, must fire as NEG
esp_rom_delay_us(1000);
TEST_ASSERT_EQUAL_UINT32(0, cnt.pos_cnt);
TEST_ASSERT_EQUAL_UINT32(1, cnt.neg_cnt);
gpio_set_level(src_chan_io, 1); // rising: POS never armed, must not fire
esp_rom_delay_us(1000);
TEST_ASSERT_EQUAL_UINT32(0, cnt.pos_cnt);
TEST_ASSERT_EQUAL_UINT32(1, cnt.neg_cnt);
TEST_ESP_OK(ana_cmpr_disable(cmpr));
TEST_ESP_OK(ana_cmpr_del_unit(cmpr));
}
#endif
}

View File

@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2023-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -31,6 +31,26 @@ extern "C" {
*/
bool test_ana_cmpr_on_cross_callback(ana_cmpr_handle_t cmpr, const ana_cmpr_cross_event_data_t *edata, void *user_ctx);
/**
* @brief Test context to count how many POS/NEG cross events were reported
*/
typedef struct {
uint32_t pos_cnt;
uint32_t neg_cnt;
} test_ana_cmpr_edge_cnt_t;
/**
* @brief Test on cross callback that tallies observed cross direction
*
* @param cmpr Analog Comparator handle
* @param edata Event data
* @param user_ctx User context, need to input a `test_ana_cmpr_edge_cnt_t *`
* @return
* - true Need to yield
* - false Don't need yield
*/
bool test_ana_cmpr_edge_cnt_callback(ana_cmpr_handle_t cmpr, const ana_cmpr_cross_event_data_t *edata, void *user_ctx);
/**
* @brief Initialize Analog Comparator source channel GPIO
*

View File

@@ -37,3 +37,14 @@ void IRAM_ATTR test_simulate_src_signal(int src_chan, uint32_t val)
{
gpio_set_level(src_chan, val);
}
bool IRAM_ATTR test_ana_cmpr_edge_cnt_callback(ana_cmpr_handle_t cmpr, const ana_cmpr_cross_event_data_t *edata, void *user_ctx)
{
test_ana_cmpr_edge_cnt_t *cnt = (test_ana_cmpr_edge_cnt_t *)user_ctx;
if (edata->cross_type == ANA_CMPR_CROSS_POS) {
cnt->pos_cnt++;
} else if (edata->cross_type == ANA_CMPR_CROSS_NEG) {
cnt->neg_cnt++;
}
return false;
}