From fc80d3e568542b0d77fd4327cd70a4c8aa813428 Mon Sep 17 00:00:00 2001 From: Chen Jichang Date: Tue, 22 Aug 2023 15:43:30 +0800 Subject: [PATCH] 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. --- .../driver/mcpwm/include/driver/mcpwm_cap.h | 1 + .../driver/mcpwm/include/driver/mcpwm_timer.h | 2 +- components/driver/mcpwm/mcpwm_cap.c | 14 ++++ components/driver/mcpwm/mcpwm_com.c | 78 +++++++++++++++++-- components/driver/mcpwm/mcpwm_oper.c | 5 +- components/driver/mcpwm/mcpwm_private.h | 6 +- components/driver/mcpwm/mcpwm_timer.c | 6 +- .../test_apps/gptimer/main/test_gptimer.c | 2 +- .../test_apps/mcpwm/main/test_mcpwm_cap.c | 2 + .../test_apps/mcpwm/main/test_mcpwm_common.c | 45 +++++++++++ components/hal/esp32/include/hal/mcpwm_ll.h | 11 ++- components/hal/esp32c6/include/hal/mcpwm_ll.h | 11 ++- components/hal/esp32h2/include/hal/mcpwm_ll.h | 11 ++- components/hal/esp32p4/include/hal/mcpwm_ll.h | 13 ++-- components/hal/esp32s3/include/hal/mcpwm_ll.h | 13 ++-- docs/en/api-reference/peripherals/mcpwm.rst | 7 ++ .../zh_CN/api-reference/peripherals/mcpwm.rst | 11 ++- 17 files changed, 200 insertions(+), 38 deletions(-) diff --git a/components/driver/mcpwm/include/driver/mcpwm_cap.h b/components/driver/mcpwm/include/driver/mcpwm_cap.h index 24838439983..4fa50c657fb 100644 --- a/components/driver/mcpwm/include/driver/mcpwm_cap.h +++ b/components/driver/mcpwm/include/driver/mcpwm_cap.h @@ -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; /** diff --git a/components/driver/mcpwm/include/driver/mcpwm_timer.h b/components/driver/mcpwm/include/driver/mcpwm_timer.h index df8bc3da2e9..5d9d1339951 100644 --- a/components/driver/mcpwm/include/driver/mcpwm_timer.h +++ b/components/driver/mcpwm/include/driver/mcpwm_timer.h @@ -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 */ diff --git a/components/driver/mcpwm/mcpwm_cap.c b/components/driver/mcpwm/mcpwm_cap.c index e4e4a01550e..3b36e428d75 100644 --- a/components/driver/mcpwm/mcpwm_cap.c +++ b/components/driver/mcpwm/mcpwm_cap.c @@ -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"); diff --git a/components/driver/mcpwm/mcpwm_com.c b/components/driver/mcpwm/mcpwm_com.c index ac022989b19..10ae40e864a 100644 --- a/components/driver/mcpwm/mcpwm_com.c +++ b/components/driver/mcpwm/mcpwm_com.c @@ -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; +} diff --git a/components/driver/mcpwm/mcpwm_oper.c b/components/driver/mcpwm/mcpwm_oper.c index 8962b10dcd6..73148b73488 100644 --- a/components/driver/mcpwm/mcpwm_oper.c +++ b/components/driver/mcpwm/mcpwm_oper.c @@ -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; diff --git a/components/driver/mcpwm/mcpwm_private.h b/components/driver/mcpwm/mcpwm_private.h index d8e5f967231..4f576857f92 100644 --- a/components/driver/mcpwm/mcpwm_private.h +++ b/components/driver/mcpwm/mcpwm_private.h @@ -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 @@ -238,6 +239,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 } diff --git a/components/driver/mcpwm/mcpwm_timer.c b/components/driver/mcpwm/mcpwm_timer.c index cf4e21cb099..c0b176ba364 100644 --- a/components/driver/mcpwm/mcpwm_timer.c +++ b/components/driver/mcpwm/mcpwm_timer.c @@ -111,12 +111,12 @@ esp_err_t mcpwm_new_timer(const mcpwm_timer_config_t *config, mcpwm_timer_handle // select the clock source mcpwm_timer_clock_source_t clk_src = config->clk_src ? config->clk_src : MCPWM_TIMER_CLK_SRC_DEFAULT; - ESP_RETURN_ON_ERROR(mcpwm_select_periph_clock(group, (soc_module_clk_t)clk_src), TAG, "set group clock failed"); + ESP_GOTO_ON_ERROR(mcpwm_select_periph_clock(group, (soc_module_clk_t)clk_src), err, TAG, "set group clock failed"); // 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) { diff --git a/components/driver/test_apps/gptimer/main/test_gptimer.c b/components/driver/test_apps/gptimer/main/test_gptimer.c index 539f50f17f1..04c08ce05e9 100644 --- a/components/driver/test_apps/gptimer/main/test_gptimer.c +++ b/components/driver/test_apps/gptimer/main/test_gptimer.c @@ -141,7 +141,7 @@ TEST_CASE("gptimer_wallclock_with_various_clock_sources", "[gptimer]") #if CONFIG_PM_ENABLE #define GPTIMER_STOP_ON_ALARM_COUNT_DELTA 150 #else -#define GPTIMER_STOP_ON_ALARM_COUNT_DELTA 30 +#define GPTIMER_STOP_ON_ALARM_COUNT_DELTA 40 #endif // CONFIG_PM_ENABLE TEST_ALARM_CALLBACK_ATTR static bool test_gptimer_alarm_stop_callback(gptimer_handle_t timer, const gptimer_alarm_event_data_t *edata, void *user_data) diff --git a/components/driver/test_apps/mcpwm/main/test_mcpwm_cap.c b/components/driver/test_apps/mcpwm/main/test_mcpwm_cap.c index d07789141a7..655862bb9c4 100644 --- a/components/driver/test_apps/mcpwm/main/test_mcpwm_cap.c +++ b/components/driver/test_apps/mcpwm/main/test_mcpwm_cap.c @@ -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"); diff --git a/components/driver/test_apps/mcpwm/main/test_mcpwm_common.c b/components/driver/test_apps/mcpwm/main/test_mcpwm_common.c index bd08856dec7..f31efcc65aa 100644 --- a/components/driver/test_apps/mcpwm/main/test_mcpwm_common.c +++ b/components/driver/test_apps/mcpwm/main/test_mcpwm_common.c @@ -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)); +} diff --git a/components/hal/esp32/include/hal/mcpwm_ll.h b/components/hal/esp32/include/hal/mcpwm_ll.h index 84aee5d9f2c..e5b2c84c4f2 100644 --- a/components/hal/esp32/include/hal/mcpwm_ll.h +++ b/components/hal/esp32/include/hal/mcpwm_ll.h @@ -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); } /** diff --git a/components/hal/esp32c6/include/hal/mcpwm_ll.h b/components/hal/esp32c6/include/hal/mcpwm_ll.h index 010fa326021..168f1a1e1d5 100644 --- a/components/hal/esp32c6/include/hal/mcpwm_ll.h +++ b/components/hal/esp32c6/include/hal/mcpwm_ll.h @@ -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); } /** diff --git a/components/hal/esp32h2/include/hal/mcpwm_ll.h b/components/hal/esp32h2/include/hal/mcpwm_ll.h index 0b0606f61d1..e6ae9ffc567 100644 --- a/components/hal/esp32h2/include/hal/mcpwm_ll.h +++ b/components/hal/esp32h2/include/hal/mcpwm_ll.h @@ -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); } /** diff --git a/components/hal/esp32p4/include/hal/mcpwm_ll.h b/components/hal/esp32p4/include/hal/mcpwm_ll.h index 6bc904f60fc..b35f4d8ee5c 100644 --- a/components/hal/esp32p4/include/hal/mcpwm_ll.h +++ b/components/hal/esp32p4/include/hal/mcpwm_ll.h @@ -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 @@ -116,15 +118,16 @@ 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 = source_clock / (prescale + 1) + // group clock: PWM_clk = source_clock / (prescale) + HAL_ASSERT(prescale <= 256 && prescale > 0); if (mcpwm == &MCPWM0) { - HAL_FORCE_MODIFY_U32_REG_FIELD(HP_SYS_CLKRST.peri_clk_ctrl20, reg_mcpwm0_clk_div_num, pre_scale - 1); + HAL_FORCE_MODIFY_U32_REG_FIELD(HP_SYS_CLKRST.peri_clk_ctrl20, reg_mcpwm0_clk_div_num, prescale - 1); } else if (mcpwm == &MCPWM1) { - HAL_FORCE_MODIFY_U32_REG_FIELD(HP_SYS_CLKRST.peri_clk_ctrl20, reg_mcpwm1_clk_div_num, pre_scale - 1); + HAL_FORCE_MODIFY_U32_REG_FIELD(HP_SYS_CLKRST.peri_clk_ctrl20, reg_mcpwm1_clk_div_num, prescale - 1); } } diff --git a/components/hal/esp32s3/include/hal/mcpwm_ll.h b/components/hal/esp32s3/include/hal/mcpwm_ll.h index 7c3c360d867..ee7f6b9577d 100644 --- a/components/hal/esp32s3/include/hal/mcpwm_ll.h +++ b/components/hal/esp32s3/include/hal/mcpwm_ll.h @@ -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); } /** diff --git a/docs/en/api-reference/peripherals/mcpwm.rst b/docs/en/api-reference/peripherals/mcpwm.rst index bea7db7ce47..83837185948 100644 --- a/docs/en/api-reference/peripherals/mcpwm.rst +++ b/docs/en/api-reference/peripherals/mcpwm.rst @@ -183,6 +183,13 @@ To allocate a capture timer, you can call the :cpp:func:`mcpwm_new_capture_timer - :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 an error code. Specifically, when there is no free capture timer left in the MCPWM group, this function will return the :c:macro:`ESP_ERR_NOT_FOUND` error. [1]_ diff --git a/docs/zh_CN/api-reference/peripherals/mcpwm.rst b/docs/zh_CN/api-reference/peripherals/mcpwm.rst index d519d4ccf86..5da5cc3923c 100644 --- a/docs/zh_CN/api-reference/peripherals/mcpwm.rst +++ b/docs/zh_CN/api-reference/peripherals/mcpwm.rst @@ -183,6 +183,13 @@ MCPWM 组有一个专用定时器,用于捕获特定事件发生时的时间 - :cpp:member:`mcpwm_capture_timer_config_t::group_id` 设置 MCPWM 组 ID,范围为 [0, :c:macro:`SOC_MCPWM_GROUPS` - 1]。 - :cpp:member:`mcpwm_capture_timer_config_t::clk_src` 设置捕获定时器的时钟源。 +- :cpp:member:`mcpwm_capture_timer_config_t::resolution_hz` 设置捕获定时器的预期分辨率。内部驱动将根据时钟源和分辨率设置合适的分频器。设置为 ``0`` 时,驱动会自己选取一个适当的分辨率,后续你可以通过 :cpp:func:`mcpwm_capture_timer_get_resolution` 查看当前定时器的分辨率。 + +.. only:: not SOC_MCPWM_CAPTURE_CLK_FROM_GROUP + + .. note:: + + 在 {IDF_TARGET_NAME} 中,:cpp:member:`mcpwm_capture_timer_config_t::resolution_hz` 参数无效,捕获定时器的分辨率始终等于 :cpp:enumerator:`MCPWM_CAPTURE_CLK_SRC_APB`。 分配成功后,:cpp:func:`mcpwm_new_capture_timer` 将返回一个指向已分配捕获定时器的指针。否则,函数将返回错误代码。具体来说,当 MCPWM 组中没有空闲捕获定时器时,将返回 :c:macro:`ESP_ERR_NOT_FOUND` 错误。[1]_ @@ -739,7 +746,7 @@ MCPWM 操作器具有载波子模块,可以根据需要(例如隔离式数 调用 :cpp:func:`mcpwm_operator_apply_carrier`,并提供配置结构体 :cpp:type:`mcpwm_carrier_config_t`,配置载波子模块: - :cpp:member:`mcpwm_carrier_config_t::clk_src` 设置载波的时钟源。 -- :cpp:member:`mcpwm_carrier_config_t::frequency_hz` 表示载波频率,单位为赫兹。 +- :cpp:member:`mcpwm_carrier_config_t::frequency_hz` 表示载波频率,单位为赫兹。内部驱动将根据时钟源和载波频率设置合适的分频器。 - :cpp:member:`mcpwm_carrier_config_t::duty_cycle` 表示载波的占空比。需注意,支持的占空比选项并不连续,驱动程序将根据配置查找最接近的占空比。 - :cpp:member:`mcpwm_carrier_config_t::first_pulse_duration_us` 表示第一个脉冲的脉宽,单位为微秒。该脉冲的分辨率由 :cpp:member:`mcpwm_carrier_config_t::frequency_hz` 中的配置决定。第一个脉冲的脉宽不能为零,且至少为一个载波周期。脉宽越长,电感传导越快。 - :cpp:member:`mcpwm_carrier_config_t::invert_before_modulate` 和 :cpp:member:`mcpwm_carrier_config_t::invert_after_modulate` 设置是否在调制前和调制后取反载波输出。 @@ -900,7 +907,7 @@ MCPWM 捕获通道支持在信号上检测到有效边沿时发送通知。须 函数 :cpp:func:`mcpwm_capture_channel_register_event_callbacks` 中的 ``user_data`` 参数用于保存用户上下文,将直接传递至各个回调函数。 -此函数会延迟安装 MCPWM 故障的中断服务。中断服务只能通过 :cpp:type:`mcpwm_del_capture_channel` 移除。 +此函数会延迟安装 MCPWM 捕获的中断服务。中断服务只能通过 :cpp:type:`mcpwm_del_capture_channel` 移除。 启用或禁用捕获通道 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~