diff --git a/components/driver/mcpwm/include/driver/mcpwm_oper.h b/components/driver/mcpwm/include/driver/mcpwm_oper.h index d3b2a1d69b1..38ab7164a4a 100644 --- a/components/driver/mcpwm/include/driver/mcpwm_oper.h +++ b/components/driver/mcpwm/include/driver/mcpwm_oper.h @@ -135,6 +135,7 @@ esp_err_t mcpwm_operator_register_event_callbacks(mcpwm_oper_handle_t oper, cons * @brief MCPWM carrier configuration structure */ typedef struct { + mcpwm_carrier_clock_source_t clk_src; /*!< MCPWM carrier clock source */ uint32_t frequency_hz; /*!< Carrier frequency in Hz */ uint32_t first_pulse_duration_us; /*!< The duration of the first PWM pulse, in us */ float duty_cycle; /*!< Carrier duty cycle */ diff --git a/components/driver/mcpwm/mcpwm_oper.c b/components/driver/mcpwm/mcpwm_oper.c index 2053f4b81ef..61851d171e9 100644 --- a/components/driver/mcpwm/mcpwm_oper.c +++ b/components/driver/mcpwm/mcpwm_oper.c @@ -182,9 +182,14 @@ esp_err_t mcpwm_operator_apply_carrier(mcpwm_oper_handle_t oper, const mcpwm_car float real_duty = 0.0; if (config && config->frequency_hz) { - uint8_t pre_scale = group->resolution_hz / 8 / config->frequency_hz; - mcpwm_ll_carrier_set_prescale(hal->dev, oper_id, pre_scale); - real_frequency = group->resolution_hz / 8 / pre_scale; + // select the clock source + 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; + 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; uint8_t duty = (uint8_t)(config->duty_cycle * 8); mcpwm_ll_carrier_set_duty(hal->dev, oper_id, duty); diff --git a/components/driver/mcpwm/mcpwm_timer.c b/components/driver/mcpwm/mcpwm_timer.c index 01a86aed23f..91a6df429a9 100644 --- a/components/driver/mcpwm/mcpwm_timer.c +++ b/components/driver/mcpwm/mcpwm_timer.c @@ -107,6 +107,7 @@ esp_err_t mcpwm_new_timer(const mcpwm_timer_config_t *config, mcpwm_timer_handle 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"); 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/mcpwm/main/test_mcpwm_oper.c b/components/driver/test_apps/mcpwm/main/test_mcpwm_oper.c index f39535c8fdf..c201d4147cb 100644 --- a/components/driver/test_apps/mcpwm/main/test_mcpwm_oper.c +++ b/components/driver/test_apps/mcpwm/main/test_mcpwm_oper.c @@ -64,22 +64,11 @@ TEST_CASE("mcpwm_operator_install_uninstall", "[mcpwm]") TEST_CASE("mcpwm_operator_carrier", "[mcpwm]") { - mcpwm_timer_config_t timer_config = { - .group_id = 0, - .clk_src = MCPWM_TIMER_CLK_SRC_DEFAULT, - .resolution_hz = 1000000, // 1MHz, 1us per tick - .period_ticks = 20000, - .count_mode = MCPWM_TIMER_COUNT_MODE_UP, - }; - 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)); mcpwm_generator_config_t generator_config = { .gen_gpio_num = 0, @@ -87,36 +76,34 @@ TEST_CASE("mcpwm_operator_carrier", "[mcpwm]") mcpwm_gen_handle_t generator = NULL; TEST_ESP_OK(mcpwm_new_generator(oper, &generator_config, &generator)); - TEST_ESP_OK(mcpwm_generator_set_action_on_timer_event(generator, - MCPWM_GEN_TIMER_EVENT_ACTION(MCPWM_TIMER_DIRECTION_UP, MCPWM_TIMER_EVENT_EMPTY, MCPWM_GEN_ACTION_TOGGLE))); - printf("add carrier to PWM wave\r\n"); mcpwm_carrier_config_t carrier_config = { + .clk_src = MCPWM_CARRIER_CLK_SRC_DEFAULT, .frequency_hz = 1000000, // 1MHz carrier .duty_cycle = 0.5, .first_pulse_duration_us = 10, }; - TEST_ESP_OK(mcpwm_operator_apply_carrier(oper, &carrier_config)); - - TEST_ESP_OK(mcpwm_timer_enable(timer)); - TEST_ESP_OK(mcpwm_timer_start_stop(timer, MCPWM_TIMER_START_NO_STOP)); - vTaskDelay(pdMS_TO_TICKS(100)); - TEST_ESP_OK(mcpwm_timer_start_stop(timer, MCPWM_TIMER_STOP_EMPTY)); - vTaskDelay(pdMS_TO_TICKS(100)); + TEST_ESP_OK(mcpwm_operator_apply_carrier(oper, &carrier_config)); + TEST_ESP_OK(mcpwm_generator_set_force_level(generator, 1, true)); + vTaskDelay(pdMS_TO_TICKS(10)); + TEST_ESP_OK(mcpwm_generator_set_force_level(generator, 0, true)); + vTaskDelay(pdMS_TO_TICKS(10)); + TEST_ESP_OK(mcpwm_generator_set_force_level(generator, 1, true)); + vTaskDelay(pdMS_TO_TICKS(10)); printf("remove carrier from PWM wave\r\n"); carrier_config.frequency_hz = 0; TEST_ESP_OK(mcpwm_operator_apply_carrier(oper, &carrier_config)); - TEST_ESP_OK(mcpwm_timer_start_stop(timer, MCPWM_TIMER_START_NO_STOP)); - vTaskDelay(pdMS_TO_TICKS(200)); - TEST_ESP_OK(mcpwm_timer_start_stop(timer, MCPWM_TIMER_STOP_EMPTY)); - vTaskDelay(pdMS_TO_TICKS(100)); + TEST_ESP_OK(mcpwm_generator_set_force_level(generator, 1, true)); + vTaskDelay(pdMS_TO_TICKS(10)); + TEST_ESP_OK(mcpwm_generator_set_force_level(generator, 0, true)); + vTaskDelay(pdMS_TO_TICKS(10)); + TEST_ESP_OK(mcpwm_generator_set_force_level(generator, 1, true)); + vTaskDelay(pdMS_TO_TICKS(10)); - TEST_ESP_OK(mcpwm_timer_disable(timer)); TEST_ESP_OK(mcpwm_del_generator(generator)); TEST_ESP_OK(mcpwm_del_operator(oper)); - TEST_ESP_OK(mcpwm_del_timer(timer)); } static bool IRAM_ATTR test_cbc_brake_on_gpio_fault_callback(mcpwm_oper_handle_t oper, const mcpwm_brake_event_data_t *edata, void *user_data) diff --git a/components/hal/esp32/include/hal/mcpwm_ll.h b/components/hal/esp32/include/hal/mcpwm_ll.h index 5192d7291df..7152c5d0fd7 100644 --- a/components/hal/esp32/include/hal/mcpwm_ll.h +++ b/components/hal/esp32/include/hal/mcpwm_ll.h @@ -43,6 +43,8 @@ extern "C" { #define MCPWM_LL_EVENT_CAPTURE(cap) (1 << ((cap) + 27)) // Maximum values due to limited register bit width +#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_DEAD_DELAY 65536 diff --git a/components/hal/esp32c6/include/hal/mcpwm_ll.h b/components/hal/esp32c6/include/hal/mcpwm_ll.h index 3a94ff3cfbd..67cab15587a 100644 --- a/components/hal/esp32c6/include/hal/mcpwm_ll.h +++ b/components/hal/esp32c6/include/hal/mcpwm_ll.h @@ -45,6 +45,8 @@ extern "C" { #define MCPWM_LL_EVENT_CAPTURE(cap) (1 << ((cap) + 27)) // Maximum values due to limited register bit width +#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_DEAD_DELAY 65536 diff --git a/components/hal/esp32h2/include/hal/mcpwm_ll.h b/components/hal/esp32h2/include/hal/mcpwm_ll.h index 1023d115310..fa8e4d4a198 100644 --- a/components/hal/esp32h2/include/hal/mcpwm_ll.h +++ b/components/hal/esp32h2/include/hal/mcpwm_ll.h @@ -43,6 +43,8 @@ extern "C" { #define MCPWM_LL_EVENT_CAPTURE(cap) (1 << ((cap) + 27)) // Maximum values due to limited register bit width +#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_DEAD_DELAY 65536 diff --git a/components/hal/esp32s3/include/hal/mcpwm_ll.h b/components/hal/esp32s3/include/hal/mcpwm_ll.h index 0111b62261b..d18459b76d3 100644 --- a/components/hal/esp32s3/include/hal/mcpwm_ll.h +++ b/components/hal/esp32s3/include/hal/mcpwm_ll.h @@ -43,6 +43,8 @@ extern "C" { #define MCPWM_LL_EVENT_CAPTURE(cap) (1 << ((cap) + 27)) // Maximum values due to limited register bit width +#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_DEAD_DELAY 65536 diff --git a/components/hal/include/hal/mcpwm_types.h b/components/hal/include/hal/mcpwm_types.h index 7ee1beb495b..098dd03c9f2 100644 --- a/components/hal/include/hal/mcpwm_types.h +++ b/components/hal/include/hal/mcpwm_types.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -31,6 +31,15 @@ typedef soc_periph_mcpwm_capture_clk_src_t mcpwm_capture_clock_source_t; typedef int mcpwm_capture_clock_source_t; #endif // SOC_MCPWM_SUPPORTED +/** + * @brief MCPWM carrier clock source + */ +#if SOC_MCPWM_SUPPORTED +typedef soc_periph_mcpwm_carrier_clk_src_t mcpwm_carrier_clock_source_t; +#else +typedef int mcpwm_carrier_clock_source_t; +#endif // SOC_MCPWM_SUPPORTED + /** * @brief MCPWM timer count direction */ diff --git a/components/soc/esp32/include/soc/clk_tree_defs.h b/components/soc/esp32/include/soc/clk_tree_defs.h index 83ef092220e..a3c569cdd43 100644 --- a/components/soc/esp32/include/soc/clk_tree_defs.h +++ b/components/soc/esp32/include/soc/clk_tree_defs.h @@ -239,6 +239,19 @@ typedef enum { MCPWM_CAPTURE_CLK_SRC_DEFAULT = SOC_MOD_CLK_APB, /*!< Select APB as the default clock choice */ } soc_periph_mcpwm_capture_clk_src_t; +/** + * @brief Array initializer for all supported clock sources of MCPWM Carrier + */ +#define SOC_MCPWM_CARRIER_CLKS {SOC_MOD_CLK_PLL_F160M} + +/** + * @brief Type of MCPWM carrier clock source + */ +typedef enum { + MCPWM_CARRIER_CLK_SRC_PLL160M = SOC_MOD_CLK_PLL_F160M, /*!< Select PLL_F160M as the source clock */ + MCPWM_CARRIER_CLK_SRC_DEFAULT = SOC_MOD_CLK_PLL_F160M, /*!< Select PLL_F160M as the default clock choice */ +} soc_periph_mcpwm_carrier_clk_src_t; + ///////////////////////////////////////////////////I2S////////////////////////////////////////////////////////////////// /** * @brief Array initializer for all supported clock sources of I2S diff --git a/components/soc/esp32c6/include/soc/clk_tree_defs.h b/components/soc/esp32c6/include/soc/clk_tree_defs.h index 4a68e548c2f..13b94792508 100644 --- a/components/soc/esp32c6/include/soc/clk_tree_defs.h +++ b/components/soc/esp32c6/include/soc/clk_tree_defs.h @@ -255,6 +255,20 @@ typedef enum { MCPWM_CAPTURE_CLK_SRC_DEFAULT = SOC_MOD_CLK_PLL_F160M, /*!< Select PLL_F160M as the default clock choice */ } soc_periph_mcpwm_capture_clk_src_t; +/** + * @brief Array initializer for all supported clock sources of MCPWM Carrier + */ +#define SOC_MCPWM_CARRIER_CLKS {SOC_MOD_CLK_PLL_F160M, SOC_MOD_CLK_XTAL} + +/** + * @brief Type of MCPWM carrier clock source + */ +typedef enum { + MCPWM_CARRIER_CLK_SRC_PLL160M = SOC_MOD_CLK_PLL_F160M, /*!< Select PLL_F160M as the source clock */ + MCPWM_CARRIER_CLK_SRC_XTAL = SOC_MOD_CLK_XTAL, /*!< Select XTAL as the source clock */ + MCPWM_CARRIER_CLK_SRC_DEFAULT = SOC_MOD_CLK_PLL_F160M, /*!< Select PLL_F160M as the default clock choice */ +} soc_periph_mcpwm_carrier_clk_src_t; + ///////////////////////////////////////////////////// I2S ////////////////////////////////////////////////////////////// /** diff --git a/components/soc/esp32h2/include/soc/clk_tree_defs.h b/components/soc/esp32h2/include/soc/clk_tree_defs.h index 39a6b9d6db3..33ba32996f9 100644 --- a/components/soc/esp32h2/include/soc/clk_tree_defs.h +++ b/components/soc/esp32h2/include/soc/clk_tree_defs.h @@ -264,6 +264,20 @@ typedef enum { MCPWM_CAPTURE_CLK_SRC_DEFAULT = SOC_MOD_CLK_PLL_F96M, /*!< Select PLL_F96M as the default clock choice */ } soc_periph_mcpwm_capture_clk_src_t; +/** + * @brief Array initializer for all supported clock sources of MCPWM Carrier + */ +#define SOC_MCPWM_CARRIER_CLKS {SOC_MOD_CLK_PLL_F96M, SOC_MOD_CLK_XTAL} + +/** + * @brief Type of MCPWM carrier clock source + */ +typedef enum { + MCPWM_CARRIER_CLK_SRC_PLL96M = SOC_MOD_CLK_PLL_F96M, /*!< Select PLL_F96M as the source clock */ + MCPWM_CARRIER_CLK_SRC_XTAL = SOC_MOD_CLK_XTAL, /*!< Select XTAL as the source clock */ + MCPWM_CARRIER_CLK_SRC_DEFAULT = SOC_MOD_CLK_PLL_F96M, /*!< Select PLL_F96M as the default clock choice */ +} soc_periph_mcpwm_carrier_clk_src_t; + ///////////////////////////////////////////////////// I2S ////////////////////////////////////////////////////////////// /** diff --git a/components/soc/esp32s3/include/soc/clk_tree_defs.h b/components/soc/esp32s3/include/soc/clk_tree_defs.h index 0e5911a8934..26e0bb6a4c0 100644 --- a/components/soc/esp32s3/include/soc/clk_tree_defs.h +++ b/components/soc/esp32s3/include/soc/clk_tree_defs.h @@ -257,6 +257,19 @@ typedef enum { MCPWM_CAPTURE_CLK_SRC_DEFAULT = SOC_MOD_CLK_APB, /*!< Select APB as the default clock choice */ } soc_periph_mcpwm_capture_clk_src_t; +/** + * @brief Array initializer for all supported clock sources of MCPWM Carrier + */ +#define SOC_MCPWM_CARRIER_CLKS {SOC_MOD_CLK_PLL_F160M} + +/** + * @brief Type of MCPWM carrier clock source + */ +typedef enum { + MCPWM_CARRIER_CLK_SRC_PLL160M = SOC_MOD_CLK_PLL_F160M, /*!< Select PLL_F160M as the source clock */ + MCPWM_CARRIER_CLK_SRC_DEFAULT = SOC_MOD_CLK_PLL_F160M, /*!< Select PLL_F160M as the default clock choice */ +} soc_periph_mcpwm_carrier_clk_src_t; + ///////////////////////////////////////////////////// I2S ////////////////////////////////////////////////////////////// /** diff --git a/docs/en/api-reference/peripherals/mcpwm.rst b/docs/en/api-reference/peripherals/mcpwm.rst index 4190e6ac90c..7957382b1a8 100644 --- a/docs/en/api-reference/peripherals/mcpwm.rst +++ b/docs/en/api-reference/peripherals/mcpwm.rst @@ -658,10 +658,11 @@ The MCPWM operator has a carrier submodule that can be used if galvanic isolatio To configure the carrier submodule, you can call :cpp:func:`mcpwm_operator_apply_carrier`, and provide configuration structure :cpp:type:`mcpwm_carrier_config_t`: -- :cpp:member:`mcpwm_carrier_config_t::frequency_hz`: The carrier frequency in Hz. -- :cpp:member:`mcpwm_carrier_config_t::duty_cycle`: The duty cycle of the carrier. Note that, the supported choices of duty cycle are discrete, the driver will search the nearest one based the user configuration. -- :cpp:member:`mcpwm_carrier_config_t::first_pulse_duration_us`: The duration of the first pulse in microseconds. The resolution of the first pulse duration is determined by the carrier frequency you set in the :cpp:member:`mcpwm_carrier_config_t::frequency_hz`. The first pulse duration can't be zero, and it has to be at least one period of the carrier. A longer pulse width can help conduct the inductance quicker. -- :cpp:member:`mcpwm_carrier_config_t::invert_before_modulate` and :cpp:member:`mcpwm_carrier_config_t::invert_after_modulate`: Set whether to invert the carrier output before and after modulation. +- :cpp:member:`mcpwm_carrier_config_t::clk_src` sets the clock source of the carrier. +- :cpp:member:`mcpwm_carrier_config_t::frequency_hz` indicates carrier frequency in Hz. +- :cpp:member:`mcpwm_carrier_config_t::duty_cycle` indicates the duty cycle of the carrier. Note that, the supported choices of the duty cycle are discrete, the driver will search for the nearest one based on your configuration. +- :cpp:member:`mcpwm_carrier_config_t::first_pulse_duration_us` indicates the duration of the first pulse in microseconds. The resolution of the first pulse duration is determined by the carrier frequency you set in the :cpp:member:`mcpwm_carrier_config_t::frequency_hz`. The first pulse duration can't be zero, and it has to be at least one period of the carrier. A longer pulse width can help conduct the inductance quicker. +- :cpp:member:`mcpwm_carrier_config_t::invert_before_modulate` and :cpp:member:`mcpwm_carrier_config_t::invert_after_modulate` set whether to invert the carrier output before and after modulation. Specifically, the carrier submodule can be disabled by calling :cpp:func:`mcpwm_operator_apply_carrier` with a ``NULL`` configuration.