Merge branch 'bugfix/fix_gpio_etm_multi_task_v5.3' into 'release/v5.3'

fix(gpio_etm): allow one GPIO binds to multiple ETM tasks (v5.3)

See merge request espressif/esp-idf!30455
This commit is contained in:
morris 2024-04-25 15:43:16 +08:00
commit e56f92aab4
20 changed files with 499 additions and 244 deletions

View File

@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD * SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
* *
* SPDX-License-Identifier: Apache-2.0 * SPDX-License-Identifier: Apache-2.0
*/ */
@ -13,20 +13,29 @@
extern "C" { extern "C" {
#endif #endif
#define GPIO_ETM_EVENT_EDGE_TYPES 3 /*!< GPIO ETM edge events are POS/NEG/ANY */
#define GPIO_ETM_TASK_ACTION_TYPES 3 /*!< GPIO ETM action tasks are SET/CLEAR/TOGGLE */
/** /**
* @brief GPIO edges that can be used as ETM event * @brief GPIO edges that can be used as ETM event
*/ */
typedef enum { typedef enum {
GPIO_ETM_EVENT_EDGE_POS, /*!< A rising edge on the GPIO will generate an ETM event signal */ GPIO_ETM_EVENT_EDGE_POS = 1, /*!< A rising edge on the GPIO will generate an ETM event signal */
GPIO_ETM_EVENT_EDGE_NEG, /*!< A falling edge on the GPIO will generate an ETM event signal */ GPIO_ETM_EVENT_EDGE_NEG, /*!< A falling edge on the GPIO will generate an ETM event signal */
GPIO_ETM_EVENT_EDGE_ANY, /*!< Any edge on the GPIO can generate an ETM event signal */ GPIO_ETM_EVENT_EDGE_ANY, /*!< Any edge on the GPIO can generate an ETM event signal */
} gpio_etm_event_edge_t; } gpio_etm_event_edge_t;
/** /**
* @brief GPIO ETM event configuration * @brief GPIO ETM event configuration
*
* If more than one kind of ETM edge event want to be triggered on the same GPIO pin, you can configure them together.
* It helps to save GPIO ETM event channel resources for other GPIOs.
*/ */
typedef struct { typedef struct {
gpio_etm_event_edge_t edge; /*!< Which kind of edge can trigger the ETM event module */ union {
gpio_etm_event_edge_t edge; /*!< Which kind of edge can trigger the ETM event module */
gpio_etm_event_edge_t edges[GPIO_ETM_EVENT_EDGE_TYPES]; /*!< Array of kinds of edges to trigger the ETM event module on the same GPIO */
};
} gpio_etm_event_config_t; } gpio_etm_event_config_t;
/** /**
@ -34,9 +43,11 @@ typedef struct {
* *
* @note The created ETM event object can be deleted later by calling `esp_etm_del_event` * @note The created ETM event object can be deleted later by calling `esp_etm_del_event`
* @note The newly created ETM event object is not bind to any GPIO, you need to call `gpio_etm_event_bind_gpio` to bind the wanted GPIO * @note The newly created ETM event object is not bind to any GPIO, you need to call `gpio_etm_event_bind_gpio` to bind the wanted GPIO
* @note Every success call to this function will acquire a free GPIO ETM event channel
* *
* @param[in] config GPIO ETM event configuration * @param[in] config GPIO ETM event configuration
* @param[out] ret_event Returned ETM event handle * @param[out] ret_event Returned ETM event handle
* @param[out] ... Other returned ETM event handles if any (the order of the returned event handles is aligned with the array order in field `edges` in `gpio_etm_event_config_t`)
* @return * @return
* - ESP_OK: Create ETM event successfully * - ESP_OK: Create ETM event successfully
* - ESP_ERR_INVALID_ARG: Create ETM event failed because of invalid argument * - ESP_ERR_INVALID_ARG: Create ETM event failed because of invalid argument
@ -44,7 +55,7 @@ typedef struct {
* - ESP_ERR_NOT_FOUND: Create ETM event failed because all events are used up and no more free one * - ESP_ERR_NOT_FOUND: Create ETM event failed because all events are used up and no more free one
* - ESP_FAIL: Create ETM event failed because of other reasons * - ESP_FAIL: Create ETM event failed because of other reasons
*/ */
esp_err_t gpio_new_etm_event(const gpio_etm_event_config_t *config, esp_etm_event_handle_t *ret_event); esp_err_t gpio_new_etm_event(const gpio_etm_event_config_t *config, esp_etm_event_handle_t *ret_event, ...);
/** /**
* @brief Bind the GPIO with the ETM event * @brief Bind the GPIO with the ETM event
@ -65,16 +76,21 @@ esp_err_t gpio_etm_event_bind_gpio(esp_etm_event_handle_t event, int gpio_num);
* @brief GPIO actions that can be taken by the ETM task * @brief GPIO actions that can be taken by the ETM task
*/ */
typedef enum { typedef enum {
GPIO_ETM_TASK_ACTION_SET, /*!< Set the GPIO level to high */ GPIO_ETM_TASK_ACTION_SET = 1, /*!< Set the GPIO level to high */
GPIO_ETM_TASK_ACTION_CLR, /*!< Clear the GPIO level to low */ GPIO_ETM_TASK_ACTION_CLR, /*!< Clear the GPIO level to low */
GPIO_ETM_TASK_ACTION_TOG, /*!< Toggle the GPIO level */ GPIO_ETM_TASK_ACTION_TOG, /*!< Toggle the GPIO level */
} gpio_etm_task_action_t; } gpio_etm_task_action_t;
/** /**
* @brief GPIO ETM task configuration * @brief GPIO ETM task configuration
*
* If multiple actions wants to be added to the same GPIO pin, you have to configure all the GPIO ETM tasks together.
*/ */
typedef struct { typedef struct {
gpio_etm_task_action_t action; /*!< Which action to take by the ETM task module */ union {
gpio_etm_task_action_t action; /*!< Action to take by the ETM task module */
gpio_etm_task_action_t actions[GPIO_ETM_TASK_ACTION_TYPES]; /*!< Array of actions to take by the ETM task module on the same GPIO */
};
} gpio_etm_task_config_t; } gpio_etm_task_config_t;
/** /**
@ -83,9 +99,11 @@ typedef struct {
* @note The created ETM task object can be deleted later by calling `esp_etm_del_task` * @note The created ETM task object can be deleted later by calling `esp_etm_del_task`
* @note The GPIO ETM task works like a container, a newly created ETM task object doesn't have GPIO members to be managed. * @note The GPIO ETM task works like a container, a newly created ETM task object doesn't have GPIO members to be managed.
* You need to call `gpio_etm_task_add_gpio` to put one or more GPIOs to the container. * You need to call `gpio_etm_task_add_gpio` to put one or more GPIOs to the container.
* @note Every success call to this function will acquire a free GPIO ETM task channel
* *
* @param[in] config GPIO ETM task configuration * @param[in] config GPIO ETM task configuration
* @param[out] ret_task Returned ETM task handle * @param[out] ret_task Returned ETM task handle
* @param[out] ... Other returned ETM task handles if any (the order of the returned task handles is aligned with the array order in field `actions` in `gpio_etm_task_config_t`)
* @return * @return
* - ESP_OK: Create ETM task successfully * - ESP_OK: Create ETM task successfully
* - ESP_ERR_INVALID_ARG: Create ETM task failed because of invalid argument * - ESP_ERR_INVALID_ARG: Create ETM task failed because of invalid argument
@ -93,7 +111,7 @@ typedef struct {
* - ESP_ERR_NOT_FOUND: Create ETM task failed because all tasks are used up and no more free one * - ESP_ERR_NOT_FOUND: Create ETM task failed because all tasks are used up and no more free one
* - ESP_FAIL: Create ETM task failed because of other reasons * - ESP_FAIL: Create ETM task failed because of other reasons
*/ */
esp_err_t gpio_new_etm_task(const gpio_etm_task_config_t *config, esp_etm_task_handle_t *ret_task); esp_err_t gpio_new_etm_task(const gpio_etm_task_config_t *config, esp_etm_task_handle_t *ret_task, ...);
/** /**
* @brief Add GPIO to the ETM task. * @brief Add GPIO to the ETM task.

View File

@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD * SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
* *
* SPDX-License-Identifier: Apache-2.0 * SPDX-License-Identifier: Apache-2.0
*/ */
@ -34,19 +34,23 @@ typedef struct gpio_etm_event_t gpio_etm_event_t;
typedef struct gpio_etm_group_t { typedef struct gpio_etm_group_t {
portMUX_TYPE spinlock; portMUX_TYPE spinlock;
gpio_etm_dev_t *dev; gpio_etm_dev_t *dev;
gpio_etm_task_t *tasks[SOC_GPIO_ETM_TASKS_PER_GROUP]; uint8_t tasks[GPIO_LL_ETM_TASK_CHANNELS_PER_GROUP]; // Array of the acquired action masks in each GPIO ETM task channel
gpio_etm_event_t *events[SOC_GPIO_ETM_EVENTS_PER_GROUP]; uint8_t events[GPIO_LL_ETM_EVENT_CHANNELS_PER_GROUP]; // Array of the acquired event masks in each GPIO ETM event channel
uint8_t actions[SOC_GPIO_PIN_COUNT]; // Array of the masks of the added actions to each GPIO
uint8_t edges[GPIO_LL_ETM_EVENT_CHANNELS_PER_GROUP]; // Array of the masks of the bound event edges in each GPIO ETM event channel
} gpio_etm_group_t; } gpio_etm_group_t;
struct gpio_etm_event_t { struct gpio_etm_event_t {
esp_etm_event_t base; esp_etm_event_t base;
int chan_id; int chan_id;
gpio_etm_event_edge_t edge_id;
gpio_etm_group_t *group; gpio_etm_group_t *group;
}; };
struct gpio_etm_task_t { struct gpio_etm_task_t {
esp_etm_task_t base; esp_etm_task_t base;
int chan_id; int chan_id;
gpio_etm_task_action_t action_id;
gpio_etm_group_t *group; gpio_etm_group_t *group;
size_t num_of_gpios; // record the number of GPIOs that are bound to the etm task size_t num_of_gpios; // record the number of GPIOs that are bound to the etm task
}; };
@ -56,81 +60,65 @@ static gpio_etm_group_t s_gpio_etm_group = {
.spinlock = portMUX_INITIALIZER_UNLOCKED, .spinlock = portMUX_INITIALIZER_UNLOCKED,
}; };
static esp_err_t gpio_etm_event_register_to_group(gpio_etm_event_t *event) static esp_err_t gpio_etm_acquire_event_channel(uint8_t event_mask, int *chan_id)
{ {
assert(chan_id);
gpio_etm_group_t *group = &s_gpio_etm_group; gpio_etm_group_t *group = &s_gpio_etm_group;
int chan_id = -1;
// loop to search free one in the group int free_chan_id = -1;
// loop to search free event channel in the group
portENTER_CRITICAL(&group->spinlock); portENTER_CRITICAL(&group->spinlock);
for (int j = 0; j < SOC_GPIO_ETM_EVENTS_PER_GROUP; j++) { for (int j = 0; j < GPIO_LL_ETM_EVENT_CHANNELS_PER_GROUP; j++) {
if (!group->events[j]) { if (!group->events[j]) {
chan_id = j; free_chan_id = j;
group->events[j] = event; group->events[j] = event_mask;
break; break;
} }
} }
portEXIT_CRITICAL(&group->spinlock); portEXIT_CRITICAL(&group->spinlock);
ESP_RETURN_ON_FALSE(chan_id != -1, ESP_ERR_NOT_FOUND, TAG, "no free event channel"); ESP_RETURN_ON_FALSE(free_chan_id != -1, ESP_ERR_NOT_FOUND, TAG, "no free event channel");
event->group = group; *chan_id = free_chan_id;
event->chan_id = chan_id;
return ESP_OK; return ESP_OK;
} }
static esp_err_t gpio_etm_task_register_to_group(gpio_etm_task_t *task) static esp_err_t gpio_etm_release_event_channel(int chan_id, uint8_t event_mask)
{ {
gpio_etm_group_t *group = &s_gpio_etm_group; gpio_etm_group_t *group = &s_gpio_etm_group;
int chan_id = -1;
// loop to search free one in the group
portENTER_CRITICAL(&group->spinlock); portENTER_CRITICAL(&group->spinlock);
for (int j = 0; j < SOC_GPIO_ETM_TASKS_PER_GROUP; j++) { group->events[chan_id] &= ~event_mask;
portEXIT_CRITICAL(&group->spinlock);
return ESP_OK;
}
static esp_err_t gpio_etm_acquire_task_channel(uint8_t task_mask, int *chan_id)
{
assert(chan_id);
gpio_etm_group_t *group = &s_gpio_etm_group;
int free_chan_id = -1;
// loop to search free task channel in the group
portENTER_CRITICAL(&group->spinlock);
for (int j = 0; j < GPIO_LL_ETM_TASK_CHANNELS_PER_GROUP; j++) {
if (!group->tasks[j]) { if (!group->tasks[j]) {
chan_id = j; free_chan_id = j;
group->tasks[j] = task; group->tasks[j] = task_mask;
break; break;
} }
} }
portEXIT_CRITICAL(&group->spinlock); portEXIT_CRITICAL(&group->spinlock);
ESP_RETURN_ON_FALSE(chan_id != -1, ESP_ERR_NOT_FOUND, TAG, "no free task channel"); ESP_RETURN_ON_FALSE(free_chan_id != -1, ESP_ERR_NOT_FOUND, TAG, "no free task channel");
task->group = group; *chan_id = free_chan_id;
task->chan_id = chan_id;
return ESP_OK; return ESP_OK;
} }
static void gpio_etm_event_unregister_from_group(gpio_etm_event_t *event) static esp_err_t gpio_etm_release_task_channel(int chan_id, uint8_t task_mask)
{ {
gpio_etm_group_t *group = event->group; gpio_etm_group_t *group = &s_gpio_etm_group;
int chan_id = event->chan_id;
portENTER_CRITICAL(&group->spinlock); portENTER_CRITICAL(&group->spinlock);
group->events[chan_id] = NULL; group->tasks[chan_id] &= ~task_mask;
portEXIT_CRITICAL(&group->spinlock); portEXIT_CRITICAL(&group->spinlock);
}
static void gpio_etm_task_unregister_from_group(gpio_etm_task_t *task)
{
gpio_etm_group_t *group = task->group;
int chan_id = task->chan_id;
portENTER_CRITICAL(&group->spinlock);
group->tasks[chan_id] = NULL;
portEXIT_CRITICAL(&group->spinlock);
}
static esp_err_t gpio_etm_event_destroy(gpio_etm_event_t *event)
{
if (event->group) {
gpio_etm_event_unregister_from_group(event);
}
free(event);
return ESP_OK;
}
static esp_err_t gpio_etm_task_destroy(gpio_etm_task_t *task)
{
if (task->group) {
gpio_etm_task_unregister_from_group(task);
}
free(task);
return ESP_OK; return ESP_OK;
} }
@ -138,9 +126,16 @@ static esp_err_t gpio_del_etm_event(esp_etm_event_t *event)
{ {
gpio_etm_event_t *gpio_event = __containerof(event, gpio_etm_event_t, base); gpio_etm_event_t *gpio_event = __containerof(event, gpio_etm_event_t, base);
gpio_etm_group_t *group = gpio_event->group; gpio_etm_group_t *group = gpio_event->group;
// disable event channel // unbind it from the GPIO and check if the GPIO ETM event channel can be disabled
gpio_ll_etm_enable_event_channel(group->dev, gpio_event->chan_id, false); portENTER_CRITICAL(&group->spinlock);
gpio_etm_event_destroy(gpio_event); group->edges[gpio_event->chan_id] &= ~(1 << gpio_event->edge_id);
if (!group->edges[gpio_event->chan_id]) {
gpio_ll_etm_enable_event_channel(group->dev, gpio_event->chan_id, false);
}
portEXIT_CRITICAL(&group->spinlock);
gpio_etm_release_event_channel(gpio_event->chan_id, 1 << gpio_event->edge_id);
free(gpio_event);
return ESP_OK; return ESP_OK;
} }
@ -149,95 +144,192 @@ static esp_err_t gpio_del_etm_task(esp_etm_task_t *task)
gpio_etm_task_t *gpio_task = __containerof(task, gpio_etm_task_t, base); gpio_etm_task_t *gpio_task = __containerof(task, gpio_etm_task_t, base);
// make sure user has called `gpio_etm_task_rm_gpio` to clean the etm task channel // make sure user has called `gpio_etm_task_rm_gpio` to clean the etm task channel
ESP_RETURN_ON_FALSE(gpio_task->num_of_gpios == 0, ESP_ERR_INVALID_STATE, TAG, "some GPIO till bounded to the etm task"); ESP_RETURN_ON_FALSE(gpio_task->num_of_gpios == 0, ESP_ERR_INVALID_STATE, TAG, "some GPIO till bounded to the etm task");
gpio_etm_task_destroy(gpio_task); gpio_etm_release_task_channel(gpio_task->chan_id, 1 << gpio_task->action_id);
free(gpio_task);
return ESP_OK; return ESP_OK;
} }
esp_err_t gpio_new_etm_event(const gpio_etm_event_config_t *config, esp_etm_event_handle_t *ret_event) esp_err_t gpio_new_etm_event(const gpio_etm_event_config_t *config, esp_etm_event_handle_t *ret_event, ...)
{ {
#if CONFIG_ETM_ENABLE_DEBUG_LOG #if CONFIG_ETM_ENABLE_DEBUG_LOG
esp_log_level_set(TAG, ESP_LOG_DEBUG); esp_log_level_set(TAG, ESP_LOG_DEBUG);
#endif #endif
esp_err_t ret = ESP_OK; esp_err_t ret = ESP_OK;
gpio_etm_event_t *event = NULL; int chan_id = -1;
ESP_GOTO_ON_FALSE(config && ret_event, ESP_ERR_INVALID_ARG, err, TAG, "invalid argument"); uint8_t event_mask = 0;
esp_etm_event_handle_t *ret_event_itor = ret_event;
va_list args;
ESP_RETURN_ON_FALSE(config && ret_event, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
for (int i = 0; i < GPIO_ETM_EVENT_EDGE_TYPES; i++) {
if (config->edges[i]) {
uint8_t msk = (1 << config->edges[i]);
ESP_RETURN_ON_FALSE(!(msk & event_mask), ESP_ERR_INVALID_ARG, TAG, "no identical edge event allowed in one call");
event_mask |= msk;
}
}
ESP_RETURN_ON_FALSE(event_mask, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
uint32_t event_num = __builtin_popcount(event_mask);
gpio_etm_event_t *events[event_num];
event = heap_caps_calloc(1, sizeof(gpio_etm_event_t), ETM_MEM_ALLOC_CAPS); for (int i = 0; i < event_num; i++) {
ESP_GOTO_ON_FALSE(event, ESP_ERR_NO_MEM, err, TAG, "no mem for event channel"); events[i] = (gpio_etm_event_t *)heap_caps_calloc(1, sizeof(gpio_etm_event_t), ETM_MEM_ALLOC_CAPS);
// register the event channel to the group ESP_GOTO_ON_FALSE(events[i], ESP_ERR_NO_MEM, err, TAG, "no mem for event channel(s)");
ESP_GOTO_ON_ERROR(gpio_etm_event_register_to_group(event), err, TAG, "register event channel to group failed");
int chan_id = event->chan_id;
uint32_t event_id = 0;
switch (config->edge) {
case GPIO_ETM_EVENT_EDGE_ANY:
event_id = GPIO_LL_ETM_EVENT_ID_ANY_EDGE(chan_id);
break;
case GPIO_ETM_EVENT_EDGE_POS:
event_id = GPIO_LL_ETM_EVENT_ID_POS_EDGE(chan_id);
break;
case GPIO_ETM_EVENT_EDGE_NEG:
event_id = GPIO_LL_ETM_EVENT_ID_NEG_EDGE(chan_id);
break;
default:
ESP_GOTO_ON_FALSE(false, ESP_ERR_INVALID_ARG, err, TAG, "invalid edge");
} }
event->base.del = gpio_del_etm_event; // register the event channel to the group
event->base.event_id = event_id; ESP_GOTO_ON_ERROR(gpio_etm_acquire_event_channel(event_mask, &chan_id), err, TAG, "register event channel to group failed");
event->base.trig_periph = ETM_TRIG_PERIPH_GPIO;
ESP_LOGD(TAG, "new event @%p, event_id=%"PRIu32", chan_id=%d", event, event_id, chan_id); bool no_avail_ret_arg = false;
*ret_event = &event->base; va_start(args, ret_event);
for (int i = 0, j = 0; i < event_num; i++, j++) {
if (!ret_event_itor) {
no_avail_ret_arg = true;
break;
}
// assign to the ret_event handles in the configuration order
gpio_etm_event_edge_t event_edge;
do {
event_edge = config->edges[j];
} while (!event_edge && ++j);
uint32_t event_id = 0;
switch (event_edge) {
case GPIO_ETM_EVENT_EDGE_ANY:
event_id = GPIO_LL_ETM_EVENT_ID_ANY_EDGE(chan_id);
break;
case GPIO_ETM_EVENT_EDGE_POS:
event_id = GPIO_LL_ETM_EVENT_ID_POS_EDGE(chan_id);
break;
case GPIO_ETM_EVENT_EDGE_NEG:
event_id = GPIO_LL_ETM_EVENT_ID_NEG_EDGE(chan_id);
break;
default:
va_end(args);
ESP_GOTO_ON_FALSE(false, ESP_ERR_INVALID_ARG, err, TAG, "invalid edge");
}
gpio_etm_event_t *event = events[i];
event->base.del = gpio_del_etm_event;
event->base.event_id = event_id;
event->base.trig_periph = ETM_TRIG_PERIPH_GPIO;
event->group = &s_gpio_etm_group;
event->chan_id = chan_id;
event->edge_id = event_edge;
ESP_LOGD(TAG, "new event @%p, event_id=%"PRIu32", chan_id=%d", event, event_id, chan_id);
*ret_event_itor = &event->base;
ret_event_itor = va_arg(args, esp_etm_event_handle_t *);
}
va_end(args);
ESP_GOTO_ON_FALSE(!no_avail_ret_arg, ESP_ERR_INVALID_ARG, err, TAG, "mismatch number of events with number of pointers to store event handles");
return ESP_OK; return ESP_OK;
err: err:
if (event) { if (chan_id != -1) {
gpio_etm_event_destroy(event); gpio_etm_release_event_channel(chan_id, event_mask);
} }
for (int i = 0; i < event_num; i++) {
if (events[i]) {
free(events[i]);
}
}
ret_event_itor = ret_event;
va_start(args, ret_event);
while (ret_event_itor) {
*ret_event_itor = NULL;
ret_event_itor = va_arg(args, esp_etm_event_handle_t *);
}
va_end(args);
return ret; return ret;
} }
esp_err_t gpio_new_etm_task(const gpio_etm_task_config_t *config, esp_etm_task_handle_t *ret_task) esp_err_t gpio_new_etm_task(const gpio_etm_task_config_t *config, esp_etm_task_handle_t *ret_task, ...)
{ {
#if CONFIG_ETM_ENABLE_DEBUG_LOG #if CONFIG_ETM_ENABLE_DEBUG_LOG
esp_log_level_set(TAG, ESP_LOG_DEBUG); esp_log_level_set(TAG, ESP_LOG_DEBUG);
#endif #endif
esp_err_t ret = ESP_OK; esp_err_t ret = ESP_OK;
gpio_etm_task_t *task = NULL; int chan_id = -1;
ESP_GOTO_ON_FALSE(config && ret_task, ESP_ERR_INVALID_ARG, err, TAG, "invalid argument"); uint8_t task_mask = 0;
esp_etm_task_handle_t *ret_task_itor = ret_task;
va_list args;
ESP_RETURN_ON_FALSE(config && ret_task, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
for (int i = 0; i < GPIO_ETM_TASK_ACTION_TYPES; i++) {
if (config->actions[i]) {
uint8_t msk = (1 << config->actions[i]);
ESP_RETURN_ON_FALSE(!(msk & task_mask), ESP_ERR_INVALID_ARG, TAG, "no identical action allowed in one call");
task_mask |= msk;
}
}
ESP_RETURN_ON_FALSE(task_mask, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
uint32_t task_num = __builtin_popcount(task_mask);
gpio_etm_task_t *tasks[task_num];
task = heap_caps_calloc(1, sizeof(gpio_etm_task_t), ETM_MEM_ALLOC_CAPS); for (int i = 0; i < task_num; i++) {
ESP_GOTO_ON_FALSE(task, ESP_ERR_NO_MEM, err, TAG, "no mem for task channel"); tasks[i] = (gpio_etm_task_t *)heap_caps_calloc(1, sizeof(gpio_etm_task_t), ETM_MEM_ALLOC_CAPS);
// register the task channel to the group ESP_GOTO_ON_FALSE(tasks[i], ESP_ERR_NO_MEM, err, TAG, "no mem for task channel(s)");
ESP_GOTO_ON_ERROR(gpio_etm_task_register_to_group(task), err, TAG, "register task channel to group failed");
int chan_id = task->chan_id;
uint32_t task_id = 0;
switch (config->action) {
case GPIO_ETM_TASK_ACTION_SET:
task_id = GPIO_LL_ETM_TASK_ID_SET(chan_id);
break;
case GPIO_ETM_TASK_ACTION_CLR:
task_id = GPIO_LL_ETM_TASK_ID_CLR(chan_id);
break;
case GPIO_ETM_TASK_ACTION_TOG:
task_id = GPIO_LL_ETM_TASK_ID_TOG(chan_id);
break;
default:
ESP_GOTO_ON_FALSE(false, ESP_ERR_INVALID_ARG, err, TAG, "invalid action");
} }
task->base.del = gpio_del_etm_task; // register the task channel to the group
task->base.task_id = task_id; ESP_GOTO_ON_ERROR(gpio_etm_acquire_task_channel(task_mask, &chan_id), err, TAG, "register task channel to group failed");
task->base.trig_periph = ETM_TRIG_PERIPH_GPIO;
ESP_LOGD(TAG, "new task @%p, task_id=%"PRIu32", chan_id=%d", task, task_id, chan_id); bool no_avail_ret_arg = false;
*ret_task = &task->base; va_start(args, ret_task);
for (int i = 0, j = 0; i < task_num; i++, j++) {
if (!ret_task_itor) {
no_avail_ret_arg = true;
break;
}
// assign to the ret_task handles in the configuration order
gpio_etm_task_action_t action;
do {
action = config->actions[i];
} while (!action && ++j);
uint32_t task_id = 0;
switch (action) {
case GPIO_ETM_TASK_ACTION_SET:
task_id = GPIO_LL_ETM_TASK_ID_SET(chan_id);
break;
case GPIO_ETM_TASK_ACTION_CLR:
task_id = GPIO_LL_ETM_TASK_ID_CLR(chan_id);
break;
case GPIO_ETM_TASK_ACTION_TOG:
task_id = GPIO_LL_ETM_TASK_ID_TOG(chan_id);
break;
default:
va_end(args);
ESP_GOTO_ON_FALSE(false, ESP_ERR_INVALID_ARG, err, TAG, "invalid action");
}
gpio_etm_task_t *task = tasks[i];
task->base.del = gpio_del_etm_task;
task->base.task_id = task_id;
task->base.trig_periph = ETM_TRIG_PERIPH_GPIO;
task->group = &s_gpio_etm_group;
task->chan_id = chan_id;
task->action_id = action;
ESP_LOGD(TAG, "new task @%p, task_id=%"PRIu32", gpio_etm_task_chan_id=%d", task, task_id, chan_id);
*ret_task_itor = &task->base;
ret_task_itor = va_arg(args, esp_etm_task_handle_t *);
}
va_end(args);
ESP_GOTO_ON_FALSE(!no_avail_ret_arg, ESP_ERR_INVALID_ARG, err, TAG, "mismatch number of tasks with number of pointers to store task handles");
return ESP_OK; return ESP_OK;
err: err:
if (task) { if (chan_id != -1) {
gpio_etm_task_destroy(task); gpio_etm_release_task_channel(chan_id, task_mask);
} }
for (int i = 0; i < task_num; i++) {
if (tasks[i]) {
free(tasks[i]);
}
}
ret_task_itor = ret_task;
va_start(args, ret_task);
while (ret_task_itor) {
*ret_task_itor = NULL;
ret_task_itor = va_arg(args, esp_etm_task_handle_t *);
}
va_end(args);
return ret; return ret;
} }
@ -248,12 +340,27 @@ esp_err_t gpio_etm_event_bind_gpio(esp_etm_event_handle_t event, int gpio_num)
ESP_RETURN_ON_FALSE(GPIO_IS_VALID_GPIO(gpio_num), ESP_ERR_INVALID_ARG, TAG, "gpio is not input capable"); ESP_RETURN_ON_FALSE(GPIO_IS_VALID_GPIO(gpio_num), ESP_ERR_INVALID_ARG, TAG, "gpio is not input capable");
gpio_etm_event_t *gpio_event = __containerof(event, gpio_etm_event_t, base); gpio_etm_event_t *gpio_event = __containerof(event, gpio_etm_event_t, base);
gpio_etm_group_t *group = gpio_event->group; gpio_etm_group_t *group = gpio_event->group;
// disable gpio etm event channel first
gpio_ll_etm_enable_event_channel(group->dev, gpio_event->chan_id, false); bool allowed = true;
// then set the gpio number portENTER_CRITICAL(&group->spinlock);
gpio_ll_etm_event_channel_set_gpio(group->dev, gpio_event->chan_id, gpio_num); // check if the GPIO ETM event channel where the new event belongs to has previously been bound to another GPIO
// enable gpio etm event channel again // one GPIO ETM event channel can only be bound to one GPIO
gpio_ll_etm_enable_event_channel(group->dev, gpio_event->chan_id, true); if (group->edges[gpio_event->chan_id]) {
if (gpio_ll_etm_event_channel_get_gpio(group->dev, gpio_event->chan_id) != gpio_num) {
allowed = false;
}
} else {
// set the GPIO number
gpio_ll_etm_event_channel_set_gpio(group->dev, gpio_event->chan_id, gpio_num);
// enable GPIO ETM event channel
gpio_ll_etm_enable_event_channel(group->dev, gpio_event->chan_id, true);
}
if (allowed) {
group->edges[gpio_event->chan_id] |= (1 << gpio_event->edge_id);
}
portEXIT_CRITICAL(&group->spinlock);
ESP_RETURN_ON_FALSE(allowed, ESP_ERR_INVALID_ARG, TAG, "the GPIO ETM event channel where the event belongs to has already been bound to another GPIO");
return ESP_OK; return ESP_OK;
} }
@ -264,19 +371,28 @@ esp_err_t gpio_etm_task_add_gpio(esp_etm_task_handle_t task, int gpio_num)
ESP_RETURN_ON_FALSE(GPIO_IS_VALID_OUTPUT_GPIO(gpio_num), ESP_ERR_INVALID_ARG, TAG, "gpio is not output capable"); ESP_RETURN_ON_FALSE(GPIO_IS_VALID_OUTPUT_GPIO(gpio_num), ESP_ERR_INVALID_ARG, TAG, "gpio is not output capable");
gpio_etm_task_t *gpio_task = __containerof(task, gpio_etm_task_t, base); gpio_etm_task_t *gpio_task = __containerof(task, gpio_etm_task_t, base);
gpio_etm_group_t *group = gpio_task->group; gpio_etm_group_t *group = gpio_task->group;
bool gpio_not_enabled = true;
bool allowed = true;
// use spinlock as this function may be called with different task object in different threads // use spinlock as this function may be called with different task object in different threads
// and the gpio_num might reside in the same register // and the gpio_num might reside in the same register
portENTER_CRITICAL(&group->spinlock); portENTER_CRITICAL(&group->spinlock);
// check if the gpio has been enabled // check if the new task is compatible with the tasks that has previously been added to the GPIO
if (!gpio_ll_etm_is_task_gpio_enabled(group->dev, gpio_num)) { // the tasks have to be from the same GPIO ETM task channel
if (group->actions[gpio_num]) {
if (gpio_ll_etm_gpio_get_task_channel(group->dev, gpio_num) != gpio_task->chan_id) {
allowed = false;
}
} else {
// first action added to the GPIO
gpio_ll_etm_gpio_set_task_channel(group->dev, gpio_num, gpio_task->chan_id); gpio_ll_etm_gpio_set_task_channel(group->dev, gpio_num, gpio_task->chan_id);
gpio_ll_etm_enable_task_gpio(group->dev, gpio_num, true); gpio_ll_etm_enable_task_gpio(group->dev, gpio_num, true);
} else { }
gpio_not_enabled = false;
if (allowed) {
group->actions[gpio_num] |= (1 << gpio_task->action_id);
} }
portEXIT_CRITICAL(&group->spinlock); portEXIT_CRITICAL(&group->spinlock);
ESP_RETURN_ON_FALSE(gpio_not_enabled, ESP_ERR_INVALID_STATE, TAG, "gpio already enabled by other task channel"); ESP_RETURN_ON_FALSE(allowed, ESP_ERR_INVALID_ARG, TAG, "the task does not belong to the GPIO ETM task channel that the GPIO has already binded to");
gpio_task->num_of_gpios++; gpio_task->num_of_gpios++;
return ESP_OK; return ESP_OK;
} }
@ -287,19 +403,24 @@ esp_err_t gpio_etm_task_rm_gpio(esp_etm_task_handle_t task, int gpio_num)
ESP_RETURN_ON_FALSE(GPIO_IS_VALID_OUTPUT_GPIO(gpio_num), ESP_ERR_INVALID_ARG, TAG, "gpio is not output capable"); ESP_RETURN_ON_FALSE(GPIO_IS_VALID_OUTPUT_GPIO(gpio_num), ESP_ERR_INVALID_ARG, TAG, "gpio is not output capable");
gpio_etm_task_t *gpio_task = __containerof(task, gpio_etm_task_t, base); gpio_etm_task_t *gpio_task = __containerof(task, gpio_etm_task_t, base);
gpio_etm_group_t *group = gpio_task->group; gpio_etm_group_t *group = gpio_task->group;
bool gpio_enabled_by_this_task = true;
bool allowed = true;
// use spinlock as this function may be called with different task object in different threads // use spinlock as this function may be called with different task object in different threads
// and the gpio_num might reside in the same register // and the gpio_num might reside in the same register
portENTER_CRITICAL(&group->spinlock); portENTER_CRITICAL(&group->spinlock);
// check if the gpio is managed by this etm task channel if ((group->actions[gpio_num] & (1 << gpio_task->action_id)) &&
if (gpio_ll_etm_is_task_gpio_enabled(group->dev, gpio_num) && gpio_ll_etm_is_task_gpio_enabled(group->dev, gpio_num) &&
(gpio_ll_etm_gpio_get_task_channel(group->dev, gpio_num) == gpio_task->chan_id)) { (gpio_ll_etm_gpio_get_task_channel(group->dev, gpio_num) == gpio_task->chan_id)) {
gpio_ll_etm_enable_task_gpio(group->dev, gpio_num, false); group->actions[gpio_num] &= ~(1 << gpio_task->action_id);
if (!group->actions[gpio_num]) {
// all actions removed from the GPIO
gpio_ll_etm_enable_task_gpio(group->dev, gpio_num, false);
}
} else { } else {
gpio_enabled_by_this_task = false; allowed = false;
} }
portEXIT_CRITICAL(&group->spinlock); portEXIT_CRITICAL(&group->spinlock);
ESP_RETURN_ON_FALSE(gpio_enabled_by_this_task, ESP_ERR_INVALID_STATE, TAG, "gpio is not enabled by this task channel"); ESP_RETURN_ON_FALSE(allowed, ESP_ERR_INVALID_ARG, TAG, "the task was not added to the GPIO");
gpio_task->num_of_gpios--; gpio_task->num_of_gpios--;
return ESP_OK; return ESP_OK;
} }

