Merge branch 'feat/mcpwm_event_comparator_driver' into 'master'

feat(MCPWM): MCPWM event comparator driver

Closes IDF-7989

See merge request espressif/esp-idf!25552
This commit is contained in:
morris 2023-09-12 14:41:07 +08:00
commit ae1a9bf015
28 changed files with 700 additions and 90 deletions

View File

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

View File

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

View File

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

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

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

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

View File

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

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

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_CMPR_ETM_EVENT_EQUAL,
};
TEST_ESP_OK(mcpwm_comparator_new_etm_event(comparator_a, &mcpwm_cmpr_event_config, &mcpwm_etm_event1));
#if SOC_MCPWM_SUPPORT_EVENT_COMPARATOR
// install event comparator
cmpb = 5000;
mcpwm_cmpr_handle_t event_comparator = NULL;
mcpwm_event_comparator_config_t event_comparator_config = {};
TEST_ESP_OK(mcpwm_new_event_comparator(oper, &event_comparator_config, &event_comparator));
TEST_ESP_OK(mcpwm_comparator_set_compare_value(event_comparator, cmpb));
TEST_ESP_OK(mcpwm_comparator_new_etm_event(event_comparator, &mcpwm_cmpr_event_config, &mcpwm_etm_event2));
#else
TEST_ESP_OK(mcpwm_comparator_new_etm_event(comparator_b, &mcpwm_cmpr_event_config, &mcpwm_etm_event2));
#endif // SOC_MCPWM_SUPPORT_EVENT_COMPARATOR
printf("create a gptimer\r\n");
gptimer_handle_t gptimer = NULL;
gptimer_config_t gptimer_config = {
.clk_src = GPTIMER_CLK_SRC_DEFAULT,
.direction = GPTIMER_COUNT_UP,
.resolution_hz = 1 * 1000 * 1000, // 1MHz, 1 tick = 1us
};
TEST_ESP_OK(gptimer_new_timer(&gptimer_config, &gptimer));
printf("get gptimer etm task handle\r\n");
esp_etm_task_handle_t gptimer_task_start, gptimer_task_stop;
gptimer_etm_task_config_t gptimer_etm_task_conf = {
.task_type = GPTIMER_ETM_TASK_START_COUNT,
};
TEST_ESP_OK(gptimer_new_etm_task(gptimer, &gptimer_etm_task_conf, &gptimer_task_start));
gptimer_etm_task_conf.task_type = GPTIMER_ETM_TASK_STOP_COUNT;
TEST_ESP_OK(gptimer_new_etm_task(gptimer, &gptimer_etm_task_conf, &gptimer_task_stop));
printf("enable timer\r\n");
TEST_ESP_OK(gptimer_enable(gptimer));
printf("connect event and task to the channel\r\n");
TEST_ESP_OK(esp_etm_channel_connect(etm_channel_a, mcpwm_etm_event1, gptimer_task_start));
TEST_ESP_OK(esp_etm_channel_connect(etm_channel_b, mcpwm_etm_event2, gptimer_task_stop));
printf("enable etm channel\r\n");
TEST_ESP_OK(esp_etm_channel_enable(etm_channel_a));
TEST_ESP_OK(esp_etm_channel_enable(etm_channel_b));
TEST_ESP_OK(mcpwm_timer_start_stop(timer, MCPWM_TIMER_START_NO_STOP));
esp_rom_delay_us(100 * 1000);
TEST_ESP_OK(mcpwm_timer_start_stop(timer, MCPWM_TIMER_STOP_EMPTY));
uint64_t cur_count_val = 0;
TEST_ESP_OK(gptimer_get_raw_count(gptimer, &cur_count_val));
printf("cur_count_val: %llu\r\n", cur_count_val);
TEST_ASSERT_UINT_WITHIN(100, (cmpb - cmpa) * 10, cur_count_val);
// delete gptimer
TEST_ESP_OK(gptimer_disable(gptimer));
TEST_ESP_OK(gptimer_del_timer(gptimer));
// delete etm primitives
TEST_ESP_OK(esp_etm_channel_disable(etm_channel_a));
TEST_ESP_OK(esp_etm_channel_disable(etm_channel_b));
TEST_ESP_OK(esp_etm_del_task(gptimer_task_start));
TEST_ESP_OK(esp_etm_del_task(gptimer_task_stop));
TEST_ESP_OK(esp_etm_del_event(mcpwm_etm_event1));
TEST_ESP_OK(esp_etm_del_event(mcpwm_etm_event2));
TEST_ESP_OK(esp_etm_del_channel(etm_channel_a));
TEST_ESP_OK(esp_etm_del_channel(etm_channel_b));
TEST_ESP_OK(mcpwm_timer_disable(timer));
TEST_ESP_OK(mcpwm_del_generator(generator));
TEST_ESP_OK(mcpwm_del_comparator(comparator_a));
TEST_ESP_OK(mcpwm_del_comparator(comparator_b));
#if SOC_MCPWM_SUPPORT_EVENT_COMPARATOR
TEST_ESP_OK(mcpwm_del_comparator(event_comparator));
#endif
TEST_ESP_OK(mcpwm_del_operator(oper));
TEST_ESP_OK(mcpwm_del_timer(timer));
}

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_CMPR_ETM_EVENT_MAX]){{ \
[MCPWM_CMPR_ETM_EVENT_EQUAL] = MCPWM_EVT_OP0_TEA + oper_id + 3 * cmpr_id, \
}}[group][event]
/**
* @brief The dead time module's clock source
*/
@ -1595,6 +1602,25 @@ static inline void mcpwm_ll_capture_set_prescale(mcpwm_dev_t *mcpwm, int channel
HAL_FORCE_MODIFY_U32_REG_FIELD(mcpwm->cap_chn_cfg[channel], capn_prescale, prescale - 1);
}
//////////////////////////////////////////MCPWM ETM Specific////////////////////////////////////////////////////////////
/**
* @brief Enable comparator ETM event
*
* @param mcpwm Peripheral instance address
* @param operator_id Operator ID, index from 0 to 2
* @param cmpr_id Comparator ID, index from 0 to 2
* @param en True: enable ETM module, False: disable ETM module
*/
static inline void mcpwm_ll_etm_enable_comparator_event(mcpwm_dev_t *mcpwm, int operator_id, int cmpr_id, bool en)
{
if (en) {
mcpwm->evt_en.val |= 1 << (operator_id + 3 * cmpr_id + 9) ;
} else {
mcpwm->evt_en.val &= ~(1 << (operator_id + 3 * cmpr_id + 9)) ;
}
}
//////////////////////////////////////////Deprecated Functions//////////////////////////////////////////////////////////
/////////////////////////////The following functions are only used by the legacy driver/////////////////////////////////
/////////////////////////////They might be removed in the next major release (ESP-IDF 6.0)//////////////////////////////

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

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

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_CMPR_ETM_EVENT_EQUAL, /*!< The count value equals the value of comparator */
MCPWM_CMPR_ETM_EVENT_MAX, /*!< Maximum number of comparator events */
} mcpwm_comparator_etm_event_type_t;
#ifdef __cplusplus
}
#endif

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

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

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

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

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