Просмотр исходного кода

Merge branch 'feat/mcpwm_event_comparator_driver' into 'master'

feat(MCPWM): MCPWM event comparator driver

Closes IDF-7989

See merge request espressif/esp-idf!25552
morris 2 лет назад
Родитель
Сommit
ae1a9bf015
28 измененных файлов с 702 добавлено и 92 удалено
  1. 4 0
      components/driver/CMakeLists.txt
  2. 0 2
      components/driver/linker.lf
  3. 23 0
      components/driver/mcpwm/include/driver/mcpwm_cmpr.h
  4. 43 0
      components/driver/mcpwm/include/driver/mcpwm_etm.h
  5. 1 0
      components/driver/mcpwm/include/driver/mcpwm_prelude.h
  6. 5 0
      components/driver/mcpwm/linker.lf
  7. 2 2
      components/driver/mcpwm/mcpwm_cap.c
  8. 119 36
      components/driver/mcpwm/mcpwm_cmpr.c
  9. 105 0
      components/driver/mcpwm/mcpwm_etm.c
  10. 2 2
      components/driver/mcpwm/mcpwm_fault.c
  11. 7 2
      components/driver/mcpwm/mcpwm_oper.c
  12. 24 3
      components/driver/mcpwm/mcpwm_private.h
  13. 2 2
      components/driver/mcpwm/mcpwm_timer.c
  14. 1 1
      components/driver/pcnt/pulse_cnt.c
  15. 1 0
      components/esp_hw_support/include/esp_private/etm_interface.h
  16. 4 0
      components/esp_hw_support/test_apps/etm/main/CMakeLists.txt
  17. 153 0
      components/esp_hw_support/test_apps/etm/main/test_mcpwm_etm.c
  18. 26 0
      components/hal/esp32c6/include/hal/mcpwm_ll.h
  19. 26 0
      components/hal/esp32h2/include/hal/mcpwm_ll.h
  20. 65 5
      components/hal/esp32p4/include/hal/mcpwm_ll.h
  21. 8 0
      components/hal/include/hal/mcpwm_types.h
  22. 1 1
      components/hal/timer_hal.c
  23. 8 0
      components/soc/esp32p4/include/soc/Kconfig.soc_caps.in
  24. 2 0
      components/soc/esp32p4/include/soc/soc_caps.h
  25. 1 0
      docs/en/api-reference/peripherals/etm.rst
  26. 34 18
      docs/en/api-reference/peripherals/mcpwm.rst
  27. 1 0
      docs/zh_CN/api-reference/peripherals/etm.rst
  28. 34 18
      docs/zh_CN/api-reference/peripherals/mcpwm.rst

+ 4 - 0
components/driver/CMakeLists.txt

@@ -134,6 +134,10 @@ if(CONFIG_SOC_MCPWM_SUPPORTED)
                      "mcpwm/mcpwm_sync.c"
                      "mcpwm/mcpwm_timer.c"
                      "deprecated/mcpwm_legacy.c")
+    if(CONFIG_SOC_MCPWM_SUPPORT_ETM)
+        list(APPEND srcs "mcpwm/mcpwm_etm.c")
+    endif()
+    list(APPEND ldfragments "mcpwm/linker.lf")
 endif()
 
 # PCNT related source files

+ 0 - 2
components/driver/linker.lf

@@ -15,8 +15,6 @@ entries:
     if DAC_CTRL_FUNC_IN_IRAM = y:
         dac_oneshot: dac_oneshot_output_voltage (noflash)
         dac_continuous: dac_continuous_write_asynchronously (noflash)
-    if MCPWM_CTRL_FUNC_IN_IRAM = y:
-        mcpwm_cmpr: mcpwm_comparator_set_compare_value (noflash)
     if LEDC_CTRL_FUNC_IN_IRAM = y:
         ledc: ledc_stop (noflash)
         ledc: ledc_update_duty (noflash)

+ 23 - 0
components/driver/mcpwm/include/driver/mcpwm_cmpr.h

@@ -54,6 +54,29 @@ esp_err_t mcpwm_new_comparator(mcpwm_oper_handle_t oper, const mcpwm_comparator_
  */
 esp_err_t mcpwm_del_comparator(mcpwm_cmpr_handle_t cmpr);
 
+#if SOC_MCPWM_SUPPORT_EVENT_COMPARATOR
+/**
+ * @brief MCPWM event comparator configuration
+ */
+typedef struct {
+} mcpwm_event_comparator_config_t;
+
+/**
+ * @brief Create MCPWM event comparator
+ *
+ * @param[in] oper MCPWM operator, allocated by `mcpwm_new_operator()`, the new event comparator will be allocated from this operator
+ * @param[in] config MCPWM comparator configuration
+ * @param[out] ret_cmpr Returned MCPWM event comparator
+ * @return
+ *      - ESP_OK: Create MCPWM event comparator successfully
+ *      - ESP_ERR_INVALID_ARG: Create MCPWM event comparator failed because of invalid argument
+ *      - ESP_ERR_NO_MEM: Create MCPWM event comparator failed because out of memory
+ *      - ESP_ERR_NOT_FOUND: Create MCPWM event comparator failed because can't find free resource
+ *      - ESP_FAIL: Create MCPWM event comparator failed because of other error
+ */
+esp_err_t mcpwm_new_event_comparator(mcpwm_oper_handle_t oper, const mcpwm_event_comparator_config_t *config, mcpwm_cmpr_handle_t *ret_cmpr);
+#endif // SOC_MCPWM_SUPPORT_EVENT_COMPARATOR
+
 /**
  * @brief Group of supported MCPWM compare event callbacks
  * @note The callbacks are all running under ISR environment

+ 43 - 0
components/driver/mcpwm/include/driver/mcpwm_etm.h

@@ -0,0 +1,43 @@
+/*
+ * SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#pragma once
+
+#include <stdint.h>
+#include <stdbool.h>
+#include "esp_err.h"
+#include "esp_etm.h"
+#include "driver/mcpwm_types.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @brief MCPWM event comparator ETM event configuration
+ */
+typedef struct {
+    mcpwm_comparator_etm_event_type_t event_type; /*!< MCPWM comparator ETM event type */
+} mcpwm_cmpr_etm_event_config_t;
+
+/**
+ * @brief Get the ETM event for MCPWM comparator
+ *
+ * @note The created ETM event object can be deleted later by calling `esp_etm_del_event`
+ *
+ * @param[in] cmpr MCPWM comparator, allocated by `mcpwm_new_comparator()` or `mcpwm_new_event_comparator()`
+ * @param[in] config MCPWM ETM comparator event configuration
+ * @param[out] out_event Returned ETM event handle
+ * @return
+ *      - ESP_OK: Get ETM event successfully
+ *      - ESP_ERR_INVALID_ARG: Get ETM event failed because of invalid argument
+ *      - ESP_FAIL: Get ETM event failed because of other error
+ */
+esp_err_t mcpwm_comparator_new_etm_event(mcpwm_cmpr_handle_t cmpr, const mcpwm_cmpr_etm_event_config_t *config, esp_etm_event_handle_t *out_event);
+
+#ifdef __cplusplus
+}
+#endif

+ 1 - 0
components/driver/mcpwm/include/driver/mcpwm_prelude.h

@@ -18,3 +18,4 @@
 #include "driver/mcpwm_fault.h"
 #include "driver/mcpwm_sync.h"
 #include "driver/mcpwm_cap.h"
+#include "driver/mcpwm_etm.h"

+ 5 - 0
components/driver/mcpwm/linker.lf

@@ -0,0 +1,5 @@
+[mapping:mcpwm_driver]
+archive: libdriver.a
+entries:
+    if MCPWM_CTRL_FUNC_IN_IRAM = y:
+        mcpwm_cmpr: mcpwm_comparator_set_compare_value (noflash)

+ 2 - 2
components/driver/mcpwm/mcpwm_cap.c