View File

@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD * SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
* *
* SPDX-License-Identifier: Apache-2.0 * SPDX-License-Identifier: Apache-2.0
*/ */
@ -81,7 +81,7 @@ TEST_CASE("gpio_etm_self_trigger", "[etm]")
// delete gpio etm task without remove all bounded GPIOs should fail // delete gpio etm task without remove all bounded GPIOs should fail
TEST_ESP_ERR(ESP_ERR_INVALID_STATE, esp_etm_del_task(gpio_task)); TEST_ESP_ERR(ESP_ERR_INVALID_STATE, esp_etm_del_task(gpio_task));
// remove unrelated GPIO from the task should fail // remove unrelated GPIO from the task should fail
TEST_ESP_ERR(ESP_ERR_INVALID_STATE, gpio_etm_task_rm_gpio(gpio_task, 10)); TEST_ESP_ERR(ESP_ERR_INVALID_ARG, gpio_etm_task_rm_gpio(gpio_task, 10));
// delete etm primitives // delete etm primitives
TEST_ESP_OK(gpio_etm_task_rm_gpio(gpio_task, output_gpio)); TEST_ESP_OK(gpio_etm_task_rm_gpio(gpio_task, output_gpio));
@ -90,3 +90,118 @@ TEST_CASE("gpio_etm_self_trigger", "[etm]")
TEST_ESP_OK(esp_etm_del_event(gpio_event)); TEST_ESP_OK(esp_etm_del_event(gpio_event));
TEST_ESP_OK(esp_etm_del_channel(etm_channel_a)); TEST_ESP_OK(esp_etm_del_channel(etm_channel_a));
} }
TEST_CASE("gpio_etm_self_trigger_multi_action", "[etm]")
{
// GPIO 0 pos edge event ---> GPIO 1 set level task
// GPIO 22 pos edge event ---> GPIO 1 clear level task
const uint32_t input_gpio1 = 0;
const uint32_t input_gpio2 = 22;
const uint32_t output_gpio = 1;
printf("allocate etm channels\r\n");
esp_etm_channel_config_t etm_config = {};
esp_etm_channel_handle_t etm_channel_a = NULL;
esp_etm_channel_handle_t etm_channel_b = NULL;
TEST_ESP_OK(esp_etm_new_channel(&etm_config, &etm_channel_a));
TEST_ESP_OK(esp_etm_new_channel(&etm_config, &etm_channel_b));
printf("allocate GPIO etm event and task\r\n");
esp_etm_task_handle_t gpio_task_a = NULL;
esp_etm_event_handle_t gpio_event_a = NULL;
esp_etm_task_handle_t gpio_task_b = NULL;
esp_etm_event_handle_t gpio_event_b = NULL;
gpio_etm_event_config_t gpio_event_config = {};
gpio_event_config.edges[0] = GPIO_ETM_EVENT_EDGE_POS;
TEST_ESP_OK(gpio_new_etm_event(&gpio_event_config, &gpio_event_a));
esp_etm_event_handle_t gpio_event_c = NULL; // an extra event only used for testing binding
gpio_event_config.edges[1] = GPIO_ETM_EVENT_EDGE_ANY;
TEST_ESP_OK(gpio_new_etm_event(&gpio_event_config, &gpio_event_b, &gpio_event_c));
gpio_etm_task_config_t gpio_task_config = {};
gpio_task_config.actions[0] = GPIO_ETM_TASK_ACTION_CLR;
gpio_task_config.actions[1] = GPIO_ETM_TASK_ACTION_SET;
TEST_ESP_OK(gpio_new_etm_task(&gpio_task_config, &gpio_task_b, &gpio_task_a));
// bind GPIO to the event and task
TEST_ESP_OK(gpio_etm_event_bind_gpio(gpio_event_a, input_gpio1));
TEST_ESP_OK(gpio_etm_event_bind_gpio(gpio_event_b, input_gpio2));
TEST_ESP_OK(gpio_etm_task_add_gpio(gpio_task_a, output_gpio));
TEST_ESP_OK(gpio_etm_task_add_gpio(gpio_task_b, output_gpio));
// try an infeasible bind of second event to a GPIO
TEST_ESP_ERR(ESP_ERR_INVALID_ARG, gpio_etm_event_bind_gpio(gpio_event_c, input_gpio1));
// try a feasible bind of second event to a GPIO
TEST_ESP_OK(gpio_etm_event_bind_gpio(gpio_event_c, input_gpio2));
// delete the event to unbind it from the GPIO
TEST_ESP_OK(esp_etm_del_event(gpio_event_c));
printf("initialize gpio\r\n");
gpio_config_t task_gpio_config = {
.intr_type = GPIO_INTR_DISABLE,
.mode = GPIO_MODE_INPUT_OUTPUT, // we want to read the GPIO value, so it should be input and output
.pin_bit_mask = 1ULL << output_gpio,
};
TEST_ESP_OK(gpio_config(&task_gpio_config));
// set the initial level
TEST_ESP_OK(gpio_set_level(output_gpio, 0));
gpio_config_t event_gpio_config = {
.intr_type = GPIO_INTR_DISABLE,
.mode = GPIO_MODE_INPUT_OUTPUT, // we want to simulate the edge signal by software, so it should be input and output
.pull_up_en = GPIO_PULLUP_ENABLE,
.pull_down_en = GPIO_PULLDOWN_DISABLE,
.pin_bit_mask = (1ULL << input_gpio1) | (1ULL << input_gpio2),
};
TEST_ESP_OK(gpio_config(&event_gpio_config));
// set the initial level
TEST_ESP_OK(gpio_set_level(input_gpio1, 0));
TEST_ESP_OK(gpio_set_level(input_gpio2, 0));
printf("connect event and task to the channel\r\n");
TEST_ESP_OK(esp_etm_channel_connect(etm_channel_a, gpio_event_a, gpio_task_a));
TEST_ESP_OK(esp_etm_channel_connect(etm_channel_b, gpio_event_b, gpio_task_b));
TEST_ESP_OK(esp_etm_channel_enable(etm_channel_a));
TEST_ESP_OK(esp_etm_channel_enable(etm_channel_b));
// input_gpio1 pos edge ---> output_gpio level being set
TEST_ESP_OK(gpio_set_level(input_gpio1, 1));
vTaskDelay(pdMS_TO_TICKS(100));
TEST_ASSERT_EQUAL(1, gpio_get_level(output_gpio));
// input_gpio1 neg edge does not affect output_gpio level
TEST_ESP_OK(gpio_set_level(input_gpio1, 0));
vTaskDelay(pdMS_TO_TICKS(100));
TEST_ASSERT_EQUAL(1, gpio_get_level(output_gpio));
// input_gpio2 pos edge ---> output_gpio level being cleared
TEST_ESP_OK(gpio_set_level(input_gpio2, 1));
vTaskDelay(pdMS_TO_TICKS(100));
TEST_ASSERT_EQUAL(0, gpio_get_level(output_gpio));
// input_gpio2 neg edge does not affect output_gpio level
TEST_ESP_OK(gpio_set_level(input_gpio2, 0));
vTaskDelay(pdMS_TO_TICKS(100));
TEST_ASSERT_EQUAL(0, gpio_get_level(output_gpio));
// Create a new gpio etm task separately, and add it to the output_gpio should fail (the task does not belong to the same GPIO ETM task channel as gpio_task_a and gpio_task_b)
esp_etm_task_handle_t gpio_task_c = NULL;
gpio_etm_task_config_t gpio_task_config_2 = {
.action = GPIO_ETM_TASK_ACTION_TOG,
};
TEST_ESP_OK(gpio_new_etm_task(&gpio_task_config_2, &gpio_task_c));
TEST_ESP_ERR(ESP_ERR_INVALID_ARG, gpio_etm_task_add_gpio(gpio_task_c, output_gpio));
// delete etm primitives
TEST_ESP_OK(gpio_etm_task_rm_gpio(gpio_task_a, output_gpio));
TEST_ESP_OK(gpio_etm_task_rm_gpio(gpio_task_b, output_gpio));
TEST_ESP_OK(esp_etm_channel_disable(etm_channel_a));
TEST_ESP_OK(esp_etm_channel_disable(etm_channel_b));
TEST_ESP_OK(esp_etm_del_task(gpio_task_a));
TEST_ESP_OK(esp_etm_del_task(gpio_task_b));
TEST_ESP_OK(esp_etm_del_task(gpio_task_c));
TEST_ESP_OK(esp_etm_del_event(gpio_event_a));
TEST_ESP_OK(esp_etm_del_event(gpio_event_b));
TEST_ESP_OK(esp_etm_del_channel(etm_channel_a));
TEST_ESP_OK(esp_etm_del_channel(etm_channel_b));
}

