diff --git a/components/esp_driver_ana_cmpr/ana_cmpr.c b/components/esp_driver_ana_cmpr/ana_cmpr.c index 294e24d6598..dd3fc922b42 100644 --- a/components/esp_driver_ana_cmpr/ana_cmpr.c +++ b/components/esp_driver_ana_cmpr/ana_cmpr.c @@ -20,8 +20,69 @@ static ana_cmpr_handle_t s_ana_cmpr[ANALOG_CMPR_LL_GET(INST_NUM)] = { [0 ...(ANALOG_CMPR_LL_GET(INST_NUM) - 1)] = NULL, }; +/* Global slot state for unit create/delete lifecycle */ +static ana_cmpr_unit_slot_state_t s_unit_slot_state[ANALOG_CMPR_LL_GET(INST_NUM)] = { + [0 ...(ANALOG_CMPR_LL_GET(INST_NUM) - 1)] = ANA_CMPR_UNIT_SLOT_FREE, +}; + /* Global spin lock */ -static portMUX_TYPE s_spinlock = portMUX_INITIALIZER_UNLOCKED; +portMUX_TYPE s_ana_cmpr_spinlock = portMUX_INITIALIZER_UNLOCKED; + +static esp_err_t ana_cmpr_reserve_unit_slot(int unit_id) +{ + esp_err_t ret = ESP_OK; + + ANA_CMPR_CRITICAL_SECTION() { + if (s_unit_slot_state[unit_id] != ANA_CMPR_UNIT_SLOT_FREE) { + ret = ESP_ERR_INVALID_STATE; + } else { + s_unit_slot_state[unit_id] = ANA_CMPR_UNIT_SLOT_ALLOCATING; + } + } + + return ret; +} + +static void ana_cmpr_release_unit_slot(int unit_id) +{ + ANA_CMPR_CRITICAL_SECTION() { + if (s_unit_slot_state[unit_id] == ANA_CMPR_UNIT_SLOT_ALLOCATING || s_unit_slot_state[unit_id] == ANA_CMPR_UNIT_SLOT_DELETING) { + s_unit_slot_state[unit_id] = ANA_CMPR_UNIT_SLOT_FREE; + } + } +} + +static esp_err_t ana_cmpr_publish_unit_slot(int unit_id, ana_cmpr_handle_t cmpr) +{ + esp_err_t ret = ESP_ERR_INVALID_STATE; + ANA_CMPR_CRITICAL_SECTION() { + if (s_unit_slot_state[unit_id] == ANA_CMPR_UNIT_SLOT_ALLOCATING && s_ana_cmpr[unit_id] == NULL) { + s_ana_cmpr[unit_id] = cmpr; + s_unit_slot_state[unit_id] = ANA_CMPR_UNIT_SLOT_READY; + ret = ESP_OK; + } + } + return ret; +} + +static esp_err_t ana_cmpr_unpublish_unit_slot_for_delete(ana_cmpr_handle_t cmpr, int *ret_unit_id) +{ + esp_err_t ret = ESP_ERR_INVALID_ARG; + int unit_id = cmpr->unit_id; + ANA_CMPR_CRITICAL_SECTION() { + if (unit_id >= 0 && unit_id < ANALOG_CMPR_LL_GET(INST_NUM) && + s_ana_cmpr[unit_id] == cmpr && + s_unit_slot_state[unit_id] == ANA_CMPR_UNIT_SLOT_READY) { + s_ana_cmpr[unit_id] = NULL; + s_unit_slot_state[unit_id] = ANA_CMPR_UNIT_SLOT_DELETING; + ret = ESP_OK; + } + } + if (ret == ESP_OK) { + *ret_unit_id = unit_id; + } + return ret; +} void ana_cmpr_default_intr_handler(void *usr_data) { @@ -36,13 +97,17 @@ void ana_cmpr_default_intr_handler(void *usr_data) ana_cmpr_cross_cb_t on_cross = cmpr_handle->cbs.on_cross; if (on_cross) { // some chip can distinguish the edge of the cross event -#if ANALOG_CMPR_LL_SUPPORT(EDGE_TYPE) +#if ANALOG_CMPR_LL_SUPPORT(EDGE_SPECIFIC_INTR_MASK) for (int i = 0; i < ANALOG_CMPR_LL_GET(SRC_CHANNEL_NUM); i++) { + if (!cmpr_handle->src_chans[i].configured) { + continue; + } evt_data.src_chan_id = i; if (status & ANALOG_CMPR_LL_POS_CROSS_INTR_MASK(cmpr_handle->unit_id, i)) { evt_data.cross_type = ANA_CMPR_CROSS_POS; need_yield |= on_cross(cmpr_handle, &evt_data, cmpr_handle->user_data); - } else if (status & ANALOG_CMPR_LL_NEG_CROSS_INTR_MASK(cmpr_handle->unit_id, i)) { + } + if (status & ANALOG_CMPR_LL_NEG_CROSS_INTR_MASK(cmpr_handle->unit_id, i)) { evt_data.cross_type = ANA_CMPR_CROSS_NEG; need_yield |= on_cross(cmpr_handle, &evt_data, cmpr_handle->user_data); } @@ -60,11 +125,6 @@ static void ana_cmpr_destroy_unit(ana_cmpr_handle_t cmpr) { int unit_id = cmpr->unit_id; - // Disable function clock first - analog_cmpr_ll_enable_function_clock(unit_id, false); - // Disable bus clock last - analog_cmpr_ll_enable_bus_clock(unit_id, false); - #if CONFIG_PM_ENABLE if (cmpr->pm_lock) { esp_pm_lock_delete(cmpr->pm_lock); @@ -74,6 +134,11 @@ static void ana_cmpr_destroy_unit(ana_cmpr_handle_t cmpr) esp_intr_free(cmpr->intr_handle); } free(cmpr); + + // Disable function clock first + analog_cmpr_ll_enable_function_clock(unit_id, false); + // Disable bus clock last + analog_cmpr_ll_enable_bus_clock(unit_id, false); } #if ANALOG_CMPR_LL_GET(IP_VERSION) > 1 @@ -90,53 +155,127 @@ static int _ana_cmpr_gpio_to_pad_id(ana_cmpr_handle_t cmpr, int gpio_num) } #endif -static void _ana_cmpr_init_default_channels(ana_cmpr_handle_t cmpr, const ana_cmpr_config_t *config) +static uint32_t _ana_cmpr_build_scan_mask(ana_cmpr_handle_t cmpr) +{ + uint32_t scan_mask = 0; + for (int i = 0; i < ANALOG_CMPR_LL_GET(SRC_CHANNEL_NUM); i++) { + if (cmpr->src_chans[i].configured) { + scan_mask |= BIT(i); + } + } + return scan_mask; +} + +static uint32_t _ana_cmpr_build_intr_mask(ana_cmpr_handle_t cmpr) +{ + uint32_t intr_mask = 0; + for (int i = 0; i < ANALOG_CMPR_LL_GET(SRC_CHANNEL_NUM); i++) { + if (cmpr->src_chans[i].configured) { + intr_mask |= analog_cmpr_ll_get_intr_mask_by_type(cmpr->unit_id, i, cmpr->src_chans[i].cross_type); + } + } + return intr_mask; +} + +static void _ana_cmpr_refresh_masks(ana_cmpr_handle_t cmpr) +{ + cmpr->intr_mask = _ana_cmpr_build_intr_mask(cmpr); + +#if ANALOG_CMPR_LL_GET(IP_VERSION) > 1 + uint32_t scan_mask = _ana_cmpr_build_scan_mask(cmpr); + analog_cmpr_ll_set_scan_mask(cmpr->dev, scan_mask); +#endif +} + +static esp_err_t _ana_cmpr_check_pad_conflict(ana_cmpr_handle_t cmpr, int src_chan_id, int pad_id) +{ + if (cmpr->ref_chan.ref_src == ANA_CMPR_REF_SRC_EXTERNAL && cmpr->ref_chan.pad_id == pad_id) { + ESP_LOGE(TAG, "source channel %d conflicts with external reference PAD %d", src_chan_id, pad_id); + return ESP_ERR_INVALID_ARG; + } + for (int i = 0; i < ANALOG_CMPR_LL_GET(SRC_CHANNEL_NUM); i++) { + if (i == src_chan_id) { + continue; + } + if (cmpr->src_chans[i].configured && cmpr->src_chans[i].pad_id == pad_id) { + ESP_LOGE(TAG, "source channel %d conflicts with source channel %d on PAD %d", src_chan_id, i, pad_id); + return ESP_ERR_INVALID_ARG; + } + } + return ESP_OK; +} + +static esp_err_t _ana_cmpr_init_default_channels(ana_cmpr_handle_t cmpr, const ana_cmpr_config_t *config) { int unit_id = cmpr->unit_id; - cmpr->ref_chan.ref_src = config->ref_src; -#if ANALOG_CMPR_LL_GET(IP_VERSION) > 1 - // GPIO number of external reference channel is configurable - // cmpr->ref_chan.gpio_num = - cmpr->ref_chan.pad_id = _ana_cmpr_gpio_to_pad_id(cmpr, cmpr->ref_chan.gpio_num); -#else - cmpr->ref_chan.gpio_num = ana_cmpr_periph[unit_id].ext_ref_gpio; -#endif - cmpr->src_chans[0].chan_id = 0; + // all channels are disabled by default + for (int i = 0; i < ANALOG_CMPR_LL_GET(SRC_CHANNEL_NUM); i++) { + cmpr->src_chans[i].chan_id = i; + cmpr->src_chans[i].cross_type = ANA_CMPR_CROSS_DISABLE; + cmpr->src_chans[i].gpio_num = -1; + cmpr->src_chans[i].pad_id = -1; + cmpr->src_chans[i].configured = false; + } + + // init source channel 0 according to the config cmpr->src_chans[0].cross_type = config->cross_type; + cmpr->src_chans[0].configured = true; + #if ANALOG_CMPR_LL_GET(IP_VERSION) > 1 // GPIO number of source channel is configurable - // cmpr->src_chans[0].gpio_num = + cmpr->src_chans[0].gpio_num = config->src_chan0_gpio; cmpr->src_chans[0].pad_id = _ana_cmpr_gpio_to_pad_id(cmpr, cmpr->src_chans[0].gpio_num); + ESP_RETURN_ON_FALSE(cmpr->src_chans[0].pad_id != -1, ESP_ERR_INVALID_ARG, TAG, "invalid source channel GPIO: %d", cmpr->src_chans[0].gpio_num); #else cmpr->src_chans[0].gpio_num = ana_cmpr_periph[unit_id].src_gpio; + cmpr->src_chans[0].pad_id = 0; #endif + cmpr->ref_chan.ref_src = config->ref_src; + if (config->ref_src == ANA_CMPR_REF_SRC_EXTERNAL) { +#if ANALOG_CMPR_LL_GET(IP_VERSION) > 1 + // GPIO number of external reference channel is configurable + cmpr->ref_chan.gpio_num = config->ext_ref_gpio; + cmpr->ref_chan.pad_id = _ana_cmpr_gpio_to_pad_id(cmpr, cmpr->ref_chan.gpio_num); + ESP_RETURN_ON_FALSE(cmpr->ref_chan.pad_id != -1, ESP_ERR_INVALID_ARG, TAG, "invalid external reference GPIO: %d", cmpr->ref_chan.gpio_num); + ESP_RETURN_ON_FALSE(cmpr->ref_chan.pad_id != cmpr->src_chans[0].pad_id, ESP_ERR_INVALID_ARG, TAG, + "source channel 0 PAD conflicts with external reference PAD"); +#else + cmpr->ref_chan.gpio_num = ana_cmpr_periph[unit_id].ext_ref_gpio; + cmpr->ref_chan.pad_id = 1; +#endif + } + analog_cmpr_ll_set_ref_source(cmpr->dev, config->ref_src); - -#if !ANALOG_CMPR_LL_SUPPORT(EDGE_TYPE) - // set which cross type can trigger the interrupt - analog_cmpr_ll_set_intr_cross_type(cmpr->dev, config->cross_type); -#endif // !ANALOG_CMPR_LL_SUPPORT(EDGE_TYPE) - // each source channel's cross type can contribute different mask to the unit's intr_mask, so set it here according to the config - cmpr->intr_mask |= analog_cmpr_ll_get_intr_mask_by_type(unit_id, 0, config->cross_type); - - // setup the gpio pad for the source and reference signal - gpio_config_as_analog(cmpr->src_chans[0].gpio_num); #if ANALOG_CMPR_LL_GET(IP_VERSION) > 1 analog_cmpr_ll_set_src_pad(cmpr->dev, 0, cmpr->src_chans[0].pad_id); + if (config->ref_src == ANA_CMPR_REF_SRC_EXTERNAL) { + analog_cmpr_ll_set_ext_ref_pad(cmpr->dev, cmpr->ref_chan.pad_id); + } #endif + + // setup the gpio pad for the source and reference signal after validation has passed + gpio_config_as_analog(cmpr->src_chans[0].gpio_num); if (config->ref_src == ANA_CMPR_REF_SRC_EXTERNAL) { gpio_config_as_analog(cmpr->ref_chan.gpio_num); -#if ANALOG_CMPR_LL_GET(IP_VERSION) > 1 - analog_cmpr_ll_set_ext_ref_pad(cmpr->dev, cmpr->ref_chan.pad_id); -#endif ESP_LOGD(TAG, "unit %d: source0 signal from GPIO %d, reference signal from GPIO %d", unit_id, cmpr->src_chans[0].gpio_num, cmpr->ref_chan.gpio_num); } else { ESP_LOGD(TAG, "unit %d: source0 signal from GPIO %d, reference signal from internal", unit_id, cmpr->src_chans[0].gpio_num); } + +#if !ANALOG_CMPR_LL_SUPPORT(EDGE_SPECIFIC_INTR_MASK) + // set which cross type can trigger the interrupt + analog_cmpr_ll_set_intr_cross_type(cmpr->dev, config->cross_type); +#endif // !ANALOG_CMPR_LL_SUPPORT(EDGE_SPECIFIC_INTR_MASK) +#if ANALOG_CMPR_LL_GET(IP_VERSION) > 1 + analog_cmpr_ll_set_resample_limit(cmpr->dev, config->resample_limit); +#endif + _ana_cmpr_refresh_masks(cmpr); + + return ESP_OK; } esp_err_t ana_cmpr_new_unit(const ana_cmpr_config_t *config, ana_cmpr_handle_t *ret_cmpr) @@ -147,16 +286,20 @@ esp_err_t ana_cmpr_new_unit(const ana_cmpr_config_t *config, ana_cmpr_handle_t * esp_err_t ret = ESP_OK; ana_cmpr_handle_t ana_cmpr_hdl = NULL; int unit_id = config->unit; + bool slot_reserved = false; ESP_RETURN_ON_FALSE(unit_id >= 0 && unit_id < ANALOG_CMPR_LL_GET(INST_NUM), ESP_ERR_INVALID_ARG, TAG, "invalid unit id"); - ESP_RETURN_ON_FALSE(!s_ana_cmpr[unit_id], ESP_ERR_INVALID_STATE, TAG, "unit has been allocated already"); - if (config->intr_priority) { - ESP_RETURN_ON_FALSE(1 << (config->intr_priority) & ANA_CMPR_ALLOW_INTR_PRIORITY_MASK, ESP_ERR_INVALID_ARG, - TAG, "invalid interrupt priority:%d", config->intr_priority); + ESP_RETURN_ON_FALSE((config->intr_priority >= 0) && (config->intr_priority < 32), + ESP_ERR_INVALID_ARG, TAG, "invalid interrupt priority:%d", config->intr_priority); + if (config->intr_priority > 0) { + ESP_RETURN_ON_FALSE((1U << config->intr_priority) & ANA_CMPR_ALLOW_INTR_PRIORITY_MASK, + ESP_ERR_INVALID_ARG, TAG, "invalid interrupt priority:%d", config->intr_priority); } + ESP_RETURN_ON_ERROR(ana_cmpr_reserve_unit_slot(unit_id), TAG, "unit has been allocated already"); + slot_reserved = true; // analog comparator unit must be allocated from internal memory because it contains atomic variable ana_cmpr_hdl = heap_caps_calloc(1, sizeof(struct ana_cmpr_t), MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT); - ESP_RETURN_ON_FALSE(ana_cmpr_hdl, ESP_ERR_NO_MEM, TAG, "no memory for analog comparator object"); + ESP_GOTO_ON_FALSE(ana_cmpr_hdl, ESP_ERR_NO_MEM, err, TAG, "no memory for analog comparator object"); /* Assign analog comparator unit */ ana_cmpr_hdl->dev = ANALOG_CMPR_LL_GET_HW(unit_id); @@ -187,7 +330,7 @@ esp_err_t ana_cmpr_new_unit(const ana_cmpr_config_t *config, ana_cmpr_handle_t * err, TAG, "get source clock frequency failed"); // init the default source and reference channels according to the config - _ana_cmpr_init_default_channels(ana_cmpr_hdl, config); + ESP_GOTO_ON_ERROR(_ana_cmpr_init_default_channels(ana_cmpr_hdl, config), err, TAG, "init default channels failed"); #if CONFIG_PM_ENABLE // Create PM lock, because the light sleep may disable the clock and power domain used by the analog comparator @@ -197,14 +340,14 @@ esp_err_t ana_cmpr_new_unit(const ana_cmpr_config_t *config, ana_cmpr_handle_t * #endif // different unit share the same interrupt register, so using a spin lock to protect it - portENTER_CRITICAL(&s_spinlock); - // disable the interrupt by default, and clear all pending status - analog_cmpr_ll_enable_intr(ana_cmpr_hdl->dev, ANALOG_CMPR_LL_ALL_INTR_MASK(unit_id), false); - analog_cmpr_ll_clear_intr(ana_cmpr_hdl->dev, ANALOG_CMPR_LL_ALL_INTR_MASK(unit_id)); - portEXIT_CRITICAL(&s_spinlock); + ANA_CMPR_CRITICAL_SECTION() { + // disable the interrupt by default, and clear all pending status + analog_cmpr_ll_enable_intr(ana_cmpr_hdl->dev, ANALOG_CMPR_LL_ALL_INTR_MASK(unit_id), false); + analog_cmpr_ll_clear_intr(ana_cmpr_hdl->dev, ANALOG_CMPR_LL_ALL_INTR_MASK(unit_id)); + } // register the analog comparator unit to the global object array - s_ana_cmpr[unit_id] = ana_cmpr_hdl; + ESP_GOTO_ON_ERROR(ana_cmpr_publish_unit_slot(unit_id, ana_cmpr_hdl), err, TAG, "publish unit slot failed"); *ret_cmpr = ana_cmpr_hdl; return ESP_OK; @@ -212,28 +355,27 @@ err: if (ana_cmpr_hdl) { ana_cmpr_destroy_unit(ana_cmpr_hdl); } + if (slot_reserved) { + ana_cmpr_release_unit_slot(unit_id); + } return ret; } esp_err_t ana_cmpr_del_unit(ana_cmpr_handle_t cmpr) { - if (cmpr == NULL) { - return ESP_ERR_INVALID_ARG; - } + esp_err_t ret = ESP_OK; + bool state_acquired = false; + ESP_RETURN_ON_FALSE(cmpr, ESP_ERR_INVALID_ARG, TAG, "null pointer"); /* Search the global object array to check if the input handle is valid */ int unit_id = -1; - for (int i = 0; i < ANALOG_CMPR_LL_GET(INST_NUM); i++) { - if (s_ana_cmpr[i] == cmpr) { - unit_id = i; - break; - } + ANA_CMPR_WITH_TEMP_STATE(cmpr, ANA_CMPR_FSM_INIT, state_acquired) { + ret = ana_cmpr_unpublish_unit_slot_for_delete(cmpr, &unit_id); } - ESP_RETURN_ON_FALSE(unit_id != -1, ESP_ERR_INVALID_ARG, TAG, "unregistered unit handle"); - ESP_RETURN_ON_FALSE(atomic_load(&cmpr->fsm) == ANA_CMPR_FSM_INIT, ESP_ERR_INVALID_STATE, TAG, "not in init state"); + ESP_RETURN_ON_FALSE(state_acquired, ESP_ERR_INVALID_STATE, TAG, "not in init state"); + ESP_RETURN_ON_ERROR(ret, TAG, "unregistered unit handle"); ana_cmpr_destroy_unit(cmpr); - // unregister it from the global object array - s_ana_cmpr[unit_id] = NULL; + ana_cmpr_release_unit_slot(unit_id); ESP_LOGD(TAG, "unit %d deleted", (int)unit_id); return ESP_OK; @@ -250,9 +392,12 @@ esp_err_t ana_cmpr_set_internal_reference(ana_cmpr_handle_t cmpr, const ana_cmpr } // the underlying register may be accessed by different threads at the same time, so use spin lock to protect it - portENTER_CRITICAL_SAFE(&s_spinlock); - analog_cmpr_ll_set_internal_ref_voltage(cmpr->dev, ref_cfg->ref_volt); - portEXIT_CRITICAL_SAFE(&s_spinlock); + ANA_CMPR_CRITICAL_SECTION() { + analog_cmpr_ll_set_internal_ref_voltage(cmpr->dev, ref_cfg->ref_volt); +#if ANALOG_CMPR_LL_GET(IP_VERSION) > 1 + analog_cmpr_ll_set_ref_hys_level(cmpr->dev, ref_cfg->ref_hys_level); +#endif + } return ESP_OK; } @@ -264,21 +409,175 @@ esp_err_t ana_cmpr_set_debounce(ana_cmpr_handle_t cmpr, const ana_cmpr_debounce_ } /* Transfer the time to clock cycles */ - uint32_t wait_cycle = dbc_cfg->wait_us * (cmpr->src_clk_freq_hz / 1000000); + uint32_t wait_cycle = (uint64_t)dbc_cfg->wait_us * cmpr->src_clk_freq_hz / 1000000; // the underlying register may be accessed by different threads at the same time, so use spin lock to protect it - portENTER_CRITICAL_SAFE(&s_spinlock); - analog_cmpr_ll_set_cross_debounce_cycle(cmpr->dev, wait_cycle); - portEXIT_CRITICAL_SAFE(&s_spinlock); + ANA_CMPR_CRITICAL_SECTION() { + analog_cmpr_ll_set_cross_debounce_cycle(cmpr->dev, wait_cycle); + } return ESP_OK; } +esp_err_t ana_cmpr_add_src_chan(ana_cmpr_handle_t cmpr, int src_chan_id, const ana_cmpr_src_chan_config_t *src_cfg) +{ + esp_err_t ret = ESP_OK; + bool state_acquired = false; + ESP_RETURN_ON_FALSE(cmpr && src_cfg, ESP_ERR_INVALID_ARG, TAG, "invalid argument"); + ESP_RETURN_ON_FALSE(_ana_cmpr_is_src_chan_id_valid(src_chan_id), ESP_ERR_INVALID_ARG, TAG, "invalid source channel id %d", src_chan_id); + ESP_RETURN_ON_FALSE(src_cfg->cross_type >= ANA_CMPR_CROSS_DISABLE && src_cfg->cross_type <= ANA_CMPR_CROSS_ANY, + ESP_ERR_INVALID_ARG, TAG, "invalid cross type %d", src_cfg->cross_type); + + int gpio_num = src_cfg->gpio_num; + int pad_id = -1; +#if ANALOG_CMPR_LL_GET(IP_VERSION) > 1 + // This check depends only on static pad capabilities, not current unit topology. + pad_id = _ana_cmpr_gpio_to_pad_id(cmpr, gpio_num); + ESP_RETURN_ON_FALSE(pad_id != -1, ESP_ERR_INVALID_ARG, TAG, "invalid source channel GPIO: %d", gpio_num); +#else + gpio_num = ana_cmpr_periph[cmpr->unit_id].src_gpio; + pad_id = 0; + ESP_RETURN_ON_FALSE(src_cfg->gpio_num == gpio_num, ESP_ERR_INVALID_ARG, TAG, "source channel 0 GPIO is fixed to %d", gpio_num); +#endif + + ANA_CMPR_WITH_TEMP_STATE(cmpr, ANA_CMPR_FSM_INIT, state_acquired) { + // Topology-dependent validation must stay inside the claimed state window. + ret = _ana_cmpr_check_pad_conflict(cmpr, src_chan_id, pad_id); + if (ret == ESP_OK) { + ANA_CMPR_CRITICAL_SECTION() { + cmpr->src_chans[src_chan_id].chan_id = src_chan_id; + cmpr->src_chans[src_chan_id].gpio_num = gpio_num; + cmpr->src_chans[src_chan_id].pad_id = pad_id; + cmpr->src_chans[src_chan_id].cross_type = src_cfg->cross_type; + cmpr->src_chans[src_chan_id].configured = true; +#if ANALOG_CMPR_LL_GET(IP_VERSION) > 1 + analog_cmpr_ll_set_src_pad(cmpr->dev, src_chan_id, pad_id); +#endif + _ana_cmpr_refresh_masks(cmpr); + } + } + } + ESP_RETURN_ON_FALSE(state_acquired, ESP_ERR_INVALID_STATE, TAG, "not in init state"); + ESP_RETURN_ON_ERROR(ret, TAG, "add source channel failed"); + + gpio_config_as_analog(gpio_num); + return ESP_OK; +} + +esp_err_t ana_cmpr_remove_src_chan(ana_cmpr_handle_t cmpr, int src_chan_id) +{ + bool state_acquired = false; + ESP_RETURN_ON_FALSE(cmpr, ESP_ERR_INVALID_ARG, TAG, "invalid argument"); + ESP_RETURN_ON_FALSE(_ana_cmpr_is_src_chan_id_valid(src_chan_id), ESP_ERR_INVALID_ARG, TAG, "invalid source channel id %d", src_chan_id); + ANA_CMPR_WITH_TEMP_STATE(cmpr, ANA_CMPR_FSM_INIT, state_acquired) { + ANA_CMPR_CRITICAL_SECTION() { + cmpr->src_chans[src_chan_id].configured = false; + cmpr->src_chans[src_chan_id].cross_type = ANA_CMPR_CROSS_DISABLE; + cmpr->src_chans[src_chan_id].gpio_num = -1; + cmpr->src_chans[src_chan_id].pad_id = -1; + _ana_cmpr_refresh_masks(cmpr); + } + } + ESP_RETURN_ON_FALSE(state_acquired, ESP_ERR_INVALID_STATE, TAG, "not in init state"); + return ESP_OK; +} + +esp_err_t ana_cmpr_set_src_chan_cross_type(ana_cmpr_handle_t cmpr, int src_chan_id, ana_cmpr_cross_type_t cross_type) +{ +#if ANALOG_CMPR_LL_SUPPORT(EDGE_SPECIFIC_INTR_MASK) + // On edge-type capable hardware, each channel's interrupt source (thus unit intr_mask composition) + // is fixed when channels are configured. Runtime cross-type switching would require rebuilding and + // re-binding interrupt sources, which this API intentionally does not do. + (void)cmpr; + (void)src_chan_id; + (void)cross_type; + return ESP_ERR_NOT_SUPPORTED; +#else + esp_err_t ret = ESP_OK; + if (cmpr == NULL) { + return ESP_ERR_INVALID_ARG; + } + if (cross_type < ANA_CMPR_CROSS_DISABLE || cross_type > ANA_CMPR_CROSS_ANY) { + return ESP_ERR_INVALID_ARG; + } + if (!_ana_cmpr_is_src_chan_id_valid(src_chan_id)) { + return ESP_ERR_INVALID_ARG; + } + ANA_CMPR_CRITICAL_SECTION() { + if (!cmpr->src_chans[src_chan_id].configured) { + ret = ESP_ERR_INVALID_STATE; + } else { + cmpr->src_chans[src_chan_id].cross_type = cross_type; + analog_cmpr_ll_set_intr_cross_type(cmpr->dev, cross_type); + // Keep software mask mirror synchronized even on targets where mask mapping is currently fixed. + _ana_cmpr_refresh_masks(cmpr); + } + } + return ret; +#endif +} + +esp_err_t ana_cmpr_set_scan_config(ana_cmpr_handle_t cmpr, const ana_cmpr_scan_config_t *scan_cfg) +{ + ESP_RETURN_ON_FALSE(cmpr && scan_cfg, ESP_ERR_INVALID_ARG, TAG, "invalid argument"); +#if ANALOG_CMPR_LL_GET(IP_VERSION) > 1 + bool state_acquired = false; + ESP_RETURN_ON_FALSE(scan_cfg->scan_mode == ANA_CMPR_SCAN_MODE_FULL || scan_cfg->scan_mode == ANA_CMPR_SCAN_MODE_STEP, + ESP_ERR_INVALID_ARG, TAG, "invalid scan mode %d", scan_cfg->scan_mode); + uint64_t poll_period_cycle = (uint64_t)scan_cfg->poll_period_us * cmpr->src_clk_freq_hz / 1000000; + ESP_RETURN_ON_FALSE(poll_period_cycle <= ANALOG_CMPR_LL_MAX_POLL_PERIOD_CYCLES, ESP_ERR_INVALID_ARG, TAG, "poll period out of range: %"PRIu64" cycles", poll_period_cycle); + ANA_CMPR_WITH_TEMP_STATE(cmpr, ANA_CMPR_FSM_INIT, state_acquired) { + ANA_CMPR_CRITICAL_SECTION() { + analog_cmpr_ll_set_scan_mode(cmpr->dev, scan_cfg->scan_mode); + analog_cmpr_ll_set_poll_period(cmpr->dev, (uint32_t)poll_period_cycle); + } + } + ESP_RETURN_ON_FALSE(state_acquired, ESP_ERR_INVALID_STATE, TAG, "not in init state"); + return ESP_OK; +#else + (void)scan_cfg; + return ESP_ERR_NOT_SUPPORTED; +#endif +} + +esp_err_t ana_cmpr_trigger_scan(ana_cmpr_handle_t cmpr) +{ + if (!cmpr) { + return ESP_ERR_INVALID_ARG; + } +#if ANALOG_CMPR_LL_SUPPORT(SW_SCAN) + bool state_acquired = false; + ANA_CMPR_WITH_TEMP_STATE(cmpr, ANA_CMPR_FSM_ENABLE, state_acquired) { + analog_cmpr_ll_start_scan(cmpr->dev); + } + if (!state_acquired) { + return ESP_ERR_INVALID_STATE; + } + return ESP_OK; +#else + return ESP_ERR_NOT_SUPPORTED; +#endif +} + +esp_err_t ana_cmpr_get_output_level(ana_cmpr_handle_t cmpr, int src_chan_id, bool *out_level) +{ + ESP_RETURN_ON_FALSE(cmpr && out_level, ESP_ERR_INVALID_ARG, TAG, "invalid argument"); +#if ANALOG_CMPR_LL_GET(IP_VERSION) > 1 + ESP_RETURN_ON_FALSE(_ana_cmpr_is_src_chan_id_valid(src_chan_id), ESP_ERR_INVALID_ARG, TAG, "invalid source channel id %d", src_chan_id); + + *out_level = analog_cmpr_ll_get_compare_result(cmpr->dev, src_chan_id); + return ESP_OK; +#else + return ESP_ERR_NOT_SUPPORTED; +#endif +} + esp_err_t ana_cmpr_register_event_callbacks(ana_cmpr_handle_t cmpr, const ana_cmpr_event_callbacks_t *cbs, void *user_data) { + esp_err_t ret = ESP_OK; + bool state_acquired = false; if (cmpr == NULL || cbs == NULL) { return ESP_ERR_INVALID_ARG; } - ESP_RETURN_ON_FALSE(atomic_load(&cmpr->fsm) == ANA_CMPR_FSM_INIT, ESP_ERR_INVALID_STATE, TAG, "not in init state"); #if CONFIG_ANA_CMPR_ISR_CACHE_SAFE if (cbs->on_cross) { ESP_RETURN_ON_FALSE(esp_ptr_in_iram(cbs->on_cross), ESP_ERR_INVALID_ARG, TAG, "on_cross is not in IRAM"); @@ -287,66 +586,76 @@ esp_err_t ana_cmpr_register_event_callbacks(ana_cmpr_handle_t cmpr, const ana_cm ESP_RETURN_ON_FALSE(esp_ptr_internal(user_data), ESP_ERR_INVALID_ARG, TAG, "user_data is not in internal RAM"); } #endif - - // the interrupt service is lazy installed. - if (!cmpr->intr_handle) { - int intr_flags = ANA_CMPR_INTR_FLAG | ((cmpr->intr_priority > 0) ? BIT(cmpr->intr_priority) : ESP_INTR_FLAG_LOWMED); - ESP_RETURN_ON_ERROR(esp_intr_alloc_intrstatus(ana_cmpr_periph[cmpr->unit_id].intr_src, intr_flags, - (uint32_t)analog_cmpr_ll_get_intr_status_reg(cmpr->dev), - ANALOG_CMPR_LL_ALL_INTR_MASK(cmpr->unit_id), ana_cmpr_default_intr_handler, - cmpr, &cmpr->intr_handle), - TAG, "allocate interrupt failed"); + ANA_CMPR_WITH_TEMP_STATE(cmpr, ANA_CMPR_FSM_INIT, state_acquired) { + // the interrupt service is lazy installed. + if (!cmpr->intr_handle) { + int intr_flags = ANA_CMPR_INTR_FLAG | ((cmpr->intr_priority > 0) ? BIT(cmpr->intr_priority) : ESP_INTR_FLAG_LOWMED); + ret = esp_intr_alloc_intrstatus(ana_cmpr_periph[cmpr->unit_id].intr_src, intr_flags, + (uint32_t)analog_cmpr_ll_get_intr_status_reg(cmpr->dev), + ANALOG_CMPR_LL_ALL_INTR_MASK(cmpr->unit_id), ana_cmpr_default_intr_handler, + cmpr, &cmpr->intr_handle); + } + if (ret == ESP_OK) { + /* Save the callback functions */ + memcpy(&(cmpr->cbs), cbs, sizeof(ana_cmpr_event_callbacks_t)); + cmpr->user_data = user_data; + ESP_LOGV(TAG, "unit %d event callback registered", cmpr->unit_id); + } } - - /* Save the callback functions */ - memcpy(&(cmpr->cbs), cbs, sizeof(ana_cmpr_event_callbacks_t)); - cmpr->user_data = user_data; - - ESP_LOGV(TAG, "unit %d event callback registered", cmpr->unit_id); + ESP_RETURN_ON_FALSE(state_acquired, ESP_ERR_INVALID_STATE, TAG, "not in init state"); + ESP_RETURN_ON_ERROR(ret, TAG, "allocate interrupt failed"); return ESP_OK; } esp_err_t ana_cmpr_enable(ana_cmpr_handle_t cmpr) { + esp_err_t ret = ESP_OK; + bool state_acquired = false; + ana_cmpr_fsm_t final_state = ANA_CMPR_FSM_INIT; if (cmpr == NULL) { return ESP_ERR_INVALID_ARG; } - ana_cmpr_fsm_t expected_fsm = ANA_CMPR_FSM_INIT; - if (atomic_compare_exchange_strong(&cmpr->fsm, &expected_fsm, ANA_CMPR_FSM_WAIT)) { -#if CONFIG_PM_ENABLE - if (cmpr->pm_lock) { - esp_pm_lock_acquire(cmpr->pm_lock); + ANA_CMPR_WITH_STATE_TRANSITION(cmpr, ANA_CMPR_FSM_INIT, final_state, state_acquired) { + // Check after claiming INIT->WAIT, so source-channel updates can't race this precondition. + if (_ana_cmpr_build_scan_mask(cmpr) == 0) { + ret = ESP_ERR_INVALID_STATE; + ESP_LOGE(TAG, "no source channel configured"); } + if (ret == ESP_OK) { +#if CONFIG_PM_ENABLE + if (cmpr->pm_lock) { + esp_pm_lock_acquire(cmpr->pm_lock); + } #endif - // the underlying register may be accessed by different threads at the same time, so use spin lock to protect it - portENTER_CRITICAL(&s_spinlock); - analog_cmpr_ll_enable_intr(cmpr->dev, cmpr->intr_mask, true); - analog_cmpr_ll_enable(cmpr->dev, true); - portEXIT_CRITICAL(&s_spinlock); + // the underlying register may be accessed by different threads at the same time, so use spin lock to protect it + ANA_CMPR_CRITICAL_SECTION() { + analog_cmpr_ll_enable_intr(cmpr->dev, cmpr->intr_mask, true); + analog_cmpr_ll_enable(cmpr->dev, true); + } - // switch the state machine to enable state - atomic_store(&cmpr->fsm, ANA_CMPR_FSM_ENABLE); - ESP_LOGD(TAG, "unit %d enabled", (int)cmpr->unit_id); - } else { - ESP_RETURN_ON_FALSE(false, ESP_ERR_INVALID_STATE, TAG, "not in init state"); + final_state = ANA_CMPR_FSM_ENABLE; + ESP_LOGD(TAG, "unit %d enabled", (int)cmpr->unit_id); + } } - - return ESP_OK; + ESP_RETURN_ON_FALSE(state_acquired, ESP_ERR_INVALID_STATE, TAG, "not in init state"); + ESP_RETURN_ON_ERROR(ret, TAG, "enable unit failed"); + return ret; } esp_err_t ana_cmpr_disable(ana_cmpr_handle_t cmpr) { + bool state_acquired = false; + ana_cmpr_fsm_t final_state = ANA_CMPR_FSM_ENABLE; if (cmpr == NULL) { return ESP_ERR_INVALID_ARG; } - ana_cmpr_fsm_t expected_fsm = ANA_CMPR_FSM_ENABLE; - if (atomic_compare_exchange_strong(&cmpr->fsm, &expected_fsm, ANA_CMPR_FSM_WAIT)) { + ANA_CMPR_WITH_STATE_TRANSITION(cmpr, ANA_CMPR_FSM_ENABLE, final_state, state_acquired) { // the underlying register may be accessed by different threads at the same time, so use spin lock to protect it - portENTER_CRITICAL(&s_spinlock); - analog_cmpr_ll_enable_intr(cmpr->dev, cmpr->intr_mask, false); - analog_cmpr_ll_enable(cmpr->dev, false); - portEXIT_CRITICAL(&s_spinlock); + ANA_CMPR_CRITICAL_SECTION() { + analog_cmpr_ll_enable_intr(cmpr->dev, cmpr->intr_mask, false); + analog_cmpr_ll_enable(cmpr->dev, false); + } #if CONFIG_PM_ENABLE if (cmpr->pm_lock) { @@ -354,11 +663,31 @@ esp_err_t ana_cmpr_disable(ana_cmpr_handle_t cmpr) } #endif - // switch the state machine to init state - atomic_store(&cmpr->fsm, ANA_CMPR_FSM_INIT); + final_state = ANA_CMPR_FSM_INIT; ESP_LOGD(TAG, "unit %d disabled", (int)cmpr->unit_id); - } else { - ESP_RETURN_ON_FALSE(false, ESP_ERR_INVALID_STATE, TAG, "not enabled yet"); + } + ESP_RETURN_ON_FALSE(state_acquired, ESP_ERR_INVALID_STATE, TAG, "not enabled yet"); + return ESP_OK; +} + +esp_err_t ana_cmpr_get_channel_gpio(ana_cmpr_handle_t cmpr, ana_cmpr_channel_type_t chan_type, int chan_id, gpio_num_t *gpio_num) +{ + ESP_RETURN_ON_FALSE(cmpr && gpio_num, ESP_ERR_INVALID_ARG, TAG, "invalid argument"); + + switch (chan_type) { + case ANA_CMPR_SOURCE_CHAN: + ESP_RETURN_ON_FALSE(_ana_cmpr_is_src_chan_id_valid(chan_id), ESP_ERR_INVALID_ARG, TAG, "invalid source channel id %d", chan_id); + ESP_RETURN_ON_FALSE(cmpr->src_chans[chan_id].configured, ESP_ERR_INVALID_STATE, TAG, "source channel %d not configured", chan_id); + *gpio_num = cmpr->src_chans[chan_id].gpio_num; + break; + case ANA_CMPR_EXT_REF_CHAN: + ESP_RETURN_ON_FALSE(chan_id == 0, ESP_ERR_INVALID_ARG, TAG, "reference channel id must be 0"); + ESP_RETURN_ON_FALSE(cmpr->ref_chan.ref_src == ANA_CMPR_REF_SRC_EXTERNAL, ESP_ERR_NOT_FOUND, TAG, "reference channel uses internal source"); + *gpio_num = cmpr->ref_chan.gpio_num; + break; + default: + ESP_LOGE(TAG, "invalid channel type"); + return ESP_ERR_INVALID_ARG; } return ESP_OK; @@ -372,39 +701,9 @@ static void ana_cmpr_override_default_log_level(void) } #endif -/////////////////////////////// Legacy API for backward compatibility, will be removed in the future /////////////////// -// These APIs are implemented based on the "legacy" ref and src channel objects in the analog comparator unit, -// which are designed for the old version driver that only support one ref and src channel and directly configured in the analog comparator unit. -// The legacy channels are still used in the new version driver for backward compatibility, but they are not recommended for new use -// because they have some limitations, such as source channel can only support one GPIO input. -// New APIs with more flexible channel configuration are provided in the new version driver, -// which are implemented based on the new ref and src channel objects and are recommended for new use. -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -esp_err_t ana_cmpr_set_cross_type(ana_cmpr_handle_t cmpr, ana_cmpr_cross_type_t cross_type) -{ -#if ANALOG_CMPR_LL_SUPPORT(EDGE_TYPE) - /* 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 - if (cmpr == NULL) { - return ESP_ERR_INVALID_ARG; - } - if (cross_type < ANA_CMPR_CROSS_DISABLE || cross_type > ANA_CMPR_CROSS_ANY) { - return ESP_ERR_INVALID_ARG; - } - - portENTER_CRITICAL_SAFE(&s_spinlock); - analog_cmpr_ll_set_intr_cross_type(cmpr->dev, cross_type); - // each source channel's cross type can contribute different mask to the unit's intr_mask - cmpr->intr_mask |= analog_cmpr_ll_get_intr_mask_by_type(cmpr->unit_id, 0, cross_type); - portEXIT_CRITICAL_SAFE(&s_spinlock); - - return ESP_OK; -#endif -} +/////////////////////////////////////////////////////////////////////////////////////////////////////////// +/////////////// Deprecated APIs for backward compatibility, will be removed in ESP-IDF v7.0 /////////////// +/////////////////////////////////////////////////////////////////////////////////////////////////////////// esp_err_t ana_cmpr_get_gpio(ana_cmpr_unit_t unit, ana_cmpr_channel_type_t chan_type, int *gpio_num) { diff --git a/components/esp_driver_ana_cmpr/ana_cmpr_etm.c b/components/esp_driver_ana_cmpr/ana_cmpr_etm.c index b66347d4330..02a2c728d3a 100644 --- a/components/esp_driver_ana_cmpr/ana_cmpr_etm.c +++ b/components/esp_driver_ana_cmpr/ana_cmpr_etm.c @@ -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 */ @@ -8,15 +8,51 @@ #include "esp_private/etm_interface.h" #include "ana_cmpr_private.h" -#define ETM_MEM_ALLOC_CAPS MALLOC_CAP_DEFAULT - typedef struct { esp_etm_event_t base; + analog_cmpr_dev_t *dev; + ana_cmpr_unit_t unit_id; + int src_chan_id; } ana_cmpr_etm_event_t; +typedef struct { + esp_etm_task_t base; +} ana_cmpr_etm_task_t; + +#if ANALOG_CMPR_LL_GET(IP_VERSION) > 1 +static uint32_t s_etm_event_ref_cnt[ANALOG_CMPR_LL_GET(INST_NUM)][ANALOG_CMPR_LL_GET(SRC_CHANNEL_NUM)] = { + [0 ...(ANALOG_CMPR_LL_GET(INST_NUM) - 1)] = {0}, +}; + +static void ana_cmpr_etm_acquire_src_chan(analog_cmpr_dev_t *dev, ana_cmpr_unit_t unit_id, int src_chan_id) +{ + ANA_CMPR_CRITICAL_SECTION() { + if (s_etm_event_ref_cnt[unit_id][src_chan_id] == 0) { + analog_cmpr_ll_enable_channel_etm(dev, src_chan_id, true); + } + s_etm_event_ref_cnt[unit_id][src_chan_id]++; + } +} + +static void ana_cmpr_etm_release_src_chan(analog_cmpr_dev_t *dev, ana_cmpr_unit_t unit_id, int src_chan_id) +{ + ANA_CMPR_CRITICAL_SECTION() { + if (s_etm_event_ref_cnt[unit_id][src_chan_id] > 0) { + s_etm_event_ref_cnt[unit_id][src_chan_id]--; + if (s_etm_event_ref_cnt[unit_id][src_chan_id] == 0) { + analog_cmpr_ll_enable_channel_etm(dev, src_chan_id, false); + } + } + } +} +#endif + 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); +#if ANALOG_CMPR_LL_GET(IP_VERSION) > 1 + ana_cmpr_etm_release_src_chan(event->dev, event->unit_id, event->src_chan_id); +#endif free(event); return ESP_OK; } @@ -24,15 +60,61 @@ static esp_err_t ana_cmpr_del_etm_event(esp_etm_event_handle_t base_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) { ESP_RETURN_ON_FALSE(cmpr && config && ret_event, ESP_ERR_INVALID_ARG, TAG, "invalid argument"); + ESP_RETURN_ON_FALSE(config->event_type < ANA_CMPR_EVENT_MAX, ESP_ERR_INVALID_ARG, TAG, "invalid event type"); ana_cmpr_unit_t unit_id = cmpr->unit_id; - ana_cmpr_etm_event_t *event = heap_caps_calloc(1, sizeof(ana_cmpr_etm_event_t), ETM_MEM_ALLOC_CAPS); + int src_chan_id = config->src_chan_id; + ESP_RETURN_ON_FALSE(_ana_cmpr_is_src_chan_id_valid(src_chan_id), ESP_ERR_INVALID_ARG, TAG, "invalid source channel id %d", src_chan_id); +#if ANALOG_CMPR_LL_GET(IP_VERSION) > 1 + ESP_RETURN_ON_FALSE(config->event_delay <= ANALOG_CMPR_LL_MAX_ETM_DELAY_CYCLES, ESP_ERR_INVALID_ARG, TAG, + "event delay out of range: %"PRIu32" cycles", config->event_delay); +#endif + ana_cmpr_etm_event_t *event = heap_caps_calloc(1, sizeof(ana_cmpr_etm_event_t), ANA_CMPR_MEM_ALLOC_CAPS); ESP_RETURN_ON_FALSE(event, ESP_ERR_NO_MEM, TAG, "no mem for analog comparator event"); - uint32_t event_id = ANALOG_CMPR_LL_ETM_SOURCE(unit_id, 0, config->event_type); +#if ANALOG_CMPR_LL_GET(IP_VERSION) > 1 + // The ETM enable bit is shared by all events on the same source channel, so + // keep it reference-counted across event handles. + ana_cmpr_etm_acquire_src_chan(cmpr->dev, unit_id, src_chan_id); + ANA_CMPR_CRITICAL_SECTION() { + analog_cmpr_ll_set_etm_delay_cycles(cmpr->dev, config->event_delay); + } +#endif + + uint32_t event_id = ANALOG_CMPR_LL_ETM_SOURCE(unit_id, src_chan_id, 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; + event->dev = cmpr->dev; + event->unit_id = unit_id; + event->src_chan_id = src_chan_id; ESP_LOGD(TAG, "new event @%p, event_id=%"PRIu32", unit_id=%d", event, event_id, unit_id); *ret_event = &event->base; return ESP_OK; } + +#if ANALOG_CMPR_LL_GET(IP_VERSION) > 1 +static esp_err_t ana_cmpr_del_etm_task(esp_etm_task_handle_t base_task) +{ + ana_cmpr_etm_task_t *task = __containerof(base_task, ana_cmpr_etm_task_t, base); + free(task); + return ESP_OK; +} + +esp_err_t ana_cmpr_new_etm_task(ana_cmpr_handle_t cmpr, const ana_cmpr_etm_task_config_t *config, esp_etm_task_handle_t *ret_task) +{ + ESP_RETURN_ON_FALSE(cmpr && config && ret_task, ESP_ERR_INVALID_ARG, TAG, "invalid argument"); + ESP_RETURN_ON_FALSE(config->task_type < ANA_CMPR_TASK_MAX, ESP_ERR_INVALID_ARG, TAG, "invalid task type"); + ana_cmpr_unit_t unit_id = cmpr->unit_id; + ana_cmpr_etm_task_t *task = heap_caps_calloc(1, sizeof(ana_cmpr_etm_task_t), ANA_CMPR_MEM_ALLOC_CAPS); + ESP_RETURN_ON_FALSE(task, ESP_ERR_NO_MEM, TAG, "no mem for analog comparator task"); + + uint32_t task_id = ANALOG_CMPR_LL_ETM_TASK(unit_id, config->task_type); + task->base.del = ana_cmpr_del_etm_task; + task->base.task_id = task_id; + task->base.trig_periph = ETM_TRIG_PERIPH_ANA_CMPR; + + ESP_LOGD(TAG, "new task @%p, task_id=%"PRIu32", unit_id=%d", task, task_id, unit_id); + *ret_task = &task->base; + return ESP_OK; +} +#endif diff --git a/components/esp_driver_ana_cmpr/ana_cmpr_private.h b/components/esp_driver_ana_cmpr/ana_cmpr_private.h index 26a87468f73..103eed46209 100644 --- a/components/esp_driver_ana_cmpr/ana_cmpr_private.h +++ b/components/esp_driver_ana_cmpr/ana_cmpr_private.h @@ -12,6 +12,7 @@ #include #include #include "sdkconfig.h" +#include "freertos/FreeRTOS.h" #if CONFIG_ANA_CMPR_ENABLE_DEBUG_LOG // The local log level must be defined before including esp_log.h // Set the maximum log level for this source file @@ -50,12 +51,29 @@ extern "C" { #endif +/* + * This driver uses two state machines at different layers: + * 1) ana_cmpr_fsm_t (per handle): functional state of a published comparator object. + * It protects transitions among init/config/enable operations after users get a valid handle. + * 2) ana_cmpr_unit_slot_state_t (per unit slot): lifecycle ownership of the global unit slot. + * It protects create/delete in-flight windows before publish and after unpublish. + * + * Keeping these state machines separated avoids overloading a single field for both + * object runtime behavior and global slot allocation lifecycle. + */ typedef enum { ANA_CMPR_FSM_INIT, // Comparator is in the initialization stage, not enabled yet ANA_CMPR_FSM_ENABLE, // Comparator is enabled ANA_CMPR_FSM_WAIT, // Comparator is in the middle of state change, so busy, other operations should wait } ana_cmpr_fsm_t; +typedef enum { + ANA_CMPR_UNIT_SLOT_FREE = 0, // no handle published, slot can be allocated + ANA_CMPR_UNIT_SLOT_ALLOCATING, // unit create is in progress + ANA_CMPR_UNIT_SLOT_READY, // handle published and available + ANA_CMPR_UNIT_SLOT_DELETING, // unit delete is in progress +} ana_cmpr_unit_slot_state_t; + typedef struct ana_cmpr_t ana_cmpr_t; typedef struct ana_cmpr_ref_chan_t { @@ -65,14 +83,15 @@ typedef struct ana_cmpr_ref_chan_t { ana_cmpr_ref_hys_t ref_hys_level; // for external reference channel int gpio_num; - uint32_t pad_id; // the pad id corresponding to the gpio_num + int pad_id; // the pad id corresponding to the gpio_num } ana_cmpr_ref_chan_t; typedef struct ana_cmpr_src_chan_t { uint8_t chan_id; // there're multiple source channels in the analog comparator unit + int pad_id; // the pad id corresponding to the gpio_num ana_cmpr_cross_type_t cross_type; int gpio_num; - uint32_t pad_id; // the pad id corresponding to the gpio_num + bool configured; } ana_cmpr_src_chan_t; struct ana_cmpr_t { @@ -92,6 +111,137 @@ struct ana_cmpr_t { #endif }; +__attribute__((always_inline)) +static inline bool _ana_cmpr_is_src_chan_id_valid(int src_chan_id) +{ + return (src_chan_id >= 0) && (src_chan_id < ANALOG_CMPR_LL_GET(SRC_CHANNEL_NUM)); +} + +extern portMUX_TYPE s_ana_cmpr_spinlock; + +__attribute__((always_inline)) +static inline void _ana_cmpr_enter_critical(void) +{ + portENTER_CRITICAL_SAFE(&s_ana_cmpr_spinlock); +} + +__attribute__((always_inline)) +static inline void _ana_cmpr_exit_critical(void) +{ + portEXIT_CRITICAL_SAFE(&s_ana_cmpr_spinlock); +} + +__attribute__((always_inline)) +static inline bool _ana_cmpr_try_acquire_state(ana_cmpr_handle_t cmpr, ana_cmpr_fsm_t expected_state) +{ + return atomic_compare_exchange_strong(&cmpr->fsm, &expected_state, ANA_CMPR_FSM_WAIT); +} + +__attribute__((always_inline)) +static inline void _ana_cmpr_release_state(ana_cmpr_handle_t cmpr, ana_cmpr_fsm_t target_state) +{ + atomic_store(&cmpr->fsm, target_state); +} + +/* + * Centralized FSM transition rules. + * + * We only model externally visible states here (INIT/ENABLE). + * WAIT is an internal transient state managed by the transition macro. + * It is never a stable/public state, so it doesn't appear in allowed table. + */ +__attribute__((always_inline)) +static inline bool _ana_cmpr_is_transition_allowed(ana_cmpr_fsm_t expected_state, ana_cmpr_fsm_t target_state) +{ + switch (expected_state) { + case ANA_CMPR_FSM_INIT: + return (target_state == ANA_CMPR_FSM_INIT || target_state == ANA_CMPR_FSM_ENABLE); + case ANA_CMPR_FSM_ENABLE: + return (target_state == ANA_CMPR_FSM_ENABLE || target_state == ANA_CMPR_FSM_INIT); + default: + return false; + } +} + +/* + * Finalize a scoped transition with centralized rule checking. + * If caller provides an invalid target, fail-safe to expected_state. + */ +__attribute__((always_inline)) +static inline void _ana_cmpr_complete_transition(ana_cmpr_handle_t cmpr, ana_cmpr_fsm_t expected_state, ana_cmpr_fsm_t target_state) +{ + // only do the validation in debug build, in release build we trust the caller to provide the correct target_state to avoid extra overhead + assert(_ana_cmpr_is_transition_allowed(expected_state, target_state) && "invalid ana_cmpr fsm transition target"); + _ana_cmpr_release_state(cmpr, target_state); +} + +/* + * Token concatenation helpers used by scoped macros below. + * + * We append __LINE__ to guard variable names so each macro expansion gets a + * unique local variable and can be used multiple times in one function. + */ +#define _ANA_CMPR_CONCAT_IMPL(x, y) x##y +#define _ANA_CMPR_CONCAT(x, y) _ANA_CMPR_CONCAT_IMPL(x, y) + +/* + * Scoped critical section helper (RAII-like in C). + * + * Usage: + * ANA_CMPR_CRITICAL_SECTION() { + * // protected code + * } + * + * Expansion model: + * - Enter critical section in for-loop initializer. + * - Run loop body exactly once while guard == true. + * - Exit critical section in for-loop increment expression. + * + * Important: + * - Do NOT use early return/goto/break/continue in this block, otherwise + * _ana_cmpr_exit_critical() may be skipped. + */ +#define ANA_CMPR_CRITICAL_SECTION() \ + for (bool _ANA_CMPR_CONCAT(_ana_cmpr_critical_guard_, __LINE__) = (_ana_cmpr_enter_critical(), true); \ + _ANA_CMPR_CONCAT(_ana_cmpr_critical_guard_, __LINE__); \ + _ana_cmpr_exit_critical(), _ANA_CMPR_CONCAT(_ana_cmpr_critical_guard_, __LINE__) = false) + +/* + * Scoped FSM transition helper. + * + * Semantics: + * - Write whether acquisition succeeds to acquired_var. + * - If acquired, run body once and complete transition using target_state_expr. + * - target_state_expr can be either a value or a mutable variable. + * + * Typical usage: + * bool acquired = false; + * ana_cmpr_fsm_t final_state = ANA_CMPR_FSM_INIT; + * ANA_CMPR_WITH_STATE_TRANSITION(cmpr, ANA_CMPR_FSM_INIT, final_state, acquired) { + * // configure HW before making FSM visible as ENABLE + * final_state = ANA_CMPR_FSM_ENABLE; + * } + * if (!acquired) { return ESP_ERR_INVALID_STATE; } + * + * Important: + * - Do NOT use early return/goto/break/continue in this block, otherwise + * restore/transition step may be skipped. + */ +#define ANA_CMPR_WITH_STATE_TRANSITION(cmpr, expected_state, target_state_var, acquired_var) \ + for (bool _ANA_CMPR_CONCAT(_ana_cmpr_temp_state_guard_, __LINE__) = (((acquired_var) = _ana_cmpr_try_acquire_state((cmpr), (expected_state))), \ + (acquired_var)); \ + _ANA_CMPR_CONCAT(_ana_cmpr_temp_state_guard_, __LINE__); \ + _ana_cmpr_complete_transition((cmpr), (expected_state), (target_state_var)), _ANA_CMPR_CONCAT(_ana_cmpr_temp_state_guard_, __LINE__) = false) + +/* + * Backward-compatible shorthand: + * - expected_state -> WAIT -> expected_state + * - writes acquisition result to acquired_var + * - implemented on top of ANA_CMPR_WITH_STATE_TRANSITION + */ +#define ANA_CMPR_WITH_TEMP_STATE(cmpr, expected_state, acquired_var) \ + ANA_CMPR_WITH_STATE_TRANSITION((cmpr), (expected_state), (expected_state), (acquired_var)) + #ifdef __cplusplus } #endif diff --git a/components/esp_driver_ana_cmpr/include/driver/ana_cmpr.h b/components/esp_driver_ana_cmpr/include/driver/ana_cmpr.h index a72218e18af..7871069d16d 100644 --- a/components/esp_driver_ana_cmpr/include/driver/ana_cmpr.h +++ b/components/esp_driver_ana_cmpr/include/driver/ana_cmpr.h @@ -1,9 +1,11 @@ /* - * SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2023-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ +#pragma once + #include #include #include "esp_err.h" @@ -17,7 +19,7 @@ extern "C" { * @brief Analog comparator unit configuration */ typedef struct { - ana_cmpr_unit_t unit; /*!< Analog comparator unit */ + int unit; /*!< Analog comparator unit ID, index from 0 */ ana_cmpr_clk_src_t clk_src; /*!< The clock source of the analog comparator, * which decide the resolution of the comparator */ @@ -26,30 +28,14 @@ typedef struct { * For internal reference, the reference voltage should be set to `internal_ref_volt`, * 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 1~3. - If set to 0, the driver will automatically select a relative low priority (1,2,3) */ + If set to 0, the driver will automatically select a relative low priority (1,2,3) */ + ana_cmpr_cross_type_t cross_type; /*!< The crossing type of source channel 0, that can trigger interrupt */ + gpio_num_t src_chan0_gpio; /*!< The GPIO number of source channel 0 signal */ + gpio_num_t ext_ref_gpio; /*!< The GPIO number of external reference signal, only valid when `ref_src` is set to `ANA_CMPR_REF_SRC_EXTERNAL` */ + uint8_t resample_limit; /*!< Unit-wide consecutive sample count required to update channel status */ } ana_cmpr_config_t; -/** - * @brief Analog comparator internal reference configuration - */ -typedef struct { - ana_cmpr_ref_voltage_t ref_volt; /*!< The internal reference voltage. It can be specified to a certain fixed percentage of - * the VDD power supply, currently supports 0%~70% VDD with a step 10% - */ -} ana_cmpr_internal_ref_config_t; - -/** - * @brief Analog comparator debounce filter configuration - */ -typedef struct { - uint32_t wait_us; /*!< The wait time to prevent frequent interrupts caused by signal noise or bouncing. - During the specified wait_us period, no new interrupts will be triggered. - Set the value according to the signal characteristics. A rapid signal requires a small wait time, - otherwise the next cross event may be missed. */ -} ana_cmpr_debounce_config_t; - /** * @brief Allocating a new analog comparator unit handle * @@ -65,6 +51,9 @@ esp_err_t ana_cmpr_new_unit(const ana_cmpr_config_t *config, ana_cmpr_handle_t * /** * @brief Delete the analog comparator unit handle + * @note Caller must ensure no active users remain before deleting the unit handle, including any + * ETM event/task handles created from this unit (delete them first via `esp_etm_del_event()` + * and `esp_etm_del_task()`). * * @param[in] cmpr The handle of analog comparator unit * @return @@ -74,6 +63,15 @@ esp_err_t ana_cmpr_new_unit(const ana_cmpr_config_t *config, ana_cmpr_handle_t * */ esp_err_t ana_cmpr_del_unit(ana_cmpr_handle_t cmpr); +/** + * @brief Analog comparator internal reference configuration + */ +typedef struct { + ana_cmpr_ref_voltage_t ref_volt; /*!< The internal reference voltage. It can be specified to a certain fixed percentage of + * the VDD power supply, currently supports 0%~70% VDD with a step 10% */ + ana_cmpr_ref_hys_t ref_hys_level; /*!< Internal reference hysteresis level */ +} ana_cmpr_internal_ref_config_t; + /** * @brief Set internal reference configuration * @note This function only need to be called when `ana_cmpr_config_t::ref_src` is set to `ANA_CMPR_REF_SRC_INTERNAL`. @@ -90,6 +88,16 @@ esp_err_t ana_cmpr_del_unit(ana_cmpr_handle_t cmpr); */ esp_err_t ana_cmpr_set_internal_reference(ana_cmpr_handle_t cmpr, const ana_cmpr_internal_ref_config_t *ref_cfg); +/** + * @brief Analog comparator debounce filter configuration + */ +typedef struct { + uint32_t wait_us; /*!< The wait time to prevent frequent interrupts caused by signal noise or bouncing. + During the specified wait_us period, no new interrupts will be triggered. + Set the value according to the signal characteristics. A rapid signal requires a small wait time, + otherwise the next cross event may be missed. */ +} ana_cmpr_debounce_config_t; + /** * @brief Set debounce configuration to the analog comparator * @note This function is allowed to run within ISR context including interrupt callbacks @@ -105,18 +113,109 @@ esp_err_t ana_cmpr_set_internal_reference(ana_cmpr_handle_t cmpr, const ana_cmpr esp_err_t ana_cmpr_set_debounce(ana_cmpr_handle_t cmpr, const ana_cmpr_debounce_config_t *dbc_cfg); /** - * @brief Set the source signal cross type - * @note The initial cross type is configured in `ana_cmpr_new_unit`, this function can update the cross type - * @note This function is allowed to run within ISR context including interrupt callbacks - * @note This function must be called before `ana_cmpr_register_event_callbacks` + * @brief Analog comparator source channel configuration + */ +typedef struct { + gpio_num_t gpio_num; /*!< Source input GPIO */ + ana_cmpr_cross_type_t cross_type; /*!< Crossing type that can trigger events for this source channel */ +} ana_cmpr_src_chan_config_t; + +/** + * @brief Add or update a source channel + * @note This function can only be called when the comparator unit is in init (disabled) state * * @param[in] cmpr The handle of analog comparator unit + * @param[in] src_chan_id The source channel index + * @param[in] src_cfg Source channel configuration + * @return + * - ESP_OK Add/update source channel success + * - ESP_ERR_INVALID_ARG NULL pointer of the parameters or invalid channel/GPIO configuration + * - ESP_ERR_INVALID_STATE The analog comparator is not in init state + * - ESP_ERR_NOT_SUPPORTED Source channel index is not supported by the current target + */ +esp_err_t ana_cmpr_add_src_chan(ana_cmpr_handle_t cmpr, int src_chan_id, const ana_cmpr_src_chan_config_t *src_cfg); + +/** + * @brief Remove a source channel from scan and interrupt routing + * @note This function can only be called when the comparator unit is in init (disabled) state + * @note This API is idempotent: removing an already-removed channel still returns ESP_OK + * + * @param[in] cmpr The handle of analog comparator unit + * @param[in] src_chan_id The source channel index + * @return + * - ESP_OK Remove source channel success + * - ESP_ERR_INVALID_ARG NULL pointer of the parameters or invalid channel id + * - ESP_ERR_INVALID_STATE The analog comparator is not in init state + * - ESP_ERR_NOT_SUPPORTED Source channel index is not supported by the current target + */ +esp_err_t ana_cmpr_remove_src_chan(ana_cmpr_handle_t cmpr, int src_chan_id); + +/** + * @brief Set cross type for the given source channel + * @note On targets with edge-type interrupt support, interrupt source selection is fixed when + * adding source channels, so runtime cross-type switching is not supported. + * + * @param[in] cmpr The handle of analog comparator unit + * @param[in] src_chan_id The source channel index * @param[in] cross_type The source signal cross type that can trigger the interrupt * @return * - ESP_OK Set cross type configuration success - * - ESP_ERR_INVALID_ARG NULL pointer of the parameters + * - ESP_ERR_INVALID_ARG NULL pointer of the parameters or invalid channel/cross type + * - ESP_ERR_INVALID_STATE The target source channel isn't configured + * - ESP_ERR_NOT_SUPPORTED Runtime cross-type switching isn't supported by the current target */ -esp_err_t ana_cmpr_set_cross_type(ana_cmpr_handle_t cmpr, ana_cmpr_cross_type_t cross_type); +esp_err_t ana_cmpr_set_src_chan_cross_type(ana_cmpr_handle_t cmpr, int src_chan_id, ana_cmpr_cross_type_t cross_type); + +/** + * @brief Analog comparator scan configuration + */ +typedef struct { + ana_cmpr_scan_mode_t scan_mode; /*!< Channel scan mode */ + uint32_t poll_period_us; /*!< Channel switching wait time in microseconds */ +} ana_cmpr_scan_config_t; + +/** + * @brief Set scan configuration + * @note This function can only be called when the comparator unit is in init (disabled) state + * + * @param[in] cmpr The handle of analog comparator unit + * @param[in] scan_cfg Scan configuration + * @return + * - ESP_OK Set scan configuration success + * - ESP_ERR_INVALID_ARG NULL pointer of the parameters + * - ESP_ERR_INVALID_STATE The analog comparator is not in init state + * - ESP_ERR_NOT_SUPPORTED Scan configuration is not supported by current target + */ +esp_err_t ana_cmpr_set_scan_config(ana_cmpr_handle_t cmpr, const ana_cmpr_scan_config_t *scan_cfg); + +/** + * @brief Trigger one analog comparator scan sequence + * + * @param[in] cmpr The handle of analog comparator unit + * @return + * - ESP_OK Trigger scan success + * - ESP_ERR_INVALID_ARG NULL pointer of the parameters + * - ESP_ERR_INVALID_STATE Invalid unit state for triggering scan + * - ESP_ERR_NOT_SUPPORTED The hardware doesn't support software triggered scan + */ +esp_err_t ana_cmpr_trigger_scan(ana_cmpr_handle_t cmpr); + +/** + * @brief Get the output level of a source channel + * @note The output level indicates whether the source voltage is higher than the reference voltage + * @note This function is allowed to run within ISR context including interrupt callbacks + * + * @param[in] cmpr The handle of analog comparator unit + * @param[in] src_chan_id The source channel index + * @param[out] out_level The output level of the source channel + * - true: source voltage > reference voltage + * - false: source voltage < reference voltage + * @return + * - ESP_OK Get output level success + * - ESP_ERR_INVALID_ARG NULL pointer of the parameters or invalid channel id + * - ESP_ERR_NOT_SUPPORTED Hardware can't report the output level of the source channel or the source channel index is not supported by the current target + */ +esp_err_t ana_cmpr_get_output_level(ana_cmpr_handle_t cmpr, int src_chan_id, bool *out_level); /** * @brief Register analog comparator interrupt event callbacks @@ -155,7 +254,44 @@ esp_err_t ana_cmpr_enable(ana_cmpr_handle_t cmpr); esp_err_t ana_cmpr_disable(ana_cmpr_handle_t cmpr); /** - * @brief Get the specific GPIO number of the analog comparator unit + * @brief Get the GPIO number of a configured analog comparator channel + * + * @param[in] cmpr The handle of analog comparator unit + * @param[in] chan_type The channel type of analog comparator, either source channel or external reference channel + * @param[in] chan_id The source channel index when `chan_type` is `ANA_CMPR_SOURCE_CHAN`. + * Must be 0 when `chan_type` is `ANA_CMPR_EXT_REF_CHAN` + * @param[out] gpio_num The output GPIO number of this channel + * @return + * - ESP_OK Get GPIO success + * - ESP_ERR_INVALID_ARG NULL pointer of the parameters or wrong channel type/channel id + * - ESP_ERR_INVALID_STATE The target channel is not configured + * - ESP_ERR_NOT_SUPPORTED The source channel index is not supported by the current target + * - ESP_ERR_NOT_FOUND The requested channel doesn't have a GPIO mapping, e.g. the reference source is internal + */ +esp_err_t ana_cmpr_get_channel_gpio(ana_cmpr_handle_t cmpr, ana_cmpr_channel_type_t chan_type, int chan_id, gpio_num_t *gpio_num); + +/** + * @brief Set the cross type for the source channel 0 that can trigger the event + * @note The initial cross type is configured in `ana_cmpr_new_unit`, this function can update the cross type + * @note This function is allowed to run within ISR context including interrupt callbacks + * @note This is a legacy API that only applies to source channel 0 + * + * @param[in] cmpr The handle of analog comparator unit + * @param[in] cross_type The source signal cross type that can trigger the interrupt + * @return + * - ESP_OK Set cross type configuration success + * - ESP_ERR_INVALID_ARG NULL pointer of the parameters + */ +__attribute__((always_inline)) +static inline esp_err_t ana_cmpr_set_cross_type(ana_cmpr_handle_t cmpr, ana_cmpr_cross_type_t cross_type) +{ + return ana_cmpr_set_src_chan_cross_type(cmpr, 0, cross_type); +} + +/** + * @brief Get the fixed GPIO number of the analog comparator unit + * + * @deprecated Please use `ana_cmpr_get_channel_gpio()` instead to query the GPIO of the configured comparator instance * * @param[in] unit The handle of analog comparator unit * @param[in] chan_type The channel type of analog comparator, like source channel or reference channel @@ -164,7 +300,8 @@ esp_err_t ana_cmpr_disable(ana_cmpr_handle_t cmpr); * - ESP_OK Get GPIO success * - ESP_ERR_INVALID_ARG NULL pointer of the parameters or wrong unit number or wrong channel type */ -esp_err_t ana_cmpr_get_gpio(ana_cmpr_unit_t unit, ana_cmpr_channel_type_t chan_type, int *gpio_num); +esp_err_t ana_cmpr_get_gpio(ana_cmpr_unit_t unit, ana_cmpr_channel_type_t chan_type, int *gpio_num) +__attribute__((deprecated("Please use ana_cmpr_get_channel_gpio() instead"))); #ifdef __cplusplus } diff --git a/components/esp_driver_ana_cmpr/include/driver/ana_cmpr_etm.h b/components/esp_driver_ana_cmpr/include/driver/ana_cmpr_etm.h index 87480189c49..98d80105aad 100644 --- a/components/esp_driver_ana_cmpr/include/driver/ana_cmpr_etm.h +++ b/components/esp_driver_ana_cmpr/include/driver/ana_cmpr_etm.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2023-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -23,10 +23,15 @@ extern "C" { */ typedef struct { ana_cmpr_event_type_t event_type; /*!< Which kind of cross type can trigger the ETM event module */ + int src_chan_id; /*!< The index of the source channel that triggers the event */ + uint32_t event_delay; /*!< The delay time after the cross event to trigger the ETM event. + The delay is shared across all channels, last-writer-wins on the global delay timer */ } ana_cmpr_etm_event_config_t; /** * @brief Allocate a new Analog Comparator ETM event + * @note The returned ETM event handle must be deleted by `esp_etm_del_event()` before calling + * `ana_cmpr_del_unit()` on the parent comparator handle. * * @param[in] cmpr Analog Comparator handle that allocated by `ana_cmpr_new_unit` * @param[in] config Analog Comparator ETM event configuration @@ -35,10 +40,31 @@ typedef struct { * - 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); +/** + * @brief Analog Comparator ETM task configuration + */ +typedef struct { + ana_cmpr_task_type_t task_type; /*!< The type of the task */ +} ana_cmpr_etm_task_config_t; + +/** + * @brief Allocate a new Analog Comparator ETM task + * @note The returned ETM task handle must be deleted by `esp_etm_del_task()` before calling + * `ana_cmpr_del_unit()` on the parent comparator handle. + * + * @param[in] cmpr Analog Comparator handle that allocated by `ana_cmpr_new_unit` + * @param[in] config Analog Comparator ETM task configuration + * @param[out] ret_task The returned generic handle of ETM task, which is used to connect to a event in the ETM driver + * @return + * - ESP_OK Success to create the new ETM task handle + * - ESP_ERR_NO_MEM No memory for the ETM task + * - ESP_ERR_INVALID_ARG NULL pointer of the input parameters + */ +esp_err_t ana_cmpr_new_etm_task(ana_cmpr_handle_t cmpr, const ana_cmpr_etm_task_config_t *config, esp_etm_task_handle_t *ret_task); + #ifdef __cplusplus } #endif diff --git a/components/esp_driver_ana_cmpr/include/driver/ana_cmpr_types.h b/components/esp_driver_ana_cmpr/include/driver/ana_cmpr_types.h index b059f61ab3b..a355cf17f81 100644 --- a/components/esp_driver_ana_cmpr/include/driver/ana_cmpr_types.h +++ b/components/esp_driver_ana_cmpr/include/driver/ana_cmpr_types.h @@ -10,6 +10,7 @@ #include "soc/soc_caps.h" #include "soc/clk_tree_defs.h" #include "hal/ana_cmpr_types.h" +#include "hal/gpio_types.h" #ifdef __cplusplus extern "C" { diff --git a/components/esp_driver_ana_cmpr/linker.lf b/components/esp_driver_ana_cmpr/linker.lf index f57eab48049..f5ac0a04f18 100644 --- a/components/esp_driver_ana_cmpr/linker.lf +++ b/components/esp_driver_ana_cmpr/linker.lf @@ -4,6 +4,10 @@ entries: if ANA_CMPR_CTRL_FUNC_IN_IRAM = y: ana_cmpr: ana_cmpr_set_internal_reference (noflash) ana_cmpr: ana_cmpr_set_debounce (noflash) - ana_cmpr: ana_cmpr_set_cross_type (noflash) + ana_cmpr: ana_cmpr_set_src_chan_cross_type (noflash) + ana_cmpr: ana_cmpr_trigger_scan (noflash) + ana_cmpr: _ana_cmpr_refresh_masks (noflash) + ana_cmpr: _ana_cmpr_build_intr_mask (noflash) + ana_cmpr: _ana_cmpr_build_scan_mask (noflash) if ANA_CMPR_ISR_HANDLER_IN_IRAM = y: ana_cmpr: ana_cmpr_default_intr_handler (noflash) diff --git a/components/esp_driver_ana_cmpr/test_apps/analog_comparator/README.md b/components/esp_driver_ana_cmpr/test_apps/analog_comparator/README.md index 592e3228eb9..0e1fac8c256 100644 --- a/components/esp_driver_ana_cmpr/test_apps/analog_comparator/README.md +++ b/components/esp_driver_ana_cmpr/test_apps/analog_comparator/README.md @@ -1,2 +1,2 @@ -| Supported Targets | ESP32-C5 | ESP32-C61 | ESP32-H2 | ESP32-H21 | ESP32-P4 | -| ----------------- | -------- | --------- | -------- | --------- | -------- | +| Supported Targets | ESP32-C5 | ESP32-C61 | ESP32-H2 | ESP32-H21 | ESP32-P4 | ESP32-S31 | +| ----------------- | -------- | --------- | -------- | --------- | -------- | --------- | diff --git a/components/esp_driver_ana_cmpr/test_apps/analog_comparator/main/test_ana_cmpr.c b/components/esp_driver_ana_cmpr/test_apps/analog_comparator/main/test_ana_cmpr.c index cddad13aae0..a5ab0e1f3d3 100644 --- a/components/esp_driver_ana_cmpr/test_apps/analog_comparator/main/test_ana_cmpr.c +++ b/components/esp_driver_ana_cmpr/test_apps/analog_comparator/main/test_ana_cmpr.c @@ -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 */ @@ -14,11 +14,18 @@ TEST_CASE("ana_cmpr unit install/uninstall", "[ana_cmpr]") .clk_src = ANA_CMPR_CLK_SRC_DEFAULT, .ref_src = ANA_CMPR_REF_SRC_INTERNAL, .cross_type = ANA_CMPR_CROSS_ANY, +#if ANALOG_CMPR_LL_GET(IP_VERSION) > 1 + .src_chan0_gpio = ana_cmpr_periph[0].pad_gpios[0], +#endif }; /* Allocate a wrong unit */ TEST_ESP_ERR(ESP_ERR_INVALID_ARG, ana_cmpr_new_unit(&config, &cmpr)); - /* Allocate a correct unit */ + /* Reject negative interrupt priority */ config.unit = 0; + config.intr_priority = -1; + TEST_ESP_ERR(ESP_ERR_INVALID_ARG, ana_cmpr_new_unit(&config, &cmpr)); + config.intr_priority = 0; + /* Allocate a correct unit */ 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)); @@ -36,20 +43,21 @@ TEST_CASE("ana_cmpr unit install/uninstall", "[ana_cmpr]") 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_ARG, 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; +#if ANALOG_CMPR_LL_GET(IP_VERSION) > 1 + config.ext_ref_gpio = ana_cmpr_periph[0].pad_gpios[1]; +#endif TEST_ESP_OK(ana_cmpr_new_unit(&config, &cmpr)); TEST_ESP_ERR(ESP_ERR_NOT_ALLOWED, ana_cmpr_set_internal_reference(cmpr, &ref_cfg)); TEST_ESP_ERR(ESP_ERR_INVALID_ARG, ana_cmpr_del_unit(NULL)); TEST_ESP_OK(ana_cmpr_del_unit(cmpr)); } -TEST_CASE("ana_cmpr internal reference", "[ana_cmpr]") +TEST_CASE("ana_cmpr event callback", "[ana_cmpr]") { uint32_t cnt = 0; ana_cmpr_handle_t cmpr = NULL; @@ -58,9 +66,14 @@ TEST_CASE("ana_cmpr internal reference", "[ana_cmpr]") .clk_src = ANA_CMPR_CLK_SRC_DEFAULT, .ref_src = ANA_CMPR_REF_SRC_INTERNAL, .cross_type = ANA_CMPR_CROSS_ANY, +#if ANALOG_CMPR_LL_GET(IP_VERSION) > 1 + .src_chan0_gpio = ana_cmpr_periph[TEST_ANA_CMPR_UNIT_ID].pad_gpios[0], + .resample_limit = 3, +#endif }; TEST_ESP_OK(ana_cmpr_new_unit(&config, &cmpr)); - int src_chan_io = test_init_src_chan_gpio(TEST_ANA_CMPR_UNIT_ID, 0); + + gpio_num_t src_chan_io = test_init_src_chan_gpio(cmpr, 0, 0); ana_cmpr_internal_ref_config_t ref_cfg = { .ref_volt = ANA_CMPR_REF_VOLT_50_PCT_VDD, }; @@ -73,6 +86,14 @@ TEST_CASE("ana_cmpr internal reference", "[ana_cmpr]") .on_cross = test_ana_cmpr_on_cross_callback, }; +#if ANALOG_CMPR_LL_GET(IP_VERSION) > 1 + ana_cmpr_scan_config_t scan_cfg = { + .scan_mode = ANA_CMPR_SCAN_MODE_FULL, + .poll_period_us = 2, + }; + TEST_ESP_OK(ana_cmpr_set_scan_config(cmpr, &scan_cfg)); +#endif + printf("register ana_cmpr event callbacks\r\n"); TEST_ESP_OK(ana_cmpr_register_event_callbacks(cmpr, &cbs, &cnt)); TEST_ESP_OK(ana_cmpr_enable(cmpr)); @@ -80,9 +101,117 @@ TEST_CASE("ana_cmpr internal reference", "[ana_cmpr]") for (uint32_t i = 1; i <= 10; i++) { gpio_set_level(src_chan_io, i % 2); esp_rom_delay_us(100); // must be larger than the debounce time +#if ANALOG_CMPR_LL_GET(IP_VERSION) > 1 + TEST_ESP_OK(ana_cmpr_trigger_scan(cmpr)); + esp_rom_delay_us(100); +#endif // we assume the cross event was triggered already, and the value of cnt should be updated TEST_ASSERT_EQUAL_UINT32(i, cnt); } TEST_ESP_OK(ana_cmpr_disable(cmpr)); TEST_ESP_OK(ana_cmpr_del_unit(cmpr)); } + +TEST_CASE("ana_cmpr source channel management api", "[ana_cmpr]") +{ + ana_cmpr_handle_t cmpr = NULL; + ana_cmpr_config_t config = { + .unit = TEST_ANA_CMPR_UNIT_ID, + .clk_src = ANA_CMPR_CLK_SRC_DEFAULT, + .ref_src = ANA_CMPR_REF_SRC_INTERNAL, + .cross_type = ANA_CMPR_CROSS_ANY, + .resample_limit = 3, + .src_chan0_gpio = ana_cmpr_periph[TEST_ANA_CMPR_UNIT_ID].pad_gpios[0], + }; + TEST_ESP_OK(ana_cmpr_new_unit(&config, &cmpr)); + +#if ANALOG_CMPR_LL_GET(IP_VERSION) > 1 + ana_cmpr_src_chan_config_t src_cfg = { + .gpio_num = ana_cmpr_periph[TEST_ANA_CMPR_UNIT_ID].pad_gpios[1], + .cross_type = ANA_CMPR_CROSS_POS, + }; + TEST_ESP_OK(ana_cmpr_add_src_chan(cmpr, 1, &src_cfg)); + TEST_ESP_OK(ana_cmpr_remove_src_chan(cmpr, 1)); + TEST_ESP_OK(ana_cmpr_remove_src_chan(cmpr, 1)); +#endif + + ana_cmpr_scan_config_t scan_cfg = { + .scan_mode = ANA_CMPR_SCAN_MODE_FULL, + .poll_period_us = 2, + }; +#if ANALOG_CMPR_LL_GET(IP_VERSION) > 1 + TEST_ESP_OK(ana_cmpr_set_scan_config(cmpr, &scan_cfg)); +#else + TEST_ESP_ERR(ESP_ERR_NOT_SUPPORTED, ana_cmpr_set_scan_config(cmpr, &scan_cfg)); +#endif + + // remove channel 0 is allowed, but enabling without any source channel should fail + TEST_ESP_OK(ana_cmpr_remove_src_chan(cmpr, 0)); + TEST_ESP_OK(ana_cmpr_remove_src_chan(cmpr, 0)); + TEST_ESP_ERR(ESP_ERR_INVALID_STATE, ana_cmpr_enable(cmpr)); + + TEST_ESP_OK(ana_cmpr_del_unit(cmpr)); +} + +TEST_CASE("ana_cmpr trigger scan and get output level", "[ana_cmpr]") +{ +#if ANALOG_CMPR_LL_GET(IP_VERSION) <= 1 + TEST_IGNORE_MESSAGE("not supported on old IP version"); +#else + ana_cmpr_handle_t cmpr = NULL; + ana_cmpr_config_t config = { + .unit = TEST_ANA_CMPR_UNIT_ID, + .clk_src = ANA_CMPR_CLK_SRC_DEFAULT, + .ref_src = ANA_CMPR_REF_SRC_INTERNAL, + .cross_type = ANA_CMPR_CROSS_ANY, + .resample_limit = 3, + .src_chan0_gpio = ana_cmpr_periph[TEST_ANA_CMPR_UNIT_ID].pad_gpios[0], + }; + TEST_ESP_OK(ana_cmpr_new_unit(&config, &cmpr)); + + ana_cmpr_src_chan_config_t src_cfg = { + .cross_type = ANA_CMPR_CROSS_ANY, + }; + // for test purpose, source channel N uses pad N. + for (int i = 1; i < ANALOG_CMPR_LL_GET(SRC_CHANNEL_NUM); i++) { + src_cfg.gpio_num = ana_cmpr_periph[TEST_ANA_CMPR_UNIT_ID].pad_gpios[i]; + TEST_ESP_OK(ana_cmpr_add_src_chan(cmpr, i, &src_cfg)); + } + + 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_scan_config_t scan_cfg = { + .scan_mode = ANA_CMPR_SCAN_MODE_FULL, + .poll_period_us = 2, + }; + TEST_ESP_OK(ana_cmpr_set_scan_config(cmpr, &scan_cfg)); + + TEST_ESP_OK(ana_cmpr_enable(cmpr)); + + for (int i = 0; i < ANALOG_CMPR_LL_GET(SRC_CHANNEL_NUM); i++) { + bool out_level = false; + gpio_output_enable(ana_cmpr_periph[TEST_ANA_CMPR_UNIT_ID].pad_gpios[i]); + // Set input to low (0V), which is lower than 50% VDD reference + gpio_set_level(ana_cmpr_periph[TEST_ANA_CMPR_UNIT_ID].pad_gpios[i], 0); + // Trigger a scan to update the comparison result + TEST_ESP_OK(ana_cmpr_trigger_scan(cmpr)); + vTaskDelay(pdMS_TO_TICKS(10)); + TEST_ESP_OK(ana_cmpr_get_output_level(cmpr, i, &out_level)); + TEST_ASSERT_EQUAL(false, out_level); + + // Set input to high (3.3V), which is higher than 50% VDD reference + gpio_set_level(ana_cmpr_periph[TEST_ANA_CMPR_UNIT_ID].pad_gpios[i], 1); + // Trigger a scan to update the comparison result + TEST_ESP_OK(ana_cmpr_trigger_scan(cmpr)); + vTaskDelay(pdMS_TO_TICKS(10)); + TEST_ESP_OK(ana_cmpr_get_output_level(cmpr, i, &out_level)); + TEST_ASSERT_EQUAL(true, out_level); + } + + TEST_ESP_OK(ana_cmpr_disable(cmpr)); + TEST_ESP_OK(ana_cmpr_del_unit(cmpr)); +#endif +} diff --git a/components/esp_driver_ana_cmpr/test_apps/analog_comparator/main/test_ana_cmpr.h b/components/esp_driver_ana_cmpr/test_apps/analog_comparator/main/test_ana_cmpr.h index 182e3909da1..41c6f185716 100644 --- a/components/esp_driver_ana_cmpr/test_apps/analog_comparator/main/test_ana_cmpr.h +++ b/components/esp_driver_ana_cmpr/test_apps/analog_comparator/main/test_ana_cmpr.h @@ -17,6 +17,7 @@ #include "driver/ana_cmpr.h" #include "driver/ana_cmpr_etm.h" #include "driver/gpio.h" +#include "hal/ana_cmpr_periph.h" #if CONFIG_IDF_TARGET_ESP32P4 // The pin of unit 0 is not exposed on some ESP32-P4 runner, so test unit 1 by default @@ -44,13 +45,14 @@ bool test_ana_cmpr_on_cross_callback(ana_cmpr_handle_t cmpr, const ana_cmpr_cros /** * @brief Initialize Analog Comparator source channel GPIO * - * @param unit_id Analog Comparator unit ID + * @param cmpr Analog Comparator handle + * @param src_chan_id The source channel index * @param init_level Initial level of the GPIO * * @return - * - int Source channel GPIO number + * - gpio_num_t Source channel GPIO number */ -int test_init_src_chan_gpio(int unit_id, int init_level); +gpio_num_t test_init_src_chan_gpio(ana_cmpr_handle_t cmpr, int src_chan_id, int init_level); #ifdef __cplusplus } diff --git a/components/esp_driver_ana_cmpr/test_apps/analog_comparator/main/test_ana_cmpr_etm.c b/components/esp_driver_ana_cmpr/test_apps/analog_comparator/main/test_ana_cmpr_etm.c index d24af340a5f..5afda8130e3 100644 --- a/components/esp_driver_ana_cmpr/test_apps/analog_comparator/main/test_ana_cmpr_etm.c +++ b/components/esp_driver_ana_cmpr/test_apps/analog_comparator/main/test_ana_cmpr_etm.c @@ -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 */ @@ -10,6 +10,7 @@ #include "esp_etm.h" #include "driver/gpio.h" #include "driver/gptimer.h" +#include "driver/gpio_etm.h" #include "driver/gptimer_etm.h" #include "test_ana_cmpr.h" @@ -44,6 +45,10 @@ static ana_cmpr_handle_t test_ana_cmpr_init(void) .clk_src = ANA_CMPR_CLK_SRC_DEFAULT, .ref_src = ANA_CMPR_REF_SRC_INTERNAL, .cross_type = ANA_CMPR_CROSS_ANY, +#if ANALOG_CMPR_LL_GET(IP_VERSION) > 1 + .src_chan0_gpio = ana_cmpr_periph[TEST_ANA_CMPR_UNIT_ID].pad_gpios[0], + .resample_limit = 3, +#endif }; TEST_ESP_OK(ana_cmpr_new_unit(&config, &cmpr)); @@ -56,6 +61,15 @@ static ana_cmpr_handle_t test_ana_cmpr_init(void) .wait_us = 10, }; TEST_ESP_OK(ana_cmpr_set_debounce(cmpr, &dbc_cfg)); + +#if ANALOG_CMPR_LL_GET(IP_VERSION) > 1 + ana_cmpr_scan_config_t scan_cfg = { + .scan_mode = ANA_CMPR_SCAN_MODE_FULL, + .poll_period_us = 2, + }; + TEST_ESP_OK(ana_cmpr_set_scan_config(cmpr, &scan_cfg)); +#endif + TEST_ESP_OK(ana_cmpr_enable(cmpr)); return cmpr; @@ -76,7 +90,26 @@ typedef struct { esp_etm_channel_handle_t etm_neg_handle; } test_ana_cmpr_etm_handles_t; -static test_ana_cmpr_etm_handles_t test_ana_cmpr_init_etm(ana_cmpr_handle_t cmpr, gptimer_handle_t gptimer) +/* + * ETM chain v1: ana_cmpr cross events drive gptimer start/stop tasks. + * + * The comparator input is toggled by software in the test body. ETM only forwards + * the comparator crossing events to the timer control tasks. + * + * src_gpio falling edge + * | + * v + * ana_cmpr NEG_CROSS event --ETM--> GPTIMER_ETM_TASK_START_COUNT + * + * src_gpio rising edge + * | + * v + * ana_cmpr POS_CROSS event --ETM--> GPTIMER_ETM_TASK_STOP_COUNT + * + * Result: gptimer counts the low pulse width between the negative and positive + * comparator crossings. + */ +static test_ana_cmpr_etm_handles_t test_ana_cmpr_init_etm_chain_v1(ana_cmpr_handle_t cmpr, gptimer_handle_t gptimer) { test_ana_cmpr_etm_handles_t etm_handles = {}; @@ -110,7 +143,7 @@ static test_ana_cmpr_etm_handles_t test_ana_cmpr_init_etm(ana_cmpr_handle_t cmpr return etm_handles; } -static void test_ana_cmpr_deinit_etm(test_ana_cmpr_etm_handles_t handles) +static void test_ana_cmpr_deinit_etm_chain_v1(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_neg_handle)); @@ -125,20 +158,31 @@ static void test_ana_cmpr_deinit_etm(test_ana_cmpr_etm_handles_t handles) TEST_ESP_OK(esp_etm_del_channel(handles.etm_neg_handle)); } -TEST_CASE("ana_cmpr etm event", "[ana_cmpr][etm]") +TEST_CASE("ana_cmpr etm cross event", "[ana_cmpr][etm]") { gptimer_handle_t gptimer = test_ana_cmpr_gptimer_init(); ana_cmpr_handle_t cmpr = test_ana_cmpr_init(); - int src_gpio = test_init_src_chan_gpio(TEST_ANA_CMPR_UNIT_ID, 1); - test_ana_cmpr_etm_handles_t handles = test_ana_cmpr_init_etm(cmpr, gptimer); + gpio_num_t src_gpio = test_init_src_chan_gpio(cmpr, 0, 1); +#if ANALOG_CMPR_LL_GET(IP_VERSION) > 1 + // Trigger a scan to make sure the initial level is sampled + TEST_ESP_OK(ana_cmpr_trigger_scan(cmpr)); +#endif + + test_ana_cmpr_etm_handles_t handles = test_ana_cmpr_init_etm_chain_v1(cmpr, gptimer); // triggers a negative pulse, whose duration is ~TEST_TIME_US // negedge triggers the gptimer to start task // posedge triggers the gptimer to stop task // gptimer will record the time between the negedge and posedge gpio_set_level(src_gpio, 0); +#if ANALOG_CMPR_LL_GET(IP_VERSION) > 1 + TEST_ESP_OK(ana_cmpr_trigger_scan(cmpr)); +#endif esp_rom_delay_us(TEST_TIME_US); gpio_set_level(src_gpio, 1); +#if ANALOG_CMPR_LL_GET(IP_VERSION) > 1 + TEST_ESP_OK(ana_cmpr_trigger_scan(cmpr)); +#endif // the gptimer should already stopped, so delay any time here is ok vTaskDelay(10); @@ -151,9 +195,248 @@ TEST_CASE("ana_cmpr etm event", "[ana_cmpr][etm]") TEST_ESP_OK(gptimer_get_raw_count(gptimer, &cnt_us_again)); TEST_ASSERT_EQUAL(cnt_us, cnt_us_again); - test_ana_cmpr_deinit_etm(handles); + test_ana_cmpr_deinit_etm_chain_v1(handles); test_ana_cmpr_deinit(cmpr); test_ana_cmpr_gptimer_deinit(gptimer); TEST_ASSERT_UINT_WITHIN(TEST_TIME_US * 0.1, TEST_TIME_US, cnt_us); } + +#if ANALOG_CMPR_LL_GET(IP_VERSION) > 1 + +TEST_CASE("ana_cmpr etm event delete keeps sibling source event working", "[ana_cmpr][etm]") +{ + gptimer_handle_t gptimer = test_ana_cmpr_gptimer_init(); + ana_cmpr_handle_t cmpr = test_ana_cmpr_init(); + gpio_num_t src_gpio = test_init_src_chan_gpio(cmpr, 0, 1); + // Trigger a scan to make sure the initial level is sampled + TEST_ESP_OK(ana_cmpr_trigger_scan(cmpr)); + + ana_cmpr_etm_event_config_t evt_cfg = { + .src_chan_id = 0, + .event_type = ANA_CMPR_EVENT_POS_CROSS, + }; + esp_etm_event_handle_t pos_evt = NULL; + esp_etm_event_handle_t neg_evt = NULL; + TEST_ESP_OK(ana_cmpr_new_etm_event(cmpr, &evt_cfg, &pos_evt)); + evt_cfg.event_type = ANA_CMPR_EVENT_NEG_CROSS; + TEST_ESP_OK(ana_cmpr_new_etm_event(cmpr, &evt_cfg, &neg_evt)); + + gptimer_etm_task_config_t gptimer_task_cfg = { + .task_type = GPTIMER_ETM_TASK_START_COUNT, + }; + esp_etm_task_handle_t start_task = NULL; + TEST_ESP_OK(gptimer_new_etm_task(gptimer, &gptimer_task_cfg, &start_task)); + + esp_etm_channel_config_t channel_cfg = {}; + esp_etm_channel_handle_t neg_handle = NULL; + TEST_ESP_OK(esp_etm_new_channel(&channel_cfg, &neg_handle)); + TEST_ESP_OK(esp_etm_channel_connect(neg_handle, neg_evt, start_task)); + TEST_ESP_OK(esp_etm_channel_enable(neg_handle)); + + // Removing the positive-cross event must not disable ETM routing for the + // remaining negative-cross event on the same source channel. + TEST_ESP_OK(esp_etm_del_event(pos_evt)); + + TEST_ESP_OK(gptimer_set_raw_count(gptimer, 0)); + TEST_ESP_OK(gpio_set_level(src_gpio, 0)); + TEST_ESP_OK(ana_cmpr_trigger_scan(cmpr)); + esp_rom_delay_us(50); + + uint64_t cnt_us = 0; + TEST_ESP_OK(gptimer_get_raw_count(gptimer, &cnt_us)); + + TEST_ESP_OK(esp_etm_channel_disable(neg_handle)); + TEST_ESP_OK(esp_etm_del_channel(neg_handle)); + TEST_ESP_OK(esp_etm_del_task(start_task)); + TEST_ESP_OK(esp_etm_del_event(neg_evt)); + test_ana_cmpr_deinit(cmpr); + test_ana_cmpr_gptimer_deinit(gptimer); + + TEST_ASSERT_GREATER_THAN_UINT32(20, (uint32_t)cnt_us); +} + +#define TEST_ETM_MONITOR_GPIO 4 +#define TEST_ETM_SCAN_PERIOD_US 50 +#define TEST_ETM_WAIT_TIMEOUT_US 2000 + +typedef struct { + esp_etm_event_handle_t gptimer_alarm_evt; + esp_etm_event_handle_t cmpr_pos_evt; + esp_etm_event_handle_t cmpr_neg_evt; + 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; + esp_etm_channel_handle_t etm_reload_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; +} test_ana_cmpr_etm_task_handles_t; + +static void test_ana_cmpr_wait_output_level(ana_cmpr_handle_t cmpr, int src_chan_id, bool expect_level) +{ + bool out_level = !expect_level; + for (int i = 0; i < TEST_ETM_WAIT_TIMEOUT_US; i++) { + TEST_ESP_OK(ana_cmpr_get_output_level(cmpr, src_chan_id, &out_level)); + if (out_level == expect_level) { + return; + } + esp_rom_delay_us(1); + } + TEST_ASSERT_EQUAL(expect_level, out_level); +} + +/* + * ETM chain v2: gptimer alarm events periodically trigger ana_cmpr scans, and the + * resulting comparator crossings drive a monitor GPIO. + * + * gptimer ALARM_MATCH event --ETM--> GPTIMER_ETM_TASK_EN_ALARM + * | + * +-------> ANA_CMPR_TASK_START + * | + * v + * ana_cmpr samples src_gpio + * | + * +--------------------+--------------------+ + * | | + * v v + * ana_cmpr POS_CROSS event --ETM--> GPIO_ETM_TASK_SET ana_cmpr NEG_CROSS event --ETM--> GPIO_ETM_TASK_CLR + * | + * v + * monitor GPIO level + * + * Result: the monitor GPIO mirrors the comparator output, proving the full + * gptimer -> ana_cmpr task -> ana_cmpr event -> gpio task ETM chain works. + */ +static test_ana_cmpr_etm_task_handles_t test_ana_cmpr_init_etm_chain_v2(ana_cmpr_handle_t cmpr, gptimer_handle_t gptimer) +{ + test_ana_cmpr_etm_task_handles_t etm_handles = {}; + + gptimer_etm_event_config_t gptimer_evt_cfg = { + .event_type = GPTIMER_ETM_EVENT_ALARM_MATCH, + }; + TEST_ESP_OK(gptimer_new_etm_event(gptimer, &gptimer_evt_cfg, &etm_handles.gptimer_alarm_evt)); + + gptimer_etm_task_config_t gptimer_task_cfg = { + .task_type = GPTIMER_ETM_TASK_EN_ALARM, + }; + TEST_ESP_OK(gptimer_new_etm_task(gptimer, &gptimer_task_cfg, &etm_handles.gptimer_en_alarm_task)); + + ana_cmpr_etm_task_config_t cmpr_task_cfg = { + .task_type = ANA_CMPR_TASK_START, + }; + TEST_ESP_OK(ana_cmpr_new_etm_task(cmpr, &cmpr_task_cfg, &etm_handles.cmpr_start_task)); + + ana_cmpr_etm_event_config_t cmpr_evt_cfg = { + .event_type = ANA_CMPR_EVENT_POS_CROSS, + }; + TEST_ESP_OK(ana_cmpr_new_etm_event(cmpr, &cmpr_evt_cfg, &etm_handles.cmpr_pos_evt)); + cmpr_evt_cfg.event_type = ANA_CMPR_EVENT_NEG_CROSS; + TEST_ESP_OK(ana_cmpr_new_etm_event(cmpr, &cmpr_evt_cfg, &etm_handles.cmpr_neg_evt)); + + 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; + TEST_ESP_OK(gpio_new_etm_task(&gpio_task_cfg, &etm_handles.gpio_set_task, &etm_handles.gpio_clr_task)); + TEST_ESP_OK(gpio_etm_task_add_gpio(etm_handles.gpio_set_task, TEST_ETM_MONITOR_GPIO)); + TEST_ESP_OK(gpio_etm_task_add_gpio(etm_handles.gpio_clr_task, TEST_ETM_MONITOR_GPIO)); + + esp_etm_channel_config_t etm_cfg = {}; + TEST_ESP_OK(esp_etm_new_channel(&etm_cfg, &etm_handles.etm_reload_handle)); + TEST_ESP_OK(esp_etm_new_channel(&etm_cfg, &etm_handles.etm_scan_handle)); + 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)); + + TEST_ESP_OK(esp_etm_channel_connect(etm_handles.etm_reload_handle, etm_handles.gptimer_alarm_evt, etm_handles.gptimer_en_alarm_task)); + TEST_ESP_OK(esp_etm_channel_connect(etm_handles.etm_scan_handle, etm_handles.gptimer_alarm_evt, etm_handles.cmpr_start_task)); + TEST_ESP_OK(esp_etm_channel_connect(etm_handles.etm_pos_handle, etm_handles.cmpr_pos_evt, etm_handles.gpio_set_task)); + TEST_ESP_OK(esp_etm_channel_connect(etm_handles.etm_neg_handle, etm_handles.cmpr_neg_evt, etm_handles.gpio_clr_task)); + + TEST_ESP_OK(esp_etm_channel_enable(etm_handles.etm_reload_handle)); + TEST_ESP_OK(esp_etm_channel_enable(etm_handles.etm_scan_handle)); + 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 test_ana_cmpr_deinit_etm_chain_v2(test_ana_cmpr_etm_task_handles_t handles) +{ + TEST_ESP_OK(esp_etm_channel_disable(handles.etm_reload_handle)); + TEST_ESP_OK(esp_etm_channel_disable(handles.etm_scan_handle)); + TEST_ESP_OK(esp_etm_channel_disable(handles.etm_pos_handle)); + TEST_ESP_OK(esp_etm_channel_disable(handles.etm_neg_handle)); + + TEST_ESP_OK(gpio_etm_task_rm_gpio(handles.gpio_set_task, TEST_ETM_MONITOR_GPIO)); + TEST_ESP_OK(gpio_etm_task_rm_gpio(handles.gpio_clr_task, TEST_ETM_MONITOR_GPIO)); + + TEST_ESP_OK(esp_etm_del_task(handles.gpio_set_task)); + TEST_ESP_OK(esp_etm_del_task(handles.gpio_clr_task)); + TEST_ESP_OK(esp_etm_del_task(handles.cmpr_start_task)); + TEST_ESP_OK(esp_etm_del_task(handles.gptimer_en_alarm_task)); + + TEST_ESP_OK(esp_etm_del_event(handles.gptimer_alarm_evt)); + 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_reload_handle)); + TEST_ESP_OK(esp_etm_del_channel(handles.etm_scan_handle)); + TEST_ESP_OK(esp_etm_del_channel(handles.etm_pos_handle)); + TEST_ESP_OK(esp_etm_del_channel(handles.etm_neg_handle)); +} + +TEST_CASE("ana_cmpr etm task periodic scan", "[ana_cmpr][etm]") +{ + gptimer_handle_t gptimer = test_ana_cmpr_gptimer_init(); + ana_cmpr_handle_t cmpr = test_ana_cmpr_init(); + gpio_num_t src_gpio = test_init_src_chan_gpio(cmpr, 0, 0); + + gpio_config_t monitor_gpio_cfg = { + .intr_type = GPIO_INTR_DISABLE, + .mode = GPIO_MODE_INPUT_OUTPUT, + .pin_bit_mask = 1ULL << TEST_ETM_MONITOR_GPIO, + }; + TEST_ESP_OK(gpio_config(&monitor_gpio_cfg)); + TEST_ESP_OK(gpio_set_level(TEST_ETM_MONITOR_GPIO, 0)); + + // Prime the comparator with a known initial low level before ETM-driven scans begin. + TEST_ESP_OK(gpio_set_level(src_gpio, 0)); + vTaskDelay(pdMS_TO_TICKS(10)); + TEST_ESP_OK(ana_cmpr_trigger_scan(cmpr)); + vTaskDelay(pdMS_TO_TICKS(10)); + test_ana_cmpr_wait_output_level(cmpr, 0, false); + + gptimer_alarm_config_t alarm_config = { + .reload_count = 0, + .alarm_count = TEST_ETM_SCAN_PERIOD_US, + .flags.auto_reload_on_alarm = true, + }; + TEST_ESP_OK(gptimer_set_alarm_action(gptimer, &alarm_config)); + + test_ana_cmpr_etm_task_handles_t handles = test_ana_cmpr_init_etm_chain_v2(cmpr, gptimer); + + // start the gptimer, which will trigger periodic scans of the comparator via ETM. + // The test will monitor the output GPIO to verify that it changes level in response to the input signal, + // demonstrating that the ETM-driven scans are working correctly. + TEST_ESP_OK(gptimer_start(gptimer)); + + TEST_ASSERT_EQUAL(0, gpio_get_level(TEST_ETM_MONITOR_GPIO)); + + TEST_ESP_OK(gpio_set_level(src_gpio, 1)); + vTaskDelay(pdMS_TO_TICKS(1)); + test_ana_cmpr_wait_output_level(cmpr, 0, true); + TEST_ASSERT_EQUAL(1, gpio_get_level(TEST_ETM_MONITOR_GPIO)); + + TEST_ESP_OK(gpio_set_level(src_gpio, 0)); + vTaskDelay(pdMS_TO_TICKS(1)); + test_ana_cmpr_wait_output_level(cmpr, 0, false); + TEST_ASSERT_EQUAL(0, gpio_get_level(TEST_ETM_MONITOR_GPIO)); + + TEST_ESP_OK(gptimer_stop(gptimer)); + TEST_ESP_OK(gpio_reset_pin(TEST_ETM_MONITOR_GPIO)); + test_ana_cmpr_deinit_etm_chain_v2(handles); + test_ana_cmpr_deinit(cmpr); + test_ana_cmpr_gptimer_deinit(gptimer); +} +#endif diff --git a/components/esp_driver_ana_cmpr/test_apps/analog_comparator/main/test_ana_cmpr_iram.c b/components/esp_driver_ana_cmpr/test_apps/analog_comparator/main/test_ana_cmpr_iram.c index e827c321fa0..4c3d826fb26 100644 --- a/components/esp_driver_ana_cmpr/test_apps/analog_comparator/main/test_ana_cmpr_iram.c +++ b/components/esp_driver_ana_cmpr/test_apps/analog_comparator/main/test_ana_cmpr_iram.c @@ -10,7 +10,7 @@ typedef struct { ana_cmpr_handle_t handle; uint32_t count; - int src_chan_io; + gpio_num_t src_chan_io; } test_ana_cmpr_data_t; static void IRAM_ATTR test_ana_cmpr_iram_safety(void *args) @@ -30,8 +30,12 @@ static void IRAM_ATTR test_ana_cmpr_iram_safety(void *args) for (int i = 1; i <= 10; i++) { gpio_set_level(data->src_chan_io, i % 2); esp_rom_delay_us(100); +#if ANALOG_CMPR_LL_GET(IP_VERSION) > 1 + ana_cmpr_trigger_scan(data->handle); + esp_rom_delay_us(100); +#endif } - ana_cmpr_set_cross_type(data->handle, ANA_CMPR_CROSS_POS); + ana_cmpr_set_src_chan_cross_type(data->handle, 0, ANA_CMPR_CROSS_POS); } TEST_CASE("ana_cmpr works with cache disabled", "[ana_cmpr]") @@ -42,6 +46,10 @@ TEST_CASE("ana_cmpr works with cache disabled", "[ana_cmpr]") .clk_src = ANA_CMPR_CLK_SRC_DEFAULT, .ref_src = ANA_CMPR_REF_SRC_INTERNAL, .cross_type = ANA_CMPR_CROSS_ANY, +#if ANALOG_CMPR_LL_GET(IP_VERSION) > 1 + .src_chan0_gpio = ana_cmpr_periph[TEST_ANA_CMPR_UNIT_ID].pad_gpios[0], + .resample_limit = 3, +#endif }; TEST_ESP_OK(ana_cmpr_new_unit(&config, &cmpr)); @@ -54,10 +62,18 @@ TEST_CASE("ana_cmpr works with cache disabled", "[ana_cmpr]") }; TEST_ESP_OK(ana_cmpr_set_debounce(cmpr, &dbc_cfg)); +#if ANALOG_CMPR_LL_GET(IP_VERSION) > 1 + ana_cmpr_scan_config_t scan_cfg = { + .scan_mode = ANA_CMPR_SCAN_MODE_FULL, + .poll_period_us = 2, + }; + TEST_ESP_OK(ana_cmpr_set_scan_config(cmpr, &scan_cfg)); +#endif + test_ana_cmpr_data_t test_data = { .handle = cmpr, .count = 0, - .src_chan_io = test_init_src_chan_gpio(TEST_ANA_CMPR_UNIT_ID, 0), + .src_chan_io = test_init_src_chan_gpio(cmpr, 0, 0), }; ana_cmpr_event_callbacks_t cbs = { diff --git a/components/esp_driver_ana_cmpr/test_apps/analog_comparator/main/test_ana_cmpr_utils.c b/components/esp_driver_ana_cmpr/test_apps/analog_comparator/main/test_ana_cmpr_utils.c index 2b862b8b108..d11727bae3c 100644 --- a/components/esp_driver_ana_cmpr/test_apps/analog_comparator/main/test_ana_cmpr_utils.c +++ b/components/esp_driver_ana_cmpr/test_apps/analog_comparator/main/test_ana_cmpr_utils.c @@ -15,10 +15,10 @@ bool IRAM_ATTR test_ana_cmpr_on_cross_callback(ana_cmpr_handle_t cmpr, const ana return false; } -int test_init_src_chan_gpio(int unit_id, int init_level) +gpio_num_t test_init_src_chan_gpio(ana_cmpr_handle_t cmpr, int src_chan_id, int init_level) { - int src_chan_num = -1; - TEST_ESP_OK(ana_cmpr_get_gpio(unit_id, ANA_CMPR_SOURCE_CHAN, &src_chan_num)); + gpio_num_t src_chan_num = GPIO_NUM_NC; + TEST_ESP_OK(ana_cmpr_get_channel_gpio(cmpr, ANA_CMPR_SOURCE_CHAN, src_chan_id, &src_chan_num)); TEST_ASSERT(src_chan_num >= 0); TEST_ESP_OK(gpio_set_level(src_chan_num, init_level)); TEST_ESP_OK(gpio_output_enable(src_chan_num)); diff --git a/components/esp_driver_ana_cmpr/test_apps/analog_comparator/main/test_app_main.c b/components/esp_driver_ana_cmpr/test_apps/analog_comparator/main/test_app_main.c index 4b5deb3110d..27f439a8532 100644 --- a/components/esp_driver_ana_cmpr/test_apps/analog_comparator/main/test_app_main.c +++ b/components/esp_driver_ana_cmpr/test_apps/analog_comparator/main/test_app_main.c @@ -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,19 +31,18 @@ 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); + printf("\r\n"); 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"); diff --git a/components/esp_driver_ana_cmpr/test_apps/analog_comparator/pytest_ana_cmpr.py b/components/esp_driver_ana_cmpr/test_apps/analog_comparator/pytest_ana_cmpr.py index fa3c711bf2f..1a38ace3274 100644 --- a/components/esp_driver_ana_cmpr/test_apps/analog_comparator/pytest_ana_cmpr.py +++ b/components/esp_driver_ana_cmpr/test_apps/analog_comparator/pytest_ana_cmpr.py @@ -1,8 +1,9 @@ -# SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD +# SPDX-FileCopyrightText: 2023-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 @@ -14,6 +15,6 @@ from pytest_embedded_idf.utils import idf_parametrize ], indirect=True, ) -@idf_parametrize('target', ['esp32h2', 'esp32p4', 'esp32c5', 'esp32c61'], indirect=['target']) +@idf_parametrize('target', soc_filtered_targets('SOC_ANA_CMPR_SUPPORTED == 1'), indirect=['target']) def test_ana_cmpr(dut: Dut) -> None: dut.run_all_single_board_cases() diff --git a/components/esp_hal_ana_cmpr/esp32c5/ana_cmpr_periph.c b/components/esp_hal_ana_cmpr/esp32c5/ana_cmpr_periph.c index d482ac89666..90afc60376c 100644 --- a/components/esp_hal_ana_cmpr/esp32c5/ana_cmpr_periph.c +++ b/components/esp_hal_ana_cmpr/esp32c5/ana_cmpr_periph.c @@ -9,8 +9,8 @@ const ana_cmpr_periph_t ana_cmpr_periph[1] = { [0] = { - .src_gpio = ANA_CMPR0_SRC_GPIO, - .ext_ref_gpio = ANA_CMPR0_EXT_REF_GPIO, + .src_gpio = 9, + .ext_ref_gpio = 8, .intr_src = ETS_GPIO_EXT_SOURCE, .module_name = "ANA_CMPR_U0", }, diff --git a/components/esp_hal_ana_cmpr/esp32c5/include/hal/ana_cmpr_ll.h b/components/esp_hal_ana_cmpr/esp32c5/include/hal/ana_cmpr_ll.h index edb0bae06b8..e98fe461c0d 100644 --- a/components/esp_hal_ana_cmpr/esp32c5/include/hal/ana_cmpr_ll.h +++ b/components/esp_hal_ana_cmpr/esp32c5/include/hal/ana_cmpr_ll.h @@ -33,7 +33,7 @@ extern "C" { #define ANALOG_CMPR_LL_SRC_CHANNEL_NUM 1 // Can detect positive/negative/any cross type -#define ANALOG_CMPR_LL_SUPPORT_EDGE_TYPE 1 +#define ANALOG_CMPR_LL_SUPPORT_EDGE_SPECIFIC_INTR_MASK 1 #define ANALOG_CMPR_LL_GET_HW(unit) (&ANALOG_CMPR[unit]) @@ -140,6 +140,7 @@ static inline void analog_cmpr_ll_set_cross_debounce_cycle(analog_cmpr_dev_t *hw * @param mask Interrupt mask * @param enable True to enable, False to disable */ +__attribute__((always_inline)) 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; diff --git a/components/esp_hal_ana_cmpr/esp32c61/ana_cmpr_periph.c b/components/esp_hal_ana_cmpr/esp32c61/ana_cmpr_periph.c index ed87543ad8b..3cc31fb4df1 100644 --- a/components/esp_hal_ana_cmpr/esp32c61/ana_cmpr_periph.c +++ b/components/esp_hal_ana_cmpr/esp32c61/ana_cmpr_periph.c @@ -9,8 +9,8 @@ const ana_cmpr_periph_t ana_cmpr_periph[1] = { [0] = { - .src_gpio = ANA_CMPR0_SRC_GPIO, - .ext_ref_gpio = ANA_CMPR0_EXT_REF_GPIO, + .src_gpio = 9, + .ext_ref_gpio = 8, .intr_src = ETS_GPIO_INTERRUPT_EXT_SOURCE, .module_name = "ANA_CMPR_U0", }, diff --git a/components/esp_hal_ana_cmpr/esp32c61/include/hal/ana_cmpr_ll.h b/components/esp_hal_ana_cmpr/esp32c61/include/hal/ana_cmpr_ll.h index 2b4b0147327..8db001d82fb 100644 --- a/components/esp_hal_ana_cmpr/esp32c61/include/hal/ana_cmpr_ll.h +++ b/components/esp_hal_ana_cmpr/esp32c61/include/hal/ana_cmpr_ll.h @@ -33,7 +33,7 @@ extern "C" { #define ANALOG_CMPR_LL_SRC_CHANNEL_NUM 1 // Can detect positive/negative/any cross type -#define ANALOG_CMPR_LL_SUPPORT_EDGE_TYPE 1 +#define ANALOG_CMPR_LL_SUPPORT_EDGE_SPECIFIC_INTR_MASK 1 #define ANALOG_CMPR_LL_GET_HW(unit) (&ANALOG_CMPR[unit]) @@ -140,6 +140,7 @@ static inline void analog_cmpr_ll_set_cross_debounce_cycle(analog_cmpr_dev_t *hw * @param mask Interrupt mask * @param enable True to enable, False to disable */ +__attribute__((always_inline)) 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; diff --git a/components/esp_hal_ana_cmpr/esp32h2/ana_cmpr_periph.c b/components/esp_hal_ana_cmpr/esp32h2/ana_cmpr_periph.c index ef7df956fe4..e0c05aec4b7 100644 --- a/components/esp_hal_ana_cmpr/esp32h2/ana_cmpr_periph.c +++ b/components/esp_hal_ana_cmpr/esp32h2/ana_cmpr_periph.c @@ -9,8 +9,8 @@ const ana_cmpr_periph_t ana_cmpr_periph[1] = { [0] = { - .src_gpio = ANA_CMPR0_SRC_GPIO, - .ext_ref_gpio = ANA_CMPR0_EXT_REF_GPIO, + .src_gpio = 11, + .ext_ref_gpio = 10, .intr_src = ETS_GPIO_INTR_SOURCE, .module_name = "ANA_CMPR_U0", }, diff --git a/components/esp_hal_ana_cmpr/esp32h2/include/hal/ana_cmpr_ll.h b/components/esp_hal_ana_cmpr/esp32h2/include/hal/ana_cmpr_ll.h index 5367732e993..0a78009741f 100644 --- a/components/esp_hal_ana_cmpr/esp32h2/include/hal/ana_cmpr_ll.h +++ b/components/esp_hal_ana_cmpr/esp32h2/include/hal/ana_cmpr_ll.h @@ -137,6 +137,7 @@ static inline void analog_cmpr_ll_set_cross_debounce_cycle(analog_cmpr_dev_t *hw * @param mask Interrupt mask * @param enable True to enable, False to disable */ +__attribute__((always_inline)) 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; diff --git a/components/esp_hal_ana_cmpr/esp32h21/ana_cmpr_periph.c b/components/esp_hal_ana_cmpr/esp32h21/ana_cmpr_periph.c index 7b52a6cbe56..32dd776ef7d 100644 --- a/components/esp_hal_ana_cmpr/esp32h21/ana_cmpr_periph.c +++ b/components/esp_hal_ana_cmpr/esp32h21/ana_cmpr_periph.c @@ -9,8 +9,8 @@ const ana_cmpr_periph_t ana_cmpr_periph[1] = { [0] = { - .src_gpio = ANA_CMPR0_SRC_GPIO, - .ext_ref_gpio = ANA_CMPR0_EXT_REF_GPIO, + .src_gpio = 7, + .ext_ref_gpio = 6, .intr_src = ETS_GPIO_INTERRUPT_PRO_SOURCE, .module_name = "ANA_CMPR_U0", }, diff --git a/components/esp_hal_ana_cmpr/esp32h21/include/hal/ana_cmpr_ll.h b/components/esp_hal_ana_cmpr/esp32h21/include/hal/ana_cmpr_ll.h index 44d2263d953..ec4c47a9e22 100644 --- a/components/esp_hal_ana_cmpr/esp32h21/include/hal/ana_cmpr_ll.h +++ b/components/esp_hal_ana_cmpr/esp32h21/include/hal/ana_cmpr_ll.h @@ -32,7 +32,7 @@ extern "C" { #define ANALOG_CMPR_LL_SRC_CHANNEL_NUM 1 // Can detect positive/negative/any cross type -#define ANALOG_CMPR_LL_SUPPORT_EDGE_TYPE 1 +#define ANALOG_CMPR_LL_SUPPORT_EDGE_SPECIFIC_INTR_MASK 1 #define ANALOG_CMPR_LL_GET_HW(unit) (&ANALOG_CMPR[unit]) @@ -137,6 +137,7 @@ static inline void analog_cmpr_ll_set_cross_debounce_cycle(analog_cmpr_dev_t *hw * @param mask Interrupt mask * @param enable True to enable, False to disable */ +__attribute__((always_inline)) 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; diff --git a/components/esp_hal_ana_cmpr/esp32h4/ana_cmpr_periph.c b/components/esp_hal_ana_cmpr/esp32h4/ana_cmpr_periph.c index 92cd717822d..b8cf9c15a10 100644 --- a/components/esp_hal_ana_cmpr/esp32h4/ana_cmpr_periph.c +++ b/components/esp_hal_ana_cmpr/esp32h4/ana_cmpr_periph.c @@ -8,12 +8,7 @@ const ana_cmpr_periph_t ana_cmpr_periph[1] = { [0] = { - .pad_gpios = { - ANA_CMPR0_PAD0_GPIO, - ANA_CMPR0_PAD1_GPIO, - ANA_CMPR0_PAD2_GPIO, - ANA_CMPR0_PAD3_GPIO, - }, + .pad_gpios = {0, 1, 2, 3}, .intr_src = ETS_ZERO_DET_INTR_SOURCE, .module_name = "ANA_CMPR_U0", }, diff --git a/components/esp_hal_ana_cmpr/esp32h4/include/hal/ana_cmpr_ll.h b/components/esp_hal_ana_cmpr/esp32h4/include/hal/ana_cmpr_ll.h index 836926f7c4f..42eaf6a428b 100644 --- a/components/esp_hal_ana_cmpr/esp32h4/include/hal/ana_cmpr_ll.h +++ b/components/esp_hal_ana_cmpr/esp32h4/include/hal/ana_cmpr_ll.h @@ -7,11 +7,14 @@ #pragma once #include +#include #include "hal/misc.h" #include "hal/assert.h" #include "hal/ana_cmpr_types.h" #include "soc/zero_det_struct.h" +#include "soc/zero_det_reg.h" #include "soc/pcr_struct.h" +#include "soc/soc_etm_struct.h" #include "soc/soc_etm_source.h" #ifdef __cplusplus @@ -19,7 +22,7 @@ extern "C" { #endif // the analog comparator on this target is also called zero detector -typedef zero_dev_t analog_cmpr_dev_t; +typedef zero_det_dev_t analog_cmpr_dev_t; #define ANALOG_CMPR_LL_GET(_attr) ANALOG_CMPR_LL_ ## _attr #define ANALOG_CMPR_LL_SUPPORT(_feat) ANALOG_CMPR_LL_SUPPORT_ ## _feat @@ -37,7 +40,7 @@ typedef zero_dev_t analog_cmpr_dev_t; #define ANALOG_CMPR_LL_SRC_CHANNEL_NUM 3 // Can detect positive/negative/any cross type -#define ANALOG_CMPR_LL_SUPPORT_EDGE_TYPE 1 +#define ANALOG_CMPR_LL_SUPPORT_EDGE_SPECIFIC_INTR_MASK 1 #define ANALOG_CMPR_LL_GET_HW(unit) (&ZERO_DET) @@ -47,6 +50,10 @@ typedef zero_dev_t analog_cmpr_dev_t; #define ANALOG_CMPR_LL_ALL_INTR_MASK(unit) 0x1FF #define ANALOG_CMPR_LL_ETM_SOURCE(unit, src_chan, type) (ZERO_DET_EVT_CHANNEL_1_POS + (src_chan) + ((type) * ANALOG_CMPR_LL_SRC_CHANNEL_NUM)) +#define ANALOG_CMPR_LL_ETM_TASK(unit, type) (ZERO_DET_TASK_START) + +#define ANALOG_CMPR_LL_MAX_ETM_DELAY_CYCLES ZERO_DET_DELAY_EVENT_TIME +#define ANALOG_CMPR_LL_MAX_POLL_PERIOD_CYCLES ZERO_DET_COMP_POLL_PERIOD /** * @brief Enable the bus clock for Analog Comparator module @@ -88,20 +95,20 @@ static inline void analog_cmpr_ll_reset_core(int unit_id) */ static inline void analog_cmpr_ll_set_clk_src(int unit_id, ana_cmpr_clk_src_t clk_src) { - // switch (clk_src) { - // case ANA_CMPR_CLK_SRC_XTAL: - // PCR.zero_det_clk_conf.zero_det_func_clk_sel = 0; - // break; - // case ANA_CMPR_CLK_SRC_RC_FAST: - // PCR.zero_det_clk_conf.zero_det_func_clk_sel = 1; - // break; - // case ANA_CMPR_CLK_SRC_PLL_F48M: - // PCR.zero_det_clk_conf.zero_det_func_clk_sel = 2; - // break; - // default: - // HAL_ASSERT(false); - // break; - // } + switch (clk_src) { + case ANA_CMPR_CLK_SRC_XTAL: + PCR.zero_det_clk_conf.zero_det_func_clk_sel = 0; + break; + case ANA_CMPR_CLK_SRC_RC_FAST: + PCR.zero_det_clk_conf.zero_det_func_clk_sel = 1; + break; + case ANA_CMPR_CLK_SRC_PLL_F48M: + PCR.zero_det_clk_conf.zero_det_func_clk_sel = 2; + break; + default: + HAL_ASSERT(false); + break; + } } /** @@ -214,7 +221,8 @@ static inline void analog_cmpr_ll_set_ref_source(analog_cmpr_dev_t *hw, ana_cmpr __attribute__((always_inline)) static inline void analog_cmpr_ll_set_cross_debounce_cycle(analog_cmpr_dev_t *hw, uint32_t cycle) { - hw->det_filter_cnt.det_filter_cnt = cycle; + // the value must be greater than or equal to 1 + hw->det_filter_cnt.det_filter_cnt = MAX(cycle, 1); } /** @@ -224,6 +232,7 @@ static inline void analog_cmpr_ll_set_cross_debounce_cycle(analog_cmpr_dev_t *hw * @param mask Interrupt mask * @param enable True to enable, False to disable */ +__attribute__((always_inline)) static inline void analog_cmpr_ll_enable_intr(analog_cmpr_dev_t *hw, uint32_t mask, bool enable) { uint32_t val = hw->det_int_ena.val; @@ -309,6 +318,7 @@ static inline void analog_cmpr_ll_set_src_pad(analog_cmpr_dev_t *hw, uint32_t sr * @param hw Analog comparator register base address * @param poll_mask Channel mask (bit0..bit2 => CH1..CH3) */ +__attribute__((always_inline)) static inline void analog_cmpr_ll_set_scan_mask(analog_cmpr_dev_t *hw, uint32_t poll_mask) { hw->det_conf.det_comp_poll_mask = poll_mask & 0x7; @@ -325,6 +335,23 @@ static inline void analog_cmpr_ll_set_scan_mode(analog_cmpr_dev_t *hw, ana_cmpr_ hw->det_conf.det_comp_poll_mode = scan_mode; } +/** + * @brief Start a scan to detect the cross event + * + * @param dev Analog comparator register base address + */ +__attribute__((always_inline)) +static inline void analog_cmpr_ll_start_scan(analog_cmpr_dev_t *dev) +{ + (void)dev; + // enable ETM register clock + PCR.etm_conf.etm_clk_en = 1; + while (PCR.etm_conf.etm_ready == 0) { + } + // use reg_etm_date[31] register to trigger analog comparator to start + SOC_ETM.etm_date.val |= 1UL << 31; +} + /** * @brief Set channel switch wait cycles * @@ -333,7 +360,8 @@ static inline void analog_cmpr_ll_set_scan_mode(analog_cmpr_dev_t *hw, ana_cmpr_ */ static inline void analog_cmpr_ll_set_poll_period(analog_cmpr_dev_t *hw, uint32_t period_cycles) { - hw->det_poll_period.det_comp_poll_period = period_cycles & 0xFFFF; + // the value must be greater than or equal to 1 + hw->det_poll_period.det_comp_poll_period = MAX(period_cycles, 1); } /** @@ -344,7 +372,7 @@ static inline void analog_cmpr_ll_set_poll_period(analog_cmpr_dev_t *hw, uint32_ * @param hw Analog comparator register base address * @param limit_cnt The resample limit count */ -static inline void analog_cmpr_ll_set_resample_limit(analog_cmpr_dev_t *hw, uint32_t limit_cnt) +static inline void analog_cmpr_ll_set_resample_limit(analog_cmpr_dev_t *hw, uint8_t limit_cnt) { hw->det_conf.det_limit_cnt = limit_cnt; } @@ -357,17 +385,18 @@ static inline void analog_cmpr_ll_set_resample_limit(analog_cmpr_dev_t *hw, uint */ static inline void analog_cmpr_ll_set_etm_delay_cycles(analog_cmpr_dev_t *hw, uint32_t delay_cycles) { - hw->det_delay_event_time.det_delay_event_time = delay_cycles & 0xFFFF; + // the value must be greater than or equal to 1 + hw->det_delay_event_time.det_delay_event_time = MAX(delay_cycles, 1); } /** - * @brief Enable per-channel delayed ETM event timer + * @brief Enable per-channel ETM event * * @param hw Analog comparator register base address * @param src_chan Source channel id (0..2) * @param enable true to enable, false to disable */ -static inline void analog_cmpr_ll_enable_channel_etm_delay(analog_cmpr_dev_t *hw, uint32_t src_chan, bool enable) +static inline void analog_cmpr_ll_enable_channel_etm(analog_cmpr_dev_t *hw, uint32_t src_chan, bool enable) { switch (src_chan) { case 0: @@ -399,6 +428,7 @@ static inline void analog_cmpr_ll_enable_capture_timer(analog_cmpr_dev_t *hw, bo * @param hw Analog comparator register base address * @param hys_level Hysteresis level enum */ +__attribute__((always_inline)) static inline void analog_cmpr_ll_set_ref_hys_level(analog_cmpr_dev_t *hw, ana_cmpr_ref_hys_t hys_level) { switch (hys_level) { @@ -411,9 +441,12 @@ static inline void analog_cmpr_ll_set_ref_hys_level(analog_cmpr_dev_t *hw, ana_c case ANA_CMPR_REF_HYS_LEVEL2: hw->det_pad_comp_cfg.det_pad_comp_hys = 2; break; - default: + case ANA_CMPR_REF_HYS_LEVEL3: hw->det_pad_comp_cfg.det_pad_comp_hys = 4; break; + default: + HAL_ASSERT(false); + break; } hw->det_pad_comp_cfg.det_pad_comp_hys_en = hys_level != ANA_CMPR_REF_HYS_LEVEL0; } @@ -446,19 +479,7 @@ static inline bool analog_cmpr_ll_get_compare_result(analog_cmpr_dev_t *hw, uint */ static inline uint32_t analog_cmpr_ll_get_current_capture_time(analog_cmpr_dev_t *hw, uint32_t src_chan) { - uint32_t reg_val = 0; - switch (src_chan) { - case 0: - reg_val = hw->det_channel_1_timer0.det_channel_1_timer0; - break; - case 1: - reg_val = hw->det_channel_2_timer0.det_channel_2_timer0; - break; - default: - reg_val = hw->det_channel_3_timer0.det_channel_3_timer0; - break; - } - return reg_val; + return hw->det_channel_timers[src_chan][0].det_channel_timer; } /** @@ -470,19 +491,7 @@ static inline uint32_t analog_cmpr_ll_get_current_capture_time(analog_cmpr_dev_t */ static inline uint32_t analog_cmpr_ll_get_previous_capture_time(analog_cmpr_dev_t *hw, uint32_t src_chan) { - uint32_t reg_val = 0; - switch (src_chan) { - case 0: - reg_val = hw->det_channel_1_timer1.det_channel_1_timer1; - break; - case 1: - reg_val = hw->det_channel_2_timer1.det_channel_2_timer1; - break; - default: - reg_val = hw->det_channel_3_timer1.det_channel_3_timer1; - break; - } - return reg_val; + return hw->det_channel_timers[src_chan][1].det_channel_timer; } #ifdef __cplusplus diff --git a/components/esp_hal_ana_cmpr/esp32p4/ana_cmpr_periph.c b/components/esp_hal_ana_cmpr/esp32p4/ana_cmpr_periph.c index a71385eaede..8945c3c1853 100644 --- a/components/esp_hal_ana_cmpr/esp32p4/ana_cmpr_periph.c +++ b/components/esp_hal_ana_cmpr/esp32p4/ana_cmpr_periph.c @@ -9,14 +9,14 @@ const ana_cmpr_periph_t ana_cmpr_periph[2] = { [0] = { - .src_gpio = ANA_CMPR0_SRC_GPIO, - .ext_ref_gpio = ANA_CMPR0_EXT_REF_GPIO, + .src_gpio = 52, + .ext_ref_gpio = 51, .intr_src = ETS_GPIO_PAD_COMP_INTR_SOURCE, .module_name = "ANA_CMPR_U0", }, [1] = { - .src_gpio = ANA_CMPR1_SRC_GPIO, - .ext_ref_gpio = ANA_CMPR1_EXT_REF_GPIO, + .src_gpio = 54, + .ext_ref_gpio = 53, .intr_src = ETS_GPIO_PAD_COMP_INTR_SOURCE, .module_name = "ANA_CMPR_U1", }, diff --git a/components/esp_hal_ana_cmpr/esp32p4/include/hal/ana_cmpr_ll.h b/components/esp_hal_ana_cmpr/esp32p4/include/hal/ana_cmpr_ll.h index 817e6c8efe1..5d8d2733c0f 100644 --- a/components/esp_hal_ana_cmpr/esp32p4/include/hal/ana_cmpr_ll.h +++ b/components/esp_hal_ana_cmpr/esp32p4/include/hal/ana_cmpr_ll.h @@ -33,7 +33,7 @@ extern "C" { #define ANALOG_CMPR_LL_SRC_CHANNEL_NUM 1 // Can detect positive/negative/any cross type -#define ANALOG_CMPR_LL_SUPPORT_EDGE_TYPE 1 +#define ANALOG_CMPR_LL_SUPPORT_EDGE_SPECIFIC_INTR_MASK 1 #define ANALOG_CMPR_LL_GET_HW(unit) (&ANALOG_CMPR[unit]) #define ANALOG_CMPR_LL_GET_UNIT(hw) ((hw) == (&ANALOG_CMPR[0]) ? 0 : 1) @@ -141,6 +141,7 @@ static inline void analog_cmpr_ll_set_cross_debounce_cycle(analog_cmpr_dev_t *hw * @param mask Interrupt mask * @param enable True to enable, False to disable */ +__attribute__((always_inline)) 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; diff --git a/components/esp_hal_ana_cmpr/esp32s31/ana_cmpr_periph.c b/components/esp_hal_ana_cmpr/esp32s31/ana_cmpr_periph.c index 92cd717822d..4a421d6a118 100644 --- a/components/esp_hal_ana_cmpr/esp32s31/ana_cmpr_periph.c +++ b/components/esp_hal_ana_cmpr/esp32s31/ana_cmpr_periph.c @@ -8,12 +8,7 @@ const ana_cmpr_periph_t ana_cmpr_periph[1] = { [0] = { - .pad_gpios = { - ANA_CMPR0_PAD0_GPIO, - ANA_CMPR0_PAD1_GPIO, - ANA_CMPR0_PAD2_GPIO, - ANA_CMPR0_PAD3_GPIO, - }, + .pad_gpios = {37, 38, 39, 40}, .intr_src = ETS_ZERO_DET_INTR_SOURCE, .module_name = "ANA_CMPR_U0", }, diff --git a/components/esp_hal_ana_cmpr/esp32s31/include/hal/ana_cmpr_ll.h b/components/esp_hal_ana_cmpr/esp32s31/include/hal/ana_cmpr_ll.h index 569a5de79b4..5a18581dbc7 100644 --- a/components/esp_hal_ana_cmpr/esp32s31/include/hal/ana_cmpr_ll.h +++ b/components/esp_hal_ana_cmpr/esp32s31/include/hal/ana_cmpr_ll.h @@ -7,10 +7,12 @@ #pragma once #include +#include #include "hal/misc.h" #include "hal/assert.h" #include "hal/ana_cmpr_types.h" #include "soc/zero_det_struct.h" +#include "soc/zero_det_reg.h" #include "soc/hp_sys_clkrst_struct.h" #include "soc/soc_etm_source.h" @@ -37,7 +39,10 @@ typedef zero_det_dev_t analog_cmpr_dev_t; #define ANALOG_CMPR_LL_SRC_CHANNEL_NUM 3 // Can detect positive/negative/any cross type -#define ANALOG_CMPR_LL_SUPPORT_EDGE_TYPE 1 +#define ANALOG_CMPR_LL_SUPPORT_EDGE_SPECIFIC_INTR_MASK 1 + +// Support software trigger channel scan +#define ANALOG_CMPR_LL_SUPPORT_SW_SCAN 1 #define ANALOG_CMPR_LL_GET_HW(unit) (&ZERO_DET) @@ -47,6 +52,10 @@ typedef zero_det_dev_t analog_cmpr_dev_t; #define ANALOG_CMPR_LL_ALL_INTR_MASK(unit) 0x1FF #define ANALOG_CMPR_LL_ETM_SOURCE(unit, src_chan, type) (ZERO_DET_EVT_CHANNEL_1_POS + (src_chan) + ((type) * ANALOG_CMPR_LL_SRC_CHANNEL_NUM)) +#define ANALOG_CMPR_LL_ETM_TASK(unit, type) (ZERO_DET_TASK_START) + +#define ANALOG_CMPR_LL_MAX_ETM_DELAY_CYCLES ZERO_DET_DELAY_EVENT_TIME +#define ANALOG_CMPR_LL_MAX_POLL_PERIOD_CYCLES ZERO_DET_COMP_POLL_PERIOD /** * @brief Enable the bus clock for Analog Comparator module @@ -89,20 +98,20 @@ static inline void analog_cmpr_ll_reset_core(int unit_id) */ static inline void analog_cmpr_ll_set_clk_src(int unit_id, ana_cmpr_clk_src_t clk_src) { - // switch (clk_src) { - // case ANA_CMPR_CLK_SRC_XTAL: - // HP_SYS_CLKRST.zero_det_ctrl0.reg_zero_det_clk_src_sel = 0; - // break; - // case ANA_CMPR_CLK_SRC_RC_FAST: - // HP_SYS_CLKRST.zero_det_ctrl0.reg_zero_det_clk_src_sel = 1; - // break; - // case ANA_CMPR_CLK_SRC_PLL_F80M: - // HP_SYS_CLKRST.zero_det_ctrl0.reg_zero_det_clk_src_sel = 2; - // break; - // default: - // HAL_ASSERT(false); - // break; - // } + switch (clk_src) { + case ANA_CMPR_CLK_SRC_XTAL: + HP_SYS_CLKRST.zero_det_ctrl0.reg_zero_det_clk_src_sel = 0; + break; + case ANA_CMPR_CLK_SRC_RC_FAST: + HP_SYS_CLKRST.zero_det_ctrl0.reg_zero_det_clk_src_sel = 1; + break; + case ANA_CMPR_CLK_SRC_PLL_F80M: + HP_SYS_CLKRST.zero_det_ctrl0.reg_zero_det_clk_src_sel = 2; + break; + default: + HAL_ASSERT(false); + break; + } } /** @@ -214,7 +223,8 @@ static inline void analog_cmpr_ll_set_ref_source(analog_cmpr_dev_t *hw, ana_cmpr __attribute__((always_inline)) static inline void analog_cmpr_ll_set_cross_debounce_cycle(analog_cmpr_dev_t *hw, uint32_t cycle) { - hw->filter_cnt.filter_cnt = cycle; + // the value must be greater than or equal to 1 + hw->filter_cnt.filter_cnt = MAX(cycle, 1); } /** @@ -224,6 +234,7 @@ static inline void analog_cmpr_ll_set_cross_debounce_cycle(analog_cmpr_dev_t *hw * @param mask Interrupt mask * @param enable True to enable, False to disable */ +__attribute__((always_inline)) 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; @@ -309,6 +320,7 @@ static inline void analog_cmpr_ll_set_src_pad(analog_cmpr_dev_t *hw, uint32_t sr * @param hw Analog comparator register base address * @param poll_mask Channel mask (bit0..bit2 => CH1..CH3) */ +__attribute__((always_inline)) static inline void analog_cmpr_ll_set_scan_mask(analog_cmpr_dev_t *hw, uint32_t poll_mask) { hw->conf.comp_poll_mask = poll_mask & 0x7; @@ -325,6 +337,17 @@ static inline void analog_cmpr_ll_set_scan_mode(analog_cmpr_dev_t *hw, ana_cmpr_ hw->conf.comp_poll_mode = scan_mode; } +/** + * @brief Start a scan to detect the cross event + * + * @param dev Analog comparator register base address + */ +__attribute__((always_inline)) +static inline void analog_cmpr_ll_start_scan(analog_cmpr_dev_t *dev) +{ + dev->start.comp_start = 1; +} + /** * @brief Set channel switch wait cycles * @@ -333,7 +356,8 @@ static inline void analog_cmpr_ll_set_scan_mode(analog_cmpr_dev_t *hw, ana_cmpr_ */ static inline void analog_cmpr_ll_set_poll_period(analog_cmpr_dev_t *hw, uint32_t period_cycles) { - hw->poll_period.comp_poll_period = period_cycles & 0xFFFF; + // the value must be greater than or equal to 1 + hw->poll_period.comp_poll_period = MAX(period_cycles, 1); } /** @@ -344,7 +368,7 @@ static inline void analog_cmpr_ll_set_poll_period(analog_cmpr_dev_t *hw, uint32_ * @param hw Analog comparator register base address * @param limit_cnt The resample limit count */ -static inline void analog_cmpr_ll_set_resample_limit(analog_cmpr_dev_t *hw, uint32_t limit_cnt) +static inline void analog_cmpr_ll_set_resample_limit(analog_cmpr_dev_t *hw, uint8_t limit_cnt) { hw->conf.limit_cnt = limit_cnt; } @@ -357,17 +381,18 @@ static inline void analog_cmpr_ll_set_resample_limit(analog_cmpr_dev_t *hw, uint */ static inline void analog_cmpr_ll_set_etm_delay_cycles(analog_cmpr_dev_t *hw, uint32_t delay_cycles) { - hw->delay_event_time.delay_event_time = delay_cycles & 0xFFFF; + // the value must be greater than or equal to 1 + hw->delay_event_time.delay_event_time = MAX(delay_cycles, 1); } /** - * @brief Enable per-channel delayed ETM event timer + * @brief Enable per-channel ETM event * * @param hw Analog comparator register base address * @param src_chan Source channel id (0..2) * @param enable true to enable, false to disable */ -static inline void analog_cmpr_ll_enable_channel_etm_delay(analog_cmpr_dev_t *hw, uint32_t src_chan, bool enable) +static inline void analog_cmpr_ll_enable_channel_etm(analog_cmpr_dev_t *hw, uint32_t src_chan, bool enable) { switch (src_chan) { case 0: @@ -399,6 +424,7 @@ static inline void analog_cmpr_ll_enable_capture_timer(analog_cmpr_dev_t *hw, bo * @param hw Analog comparator register base address * @param hys_level Hysteresis level enum */ +__attribute__((always_inline)) static inline void analog_cmpr_ll_set_ref_hys_level(analog_cmpr_dev_t *hw, ana_cmpr_ref_hys_t hys_level) { switch (hys_level) { @@ -411,9 +437,12 @@ static inline void analog_cmpr_ll_set_ref_hys_level(analog_cmpr_dev_t *hw, ana_c case ANA_CMPR_REF_HYS_LEVEL2: hw->pad_comp_cfg.pad_comp_hys = 2; break; - default: + case ANA_CMPR_REF_HYS_LEVEL3: hw->pad_comp_cfg.pad_comp_hys = 4; break; + default: + HAL_ASSERT(false); + break; } hw->pad_comp_cfg.pad_comp_hys_en = hys_level != ANA_CMPR_REF_HYS_LEVEL0; } @@ -446,19 +475,7 @@ static inline bool analog_cmpr_ll_get_compare_result(analog_cmpr_dev_t *hw, uint */ static inline uint32_t analog_cmpr_ll_get_current_capture_time(analog_cmpr_dev_t *hw, uint32_t src_chan) { - uint32_t reg_val = 0; - switch (src_chan) { - case 0: - reg_val = hw->channel_1_timer0.channel_1_timer0; - break; - case 1: - reg_val = hw->channel_2_timer0.channel_2_timer0; - break; - default: - reg_val = hw->channel_3_timer0.channel_3_timer0; - break; - } - return reg_val; + return hw->channel_timers[src_chan][0].channel_timer; } /** @@ -470,19 +487,7 @@ static inline uint32_t analog_cmpr_ll_get_current_capture_time(analog_cmpr_dev_t */ static inline uint32_t analog_cmpr_ll_get_previous_capture_time(analog_cmpr_dev_t *hw, uint32_t src_chan) { - uint32_t reg_val = 0; - switch (src_chan) { - case 0: - reg_val = hw->channel_1_timer1.channel_1_timer1; - break; - case 1: - reg_val = hw->channel_2_timer1.channel_2_timer1; - break; - default: - reg_val = hw->channel_3_timer1.channel_3_timer1; - break; - } - return reg_val; + return hw->channel_timers[src_chan][1].channel_timer; } #ifdef __cplusplus diff --git a/components/esp_hal_ana_cmpr/include/hal/ana_cmpr_periph.h b/components/esp_hal_ana_cmpr/include/hal/ana_cmpr_periph.h index f6bd5d3b420..096e9c59cba 100644 --- a/components/esp_hal_ana_cmpr/include/hal/ana_cmpr_periph.h +++ b/components/esp_hal_ana_cmpr/include/hal/ana_cmpr_periph.h @@ -10,7 +10,6 @@ #include "soc/soc_caps.h" #include "soc/interrupts.h" #if SOC_ANA_CMPR_SUPPORTED -#include "soc/ana_cmpr_pins.h" #include "hal/ana_cmpr_ll.h" #endif @@ -22,8 +21,8 @@ extern "C" { typedef struct { union { struct { - const int ext_ref_gpio; // External reference GPIO number const int src_gpio; // Source GPIO number + const int ext_ref_gpio; // External reference GPIO number }; const int pad_gpios[ANALOG_CMPR_LL_GET(PAD_NUM)]; // Array of GPIO numbers for the pads, indexed by pad number }; diff --git a/components/esp_hal_ana_cmpr/include/hal/ana_cmpr_types.h b/components/esp_hal_ana_cmpr/include/hal/ana_cmpr_types.h index 3c0136b9ddc..d79972acbe8 100644 --- a/components/esp_hal_ana_cmpr/include/hal/ana_cmpr_types.h +++ b/components/esp_hal_ana_cmpr/include/hal/ana_cmpr_types.h @@ -50,6 +50,7 @@ typedef enum { * * @note The exact hysteresis voltage is hardware-dependent. The level-to-voltage * mapping on ESP32-H4 is approximately: + * - LEVEL0: ~0V * - LEVEL1: ~0.04V * - LEVEL2: ~0.08V * - LEVEL3: ~0.12V @@ -75,8 +76,17 @@ typedef enum { typedef enum { 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_MAX, /*!< The max number of events, not a real event */ } ana_cmpr_event_type_t; +/** + * @brief Analog Comparator ETM Tasks for each unit + */ +typedef enum { + ANA_CMPR_TASK_START, /*!< Start the comparison */ + ANA_CMPR_TASK_MAX, /*!< The max number of tasks, not a real task */ +} ana_cmpr_task_type_t; + #if SOC_ANA_CMPR_SUPPORTED /** * @brief Analog comparator clock source diff --git a/components/soc/esp32c5/include/soc/Kconfig.soc_caps.in b/components/soc/esp32c5/include/soc/Kconfig.soc_caps.in index dc8f4a97526..fec46e0c5c3 100644 --- a/components/soc/esp32c5/include/soc/Kconfig.soc_caps.in +++ b/components/soc/esp32c5/include/soc/Kconfig.soc_caps.in @@ -647,6 +647,10 @@ config SOC_ANA_CMPR_SUPPORT_ETM bool default y +config SOC_ANA_CMPR_SUPPORT_AUTO_SCAN + bool + default y + config SOC_I2C_NUM int default 2 diff --git a/components/soc/esp32c5/include/soc/soc_caps.h b/components/soc/esp32c5/include/soc/soc_caps.h index c15d7a1bed0..2ff7c745af9 100644 --- a/components/soc/esp32c5/include/soc/soc_caps.h +++ b/components/soc/esp32c5/include/soc/soc_caps.h @@ -268,6 +268,7 @@ /*------------------------- Analog Comparator CAPS ---------------------------*/ #define SOC_ANA_CMPR_SUPPORT_ETM (1) +#define SOC_ANA_CMPR_SUPPORT_AUTO_SCAN (1) /*-------------------------- I2C CAPS ----------------------------------------*/ #define SOC_I2C_NUM (2U) diff --git a/components/soc/esp32c61/include/soc/Kconfig.soc_caps.in b/components/soc/esp32c61/include/soc/Kconfig.soc_caps.in index 1481f3204e0..a93669926d0 100644 --- a/components/soc/esp32c61/include/soc/Kconfig.soc_caps.in +++ b/components/soc/esp32c61/include/soc/Kconfig.soc_caps.in @@ -507,6 +507,10 @@ config SOC_ANA_CMPR_SUPPORT_ETM bool default y +config SOC_ANA_CMPR_SUPPORT_AUTO_SCAN + bool + default y + config SOC_I2C_NUM int default 1 diff --git a/components/soc/esp32c61/include/soc/soc_caps.h b/components/soc/esp32c61/include/soc/soc_caps.h index cc7c7408de9..18a38f43d9a 100644 --- a/components/soc/esp32c61/include/soc/soc_caps.h +++ b/components/soc/esp32c61/include/soc/soc_caps.h @@ -217,6 +217,7 @@ /*------------------------- Analog Comparator CAPS ---------------------------*/ #define SOC_ANA_CMPR_SUPPORT_ETM (1) +#define SOC_ANA_CMPR_SUPPORT_AUTO_SCAN (1) /*-------------------------- I2C CAPS ----------------------------------------*/ #define SOC_I2C_NUM (1U) diff --git a/components/soc/esp32h2/include/soc/Kconfig.soc_caps.in b/components/soc/esp32h2/include/soc/Kconfig.soc_caps.in index b271bf933c2..51b7d0f2f8a 100644 --- a/components/soc/esp32h2/include/soc/Kconfig.soc_caps.in +++ b/components/soc/esp32h2/include/soc/Kconfig.soc_caps.in @@ -387,6 +387,10 @@ config SOC_APB_BACKUP_DMA bool default n +config SOC_ANA_CMPR_SUPPORT_AUTO_SCAN + bool + default y + config SOC_BROWNOUT_RESET_SUPPORTED bool default y diff --git a/components/soc/esp32h2/include/soc/soc_caps.h b/components/soc/esp32h2/include/soc/soc_caps.h index adcdb5f21bd..e25952bae16 100644 --- a/components/soc/esp32h2/include/soc/soc_caps.h +++ b/components/soc/esp32h2/include/soc/soc_caps.h @@ -163,6 +163,9 @@ /*-------------------------- APB BACKUP DMA CAPS -------------------------------*/ #define SOC_APB_BACKUP_DMA (0) +/*-------------------------- ANALOG COMPARATOR CAPS -------------------------------*/ +#define SOC_ANA_CMPR_SUPPORT_AUTO_SCAN (1) + /*-------------------------- BROWNOUT CAPS -----------------------------------*/ #define SOC_BROWNOUT_RESET_SUPPORTED 1 diff --git a/components/soc/esp32h21/include/soc/Kconfig.soc_caps.in b/components/soc/esp32h21/include/soc/Kconfig.soc_caps.in index bcc87f73d61..07d9d315d46 100644 --- a/components/soc/esp32h21/include/soc/Kconfig.soc_caps.in +++ b/components/soc/esp32h21/include/soc/Kconfig.soc_caps.in @@ -299,6 +299,10 @@ config SOC_APB_BACKUP_DMA bool default n +config SOC_ANA_CMPR_SUPPORT_AUTO_SCAN + bool + default y + config SOC_BROWNOUT_RESET_SUPPORTED bool default y diff --git a/components/soc/esp32h21/include/soc/soc_caps.h b/components/soc/esp32h21/include/soc/soc_caps.h index 25cac97c596..fef453cb421 100644 --- a/components/soc/esp32h21/include/soc/soc_caps.h +++ b/components/soc/esp32h21/include/soc/soc_caps.h @@ -146,6 +146,9 @@ /*-------------------------- APB BACKUP DMA CAPS -------------------------------*/ #define SOC_APB_BACKUP_DMA (0) +/*-------------------------- Analog Comparator CAPS -------------------------------*/ +#define SOC_ANA_CMPR_SUPPORT_AUTO_SCAN (1) + /*-------------------------- BROWNOUT CAPS -----------------------------------*/ #define SOC_BROWNOUT_RESET_SUPPORTED 1 diff --git a/components/soc/esp32h4/include/soc/clk_tree_defs.h b/components/soc/esp32h4/include/soc/clk_tree_defs.h index f406b1995cb..d9127df3ff0 100644 --- a/components/soc/esp32h4/include/soc/clk_tree_defs.h +++ b/components/soc/esp32h4/include/soc/clk_tree_defs.h @@ -490,6 +490,23 @@ typedef enum { GLITCH_FILTER_CLK_SRC_DEFAULT = SOC_MOD_CLK_PLL_F48M, /*!< Select PLL_F48M clock as the default clock choice */ } soc_periph_glitch_filter_clk_src_t; +////////////////////////////////////////////////ANA_CMPR//////////////////////////////////////////////////// + +/** + * @brief Array initializer for all supported clock sources of Analog Comparator + */ +#define SOC_ANA_CMPR_CLKS {SOC_MOD_CLK_XTAL, SOC_MOD_CLK_RC_FAST, SOC_MOD_CLK_PLL_F48M} + +/** + * @brief Type of Analog Comparator clock source + */ +typedef enum { + ANA_CMPR_CLK_SRC_XTAL = SOC_MOD_CLK_XTAL, /*!< Select XTAL clock as the source clock */ + ANA_CMPR_CLK_SRC_RC_FAST = SOC_MOD_CLK_RC_FAST, /*!< Select RC_FAST as the source clock */ + ANA_CMPR_CLK_SRC_PLL_F48M = SOC_MOD_CLK_PLL_F48M, /*!< Select PLL_F48M as the source clock */ + ANA_CMPR_CLK_SRC_DEFAULT = SOC_MOD_CLK_PLL_F48M, /*!< Select PLL_F48M as the default clock choice */ +} soc_periph_ana_cmpr_clk_src_t; + //////////////////////////////////////////////////TWAI////////////////////////////////////////////////////////////////// /** diff --git a/components/soc/esp32h4/include/soc/soc_caps.h b/components/soc/esp32h4/include/soc/soc_caps.h index a7dcdd11ba7..c8ccd0431a4 100644 --- a/components/soc/esp32h4/include/soc/soc_caps.h +++ b/components/soc/esp32h4/include/soc/soc_caps.h @@ -32,7 +32,7 @@ /*-------------------------- COMMON CAPS ---------------------------------------*/ #define SOC_ADC_SUPPORTED 1 -// #define SOC_ANA_CMPR_SUPPORTED 1 // TODO: [ESP32H4] IDF-12395 big change!! +// #define SOC_ANA_CMPR_SUPPORTED 1 #define SOC_DEDICATED_GPIO_SUPPORTED 1 #define SOC_UART_SUPPORTED 1 #define SOC_UHCI_SUPPORTED 1 @@ -152,6 +152,10 @@ /*-------------------------- APB BACKUP DMA CAPS -------------------------------*/ #define SOC_APB_BACKUP_DMA (0) +/*------------------------- Analog Comparator CAPS ---------------------------*/ +// #define SOC_ANA_CMPR_SUPPORT_ETM (1) +// #define SOC_ANA_CMPR_SUPPORT_ETM_SCAN (1) + /*-------------------------- BROWNOUT CAPS -----------------------------------*/ // #define SOC_BROWNOUT_RESET_SUPPORTED 1 diff --git a/components/soc/esp32h4/register/soc/zero_det_struct.h b/components/soc/esp32h4/register/soc/zero_det_struct.h index 3e8ce1a0cc2..faa13100b7c 100644 --- a/components/soc/esp32h4/register/soc/zero_det_struct.h +++ b/components/soc/esp32h4/register/soc/zero_det_struct.h @@ -1,5 +1,5 @@ /** - * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2025-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 OR MIT */ @@ -341,83 +341,18 @@ typedef union { uint32_t val; } zero_det_int_st_reg_t; -/** Type of det_channel_1_timer0 register +/** Type of det_channel_timer register * record timer reg */ typedef union { struct { - /** det_channel_1_timer0 : RO; bitpos: [31:0]; default: 0; + /** det_channel_timer : RO; bitpos: [31:0]; default: 0; * record the time while detect the first zero det int in channel 1 */ - uint32_t det_channel_1_timer0:32; + uint32_t det_channel_timer:32; }; uint32_t val; -} zero_det_channel_1_timer0_reg_t; - -/** Type of det_channel_1_timer1 register - * record timer reg - */ -typedef union { - struct { - /** det_channel_1_timer1 : RO; bitpos: [31:0]; default: 0; - * record the time while detect the second zero det int in channel 1 - */ - uint32_t det_channel_1_timer1:32; - }; - uint32_t val; -} zero_det_channel_1_timer1_reg_t; - -/** Type of det_channel_2_timer0 register - * record timer reg - */ -typedef union { - struct { - /** det_channel_2_timer0 : RO; bitpos: [31:0]; default: 0; - * record the time while detect the first zero det int in channel 2 - */ - uint32_t det_channel_2_timer0:32; - }; - uint32_t val; -} zero_det_channel_2_timer0_reg_t; - -/** Type of det_channel_2_timer1 register - * record timer reg - */ -typedef union { - struct { - /** det_channel_2_timer1 : RO; bitpos: [31:0]; default: 0; - * record the time while detect the second zero det int in channel 2 - */ - uint32_t det_channel_2_timer1:32; - }; - uint32_t val; -} zero_det_channel_2_timer1_reg_t; - -/** Type of det_channel_3_timer0 register - * record timer reg - */ -typedef union { - struct { - /** det_channel_3_timer0 : RO; bitpos: [31:0]; default: 0; - * record the time while detect the first zero det int in channel 3 - */ - uint32_t det_channel_3_timer0:32; - }; - uint32_t val; -} zero_det_channel_3_timer0_reg_t; - -/** Type of det_channel_3_timer1 register - * record timer reg - */ -typedef union { - struct { - /** det_channel_3_timer1 : RO; bitpos: [31:0]; default: 0; - * record the time while detect the second zero det int in channel 3 - */ - uint32_t det_channel_3_timer1:32; - }; - uint32_t val; -} zero_det_channel_3_timer1_reg_t; +} zero_det_channel_timer_reg_t; /** Type of det_channel_status register * pad comp status reg @@ -504,21 +439,16 @@ typedef struct { volatile zero_det_int_raw_reg_t det_int_raw; volatile zero_det_int_clr_reg_t det_int_clr; volatile zero_det_int_st_reg_t det_int_st; - volatile zero_det_channel_1_timer0_reg_t det_channel_1_timer0; - volatile zero_det_channel_1_timer1_reg_t det_channel_1_timer1; - volatile zero_det_channel_2_timer0_reg_t det_channel_2_timer0; - volatile zero_det_channel_2_timer1_reg_t det_channel_2_timer1; - volatile zero_det_channel_3_timer0_reg_t det_channel_3_timer0; - volatile zero_det_channel_3_timer1_reg_t det_channel_3_timer1; + volatile zero_det_channel_timer_reg_t det_channel_timers[3][2]; // [channel], [timer_id (0: current, 1: previous)] volatile zero_det_channel_status_reg_t det_channel_status; volatile zero_det_pad_comp_cfg_reg_t det_pad_comp_cfg; volatile zero_det_date_reg_t det_date; -} zero_dev_t; +} zero_det_dev_t; -extern zero_dev_t ZERO_DET; +extern zero_det_dev_t ZERO_DET; #ifndef __cplusplus -_Static_assert(sizeof(zero_dev_t) == 0x44, "Invalid size of zero_dev_t structure"); +_Static_assert(sizeof(zero_det_dev_t) == 0x44, "Invalid size of zero_det_dev_t structure"); #endif #ifdef __cplusplus diff --git a/components/soc/esp32p4/include/soc/Kconfig.soc_caps.in b/components/soc/esp32p4/include/soc/Kconfig.soc_caps.in index 3939a0ac0ab..22f0913e4eb 100644 --- a/components/soc/esp32p4/include/soc/Kconfig.soc_caps.in +++ b/components/soc/esp32p4/include/soc/Kconfig.soc_caps.in @@ -779,6 +779,10 @@ config SOC_ANA_CMPR_SUPPORT_ETM bool default y +config SOC_ANA_CMPR_SUPPORT_AUTO_SCAN + bool + default y + config SOC_I2C_NUM int default 3 diff --git a/components/soc/esp32p4/include/soc/soc_caps.h b/components/soc/esp32p4/include/soc/soc_caps.h index 29216cd4006..72976610f36 100644 --- a/components/soc/esp32p4/include/soc/soc_caps.h +++ b/components/soc/esp32p4/include/soc/soc_caps.h @@ -305,6 +305,7 @@ /*------------------------- Analog Comparator CAPS ---------------------------*/ #define SOC_ANA_CMPR_SUPPORT_ETM (1) +#define SOC_ANA_CMPR_SUPPORT_AUTO_SCAN (1) /*-------------------------- I2C CAPS ----------------------------------------*/ #define SOC_I2C_NUM (3U) diff --git a/components/soc/esp32s31/include/soc/Kconfig.soc_caps.in b/components/soc/esp32s31/include/soc/Kconfig.soc_caps.in index f585d64f716..b6c8a87b3c2 100644 --- a/components/soc/esp32s31/include/soc/Kconfig.soc_caps.in +++ b/components/soc/esp32s31/include/soc/Kconfig.soc_caps.in @@ -3,6 +3,10 @@ # using gen_soc_caps_kconfig.py, do not edit manually ##################################################### +config SOC_ANA_CMPR_SUPPORTED + bool + default y + config SOC_DEDICATED_GPIO_SUPPORTED bool default y @@ -251,6 +255,14 @@ config SOC_JPEG_CODEC_SUPPORTED bool default y +config SOC_ANA_CMPR_SUPPORT_ETM + bool + default y + +config SOC_ANA_CMPR_SUPPORT_ETM_SCAN + bool + default y + config SOC_USB_OTG_PERIPH_NUM int default 1 diff --git a/components/soc/esp32s31/include/soc/clk_tree_defs.h b/components/soc/esp32s31/include/soc/clk_tree_defs.h index 570cac1aa38..32258c171a2 100644 --- a/components/soc/esp32s31/include/soc/clk_tree_defs.h +++ b/components/soc/esp32s31/include/soc/clk_tree_defs.h @@ -414,6 +414,23 @@ typedef enum { GLITCH_FILTER_CLK_SRC_DEFAULT = SOC_MOD_CLK_PLL_F80M, /*!< Select PLL_F80M clock as the default clock choice */ } soc_periph_glitch_filter_clk_src_t; +////////////////////////////////////////////////ANA_CMPR//////////////////////////////////////////////////// + +/** + * @brief Array initializer for all supported clock sources of Analog Comparator + */ +#define SOC_ANA_CMPR_CLKS {SOC_MOD_CLK_XTAL, SOC_MOD_CLK_RC_FAST, SOC_MOD_CLK_PLL_F80M} + +/** + * @brief Type of Analog Comparator clock source + */ +typedef enum { + ANA_CMPR_CLK_SRC_XTAL = SOC_MOD_CLK_XTAL, /*!< Select XTAL clock as the source clock */ + ANA_CMPR_CLK_SRC_RC_FAST = SOC_MOD_CLK_RC_FAST, /*!< Select RC_FAST as the source clock */ + ANA_CMPR_CLK_SRC_PLL_F80M = SOC_MOD_CLK_PLL_F80M, /*!< Select PLL_F80M as the source clock */ + ANA_CMPR_CLK_SRC_DEFAULT = SOC_MOD_CLK_PLL_F80M, /*!< Select PLL_F80M as the default clock choice */ +} soc_periph_ana_cmpr_clk_src_t; + //////////////////////////////////////////////////TWAI////////////////////////////////////////////////////////////////// /** diff --git a/components/soc/esp32s31/include/soc/soc_caps.h b/components/soc/esp32s31/include/soc/soc_caps.h index 10afb1e738e..5ac052ed21b 100644 --- a/components/soc/esp32s31/include/soc/soc_caps.h +++ b/components/soc/esp32s31/include/soc/soc_caps.h @@ -24,7 +24,7 @@ /*-------------------------- COMMON CAPS ---------------------------------------*/ // #define SOC_ADC_SUPPORTED 1 // TODO: [ESP32S31] IDF-14741 -// #define SOC_ANA_CMPR_SUPPORTED 1 // TODO: [ESP32S31] IDF-14787 +#define SOC_ANA_CMPR_SUPPORTED 1 #define SOC_DEDICATED_GPIO_SUPPORTED 1 #define SOC_UART_SUPPORTED 1 #define SOC_GDMA_SUPPORTED 1 @@ -107,6 +107,10 @@ #define SOC_REGI2C_SUPPORTED 1 #define SOC_JPEG_CODEC_SUPPORTED 1 +/*------------------------- Analog Comparator CAPS ---------------------------*/ +#define SOC_ANA_CMPR_SUPPORT_ETM (1) +#define SOC_ANA_CMPR_SUPPORT_ETM_SCAN (1) + /*-------------------------- USB CAPS ----------------------------------------*/ #define SOC_USB_OTG_PERIPH_NUM (1U) #define SOC_USB_FSLS_PHY_NUM (0U) diff --git a/components/soc/esp32s31/include/soc/soc_etm_source.h b/components/soc/esp32s31/include/soc/soc_etm_source.h index be523ab3f7d..bbb51418d3d 100644 --- a/components/soc/esp32s31/include/soc/soc_etm_source.h +++ b/components/soc/esp32s31/include/soc/soc_etm_source.h @@ -397,12 +397,12 @@ #define MODEM_EVT_G1 389 #define MODEM_EVT_G2 390 #define MODEM_EVT_G3 391 -#define ZERO_DET_EVT_DELAY_CHANNEL_1_POS 392 -#define ZERO_DET_EVT_DELAY_CHANNEL_2_POS 393 -#define ZERO_DET_EVT_DELAY_CHANNEL_3_POS 394 -#define ZERO_DET_EVT_DELAY_CHANNEL_1_NEG 395 -#define ZERO_DET_EVT_DELAY_CHANNEL_2_NEG 396 -#define ZERO_DET_EVT_DELAY_CHANNEL_3_NEG 397 +#define ZERO_DET_EVT_CHANNEL_1_POS 392 +#define ZERO_DET_EVT_CHANNEL_2_POS 393 +#define ZERO_DET_EVT_CHANNEL_3_POS 394 +#define ZERO_DET_EVT_CHANNEL_1_NEG 395 +#define ZERO_DET_EVT_CHANNEL_2_NEG 396 +#define ZERO_DET_EVT_CHANNEL_3_NEG 397 #define CORDIC_EVT_RES_RDY 398 #define GPIO_TASK_CH0_SET 1 #define GPIO_TASK_CH1_SET 2 diff --git a/components/soc/esp32s31/register/soc/zero_det_struct.h b/components/soc/esp32s31/register/soc/zero_det_struct.h index 788a349aeb5..c71897deeec 100644 --- a/components/soc/esp32s31/register/soc/zero_det_struct.h +++ b/components/soc/esp32s31/register/soc/zero_det_struct.h @@ -1,5 +1,5 @@ /** - * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2025-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 OR MIT */ @@ -342,83 +342,18 @@ typedef union { uint32_t val; } zero_det_int_st_reg_t; -/** Type of channel_1_timer0 register +/** Type of channel_timer register * record timer reg */ typedef union { struct { - /** channel_1_timer0 : RO; bitpos: [31:0]; default: 0; + /** channel_timer : RO; bitpos: [31:0]; default: 0; * record the time while detect the first zero det int in channel 1 */ - uint32_t channel_1_timer0:32; + uint32_t channel_timer:32; }; uint32_t val; -} zero_det_channel_1_timer0_reg_t; - -/** Type of channel_1_timer1 register - * record timer reg - */ -typedef union { - struct { - /** channel_1_timer1 : RO; bitpos: [31:0]; default: 0; - * record the time while detect the second zero det int in channel 1 - */ - uint32_t channel_1_timer1:32; - }; - uint32_t val; -} zero_det_channel_1_timer1_reg_t; - -/** Type of channel_2_timer0 register - * record timer reg - */ -typedef union { - struct { - /** channel_2_timer0 : RO; bitpos: [31:0]; default: 0; - * record the time while detect the first zero det int in channel 2 - */ - uint32_t channel_2_timer0:32; - }; - uint32_t val; -} zero_det_channel_2_timer0_reg_t; - -/** Type of channel_2_timer1 register - * record timer reg - */ -typedef union { - struct { - /** channel_2_timer1 : RO; bitpos: [31:0]; default: 0; - * record the time while detect the second zero det int in channel 2 - */ - uint32_t channel_2_timer1:32; - }; - uint32_t val; -} zero_det_channel_2_timer1_reg_t; - -/** Type of channel_3_timer0 register - * record timer reg - */ -typedef union { - struct { - /** channel_3_timer0 : RO; bitpos: [31:0]; default: 0; - * record the time while detect the first zero det int in channel 3 - */ - uint32_t channel_3_timer0:32; - }; - uint32_t val; -} zero_det_channel_3_timer0_reg_t; - -/** Type of channel_3_timer1 register - * record timer reg - */ -typedef union { - struct { - /** channel_3_timer1 : RO; bitpos: [31:0]; default: 0; - * record the time while detect the second zero det int in channel 3 - */ - uint32_t channel_3_timer1:32; - }; - uint32_t val; -} zero_det_channel_3_timer1_reg_t; +} zero_det_channel_timer_reg_t; /** Type of channel_status register * pad comp status reg @@ -519,12 +454,7 @@ typedef struct { volatile zero_det_int_raw_reg_t int_raw; volatile zero_det_int_clr_reg_t int_clr; volatile zero_det_int_st_reg_t int_st; - volatile zero_det_channel_1_timer0_reg_t channel_1_timer0; - volatile zero_det_channel_1_timer1_reg_t channel_1_timer1; - volatile zero_det_channel_2_timer0_reg_t channel_2_timer0; - volatile zero_det_channel_2_timer1_reg_t channel_2_timer1; - volatile zero_det_channel_3_timer0_reg_t channel_3_timer0; - volatile zero_det_channel_3_timer1_reg_t channel_3_timer1; + volatile zero_det_channel_timer_reg_t channel_timers[3][2]; // [channel], [timer_id (0: current, 1: previous)] volatile zero_det_channel_status_reg_t channel_status; volatile zero_det_pad_comp_cfg_reg_t pad_comp_cfg; volatile zero_det_start_reg_t start; @@ -532,6 +462,7 @@ typedef struct { volatile zero_det_date_reg_t date; } zero_det_dev_t; +extern zero_det_dev_t ZERO_DET; #ifndef __cplusplus _Static_assert(sizeof(zero_det_dev_t) == 0x400, "Invalid size of zero_det_dev_t structure"); diff --git a/examples/peripherals/.build-test-rules.yml b/examples/peripherals/.build-test-rules.yml index 2f1573ef980..4a1dcc7d990 100644 --- a/examples/peripherals/.build-test-rules.yml +++ b/examples/peripherals/.build-test-rules.yml @@ -28,9 +28,9 @@ examples/peripherals/adc/oneshot_read: reason: no runners for esp32h4 ADC test <<: *adc_dependencies -examples/peripherals/analog_comparator: +examples/peripherals/analog_comparator/auto_scan: disable: - - if: SOC_ANA_CMPR_SUPPORTED != 1 + - if: SOC_ANA_CMPR_SUPPORT_AUTO_SCAN != 1 depends_components: - esp_driver_gpio - esp_driver_ana_cmpr diff --git a/examples/peripherals/analog_comparator/CMakeLists.txt b/examples/peripherals/analog_comparator/auto_scan/CMakeLists.txt similarity index 93% rename from examples/peripherals/analog_comparator/CMakeLists.txt rename to examples/peripherals/analog_comparator/auto_scan/CMakeLists.txt index d4dc7094623..83262a707cf 100644 --- a/examples/peripherals/analog_comparator/CMakeLists.txt +++ b/examples/peripherals/analog_comparator/auto_scan/CMakeLists.txt @@ -8,4 +8,4 @@ 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(analog_comparator_example) +project(ana_cmpr_auto_scan) diff --git a/examples/peripherals/analog_comparator/README.md b/examples/peripherals/analog_comparator/auto_scan/README.md similarity index 81% rename from examples/peripherals/analog_comparator/README.md rename to examples/peripherals/analog_comparator/auto_scan/README.md index 946bab10756..ec6ddfa54e4 100644 --- a/examples/peripherals/analog_comparator/README.md +++ b/examples/peripherals/analog_comparator/auto_scan/README.md @@ -1,17 +1,17 @@ | Supported Targets | ESP32-C5 | ESP32-C61 | ESP32-H2 | ESP32-H21 | ESP32-P4 | | ----------------- | -------- | --------- | -------- | --------- | -------- | -# Analog Comparator Example +# Analog Comparator Auto Scan 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. The example toggles a GPIO to monitor the positive and negative crosses on the analog comparator unit. +This example shows how to use the Analog Comparator auto scan mode. After enabling the comparator unit, hardware keeps scanning source channels automatically and updates comparator outputs continuously. The example toggles a GPIO to monitor positive and negative crossing results in real time. ## 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 supports generating analog comparator events (like ESP32-P4), the example toggles the monitoring GPIO via Event Task Matrix (ETM). ETM binds comparator cross events (positive and negative) to GPIO tasks (set/clear), so each auto-scan crossing result can drive hardware actions without CPU involvement. -- 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. +- If the target does not support generating analog comparator events (like ESP32-H2), the example registers an interrupt callback and toggles the GPIO in the callback. This still relies on auto scan for continuous output updates, but every crossing ISR must be handled by CPU, so it is less efficient than ETM. ## How to Use Example diff --git a/examples/peripherals/analog_comparator/ext_ref.png b/examples/peripherals/analog_comparator/auto_scan/ext_ref.png similarity index 100% rename from examples/peripherals/analog_comparator/ext_ref.png rename to examples/peripherals/analog_comparator/auto_scan/ext_ref.png diff --git a/examples/peripherals/analog_comparator/hysteresis_ref.png b/examples/peripherals/analog_comparator/auto_scan/hysteresis_ref.png similarity index 100% rename from examples/peripherals/analog_comparator/hysteresis_ref.png rename to examples/peripherals/analog_comparator/auto_scan/hysteresis_ref.png diff --git a/examples/peripherals/analog_comparator/main/CMakeLists.txt b/examples/peripherals/analog_comparator/auto_scan/main/CMakeLists.txt similarity index 100% rename from examples/peripherals/analog_comparator/main/CMakeLists.txt rename to examples/peripherals/analog_comparator/auto_scan/main/CMakeLists.txt diff --git a/examples/peripherals/analog_comparator/main/Kconfig.projbuild b/examples/peripherals/analog_comparator/auto_scan/main/Kconfig.projbuild similarity index 100% rename from examples/peripherals/analog_comparator/main/Kconfig.projbuild rename to examples/peripherals/analog_comparator/auto_scan/main/Kconfig.projbuild diff --git a/examples/peripherals/analog_comparator/main/ana_cmpr_example_etm.c b/examples/peripherals/analog_comparator/auto_scan/main/ana_cmpr_example_etm.c similarity index 94% rename from examples/peripherals/analog_comparator/main/ana_cmpr_example_etm.c rename to examples/peripherals/analog_comparator/auto_scan/main/ana_cmpr_example_etm.c index 67a77f2dbb3..f9c06e06bb4 100644 --- a/examples/peripherals/analog_comparator/main/ana_cmpr_example_etm.c +++ b/examples/peripherals/analog_comparator/auto_scan/main/ana_cmpr_example_etm.c @@ -52,8 +52,7 @@ static void example_etm_bind_ana_cmpr_event_with_gpio_task(ana_cmpr_handle_t cmp static void example_init_analog_comparator_etm(void) { ana_cmpr_handle_t cmpr = NULL; - int src_gpio = -1; - ESP_ERROR_CHECK(ana_cmpr_get_gpio(EXAMPLE_ANA_CMPR_UNIT, ANA_CMPR_SOURCE_CHAN, &src_gpio)); + gpio_num_t src_gpio = -1; #if CONFIG_EXAMPLE_REF_FROM_INTERNAL /* Step 1: Allocate the new analog comparator unit */ @@ -64,6 +63,7 @@ static void example_init_analog_comparator_etm(void) .cross_type = ANA_CMPR_CROSS_ANY, }; 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)); /* 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 = { @@ -81,8 +81,9 @@ static void example_init_analog_comparator_etm(void) .cross_type = ANA_CMPR_CROSS_ANY, }; ESP_ERROR_CHECK(ana_cmpr_new_unit(&config, &cmpr)); - int ext_ref_gpio = -1; - ESP_ERROR_CHECK(ana_cmpr_get_gpio(EXAMPLE_ANA_CMPR_UNIT, ANA_CMPR_EXT_REF_CHAN, &ext_ref_gpio)); + ESP_ERROR_CHECK(ana_cmpr_get_channel_gpio(cmpr, ANA_CMPR_SOURCE_CHAN, 0, &src_gpio)); + gpio_num_t ext_ref_gpio = -1; + ESP_ERROR_CHECK(ana_cmpr_get_channel_gpio(cmpr, ANA_CMPR_EXT_REF_CHAN, 0, &ext_ref_gpio)); ESP_LOGI(TAG, "Allocate Analog Comparator with external reference from GPIO %d, source from GPIO %d", ext_ref_gpio, src_gpio); #endif // CONFIG_EXAMPLE_REF_FROM_INTERNAL diff --git a/examples/peripherals/analog_comparator/main/ana_cmpr_example_intr.c b/examples/peripherals/analog_comparator/auto_scan/main/ana_cmpr_example_intr.c similarity index 93% rename from examples/peripherals/analog_comparator/main/ana_cmpr_example_intr.c rename to examples/peripherals/analog_comparator/auto_scan/main/ana_cmpr_example_intr.c index edad6b333c1..4df76e1f998 100644 --- a/examples/peripherals/analog_comparator/main/ana_cmpr_example_intr.c +++ b/examples/peripherals/analog_comparator/auto_scan/main/ana_cmpr_example_intr.c @@ -35,8 +35,7 @@ static bool example_ana_cmpr_on_cross_callback(ana_cmpr_handle_t cmpr, static void example_init_analog_comparator_intr(void) { ana_cmpr_handle_t cmpr = NULL; - int src_gpio = -1; - ESP_ERROR_CHECK(ana_cmpr_get_gpio(EXAMPLE_ANA_CMPR_UNIT, ANA_CMPR_SOURCE_CHAN, &src_gpio)); + gpio_num_t src_gpio = -1; #if CONFIG_EXAMPLE_REF_FROM_INTERNAL /* Step 1: Allocate the new analog comparator unit */ @@ -47,6 +46,7 @@ static void example_init_analog_comparator_intr(void) .cross_type = ANA_CMPR_CROSS_ANY, }; 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)); /* 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 = { @@ -69,8 +69,9 @@ static void example_init_analog_comparator_intr(void) .cross_type = ANA_CMPR_CROSS_ANY, }; ESP_ERROR_CHECK(ana_cmpr_new_unit(&config, &cmpr)); - int ext_ref_gpio = -1; - ESP_ERROR_CHECK(ana_cmpr_get_gpio(EXAMPLE_ANA_CMPR_UNIT, ANA_CMPR_EXT_REF_CHAN, &ext_ref_gpio)); + ESP_ERROR_CHECK(ana_cmpr_get_channel_gpio(cmpr, ANA_CMPR_SOURCE_CHAN, 0, &src_gpio)); + gpio_num_t ext_ref_gpio = -1; + ESP_ERROR_CHECK(ana_cmpr_get_channel_gpio(cmpr, ANA_CMPR_EXT_REF_CHAN, 0, &ext_ref_gpio)); ESP_LOGI(TAG, "Allocate Analog Comparator with external reference from GPIO %d, source from GPIO %d", ext_ref_gpio, src_gpio); #endif // CONFIG_EXAMPLE_REF_FROM_INTERNAL diff --git a/examples/peripherals/analog_comparator/main/ana_cmpr_example_main.c b/examples/peripherals/analog_comparator/auto_scan/main/ana_cmpr_example_main.c similarity index 90% rename from examples/peripherals/analog_comparator/main/ana_cmpr_example_main.c rename to examples/peripherals/analog_comparator/auto_scan/main/ana_cmpr_example_main.c index 44a12946fae..80992b460e0 100644 --- a/examples/peripherals/analog_comparator/main/ana_cmpr_example_main.c +++ b/examples/peripherals/analog_comparator/auto_scan/main/ana_cmpr_example_main.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2023-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Unlicense OR CC0-1.0 */ diff --git a/examples/peripherals/analog_comparator/main/ana_cmpr_example_main.h b/examples/peripherals/analog_comparator/auto_scan/main/ana_cmpr_example_main.h similarity index 96% rename from examples/peripherals/analog_comparator/main/ana_cmpr_example_main.h rename to examples/peripherals/analog_comparator/auto_scan/main/ana_cmpr_example_main.h index ad2c3b685d6..ee80eef880b 100644 --- a/examples/peripherals/analog_comparator/main/ana_cmpr_example_main.h +++ b/examples/peripherals/analog_comparator/auto_scan/main/ana_cmpr_example_main.h @@ -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: Unlicense OR CC0-1.0 */ diff --git a/examples/peripherals/analog_comparator/pytest_ana_cmpr_example.py b/examples/peripherals/analog_comparator/auto_scan/pytest_ana_cmpr_auto_scan.py similarity index 68% rename from examples/peripherals/analog_comparator/pytest_ana_cmpr_example.py rename to examples/peripherals/analog_comparator/auto_scan/pytest_ana_cmpr_auto_scan.py index cb19a4fc705..4855b4cccf5 100644 --- a/examples/peripherals/analog_comparator/pytest_ana_cmpr_example.py +++ b/examples/peripherals/analog_comparator/auto_scan/pytest_ana_cmpr_auto_scan.py @@ -1,8 +1,9 @@ -# SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD +# SPDX-FileCopyrightText: 2023-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 @@ -14,8 +15,12 @@ from pytest_embedded_idf.utils import idf_parametrize ], indirect=True, ) -@idf_parametrize('target', ['esp32h2', 'esp32p4', 'esp32c5', 'esp32c61'], indirect=['target']) -def test_ana_cmpr_example(dut: Dut) -> None: +@idf_parametrize( + 'target', + soc_filtered_targets('SOC_ANA_CMPR_SUPPORT_AUTO_SCAN == 1'), + indirect=['target'], +) +def test_ana_cmpr_auto_scan(dut: Dut) -> None: sdkconfig = dut.app.sdkconfig if sdkconfig['EXAMPLE_REF_FROM_INTERNAL']: dut.expect_exact('Allocate Analog Comparator with internal reference voltage') diff --git a/examples/peripherals/analog_comparator/sdkconfig.ci.ext b/examples/peripherals/analog_comparator/auto_scan/sdkconfig.ci.ext similarity index 100% rename from examples/peripherals/analog_comparator/sdkconfig.ci.ext rename to examples/peripherals/analog_comparator/auto_scan/sdkconfig.ci.ext diff --git a/examples/peripherals/analog_comparator/sdkconfig.ci.intl b/examples/peripherals/analog_comparator/auto_scan/sdkconfig.ci.intl similarity index 100% rename from examples/peripherals/analog_comparator/sdkconfig.ci.intl rename to examples/peripherals/analog_comparator/auto_scan/sdkconfig.ci.intl diff --git a/examples/peripherals/analog_comparator/static_50p_ref.png b/examples/peripherals/analog_comparator/auto_scan/static_50p_ref.png similarity index 100% rename from examples/peripherals/analog_comparator/static_50p_ref.png rename to examples/peripherals/analog_comparator/auto_scan/static_50p_ref.png