@@ -262,8 +262,8 @@ esp_err_t mcpwm_new_capture_channel(mcpwm_cap_timer_handle_t cap_timer, const mc
     ESP_GOTO_ON_FALSE(cap_timer && config && ret_cap_channel, ESP_ERR_INVALID_ARG, err, TAG, "invalid argument");
     ESP_GOTO_ON_FALSE(config->prescale && config->prescale <= MCPWM_LL_MAX_CAPTURE_PRESCALE, ESP_ERR_INVALID_ARG, err, TAG, "invalid prescale");
     if (config->intr_priority) {
-        ESP_RETURN_ON_FALSE(1 << (config->intr_priority) & MCPWM_ALLOW_INTR_PRIORITY_MASK, ESP_ERR_INVALID_ARG,
-                            TAG, "invalid interrupt priority:%d", config->intr_priority);
+        ESP_GOTO_ON_FALSE(1 << (config->intr_priority) & MCPWM_ALLOW_INTR_PRIORITY_MASK, ESP_ERR_INVALID_ARG, err,
+                          TAG, "invalid interrupt priority:%d", config->intr_priority);
     }
 
     // create instance firstly, then install onto platform

+ 119 - 36
components/driver/mcpwm/mcpwm_cmpr.c

@@ -33,12 +33,31 @@ static esp_err_t mcpwm_comparator_register_to_operator(mcpwm_cmpr_t *cmpr, mcpwm
 {
     int cmpr_id = -1;
     portENTER_CRITICAL(&oper->spinlock);
-    for (int i = 0; i < SOC_MCPWM_COMPARATORS_PER_OPERATOR; i++) {
-        if (!oper->comparators[i]) {
-            oper->comparators[i] = cmpr;
-            cmpr_id = i;
-            break;
+    switch (cmpr->type) {
+    case MCPWM_OPERATOR_COMPARATOR: {
+        mcpwm_oper_cmpr_t *oper_cmpr = __containerof(cmpr, mcpwm_oper_cmpr_t, base);
+        for (int i = 0; i < SOC_MCPWM_COMPARATORS_PER_OPERATOR; i++) {
+            if (!oper->comparators[i]) {
+                oper->comparators[i] = oper_cmpr;
+                cmpr_id = i;
+                break;
+            }
         }
+        break;
+    }
+#if SOC_MCPWM_SUPPORT_EVENT_COMPARATOR
+    case MCPWM_EVENT_COMPARATOR: {
+        mcpwm_evt_cmpr_t *evt_cmpr = __containerof(cmpr, mcpwm_evt_cmpr_t, base);
+        for (int i = 0; i < SOC_MCPWM_EVENT_COMPARATORS_PER_OPERATOR; i++) {
+            if (!oper->event_comparators[i]) {
+                oper->event_comparators[i] = evt_cmpr;
+                cmpr_id = i;
+                break;
+            }
+        }
+        break;
+    }
+#endif
     }
     portEXIT_CRITICAL(&oper->spinlock);
     ESP_RETURN_ON_FALSE(cmpr_id >= 0, ESP_ERR_NOT_FOUND, TAG, "no free comparator in operator (%d,%d)", oper->group->group_id, oper->oper_id);
@@ -54,58 +73,81 @@ static void mcpwm_comparator_unregister_from_operator(mcpwm_cmpr_t *cmpr)
     int cmpr_id = cmpr->cmpr_id;
 
     portENTER_CRITICAL(&oper->spinlock);
-    oper->comparators[cmpr_id] = NULL;
+    switch (cmpr->type) {
+    case MCPWM_OPERATOR_COMPARATOR:
+        oper->comparators[cmpr_id] = NULL;
+        break;
+#if SOC_MCPWM_SUPPORT_EVENT_COMPARATOR
+    case MCPWM_EVENT_COMPARATOR:
+        oper->event_comparators[cmpr_id] = NULL;
+        break;
+#endif
+    }
     portEXIT_CRITICAL(&oper->spinlock);
 }
 
 static esp_err_t mcpwm_comparator_destroy(mcpwm_cmpr_t *cmpr)
 {
-    if (cmpr->intr) {
-        ESP_RETURN_ON_ERROR(esp_intr_free(cmpr->intr), TAG, "uninstall interrupt service failed");
-    }
     if (cmpr->oper) {
         mcpwm_comparator_unregister_from_operator(cmpr);
     }
-    free(cmpr);
+    switch (cmpr->type) {
+    case MCPWM_OPERATOR_COMPARATOR: {
+        mcpwm_oper_cmpr_t *oper_cmpr = __containerof(cmpr, mcpwm_oper_cmpr_t, base);
+        if (oper_cmpr->intr) {
+            ESP_RETURN_ON_ERROR(esp_intr_free(oper_cmpr->intr), TAG, "uninstall interrupt service failed");
+        }
+        free(oper_cmpr);
+        break;
+    }
+#if SOC_MCPWM_SUPPORT_EVENT_COMPARATOR
+    case MCPWM_EVENT_COMPARATOR: {
+        mcpwm_evt_cmpr_t *evt_cmpr = __containerof(cmpr, mcpwm_evt_cmpr_t, base);
+        free(evt_cmpr);
+        break;
+    }
+#endif // SOC_MCPWM_SUPPORT_EVENT_COMPARATOR
+    }
     return ESP_OK;
 }
 
 esp_err_t mcpwm_new_comparator(mcpwm_oper_handle_t oper, const mcpwm_comparator_config_t *config, mcpwm_cmpr_handle_t *ret_cmpr)
 {
     esp_err_t ret = ESP_OK;
-    mcpwm_cmpr_t *cmpr = NULL;
+    mcpwm_oper_cmpr_t *cmpr = NULL;
     ESP_GOTO_ON_FALSE(oper && config && ret_cmpr, ESP_ERR_INVALID_ARG, err, TAG, "invalid argument");
     if (config->intr_priority) {
-        ESP_RETURN_ON_FALSE(1 << (config->intr_priority) & MCPWM_ALLOW_INTR_PRIORITY_MASK, ESP_ERR_INVALID_ARG,
-                            TAG, "invalid interrupt priority:%d", config->intr_priority);
+        ESP_GOTO_ON_FALSE(1 << (config->intr_priority) & MCPWM_ALLOW_INTR_PRIORITY_MASK, ESP_ERR_INVALID_ARG, err,
+                          TAG, "invalid interrupt priority:%d", config->intr_priority);
     }
 
-    cmpr = heap_caps_calloc(1, sizeof(mcpwm_cmpr_t), MCPWM_MEM_ALLOC_CAPS);
+    cmpr = heap_caps_calloc(1, sizeof(mcpwm_oper_cmpr_t), MCPWM_MEM_ALLOC_CAPS);
     ESP_GOTO_ON_FALSE(cmpr, ESP_ERR_NO_MEM, err, TAG, "no mem for comparator");
+    cmpr->base.type = MCPWM_OPERATOR_COMPARATOR;
 
-    ESP_GOTO_ON_ERROR(mcpwm_comparator_register_to_operator(cmpr, oper), err, TAG, "register comparator failed");
+    ESP_GOTO_ON_ERROR(mcpwm_comparator_register_to_operator(&cmpr->base, oper), err, TAG, "register comparator failed");
     mcpwm_group_t *group = oper->group;
     mcpwm_hal_context_t *hal = &group->hal;
     int oper_id = oper->oper_id;
-    int cmpr_id = cmpr->cmpr_id;
+    int cmpr_id = cmpr->base.cmpr_id;
 
     // if interrupt priority specified before, it cannot be changed until the group is released
-    // check if the new priority specified consistents with the old one
-    ESP_GOTO_ON_ERROR(mcpwm_check_intr_priority(group, config->intr_priority), err, TAG, "set group intrrupt priority failed");
+    // check if the new priority specified consistent with the old one
+    ESP_GOTO_ON_ERROR(mcpwm_check_intr_priority(group, config->intr_priority), err, TAG, "set group interrupt priority failed");
 
     mcpwm_ll_operator_enable_update_compare_on_tez(hal->dev, oper_id, cmpr_id, config->flags.update_cmp_on_tez);
     mcpwm_ll_operator_enable_update_compare_on_tep(hal->dev, oper_id, cmpr_id, config->flags.update_cmp_on_tep);
     mcpwm_ll_operator_enable_update_compare_on_sync(hal->dev, oper_id, cmpr_id, config->flags.update_cmp_on_sync);
 
     // fill in other comparator members
-    cmpr->spinlock = (portMUX_TYPE)portMUX_INITIALIZER_UNLOCKED;
-    *ret_cmpr = cmpr;
-    ESP_LOGD(TAG, "new comparator (%d,%d,%d) at %p", group->group_id, oper_id, cmpr_id, cmpr);
+    cmpr->base.spinlock = (portMUX_TYPE)portMUX_INITIALIZER_UNLOCKED;
+    *ret_cmpr = &cmpr->base;
+    ESP_LOGD(TAG, "new operator comparator (%d,%d,%d) at %p", group->group_id, oper_id, cmpr_id, cmpr);
     return ESP_OK;
 
 err:
     if (cmpr) {
-        mcpwm_comparator_destroy(cmpr);
+        mcpwm_comparator_destroy(&cmpr->base);
     }
     return ret;
 }
@@ -120,8 +162,14 @@ esp_err_t mcpwm_del_comparator(mcpwm_cmpr_handle_t cmpr)
     int cmpr_id = cmpr->cmpr_id;
 
     portENTER_CRITICAL(&group->spinlock);
-    mcpwm_ll_intr_enable(hal->dev, MCPWM_LL_EVENT_CMP_EQUAL(oper_id, cmpr_id), false);
-    mcpwm_ll_intr_clear_status(hal->dev, MCPWM_LL_EVENT_CMP_EQUAL(oper_id, cmpr_id));
+    switch (cmpr->type) {
+    case MCPWM_OPERATOR_COMPARATOR:
+        mcpwm_ll_intr_enable(hal->dev, MCPWM_LL_EVENT_CMP_EQUAL(oper_id, cmpr_id), false);
+        mcpwm_ll_intr_clear_status(hal->dev, MCPWM_LL_EVENT_CMP_EQUAL(oper_id, cmpr_id));
+        break;
+    default:
+        break;
+    }
     portEXIT_CRITICAL(&group->spinlock);
 
     ESP_LOGD(TAG, "del comparator (%d,%d,%d)", group->group_id, oper_id, cmpr_id);
@@ -130,6 +178,32 @@ esp_err_t mcpwm_del_comparator(mcpwm_cmpr_handle_t cmpr)
     return ESP_OK;
 }
 
+#if SOC_MCPWM_SUPPORT_EVENT_COMPARATOR
+esp_err_t mcpwm_new_event_comparator(mcpwm_oper_handle_t oper, const mcpwm_event_comparator_config_t *config, mcpwm_cmpr_handle_t *ret_cmpr)
+{
+    esp_err_t ret = ESP_OK;
+    mcpwm_evt_cmpr_t *cmpr = NULL;
+    ESP_GOTO_ON_FALSE(oper && config && ret_cmpr, ESP_ERR_INVALID_ARG, err, TAG, "invalid argument");
+    cmpr = heap_caps_calloc(1, sizeof(mcpwm_evt_cmpr_t), MCPWM_MEM_ALLOC_CAPS);
+    ESP_GOTO_ON_FALSE(cmpr, ESP_ERR_NO_MEM, err, TAG, "no mem for event comparator");
+    cmpr->base.type = MCPWM_EVENT_COMPARATOR;
+
+    ESP_GOTO_ON_ERROR(mcpwm_comparator_register_to_operator(&cmpr->base, oper), err, TAG, "register event comparator failed");
+
+    // fill in other comparator members
+    cmpr->base.spinlock = (portMUX_TYPE)portMUX_INITIALIZER_UNLOCKED;
+    *ret_cmpr = &cmpr->base;
+    ESP_LOGD(TAG, "new event comparator (%d,%d,%d) at %p", oper->group->group_id, oper->oper_id, cmpr->base.cmpr_id, cmpr);
+    return ESP_OK;
+
+err:
+    if (cmpr) {
+        mcpwm_comparator_destroy(&cmpr->base);
+    }
+    return ret;
+}
+#endif // SOC_MCPWM_SUPPORT_EVENT_COMPARATOR
+
 esp_err_t mcpwm_comparator_set_compare_value(mcpwm_cmpr_handle_t cmpr, uint32_t cmp_ticks)
 {
     ESP_RETURN_ON_FALSE_ISR(cmpr, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
@@ -139,9 +213,16 @@ esp_err_t mcpwm_comparator_set_compare_value(mcpwm_cmpr_handle_t cmpr, uint32_t
     ESP_RETURN_ON_FALSE_ISR(timer, ESP_ERR_INVALID_STATE, TAG, "timer and operator are not connected");
     ESP_RETURN_ON_FALSE_ISR(cmp_ticks <= timer->peak_ticks, ESP_ERR_INVALID_ARG, TAG, "compare value out of range");
 
-    portENTER_CRITICAL_SAFE(&cmpr->spinlock);
-    mcpwm_ll_operator_set_compare_value(group->hal.dev, oper->oper_id, cmpr->cmpr_id, cmp_ticks);
-    portEXIT_CRITICAL_SAFE(&cmpr->spinlock);
+    switch (cmpr->type) {
+    case MCPWM_OPERATOR_COMPARATOR:
+        mcpwm_ll_operator_set_compare_value(group->hal.dev, oper->oper_id, cmpr->cmpr_id, cmp_ticks);
+        break;
+#if SOC_MCPWM_SUPPORT_EVENT_COMPARATOR
+    case MCPWM_EVENT_COMPARATOR:
+        mcpwm_ll_operator_set_event_compare_value(group->hal.dev, oper->oper_id, cmpr->cmpr_id, cmp_ticks);
+        break;
+#endif
+    }
 
     cmpr->compare_ticks = cmp_ticks;
     return ESP_OK;
@@ -150,6 +231,7 @@ esp_err_t mcpwm_comparator_set_compare_value(mcpwm_cmpr_handle_t cmpr, uint32_t
 esp_err_t mcpwm_comparator_register_event_callbacks(mcpwm_cmpr_handle_t cmpr, const mcpwm_comparator_event_callbacks_t *cbs, void *user_data)
 {
     ESP_RETURN_ON_FALSE(cmpr && cbs, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
+    ESP_RETURN_ON_FALSE(cmpr->type == MCPWM_OPERATOR_COMPARATOR, ESP_ERR_INVALID_ARG, TAG, "only oper cmpr can register event callback");
     mcpwm_oper_t *oper = cmpr->oper;
     mcpwm_group_t *group = oper->group;
     mcpwm_hal_context_t *hal = &group->hal;
@@ -166,49 +248,50 @@ esp_err_t mcpwm_comparator_register_event_callbacks(mcpwm_cmpr_handle_t cmpr, co
     }
 #endif
 
+    mcpwm_oper_cmpr_t *oper_cmpr = __containerof(cmpr, mcpwm_oper_cmpr_t, base);
     // lazy install interrupt service
-    if (!cmpr->intr) {
+    if (!oper_cmpr->intr) {
         // we want the interrupt service to be enabled after allocation successfully
         int isr_flags = MCPWM_INTR_ALLOC_FLAG & ~ ESP_INTR_FLAG_INTRDISABLED;
         isr_flags |= mcpwm_get_intr_priority_flag(group);
         ESP_RETURN_ON_ERROR(esp_intr_alloc_intrstatus(mcpwm_periph_signals.groups[group_id].irq_id, isr_flags,
                             (uint32_t)mcpwm_ll_intr_get_status_reg(hal->dev), MCPWM_LL_EVENT_CMP_EQUAL(oper_id, cmpr_id),
-                            mcpwm_comparator_default_isr, cmpr, &cmpr->intr), TAG, "install interrupt service for comparator failed");
+                            mcpwm_comparator_default_isr, oper_cmpr, &oper_cmpr->intr), TAG, "install interrupt service for comparator failed");
     }
 
     portENTER_CRITICAL(&group->spinlock);
     mcpwm_ll_intr_enable(hal->dev, MCPWM_LL_EVENT_CMP_EQUAL(oper_id, cmpr_id), cbs->on_reach != NULL);
     portEXIT_CRITICAL(&group->spinlock);
 
-    cmpr->on_reach = cbs->on_reach;
-    cmpr->user_data = user_data;
+    oper_cmpr->on_reach = cbs->on_reach;
+    oper_cmpr->user_data = user_data;
 
     return ESP_OK;
 }
 
 static void IRAM_ATTR mcpwm_comparator_default_isr(void *args)
 {
-    mcpwm_cmpr_t *cmpr = (mcpwm_cmpr_t *)args;
-    mcpwm_oper_t *oper = cmpr->oper;
+    mcpwm_oper_cmpr_t *cmpr = (mcpwm_oper_cmpr_t *)args;
+    mcpwm_oper_t *oper = cmpr->base.oper;
     mcpwm_timer_t *timer = oper->timer;
     mcpwm_group_t *group = oper->group;
     mcpwm_hal_context_t *hal = &group->hal;
     int oper_id = oper->oper_id;
-    int cmpr_id = cmpr->cmpr_id;
+    int cmpr_id = cmpr->base.cmpr_id;
     bool need_yield = false;
 
     uint32_t status = mcpwm_ll_intr_get_status(hal->dev);
     mcpwm_ll_intr_clear_status(hal->dev, status & MCPWM_LL_EVENT_CMP_EQUAL(oper_id, cmpr_id));
 
     mcpwm_compare_event_data_t edata = {
-        .compare_ticks = cmpr->compare_ticks,
+        .compare_ticks = cmpr->base.compare_ticks,
         .direction = mcpwm_ll_timer_get_count_direction(hal->dev, timer->timer_id),
     };
 
     if (status & MCPWM_LL_EVENT_CMP_EQUAL(oper_id, cmpr_id)) {
         mcpwm_compare_event_cb_t cb = cmpr->on_reach;
         if (cb) {
-            if (cb(cmpr, &edata, cmpr->user_data)) {
+            if (cb(&cmpr->base, &edata, cmpr->user_data)) {
                 need_yield = true;
             }
         }

+ 105 - 0
components/driver/mcpwm/mcpwm_etm.c

@@ -0,0 +1,105 @@
+/*
+ * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include <stdlib.h>
+#include <stdarg.h>
+#include <sys/cdefs.h>
+#include "sdkconfig.h"
+#include "freertos/FreeRTOS.h"
+#include "esp_attr.h"
+#include "esp_check.h"
+#include "esp_err.h"
+#include "esp_log.h"
+#include "soc/soc_caps.h"
+#include "soc/mcpwm_periph.h"
+#include "hal/mcpwm_ll.h"
+#include "driver/mcpwm_etm.h"
+#include "mcpwm_private.h"
+#include "esp_private/etm_interface.h"
+
+static const char *TAG = "mcpwm-etm";
+
+typedef struct {
+    esp_etm_event_t base;
+    mcpwm_cmpr_handle_t cmpr;
+} mcpwm_comparator_etm_event_t;
+
+static esp_err_t mcpwm_del_etm_event(esp_etm_event_t *event)
+{
+    mcpwm_comparator_etm_event_t *etm_event = __containerof(event, mcpwm_comparator_etm_event_t, base);
+    mcpwm_cmpr_handle_t cmpr = etm_event->cmpr;
+    mcpwm_oper_t *oper = cmpr->oper;
+    mcpwm_group_t *group = oper->group;
+    mcpwm_hal_context_t *hal = &group->hal;
+
+    switch (cmpr->type) {
+    case MCPWM_OPERATOR_COMPARATOR:
+        portENTER_CRITICAL(&group->spinlock);
+        mcpwm_ll_etm_enable_comparator_event(hal->dev, oper->oper_id, cmpr->cmpr_id, false);
+        portEXIT_CRITICAL(&group->spinlock);
+        break;
+#if SOC_MCPWM_SUPPORT_EVENT_COMPARATOR
+    case MCPWM_EVENT_COMPARATOR:
+        portENTER_CRITICAL(&group->spinlock);
+        mcpwm_ll_etm_enable_evt_comparator_event(hal->dev, oper->oper_id, cmpr->cmpr_id, false);
+        portEXIT_CRITICAL(&group->spinlock);
+        break;
+#endif // SOC_MCPWM_SUPPORT_EVENT_COMPARATOR
+    }
+    free(etm_event);
+    return ESP_OK;
+}
+
+esp_err_t mcpwm_comparator_new_etm_event(mcpwm_cmpr_handle_t cmpr, const mcpwm_cmpr_etm_event_config_t *config, esp_etm_event_handle_t *out_event)
+{
+    esp_err_t ret = ESP_OK;
+    mcpwm_comparator_etm_event_t *event = NULL;
+    ESP_RETURN_ON_FALSE(cmpr && config && out_event, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
+    event = heap_caps_calloc(1, sizeof(mcpwm_comparator_etm_event_t), MCPWM_MEM_ALLOC_CAPS);
+    ESP_RETURN_ON_FALSE(event, ESP_ERR_NO_MEM, TAG, "no memory for ETM event");
+
+    mcpwm_oper_t *oper = cmpr->oper;
+    mcpwm_group_t *group = oper->group;
+    mcpwm_hal_context_t *hal = &group->hal;
+    int group_id = group->group_id;
+    int oper_id = oper->oper_id;
+    int cmpr_id = cmpr->cmpr_id;
+    uint32_t event_id = 0;
+
+    switch (cmpr->type) {
+    case MCPWM_OPERATOR_COMPARATOR:
+        portENTER_CRITICAL(&group->spinlock);
+        mcpwm_ll_etm_enable_comparator_event(hal->dev, oper_id, cmpr_id, true);
+        portEXIT_CRITICAL(&group->spinlock);
+        event_id = MCPWM_LL_ETM_COMPARATOR_EVENT_TABLE(group_id, oper_id, cmpr_id, config->event_type);
+        break;
+#if SOC_MCPWM_SUPPORT_EVENT_COMPARATOR
+    case MCPWM_EVENT_COMPARATOR:
+        portENTER_CRITICAL(&group->spinlock);
+        mcpwm_ll_etm_enable_evt_comparator_event(hal->dev, oper->oper_id, cmpr->cmpr_id, true);
+        portEXIT_CRITICAL(&group->spinlock);
+        event_id = MCPWM_LL_ETM_EVENT_COMPARATOR_EVENT_TABLE(group_id, oper_id, cmpr_id, config->event_type);
+        break;
+#endif // SOC_MCPWM_SUPPORT_EVENT_COMPARATOR
+    }
+    ESP_GOTO_ON_FALSE(event_id != 0, ESP_ERR_NOT_SUPPORTED, err, TAG, "not supported event type");
+    ESP_LOGD(TAG, "MCPWM (%d) oper (%d) cmpr(%d) event_id (%"PRId32")", group_id, oper_id, cmpr_id, event_id);
+
+    // fill the ETM event object
+    event->cmpr = cmpr;
+    event->base.event_id = event_id;
+    event->base.trig_periph = ETM_TRIG_PERIPH_MCPWM;
+    event->base.del = mcpwm_del_etm_event;
+
+    *out_event = &event->base;
+    return ESP_OK;
+
+err:
+    if (event) {
+        mcpwm_del_etm_event(&event->base);
+    }
+    return ret;
+}

+ 2 - 2
components/driver/mcpwm/mcpwm_fault.c

@@ -94,8 +94,8 @@ esp_err_t mcpwm_new_gpio_fault(const mcpwm_gpio_fault_config_t *config, mcpwm_fa
     ESP_GOTO_ON_FALSE(config->group_id < SOC_MCPWM_GROUPS && config->group_id >= 0, ESP_ERR_INVALID_ARG,
                       err, TAG, "invalid group ID:%d", config->group_id);
     if (config->intr_priority) {
-        ESP_RETURN_ON_FALSE(1 << (config->intr_priority) & MCPWM_ALLOW_INTR_PRIORITY_MASK, ESP_ERR_INVALID_ARG,
-                            TAG, "invalid interrupt priority:%d", config->intr_priority);
+        ESP_GOTO_ON_FALSE(1 << (config->intr_priority) & MCPWM_ALLOW_INTR_PRIORITY_MASK, ESP_ERR_INVALID_ARG, err,
+                          TAG, "invalid interrupt priority:%d", config->intr_priority);
     }
 
     fault = heap_caps_calloc(1, sizeof(mcpwm_gpio_fault_t), MCPWM_MEM_ALLOC_CAPS);

+ 7 - 2
components/driver/mcpwm/mcpwm_oper.c

@@ -91,8 +91,8 @@ esp_err_t mcpwm_new_operator(const mcpwm_operator_config_t *config, mcpwm_oper_h
     ESP_GOTO_ON_FALSE(config->group_id < SOC_MCPWM_GROUPS && config->group_id >= 0, ESP_ERR_INVALID_ARG,
                       err, TAG, "invalid group ID:%d", config->group_id);
     if (config->intr_priority) {
-        ESP_RETURN_ON_FALSE(1 << (config->intr_priority) & MCPWM_ALLOW_INTR_PRIORITY_MASK, ESP_ERR_INVALID_ARG,
-                            TAG, "invalid interrupt priority:%d", config->intr_priority);
+        ESP_GOTO_ON_FALSE(1 << (config->intr_priority) & MCPWM_ALLOW_INTR_PRIORITY_MASK, ESP_ERR_INVALID_ARG, err,
+                          TAG, "invalid interrupt priority:%d", config->intr_priority);
     }
 
     oper = heap_caps_calloc(1, sizeof(mcpwm_oper_t), MCPWM_MEM_ALLOC_CAPS);
@@ -146,6 +146,11 @@ esp_err_t mcpwm_del_operator(mcpwm_oper_handle_t oper)
         ESP_RETURN_ON_FALSE(!oper->generators[i], ESP_ERR_INVALID_STATE, TAG, "generator still in working");
     }
     ESP_RETURN_ON_FALSE(!oper->soft_fault, ESP_ERR_INVALID_STATE, TAG, "soft fault still in working");
+#if SOC_MCPWM_SUPPORT_EVENT_COMPARATOR
+    for (int i = 0; i < SOC_MCPWM_EVENT_COMPARATORS_PER_OPERATOR; i++) {
+        ESP_RETURN_ON_FALSE(!oper->event_comparators[i], ESP_ERR_INVALID_STATE, TAG, "event comparator still in working");
+    }
+#endif
     mcpwm_group_t *group = oper->group;
     int oper_id = oper->oper_id;
     mcpwm_hal_context_t *hal = &group->hal;

+ 24 - 3
components/driver/mcpwm/mcpwm_private.h

@@ -44,6 +44,8 @@ typedef struct mcpwm_timer_t mcpwm_timer_t;
 typedef struct mcpwm_cap_timer_t mcpwm_cap_timer_t;
 typedef struct mcpwm_oper_t mcpwm_oper_t;
 typedef struct mcpwm_cmpr_t mcpwm_cmpr_t;
+typedef struct mcpwm_oper_cmpr_t mcpwm_oper_cmpr_t;
+typedef struct mcpwm_evt_cmpr_t mcpwm_evt_cmpr_t;
 typedef struct mcpwm_gen_t mcpwm_gen_t;
 typedef struct mcpwm_fault_t mcpwm_fault_t;
 typedef struct mcpwm_gpio_fault_t mcpwm_gpio_fault_t;
@@ -107,7 +109,10 @@ struct mcpwm_oper_t {
     portMUX_TYPE spinlock; // spin lock
     intr_handle_t intr;    // interrupt handle
     mcpwm_gen_t *generators[SOC_MCPWM_GENERATORS_PER_OPERATOR];    // mcpwm generator array
-    mcpwm_cmpr_t *comparators[SOC_MCPWM_COMPARATORS_PER_OPERATOR]; // mcpwm comparator array
+    mcpwm_oper_cmpr_t *comparators[SOC_MCPWM_COMPARATORS_PER_OPERATOR]; // mcpwm operator comparator array
+#if SOC_MCPWM_SUPPORT_EVENT_COMPARATOR
+    mcpwm_evt_cmpr_t *event_comparators[SOC_MCPWM_EVENT_COMPARATORS_PER_OPERATOR]; // mcpwm event comparator array
+#endif
     mcpwm_trigger_source_t triggers[SOC_MCPWM_TRIGGERS_PER_OPERATOR];                 // mcpwm trigger array, can be either a fault or a sync
     mcpwm_soft_fault_t *soft_fault;                                // mcpwm software fault
     mcpwm_operator_brake_mode_t brake_mode_on_soft_fault;          // brake mode on software triggered fault
@@ -120,16 +125,32 @@ struct mcpwm_oper_t {
     void *user_data;                     // user data which would be passed to the trip zone callback
 };
 
+typedef enum {
+    MCPWM_OPERATOR_COMPARATOR, // operator comparator, can affect generator's behaviour
+#if SOC_MCPWM_SUPPORT_EVENT_COMPARATOR
+    MCPWM_EVENT_COMPARATOR,    // event comparator, can only generate ETM event
+#endif
+} mcpwm_comparator_type_t;
+
 struct mcpwm_cmpr_t {
     int cmpr_id;                       // comparator ID, index from 0
     mcpwm_oper_t *oper;                // which operator that the comparator resides in
-    intr_handle_t intr;                // interrupt handle
     portMUX_TYPE spinlock;             // spin lock
     uint32_t compare_ticks;            // compare value of this comparator
+    mcpwm_comparator_type_t type;      // comparator type
+};
+
+struct mcpwm_oper_cmpr_t {
+    mcpwm_cmpr_t base;                 // base class
+    intr_handle_t intr;                // interrupt handle
     mcpwm_compare_event_cb_t on_reach; // ISR callback function  which would be invoked on timer counter reaches compare value
     void *user_data;                   // user data which would be passed to the comparator callbacks
 };
 
+struct mcpwm_evt_cmpr_t {
+    mcpwm_cmpr_t base; // base class
+};
+
 struct mcpwm_gen_t {
     int gen_id;             // generator ID, index from 0
     mcpwm_oper_t *oper;     // which operator that the generator resides in
@@ -239,7 +260,7 @@ void mcpwm_release_group_handle(mcpwm_group_t *group);
 esp_err_t mcpwm_check_intr_priority(mcpwm_group_t *group, int intr_priority);
 int mcpwm_get_intr_priority_flag(mcpwm_group_t *group);
 esp_err_t mcpwm_select_periph_clock(mcpwm_group_t *group, soc_module_clk_t clk_src);
-esp_err_t mcpwm_set_prescale(mcpwm_group_t *group, uint32_t expect_module_resolution_hz, uint32_t module_prescale_max, uint32_t* ret_module_prescale);
+esp_err_t mcpwm_set_prescale(mcpwm_group_t *group, uint32_t expect_module_resolution_hz, uint32_t module_prescale_max, uint32_t *ret_module_prescale);
 
 #ifdef __cplusplus
 }

+ 2 - 2
components/driver/mcpwm/mcpwm_timer.c

@@ -92,8 +92,8 @@ esp_err_t mcpwm_new_timer(const mcpwm_timer_config_t *config, mcpwm_timer_handle
     ESP_GOTO_ON_FALSE(config->group_id < SOC_MCPWM_GROUPS && config->group_id >= 0, ESP_ERR_INVALID_ARG,
                       err, TAG, "invalid group ID:%d", config->group_id);
     if (config->intr_priority) {
-        ESP_RETURN_ON_FALSE(1 << (config->intr_priority) & MCPWM_ALLOW_INTR_PRIORITY_MASK, ESP_ERR_INVALID_ARG,
-                            TAG, "invalid interrupt priority:%d", config->intr_priority);
+        ESP_GOTO_ON_FALSE(1 << (config->intr_priority) & MCPWM_ALLOW_INTR_PRIORITY_MASK, ESP_ERR_INVALID_ARG, err,
+                          TAG, "invalid interrupt priority:%d", config->intr_priority);
     }
 
     timer = heap_caps_calloc(1, sizeof(mcpwm_timer_t), MCPWM_MEM_ALLOC_CAPS);

+ 1 - 1
components/driver/pcnt/pulse_cnt.c

@@ -185,7 +185,7 @@ esp_err_t pcnt_new_unit(const pcnt_unit_config_t *config, pcnt_unit_handle_t *re
                       config->high_limit <= PCNT_LL_MAX_LIM, ESP_ERR_INVALID_ARG, err, TAG,
                       "invalid limit range:[%d,%d]", config->low_limit, config->high_limit);
     if (config->intr_priority) {
-        ESP_RETURN_ON_FALSE(1 << (config->intr_priority) & PCNT_ALLOW_INTR_PRIORITY_MASK, ESP_ERR_INVALID_ARG,
+        ESP_GOTO_ON_FALSE(1 << (config->intr_priority) & PCNT_ALLOW_INTR_PRIORITY_MASK, ESP_ERR_INVALID_ARG, err,
                             TAG, "invalid interrupt priority:%d", config->intr_priority);
     }
 

+ 1 - 0
components/esp_hw_support/include/esp_private/etm_interface.h

@@ -24,6 +24,7 @@ typedef enum {
     ETM_TRIG_PERIPH_GDMA,     /*!< ETM trigger source: GDMA */
     ETM_TRIG_PERIPH_GPTIMER,  /*!< ETM trigger source: GPTimer */
     ETM_TRIG_PERIPH_SYSTIMER, /*!< ETM trigger source: Systimer */
+    ETM_TRIG_PERIPH_MCPWM,    /*!< ETM trigger source: MCPWM */
 } etm_trigger_peripheral_t;
 
 /**

+ 4 - 0
components/esp_hw_support/test_apps/etm/main/CMakeLists.txt

@@ -17,6 +17,10 @@ if(CONFIG_SOC_GDMA_SUPPORT_ETM)
     list(APPEND srcs "test_gdma_etm.c")
 endif()
 
+if(CONFIG_SOC_MCPWM_SUPPORT_ETM)
+    list(APPEND srcs "test_mcpwm_etm.c")
+endif()
+
 # In order for the cases defined by `TEST_CASE` to be linked into the final elf,
 # the component can be registered as WHOLE_ARCHIVE
 idf_component_register(SRCS ${srcs}

+ 153 - 0
components/esp_hw_support/test_apps/etm/main/test_mcpwm_etm.c

@@ -0,0 +1,153 @@
+/*
+ * SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include <stdio.h>
+#include <inttypes.h>
+#include "unity.h"
+#include "unity_test_utils.h"
+#include "esp_attr.h"
+#include "driver/gptimer_etm.h"
+#include "driver/gptimer.h"
+#include "driver/gpio.h"
+#include "driver/mcpwm_prelude.h"
+
+TEST_CASE("mcpwm_comparator_etm_event", "[etm]")
+{
+    // MCPWM cmpra -------------------------------------> ETM channel A ---> GPTimer start
+    // MCPWM cmprb / evt_cmpra (if support evt_cmpr) ---> ETM channel B ---> GPTimer stop
+
+    mcpwm_timer_config_t timer_config = {
+        .group_id = 0,
+        .clk_src = MCPWM_TIMER_CLK_SRC_DEFAULT,
+        .resolution_hz = 1 * 1000 * 1000,
+        .count_mode = MCPWM_TIMER_COUNT_MODE_UP,
+        .period_ticks = 10000,
+    };
+    mcpwm_timer_handle_t timer = NULL;
+    TEST_ESP_OK(mcpwm_new_timer(&timer_config, &timer));
+
+    mcpwm_operator_config_t operator_config = {
+        .group_id = 0,
+    };
+    mcpwm_oper_handle_t oper = NULL;
+    TEST_ESP_OK(mcpwm_new_operator(&operator_config, &oper));
+    TEST_ESP_OK(mcpwm_operator_connect_timer(oper, timer));
+
+    TEST_ESP_OK(mcpwm_timer_enable(timer));
+
+    // install comparator
+    int cmpa = 2000, cmpb = 8000;
+    mcpwm_cmpr_handle_t comparator_a = NULL;
+    mcpwm_cmpr_handle_t comparator_b = NULL;
+    mcpwm_comparator_config_t comparator_config = {
+        .flags.update_cmp_on_tez = true,
+    };
+    TEST_ESP_OK(mcpwm_new_comparator(oper, &comparator_config, &comparator_a));
+    TEST_ESP_OK(mcpwm_new_comparator(oper, &comparator_config, &comparator_b));
+    TEST_ESP_OK(mcpwm_comparator_set_compare_value(comparator_a, cmpa));
+    TEST_ESP_OK(mcpwm_comparator_set_compare_value(comparator_b, cmpb));
+
+    // install generator
+    const uint32_t gen_gpio = 10;
+    mcpwm_gen_handle_t generator = NULL;
+    mcpwm_generator_config_t generator_config = {
+        .gen_gpio_num = gen_gpio,
+    };
+    TEST_ESP_OK(mcpwm_new_generator(oper, &generator_config, &generator));
+    TEST_ESP_OK(mcpwm_generator_set_action_on_compare_event(generator,
+                MCPWM_GEN_COMPARE_EVENT_ACTION(MCPWM_TIMER_DIRECTION_UP, comparator_a, MCPWM_GEN_ACTION_HIGH)));
+    TEST_ESP_OK(mcpwm_generator_set_action_on_compare_event(generator,
+                MCPWM_GEN_COMPARE_EVENT_ACTION(MCPWM_TIMER_DIRECTION_UP, comparator_b, MCPWM_GEN_ACTION_LOW)));
+
+    // allocate etm channels
+    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 mcpwm comparator event\r\n");
+    esp_etm_event_handle_t mcpwm_etm_event1 = NULL;
+    esp_etm_event_handle_t mcpwm_etm_event2 = NULL;
+    mcpwm_cmpr_etm_event_config_t mcpwm_cmpr_event_config = {
+        .event_type = MCPWM_CMPR_ETM_EVENT_EQUAL,
+    };
+    TEST_ESP_OK(mcpwm_comparator_new_etm_event(comparator_a, &mcpwm_cmpr_event_config, &mcpwm_etm_event1));
+
+#if SOC_MCPWM_SUPPORT_EVENT_COMPARATOR
+    // install event comparator
+    cmpb = 5000;
+    mcpwm_cmpr_handle_t event_comparator = NULL;
+    mcpwm_event_comparator_config_t event_comparator_config = {};
+    TEST_ESP_OK(mcpwm_new_event_comparator(oper, &event_comparator_config, &event_comparator));
+    TEST_ESP_OK(mcpwm_comparator_set_compare_value(event_comparator, cmpb));
+    TEST_ESP_OK(mcpwm_comparator_new_etm_event(event_comparator, &mcpwm_cmpr_event_config, &mcpwm_etm_event2));
+#else
+    TEST_ESP_OK(mcpwm_comparator_new_etm_event(comparator_b, &mcpwm_cmpr_event_config, &mcpwm_etm_event2));
+#endif // SOC_MCPWM_SUPPORT_EVENT_COMPARATOR
+
+    printf("create a gptimer\r\n");
+    gptimer_handle_t gptimer = NULL;
+    gptimer_config_t gptimer_config = {
+        .clk_src = GPTIMER_CLK_SRC_DEFAULT,
+        .direction = GPTIMER_COUNT_UP,
+        .resolution_hz = 1 * 1000 * 1000, // 1MHz, 1 tick = 1us
+    };
+    TEST_ESP_OK(gptimer_new_timer(&gptimer_config, &gptimer));
+
+    printf("get gptimer etm task handle\r\n");
+    esp_etm_task_handle_t gptimer_task_start, gptimer_task_stop;
+    gptimer_etm_task_config_t gptimer_etm_task_conf = {
+        .task_type = GPTIMER_ETM_TASK_START_COUNT,
+    };
+    TEST_ESP_OK(gptimer_new_etm_task(gptimer, &gptimer_etm_task_conf, &gptimer_task_start));
+    gptimer_etm_task_conf.task_type = GPTIMER_ETM_TASK_STOP_COUNT;
+    TEST_ESP_OK(gptimer_new_etm_task(gptimer, &gptimer_etm_task_conf, &gptimer_task_stop));
+
+    printf("enable timer\r\n");
+    TEST_ESP_OK(gptimer_enable(gptimer));
+
+    printf("connect event and task to the channel\r\n");
+    TEST_ESP_OK(esp_etm_channel_connect(etm_channel_a, mcpwm_etm_event1, gptimer_task_start));
+    TEST_ESP_OK(esp_etm_channel_connect(etm_channel_b, mcpwm_etm_event2, gptimer_task_stop));
+
+    printf("enable etm channel\r\n");
+    TEST_ESP_OK(esp_etm_channel_enable(etm_channel_a));
+    TEST_ESP_OK(esp_etm_channel_enable(etm_channel_b));
+
+    TEST_ESP_OK(mcpwm_timer_start_stop(timer, MCPWM_TIMER_START_NO_STOP));
+    esp_rom_delay_us(100 * 1000);
+    TEST_ESP_OK(mcpwm_timer_start_stop(timer, MCPWM_TIMER_STOP_EMPTY));
+
+    uint64_t cur_count_val = 0;
+    TEST_ESP_OK(gptimer_get_raw_count(gptimer, &cur_count_val));
+    printf("cur_count_val: %llu\r\n", cur_count_val);
+    TEST_ASSERT_UINT_WITHIN(100, (cmpb - cmpa) * 10, cur_count_val);
+
+    // delete gptimer
+    TEST_ESP_OK(gptimer_disable(gptimer));
+    TEST_ESP_OK(gptimer_del_timer(gptimer));
+
+    // delete etm primitives
+    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(gptimer_task_start));
+    TEST_ESP_OK(esp_etm_del_task(gptimer_task_stop));
+    TEST_ESP_OK(esp_etm_del_event(mcpwm_etm_event1));
+    TEST_ESP_OK(esp_etm_del_event(mcpwm_etm_event2));
+    TEST_ESP_OK(esp_etm_del_channel(etm_channel_a));
+    TEST_ESP_OK(esp_etm_del_channel(etm_channel_b));
+    TEST_ESP_OK(mcpwm_timer_disable(timer));
+    TEST_ESP_OK(mcpwm_del_generator(generator));
+    TEST_ESP_OK(mcpwm_del_comparator(comparator_a));
+    TEST_ESP_OK(mcpwm_del_comparator(comparator_b));
+#if SOC_MCPWM_SUPPORT_EVENT_COMPARATOR
+    TEST_ESP_OK(mcpwm_del_comparator(event_comparator));
+#endif
+    TEST_ESP_OK(mcpwm_del_operator(oper));
+    TEST_ESP_OK(mcpwm_del_timer(timer));
+}

+ 26 - 0
components/hal/esp32c6/include/hal/mcpwm_ll.h

@@ -22,6 +22,7 @@
 #include "hal/mcpwm_types.h"
 #include "hal/misc.h"
 #include "hal/assert.h"
+#include "soc/soc_etm_source.h"
 
 #ifdef __cplusplus
 extern "C" {
@@ -59,6 +60,12 @@ extern "C" {
 #define MCPWM_LL_GEN_ACTION_TO_REG_CAL(action) ((uint8_t[]) {0, 1, 2, 3}[(action)])
 #define MCPWM_LL_BRAKE_MODE_TO_REG_VAL(mode)  ((uint8_t[]) {0, 1}[(mode)])
 
+// MCPWM ETM comparator event table
+#define MCPWM_LL_ETM_COMPARATOR_EVENT_TABLE(group, oper_id, cmpr_id, event)                           \
+    (uint32_t [1][MCPWM_CMPR_ETM_EVENT_MAX]){{                                                        \
+                            [MCPWM_CMPR_ETM_EVENT_EQUAL] = MCPWM_EVT_OP0_TEA + oper_id + 3 * cmpr_id, \
+    }}[group][event]
+
 /**
  * @brief The dead time module's clock source
  */
@@ -1595,6 +1602,25 @@ static inline void mcpwm_ll_capture_set_prescale(mcpwm_dev_t *mcpwm, int channel
     HAL_FORCE_MODIFY_U32_REG_FIELD(mcpwm->cap_chn_cfg[channel], capn_prescale, prescale - 1);
 }
 
+//////////////////////////////////////////MCPWM ETM Specific////////////////////////////////////////////////////////////
+
+/**
+ * @brief Enable comparator ETM event
+ *
+ * @param mcpwm Peripheral instance address
+ * @param operator_id Operator ID, index from 0 to 2
+ * @param cmpr_id Comparator ID, index from 0 to 2
+ * @param en True: enable ETM module, False: disable ETM module
+ */
+static inline void mcpwm_ll_etm_enable_comparator_event(mcpwm_dev_t *mcpwm, int operator_id, int cmpr_id, bool en)
+{
+    if (en) {
+        mcpwm->evt_en.val |= 1 << (operator_id + 3 * cmpr_id + 9) ;
+    } else {
+        mcpwm->evt_en.val &= ~(1 << (operator_id + 3 * cmpr_id + 9)) ;
+    }
+}
+
 //////////////////////////////////////////Deprecated Functions//////////////////////////////////////////////////////////
 /////////////////////////////The following functions are only used by the legacy driver/////////////////////////////////
 /////////////////////////////They might be removed in the next major release (ESP-IDF 6.0)//////////////////////////////

+ 26 - 0
components/hal/esp32h2/include/hal/mcpwm_ll.h

@@ -20,6 +20,7 @@
 #include "hal/mcpwm_types.h"
 #include "hal/misc.h"
 #include "hal/assert.h"
+#include "soc/soc_etm_source.h"
 
 #ifdef __cplusplus
 extern "C" {
@@ -57,6 +58,12 @@ extern "C" {
 #define MCPWM_LL_GEN_ACTION_TO_REG_CAL(action) ((uint8_t[]) {0, 1, 2, 3}[(action)])
 #define MCPWM_LL_BRAKE_MODE_TO_REG_VAL(mode)  ((uint8_t[]) {0, 1}[(mode)])
 
+// MCPWM ETM comparator event table
+#define MCPWM_LL_ETM_COMPARATOR_EVENT_TABLE(group, oper_id, cmpr_id, event)                           \
+    (uint32_t [1][MCPWM_CMPR_ETM_EVENT_MAX]){{                                                        \
+                            [MCPWM_CMPR_ETM_EVENT_EQUAL] = MCPWM_EVT_OP0_TEA + oper_id + 3 * cmpr_id, \
+    }}[group][event]
+
 /**
  * @brief The dead time module's clock source
  */
@@ -1593,6 +1600,25 @@ static inline void mcpwm_ll_capture_set_prescale(mcpwm_dev_t *mcpwm, int channel
     HAL_FORCE_MODIFY_U32_REG_FIELD(mcpwm->cap_chn_cfg[channel], capn_prescale, prescale - 1);
 }
 
+//////////////////////////////////////////MCPWM ETM Specific////////////////////////////////////////////////////////////
+
+/**
+ * @brief Enable comparator ETM event
+ *
+ * @param mcpwm Peripheral instance address
+ * @param operator_id Operator ID, index from 0 to 2
+ * @param cmpr_id Comparator ID, index from 0 to 2
+ * @param en True: enable ETM module, False: disable ETM module
+ */
+static inline void mcpwm_ll_etm_enable_comparator_event(mcpwm_dev_t *mcpwm, int operator_id, int cmpr_id, bool en)
+{
+    if (en) {
+        mcpwm->evt_en.val |= 1 << (operator_id + 3 * cmpr_id + 9) ;
+    } else {
+        mcpwm->evt_en.val &= ~(1 << (operator_id + 3 * cmpr_id + 9)) ;
+    }
+}
+
 //////////////////////////////////////////Deprecated Functions//////////////////////////////////////////////////////////
 /////////////////////////////The following functions are only used by the legacy driver/////////////////////////////////
 /////////////////////////////They might be removed in the next major release (ESP-IDF 6.0)//////////////////////////////

+ 65 - 5
components/hal/esp32p4/include/hal/mcpwm_ll.h

@@ -23,6 +23,8 @@
 #include "hal/misc.h"
 #include "hal/assert.h"
 #include <stdio.h>
+#include "soc/soc_etm_source.h"
+
 #ifdef __cplusplus
 extern "C" {
 #endif
@@ -59,6 +61,28 @@ extern "C" {
 #define MCPWM_LL_GEN_ACTION_TO_REG_CAL(action) ((uint8_t[]) {0, 1, 2, 3}[(action)])
 #define MCPWM_LL_BRAKE_MODE_TO_REG_VAL(mode)  ((uint8_t[]) {0, 1}[(mode)])
 
+// MCPWM ETM comparator event table
+#define MCPWM_LL_ETM_COMPARATOR_EVENT_TABLE(group, oper_id, cmpr_id, event)            \
+    (uint32_t[2][MCPWM_CMPR_ETM_EVENT_MAX]){                                           \
+        {                                                                              \
+            [MCPWM_CMPR_ETM_EVENT_EQUAL] = MCPWM0_EVT_OP0_TEA + oper_id + 3 * cmpr_id, \
+        },                                                                             \
+        {                                                                              \
+            [MCPWM_CMPR_ETM_EVENT_EQUAL] = MCPWM1_EVT_OP0_TEA + oper_id + 3 * cmpr_id, \
+        },                                                                             \
+    }[group][event]
+
+// MCPWM ETM event comparator event table
+#define MCPWM_LL_ETM_EVENT_COMPARATOR_EVENT_TABLE(group, oper_id, cmpr_id, event)       \
+    (uint32_t[2][MCPWM_CMPR_ETM_EVENT_MAX]){                                            \
+        {                                                                               \
+            [MCPWM_CMPR_ETM_EVENT_EQUAL] = MCPWM0_EVT_OP0_TEE1 + oper_id + 3 * cmpr_id, \
+        },                                                                              \
+        {                                                                               \
+            [MCPWM_CMPR_ETM_EVENT_EQUAL] = MCPWM1_EVT_OP0_TEE1 + oper_id + 3 * cmpr_id, \
+        },                                                                              \
+    }[group][event]
+
 /**
  * @brief The dead time module's clock source
  */
@@ -652,17 +676,17 @@ static inline void mcpwm_ll_operator_set_compare_value(mcpwm_dev_t *mcpwm, int o
 }
 
 /**
- * @brief Set equal value for operator
+ * @brief Set equal value for operator event comparator
  *
  * @param mcpwm Peripheral instance address
  * @param operator_id Operator ID, index from 0 to 2
- * @param equal_id Equal ID, index from 0 to 1
- * @param equal_value Equal value
+ * @param event_cmpr_id Event Comparator ID, index from 0 to 1
+ * @param compare_value Compare value
  */
 __attribute__((always_inline))
-static inline void mcpwm_ll_operator_set_equal_value(mcpwm_dev_t *mcpwm, int operator_id, int event_cmpr_id, uint32_t equal_value)
+static inline void mcpwm_ll_operator_set_event_compare_value(mcpwm_dev_t *mcpwm, int operator_id, int event_cmpr_id, uint32_t compare_value)
 {
-    HAL_FORCE_MODIFY_U32_REG_FIELD(mcpwm->operators_timestamp[operator_id].timestamp[event_cmpr_id], op_tstmp_e, equal_value);
+    HAL_FORCE_MODIFY_U32_REG_FIELD(mcpwm->operators_timestamp[operator_id].timestamp[event_cmpr_id], op_tstmp_e, compare_value);
 }
 
 /**
@@ -1623,6 +1647,42 @@ static inline void mcpwm_ll_capture_set_prescale(mcpwm_dev_t *mcpwm, int channel
     HAL_FORCE_MODIFY_U32_REG_FIELD(mcpwm->cap_chn_cfg[channel], capn_prescale, prescale - 1);
 }
 
+//////////////////////////////////////////MCPWM ETM Specific////////////////////////////////////////////////////////////
+
+/**
+ * @brief Enable comparator ETM event
+ *
+ * @param mcpwm Peripheral instance address
+ * @param operator_id Operator ID, index from 0 to 2
+ * @param cmpr_id Comparator ID, index from 0 to 2
+ * @param en True: enable ETM module, False: disable ETM module
+ */
+static inline void mcpwm_ll_etm_enable_comparator_event(mcpwm_dev_t *mcpwm, int operator_id, int cmpr_id, bool en)
+{
+    if (en) {
+        mcpwm->evt_en.val |= 1 << (operator_id + 3 * cmpr_id + 9);
+    } else {
+        mcpwm->evt_en.val &= ~(1 << (operator_id + 3 * cmpr_id + 9));
+    }
+}
+
+/**
+ * @brief Enable event_comparator ETM event
+ *
+ * @param mcpwm Peripheral instance address
+ * @param operator_id Operator ID, index from 0 to 2
+ * @param evt_cmpr_id Event comparator ID, index from 0 to 2
+ * @param en True: enable ETM module, False: disable ETM module
+ */
+static inline void mcpwm_ll_etm_enable_evt_comparator_event(mcpwm_dev_t *mcpwm, int operator_id, int evt_cmpr_id, bool en)
+{
+    if (en) {
+        mcpwm->evt_en2.val |= 1 << (operator_id + 3 * evt_cmpr_id);
+    } else {
+        mcpwm->evt_en2.val &= ~(1 << (operator_id + 3 * evt_cmpr_id));
+    }
+}
+
 //////////////////////////////////////////Deprecated Functions//////////////////////////////////////////////////////////
 /////////////////////////////The following functions are only used by the legacy driver/////////////////////////////////
 /////////////////////////////They might be removed in the next major release (ESP-IDF 6.0)//////////////////////////////

+ 8 - 0
components/hal/include/hal/mcpwm_types.h

@@ -105,6 +105,14 @@ typedef enum {
     MCPWM_CAP_EDGE_NEG, /*!< Capture on the negative edge */
 } mcpwm_capture_edge_t;
 
+/**
+ * @brief MCPWM comparator specific events that supported by the ETM module
+ */
+typedef enum {
+    MCPWM_CMPR_ETM_EVENT_EQUAL, /*!< The count value equals the value of comparator */
+    MCPWM_CMPR_ETM_EVENT_MAX,   /*!< Maximum number of comparator events */
+} mcpwm_comparator_etm_event_type_t;
+
 #ifdef __cplusplus
 }
 #endif

+ 1 - 1
components/hal/timer_hal.c

@@ -17,7 +17,7 @@ void timer_hal_init(timer_hal_context_t *hal, uint32_t group_num, uint32_t timer
     timer_ll_enable_counter(hal->dev, timer_num, false);
     timer_ll_enable_auto_reload(hal->dev, timer_num, false);
     timer_ll_enable_alarm(hal->dev, timer_num, false);
-    // enable RTM subsystem if available
+    // enable ETM subsystem if available
 #if SOC_TIMER_SUPPORT_ETM
     timer_ll_enable_etm(hal->dev, true);
 #endif

+ 8 - 0
components/soc/esp32p4/include/soc/Kconfig.soc_caps.in

@@ -547,6 +547,10 @@ config SOC_MCPWM_COMPARATORS_PER_OPERATOR
     int
     default 2
 
+config SOC_MCPWM_EVENT_COMPARATORS_PER_OPERATOR
+    int
+    default 2
+
 config SOC_MCPWM_GENERATORS_PER_OPERATOR
     int
     default 2
@@ -579,6 +583,10 @@ config SOC_MCPWM_SUPPORT_ETM
     bool
     default y
 
+config SOC_MCPWM_SUPPORT_EVENT_COMPARATOR
+    bool
+    default y
+
 config SOC_MCPWM_CAPTURE_CLK_FROM_GROUP
     bool
     default y

+ 2 - 0
components/soc/esp32p4/include/soc/soc_caps.h

@@ -290,6 +290,7 @@
 #define SOC_MCPWM_TIMERS_PER_GROUP           (3)    ///< The number of timers that each group has
 #define SOC_MCPWM_OPERATORS_PER_GROUP        (3)    ///< The number of operators that each group has
 #define SOC_MCPWM_COMPARATORS_PER_OPERATOR   (2)    ///< The number of comparators that each operator has
+#define SOC_MCPWM_EVENT_COMPARATORS_PER_OPERATOR   (2)    ///< The number of event comparators that each operator has
 #define SOC_MCPWM_GENERATORS_PER_OPERATOR    (2)    ///< The number of generators that each operator has
 #define SOC_MCPWM_TRIGGERS_PER_OPERATOR      (2)    ///< The number of triggers that each operator has
 #define SOC_MCPWM_GPIO_FAULTS_PER_GROUP      (3)    ///< The number of fault signal detectors that each group has
@@ -298,6 +299,7 @@
 #define SOC_MCPWM_GPIO_SYNCHROS_PER_GROUP    (3)    ///< The number of GPIO synchros that each group has
 #define SOC_MCPWM_SWSYNC_CAN_PROPAGATE       (1)    ///< Software sync event can be routed to its output
 #define SOC_MCPWM_SUPPORT_ETM                (1)    ///< Support ETM (Event Task Matrix)
+#define SOC_MCPWM_SUPPORT_EVENT_COMPARATOR   (1)    ///< Support event comparator (based on ETM)
 #define SOC_MCPWM_CAPTURE_CLK_FROM_GROUP     (1)    ///< Capture timer shares clock with other PWM timers
 
 /*------------------------ USB SERIAL JTAG CAPS ------------------------------*/

+ 1 - 0
docs/en/api-reference/peripherals/etm.rst

@@ -69,6 +69,7 @@ Other Peripheral Events
     :SOC_SYSTIMER_SUPPORT_ETM: - Refer to :doc:`/api-reference/system/esp_timer` for how to get the ETM event handle from esp_timer.
     :SOC_TIMER_SUPPORT_ETM: - Refer to :doc:`/api-reference/peripherals/gptimer` for how to get the ETM event handle from GPTimer.
     :SOC_GDMA_SUPPORT_ETM: - Refer to :doc:`/api-reference/system/async_memcpy` for how to get the ETM event handle from async memcpy.
+    :SOC_MCPWM_SUPPORT_ETM: - Refer to :doc:`/api-reference/peripherals/mcpwm` for how to get the ETM event handle from MCPWM.
 
 .. _etm-task:
 

+ 34 - 18
docs/en/api-reference/peripherals/mcpwm.rst

@@ -34,23 +34,25 @@ Functional Overview
 
 Description of the MCPWM functionality is divided into the following sections:
 
-- :ref:`mcpwm-resource-allocation-and-initialization` - covers how to allocate various MCPWM objects, like timers, operators, comparators, generators and so on. These objects are the basis of the following IO setting and control functions.
-- :ref:`mcpwm-timer-operations-and-events` - describes control functions and event callbacks supported by the MCPWM timer.
-- :ref:`mcpwm-comparator-operations-and-events` - describes control functions and event callbacks supported by the MCPWM comparator.
-- :ref:`mcpwm-generator-actions-on-events` - describes how to set actions for MCPWM generators on particular events that are generated by the MCPWM timer and comparators.
-- :ref:`mcpwm-classical-pwm-waveforms-and-generator-configurations` - demonstrates some classical PWM waveforms that can be achieved by configuring generator actions.
-- :ref:`mcpwm-dead-time` - describes how to set dead time for MCPWM generators.
-- :ref:`mcpwm-classical-pwm-waveforms-and-dead-time-configurations` - demonstrates some classical PWM waveforms that can be achieved by configuring dead time.
-- :ref:`mcpwm-carrier-modulation` - describes how to set and modulate a high frequency onto the final PWM waveforms.
-- :ref:`mcpwm-faults-and-brake-actions` - describes how to set brake actions for MCPWM operators on particular fault events.
-- :ref:`mcpwm-generator-force-actions` - describes how to control the generator output level asynchronously in a forceful way.
-- :ref:`mcpwm-synchronization` - describes how to synchronize the MCPWM timers and get a fixed phase difference between the generated PWM signals.
-- :ref:`mcpwm-capture` - describes how to use the MCPWM capture module to measure the pulse width of a signal.
-- :ref:`mcpwm-power-management` - describes how different source clocks affects power consumption.
-- :ref:`mcpwm-iram-safe` - describes tips on how to make the RMT interrupt work better along with a disabled cache.
-- :ref:`mcpwm-thread-safety` - lists which APIs are guaranteed to be thread-safe by the driver.
-- :ref:`mcpwm-kconfig-options` - lists the supported Kconfig options that can bring different effects to the driver.
-
+.. list::
+
+    - :ref:`mcpwm-resource-allocation-and-initialization` - covers how to allocate various MCPWM objects, like timers, operators, comparators, generators and so on. These objects are the basis of the following IO setting and control functions.
+    - :ref:`mcpwm-timer-operations-and-events` - describes control functions and event callbacks supported by the MCPWM timer.
+    - :ref:`mcpwm-comparator-operations-and-events` - describes control functions and event callbacks supported by the MCPWM comparator.
+    - :ref:`mcpwm-generator-actions-on-events` - describes how to set actions for MCPWM generators on particular events that are generated by the MCPWM timer and comparators.
+    - :ref:`mcpwm-classical-pwm-waveforms-and-generator-configurations` - demonstrates some classical PWM waveforms that can be achieved by configuring generator actions.
+    - :ref:`mcpwm-dead-time` - describes how to set dead time for MCPWM generators.
+    - :ref:`mcpwm-classical-pwm-waveforms-and-dead-time-configurations` - demonstrates some classical PWM waveforms that can be achieved by configuring dead time.
+    - :ref:`mcpwm-carrier-modulation` - describes how to set and modulate a high frequency onto the final PWM waveforms.
+    - :ref:`mcpwm-faults-and-brake-actions` - describes how to set brake actions for MCPWM operators on particular fault events.
+    - :ref:`mcpwm-generator-force-actions` - describes how to control the generator output level asynchronously in a forceful way.
+    - :ref:`mcpwm-synchronization` - describes how to synchronize the MCPWM timers and get a fixed phase difference between the generated PWM signals.
+    - :ref:`mcpwm-capture` - describes how to use the MCPWM capture module to measure the pulse width of a signal.
+    :SOC_MCPWM_SUPPORT_ETM: - :ref:`mcpwm-etm-event-and-task` - describes what the events and tasks can be connected to the ETM channel.
+    - :ref:`mcpwm-power-management` - describes how different source clocks affects power consumption.
+    - :ref:`mcpwm-iram-safe` - describes tips on how to make the RMT interrupt work better along with a disabled cache.
+    - :ref:`mcpwm-thread-safety` - lists which APIs are guaranteed to be thread-safe by the driver.
+    - :ref:`mcpwm-kconfig-options` - lists the supported Kconfig options that can bring different effects to the driver.
 
 .. _mcpwm-resource-allocation-and-initialization:
 
@@ -934,8 +936,22 @@ Trigger a Software Capture Event
 
 Sometimes, the software also wants to trigger a "fake" capture event. The :cpp:func:`mcpwm_capture_channel_trigger_soft_catch` is provided for that purpose. Please note that, even though it is a "fake" capture event, it can still cause an interrupt, thus your capture event callback function gets invoked as well.
 
+.. only:: SOC_MCPWM_SUPPORT_ETM
+
+    .. _mcpwm-etm-event-and-task:
+
+    ETM Event and Task
+    ^^^^^^^^^^^^^^^^^^
+
+    MCPWM comparator is able to generate events that can interact with the :doc:`ETM </api-reference/peripherals/etm>` module. The supported events are listed in the :cpp:type:`mcpwm_comparator_etm_event_type_t`. You can call :cpp:func:`mcpwm_comparator_new_etm_event` to get the corresponding ETM event handle.
+
+    For how to connect the event and task to an ETM channel, please refer to the :doc:`ETM </api-reference/peripherals/etm>` documentation.
+
+    .. _mcpwm-power-management:
+
+.. only:: not SOC_MCPWM_SUPPORT_ETM
 
-.. _mcpwm-power-management:
+    .. _mcpwm-power-management:
 
 Power Management
 ^^^^^^^^^^^^^^^^

+ 1 - 0
docs/zh_CN/api-reference/peripherals/etm.rst

@@ -69,6 +69,7 @@ GPIO **边沿** 事件是最常见的事件类型,任何 GPIO 管脚均可触
     :SOC_SYSTIMER_SUPPORT_ETM: - 要了解如何从 esp_timer 获取 ETM 事件句柄,请参阅 :doc:`/api-reference/system/esp_timer`。
     :SOC_TIMER_SUPPORT_ETM: - 要了解如何从 GPTimer 获取 ETM 事件句柄,请参阅 :doc:`/api-reference/peripherals/gptimer`。
     :SOC_GDMA_SUPPORT_ETM: - 要了解如何从 async memcpy 获取 ETM 事件句柄,请参阅 :doc:`/api-reference/system/async_memcpy`。
+    :SOC_MCPWM_SUPPORT_ETM: - 要了解如何从 MCPWM 中获取 ETM 事件句柄,请参阅 :doc:`/api-reference/peripherals/mcpwm`。
 
 .. _etm-task:
 

+ 34 - 18
docs/zh_CN/api-reference/peripherals/mcpwm.rst

@@ -34,23 +34,25 @@ MCPWM 外设是一个多功能 PWM 生成器,集成多个子模块,在电力
 
 下文将分节概述 MCPWM 的功能:
 
-- :ref:`mcpwm-resource-allocation-and-initialization` - 介绍各类 MCPWM 模块的分配,如定时器、操作器、比较器、生成器等。随后介绍的 IO 设置和控制功能也将围绕这些模块进行。
-- :ref:`mcpwm-timer-operations-and-events` - 介绍 MCPWM 定时器支持的控制功能和事件回调。
-- :ref:`mcpwm-comparator-operations-and-events` - 介绍 MCPWM 比较器支持的控制功能和事件回调。
-- :ref:`mcpwm-generator-actions-on-events` - 介绍如何针对 MCPWM 定时器和比较器生成的特定事件,设置 MCPWM 生成器的相应执行操作。
-- :ref:`mcpwm-classical-pwm-waveforms-and-generator-configurations` - 介绍一些经典 PWM 波形的生成器配置。
-- :ref:`mcpwm-dead-time` - 介绍如何设置 MCPWM 生成器的死区时间。
-- :ref:`mcpwm-classical-pwm-waveforms-and-dead-time-configurations` - 介绍一些经典 PWM 波形的死区配置。
-- :ref:`mcpwm-carrier-modulation` - 介绍如何在最终输出的 PWM 波形上调制高频载波。
-- :ref:`mcpwm-faults-and-brake-actions` - 介绍如何为 MCPWM 操作器配置特定故障事件下的制动操作。
-- :ref:`mcpwm-generator-force-actions` - 介绍如何强制异步控制生成器的输出水平。
-- :ref:`mcpwm-synchronization` - 介绍如何同步 MCPWM 定时器,并确保生成的最终输出 PWM 信号具有固定的相位差。
-- :ref:`mcpwm-capture` - 介绍如何使用 MCPWM 捕获模块测量信号脉宽。
-- :ref:`mcpwm-power-management` - 介绍不同的时钟源对功耗的影响。
-- :ref:`mcpwm-iram-safe` - 介绍如何协调 RMT 中断与禁用缓存。
-- :ref:`mcpwm-thread-safety` - 列出了由驱动程序认证为线程安全的 API。
-- :ref:`mcpwm-kconfig-options` - 列出了针对驱动的数个 Kconfig 支持选项。
-
+.. list::
+
+    - :ref:`mcpwm-resource-allocation-and-initialization` - 介绍各类 MCPWM 模块的分配,如定时器、操作器、比较器、生成器等。随后介绍的 IO 设置和控制功能也将围绕这些模块进行。
+    - :ref:`mcpwm-timer-operations-and-events` - 介绍 MCPWM 定时器支持的控制功能和事件回调。
+    - :ref:`mcpwm-comparator-operations-and-events` - 介绍 MCPWM 比较器支持的控制功能和事件回调。
+    - :ref:`mcpwm-generator-actions-on-events` - 介绍如何针对 MCPWM 定时器和比较器生成的特定事件,设置 MCPWM 生成器的相应执行操作。
+    - :ref:`mcpwm-classical-pwm-waveforms-and-generator-configurations` - 介绍一些经典 PWM 波形的生成器配置。
+    - :ref:`mcpwm-dead-time` - 介绍如何设置 MCPWM 生成器的死区时间。
+    - :ref:`mcpwm-classical-pwm-waveforms-and-dead-time-configurations` - 介绍一些经典 PWM 波形的死区配置。
+    - :ref:`mcpwm-carrier-modulation` - 介绍如何在最终输出的 PWM 波形上调制高频载波。
+    - :ref:`mcpwm-faults-and-brake-actions` - 介绍如何为 MCPWM 操作器配置特定故障事件下的制动操作。
+    - :ref:`mcpwm-generator-force-actions` - 介绍如何强制异步控制生成器的输出水平。
+    - :ref:`mcpwm-synchronization` - 介绍如何同步 MCPWM 定时器,并确保生成的最终输出 PWM 信号具有固定的相位差。
+    - :ref:`mcpwm-capture` - 介绍如何使用 MCPWM 捕获模块测量信号脉宽。
+    :SOC_MCPWM_SUPPORT_ETM: - :ref:`mcpwm-etm-event-and-task` - MCPWM 提供了哪些事件和任务可以连接到 ETM 通道上。
+    - :ref:`mcpwm-power-management` - 介绍不同的时钟源对功耗的影响。
+    - :ref:`mcpwm-iram-safe` - 介绍如何协调 RMT 中断与禁用缓存。
+    - :ref:`mcpwm-thread-safety` - 列出了由驱动程序认证为线程安全的 API。
+    - :ref:`mcpwm-kconfig-options` - 列出了针对驱动的数个 Kconfig 支持选项。
 
 .. _mcpwm-resource-allocation-and-initialization:
 
@@ -934,8 +936,22 @@ MCPWM 捕获通道支持在信号上检测到有效边沿时发送通知。须
 
 某些场景下,可能存在需要软件触发“虚假”捕获事件的需求。此时,可以调用 :cpp:func:`mcpwm_capture_channel_trigger_soft_catch` 实现。需注意,此类“虚假”捕获事件仍然会触发中断,并从而调用捕获事件回调函数。
 
+.. only:: SOC_MCPWM_SUPPORT_ETM
+
+    .. _mcpwm-etm-event-and-task:
+
+    ETM 事件与任务
+    ^^^^^^^^^^^^^^^^^^
+
+    MCPWM 比较器可以产生事件,这些事件可以连接到 :doc:`ETM </api-reference/peripherals/etm>` 模块。:cpp:type:`mcpwm_comparator_etm_event_type_t` 中列出了 MCPWM 比较器能够产生的事件类型。用户可以通过调用 :cpp:func:`mcpwm_comparator_new_etm_event` 来获得相应事件的 ETM event 句柄。
+
+    关于如何将 MCPWM 比较器事件连接到 ETM 通道中,请参阅 :doc:`ETM </api-reference/peripherals/etm>` 文档。
+
+    .. _mcpwm-power-management:
+
+.. only:: not SOC_MCPWM_SUPPORT_ETM
 
-.. _mcpwm-power-management:
+    .. _mcpwm-power-management:
 
 电源管理
 ^^^^^^^^^^^^^^^^