feat(mcpwm): Set group clock prescale dynamically

MCPWM group clock pre scale was originally fixed to 2, which is
inconvenient. Set group clock prescale dynamically. Now the maximum
resolution of the MCPWM timer is up to 160MHz(when the prescale set
to 1). And add a resulotion config for MCPWM capture.
This commit is contained in:
Chen Jichang 2023-08-22 15:43:30 +08:00 committed by morris
parent 1d91310e0f
commit d8e5b2ac41
14 changed files with 181 additions and 29 deletions

View File

@ -21,6 +21,7 @@ extern "C" {
typedef struct {
int group_id; /*!< Specify from which group to allocate the capture timer */
mcpwm_capture_clock_source_t clk_src; /*!< MCPWM capture timer clock source */
uint32_t resolution_hz; /*!< Resolution of capture timer */
} mcpwm_capture_timer_config_t;
/**

View File

@ -31,7 +31,7 @@ typedef struct {
typedef struct {
int group_id; /*!< Specify from which group to allocate the MCPWM timer */
mcpwm_timer_clock_source_t clk_src; /*!< MCPWM timer clock source */
uint32_t resolution_hz; /*!< Counter resolution in Hz, ranges from around 300KHz to 80MHz.
uint32_t resolution_hz; /*!< Counter resolution in Hz
The step size of each count tick equals to (1 / resolution_hz) seconds */
mcpwm_timer_count_mode_t count_mode; /*!< Count mode */
uint32_t period_ticks; /*!< Number of count ticks within a period */

View File

@ -17,6 +17,7 @@
#include "esp_attr.h"
#include "esp_check.h"
#include "esp_private/esp_clk.h"
#include "esp_clk_tree.h"
#include "esp_err.h"
#include "esp_log.h"
#include "esp_memory_utils.h"
@ -100,12 +101,25 @@ esp_err_t mcpwm_new_capture_timer(const mcpwm_capture_timer_config_t *config, mc
#if SOC_MCPWM_CAPTURE_CLK_FROM_GROUP
// capture timer clock source is same as the MCPWM group
ESP_GOTO_ON_ERROR(mcpwm_select_periph_clock(group, (soc_module_clk_t)clk_src), err, TAG, "set group clock failed");
uint32_t periph_src_clk_hz = 0;
ESP_GOTO_ON_ERROR(esp_clk_tree_src_get_freq_hz((soc_module_clk_t)clk_src, ESP_CLK_TREE_SRC_FREQ_PRECISION_CACHED, &periph_src_clk_hz), err, TAG, "get clock source freq failed");
ESP_LOGD(TAG, "periph_src_clk_hz %"PRIu32"", periph_src_clk_hz);
// when resolution_hz set to zero, use default resolution_hz
uint32_t resolution_hz = config->resolution_hz ? config->resolution_hz : periph_src_clk_hz / MCPWM_GROUP_CLOCK_DEFAULT_PRESCALE;
ESP_GOTO_ON_ERROR(mcpwm_set_prescale(group, resolution_hz, MCPWM_LL_MAX_CAPTURE_TIMER_PRESCALE, NULL), err, TAG, "set prescale failed");
cap_timer->resolution_hz = group->resolution_hz;
if (cap_timer->resolution_hz != resolution_hz) {
ESP_LOGW(TAG, "adjust cap_timer resolution to %"PRIu32"Hz", cap_timer->resolution_hz);
}
#else
// capture timer has independent clock source selection
switch (clk_src) {
case MCPWM_CAPTURE_CLK_SRC_APB:
cap_timer->resolution_hz = esp_clk_apb_freq();
if (config->resolution_hz) {
ESP_LOGW(TAG, "cap_timer resolution is not adjustable in current target, still %"PRIu32"Hz", cap_timer->resolution_hz);
}
#if CONFIG_PM_ENABLE
ret = esp_pm_lock_create(ESP_PM_APB_FREQ_MAX, 0, "mcpwm_cap_timer", &cap_timer->pm_lock);
ESP_GOTO_ON_ERROR(ret, err, TAG, "create ESP_PM_APB_FREQ_MAX lock failed");

View File

@ -126,10 +126,9 @@ 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 ret = ESP_OK;
uint32_t periph_src_clk_hz = 0;
bool clock_selection_conflict = false;
bool do_clock_init = false;
// check if we need to update the group clock source, group clock source is shared by all mcpwm objects
// check if we need to update the group clock source, group clock source is shared by all mcpwm modules
portENTER_CRITICAL(&group->spinlock);
if (group->clk_src == 0) {
group->clk_src = clk_src;
@ -142,7 +141,6 @@ esp_err_t mcpwm_select_periph_clock(mcpwm_group_t *group, soc_module_clk_t clk_s
"group clock conflict, already is %d but attempt to %d", group->clk_src, clk_src);
if (do_clock_init) {
ESP_RETURN_ON_ERROR(esp_clk_tree_src_get_freq_hz(clk_src, ESP_CLK_TREE_SRC_FREQ_PRECISION_CACHED, &periph_src_clk_hz), TAG, "get clock source freq failed");
#if CONFIG_PM_ENABLE
sprintf(group->pm_lock_name, "mcpwm_%d", group->group_id); // e.g. mcpwm_0
@ -152,9 +150,77 @@ esp_err_t mcpwm_select_periph_clock(mcpwm_group_t *group, soc_module_clk_t clk_s
#endif // CONFIG_PM_ENABLE
mcpwm_ll_group_set_clock_source(group->hal.dev, clk_src);
mcpwm_ll_group_set_clock_prescale(group->hal.dev, MCPWM_PERIPH_CLOCK_PRE_SCALE);
group->resolution_hz = periph_src_clk_hz / MCPWM_PERIPH_CLOCK_PRE_SCALE;
ESP_LOGD(TAG, "group (%d) clock resolution:%"PRIu32"Hz", group->group_id, group->resolution_hz);
}
return ret;
}
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_RETURN_ON_FALSE(group && expect_module_resolution_hz && module_prescale_max, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
uint32_t periph_src_clk_hz = 0;
int group_id = group->group_id;
uint32_t group_resolution_hz = 0;
uint32_t group_prescale = group->prescale > 0 ? group->prescale : MCPWM_GROUP_CLOCK_DEFAULT_PRESCALE; // range: 1~256, 0 means not calculated
uint32_t module_prescale = 0; // range: 1~256 (for timer) or 1~16 (for carrier) or 1 (for capture)
ESP_RETURN_ON_ERROR(esp_clk_tree_src_get_freq_hz(group->clk_src, ESP_CLK_TREE_SRC_FREQ_PRECISION_CACHED, &periph_src_clk_hz), TAG, "get clock source freq failed");
// calc the group prescale
group_resolution_hz = periph_src_clk_hz / group_prescale;
module_prescale = group_resolution_hz / expect_module_resolution_hz;
// default prescale cannot match
// try to ensure accurate division. If none of the division factors can be guaranteed to be integers, then allocate the clock frequency to the highest divisor
uint32_t fit_module_prescale = 0;
uint32_t fit_group_prescale = 0;
if (!(module_prescale >= 1 && module_prescale <= module_prescale_max)) {
group_prescale = 0;
while (++group_prescale <= MCPWM_LL_MAX_GROUP_PRESCALE) {
group_resolution_hz = periph_src_clk_hz / group_prescale;
module_prescale = group_resolution_hz / expect_module_resolution_hz;
if (module_prescale >= 1 && module_prescale <= module_prescale_max) {
// maintain the first value found during the search that satisfies the division requirement (highest frequency), applicable for cases where integer division is not possible."
fit_module_prescale = fit_module_prescale ? fit_module_prescale : module_prescale;
fit_group_prescale = fit_group_prescale ? fit_group_prescale : group_prescale;
// find accurate division
if (group_resolution_hz == expect_module_resolution_hz * module_prescale) {
fit_module_prescale = module_prescale;
fit_group_prescale = group_prescale;
break;
}
}
}
module_prescale = fit_module_prescale;
group_prescale = fit_group_prescale;
group_resolution_hz = periph_src_clk_hz / group_prescale;
}
ESP_LOGD(TAG, "group (%d) calc prescale:%"PRIu32", module calc prescale:%"PRIu32"", group_id, group_prescale, module_prescale);
ESP_RETURN_ON_FALSE(group_prescale > 0 && group_prescale <= MCPWM_LL_MAX_GROUP_PRESCALE, ESP_ERR_INVALID_STATE, TAG,
"set group prescale failed, group clock cannot match the resolution");
// check if we need to update the group prescale, group prescale is shared by all mcpwm modules
bool prescale_conflict = false;
portENTER_CRITICAL(&group->spinlock);
if (group->prescale == 0) {
group->prescale = group_prescale;
group->resolution_hz = group_resolution_hz;
mcpwm_ll_group_set_clock_prescale(group->hal.dev, group_prescale);
} else {
prescale_conflict = (group->prescale != group_prescale);
}
portEXIT_CRITICAL(&group->spinlock);
ESP_RETURN_ON_FALSE(!prescale_conflict, ESP_ERR_INVALID_STATE, TAG,
"group prescale conflict, already is %"PRIu32" but attempt to %"PRIu32"", group->prescale, group_prescale);
ESP_LOGD(TAG, "group (%d) clock resolution:%"PRIu32"Hz", group_id, group->resolution_hz);
// set module resolution
if (ret_module_prescale) {
*ret_module_prescale = module_prescale;
}
return ESP_OK;
}

View File

@ -194,7 +194,10 @@ esp_err_t mcpwm_operator_apply_carrier(mcpwm_oper_handle_t oper, const mcpwm_car
mcpwm_carrier_clock_source_t clk_src = config->clk_src ? config->clk_src : MCPWM_CARRIER_CLK_SRC_DEFAULT;
ESP_RETURN_ON_ERROR(mcpwm_select_periph_clock(group, (soc_module_clk_t)clk_src), TAG, "set group clock failed");
uint8_t prescale = group->resolution_hz / 8 / config->frequency_hz;
uint32_t prescale = 0;
ESP_RETURN_ON_ERROR(mcpwm_set_prescale(group, config->frequency_hz, MCPWM_LL_MAX_CARRIER_PRESCALE * 8, &prescale), TAG, "set prescale failed");
// here div 8 because the duty has 3 register bits
prescale /= 8;
ESP_RETURN_ON_FALSE(prescale > 0 && prescale <= MCPWM_LL_MAX_CARRIER_PRESCALE, ESP_ERR_INVALID_STATE, TAG, "group clock cannot match the frequency");
mcpwm_ll_carrier_set_prescale(hal->dev, oper_id, prescale);
real_frequency = group->resolution_hz / 8 / prescale;

View File

@ -36,7 +36,7 @@ extern "C" {
#define MCPWM_ALLOW_INTR_PRIORITY_MASK ESP_INTR_FLAG_LOWMED
#define MCPWM_PERIPH_CLOCK_PRE_SCALE (2)
#define MCPWM_GROUP_CLOCK_DEFAULT_PRESCALE 2
#define MCPWM_PM_LOCK_NAME_LEN_MAX 16
typedef struct mcpwm_group_t mcpwm_group_t;
@ -59,7 +59,8 @@ struct mcpwm_group_t {
int intr_priority; // MCPWM interrupt priority
mcpwm_hal_context_t hal; // HAL instance is at group level
portMUX_TYPE spinlock; // group level spinlock
uint32_t resolution_hz; // MCPWM group clock resolution
uint32_t prescale; // group prescale
uint32_t resolution_hz; // MCPWM group clock resolution: clock_src_hz / clock_prescale = resolution_hz
esp_pm_lock_handle_t pm_lock; // power management lock
soc_module_clk_t clk_src; // peripheral source clock
mcpwm_cap_timer_t *cap_timer; // mcpwm capture timers
@ -231,6 +232,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);
#ifdef __cplusplus
}

View File

@ -115,8 +115,8 @@ esp_err_t mcpwm_new_timer(const mcpwm_timer_config_t *config, mcpwm_timer_handle
// reset the timer to a determined state
mcpwm_hal_timer_reset(hal, timer_id);
// set timer resolution
uint32_t prescale = group->resolution_hz / config->resolution_hz;
ESP_RETURN_ON_FALSE(prescale > 0 && prescale <= MCPWM_LL_MAX_TIMER_PRESCALE, ESP_ERR_INVALID_STATE, TAG, "group clock cannot match the resolution");
uint32_t prescale = 0;
ESP_GOTO_ON_ERROR(mcpwm_set_prescale(group, config->resolution_hz, MCPWM_LL_MAX_TIMER_PRESCALE, &prescale), err, TAG, "set prescale failed");
mcpwm_ll_timer_set_clock_prescale(hal->dev, timer_id, prescale);
timer->resolution_hz = group->resolution_hz / prescale;
if (timer->resolution_hz != config->resolution_hz) {

View File

@ -76,6 +76,7 @@ TEST_CASE("mcpwm_capture_ext_gpio", "[mcpwm]")
mcpwm_capture_timer_config_t cap_timer_config = {
.clk_src = MCPWM_CAPTURE_CLK_SRC_DEFAULT,
.group_id = 0,
.resolution_hz = 8 * 1000 * 1000,
};
TEST_ESP_OK(mcpwm_new_capture_timer(&cap_timer_config, &cap_timer));
@ -118,6 +119,7 @@ TEST_CASE("mcpwm_capture_ext_gpio", "[mcpwm]")
uint32_t clk_src_res;
TEST_ESP_OK(mcpwm_capture_timer_get_resolution(cap_timer, &clk_src_res));
clk_src_res /= 1000; // convert to kHz
printf("timer resolution:%"PRIu32"KHz\r\n", clk_src_res);
TEST_ASSERT_UINT_WITHIN(1000, 10000, (cap_value[1] - cap_value[0]) * 1000 / clk_src_res);
printf("uninstall capture channel and timer\r\n");

View File

@ -123,3 +123,48 @@ TEST_CASE("mcpwm_set_interrupt_priority", "[mcpwm]")
TEST_ESP_OK(mcpwm_del_timer(timer));
TEST_ESP_OK(mcpwm_del_fault(fault));
}
TEST_CASE("mcpwm_group_set_prescale_dynamically", "[mcpwm]")
{
mcpwm_timer_config_t timer_config = {
.clk_src = MCPWM_TIMER_CLK_SRC_DEFAULT,
.resolution_hz = 100 * 1000, // 100kHz
.period_ticks = 400,
.count_mode = MCPWM_TIMER_COUNT_MODE_UP_DOWN,
.group_id = 0,
};
mcpwm_timer_handle_t timer = NULL;
printf("create mcpwm timer\r\n");
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));
mcpwm_generator_config_t generator_config = {
.gen_gpio_num = 0,
};
mcpwm_gen_handle_t generator = NULL;
TEST_ESP_OK(mcpwm_new_generator(oper, &generator_config, &generator));
printf("add carrier to PWM wave\r\n");
mcpwm_carrier_config_t carrier_config = {
.clk_src = MCPWM_CARRIER_CLK_SRC_DEFAULT,
.frequency_hz = 100000, // 100KHz carrier need higher group prescale
.duty_cycle = 0.5,
.first_pulse_duration_us = 10,
};
TEST_ESP_ERR(ESP_ERR_INVALID_STATE, mcpwm_operator_apply_carrier(oper, &carrier_config));
carrier_config.frequency_hz = 2000000; // 2MHz carrier
carrier_config.first_pulse_duration_us = 5;
TEST_ESP_OK(mcpwm_operator_apply_carrier(oper, &carrier_config));
TEST_ESP_OK(mcpwm_del_generator(generator));
TEST_ESP_OK(mcpwm_del_operator(oper));
TEST_ESP_OK(mcpwm_del_timer(timer));
}

View File

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@ -43,10 +43,12 @@ extern "C" {
#define MCPWM_LL_EVENT_CAPTURE(cap) (1 << ((cap) + 27))
// Maximum values due to limited register bit width
#define MCPWM_LL_MAX_GROUP_PRESCALE 256
#define MCPWM_LL_MAX_TIMER_PRESCALE 256
#define MCPWM_LL_MAX_CARRIER_PRESCALE 16
#define MCPWM_LL_MAX_CARRIER_ONESHOT 16
#define MCPWM_LL_MAX_CAPTURE_PRESCALE 256
#define MCPWM_LL_MAX_CAPTURE_TIMER_PRESCALE 1
#define MCPWM_LL_MAX_DEAD_DELAY 65536
#define MCPWM_LL_MAX_COUNT_VALUE 65536
@ -97,10 +99,11 @@ static inline void mcpwm_ll_group_enable_clock(mcpwm_dev_t *mcpwm, bool en)
* @param mcpwm Peripheral instance address
* @param pre_scale Prescale value
*/
static inline void mcpwm_ll_group_set_clock_prescale(mcpwm_dev_t *mcpwm, int pre_scale)
static inline void mcpwm_ll_group_set_clock_prescale(mcpwm_dev_t *mcpwm, int prescale)
{
// group clock: PWM_clk = CLK_160M / (prescale + 1)
HAL_FORCE_MODIFY_U32_REG_FIELD(mcpwm->clk_cfg, clk_prescale, pre_scale - 1);
// group clock: PWM_clk = CLK_160M / (prescale)
HAL_ASSERT(prescale <= 256 && prescale > 0);
HAL_FORCE_MODIFY_U32_REG_FIELD(mcpwm->clk_cfg, clk_prescale, prescale - 1);
}
/**

View File

@ -45,10 +45,12 @@ extern "C" {
#define MCPWM_LL_EVENT_CAPTURE(cap) (1 << ((cap) + 27))
// Maximum values due to limited register bit width
#define MCPWM_LL_MAX_GROUP_PRESCALE 256
#define MCPWM_LL_MAX_TIMER_PRESCALE 256
#define MCPWM_LL_MAX_CARRIER_PRESCALE 16
#define MCPWM_LL_MAX_CARRIER_ONESHOT 16
#define MCPWM_LL_MAX_CAPTURE_PRESCALE 256
#define MCPWM_LL_MAX_CAPTURE_TIMER_PRESCALE 1
#define MCPWM_LL_MAX_DEAD_DELAY 65536
#define MCPWM_LL_MAX_COUNT_VALUE 65536
@ -105,13 +107,14 @@ static inline void mcpwm_ll_group_enable_clock(mcpwm_dev_t *mcpwm, bool en)
* @brief Set the MCPWM group clock prescale
*
* @param mcpwm Peripheral instance address
* @param pre_scale Prescale value
* @param prescale Prescale value
*/
static inline void mcpwm_ll_group_set_clock_prescale(mcpwm_dev_t *mcpwm, int pre_scale)
static inline void mcpwm_ll_group_set_clock_prescale(mcpwm_dev_t *mcpwm, int prescale)
{
(void)mcpwm; // only one MCPWM instance
// group clock: PWM_clk = source_clock / (prescale + 1)
HAL_FORCE_MODIFY_U32_REG_FIELD(PCR.pwm_clk_conf, pwm_div_num, pre_scale - 1);
// group clock: PWM_clk = source_clock / (prescale)
HAL_ASSERT(prescale <= 256 && prescale > 0);
HAL_FORCE_MODIFY_U32_REG_FIELD(PCR.pwm_clk_conf, pwm_div_num, prescale - 1);
}
/**

View File

@ -43,10 +43,12 @@ extern "C" {
#define MCPWM_LL_EVENT_CAPTURE(cap) (1 << ((cap) + 27))
// Maximum values due to limited register bit width
#define MCPWM_LL_MAX_GROUP_PRESCALE 256
#define MCPWM_LL_MAX_TIMER_PRESCALE 256
#define MCPWM_LL_MAX_CARRIER_PRESCALE 16
#define MCPWM_LL_MAX_CARRIER_ONESHOT 16
#define MCPWM_LL_MAX_CAPTURE_PRESCALE 256
#define MCPWM_LL_MAX_CAPTURE_TIMER_PRESCALE 1
#define MCPWM_LL_MAX_DEAD_DELAY 65536
#define MCPWM_LL_MAX_COUNT_VALUE 65536
@ -103,13 +105,14 @@ static inline void mcpwm_ll_group_enable_clock(mcpwm_dev_t *mcpwm, bool en)
* @brief Set the MCPWM group clock prescale
*
* @param mcpwm Peripheral instance address
* @param pre_scale Prescale value
* @param prescale Prescale value
*/
static inline void mcpwm_ll_group_set_clock_prescale(mcpwm_dev_t *mcpwm, int pre_scale)
static inline void mcpwm_ll_group_set_clock_prescale(mcpwm_dev_t *mcpwm, int prescale)
{
(void)mcpwm; // only one MCPWM instance
// group clock: PWM_clk = source_clock / (prescale + 1)
HAL_FORCE_MODIFY_U32_REG_FIELD(PCR.pwm_clk_conf, pwm_div_num, pre_scale - 1);
// group clock: PWM_clk = source_clock / (prescale)
HAL_ASSERT(prescale <= 256 && prescale > 0);
HAL_FORCE_MODIFY_U32_REG_FIELD(PCR.pwm_clk_conf, pwm_div_num, prescale - 1);
}
/**

View File

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@ -43,10 +43,12 @@ extern "C" {
#define MCPWM_LL_EVENT_CAPTURE(cap) (1 << ((cap) + 27))
// Maximum values due to limited register bit width
#define MCPWM_LL_MAX_GROUP_PRESCALE 256
#define MCPWM_LL_MAX_TIMER_PRESCALE 256
#define MCPWM_LL_MAX_CARRIER_PRESCALE 16
#define MCPWM_LL_MAX_CARRIER_ONESHOT 16
#define MCPWM_LL_MAX_CAPTURE_PRESCALE 256
#define MCPWM_LL_MAX_CAPTURE_TIMER_PRESCALE 1
#define MCPWM_LL_MAX_DEAD_DELAY 65536
#define MCPWM_LL_MAX_COUNT_VALUE 65536
@ -95,12 +97,13 @@ static inline void mcpwm_ll_group_enable_clock(mcpwm_dev_t *mcpwm, bool en)
* @brief Set the MCPWM group clock prescale
*
* @param mcpwm Peripheral instance address
* @param pre_scale Prescale value
* @param prescale Prescale value
*/
static inline void mcpwm_ll_group_set_clock_prescale(mcpwm_dev_t *mcpwm, int pre_scale)
static inline void mcpwm_ll_group_set_clock_prescale(mcpwm_dev_t *mcpwm, int prescale)
{
// group clock: PWM_clk = CLK_160M / (prescale + 1)
HAL_FORCE_MODIFY_U32_REG_FIELD(mcpwm->clk_cfg, clk_prescale, pre_scale - 1);
// group clock: PWM_clk = CLK_160M / (prescale)
HAL_ASSERT(prescale <= 256 && prescale > 0);
HAL_FORCE_MODIFY_U32_REG_FIELD(mcpwm->clk_cfg, clk_prescale, prescale - 1);
}
/**

View File

@ -173,6 +173,13 @@ To allocate a capture timer, you can call :cpp:func:`mcpwm_new_capture_timer` fu
- :cpp:member:`mcpwm_capture_timer_config_t::group_id` sets the MCPWM group ID. The ID should belong to [0, :c:macro:`SOC_MCPWM_GROUPS` - 1] range.
- :cpp:member:`mcpwm_capture_timer_config_t::clk_src` sets the clock source of the capture timer.
- :cpp:member:`mcpwm_capture_timer_config_t::resolution_hz` The driver internally will set a proper divider based on the clock source and the resolution. If it is set to ``0``, the driver will pick an appropriate resolution on its own, and you can subsequently view the current timer resolution via :cpp:func:`mcpwm_capture_timer_get_resolution`.
.. only:: not SOC_MCPWM_CAPTURE_CLK_FROM_GROUP
.. note::
In {IDF_TARGET_NAME}, :cpp:member:`mcpwm_capture_timer_config_t::resolution_hz` parameter is invalid, the capture timer resolution is always equal to the :cpp:enumerator:`MCPWM_CAPTURE_CLK_SRC_APB`.
The :cpp:func:`mcpwm_new_capture_timer` will return a pointer to the allocated capture timer object if the allocation succeeds. Otherwise, it will return error code. Specifically, when there are no free capture timer left in the MCPWM group, this function will return :c:macro:`ESP_ERR_NOT_FOUND` error. [1]_