From b6de55b6340c1d8315e8afa1744f615a3580effe Mon Sep 17 00:00:00 2001 From: laokaiyao Date: Tue, 9 Jul 2024 19:44:52 +0800 Subject: [PATCH 1/3] feat(i2s): support i2s etm event and task --- components/soc/esp32c5/include/soc/Kconfig.soc_caps.in | 4 ++++ components/soc/esp32c5/include/soc/soc_caps.h | 1 + components/soc/esp32c61/include/soc/Kconfig.soc_caps.in | 4 ++++ components/soc/esp32c61/include/soc/soc_caps.h | 1 + components/soc/esp32p4/include/soc/Kconfig.soc_caps.in | 4 ++++ components/soc/esp32p4/include/soc/soc_caps.h | 1 + 6 files changed, 15 insertions(+) diff --git a/components/soc/esp32c5/include/soc/Kconfig.soc_caps.in b/components/soc/esp32c5/include/soc/Kconfig.soc_caps.in index 92e7392889e..5e1f813b1f9 100644 --- a/components/soc/esp32c5/include/soc/Kconfig.soc_caps.in +++ b/components/soc/esp32c5/include/soc/Kconfig.soc_caps.in @@ -415,6 +415,10 @@ config SOC_ETM_CHANNELS_PER_GROUP int default 50 +config SOC_ETM_SUPPORT_STATUS + bool + default y + config SOC_GPIO_PORT int default 1 diff --git a/components/soc/esp32c5/include/soc/soc_caps.h b/components/soc/esp32c5/include/soc/soc_caps.h index 297c43f8e43..3e65dba720d 100644 --- a/components/soc/esp32c5/include/soc/soc_caps.h +++ b/components/soc/esp32c5/include/soc/soc_caps.h @@ -187,6 +187,7 @@ /*-------------------------- ETM CAPS --------------------------------------*/ #define SOC_ETM_GROUPS 1U // Number of ETM groups #define SOC_ETM_CHANNELS_PER_GROUP 50 // Number of ETM channels in the group +#define SOC_ETM_SUPPORT_STATUS 1 // Support to get and clear the status of the ETM event and task /*-------------------------- GPIO CAPS ---------------------------------------*/ // ESP32-C5 has 1 GPIO peripheral diff --git a/components/soc/esp32c61/include/soc/Kconfig.soc_caps.in b/components/soc/esp32c61/include/soc/Kconfig.soc_caps.in index 5f96bc26840..e6c3141f6ab 100644 --- a/components/soc/esp32c61/include/soc/Kconfig.soc_caps.in +++ b/components/soc/esp32c61/include/soc/Kconfig.soc_caps.in @@ -187,6 +187,10 @@ config SOC_ETM_CHANNELS_PER_GROUP int default 50 +config SOC_ETM_SUPPORT_STATUS + bool + default y + config SOC_GPIO_PORT int default 1 diff --git a/components/soc/esp32c61/include/soc/soc_caps.h b/components/soc/esp32c61/include/soc/soc_caps.h index 69b3dfe8a4b..2789c9682a3 100644 --- a/components/soc/esp32c61/include/soc/soc_caps.h +++ b/components/soc/esp32c61/include/soc/soc_caps.h @@ -177,6 +177,7 @@ /*-------------------------- ETM CAPS --------------------------------------*/ #define SOC_ETM_GROUPS 1U // Number of ETM groups #define SOC_ETM_CHANNELS_PER_GROUP 50 // Number of ETM channels in the group +#define SOC_ETM_SUPPORT_STATUS 1 // Support to get and clear the status of the ETM event and task /*-------------------------- GPIO CAPS ---------------------------------------*/ // ESP32-C61 has 1 GPIO peripheral diff --git a/components/soc/esp32p4/include/soc/Kconfig.soc_caps.in b/components/soc/esp32p4/include/soc/Kconfig.soc_caps.in index fd4fb65c29b..bfd57ec5636 100644 --- a/components/soc/esp32p4/include/soc/Kconfig.soc_caps.in +++ b/components/soc/esp32p4/include/soc/Kconfig.soc_caps.in @@ -575,6 +575,10 @@ config SOC_ETM_CHANNELS_PER_GROUP int default 50 +config SOC_ETM_SUPPORT_STATUS + bool + default y + config SOC_GPIO_PORT int default 1 diff --git a/components/soc/esp32p4/include/soc/soc_caps.h b/components/soc/esp32p4/include/soc/soc_caps.h index 0dce165cf34..26837019755 100644 --- a/components/soc/esp32p4/include/soc/soc_caps.h +++ b/components/soc/esp32p4/include/soc/soc_caps.h @@ -219,6 +219,7 @@ /*-------------------------- ETM CAPS --------------------------------------*/ #define SOC_ETM_GROUPS 1U // Number of ETM groups #define SOC_ETM_CHANNELS_PER_GROUP 50 // Number of ETM channels in the group +#define SOC_ETM_SUPPORT_STATUS 1 // Support to get and clear the status of the ETM event and task /*-------------------------- GPIO CAPS ---------------------------------------*/ // ESP32-P4 has 1 GPIO peripheral From a51b5dbe78ed5990e9c2e89c4fed639d5f0a2588 Mon Sep 17 00:00:00 2001 From: laokaiyao Date: Wed, 24 Jul 2024 15:28:08 +0800 Subject: [PATCH 2/3] refactor(i2s): refactor to use i2s etm controlling --- components/esp_driver_i2s/i2s_common.c | 16 +++-- components/esp_driver_i2s/i2s_etm.c | 46 +++++++++--- components/esp_driver_i2s/i2s_private.h | 2 + .../test_apps/i2s/main/CMakeLists.txt | 2 +- .../test_apps/i2s/main/test_i2s_etm.c | 72 ++++++++++++------- .../esp32c5/include/soc/Kconfig.soc_caps.in | 4 -- components/soc/esp32c5/include/soc/soc_caps.h | 1 - .../esp32c61/include/soc/Kconfig.soc_caps.in | 4 -- .../soc/esp32c61/include/soc/soc_caps.h | 1 - .../esp32p4/include/soc/Kconfig.soc_caps.in | 4 -- components/soc/esp32p4/include/soc/soc_caps.h | 1 - 11 files changed, 100 insertions(+), 53 deletions(-) diff --git a/components/esp_driver_i2s/i2s_common.c b/components/esp_driver_i2s/i2s_common.c index 407abb042a4..cae8b507080 100644 --- a/components/esp_driver_i2s/i2s_common.c +++ b/components/esp_driver_i2s/i2s_common.c @@ -97,7 +97,9 @@ static void i2s_tx_channel_start(i2s_chan_handle_t handle) i2s_hal_tx_enable_dma(&(handle->controller->hal)); i2s_hal_tx_start_link(&(handle->controller->hal), (uint32_t) handle->dma.desc[0]); #endif - i2s_hal_tx_start(&(handle->controller->hal)); + if (!handle->is_etm_start) { + i2s_hal_tx_start(&(handle->controller->hal)); + } } static void i2s_rx_channel_start(i2s_chan_handle_t handle) @@ -117,12 +119,16 @@ static void i2s_rx_channel_start(i2s_chan_handle_t handle) i2s_hal_rx_enable_dma(&(handle->controller->hal)); i2s_hal_rx_start_link(&(handle->controller->hal), (uint32_t) handle->dma.desc[0]); #endif - i2s_hal_rx_start(&(handle->controller->hal)); + if (!handle->is_etm_start) { + i2s_hal_rx_start(&(handle->controller->hal)); + } } static void i2s_tx_channel_stop(i2s_chan_handle_t handle) { - i2s_hal_tx_stop(&(handle->controller->hal)); + if (!handle->is_etm_stop) { + i2s_hal_tx_stop(&(handle->controller->hal)); + } #if SOC_GDMA_SUPPORTED gdma_stop(handle->dma.dma_chan); #else @@ -135,7 +141,9 @@ static void i2s_tx_channel_stop(i2s_chan_handle_t handle) static void i2s_rx_channel_stop(i2s_chan_handle_t handle) { - i2s_hal_rx_stop(&(handle->controller->hal)); + if (!handle->is_etm_stop) { + i2s_hal_rx_stop(&(handle->controller->hal)); + } #if SOC_GDMA_SUPPORTED gdma_stop(handle->dma.dma_chan); #else diff --git a/components/esp_driver_i2s/i2s_etm.c b/components/esp_driver_i2s/i2s_etm.c index 8fb5083477a..c73058a4c7a 100644 --- a/components/esp_driver_i2s/i2s_etm.c +++ b/components/esp_driver_i2s/i2s_etm.c @@ -21,15 +21,29 @@ static const char *TAG = "i2s-etm"; -static esp_err_t i2s_del_etm_event(esp_etm_event_t *event) +typedef struct { + esp_etm_task_t base; /*!< Base ETM task object */ + i2s_chan_handle_t handle; /*!< I2S channel handle of this etm task */ + i2s_etm_task_type_t task_type; /*!< I2S ETM task type */ +} i2s_etm_task_t; + +static esp_err_t s_i2s_del_etm_event(esp_etm_event_t *event) { free(event); return ESP_OK; } -static esp_err_t i2s_del_etm_task(esp_etm_task_t *task) +static esp_err_t s_i2s_del_etm_task(esp_etm_task_t *task) { - free(task); + i2s_etm_task_t *i2s_task = __containerof(task, i2s_etm_task_t, base); + if (i2s_task->task_type == I2S_ETM_TASK_START) { + // The i2s start no longer be controlled by etm + i2s_task->handle->is_etm_start = false; + } else { + // The i2s stop no longer be controlled by etm + i2s_task->handle->is_etm_stop = false; + } + free(i2s_task); return ESP_OK; } @@ -41,8 +55,11 @@ esp_err_t i2s_new_etm_event(i2s_chan_handle_t handle, const i2s_etm_event_config esp_etm_event_t *event = heap_caps_calloc(1, sizeof(esp_etm_event_t), ETM_MEM_ALLOC_CAPS); ESP_RETURN_ON_FALSE(event, ESP_ERR_NO_MEM, TAG, "no memory for ETM event"); + // Get the event id from the I2S ETM event table uint32_t event_id = I2S_LL_ETM_EVENT_TABLE(handle->controller->id, handle->dir, config->event_type); + // If the event type is threshold, set the threshold to the hardware if (config->event_type == I2S_ETM_EVENT_REACH_THRESH) { + // Check if the threshold within the supported range ESP_GOTO_ON_FALSE(config->threshold <= I2S_LL_ETM_MAX_THRESH_NUM, ESP_ERR_INVALID_ARG, err, TAG, "exceed the max threshold %"PRIu32, (uint32_t)I2S_LL_ETM_MAX_THRESH_NUM); if (handle->dir == I2S_DIR_TX) { @@ -55,7 +72,7 @@ esp_err_t i2s_new_etm_event(i2s_chan_handle_t handle, const i2s_etm_event_config // fill the ETM event object event->event_id = event_id; event->trig_periph = ETM_TRIG_PERIPH_I2S; - event->del = i2s_del_etm_event; + event->del = s_i2s_del_etm_event; *out_event = event; return ret; err: @@ -67,15 +84,26 @@ esp_err_t i2s_new_etm_task(i2s_chan_handle_t handle, const i2s_etm_task_config_t { ESP_RETURN_ON_FALSE(handle && config && out_task, ESP_ERR_INVALID_ARG, TAG, "invalid argument"); ESP_RETURN_ON_FALSE(config->task_type < I2S_ETM_TASK_MAX, ESP_ERR_INVALID_ARG, TAG, "invalid task type"); - esp_etm_task_t *task = heap_caps_calloc(1, sizeof(esp_etm_task_t), ETM_MEM_ALLOC_CAPS); + i2s_etm_task_t *task = heap_caps_calloc(1, sizeof(i2s_etm_task_t), ETM_MEM_ALLOC_CAPS); ESP_RETURN_ON_FALSE(task, ESP_ERR_NO_MEM, TAG, "no memory for ETM task"); + // Get the task id from the I2S ETM task table uint32_t task_id = I2S_LL_ETM_TASK_TABLE(handle->controller->id, handle->dir, config->task_type); // fill the ETM task object - task->task_id = task_id; - task->trig_periph = ETM_TRIG_PERIPH_I2S; - task->del = i2s_del_etm_task; - *out_task = task; + task->base.task_id = task_id; + task->base.trig_periph = ETM_TRIG_PERIPH_I2S; + task->base.del = s_i2s_del_etm_task; + task->handle = handle; + task->task_type = config->task_type; + if (config->task_type == I2S_ETM_TASK_START) { + // The i2s start will be controlled by etm + handle->is_etm_start = true; + } else { + // The i2s stop will be controlled by etm + handle->is_etm_stop = true; + } + *out_task = &(task->base); + return ESP_OK; } diff --git a/components/esp_driver_i2s/i2s_private.h b/components/esp_driver_i2s/i2s_private.h index 0071d157f8a..52953001962 100644 --- a/components/esp_driver_i2s/i2s_private.h +++ b/components/esp_driver_i2s/i2s_private.h @@ -147,6 +147,8 @@ struct i2s_channel_obj_t { /* Stored configurations */ int intr_prio_flags;/*!< i2s interrupt priority flags */ void *mode_info; /*!< Slot, clock and gpio information of each mode */ + bool is_etm_start; /*!< Whether start/stop by etm tasks */ + bool is_etm_stop; /*!< Whether start/stop by etm tasks */ #if SOC_I2S_SUPPORTS_APLL bool apll_en; /*!< Flag of whether APLL enabled */ #endif diff --git a/components/esp_driver_i2s/test_apps/i2s/main/CMakeLists.txt b/components/esp_driver_i2s/test_apps/i2s/main/CMakeLists.txt index 7b75f96a4f1..34b5a6d39f8 100644 --- a/components/esp_driver_i2s/test_apps/i2s/main/CMakeLists.txt +++ b/components/esp_driver_i2s/test_apps/i2s/main/CMakeLists.txt @@ -7,5 +7,5 @@ if(CONFIG_SOC_I2S_SUPPORTS_ETM AND CONFIG_SOC_GPIO_SUPPORT_ETM) endif() idf_component_register(SRCS ${srcs} - PRIV_REQUIRES unity esp_driver_pcnt driver spi_flash esp_driver_gpio + PRIV_REQUIRES unity esp_driver_pcnt spi_flash esp_driver_gpio esp_driver_i2s WHOLE_ARCHIVE) diff --git a/components/esp_driver_i2s/test_apps/i2s/main/test_i2s_etm.c b/components/esp_driver_i2s/test_apps/i2s/main/test_i2s_etm.c index 480a59fb7ed..603dd9d8d20 100644 --- a/components/esp_driver_i2s/test_apps/i2s/main/test_i2s_etm.c +++ b/components/esp_driver_i2s/test_apps/i2s/main/test_i2s_etm.c @@ -46,7 +46,7 @@ static void s_i2s_etm_check_status(void) } #endif // ETM_LL_SUPPORT_STATUS -static void s_i2s_init(uint8_t *buf) +static void s_i2s_init(void *buf) { i2s_chan_config_t chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_AUTO, I2S_ROLE_MASTER); chan_cfg.dma_desc_num = TEST_DESC_NUM; @@ -92,9 +92,11 @@ static void s_gpio_init(void) TEST_CASE("i2s_etm_event_test", "[etm]") { - uint8_t *buf = calloc(1, TEST_BUFF_SIZE); + uint32_t *buf = calloc(1, TEST_BUFF_SIZE); assert(buf); - memset(buf, 0x3C, TEST_BUFF_SIZE); + for (int i = 0; i < TEST_BUFF_SIZE / 4; i++) { + buf[i] = i; + } /* I2S init */ s_i2s_init(buf); @@ -153,9 +155,11 @@ TEST_CASE("i2s_etm_event_test", "[etm]") TEST_CASE("i2s_etm_task_test", "[etm]") { - uint8_t *buf = calloc(1, TEST_BUFF_SIZE); + uint32_t *buf = calloc(1, TEST_BUFF_SIZE); assert(buf); - memset(buf, 0x3C, TEST_BUFF_SIZE); + for (int i = 0; i < TEST_BUFF_SIZE / 4; i++) { + buf[i] = i; + } /* I2S init */ s_i2s_init(buf); @@ -165,39 +169,55 @@ TEST_CASE("i2s_etm_task_test", "[etm]") /* GPIO ETM event */ gpio_etm_event_config_t gpio_event_cfg = { - .edge = GPIO_ETM_EVENT_EDGE_POS, + .edges = {GPIO_ETM_EVENT_EDGE_POS, GPIO_ETM_EVENT_EDGE_NEG}, }; - esp_etm_event_handle_t gpio_event_handle; - TEST_ESP_OK(gpio_new_etm_event(&gpio_event_cfg, &gpio_event_handle)); - TEST_ESP_OK(gpio_etm_event_bind_gpio(gpio_event_handle, TEST_GPIO_ETM_NUM)); + esp_etm_event_handle_t gpio_pos_event_handle; + esp_etm_event_handle_t gpio_neg_event_handle; + TEST_ESP_OK(gpio_new_etm_event(&gpio_event_cfg, &gpio_pos_event_handle, &gpio_neg_event_handle)); + TEST_ESP_OK(gpio_etm_event_bind_gpio(gpio_pos_event_handle, TEST_GPIO_ETM_NUM)); + TEST_ESP_OK(gpio_etm_event_bind_gpio(gpio_neg_event_handle, TEST_GPIO_ETM_NUM)); /* I2S Task init */ - i2s_etm_task_config_t i2s_task_cfg = { + i2s_etm_task_config_t i2s_start_task_cfg = { + .task_type = I2S_ETM_TASK_START, + }; + esp_etm_task_handle_t i2s_start_task_handle; + TEST_ESP_OK(i2s_new_etm_task(s_tx_handle, &i2s_start_task_cfg, &i2s_start_task_handle)); + i2s_etm_task_config_t i2s_stop_task_cfg = { .task_type = I2S_ETM_TASK_STOP, }; - esp_etm_task_handle_t i2s_task_handle; - TEST_ESP_OK(i2s_new_etm_task(s_tx_handle, &i2s_task_cfg, &i2s_task_handle)); + esp_etm_task_handle_t i2s_stop_task_handle; + TEST_ESP_OK(i2s_new_etm_task(s_tx_handle, &i2s_stop_task_cfg, &i2s_stop_task_handle)); /* ETM connect */ esp_etm_channel_config_t etm_config = {}; - esp_etm_channel_handle_t etm_channel = NULL; - TEST_ESP_OK(esp_etm_new_channel(&etm_config, &etm_channel)); - TEST_ESP_OK(esp_etm_channel_connect(etm_channel, gpio_event_handle, i2s_task_handle)); - TEST_ESP_OK(esp_etm_channel_enable(etm_channel)); + esp_etm_channel_handle_t i2s_etm_start_chan = NULL; + esp_etm_channel_handle_t i2s_etm_stop_chan = NULL; + TEST_ESP_OK(esp_etm_new_channel(&etm_config, &i2s_etm_start_chan)); + TEST_ESP_OK(esp_etm_new_channel(&etm_config, &i2s_etm_stop_chan)); + TEST_ESP_OK(esp_etm_channel_connect(i2s_etm_start_chan, gpio_pos_event_handle, i2s_start_task_handle)); + TEST_ESP_OK(esp_etm_channel_connect(i2s_etm_stop_chan, gpio_neg_event_handle, i2s_stop_task_handle)); + TEST_ESP_OK(esp_etm_channel_enable(i2s_etm_start_chan)); + TEST_ESP_OK(esp_etm_channel_enable(i2s_etm_stop_chan)); esp_etm_dump(stdout); TEST_ESP_OK(i2s_channel_enable(s_tx_handle)); TEST_ESP_OK(i2s_channel_enable(s_rx_handle)); /* Test */ - // receive normally - i2s_channel_read(s_rx_handle, buf, TEST_BUFF_SIZE, NULL, portMAX_DELAY); - // Set the GPIO to stop the I2S TX via ETM + // TX not started, read timeout + TEST_ESP_ERR(ESP_ERR_TIMEOUT, i2s_channel_read(s_rx_handle, buf, TEST_BUFF_SIZE, NULL, 100)); + // start TX via GPIO pos event TEST_ESP_OK(gpio_set_level(TEST_GPIO_ETM_NUM, 1)); + // RX can receive data normally + TEST_ESP_OK(i2s_channel_read(s_rx_handle, buf, TEST_BUFF_SIZE, NULL, 100)); + // Stop TX via GPIO neg event + TEST_ESP_OK(gpio_set_level(TEST_GPIO_ETM_NUM, 0)); + // TX stopped, read will timeout when no legacy data in the queue esp_err_t ret = ESP_OK; // Receive will timeout after TX stopped for (int i = 0; i < 20 && ret == ESP_OK; i++) { - ret = i2s_channel_read(s_rx_handle, buf, TEST_BUFF_SIZE, NULL, 1000); + ret = i2s_channel_read(s_rx_handle, buf, TEST_BUFF_SIZE, NULL, 100); } TEST_ESP_ERR(ESP_ERR_TIMEOUT, ret); @@ -206,10 +226,14 @@ TEST_CASE("i2s_etm_task_test", "[etm]") TEST_ESP_OK(i2s_channel_disable(s_tx_handle)); free(buf); - TEST_ESP_OK(esp_etm_channel_disable(etm_channel)); - TEST_ESP_OK(esp_etm_del_event(gpio_event_handle)); - TEST_ESP_OK(esp_etm_del_task(i2s_task_handle)); - TEST_ESP_OK(esp_etm_del_channel(etm_channel)); + TEST_ESP_OK(esp_etm_channel_disable(i2s_etm_start_chan)); + TEST_ESP_OK(esp_etm_channel_disable(i2s_etm_stop_chan)); + TEST_ESP_OK(esp_etm_del_event(gpio_pos_event_handle)); + TEST_ESP_OK(esp_etm_del_event(gpio_neg_event_handle)); + TEST_ESP_OK(esp_etm_del_task(i2s_start_task_handle)); + TEST_ESP_OK(esp_etm_del_task(i2s_stop_task_handle)); + TEST_ESP_OK(esp_etm_del_channel(i2s_etm_start_chan)); + TEST_ESP_OK(esp_etm_del_channel(i2s_etm_stop_chan)); s_i2s_deinit(); } diff --git a/components/soc/esp32c5/include/soc/Kconfig.soc_caps.in b/components/soc/esp32c5/include/soc/Kconfig.soc_caps.in index 5e1f813b1f9..92e7392889e 100644 --- a/components/soc/esp32c5/include/soc/Kconfig.soc_caps.in +++ b/components/soc/esp32c5/include/soc/Kconfig.soc_caps.in @@ -415,10 +415,6 @@ config SOC_ETM_CHANNELS_PER_GROUP int default 50 -config SOC_ETM_SUPPORT_STATUS - bool - default y - config SOC_GPIO_PORT int default 1 diff --git a/components/soc/esp32c5/include/soc/soc_caps.h b/components/soc/esp32c5/include/soc/soc_caps.h index 3e65dba720d..297c43f8e43 100644 --- a/components/soc/esp32c5/include/soc/soc_caps.h +++ b/components/soc/esp32c5/include/soc/soc_caps.h @@ -187,7 +187,6 @@ /*-------------------------- ETM CAPS --------------------------------------*/ #define SOC_ETM_GROUPS 1U // Number of ETM groups #define SOC_ETM_CHANNELS_PER_GROUP 50 // Number of ETM channels in the group -#define SOC_ETM_SUPPORT_STATUS 1 // Support to get and clear the status of the ETM event and task /*-------------------------- GPIO CAPS ---------------------------------------*/ // ESP32-C5 has 1 GPIO peripheral diff --git a/components/soc/esp32c61/include/soc/Kconfig.soc_caps.in b/components/soc/esp32c61/include/soc/Kconfig.soc_caps.in index e6c3141f6ab..5f96bc26840 100644 --- a/components/soc/esp32c61/include/soc/Kconfig.soc_caps.in +++ b/components/soc/esp32c61/include/soc/Kconfig.soc_caps.in @@ -187,10 +187,6 @@ config SOC_ETM_CHANNELS_PER_GROUP int default 50 -config SOC_ETM_SUPPORT_STATUS - bool - default y - config SOC_GPIO_PORT int default 1 diff --git a/components/soc/esp32c61/include/soc/soc_caps.h b/components/soc/esp32c61/include/soc/soc_caps.h index 2789c9682a3..69b3dfe8a4b 100644 --- a/components/soc/esp32c61/include/soc/soc_caps.h +++ b/components/soc/esp32c61/include/soc/soc_caps.h @@ -177,7 +177,6 @@ /*-------------------------- ETM CAPS --------------------------------------*/ #define SOC_ETM_GROUPS 1U // Number of ETM groups #define SOC_ETM_CHANNELS_PER_GROUP 50 // Number of ETM channels in the group -#define SOC_ETM_SUPPORT_STATUS 1 // Support to get and clear the status of the ETM event and task /*-------------------------- GPIO CAPS ---------------------------------------*/ // ESP32-C61 has 1 GPIO peripheral diff --git a/components/soc/esp32p4/include/soc/Kconfig.soc_caps.in b/components/soc/esp32p4/include/soc/Kconfig.soc_caps.in index bfd57ec5636..fd4fb65c29b 100644 --- a/components/soc/esp32p4/include/soc/Kconfig.soc_caps.in +++ b/components/soc/esp32p4/include/soc/Kconfig.soc_caps.in @@ -575,10 +575,6 @@ config SOC_ETM_CHANNELS_PER_GROUP int default 50 -config SOC_ETM_SUPPORT_STATUS - bool - default y - config SOC_GPIO_PORT int default 1 diff --git a/components/soc/esp32p4/include/soc/soc_caps.h b/components/soc/esp32p4/include/soc/soc_caps.h index 26837019755..0dce165cf34 100644 --- a/components/soc/esp32p4/include/soc/soc_caps.h +++ b/components/soc/esp32p4/include/soc/soc_caps.h @@ -219,7 +219,6 @@ /*-------------------------- ETM CAPS --------------------------------------*/ #define SOC_ETM_GROUPS 1U // Number of ETM groups #define SOC_ETM_CHANNELS_PER_GROUP 50 // Number of ETM channels in the group -#define SOC_ETM_SUPPORT_STATUS 1 // Support to get and clear the status of the ETM event and task /*-------------------------- GPIO CAPS ---------------------------------------*/ // ESP32-P4 has 1 GPIO peripheral From 1c52462a4d8a97f82e3d13a5cc413e8f945fb569 Mon Sep 17 00:00:00 2001 From: laokaiyao Date: Fri, 2 Aug 2024 16:21:41 +0800 Subject: [PATCH 3/3] docs(i2s): update programming guide for I2S ETM feature --- components/esp_driver_i2s/i2s_private.h | 4 +- docs/en/api-reference/peripherals/etm.rst | 2 + docs/en/api-reference/peripherals/i2s.rst | 73 ++++++++++++++++++++ docs/zh_CN/api-reference/peripherals/etm.rst | 2 + docs/zh_CN/api-reference/peripherals/i2s.rst | 73 ++++++++++++++++++++ 5 files changed, 152 insertions(+), 2 deletions(-) diff --git a/components/esp_driver_i2s/i2s_private.h b/components/esp_driver_i2s/i2s_private.h index 52953001962..40ff52fce56 100644 --- a/components/esp_driver_i2s/i2s_private.h +++ b/components/esp_driver_i2s/i2s_private.h @@ -147,8 +147,8 @@ struct i2s_channel_obj_t { /* Stored configurations */ int intr_prio_flags;/*!< i2s interrupt priority flags */ void *mode_info; /*!< Slot, clock and gpio information of each mode */ - bool is_etm_start; /*!< Whether start/stop by etm tasks */ - bool is_etm_stop; /*!< Whether start/stop by etm tasks */ + bool is_etm_start; /*!< Whether start by etm tasks */ + bool is_etm_stop; /*!< Whether stop by etm tasks */ #if SOC_I2S_SUPPORTS_APLL bool apll_en; /*!< Flag of whether APLL enabled */ #endif diff --git a/docs/en/api-reference/peripherals/etm.rst b/docs/en/api-reference/peripherals/etm.rst index 467ed590ad2..b5a6a963ddd 100644 --- a/docs/en/api-reference/peripherals/etm.rst +++ b/docs/en/api-reference/peripherals/etm.rst @@ -72,6 +72,7 @@ Other Peripheral Events :SOC_MCPWM_SUPPORT_ETM: - Refer to :doc:`/api-reference/peripherals/mcpwm` for how to get the ETM event handle from MCPWM. :SOC_ANA_CMPR_SUPPORT_ETM: - Refer to :doc:`/api-reference/peripherals/ana_cmpr` for how to get the ETM event handle from analog comparator. :SOC_TEMPERATURE_SENSOR_SUPPORT_ETM: - Refer to :doc:`/api-reference/peripherals/temp_sensor` for how to get the ETM event handle from temperature sensor. + :SOC_I2S_SUPPORTS_ETM: - Refer to :doc:`/api-reference/peripherals/i2s` for how to get the ETM event handle from I2S. .. _etm-task: @@ -98,6 +99,7 @@ Other Peripheral Tasks :SOC_TIMER_SUPPORT_ETM: - Refer to :doc:`GPTimer ` for how to get the ETM task handle from GPTimer. :SOC_TEMPERATURE_SENSOR_SUPPORT_ETM: - Refer to :doc:`/api-reference/peripherals/temp_sensor` for how to get the ETM task handle from temperature sensor. + :SOC_I2S_SUPPORTS_ETM: - Refer to :doc:`/api-reference/peripherals/i2s` for how to get the ETM task handle from I2S. .. _etm-channel-control: diff --git a/docs/en/api-reference/peripherals/i2s.rst b/docs/en/api-reference/peripherals/i2s.rst index 2659546bb7a..80f28424476 100644 --- a/docs/en/api-reference/peripherals/i2s.rst +++ b/docs/en/api-reference/peripherals/i2s.rst @@ -979,6 +979,79 @@ Here is an example of how to allocate a pair of full-duplex channels: i2s_channel_init_std_mode(rx_handle, &std_rx_cfg); i2s_channel_enable(rx_handle); +.. only:: SOC_I2S_SUPPORTS_ETM + + I2S ETM Usage + ^^^^^^^^^^^^^ + + {IDF_TARGET_NAME} supports I2S ETM (Event Task Matrix), which allows to trigger other ETM tasks via I2S ETM events, or to control the start/stop by I2S ETM tasks. + + The I2S ETM APIs can be found in ``driver/i2s_etm.h``, the following example shows how to use GPIO to start/stop I2S channel via ETM: + + .. code-block:: c + + #include "driver/i2s_etm.h" + // ... + i2s_chan_handle_t tx_handle; + // Initialize I2S channel + // ...... + int ctrl_gpio = 4; + // Initialize GPIO + // ...... + /* Register GPIO ETM events */ + gpio_etm_event_config_t gpio_event_cfg = { + .edges = {GPIO_ETM_EVENT_EDGE_POS, GPIO_ETM_EVENT_EDGE_NEG}, + }; + esp_etm_event_handle_t gpio_pos_event_handle; + esp_etm_event_handle_t gpio_neg_event_handle; + gpio_new_etm_event(&gpio_event_cfg, &gpio_pos_event_handle, &gpio_neg_event_handle); + gpio_etm_event_bind_gpio(gpio_pos_event_handle, ctrl_gpio); + gpio_etm_event_bind_gpio(gpio_neg_event_handle, ctrl_gpio); + /* Register I2S ETM tasks */ + i2s_etm_task_config_t i2s_start_task_cfg = { + .task_type = I2S_ETM_TASK_START, + }; + esp_etm_task_handle_t i2s_start_task_handle; + i2s_new_etm_task(tx_handle, &i2s_start_task_cfg, &i2s_start_task_handle); + i2s_etm_task_config_t i2s_stop_task_cfg = { + .task_type = I2S_ETM_TASK_STOP, + }; + esp_etm_task_handle_t i2s_stop_task_handle; + i2s_new_etm_task(tx_handle, &i2s_stop_task_cfg, &i2s_stop_task_handle); + /* Bind GPIO events to I2S ETM tasks */ + esp_etm_channel_config_t etm_config = {}; + esp_etm_channel_handle_t i2s_etm_start_chan = NULL; + esp_etm_channel_handle_t i2s_etm_stop_chan = NULL; + esp_etm_new_channel(&etm_config, &i2s_etm_start_chan); + esp_etm_new_channel(&etm_config, &i2s_etm_stop_chan); + esp_etm_channel_connect(i2s_etm_start_chan, gpio_pos_event_handle, i2s_start_task_handle); + esp_etm_channel_connect(i2s_etm_stop_chan, gpio_neg_event_handle, i2s_stop_task_handle); + esp_etm_channel_enable(i2s_etm_start_chan); + esp_etm_channel_enable(i2s_etm_stop_chan); + /* Enable I2S channel first before starting I2S channel */ + i2s_channel_enable(tx_handle); + // (Optional) Able to load the data into the internal DMA buffer here, + // but tx_channel does not start yet, will timeout when the internal buffer is full + // i2s_channel_write(tx_handle, data, data_size, NULL, 0); + /* Start I2S channel by setting the GPIO to high */ + gpio_set_level(ctrl_gpio, 1); + // Write data ...... + // i2s_channel_write(tx_handle, data, data_size, NULL, 1000); + /* Stop I2S channel by setting the GPIO to low */ + gpio_set_level(ctrl_gpio, 0); + + /* Free resources */ + i2s_channel_disable(tx_handle); + esp_etm_channel_disable(i2s_etm_start_chan); + esp_etm_channel_disable(i2s_etm_stop_chan); + esp_etm_del_event(gpio_pos_event_handle); + esp_etm_del_event(gpio_neg_event_handle); + esp_etm_del_task(i2s_start_task_handle); + esp_etm_del_task(i2s_stop_task_handle); + esp_etm_del_channel(i2s_etm_start_chan); + esp_etm_del_channel(i2s_etm_stop_chan); + // De-initialize I2S and GPIO + // ...... Application Notes ----------------- diff --git a/docs/zh_CN/api-reference/peripherals/etm.rst b/docs/zh_CN/api-reference/peripherals/etm.rst index c18c506e1e3..9c1967dcadd 100644 --- a/docs/zh_CN/api-reference/peripherals/etm.rst +++ b/docs/zh_CN/api-reference/peripherals/etm.rst @@ -72,6 +72,7 @@ GPIO **边沿** 事件是最常见的事件类型,任何 GPIO 管脚均可触 :SOC_MCPWM_SUPPORT_ETM: - 要了解如何从 MCPWM 中获取 ETM 事件句柄,请参阅 :doc:`/api-reference/peripherals/mcpwm`。 :SOC_ANA_CMPR_SUPPORT_ETM: - 要了解如何从模拟比较器获取 ETM 事件句柄,请参阅 :doc:`/api-reference/peripherals/ana_cmpr`。 :SOC_TEMPERATURE_SENSOR_SUPPORT_ETM: - 要了解如何从温度传感器获取 ETM 事件句柄,请参阅 :doc:`/api-reference/peripherals/temp_sensor`。 + :SOC_I2S_SUPPORTS_ETM: - 要了解如何从 I2S 获取 ETM 事件句柄,请参阅 :doc:`/api-reference/peripherals/i2s`。 .. _etm-task: @@ -98,6 +99,7 @@ GPIO 任务是最常见的任务类型。一个 GPIO 可以采取一个或多个 :SOC_TIMER_SUPPORT_ETM: - 要了解如何从 GPTimer 获取 ETM 任务句柄,请参阅 :doc:`/api-reference/peripherals/gptimer`。 :SOC_TEMPERATURE_SENSOR_SUPPORT_ETM: - 要了解如何从温度传感器获取 ETM 任务句柄,请参阅 :doc:`/api-reference/peripherals/temp_sensor`。 + :SOC_I2S_SUPPORTS_ETM: - 要了解如何从 I2S 获取 ETM 任务句柄,请参阅 :doc:`/api-reference/peripherals/i2s`。 .. _etm-channel-control: diff --git a/docs/zh_CN/api-reference/peripherals/i2s.rst b/docs/zh_CN/api-reference/peripherals/i2s.rst index 3b70174581e..9b25759aa16 100644 --- a/docs/zh_CN/api-reference/peripherals/i2s.rst +++ b/docs/zh_CN/api-reference/peripherals/i2s.rst @@ -979,6 +979,79 @@ STD RX 模式 i2s_channel_init_std_mode(rx_handle, &std_rx_cfg); i2s_channel_enable(rx_handle); +.. only:: SOC_I2S_SUPPORTS_ETM + + I2S ETM 用法 + ^^^^^^^^^^^^^^^^^^^^^^^^^ + + {IDF_TARGET_NAME} 支持 I2S ETM (Event Task Matrix,事件任务矩阵)。 它可以通过 I2S 事件触发一个其他的 ETM 任务,或者通过其他的 ETM 事件来控制 I2S 的启停任务。 + + 头文件 ``driver/i2s_etm.h`` 中可以找到 I2S ETM 所需的接口函数,下面示例代码将展示如何通过 GPIO 的 ETM 事件控制 I2S 的启停。 + + .. code-block:: c + + #include "driver/i2s_etm.h" + // ... + i2s_chan_handle_t tx_handle; + // 初始化 I2S 通道 + // ...... + int ctrl_gpio = 4; + // 初始化 GPIO 用于控制 + // ...... + /* 注册 GPIO ETM 事件 */ + gpio_etm_event_config_t gpio_event_cfg = { + .edges = {GPIO_ETM_EVENT_EDGE_POS, GPIO_ETM_EVENT_EDGE_NEG}, + }; + esp_etm_event_handle_t gpio_pos_event_handle; + esp_etm_event_handle_t gpio_neg_event_handle; + gpio_new_etm_event(&gpio_event_cfg, &gpio_pos_event_handle, &gpio_neg_event_handle); + gpio_etm_event_bind_gpio(gpio_pos_event_handle, ctrl_gpio); + gpio_etm_event_bind_gpio(gpio_neg_event_handle, ctrl_gpio); + /* 注册 I2S ETM 任务 */ + i2s_etm_task_config_t i2s_start_task_cfg = { + .task_type = I2S_ETM_TASK_START, + }; + esp_etm_task_handle_t i2s_start_task_handle; + i2s_new_etm_task(tx_handle, &i2s_start_task_cfg, &i2s_start_task_handle); + i2s_etm_task_config_t i2s_stop_task_cfg = { + .task_type = I2S_ETM_TASK_STOP, + }; + esp_etm_task_handle_t i2s_stop_task_handle; + i2s_new_etm_task(tx_handle, &i2s_stop_task_cfg, &i2s_stop_task_handle); + /* 绑定 GPIO 事件和 I2S ETM 任务 */ + esp_etm_channel_config_t etm_config = {}; + esp_etm_channel_handle_t i2s_etm_start_chan = NULL; + esp_etm_channel_handle_t i2s_etm_stop_chan = NULL; + esp_etm_new_channel(&etm_config, &i2s_etm_start_chan); + esp_etm_new_channel(&etm_config, &i2s_etm_stop_chan); + esp_etm_channel_connect(i2s_etm_start_chan, gpio_pos_event_handle, i2s_start_task_handle); + esp_etm_channel_connect(i2s_etm_stop_chan, gpio_neg_event_handle, i2s_stop_task_handle); + esp_etm_channel_enable(i2s_etm_start_chan); + esp_etm_channel_enable(i2s_etm_stop_chan); + /* 通过 ETM 启动 I2S 前需要先使能这个通道 */ + i2s_channel_enable(tx_handle); + // (可选)这里可以把要发送的数据先加载到内部的发送缓冲区中 + // 但是由于 tx_channel 还没有启动,所以当内部缓冲区加载满后,再写入会超时 + // i2s_channel_write(tx_handle, data, data_size, NULL, 0); + /* 通过拉高 GPIO 启动 I2S tx 通道 */ + gpio_set_level(ctrl_gpio, 1); + // 写数据 ...... + // i2s_channel_write(tx_handle, data, data_size, NULL, 1000); + /* 通过拉低 GPIO 停止 I2S tx 通道 */ + gpio_set_level(ctrl_gpio, 0); + + /* 释放 ETM 相关资源 */ + i2s_channel_disable(tx_handle); + esp_etm_channel_disable(i2s_etm_start_chan); + esp_etm_channel_disable(i2s_etm_stop_chan); + esp_etm_del_event(gpio_pos_event_handle); + esp_etm_del_event(gpio_neg_event_handle); + esp_etm_del_task(i2s_start_task_handle); + esp_etm_del_task(i2s_stop_task_handle); + esp_etm_del_channel(i2s_etm_start_chan); + esp_etm_del_channel(i2s_etm_stop_chan); + // 去初始化 I2S 和 GPIO + // ...... 应用注意事项 ------------