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.
This commit is contained in:
Chen Jichang 2023-08-24 16:13:17 +08:00 committed by morris
parent bd0eac77b2
commit 7638235311
24 changed files with 685 additions and 82 deletions

View File

@ -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

View File

@ -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

View File

@ -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

View File

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

View File

@ -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

View File

@ -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;
}
}

View File

@ -0,0 +1,80 @@
/*
* 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"
#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;
}

View File

@ -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);

View File

@ -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;

View File

@ -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

View File

@ -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);

View File

@ -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);
}

View File

@ -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;
/**

View File

@ -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}

View File

@ -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_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));
}

View File

@ -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)//////////////////////////////

View File

@ -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)//////////////////////////////

View File

@ -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,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)//////////////////////////////

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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 ------------------------------*/

View File

@ -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 </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:
Power Management
^^^^^^^^^^^^^^^^

View File

@ -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 </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:
电源管理
^^^^^^^^^^^^^^^^