From 7638235311ead62b4bc646461cc87ab9ab16a9a8 Mon Sep 17 00:00:00 2001 From: Chen Jichang Date: Thu, 24 Aug 2023 16:13:17 +0800 Subject: [PATCH] feat(mcpwm): MCPWM event comparator driver In esp32p4, the MCPWM has a new feature, mainly ETM-oriented. Each operator has two event comparators, can generate an event when the count value of the timer that operator connects is equal to the value of event comparator. --- components/driver/CMakeLists.txt | 3 + .../driver/mcpwm/include/driver/mcpwm_cmpr.h | 25 +++ .../driver/mcpwm/include/driver/mcpwm_etm.h | 43 +++++ .../mcpwm/include/driver/mcpwm_prelude.h | 1 + components/driver/mcpwm/mcpwm_cap.c | 4 +- components/driver/mcpwm/mcpwm_cmpr.c | 164 ++++++++++++++---- components/driver/mcpwm/mcpwm_etm.c | 80 +++++++++ components/driver/mcpwm/mcpwm_fault.c | 4 +- components/driver/mcpwm/mcpwm_oper.c | 9 +- components/driver/mcpwm/mcpwm_private.h | 29 +++- components/driver/mcpwm/mcpwm_timer.c | 4 +- components/driver/pcnt/pulse_cnt.c | 2 +- .../include/esp_private/etm_interface.h | 1 + .../test_apps/etm/main/CMakeLists.txt | 4 + .../test_apps/etm/main/test_mcpwm_etm.c | 153 ++++++++++++++++ components/hal/esp32c6/include/hal/mcpwm_ll.h | 26 +++ components/hal/esp32h2/include/hal/mcpwm_ll.h | 27 +++ components/hal/esp32p4/include/hal/mcpwm_ll.h | 68 +++++++- components/hal/include/hal/mcpwm_types.h | 8 + components/hal/timer_hal.c | 2 +- .../esp32p4/include/soc/Kconfig.soc_caps.in | 8 + components/soc/esp32p4/include/soc/soc_caps.h | 2 + docs/en/api-reference/peripherals/mcpwm.rst | 50 ++++-- .../zh_CN/api-reference/peripherals/mcpwm.rst | 50 ++++-- 24 files changed, 685 insertions(+), 82 deletions(-) create mode 100644 components/driver/mcpwm/include/driver/mcpwm_etm.h create mode 100644 components/driver/mcpwm/mcpwm_etm.c create mode 100644 components/esp_hw_support/test_apps/etm/main/test_mcpwm_etm.c diff --git a/components/driver/CMakeLists.txt b/components/driver/CMakeLists.txt index 6ca5c3c09b..aed2bba6c0 100644 --- a/components/driver/CMakeLists.txt +++ b/components/driver/CMakeLists.txt @@ -134,6 +134,9 @@ 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() endif() # PCNT related source files diff --git a/components/driver/mcpwm/include/driver/mcpwm_cmpr.h b/components/driver/mcpwm/include/driver/mcpwm_cmpr.h index 0e5a77d8ab..189e9cf04f 100644 --- a/components/driver/mcpwm/include/driver/mcpwm_cmpr.h +++ b/components/driver/mcpwm/include/driver/mcpwm_cmpr.h @@ -28,6 +28,14 @@ typedef struct { } flags; /*!< Extra configuration flags for comparator */ } mcpwm_comparator_config_t; +/** + * @brief MCPWM event comparator configuration + */ +typedef struct { + +} mcpwm_event_comparator_config_t; + + /** * @brief Create MCPWM comparator * @@ -54,6 +62,23 @@ 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 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 + /** * @brief Group of supported MCPWM compare event callbacks * @note The callbacks are all running under ISR environment diff --git a/components/driver/mcpwm/include/driver/mcpwm_etm.h b/components/driver/mcpwm/include/driver/mcpwm_etm.h new file mode 100644 index 0000000000..f9ac6e16e9 --- /dev/null +++ b/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 +#include +#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 diff --git a/components/driver/mcpwm/include/driver/mcpwm_prelude.h b/components/driver/mcpwm/include/driver/mcpwm_prelude.h index d143d0f545..c3b2088b2d 100644 --- a/components/driver/mcpwm/include/driver/mcpwm_prelude.h +++ b/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" diff --git a/components/driver/mcpwm/mcpwm_cap.c b/components/driver/mcpwm/mcpwm_cap.c index 3b36e428d7..fda51f38c8 100644 --- a/components/driver/mcpwm/mcpwm_cap.c +++ b/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 diff --git a/components/driver/mcpwm/mcpwm_cmpr.c b/components/driver/mcpwm/mcpwm_cmpr.c index 77aae8f9cc..b68a05ac2b 100644 --- a/components/driver/mcpwm/mcpwm_cmpr.c +++ b/components/driver/mcpwm/mcpwm_cmpr.c @@ -33,13 +33,34 @@ 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 + default: + ESP_RETURN_ON_FALSE(false, ESP_ERR_INVALID_ARG, TAG, "unknown comparator type:%d", cmpr->type); + break; } + 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,40 +75,61 @@ 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 + } 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 @@ -98,14 +140,14 @@ esp_err_t mcpwm_new_comparator(mcpwm_oper_handle_t oper, const mcpwm_comparator_ 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; + cmpr->base.spinlock = (portMUX_TYPE)portMUX_INITIALIZER_UNLOCKED; + *ret_cmpr = &cmpr->base; ESP_LOGD(TAG, "new 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,21 @@ 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)); + mcpwm_ll_operator_enable_update_compare_on_sync(hal->dev, oper_id, cmpr_id, false); +#if SOC_MCPWM_SUPPORT_ETM + mcpwm_ll_etm_enable_comparator_event(hal->dev, oper_id, cmpr_id, false); +#endif + break; +#if SOC_MCPWM_SUPPORT_EVENT_COMPARATOR + case MCPWM_EVENT_COMPARATOR: + mcpwm_ll_etm_enable_evt_comparator_event(hal->dev, oper_id, cmpr_id, false); + break; +#endif + } portEXIT_CRITICAL(&group->spinlock); ESP_LOGD(TAG, "del comparator (%d,%d,%d)", group->group_id, oper_id, cmpr_id); @@ -130,6 +185,42 @@ 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"); + mcpwm_group_t *group = oper->group; + mcpwm_hal_context_t *hal = &group->hal; + int oper_id = oper->oper_id; + int cmpr_id = cmpr->base.cmpr_id; + + portENTER_CRITICAL(&group->spinlock); + mcpwm_ll_etm_enable_evt_comparator_event(hal->dev, oper_id, cmpr_id, true); + portEXIT_CRITICAL(&group->spinlock); + + // fill in other comparator members + cmpr->base.spinlock = (portMUX_TYPE)portMUX_INITIALIZER_UNLOCKED; + *ret_cmpr = &cmpr->base; + ESP_LOGD(TAG, "new comparator (%d,%d,%d) at %p", group->group_id, oper_id, cmpr_id, cmpr); + return ESP_OK; + +err: + if (cmpr) { + mcpwm_comparator_destroy(&cmpr->base); + } + return ret; +} +# endif + 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"); @@ -140,7 +231,16 @@ esp_err_t mcpwm_comparator_set_compare_value(mcpwm_cmpr_handle_t cmpr, uint32_t 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); + 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 + } portEXIT_CRITICAL_SAFE(&cmpr->spinlock); cmpr->compare_ticks = cmp_ticks; @@ -150,6 +250,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 +267,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; } } diff --git a/components/driver/mcpwm/mcpwm_etm.c b/components/driver/mcpwm/mcpwm_etm.c new file mode 100644 index 0000000000..aa91adfb73 --- /dev/null +++ b/components/driver/mcpwm/mcpwm_etm.c @@ -0,0 +1,80 @@ +/* + * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include "sdkconfig.h" +#if CONFIG_MCPWM_ENABLE_DEBUG_LOG +// The local log level must be defined before including esp_log.h +// Set the maximum log level for this source file +#define LOG_LOCAL_LEVEL ESP_LOG_DEBUG +#endif +#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"; + +static esp_err_t mcpwm_del_etm_event(esp_etm_event_t *event) +{ + free(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_etm_event_t *event = NULL; + esp_err_t ret = ESP_OK; + ESP_GOTO_ON_FALSE(cmpr && config && out_event, ESP_ERR_INVALID_ARG, err, TAG, "invalid argument"); + event = heap_caps_calloc(1, sizeof(esp_etm_event_t), MCPWM_MEM_ALLOC_CAPS); + ESP_GOTO_ON_FALSE(event, ESP_ERR_NO_MEM, err, 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: + event_id = MCPWM_LL_ETM_EVENT_COMPARATOR_EVENT_TABLE(group_id, oper_id, cmpr_id, config->event_type); + break; +#endif + } + 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->event_id = event_id; + event->trig_periph = ETM_TRIG_PERIPH_MCPWM; + event->del = mcpwm_del_etm_event; + *out_event = event; + return ESP_OK; + +err: + if (event) { + mcpwm_del_etm_event(event); + } + return ret; +} diff --git a/components/driver/mcpwm/mcpwm_fault.c b/components/driver/mcpwm/mcpwm_fault.c index 725e36dcb2..52070ca712 100644 --- a/components/driver/mcpwm/mcpwm_fault.c +++ b/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); diff --git a/components/driver/mcpwm/mcpwm_oper.c b/components/driver/mcpwm/mcpwm_oper.c index 73148b7348..27167d477e 100644 --- a/components/driver/mcpwm/mcpwm_oper.c +++ b/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; diff --git a/components/driver/mcpwm/mcpwm_private.h b/components/driver/mcpwm/mcpwm_private.h index 4f576857f9..e4a06e4ab7 100644 --- a/components/driver/mcpwm/mcpwm_private.h +++ b/components/driver/mcpwm/mcpwm_private.h @@ -44,6 +44,10 @@ 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; +#if SOC_MCPWM_SUPPORT_EVENT_COMPARATOR +typedef struct mcpwm_evt_cmpr_t mcpwm_evt_cmpr_t; +#endif 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 +111,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 +127,34 @@ 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 +#if SOC_MCPWM_SUPPORT_EVENT_COMPARATOR + MCPWM_EVENT_COMPARATOR, // event comparator +#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 }; +#if SOC_MCPWM_SUPPORT_EVENT_COMPARATOR +struct mcpwm_evt_cmpr_t { + mcpwm_cmpr_t base; // base class +}; +#endif + struct mcpwm_gen_t { int gen_id; // generator ID, index from 0 mcpwm_oper_t *oper; // which operator that the generator resides in diff --git a/components/driver/mcpwm/mcpwm_timer.c b/components/driver/mcpwm/mcpwm_timer.c index c0b176ba36..dfae398c71 100644 --- a/components/driver/mcpwm/mcpwm_timer.c +++ b/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); diff --git a/components/driver/pcnt/pulse_cnt.c b/components/driver/pcnt/pulse_cnt.c index b7721dd5ce..c8dee2346e 100644 --- a/components/driver/pcnt/pulse_cnt.c +++ b/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); } diff --git a/components/esp_hw_support/include/esp_private/etm_interface.h b/components/esp_hw_support/include/esp_private/etm_interface.h index 860d894760..351f89c702 100644 --- a/components/esp_hw_support/include/esp_private/etm_interface.h +++ b/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; /** diff --git a/components/esp_hw_support/test_apps/etm/main/CMakeLists.txt b/components/esp_hw_support/test_apps/etm/main/CMakeLists.txt index fb291cc16a..e824508d65 100644 --- a/components/esp_hw_support/test_apps/etm/main/CMakeLists.txt +++ b/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} diff --git a/components/esp_hw_support/test_apps/etm/main/test_mcpwm_etm.c b/components/esp_hw_support/test_apps/etm/main/test_mcpwm_etm.c new file mode 100644 index 0000000000..e5566021d2 --- /dev/null +++ b/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 +#include +#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_ETM_EVENT_CMPR_EQUAL_THRESHOLD, + }; + 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)); +} diff --git a/components/hal/esp32c6/include/hal/mcpwm_ll.h b/components/hal/esp32c6/include/hal/mcpwm_ll.h index 168f1a1e1d..4891a6ed43 100644 --- a/components/hal/esp32c6/include/hal/mcpwm_ll.h +++ b/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_ETM_COMPARATOR_EVENT_MAX]){{ \ + [MCPWM_ETM_EVENT_CMPR_EQUAL_THRESHOLD] = 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)////////////////////////////// diff --git a/components/hal/esp32h2/include/hal/mcpwm_ll.h b/components/hal/esp32h2/include/hal/mcpwm_ll.h index e6ae9ffc56..d99ecd148d 100644 --- a/components/hal/esp32h2/include/hal/mcpwm_ll.h +++ b/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,13 @@ 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_ETM_COMPARATOR_EVENT_MAX]){{ \ + [MCPWM_ETM_EVENT_CMPR_EQUAL_THRESHOLD] = MCPWM_EVT_OP0_TEA + oper_id + 3 * cmpr_id, \ + }}[group][event] + + /** * @brief The dead time module's clock source */ @@ -1593,6 +1601,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)////////////////////////////// diff --git a/components/hal/esp32p4/include/hal/mcpwm_ll.h b/components/hal/esp32p4/include/hal/mcpwm_ll.h index b35f4d8ee5..88e68b7715 100644 --- a/components/hal/esp32p4/include/hal/mcpwm_ll.h +++ b/components/hal/esp32p4/include/hal/mcpwm_ll.h @@ -23,6 +23,8 @@ #include "hal/misc.h" #include "hal/assert.h" #include +#include "soc/soc_etm_source.h" + #ifdef __cplusplus extern "C" { #endif @@ -59,6 +61,26 @@ 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_ETM_COMPARATOR_EVENT_MAX]){{ \ + [MCPWM_ETM_EVENT_CMPR_EQUAL_THRESHOLD] = MCPWM0_EVT_OP0_TEA + oper_id + 3 * cmpr_id, \ + }, \ + { \ + [MCPWM_ETM_EVENT_CMPR_EQUAL_THRESHOLD] = 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_ETM_COMPARATOR_EVENT_MAX]){{ \ + [MCPWM_ETM_EVENT_CMPR_EQUAL_THRESHOLD] = MCPWM0_EVT_OP0_TEE1 + oper_id + 3 * cmpr_id, \ + }, \ + { \ + [MCPWM_ETM_EVENT_CMPR_EQUAL_THRESHOLD] = MCPWM1_EVT_OP0_TEE1 + oper_id + 3 * cmpr_id, \ + }, \ + }[group][event] + /** * @brief The dead time module's clock source */ @@ -652,17 +674,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 +1645,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)////////////////////////////// diff --git a/components/hal/include/hal/mcpwm_types.h b/components/hal/include/hal/mcpwm_types.h index 2490a04e0c..4386278aba 100644 --- a/components/hal/include/hal/mcpwm_types.h +++ b/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_ETM_EVENT_CMPR_EQUAL_THRESHOLD, /* !< The count value of the timer that PWM operator connects is equal to the value of comparator */ + MCPWM_ETM_COMPARATOR_EVENT_MAX, /*!< Maximum number of comparator events */ +} mcpwm_comparator_etm_event_type_t; + #ifdef __cplusplus } #endif diff --git a/components/hal/timer_hal.c b/components/hal/timer_hal.c index 81e1229f78..cd1f37fe8d 100644 --- a/components/hal/timer_hal.c +++ b/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 diff --git a/components/soc/esp32p4/include/soc/Kconfig.soc_caps.in b/components/soc/esp32p4/include/soc/Kconfig.soc_caps.in index 4577f87f85..ed0ff6ae03 100644 --- a/components/soc/esp32p4/include/soc/Kconfig.soc_caps.in +++ b/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 diff --git a/components/soc/esp32p4/include/soc/soc_caps.h b/components/soc/esp32p4/include/soc/soc_caps.h index b31d3f1ac6..09105238d2 100644 --- a/components/soc/esp32p4/include/soc/soc_caps.h +++ b/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 ------------------------------*/ diff --git a/docs/en/api-reference/peripherals/mcpwm.rst b/docs/en/api-reference/peripherals/mcpwm.rst index 8383718594..366f91b0d9 100644 --- a/docs/en/api-reference/peripherals/mcpwm.rst +++ b/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-power-management: + .. _mcpwm-etm-event-and-task: + + ETM Event and Task + ^^^^^^^^^^^^^^^^^^ + + MCPWM comparator is able to generate events that can interact with the :doc:`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 ` documentation. + + .. _mcpwm-power-management: + +.. only:: not SOC_MCPWM_SUPPORT_ETM + + .. _mcpwm-power-management: Power Management ^^^^^^^^^^^^^^^^ diff --git a/docs/zh_CN/api-reference/peripherals/mcpwm.rst b/docs/zh_CN/api-reference/peripherals/mcpwm.rst index 5da5cc3923..073a44a1af 100644 --- a/docs/zh_CN/api-reference/peripherals/mcpwm.rst +++ b/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-power-management: + .. _mcpwm-etm-event-and-task: + + ETM 事件与任务 + ^^^^^^^^^^^^^^^^^^ + + MCPWM 比较器可以产生事件,这些事件可以连接到 :doc:`ETM ` 模块。:cpp:type:`mcpwm_comparator_etm_event_type_t` 中列出了 MCPWM 比较器能够产生的事件类型。用户可以通过调用 :cpp:func:`mcpwm_comparator_new_etm_event` 来获得相应事件的 ETM event 句柄。 + + 关于如何将 MCPWM 比较器事件连接到 ETM 通道中,请参阅 :doc:`ETM ` 文档。 + + .. _mcpwm-power-management: + +.. only:: not SOC_MCPWM_SUPPORT_ETM + + .. _mcpwm-power-management: 电源管理 ^^^^^^^^^^^^^^^^