mirror of
https://github.com/espressif/esp-idf.git
synced 2024-10-05 20:47:46 -04:00
Merge branch 'bugfix/fix_gpio_etm_multi_task_v5.1' into 'release/v5.1'
fix(gpio_etm): allow one GPIO binds to multiple ETM tasks (v5.1) See merge request espressif/esp-idf!30457
This commit is contained in:
commit
9808619d52
@ -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
|
||||
*/
|
||||
@ -34,19 +34,23 @@ typedef struct gpio_etm_event_t gpio_etm_event_t;
|
||||
typedef struct gpio_etm_group_t {
|
||||
portMUX_TYPE spinlock;
|
||||
gpio_etm_dev_t *dev;
|
||||
gpio_etm_task_t *tasks[SOC_GPIO_ETM_TASKS_PER_GROUP];
|
||||
gpio_etm_event_t *events[SOC_GPIO_ETM_EVENTS_PER_GROUP];
|
||||
uint8_t tasks[GPIO_LL_ETM_TASK_CHANNELS_PER_GROUP]; // Array of the acquired action masks in each GPIO ETM task channel
|
||||
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;
|
||||
|
||||
struct gpio_etm_event_t {
|
||||
esp_etm_event_t base;
|
||||
int chan_id;
|
||||
gpio_etm_event_edge_t edge_id;
|
||||
gpio_etm_group_t *group;
|
||||
};
|
||||
|
||||
struct gpio_etm_task_t {
|
||||
esp_etm_task_t base;
|
||||
int chan_id;
|
||||
gpio_etm_task_action_t action_id;
|
||||
gpio_etm_group_t *group;
|
||||
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,
|
||||
};
|
||||
|
||||
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;
|
||||
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);
|
||||
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]) {
|
||||
chan_id = j;
|
||||
group->events[j] = event;
|
||||
free_chan_id = j;
|
||||
group->events[j] = event_mask;
|
||||
break;
|
||||
}
|
||||
}
|
||||
portEXIT_CRITICAL(&group->spinlock);
|
||||
|
||||
ESP_RETURN_ON_FALSE(chan_id != -1, ESP_ERR_NOT_FOUND, TAG, "no free event channel");
|
||||
event->group = group;
|
||||
event->chan_id = chan_id;
|
||||
ESP_RETURN_ON_FALSE(free_chan_id != -1, ESP_ERR_NOT_FOUND, TAG, "no free event channel");
|
||||
*chan_id = free_chan_id;
|
||||
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;
|
||||
int chan_id = -1;
|
||||
// loop to search free one in the group
|
||||
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]) {
|
||||
chan_id = j;
|
||||
group->tasks[j] = task;
|
||||
free_chan_id = j;
|
||||
group->tasks[j] = task_mask;
|
||||
break;
|
||||
}
|
||||
}
|
||||
portEXIT_CRITICAL(&group->spinlock);
|
||||
|
||||
ESP_RETURN_ON_FALSE(chan_id != -1, ESP_ERR_NOT_FOUND, TAG, "no free task channel");
|
||||
task->group = group;
|
||||
task->chan_id = chan_id;
|
||||
ESP_RETURN_ON_FALSE(free_chan_id != -1, ESP_ERR_NOT_FOUND, TAG, "no free task channel");
|
||||
*chan_id = free_chan_id;
|
||||
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;
|
||||
int chan_id = event->chan_id;
|
||||
gpio_etm_group_t *group = &s_gpio_etm_group;
|
||||
portENTER_CRITICAL(&group->spinlock);
|
||||
group->events[chan_id] = NULL;
|
||||
group->tasks[chan_id] &= ~task_mask;
|
||||
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;
|
||||
}
|
||||
|
||||
@ -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_group_t *group = gpio_event->group;
|
||||
// disable event channel
|
||||
gpio_ll_etm_enable_event_channel(group->dev, gpio_event->chan_id, false);
|
||||
gpio_etm_event_destroy(gpio_event);
|
||||
// unbind it from the GPIO and check if the GPIO ETM event channel can be disabled
|
||||
portENTER_CRITICAL(&group->spinlock);
|
||||
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;
|
||||
}
|
||||
|
||||
@ -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);
|
||||
// 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");
|
||||
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;
|
||||
}
|
||||
|
||||
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
|
||||
esp_log_level_set(TAG, ESP_LOG_DEBUG);
|
||||
#endif
|
||||
esp_err_t ret = ESP_OK;
|
||||
gpio_etm_event_t *event = NULL;
|
||||
ESP_GOTO_ON_FALSE(config && ret_event, ESP_ERR_INVALID_ARG, err, TAG, "invalid argument");
|
||||
int chan_id = -1;
|
||||
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);
|
||||
ESP_GOTO_ON_FALSE(event, ESP_ERR_NO_MEM, err, TAG, "no mem for event channel");
|
||||
// register the event channel to the group
|
||||
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");
|
||||
for (int i = 0; i < event_num; i++) {
|
||||
events[i] = (gpio_etm_event_t *)heap_caps_calloc(1, sizeof(gpio_etm_event_t), ETM_MEM_ALLOC_CAPS);
|
||||
ESP_GOTO_ON_FALSE(events[i], ESP_ERR_NO_MEM, err, TAG, "no mem for event channel(s)");
|
||||
}
|
||||
|
||||
event->base.del = gpio_del_etm_event;
|
||||
event->base.event_id = event_id;
|
||||
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);
|
||||
*ret_event = &event->base;
|
||||
// register the event channel to the group
|
||||
ESP_GOTO_ON_ERROR(gpio_etm_acquire_event_channel(event_mask, &chan_id), err, TAG, "register event channel to group failed");
|
||||
|
||||
bool no_avail_ret_arg = false;
|
||||
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;
|
||||
|
||||
err:
|
||||
if (event) {
|
||||
gpio_etm_event_destroy(event);
|
||||
if (chan_id != -1) {
|
||||
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;
|
||||
}
|
||||
|
||||
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
|
||||
esp_log_level_set(TAG, ESP_LOG_DEBUG);
|
||||
#endif
|
||||
esp_err_t ret = ESP_OK;
|
||||
gpio_etm_task_t *task = NULL;
|
||||
ESP_GOTO_ON_FALSE(config && ret_task, ESP_ERR_INVALID_ARG, err, TAG, "invalid argument");
|
||||
int chan_id = -1;
|
||||
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);
|
||||
ESP_GOTO_ON_FALSE(task, ESP_ERR_NO_MEM, err, TAG, "no mem for task channel");
|
||||
// register the task channel to the group
|
||||
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");
|
||||
for (int i = 0; i < task_num; i++) {
|
||||
tasks[i] = (gpio_etm_task_t *)heap_caps_calloc(1, sizeof(gpio_etm_task_t), ETM_MEM_ALLOC_CAPS);
|
||||
ESP_GOTO_ON_FALSE(tasks[i], ESP_ERR_NO_MEM, err, TAG, "no mem for task channel(s)");
|
||||
}
|
||||
|
||||
task->base.del = gpio_del_etm_task;
|
||||
task->base.task_id = task_id;
|
||||
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);
|
||||
*ret_task = &task->base;
|
||||
// register the task channel to the group
|
||||
ESP_GOTO_ON_ERROR(gpio_etm_acquire_task_channel(task_mask, &chan_id), err, TAG, "register task channel to group failed");
|
||||
|
||||
bool no_avail_ret_arg = false;
|
||||
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;
|
||||
|
||||
err:
|
||||
if (task) {
|
||||
gpio_etm_task_destroy(task);
|
||||
if (chan_id != -1) {
|
||||
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;
|
||||
}
|
||||
|
||||
@ -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");
|
||||
gpio_etm_event_t *gpio_event = __containerof(event, gpio_etm_event_t, base);
|
||||
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);
|
||||
// then set the gpio number
|
||||
gpio_ll_etm_event_channel_set_gpio(group->dev, gpio_event->chan_id, gpio_num);
|
||||
// enable gpio etm event channel again
|
||||
gpio_ll_etm_enable_event_channel(group->dev, gpio_event->chan_id, true);
|
||||
|
||||
bool allowed = true;
|
||||
portENTER_CRITICAL(&group->spinlock);
|
||||
// check if the GPIO ETM event channel where the new event belongs to has previously been bound to another GPIO
|
||||
// one GPIO ETM event channel can only be bound to one GPIO
|
||||
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;
|
||||
}
|
||||
|
||||
@ -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");
|
||||
gpio_etm_task_t *gpio_task = __containerof(task, gpio_etm_task_t, base);
|
||||
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
|
||||
// and the gpio_num might reside in the same register
|
||||
portENTER_CRITICAL(&group->spinlock);
|
||||
// check if the gpio has been enabled
|
||||
if (!gpio_ll_etm_is_task_gpio_enabled(group->dev, gpio_num)) {
|
||||
// check if the new task is compatible with the tasks that has previously been added to the GPIO
|
||||
// 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_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);
|
||||
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++;
|
||||
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");
|
||||
gpio_etm_task_t *gpio_task = __containerof(task, gpio_etm_task_t, base);
|
||||
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
|
||||
// and the gpio_num might reside in the same register
|
||||
portENTER_CRITICAL(&group->spinlock);
|
||||
// check if the gpio is managed by this etm task channel
|
||||
if (gpio_ll_etm_is_task_gpio_enabled(group->dev, gpio_num) &&
|
||||
if ((group->actions[gpio_num] & (1 << gpio_task->action_id)) &&
|
||||
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_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 {
|
||||
gpio_enabled_by_this_task = false;
|
||||
allowed = false;
|
||||
}
|
||||
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--;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
@ -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
|
||||
*/
|
||||
@ -13,20 +13,29 @@
|
||||
extern "C" {
|
||||
#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
|
||||
*/
|
||||
typedef enum {
|
||||
GPIO_ETM_EVENT_EDGE_POS, /*!< 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_ANY, /*!< Any edge on the GPIO can 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_ANY, /*!< Any edge on the GPIO can generate an ETM event signal */
|
||||
} gpio_etm_event_edge_t;
|
||||
|
||||
/**
|
||||
* @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 {
|
||||
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;
|
||||
|
||||
/**
|
||||
@ -34,9 +43,11 @@ typedef struct {
|
||||
*
|
||||
* @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 Every success call to this function will acquire a free GPIO ETM event channel
|
||||
*
|
||||
* @param[in] config GPIO ETM event configuration
|
||||
* @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
|
||||
* - ESP_OK: Create ETM event successfully
|
||||
* - 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_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
|
||||
@ -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
|
||||
*/
|
||||
typedef enum {
|
||||
GPIO_ETM_TASK_ACTION_SET, /*!< Set the GPIO level to high */
|
||||
GPIO_ETM_TASK_ACTION_CLR, /*!< Clear the GPIO level to low */
|
||||
GPIO_ETM_TASK_ACTION_TOG, /*!< Toggle the GPIO level */
|
||||
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_TOG, /*!< Toggle the GPIO level */
|
||||
} gpio_etm_task_action_t;
|
||||
|
||||
/**
|
||||
* @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 {
|
||||
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;
|
||||
|
||||
/**
|
||||
@ -83,9 +99,11 @@ typedef struct {
|
||||
* @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.
|
||||
* 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[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
|
||||
* - ESP_OK: Create ETM task successfully
|
||||
* - 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_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.
|
||||
|
@ -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
|
||||
*/
|
||||
@ -81,7 +81,7 @@ TEST_CASE("gpio_etm_self_trigger", "[etm]")
|
||||
// delete gpio etm task without remove all bounded GPIOs should fail
|
||||
TEST_ESP_ERR(ESP_ERR_INVALID_STATE, esp_etm_del_task(gpio_task));
|
||||
// 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
|
||||
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_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));
|
||||
}
|
||||
|
@ -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
|
||||
*/
|
||||
@ -22,15 +22,20 @@
|
||||
#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_EVENT_CHANNELS_PER_GROUP 8
|
||||
#define GPIO_LL_ETM_TASK_CHANNELS_PER_GROUP 8
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#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 chan Channel number
|
||||
* @param chan GPIO ETM Event channel 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)
|
||||
@ -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 chan Channel number
|
||||
* @param chan GPIO ETM Event channel number
|
||||
* @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)
|
||||
@ -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
|
||||
*
|
||||
* @note One channel can be bounded to multiple different GPIOs
|
||||
* @brief Get which GPIO is bound to the event channel
|
||||
*
|
||||
* @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_ch0_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
|
||||
*/
|
||||
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 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 gpio_num GPIO number
|
||||
|
@ -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
|
||||
*/
|
||||
@ -22,15 +22,20 @@
|
||||
#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_EVENT_CHANNELS_PER_GROUP 8
|
||||
#define GPIO_LL_ETM_TASK_CHANNELS_PER_GROUP 8
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#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 chan Channel number
|
||||
* @param chan GPIO ETM Event channel 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)
|
||||
@ -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 chan Channel number
|
||||
* @param chan GPIO ETM Event channel number
|
||||
* @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)
|
||||
@ -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
|
||||
*
|
||||
* @note One channel can be bounded to multiple different GPIOs
|
||||
* @brief Get which GPIO is bound to the event channel
|
||||
*
|
||||
* @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_ch0_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
|
||||
*/
|
||||
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 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 gpio_num GPIO number
|
||||
|
@ -403,14 +403,6 @@ config SOC_GPIO_SUPPORT_ETM
|
||||
bool
|
||||
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
|
||||
bool
|
||||
default y
|
||||
|
@ -175,8 +175,6 @@
|
||||
|
||||
// GPIO peripheral has the ETM extension
|
||||
#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
|
||||
// On ESP32-C6, Digital IOs have their own registers to control pullup/down capability, independent of LP registers.
|
||||
|
@ -423,14 +423,6 @@ config SOC_GPIO_SUPPORT_ETM
|
||||
bool
|
||||
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
|
||||
bool
|
||||
default y
|
||||
|
@ -181,8 +181,6 @@
|
||||
|
||||
// GPIO peripheral has the ETM extension
|
||||
#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)
|
||||
// Digital IOs have their own registers to control pullup/down/capability
|
||||
|
@ -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`:
|
||||
|
||||
- :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.
|
||||
|
||||
@ -80,9 +80,9 @@ ETM Task abstracts the task action and is represented by :cpp:type:`esp_etm_task
|
||||
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.
|
||||
|
||||
|
@ -56,7 +56,7 @@ GPIO 事件
|
||||
|
||||
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 管脚的属性,如方向、高/低电平模式等。
|
||||
|
||||
@ -80,9 +80,9 @@ ETM 任务对其操作进行了抽象,在软件中表示为 :cpp:type:`esp_etm
|
||||
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 管脚的属性,如方向、高/低电平模式等。
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user