Skip to content

Commit

Permalink
feat(MCPWM): Add mcpwm carrier clk source
Browse files Browse the repository at this point in the history
The MCPWM carrier is part of the operator and can work independently
without the MCPWM timer being enabled. This commit add the MCPWM
carrier clk source.
  • Loading branch information
Kainarx committed Aug 25, 2023
1 parent 06a4943 commit c240a1f
Show file tree
Hide file tree
Showing 14 changed files with 101 additions and 35 deletions.
1 change: 1 addition & 0 deletions components/driver/mcpwm/include/driver/mcpwm_oper.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand Down
11 changes: 8 additions & 3 deletions components/driver/mcpwm/mcpwm_oper.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
1 change: 1 addition & 0 deletions components/driver/mcpwm/mcpwm_timer.c
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,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) {
Expand Down
41 changes: 14 additions & 27 deletions components/driver/test_apps/mcpwm/main/test_mcpwm_oper.c
Original file line number Diff line number Diff line change
Expand Up @@ -64,59 +64,46 @@ 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,
};
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)
Expand Down
2 changes: 2 additions & 0 deletions components/hal/esp32/include/hal/mcpwm_ll.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 2 additions & 0 deletions components/hal/esp32c6/include/hal/mcpwm_ll.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 2 additions & 0 deletions components/hal/esp32h2/include/hal/mcpwm_ll.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 2 additions & 0 deletions components/hal/esp32s3/include/hal/mcpwm_ll.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
11 changes: 10 additions & 1 deletion components/hal/include/hal/mcpwm_types.h
Original file line number Diff line number Diff line change
@@ -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
*/
Expand Down Expand Up @@ -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
*/
Expand Down
13 changes: 13 additions & 0 deletions components/soc/esp32/include/soc/clk_tree_defs.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
14 changes: 14 additions & 0 deletions components/soc/esp32c6/include/soc/clk_tree_defs.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 //////////////////////////////////////////////////////////////

/**
Expand Down
14 changes: 14 additions & 0 deletions components/soc/esp32h2/include/soc/clk_tree_defs.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 //////////////////////////////////////////////////////////////

/**
Expand Down
13 changes: 13 additions & 0 deletions components/soc/esp32s3/include/soc/clk_tree_defs.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 //////////////////////////////////////////////////////////////

/**
Expand Down
9 changes: 5 additions & 4 deletions docs/en/api-reference/peripherals/mcpwm.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand Down

0 comments on commit c240a1f

Please sign in to comment.