View File

@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD * SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
* *
* SPDX-License-Identifier: Apache-2.0 * SPDX-License-Identifier: Apache-2.0
*/ */
@ -22,15 +22,20 @@
#define GPIO_LL_ETM_TASK_ID_CLR(ch) (GPIO_TASK_CH0_CLEAR + (ch)) #define GPIO_LL_ETM_TASK_ID_CLR(ch) (GPIO_TASK_CH0_CLEAR + (ch))
#define GPIO_LL_ETM_TASK_ID_TOG(ch) (GPIO_TASK_CH0_TOGGLE + (ch)) #define GPIO_LL_ETM_TASK_ID_TOG(ch) (GPIO_TASK_CH0_TOGGLE + (ch))
#define GPIO_LL_ETM_EVENT_CHANNELS_PER_GROUP 8
#define GPIO_LL_ETM_TASK_CHANNELS_PER_GROUP 8
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
#endif #endif
/** /**
* @brief Set which GPIO to be bounded to the event channel * @brief Set which GPIO to be bound to the event channel
*
* @note Different channels can be bound to one GPIO
* *
* @param dev Register base address * @param dev Register base address
* @param chan Channel number * @param chan GPIO ETM Event channel number
* @param gpio_num GPIO number * @param gpio_num GPIO number
*/ */
static inline void gpio_ll_etm_event_channel_set_gpio(gpio_etm_dev_t *dev, uint32_t chan, uint32_t gpio_num) static inline void gpio_ll_etm_event_channel_set_gpio(gpio_etm_dev_t *dev, uint32_t chan, uint32_t gpio_num)
@ -39,10 +44,10 @@ static inline void gpio_ll_etm_event_channel_set_gpio(gpio_etm_dev_t *dev, uint3
} }
/** /**
* @brief Wether to enable the event channel * @brief Whether to enable the event channel
* *
* @param dev Register base address * @param dev Register base address
* @param chan Channel number * @param chan GPIO ETM Event channel number
* @param enable True to enable, false to disable * @param enable True to enable, false to disable
*/ */
static inline void gpio_ll_etm_enable_event_channel(gpio_etm_dev_t *dev, uint32_t chan, bool enable) static inline void gpio_ll_etm_enable_event_channel(gpio_etm_dev_t *dev, uint32_t chan, bool enable)
@ -51,12 +56,24 @@ static inline void gpio_ll_etm_enable_event_channel(gpio_etm_dev_t *dev, uint32_
} }
/** /**
* @brief Set which GPIO to be bounded to the task channel * @brief Get which GPIO is bound to the event channel
*
* @note One channel can be bounded to multiple different GPIOs
* *
* @param dev Register base address * @param dev Register base address
* @param chan Channel number * @param chan GPIO ETM Event channel number
* @return GPIO number
*/
static inline uint32_t gpio_ll_etm_event_channel_get_gpio(gpio_etm_dev_t *dev, uint32_t chan)
{
return dev->event_chn_cfg[chan].etm_chn_event_sel;
}
/**
* @brief Set which GPIO to be bound to the task channel
*
* @note One channel can be bound to multiple different GPIOs
*
* @param dev Register base address
* @param chan GPIO ETM Task channel number
* @param gpio_num GPIO number * @param gpio_num GPIO number
*/ */
static inline void gpio_ll_etm_gpio_set_task_channel(gpio_etm_dev_t *dev, uint32_t gpio_num, uint32_t chan) static inline void gpio_ll_etm_gpio_set_task_channel(gpio_etm_dev_t *dev, uint32_t gpio_num, uint32_t chan)
@ -70,7 +87,7 @@ static inline void gpio_ll_etm_gpio_set_task_channel(gpio_etm_dev_t *dev, uint32
} }
/** /**
* @brief Wether to enable the GPIO to be managed by the task channel * @brief Whether to enable the GPIO to be managed by the task channel
* *
* @param dev Register base address * @param dev Register base address
* @param gpio_num GPIO number * @param gpio_num GPIO number
@ -101,7 +118,7 @@ static inline bool gpio_ll_etm_is_task_gpio_enabled(gpio_etm_dev_t *dev, uint32_
} }
/** /**
* @brief Get the channel number that the GPIO is bounded to * @brief Get the channel number that the GPIO is bound to
* *
* @param dev Register base address * @param dev Register base address
* @param gpio_num GPIO number * @param gpio_num GPIO number

View File

@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
* *
* SPDX-License-Identifier: Apache-2.0 * SPDX-License-Identifier: Apache-2.0
*/ */
@ -22,15 +22,20 @@
#define GPIO_LL_ETM_TASK_ID_CLR(ch) (GPIO_TASK_CH0_CLEAR + (ch)) #define GPIO_LL_ETM_TASK_ID_CLR(ch) (GPIO_TASK_CH0_CLEAR + (ch))
#define GPIO_LL_ETM_TASK_ID_TOG(ch) (GPIO_TASK_CH0_TOGGLE + (ch)) #define GPIO_LL_ETM_TASK_ID_TOG(ch) (GPIO_TASK_CH0_TOGGLE + (ch))
#define GPIO_LL_ETM_EVENT_CHANNELS_PER_GROUP 8
#define GPIO_LL_ETM_TASK_CHANNELS_PER_GROUP 8
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
#endif #endif
/** /**
* @brief Set which GPIO to be bounded to the event channel * @brief Set which GPIO to be bound to the event channel
*
* @note Different channels can be bound to one GPIO
* *
* @param dev Register base address * @param dev Register base address
* @param chan Channel number * @param chan GPIO ETM Event channel number
* @param gpio_num GPIO number * @param gpio_num GPIO number
*/ */
static inline void gpio_ll_etm_event_channel_set_gpio(gpio_etm_dev_t *dev, uint32_t chan, uint32_t gpio_num) static inline void gpio_ll_etm_event_channel_set_gpio(gpio_etm_dev_t *dev, uint32_t chan, uint32_t gpio_num)
@ -39,10 +44,10 @@ static inline void gpio_ll_etm_event_channel_set_gpio(gpio_etm_dev_t *dev, uint3
} }
/** /**
* @brief Wether to enable the event channel * @brief Whether to enable the event channel
* *
* @param dev Register base address * @param dev Register base address
* @param chan Channel number * @param chan GPIO ETM Event channel number
* @param enable True to enable, false to disable * @param enable True to enable, false to disable
*/ */
static inline void gpio_ll_etm_enable_event_channel(gpio_etm_dev_t *dev, uint32_t chan, bool enable) static inline void gpio_ll_etm_enable_event_channel(gpio_etm_dev_t *dev, uint32_t chan, bool enable)
@ -51,12 +56,24 @@ static inline void gpio_ll_etm_enable_event_channel(gpio_etm_dev_t *dev, uint32_
} }
/** /**
* @brief Set which GPIO to be bounded to the task channel * @brief Get which GPIO is bound to the event channel
*
* @note One channel can be bounded to multiple different GPIOs
* *
* @param dev Register base address * @param dev Register base address
* @param chan Channel number * @param chan GPIO ETM Event channel number
* @return GPIO number
*/
static inline uint32_t gpio_ll_etm_event_channel_get_gpio(gpio_etm_dev_t *dev, uint32_t chan)
{
return dev->etm_event_chn_cfg[chan].etm_chn_event_sel;
}
/**
* @brief Set which GPIO to be bound to the task channel
*
* @note One channel can be bound to multiple different GPIOs
*
* @param dev Register base address
* @param chan GPIO ETM Task channel number
* @param gpio_num GPIO number * @param gpio_num GPIO number
*/ */
static inline void gpio_ll_etm_gpio_set_task_channel(gpio_etm_dev_t *dev, uint32_t gpio_num, uint32_t chan) static inline void gpio_ll_etm_gpio_set_task_channel(gpio_etm_dev_t *dev, uint32_t gpio_num, uint32_t chan)
@ -70,7 +87,7 @@ static inline void gpio_ll_etm_gpio_set_task_channel(gpio_etm_dev_t *dev, uint32
} }
/** /**
* @brief Wether to enable the GPIO to be managed by the task channel * @brief Whether to enable the GPIO to be managed by the task channel
* *
* @param dev Register base address * @param dev Register base address
* @param gpio_num GPIO number * @param gpio_num GPIO number
@ -101,7 +118,7 @@ static inline bool gpio_ll_etm_is_task_gpio_enabled(gpio_etm_dev_t *dev, uint32_
} }
/** /**
* @brief Get the channel number that the GPIO is bounded to * @brief Get the channel number that the GPIO is bound to
* *
* @param dev Register base address * @param dev Register base address
* @param gpio_num GPIO number * @param gpio_num GPIO number

View File

@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
* *
* SPDX-License-Identifier: Apache-2.0 * SPDX-License-Identifier: Apache-2.0
*/ */
@ -22,15 +22,20 @@
#define GPIO_LL_ETM_TASK_ID_CLR(ch) (GPIO_TASK_CH0_CLEAR + (ch)) #define GPIO_LL_ETM_TASK_ID_CLR(ch) (GPIO_TASK_CH0_CLEAR + (ch))
#define GPIO_LL_ETM_TASK_ID_TOG(ch) (GPIO_TASK_CH0_TOGGLE + (ch)) #define GPIO_LL_ETM_TASK_ID_TOG(ch) (GPIO_TASK_CH0_TOGGLE + (ch))
#define GPIO_LL_ETM_EVENT_CHANNELS_PER_GROUP 8
#define GPIO_LL_ETM_TASK_CHANNELS_PER_GROUP 8
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
#endif #endif
/** /**
* @brief Set which GPIO to be bounded to the event channel * @brief Set which GPIO to be bound to the event channel
*
* @note Different channels can be bound to one GPIO
* *
* @param dev Register base address * @param dev Register base address
* @param chan Channel number * @param chan GPIO ETM Event channel number
* @param gpio_num GPIO number * @param gpio_num GPIO number
*/ */
static inline void gpio_ll_etm_event_channel_set_gpio(gpio_etm_dev_t *dev, uint32_t chan, uint32_t gpio_num) static inline void gpio_ll_etm_event_channel_set_gpio(gpio_etm_dev_t *dev, uint32_t chan, uint32_t gpio_num)
@ -39,10 +44,10 @@ static inline void gpio_ll_etm_event_channel_set_gpio(gpio_etm_dev_t *dev, uint3
} }
/** /**
* @brief Wether to enable the event channel * @brief Whether to enable the event channel
* *
* @param dev Register base address * @param dev Register base address
* @param chan Channel number * @param chan GPIO ETM Event channel number
* @param enable True to enable, false to disable * @param enable True to enable, false to disable
*/ */
static inline void gpio_ll_etm_enable_event_channel(gpio_etm_dev_t *dev, uint32_t chan, bool enable) static inline void gpio_ll_etm_enable_event_channel(gpio_etm_dev_t *dev, uint32_t chan, bool enable)
@ -51,12 +56,24 @@ static inline void gpio_ll_etm_enable_event_channel(gpio_etm_dev_t *dev, uint32_
} }
/** /**
* @brief Set which GPIO to be bounded to the task channel * @brief Get which GPIO is bound to the event channel
*
* @note One channel can be bounded to multiple different GPIOs
* *
* @param dev Register base address * @param dev Register base address
* @param chan Channel number * @param chan GPIO ETM Event channel number
* @return GPIO number
*/
static inline uint32_t gpio_ll_etm_event_channel_get_gpio(gpio_etm_dev_t *dev, uint32_t chan)
{
return dev->etm_event_chn_cfg[chan].etm_chn_event_sel;
}
/**
* @brief Set which GPIO to be bound to the task channel
*
* @note One channel can be bound to multiple different GPIOs
*
* @param dev Register base address
* @param chan GPIO ETM Task channel number
* @param gpio_num GPIO number * @param gpio_num GPIO number
*/ */
static inline void gpio_ll_etm_gpio_set_task_channel(gpio_etm_dev_t *dev, uint32_t gpio_num, uint32_t chan) static inline void gpio_ll_etm_gpio_set_task_channel(gpio_etm_dev_t *dev, uint32_t gpio_num, uint32_t chan)
@ -70,7 +87,7 @@ static inline void gpio_ll_etm_gpio_set_task_channel(gpio_etm_dev_t *dev, uint32
} }
/** /**
* @brief Wether to enable the GPIO to be managed by the task channel * @brief Whether to enable the GPIO to be managed by the task channel
* *
* @param dev Register base address * @param dev Register base address
* @param gpio_num GPIO number * @param gpio_num GPIO number
@ -101,7 +118,7 @@ static inline bool gpio_ll_etm_is_task_gpio_enabled(gpio_etm_dev_t *dev, uint32_
} }
/** /**
* @brief Get the channel number that the GPIO is bounded to * @brief Get the channel number that the GPIO is bound to
* *
* @param dev Register base address * @param dev Register base address
* @param gpio_num GPIO number * @param gpio_num GPIO number

View File

@ -251,14 +251,6 @@ config SOC_GPIO_SUPPORT_PIN_HYS_FILTER
bool bool
default y default y
config SOC_GPIO_ETM_EVENTS_PER_GROUP
int
default 8
config SOC_GPIO_ETM_TASKS_PER_GROUP
int
default 8
config SOC_GPIO_SUPPORT_RTC_INDEPENDENT config SOC_GPIO_SUPPORT_RTC_INDEPENDENT
bool bool
default y default y

View File

@ -193,8 +193,6 @@
// GPIO peripheral has the ETM extension // GPIO peripheral has the ETM extension
// #define SOC_GPIO_SUPPORT_ETM 1 // #define SOC_GPIO_SUPPORT_ETM 1
#define SOC_GPIO_ETM_EVENTS_PER_GROUP 8
#define SOC_GPIO_ETM_TASKS_PER_GROUP 8
// Target has the full LP IO subsystem // Target has the full LP IO subsystem
// On ESP32-C5, Digital IOs have their own registers to control pullup/down capability, independent of LP registers. // On ESP32-C5, Digital IOs have their own registers to control pullup/down capability, independent of LP registers.

View File

@ -183,8 +183,6 @@
// GPIO peripheral has the ETM extension // GPIO peripheral has the ETM extension
// #define SOC_GPIO_SUPPORT_ETM 1 // #define SOC_GPIO_SUPPORT_ETM 1
// #define SOC_GPIO_ETM_EVENTS_PER_GROUP 8
// #define SOC_GPIO_ETM_TASKS_PER_GROUP 8
// Target has the full LP IO subsystem // Target has the full LP IO subsystem
// On ESP32-C5, Digital IOs have their own registers to control pullup/down capability, independent of LP registers. // On ESP32-C5, Digital IOs have their own registers to control pullup/down capability, independent of LP registers.

View File

@ -471,14 +471,6 @@ config SOC_GPIO_SUPPORT_ETM
bool bool
default y default y
config SOC_GPIO_ETM_EVENTS_PER_GROUP
int
default 8
config SOC_GPIO_ETM_TASKS_PER_GROUP
int
default 8
config SOC_GPIO_SUPPORT_RTC_INDEPENDENT config SOC_GPIO_SUPPORT_RTC_INDEPENDENT
bool bool
default y default y

View File

@ -186,8 +186,6 @@
// GPIO peripheral has the ETM extension // GPIO peripheral has the ETM extension
#define SOC_GPIO_SUPPORT_ETM 1 #define SOC_GPIO_SUPPORT_ETM 1
#define SOC_GPIO_ETM_EVENTS_PER_GROUP 8
#define SOC_GPIO_ETM_TASKS_PER_GROUP 8
// Target has the full LP IO subsystem // Target has the full LP IO subsystem
// On ESP32-C6, Digital IOs have their own registers to control pullup/down capability, independent of LP registers. // On ESP32-C6, Digital IOs have their own registers to control pullup/down capability, independent of LP registers.

View File

@ -163,14 +163,6 @@ config SOC_GPIO_PIN_COUNT
int int
default 25 default 25
config SOC_GPIO_ETM_EVENTS_PER_GROUP
int
default 8
config SOC_GPIO_ETM_TASKS_PER_GROUP
int
default 8
config SOC_GPIO_SUPPORT_RTC_INDEPENDENT config SOC_GPIO_SUPPORT_RTC_INDEPENDENT
bool bool
default y default y

View File

@ -187,8 +187,6 @@
// GPIO peripheral has the ETM extension // GPIO peripheral has the ETM extension
// #define SOC_GPIO_SUPPORT_ETM 1 //TODO: [ESP32C61] IDF-9340 // #define SOC_GPIO_SUPPORT_ETM 1 //TODO: [ESP32C61] IDF-9340
#define SOC_GPIO_ETM_EVENTS_PER_GROUP 8
#define SOC_GPIO_ETM_TASKS_PER_GROUP 8
// Target has the full LP IO subsystem // Target has the full LP IO subsystem
// On ESP32-C61, Digital IOs have their own registers to control pullup/down capability, independent of LP registers. // On ESP32-C61, Digital IOs have their own registers to control pullup/down capability, independent of LP registers.

View File

@ -483,14 +483,6 @@ config SOC_GPIO_SUPPORT_ETM
bool bool
default y default y
config SOC_GPIO_ETM_EVENTS_PER_GROUP
int
default 8
config SOC_GPIO_ETM_TASKS_PER_GROUP
int
default 8
config SOC_GPIO_SUPPORT_RTC_INDEPENDENT config SOC_GPIO_SUPPORT_RTC_INDEPENDENT
bool bool
default y default y

View File

@ -190,8 +190,6 @@
// GPIO peripheral has the ETM extension // GPIO peripheral has the ETM extension
#define SOC_GPIO_SUPPORT_ETM 1 #define SOC_GPIO_SUPPORT_ETM 1
#define SOC_GPIO_ETM_EVENTS_PER_GROUP 8
#define SOC_GPIO_ETM_TASKS_PER_GROUP 8
// Target has no full LP IO subsystem, GPIO7~14 remain LP function (powered by VDD3V3_LP, and can be used as ext1 wakeup pins) // Target has no full LP IO subsystem, GPIO7~14 remain LP function (powered by VDD3V3_LP, and can be used as ext1 wakeup pins)
// Digital IOs have their own registers to control pullup/down/capability // Digital IOs have their own registers to control pullup/down/capability

View File

@ -519,14 +519,6 @@ config SOC_GPIO_SUPPORT_ETM
bool bool
default y default y
config SOC_GPIO_ETM_EVENTS_PER_GROUP
int
default 8
config SOC_GPIO_ETM_TASKS_PER_GROUP
int
default 8
config SOC_GPIO_SUPPORT_RTC_INDEPENDENT config SOC_GPIO_SUPPORT_RTC_INDEPENDENT
bool bool
default y default y

View File

@ -211,8 +211,6 @@
// GPIO peripheral has the ETM extension // GPIO peripheral has the ETM extension
#define SOC_GPIO_SUPPORT_ETM 1 #define SOC_GPIO_SUPPORT_ETM 1
#define SOC_GPIO_ETM_EVENTS_PER_GROUP 8
#define SOC_GPIO_ETM_TASKS_PER_GROUP 8
// Target has the full LP IO subsystem // Target has the full LP IO subsystem
// On ESP32-P4, Digital IOs have their own registers to control pullup/down capability, independent of LP registers. // On ESP32-P4, Digital IOs have their own registers to control pullup/down capability, independent of LP registers.

View File

@ -56,7 +56,7 @@ GPIO Events
GPIO **edge** event is the most common event type, it can be generated by any GPIO pin. You can call :cpp:func:`gpio_new_etm_event` to create a GPIO event handle, with the configurations provided in :cpp:type:`gpio_etm_event_config_t`: GPIO **edge** event is the most common event type, it can be generated by any GPIO pin. You can call :cpp:func:`gpio_new_etm_event` to create a GPIO event handle, with the configurations provided in :cpp:type:`gpio_etm_event_config_t`:
- :cpp:member:`gpio_etm_event_config_t::edge` decides which edge to trigger the event, supported edge types are listed in the :cpp:type:`gpio_etm_event_edge_t`. - :cpp:member:`gpio_etm_event_config_t::edge` or :cpp:member:`gpio_etm_event_config_t::edges` decides which edge(s) to trigger the event(s), supported edge types are listed in the :cpp:type:`gpio_etm_event_edge_t`.
You need to build a connection between the GPIO ETM event handle and the GPIO number. So you should call :cpp:func:`gpio_etm_event_bind_gpio` afterwards. Please note, only the ETM event handle that created by :cpp:func:`gpio_new_etm_event` can set a GPIO number. Calling this function with other kinds of ETM events returns :c:macro:`ESP_ERR_INVALID_ARG` error. Needless to say, this function does not help with the GPIO initialization, you still need to call :cpp:func:`gpio_config` to set the property like direction, pull up/down mode separately. You need to build a connection between the GPIO ETM event handle and the GPIO number. So you should call :cpp:func:`gpio_etm_event_bind_gpio` afterwards. Please note, only the ETM event handle that created by :cpp:func:`gpio_new_etm_event` can set a GPIO number. Calling this function with other kinds of ETM events returns :c:macro:`ESP_ERR_INVALID_ARG` error. Needless to say, this function does not help with the GPIO initialization, you still need to call :cpp:func:`gpio_config` to set the property like direction, pull up/down mode separately.
@ -83,9 +83,9 @@ ETM Task abstracts the task action and is represented by :cpp:type:`esp_etm_task
GPIO Tasks GPIO Tasks
~~~~~~~~~~ ~~~~~~~~~~
GPIO task is the most common task type, one GPIO task can even manage multiple GPIOs. When the task gets activated by the ETM channel, all managed GPIOs can set/clear/toggle at the same time. You can call :cpp:func:`gpio_new_etm_task` to create a GPIO task handle, with the configurations provided in :cpp:type:`gpio_etm_task_config_t`: GPIO task is the most common task type. One GPIO can take one or more GPIO ETM task actions, and one GPIO ETM task action can even manage multiple GPIOs. When the task gets activated by the ETM channel, all managed GPIOs can set/clear/toggle at the same time. You can call :cpp:func:`gpio_new_etm_task` to create a GPIO task handle, with the configurations provided in :cpp:type:`gpio_etm_task_config_t`:
- :cpp:member:`gpio_etm_task_config_t::action` decides what GPIO action would be taken by the ETM task. Supported actions are listed in the :cpp:type:`gpio_etm_task_action_t`. - :cpp:member:`gpio_etm_task_config_t::action` or :cpp:member:`gpio_etm_task_config_t::actions` decides what GPIO action(s) would be taken by the ETM task. Supported actions are listed in the :cpp:type:`gpio_etm_task_action_t`. If one GPIO needs to take more than one actions, the action tasks have to be created in one :cpp:func:`gpio_new_etm_task` call with filling the actions into the array of :cpp:member:`gpio_etm_task_config_t::actions`.
To build a connection between the GPIO ETM task and the GPIO number, you should call :cpp:func:`gpio_etm_task_add_gpio`. You can call this function by several times if you want the task handle to manage more GPIOs. Please note, only the ETM task handle that created by :cpp:func:`gpio_new_etm_task` can manage a GPIO. Calling this function with other kinds of ETM tasks returns :c:macro:`ESP_ERR_INVALID_ARG` error. Needless to say, this function does not help with the GPIO initialization, you still need to call :cpp:func:`gpio_config` to set the property like direction, pull up/down mode separately. To build a connection between the GPIO ETM task and the GPIO number, you should call :cpp:func:`gpio_etm_task_add_gpio`. You can call this function by several times if you want the task handle to manage more GPIOs. Please note, only the ETM task handle that created by :cpp:func:`gpio_new_etm_task` can manage a GPIO. Calling this function with other kinds of ETM tasks returns :c:macro:`ESP_ERR_INVALID_ARG` error. Needless to say, this function does not help with the GPIO initialization, you still need to call :cpp:func:`gpio_config` to set the property like direction, pull up/down mode separately.

View File

@ -56,7 +56,7 @@ GPIO 事件
GPIO **边沿** 事件是最常见的事件类型,任何 GPIO 管脚均可触发这类事件。要创建 GPIO 事件句柄,请调用 :cpp:func:`gpio_new_etm_event`,并使用 :cpp:type:`gpio_etm_event_config_t` 提供的配置信息: GPIO **边沿** 事件是最常见的事件类型,任何 GPIO 管脚均可触发这类事件。要创建 GPIO 事件句柄,请调用 :cpp:func:`gpio_new_etm_event`,并使用 :cpp:type:`gpio_etm_event_config_t` 提供的配置信息:
- :cpp:member:`gpio_etm_event_config_t::edge` 决定触发事件的边沿类型,支持的边沿类型已在 :cpp:type:`gpio_etm_event_edge_t` 中列出。 - :cpp:member:`gpio_etm_event_config_t::edge` :cpp:member:`gpio_etm_event_config_t::edges` 决定触发事件的边沿类型,支持的边沿类型已在 :cpp:type:`gpio_etm_event_edge_t` 中列出。
接下来,请调用 :cpp:func:`gpio_etm_event_bind_gpio` 函数,连接 GPIO ETM 事件句柄与 GPIO 管脚。注意,要设置 GPIO 管脚,只能使用由 :cpp:func:`gpio_new_etm_event` 函数创建的 ETM 事件句柄。对于其他类型的 ETM 事件,调用此函数,将返回 :c:macro:`ESP_ERR_INVALID_ARG` 错误。该函数也无法完成 GPIO 的初始化,在使用 GPIO ETM 事件之前,仍需调用 :cpp:func:`gpio_config` 函数,设置 GPIO 管脚的属性,如方向、高/低电平模式等。 接下来,请调用 :cpp:func:`gpio_etm_event_bind_gpio` 函数,连接 GPIO ETM 事件句柄与 GPIO 管脚。注意,要设置 GPIO 管脚,只能使用由 :cpp:func:`gpio_new_etm_event` 函数创建的 ETM 事件句柄。对于其他类型的 ETM 事件,调用此函数,将返回 :c:macro:`ESP_ERR_INVALID_ARG` 错误。该函数也无法完成 GPIO 的初始化,在使用 GPIO ETM 事件之前,仍需调用 :cpp:func:`gpio_config` 函数,设置 GPIO 管脚的属性,如方向、高/低电平模式等。
@ -82,9 +82,9 @@ ETM 任务对其操作进行了抽象,在软件中表示为 :cpp:type:`esp_etm
GPIO 任务 GPIO 任务
~~~~~~~~~~ ~~~~~~~~~~
GPIO 任务是最常见的任务类型,一个 GPIO 任务可以同时管理多个 GPIO 管脚。当 ETM 通道激活任务时,任务可以同时设置管理的所有 GPIO 引脚,使其设置/清除/切换状态。要创建 GPIO 任务句柄,请调用 :cpp:func:`gpio_new_etm_task`,并使用 :cpp:type:`gpio_etm_task_config_t` 提供的配置信息: GPIO 任务是最常见的任务类型。一个 GPIO 可以采取一个或多个 GPIO 操作一个 GPIO 任务可以同时管理多个 GPIO 管脚。当 ETM 通道激活任务时,任务可以同时设置管理的所有 GPIO 引脚,使其设置/清除/切换状态。要创建 GPIO 任务句柄,请调用 :cpp:func:`gpio_new_etm_task`,并使用 :cpp:type:`gpio_etm_task_config_t` 提供的配置信息:
- :cpp:member:`gpio_etm_task_config_t::action` 决定 ETM 任务将采取的 GPIO 操作,支持的操作类型在 :cpp:type:`gpio_etm_task_action_t` 中列出。 - :cpp:member:`gpio_etm_task_config_t::action` :cpp:member:`gpio_etm_task_config_t::actions` 决定 ETM 任务将采取的 GPIO 操作,支持的操作类型在 :cpp:type:`gpio_etm_task_action_t` 中列出。如果一个 GPIO 需要采取多个 GPIO 操作,这些操作任务的创建必须通过配置 :cpp:member:`gpio_etm_task_config_t::actions` 的数组并在一次 :cpp:func:`gpio_new_etm_task` 调用中一并完成。
接下来,需要连接 GPIO ETM 任务句柄与 GPIO 管脚。为此,请调用 :cpp:func:`gpio_etm_task_add_gpio` 函数。如果需要任务句柄管理更多的 GPIO 管脚,可以重复调用以上函数,注意,要设置 GPIO 管脚,只能使用由 :cpp:func:`gpio_new_etm_task` 函数创建的 ETM 任务句柄。对于其他类型的 ETM 任务,调用此函数,将返回 :c:macro:`ESP_ERR_INVALID_ARG` 错误。该函数也无法完成 GPIO 的初始化,在使用 GPIO ETM 任务之前,仍需调用 :cpp:func:`gpio_config` 函数,设置 GPIO 管脚的属性,如方向、高/低电平模式等。 接下来,需要连接 GPIO ETM 任务句柄与 GPIO 管脚。为此,请调用 :cpp:func:`gpio_etm_task_add_gpio` 函数。如果需要任务句柄管理更多的 GPIO 管脚,可以重复调用以上函数,注意,要设置 GPIO 管脚,只能使用由 :cpp:func:`gpio_new_etm_task` 函数创建的 ETM 任务句柄。对于其他类型的 ETM 任务,调用此函数,将返回 :c:macro:`ESP_ERR_INVALID_ARG` 错误。该函数也无法完成 GPIO 的初始化,在使用 GPIO ETM 任务之前,仍需调用 :cpp:func:`gpio_config` 函数,设置 GPIO 管脚的属性,如方向、高/低电平模式等。

View File

@ -26,17 +26,19 @@ static void example_etm_bind_ana_cmpr_event_with_gpio_task(ana_cmpr_handle_t cmp
.event_type = ANA_CMPR_EVENT_POS_CROSS, .event_type = ANA_CMPR_EVENT_POS_CROSS,
}; };
ESP_ERROR_CHECK(ana_cmpr_new_etm_event(cmpr, &evt_cfg, &cmpr_pos_evt)); ESP_ERROR_CHECK(ana_cmpr_new_etm_event(cmpr, &evt_cfg, &cmpr_pos_evt));
evt_cfg.event_type = GPIO_ETM_EVENT_EDGE_NEG; evt_cfg.event_type = ANA_CMPR_EVENT_NEG_CROSS;
ESP_ERROR_CHECK(ana_cmpr_new_etm_event(cmpr, &evt_cfg, &cmpr_neg_evt)); ESP_ERROR_CHECK(ana_cmpr_new_etm_event(cmpr, &evt_cfg, &cmpr_neg_evt));
/* Allocate the GPIO set & clear tasks */ /* Allocate the GPIO set & clear tasks */
esp_etm_task_handle_t gpio_toggle_task = NULL; esp_etm_task_handle_t gpio_set_task = NULL;
gpio_etm_task_config_t task_cfg = { esp_etm_task_handle_t gpio_clr_task = NULL;
.action = GPIO_ETM_TASK_ACTION_TOG, gpio_etm_task_config_t task_cfg = {};
}; task_cfg.actions[0] = GPIO_ETM_TASK_ACTION_SET;
ESP_ERROR_CHECK(gpio_new_etm_task(&task_cfg, &gpio_toggle_task)); task_cfg.actions[1] = GPIO_ETM_TASK_ACTION_CLR;
ESP_ERROR_CHECK(gpio_new_etm_task(&task_cfg, &gpio_set_task, &gpio_clr_task));
/* Add task to the monitor GPIO */ /* Add task to the monitor GPIO */
ESP_ERROR_CHECK(gpio_etm_task_add_gpio(gpio_toggle_task, EXAMPLE_MONITOR_GPIO_NUM)); ESP_ERROR_CHECK(gpio_etm_task_add_gpio(gpio_set_task, EXAMPLE_MONITOR_GPIO_NUM));
ESP_ERROR_CHECK(gpio_etm_task_add_gpio(gpio_clr_task, EXAMPLE_MONITOR_GPIO_NUM));
/* Allocate the Event Task Matrix channels */ /* Allocate the Event Task Matrix channels */
esp_etm_channel_handle_t etm_pos_handle; esp_etm_channel_handle_t etm_pos_handle;
@ -45,8 +47,8 @@ static void example_etm_bind_ana_cmpr_event_with_gpio_task(ana_cmpr_handle_t cmp
ESP_ERROR_CHECK(esp_etm_new_channel(&etm_cfg, &etm_pos_handle)); ESP_ERROR_CHECK(esp_etm_new_channel(&etm_cfg, &etm_pos_handle));
ESP_ERROR_CHECK(esp_etm_new_channel(&etm_cfg, &etm_neg_handle)); ESP_ERROR_CHECK(esp_etm_new_channel(&etm_cfg, &etm_neg_handle));
/* Bind the events and tasks */ /* Bind the events and tasks */
ESP_ERROR_CHECK(esp_etm_channel_connect(etm_pos_handle, cmpr_pos_evt, gpio_toggle_task)); ESP_ERROR_CHECK(esp_etm_channel_connect(etm_pos_handle, cmpr_pos_evt, gpio_set_task));
ESP_ERROR_CHECK(esp_etm_channel_connect(etm_neg_handle, cmpr_neg_evt, gpio_toggle_task)); ESP_ERROR_CHECK(esp_etm_channel_connect(etm_neg_handle, cmpr_neg_evt, gpio_clr_task));
/* Enable the ETM channels */ /* Enable the ETM channels */
ESP_ERROR_CHECK(esp_etm_channel_enable(etm_pos_handle)); ESP_ERROR_CHECK(esp_etm_channel_enable(etm_pos_handle));
ESP_ERROR_CHECK(esp_etm_channel_enable(etm_neg_handle)); ESP_ERROR_CHECK(esp_etm_channel_enable(etm_neg_handle));