feat(ana_cmpr): support multi-channel scan on esp32s31

This commit is contained in:
morris
2026-05-06 09:39:48 +08:00
parent 05b5dc8938
commit 47863cb651
64 changed files with 1622 additions and 524 deletions
+438 -139
View File
@@ -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)
{
+87 -5
View File
@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2023-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -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
@@ -12,6 +12,7 @@
#include <inttypes.h>
#include <stdatomic.h>
#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
@@ -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 <stdint.h>
#include <stdbool.h>
#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
}
@@ -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
@@ -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" {
+5 -1
View File
@@ -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)
@@ -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 |
| ----------------- | -------- | --------- | -------- | --------- | -------- | --------- |
@@ -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
}
@@ -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
}
@@ -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
@@ -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 = {
@@ -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));
@@ -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");
@@ -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()
@@ -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",
},
@@ -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;
@@ -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",
},
@@ -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;
@@ -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",
},
@@ -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;
@@ -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",
},
@@ -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;
@@ -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",
},
@@ -7,11 +7,14 @@
#pragma once
#include <stdbool.h>
#include <sys/param.h>
#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
@@ -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",
},
@@ -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;
@@ -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",
},
@@ -7,10 +7,12 @@
#pragma once
#include <stdbool.h>
#include <sys/param.h>
#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
@@ -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
};
@@ -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
@@ -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
@@ -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)
@@ -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
@@ -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)
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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//////////////////////////////////////////////////////////////////
/**
@@ -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
@@ -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
@@ -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
@@ -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)
@@ -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
@@ -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//////////////////////////////////////////////////////////////////
/**
@@ -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)
@@ -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
@@ -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");