diff --git a/components/driver/include/driver/mcpwm.h b/components/driver/include/driver/mcpwm.h index 7c41efc5db..3e34fac02c 100644 --- a/components/driver/include/driver/mcpwm.h +++ b/components/driver/include/driver/mcpwm.h @@ -11,7 +11,6 @@ #include "esp_err.h" #include "soc/soc.h" #include "driver/gpio.h" -#include "driver/periph_ctrl.h" #include "esp_intr_alloc.h" #include "hal/mcpwm_types.h" @@ -46,8 +45,7 @@ typedef enum { } mcpwm_io_signals_t; /** - * @brief MCPWM pin number for - * + * @brief pin number for MCPWM */ typedef struct { int mcpwm0a_out_num; /*!= SOC_MCPWM_TIMERS_PER_GROUP, "This driver assumes the timer num equals to the operator num."); @@ -100,12 +101,12 @@ esp_err_t mcpwm_gpio_init(mcpwm_unit_t mcpwm_num, mcpwm_io_signals_t io_signal, esp_rom_gpio_connect_out_signal(gpio_num, mcpwm_periph_signals.groups[mcpwm_num].operators[operator_id].generators[generator_id].pwm_sig, 0, 0); } else if (io_signal <= MCPWM_SYNC_2) { // External sync input signal gpio_set_direction(gpio_num, GPIO_MODE_INPUT); - int ext_sync_id = io_signal - MCPWM_SYNC_0; - esp_rom_gpio_connect_in_signal(gpio_num, mcpwm_periph_signals.groups[mcpwm_num].ext_syncers[ext_sync_id].sync_sig, 0); + int gpio_sync_id = io_signal - MCPWM_SYNC_0; + esp_rom_gpio_connect_in_signal(gpio_num, mcpwm_periph_signals.groups[mcpwm_num].gpio_synchros[gpio_sync_id].sync_sig, 0); } else if (io_signal <= MCPWM_FAULT_2) { // Fault input signal gpio_set_direction(gpio_num, GPIO_MODE_INPUT); int fault_id = io_signal - MCPWM_FAULT_0; - esp_rom_gpio_connect_in_signal(gpio_num, mcpwm_periph_signals.groups[mcpwm_num].detectors[fault_id].fault_sig, 0); + esp_rom_gpio_connect_in_signal(gpio_num, mcpwm_periph_signals.groups[mcpwm_num].gpio_faults[fault_id].fault_sig, 0); } else if (io_signal >= MCPWM_CAP_0 && io_signal <= MCPWM_CAP_2) { // Capture input signal gpio_set_direction(gpio_num, GPIO_MODE_INPUT); int capture_id = io_signal - MCPWM_CAP_0; @@ -141,7 +142,7 @@ esp_err_t mcpwm_start(mcpwm_unit_t mcpwm_num, mcpwm_timer_t timer_num) MCPWM_TIMER_CHECK(mcpwm_num, timer_num); mcpwm_critical_enter(mcpwm_num); - mcpwm_ll_timer_set_operate_command(context[mcpwm_num].hal.dev, timer_num, MCPWM_TIMER_START_NO_STOP); + mcpwm_ll_timer_set_execute_command(context[mcpwm_num].hal.dev, timer_num, MCPWM_TIMER_START_NO_STOP); mcpwm_critical_exit(mcpwm_num); return ESP_OK; } @@ -151,7 +152,7 @@ esp_err_t mcpwm_stop(mcpwm_unit_t mcpwm_num, mcpwm_timer_t timer_num) MCPWM_TIMER_CHECK(mcpwm_num, timer_num); mcpwm_critical_enter(mcpwm_num); - mcpwm_ll_timer_set_operate_command(context[mcpwm_num].hal.dev, timer_num, MCPWM_TIMER_STOP_AT_ZERO); + mcpwm_ll_timer_set_execute_command(context[mcpwm_num].hal.dev, timer_num, MCPWM_TIMER_STOP_AT_ZERO); mcpwm_critical_exit(mcpwm_num); return ESP_OK; } @@ -312,10 +313,10 @@ esp_err_t mcpwm_init(mcpwm_unit_t mcpwm_num, mcpwm_timer_t timer_num, const mcpw mcpwm_hal_init(hal, &config); mcpwm_critical_enter(mcpwm_num); - mcpwm_ll_group_set_clock(hal->dev, MCPWM_GROUP_CLK_HZ); + mcpwm_ll_group_set_clock_prescale(hal->dev, MCPWM_GROUP_CLK_PRESCALE); mcpwm_ll_group_enable_shadow_mode(hal->dev); mcpwm_ll_group_flush_shadow(hal->dev); - mcpwm_ll_timer_set_clock(hal->dev, timer_num, MCPWM_GROUP_CLK_HZ, MCPWM_TIMER_CLK_HZ); + mcpwm_ll_timer_set_clock_prescale(hal->dev, timer_num, MCPWM_GROUP_CLK_HZ / MCPWM_TIMER_CLK_HZ); mcpwm_ll_timer_set_count_mode(hal->dev, timer_num, mcpwm_conf->counter_mode); mcpwm_ll_timer_update_period_at_once(hal->dev, timer_num); mcpwm_ll_timer_set_peak(hal->dev, timer_num, MCPWM_TIMER_CLK_HZ / mcpwm_conf->frequency, false); @@ -336,8 +337,8 @@ uint32_t mcpwm_get_frequency(mcpwm_unit_t mcpwm_num, mcpwm_timer_t timer_num) MCPWM_TIMER_CHECK(mcpwm_num, timer_num); mcpwm_hal_context_t *hal = &context[mcpwm_num].hal; mcpwm_critical_enter(mcpwm_num); - unsigned long long group_clock = mcpwm_ll_group_get_clock(hal->dev); - unsigned long long timer_clock = mcpwm_ll_timer_get_clock(hal->dev, timer_num, group_clock); + unsigned int group_clock = SOC_MCPWM_BASE_CLK_HZ / mcpwm_ll_group_get_clock_prescale(hal->dev); + unsigned int timer_clock = group_clock / mcpwm_ll_timer_get_clock_prescale(hal->dev, timer_num); mcpwm_critical_exit(mcpwm_num); return (uint32_t)timer_clock; } @@ -479,7 +480,8 @@ esp_err_t mcpwm_deadtime_enable(mcpwm_unit_t mcpwm_num, mcpwm_timer_t timer_num, mcpwm_critical_enter(mcpwm_num); mcpwm_ll_deadtime_enable_update_delay_on_tez(hal->dev, op, true); - mcpwm_ll_deadtime_set_resolution_same_to_timer(hal->dev, op, false); + // The dead time delay unit equals to MCPWM group resolution + mcpwm_ll_deadtime_resolution_to_timer(hal->dev, op, false); mcpwm_ll_deadtime_set_rising_delay(hal->dev, op, red + 1); mcpwm_ll_deadtime_set_falling_delay(hal->dev, op, fed + 1); switch (dt_mode) { @@ -611,11 +613,12 @@ esp_err_t mcpwm_fault_set_cyc_mode(mcpwm_unit_t mcpwm_num, mcpwm_timer_t timer_n mcpwm_critical_enter(mcpwm_num); mcpwm_ll_fault_enable_cbc_mode(hal->dev, op, fault_sig, true); + mcpwm_ll_fault_enable_cbc_refresh_on_tez(hal->dev, op, true); mcpwm_ll_fault_enable_oneshot_mode(hal->dev, op, fault_sig, false); - mcpwm_ll_generator_set_action_on_fault_event(hal->dev, op, 0, MCPWM_TIMER_DIRECTION_DOWN, MCPWM_FAULT_REACTION_CBC, action_on_pwmxa); - mcpwm_ll_generator_set_action_on_fault_event(hal->dev, op, 0, MCPWM_TIMER_DIRECTION_UP, MCPWM_FAULT_REACTION_CBC, action_on_pwmxa); - mcpwm_ll_generator_set_action_on_fault_event(hal->dev, op, 1, MCPWM_TIMER_DIRECTION_DOWN, MCPWM_FAULT_REACTION_CBC, action_on_pwmxb); - mcpwm_ll_generator_set_action_on_fault_event(hal->dev, op, 1, MCPWM_TIMER_DIRECTION_UP, MCPWM_FAULT_REACTION_CBC, action_on_pwmxb); + mcpwm_ll_generator_set_action_on_trip_event(hal->dev, op, 0, MCPWM_TIMER_DIRECTION_DOWN, MCPWM_TRIP_TYPE_CBC, action_on_pwmxa); + mcpwm_ll_generator_set_action_on_trip_event(hal->dev, op, 0, MCPWM_TIMER_DIRECTION_UP, MCPWM_TRIP_TYPE_CBC, action_on_pwmxa); + mcpwm_ll_generator_set_action_on_trip_event(hal->dev, op, 1, MCPWM_TIMER_DIRECTION_DOWN, MCPWM_TRIP_TYPE_CBC, action_on_pwmxb); + mcpwm_ll_generator_set_action_on_trip_event(hal->dev, op, 1, MCPWM_TIMER_DIRECTION_UP, MCPWM_TRIP_TYPE_CBC, action_on_pwmxb); mcpwm_critical_exit(mcpwm_num); return ESP_OK; } @@ -632,10 +635,10 @@ esp_err_t mcpwm_fault_set_oneshot_mode(mcpwm_unit_t mcpwm_num, mcpwm_timer_t tim mcpwm_ll_fault_clear_ost(hal->dev, op); mcpwm_ll_fault_enable_oneshot_mode(hal->dev, op, fault_sig, true); mcpwm_ll_fault_enable_cbc_mode(hal->dev, op, fault_sig, false); - mcpwm_ll_generator_set_action_on_fault_event(hal->dev, op, 0, MCPWM_TIMER_DIRECTION_DOWN, MCPWM_FAULT_REACTION_OST, action_on_pwmxa); - mcpwm_ll_generator_set_action_on_fault_event(hal->dev, op, 0, MCPWM_TIMER_DIRECTION_UP, MCPWM_FAULT_REACTION_OST, action_on_pwmxa); - mcpwm_ll_generator_set_action_on_fault_event(hal->dev, op, 1, MCPWM_TIMER_DIRECTION_DOWN, MCPWM_FAULT_REACTION_OST, action_on_pwmxb); - mcpwm_ll_generator_set_action_on_fault_event(hal->dev, op, 1, MCPWM_TIMER_DIRECTION_UP, MCPWM_FAULT_REACTION_OST, action_on_pwmxb); + mcpwm_ll_generator_set_action_on_trip_event(hal->dev, op, 0, MCPWM_TIMER_DIRECTION_DOWN, MCPWM_TRIP_TYPE_OST, action_on_pwmxa); + mcpwm_ll_generator_set_action_on_trip_event(hal->dev, op, 0, MCPWM_TIMER_DIRECTION_UP, MCPWM_TRIP_TYPE_OST, action_on_pwmxa); + mcpwm_ll_generator_set_action_on_trip_event(hal->dev, op, 1, MCPWM_TIMER_DIRECTION_DOWN, MCPWM_TRIP_TYPE_OST, action_on_pwmxb); + mcpwm_ll_generator_set_action_on_trip_event(hal->dev, op, 1, MCPWM_TIMER_DIRECTION_UP, MCPWM_TRIP_TYPE_OST, action_on_pwmxb); mcpwm_critical_exit(mcpwm_num); return ESP_OK; } @@ -644,7 +647,7 @@ esp_err_t mcpwm_capture_enable(mcpwm_unit_t mcpwm_num, mcpwm_capture_signal_t ca uint32_t num_of_pulse) { ESP_RETURN_ON_FALSE(mcpwm_num < SOC_MCPWM_GROUPS, ESP_ERR_INVALID_ARG, TAG, MCPWM_GROUP_NUM_ERROR); - ESP_RETURN_ON_FALSE(num_of_pulse <= MCPWM_LL_MAX_PRESCALE, ESP_ERR_INVALID_ARG, TAG, MCPWM_PRESCALE_ERROR); + ESP_RETURN_ON_FALSE(num_of_pulse <= MCPWM_LL_MAX_CAPTURE_PRESCALE, ESP_ERR_INVALID_ARG, TAG, MCPWM_PRESCALE_ERROR); ESP_RETURN_ON_FALSE(cap_sig < SOC_MCPWM_CAPTURE_CHANNELS_PER_TIMER, ESP_ERR_INVALID_ARG, TAG, MCPWM_CAPTURE_ERROR); mcpwm_hal_context_t *hal = &context[mcpwm_num].hal; // enable MCPWM module incase user don't use `mcpwm_init` at all @@ -654,7 +657,7 @@ esp_err_t mcpwm_capture_enable(mcpwm_unit_t mcpwm_num, mcpwm_capture_signal_t ca }; mcpwm_critical_enter(mcpwm_num); mcpwm_hal_init(hal, &init_config); - mcpwm_ll_group_set_clock(hal->dev, MCPWM_GROUP_CLK_HZ); + mcpwm_ll_group_set_clock_prescale(hal->dev, MCPWM_GROUP_CLK_PRESCALE); mcpwm_ll_capture_enable_timer(hal->dev, true); mcpwm_ll_capture_enable_channel(hal->dev, cap_sig, true); mcpwm_ll_capture_enable_negedge(hal->dev, cap_sig, cap_edge & MCPWM_NEG_EDGE); @@ -707,9 +710,9 @@ esp_err_t mcpwm_sync_enable(mcpwm_unit_t mcpwm_num, mcpwm_timer_t timer_num, mcp uint32_t set_phase = mcpwm_ll_timer_get_peak(hal->dev, timer_num, false) * phase_val / 1000; mcpwm_ll_timer_set_sync_phase_value(hal->dev, timer_num, set_phase); if (sync_sig >= MCPWM_SELECT_SYNC0) { - mcpwm_ll_timer_enable_sync_from_external(hal->dev, timer_num, sync_sig - MCPWM_SELECT_SYNC0); + mcpwm_ll_timer_set_timer_synchro(hal->dev, timer_num, sync_sig - MCPWM_SELECT_SYNC0); } - mcpwm_ll_timer_sync_out_same_in(hal->dev, timer_num); + mcpwm_ll_timer_sync_out_penetrate(hal->dev, timer_num); mcpwm_ll_timer_enable_sync_input(hal->dev, timer_num, true); mcpwm_critical_exit(mcpwm_num); return ESP_OK; diff --git a/components/driver/test/test_pwm.c b/components/driver/test/test_pwm.c index 9baef70d5a..17422ee5c9 100644 --- a/components/driver/test/test_pwm.c +++ b/components/driver/test/test_pwm.c @@ -52,12 +52,12 @@ static esp_err_t test_mcpwm_gpio_init(mcpwm_unit_t mcpwm_num, mcpwm_io_signals_t esp_rom_gpio_connect_out_signal(gpio_num, mcpwm_periph_signals.groups[mcpwm_num].operators[operator_id].generators[generator_id].pwm_sig, 0, 0); } else if (io_signal <= MCPWM_SYNC_2) { // External sync input signal gpio_set_direction(gpio_num, GPIO_MODE_INPUT_OUTPUT); - int ext_sync_id = io_signal - MCPWM_SYNC_0; - esp_rom_gpio_connect_in_signal(gpio_num, mcpwm_periph_signals.groups[mcpwm_num].ext_syncers[ext_sync_id].sync_sig, 0); + int gpio_sync_id = io_signal - MCPWM_SYNC_0; + esp_rom_gpio_connect_in_signal(gpio_num, mcpwm_periph_signals.groups[mcpwm_num].gpio_synchros[gpio_sync_id].sync_sig, 0); } else if (io_signal <= MCPWM_FAULT_2) { // Fault input signal gpio_set_direction(gpio_num, GPIO_MODE_INPUT_OUTPUT); int fault_id = io_signal - MCPWM_FAULT_0; - esp_rom_gpio_connect_in_signal(gpio_num, mcpwm_periph_signals.groups[mcpwm_num].detectors[fault_id].fault_sig, 0); + esp_rom_gpio_connect_in_signal(gpio_num, mcpwm_periph_signals.groups[mcpwm_num].gpio_faults[fault_id].fault_sig, 0); } else if (io_signal >= MCPWM_CAP_0 && io_signal <= MCPWM_CAP_2) { // Capture input signal gpio_set_direction(gpio_num, GPIO_MODE_INPUT_OUTPUT); int capture_id = io_signal - MCPWM_CAP_0; diff --git a/components/hal/esp32/include/hal/mcpwm_ll.h b/components/hal/esp32/include/hal/mcpwm_ll.h index 09674c02da..eff518fc13 100644 --- a/components/hal/esp32/include/hal/mcpwm_ll.h +++ b/components/hal/esp32/include/hal/mcpwm_ll.h @@ -26,31 +26,36 @@ #include "soc/soc_caps.h" #include "soc/mcpwm_struct.h" #include "hal/mcpwm_types.h" +#include "hal/assert.h" #ifdef __cplusplus extern "C" { #endif /// Get the address of peripheral registers -#define MCPWM_LL_GET_HW(ID) (((ID) == 0) ? &MCPWM0 : &MCPWM1) -#define MCPWM_LL_MAX_PRESCALE 255 +#define MCPWM_LL_GET_HW(ID) (((ID) == 0) ? &MCPWM0 : &MCPWM1) +#define MCPWM_LL_MAX_CAPTURE_PRESCALE 255 +#define MCPWM_LL_MAX_COMPARE_VALUE 65535 +#define MCPWM_LL_MAX_DEAD_DELAY 65535 +#define MCPWM_LL_MAX_PHASE_VALUE 65535 /********************* Group registers *******************/ // Set/Get group clock: PWM_clk = CLK_160M / (prescale + 1) -static inline void mcpwm_ll_group_set_clock(mcpwm_dev_t *mcpwm, unsigned long long group_clk_hz) +static inline void mcpwm_ll_group_set_clock_prescale(mcpwm_dev_t *mcpwm, int pre_scale) { - mcpwm->clk_cfg.prescale = (SOC_MCPWM_BASE_CLK_HZ / group_clk_hz) - 1; + mcpwm->clk_cfg.prescale = pre_scale - 1; } -static inline unsigned long long mcpwm_ll_group_get_clock(mcpwm_dev_t *mcpwm) +static inline uint32_t mcpwm_ll_group_get_clock_prescale(mcpwm_dev_t *mcpwm) { - return SOC_MCPWM_BASE_CLK_HZ / (mcpwm->clk_cfg.prescale + 1); + return mcpwm->clk_cfg.prescale + 1; } static inline void mcpwm_ll_group_enable_shadow_mode(mcpwm_dev_t *mcpwm) { mcpwm->update_cfg.global_up_en = 1; + // updating of active registers in MCPWM operators should be enabled mcpwm->update_cfg.op0_up_en = 1; mcpwm->update_cfg.op1_up_en = 1; mcpwm->update_cfg.op2_up_en = 1; @@ -58,7 +63,8 @@ static inline void mcpwm_ll_group_enable_shadow_mode(mcpwm_dev_t *mcpwm) static inline void mcpwm_ll_group_flush_shadow(mcpwm_dev_t *mcpwm) { - mcpwm->update_cfg.val ^= (1 << 1); + // a toggle can trigger a forced update of all active registers in MCPWM, i.e. shadow->active + mcpwm->update_cfg.global_force_up = ~mcpwm->update_cfg.global_force_up; } /********************* Interrupt registers *******************/ @@ -129,47 +135,47 @@ static inline uint32_t mcpwm_ll_intr_get_capture_status(mcpwm_dev_t *mcpwm) static inline void mcpwm_ll_intr_clear_timer_stop_status(mcpwm_dev_t *mcpwm, uint32_t timer_mask) { - mcpwm->int_clr.val |= (timer_mask & 0x07) << 0; + mcpwm->int_clr.val = (timer_mask & 0x07) << 0; } static inline void mcpwm_ll_intr_clear_timer_tez_status(mcpwm_dev_t *mcpwm, uint32_t timer_mask) { - mcpwm->int_clr.val |= (timer_mask & 0x07) << 3; + mcpwm->int_clr.val = (timer_mask & 0x07) << 3; } static inline void mcpwm_ll_intr_clear_timer_tep_status(mcpwm_dev_t *mcpwm, uint32_t timer_mask) { - mcpwm->int_clr.val |= (timer_mask & 0x07) << 6; + mcpwm->int_clr.val = (timer_mask & 0x07) << 6; } static inline void mcpwm_ll_intr_clear_fault_enter_status(mcpwm_dev_t *mcpwm, uint32_t fault_mask) { - mcpwm->int_clr.val |= (fault_mask & 0x07) << 9; + mcpwm->int_clr.val = (fault_mask & 0x07) << 9; } static inline void mcpwm_ll_intr_clear_fault_exit_status(mcpwm_dev_t *mcpwm, uint32_t fault_mask) { - mcpwm->int_clr.val |= (fault_mask & 0x07) << 12; + mcpwm->int_clr.val = (fault_mask & 0x07) << 12; } static inline void mcpwm_ll_intr_clear_compare_status(mcpwm_dev_t *mcpwm, uint32_t operator_mask, uint32_t cmp_id) { - mcpwm->int_clr.val |= (operator_mask & 0x07) << (15 + cmp_id * 3); + mcpwm->int_clr.val = (operator_mask & 0x07) << (15 + cmp_id * 3); } static inline void mcpwm_ll_intr_clear_trip_cbc_status(mcpwm_dev_t *mcpwm, uint32_t cbc_mask) { - mcpwm->int_clr.val |= (cbc_mask & 0x07) << 21; + mcpwm->int_clr.val = (cbc_mask & 0x07) << 21; } static inline void mcpwm_ll_intr_clear_trip_ost_status(mcpwm_dev_t *mcpwm, uint32_t ost_mask) { - mcpwm->int_clr.val |= (ost_mask & 0x07) << 24; + mcpwm->int_clr.val = (ost_mask & 0x07) << 24; } static inline void mcpwm_ll_intr_clear_capture_status(mcpwm_dev_t *mcpwm, uint32_t capture_mask) { - mcpwm->int_clr.val |= (capture_mask & 0x07) << 27; + mcpwm->int_clr.val = (capture_mask & 0x07) << 27; } //////////// enable interrupt for each event //////////////// @@ -201,13 +207,20 @@ static inline void mcpwm_ll_intr_enable_timer_tep(mcpwm_dev_t *mcpwm, uint32_t t } } -static inline void mcpwm_ll_intr_enable_fault(mcpwm_dev_t *mcpwm, uint32_t fault_id, bool enable) +static inline void mcpwm_ll_intr_enable_fault_enter(mcpwm_dev_t *mcpwm, uint32_t fault_id, bool enable) { if (enable) { mcpwm->int_ena.val |= 1 << (9 + fault_id); // enter fault interrupt - mcpwm->int_ena.val |= 1 << (12 + fault_id); // exit fault interrupt } else { mcpwm->int_ena.val &= ~(1 << (9 + fault_id)); + } +} + +static inline void mcpwm_ll_intr_enable_fault_exit(mcpwm_dev_t *mcpwm, uint32_t fault_id, bool enable) +{ + if (enable) { + mcpwm->int_ena.val |= 1 << (12 + fault_id); // exit fault interrupt + } else { mcpwm->int_ena.val &= ~(1 << (12 + fault_id)); } } @@ -221,13 +234,20 @@ static inline void mcpwm_ll_intr_enable_compare(mcpwm_dev_t *mcpwm, uint32_t ope } } -static inline void mcpwm_ll_intr_enable_trip(mcpwm_dev_t *mcpwm, uint32_t operator_id, bool enable) +static inline void mcpwm_ll_intr_enable_trip_cbc(mcpwm_dev_t *mcpwm, uint32_t operator_id, bool enable) { if (enable) { mcpwm->int_ena.val |= (1 << (21 + operator_id)); - mcpwm->int_ena.val |= (1 << (24 + operator_id)); } else { mcpwm->int_ena.val &= ~(1 << (21 + operator_id)); + } +} + +static inline void mcpwm_ll_intr_enable_trip_ost(mcpwm_dev_t *mcpwm, uint32_t operator_id, bool enable) +{ + if (enable) { + mcpwm->int_ena.val |= (1 << (24 + operator_id)); + } else { mcpwm->int_ena.val &= ~(1 << (24 + operator_id)); } } @@ -243,14 +263,14 @@ static inline void mcpwm_ll_intr_enable_capture(mcpwm_dev_t *mcpwm, uint32_t cap /********************* Timer registers *******************/ -static inline void mcpwm_ll_timer_set_clock(mcpwm_dev_t *mcpwm, int timer_id, unsigned long long group_clock, unsigned long long timer_clock) +static inline void mcpwm_ll_timer_set_clock_prescale(mcpwm_dev_t *mcpwm, int timer_id, uint32_t prescale) { - mcpwm->timer[timer_id].period.prescale = group_clock / timer_clock - 1; + mcpwm->timer[timer_id].period.prescale = prescale - 1; } -static inline unsigned long long mcpwm_ll_timer_get_clock(mcpwm_dev_t *mcpwm, int timer_id, unsigned long long group_clock) +static inline uint32_t mcpwm_ll_timer_get_clock_prescale(mcpwm_dev_t *mcpwm, int timer_id) { - return group_clock / (mcpwm->timer[timer_id].period.prescale + 1); + return mcpwm->timer[timer_id].period.prescale + 1; } static inline void mcpwm_ll_timer_set_peak(mcpwm_dev_t *mcpwm, int timer_id, uint32_t peak, bool symmetric) @@ -327,9 +347,9 @@ static inline mcpwm_timer_count_mode_t mcpwm_ll_timer_get_count_mode(mcpwm_dev_t } } -static inline void mcpwm_ll_timer_set_operate_command(mcpwm_dev_t *mcpwm, int timer_id, mcpwm_timer_operate_cmd_t mode) +static inline void mcpwm_ll_timer_set_execute_command(mcpwm_dev_t *mcpwm, int timer_id, mcpwm_timer_execute_cmd_t cmd) { - switch (mode) { + switch (cmd) { case MCPWM_TIMER_STOP_AT_ZERO: mcpwm->timer[timer_id].mode.start = 0; break; @@ -348,23 +368,14 @@ static inline void mcpwm_ll_timer_set_operate_command(mcpwm_dev_t *mcpwm, int ti } } -static inline void mcpwm_ll_timer_set_count_value(mcpwm_dev_t *mcpwm, int timer_id, uint32_t value) -{ - // we use software sync to set count value - int previous_phase = mcpwm->timer[timer_id].sync.timer_phase; - mcpwm->timer[timer_id].sync.timer_phase = value; - mcpwm->timer[timer_id].sync.sync_sw = ~mcpwm->timer[timer_id].sync.sync_sw; - mcpwm->timer[timer_id].sync.timer_phase = previous_phase; -} - static inline uint32_t mcpwm_ll_timer_get_count_value(mcpwm_dev_t *mcpwm, int timer_id) { return mcpwm->timer[timer_id].status.value; } -static inline bool mcpwm_ll_is_timer_decreasing(mcpwm_dev_t *mcpwm, int timer_id) +static inline mcpwm_timer_direction_t mcpwm_ll_timer_get_count_direction(mcpwm_dev_t *mcpwm, int timer_id) { - return mcpwm->timer[timer_id].status.direction; + return mcpwm->timer[timer_id].status.direction ? MCPWM_TIMER_DIRECTION_DOWN : MCPWM_TIMER_DIRECTION_UP; } static inline void mcpwm_ll_timer_enable_sync_input(mcpwm_dev_t *mcpwm, int timer_id, bool enable) @@ -372,8 +383,9 @@ static inline void mcpwm_ll_timer_enable_sync_input(mcpwm_dev_t *mcpwm, int time mcpwm->timer[timer_id].sync.in_en = enable; } -static inline void mcpwm_ll_timer_sync_out_same_in(mcpwm_dev_t *mcpwm, int timer_id) +static inline void mcpwm_ll_timer_sync_out_penetrate(mcpwm_dev_t *mcpwm, int timer_id) { + // sync_out is selected to sync_in mcpwm->timer[timer_id].sync.out_sel = 0; } @@ -383,47 +395,51 @@ static inline void mcpwm_ll_timer_sync_out_on_timer_event(mcpwm_dev_t *mcpwm, in mcpwm->timer[timer_id].sync.out_sel = 1; } else if (event == MCPWM_TIMER_EVENT_PEAK) { mcpwm->timer[timer_id].sync.out_sel = 2; + } else { + HAL_ASSERT(false); } } static inline void mcpwm_ll_timer_disable_sync_out(mcpwm_dev_t *mcpwm, int timer_id) { + // sync_out will always be zero mcpwm->timer[timer_id].sync.out_sel = 3; } -static inline void mcpwm_ll_timer_trigger_sw_sync(mcpwm_dev_t *mcpwm, int timer_id) +static inline void mcpwm_ll_timer_trigger_soft_sync(mcpwm_dev_t *mcpwm, int timer_id) { mcpwm->timer[timer_id].sync.sync_sw = ~mcpwm->timer[timer_id].sync.sync_sw; } -static inline void mcpwm_ll_timer_set_sync_phase_value(mcpwm_dev_t *mcpwm, int timer_id, uint32_t reload_val) +static inline void mcpwm_ll_timer_set_sync_phase_value(mcpwm_dev_t *mcpwm, int timer_id, uint32_t phase_value) { - mcpwm->timer[timer_id].sync.timer_phase = reload_val; + mcpwm->timer[timer_id].sync.timer_phase = phase_value; } -static inline uint32_t mcpwm_ll_timer_get_sync_phase_value(mcpwm_dev_t *mcpwm, int timer_id) +static inline void mcpwm_ll_timer_set_sync_phase_direction(mcpwm_dev_t *mcpwm, int timer_id, mcpwm_timer_direction_t direction) { - return mcpwm->timer[timer_id].sync.timer_phase; + mcpwm->timer[timer_id].sync.phase_direct = direction; } -static inline void mcpwm_ll_timer_set_sync_phase_direction(mcpwm_dev_t *mcpwm, int timer_id, bool decrease) +static inline void mcpwm_ll_timer_set_gpio_synchro(mcpwm_dev_t *mcpwm, int timer, int gpio_sync_id) { - mcpwm->timer[timer_id].sync.phase_direct = decrease; + mcpwm->timer_synci_cfg.val &= ~(0x07 << (timer * 3)); + mcpwm->timer_synci_cfg.val |= (gpio_sync_id + 4) << (timer * 3); } -static inline void mcpwm_ll_timer_enable_sync_from_internal_timer(mcpwm_dev_t *mcpwm, int this_timer, int internal_sync_timer) +static inline void mcpwm_ll_timer_set_timer_synchro(mcpwm_dev_t *mcpwm, int timer, int timer_sync_id) { - mcpwm->timer_synci_cfg.val &= ~(0x07 << (this_timer * 3)); - mcpwm->timer_synci_cfg.val |= (internal_sync_timer + 1) << (this_timer * 3); + mcpwm->timer_synci_cfg.val &= ~(0x07 << (timer * 3)); + mcpwm->timer_synci_cfg.val |= (timer_sync_id + 1) << (timer * 3); } -static inline void mcpwm_ll_timer_enable_sync_from_external(mcpwm_dev_t *mcpwm, int this_timer, int extern_syncer) +static inline void mcpwm_ll_timer_set_soft_synchro(mcpwm_dev_t *mcpwm, int timer) { - mcpwm->timer_synci_cfg.val &= ~(0x07 << (this_timer * 3)); - mcpwm->timer_synci_cfg.val |= (extern_syncer + 4) << (this_timer * 3); + // no sync input is selected, but software sync can still work + mcpwm->timer_synci_cfg.val &= ~(0x07 << (timer * 3)); } -static inline void mcpwm_ll_invert_external_syncer(mcpwm_dev_t *mcpwm, int sync_id, bool invert) +static inline void mcpwm_ll_invert_gpio_synchro(mcpwm_dev_t *mcpwm, int sync_id, bool invert) { if (invert) { mcpwm->timer_synci_cfg.val |= 1 << (sync_id + 9); @@ -524,6 +540,19 @@ static inline void mcpwm_ll_operator_enable_update_action_on_sync(mcpwm_dev_t *m } } +static inline void mcpwm_ll_operator_set_trigger_gpio_fault(mcpwm_dev_t *mcpwm, int operator_id, int trig_id, int fault_id) +{ + mcpwm->channel[operator_id].gen_cfg0.val &= ~(0x07 << (4 + 3 * trig_id)); + mcpwm->channel[operator_id].gen_cfg0.val |= (fault_id << (4 + 3 * trig_id)); +} + +static inline void mcpwm_ll_operator_set_trigger_timer_sync(mcpwm_dev_t *mcpwm, int operator_id, int trig_id) +{ + // the timer here is not selectable, must be the one connected with the operator + mcpwm->channel[operator_id].gen_cfg0.val &= ~(0x07 << (4 + 3 * trig_id)); + mcpwm->channel[operator_id].gen_cfg0.val |= (3 << (4 + 3 * trig_id)); +} + /********************* Generator registers *******************/ static inline void mcpwm_ll_generator_reset_actions(mcpwm_dev_t *mcpwm, int operator_id, int generator_id) @@ -567,31 +596,58 @@ static inline void mcpwm_ll_generator_set_action_on_trigger_event(mcpwm_dev_t *m } } -static inline void mcpwm_ll_gen_set_onetime_force_action(mcpwm_dev_t *mcpwm, int operator_id, int generator_id, int action) +static inline void mcpwm_ll_gen_trigger_noncontinue_force_action(mcpwm_dev_t *mcpwm, int operator_id, int generator_id) { if (generator_id == 0) { - mcpwm->channel[operator_id].gen_force.a_nciforce_mode = action; mcpwm->channel[operator_id].gen_force.a_nciforce = ~mcpwm->channel[operator_id].gen_force.a_nciforce; } else { - mcpwm->channel[operator_id].gen_force.b_nciforce_mode = action; mcpwm->channel[operator_id].gen_force.b_nciforce = ~mcpwm->channel[operator_id].gen_force.b_nciforce; } } -static inline void mcpwm_ll_gen_set_continuous_force_action(mcpwm_dev_t *mcpwm, int operator_id, int generator_id, int action) +static inline void mcpwm_ll_gen_disable_continue_force_action(mcpwm_dev_t *mcpwm, int operator_id, int generator_id) { - mcpwm->channel[operator_id].gen_force.cntu_force_upmethod = 0; // force action immediately + mcpwm->channel[operator_id].gen_force.cntu_force_upmethod = 0; // update force method immediately if (generator_id == 0) { - mcpwm->channel[operator_id].gen_force.a_cntuforce_mode = action; + mcpwm->channel[operator_id].gen_force.a_cntuforce_mode = 0; } else { - mcpwm->channel[operator_id].gen_force.b_cntuforce_mode = action; + mcpwm->channel[operator_id].gen_force.b_cntuforce_mode = 0; + } +} + +static inline void mcpwm_ll_gen_disable_noncontinue_force_action(mcpwm_dev_t *mcpwm, int operator_id, int generator_id) +{ + if (generator_id == 0) { + mcpwm->channel[operator_id].gen_force.a_nciforce_mode = 0; + } else { + mcpwm->channel[operator_id].gen_force.b_nciforce_mode = 0; + } +} + +static inline void mcpwm_ll_gen_set_continue_force_level(mcpwm_dev_t *mcpwm, int operator_id, int generator_id, int level) +{ + mcpwm->channel[operator_id].gen_force.cntu_force_upmethod = 0; // update force method immediately + if (generator_id == 0) { + mcpwm->channel[operator_id].gen_force.a_cntuforce_mode = level + 1; + } else { + mcpwm->channel[operator_id].gen_force.b_cntuforce_mode = level + 1; + } +} + +static inline void mcpwm_ll_gen_set_noncontinue_force_level(mcpwm_dev_t *mcpwm, int operator_id, int generator_id, int level) +{ + if (generator_id == 0) { + mcpwm->channel[operator_id].gen_force.a_nciforce_mode = level + 1; + } else { + mcpwm->channel[operator_id].gen_force.b_nciforce_mode = level + 1; } } /********************* Dead time registers *******************/ -static inline void mcpwm_ll_deadtime_set_resolution_same_to_timer(mcpwm_dev_t *mcpwm, int operator_id, bool same) +static inline void mcpwm_ll_deadtime_resolution_to_timer(mcpwm_dev_t *mcpwm, int operator_id, bool same) { + // whether to make the resolution of dead time delay module the same to the timer connected with operator mcpwm->channel[operator_id].db_cfg.clk_sel = same; } @@ -637,7 +693,7 @@ static inline void mcpwm_ll_deadtime_enable_deb(mcpwm_dev_t *mcpwm, int operator mcpwm->channel[operator_id].db_cfg.deb_mode = enable; } -static inline uint32_t mcpwm_ll_deadtime_get_topology_code(mcpwm_dev_t *mcpwm, int operator_id) +static inline uint32_t mcpwm_ll_deadtime_get_switch_topology(mcpwm_dev_t *mcpwm, int operator_id) { return (mcpwm->channel[operator_id].db_cfg.deb_mode << 8) | (mcpwm->channel[operator_id].db_cfg.b_outswap << 7) | (mcpwm->channel[operator_id].db_cfg.a_outswap << 6) | (mcpwm->channel[operator_id].db_cfg.fed_insel << 5) | @@ -781,25 +837,32 @@ static inline void mcpwm_ll_fault_clear_ost(mcpwm_dev_t *mcpwm, int operator_id) static inline void mcpwm_ll_fault_enable_oneshot_mode(mcpwm_dev_t *mcpwm, int operator_id, int fault_sig, bool enable) { - if (fault_sig == 0) { - mcpwm->channel[operator_id].tz_cfg0.f0_ost = enable; - } else if (fault_sig == 1) { - mcpwm->channel[operator_id].tz_cfg0.f1_ost = enable; - } else { - mcpwm->channel[operator_id].tz_cfg0.f2_ost = enable; - } + mcpwm->channel[operator_id].tz_cfg0.val &= ~(1 << (7 - fault_sig)); + mcpwm->channel[operator_id].tz_cfg0.val |= (enable << (7 - fault_sig)); } static inline void mcpwm_ll_fault_enable_cbc_mode(mcpwm_dev_t *mcpwm, int operator_id, int fault_sig, bool enable) { - if (fault_sig == 0) { - mcpwm->channel[operator_id].tz_cfg0.f0_cbc = enable; - } else if (fault_sig == 1) { - mcpwm->channel[operator_id].tz_cfg0.f1_cbc = enable; + mcpwm->channel[operator_id].tz_cfg0.val &= ~(1 << (3 - fault_sig)); + mcpwm->channel[operator_id].tz_cfg0.val |= (enable << (3 - fault_sig)); +} + +static inline void mcpwm_ll_fault_enable_cbc_refresh_on_tez(mcpwm_dev_t *mcpwm, int operator_id, bool enable) +{ + if (enable) { + mcpwm->channel[operator_id].tz_cfg1.val |= 1 << 1; } else { - mcpwm->channel[operator_id].tz_cfg0.f2_cbc = enable; + mcpwm->channel[operator_id].tz_cfg1.val &= ~(1 << 1); + } +} + +static inline void mcpwm_ll_fault_enable_cbc_refresh_on_tep(mcpwm_dev_t *mcpwm, int operator_id, bool enable) +{ + if (enable) { + mcpwm->channel[operator_id].tz_cfg1.val |= 1 << 2; + } else { + mcpwm->channel[operator_id].tz_cfg1.val &= ~(1 << 2); } - mcpwm->channel[operator_id].tz_cfg1.cbcpulse = 1 << 0; } static inline void mcpwm_ll_fault_enable_sw_cbc(mcpwm_dev_t *mcpwm, int operator_id, bool enable) @@ -817,20 +880,20 @@ static inline void mcpwm_ll_fault_trigger_sw_cbc(mcpwm_dev_t *mcpwm, int operato mcpwm->channel[operator_id].tz_cfg1.force_cbc = ~mcpwm->channel[operator_id].tz_cfg1.force_cbc; } -static inline void mcpwm_ll_fault_trigger_sw_oneshot(mcpwm_dev_t *mcpwm, int operator_id) +static inline void mcpwm_ll_fault_trigger_sw_ost(mcpwm_dev_t *mcpwm, int operator_id) { mcpwm->channel[operator_id].tz_cfg1.force_ost = ~mcpwm->channel[operator_id].tz_cfg1.force_ost; } -static inline void mcpwm_ll_generator_set_action_on_fault_event(mcpwm_dev_t *mcpwm, int operator_id, int generator_id, - mcpwm_timer_direction_t direction, mcpwm_fault_reaction_t reaction, int action) +static inline void mcpwm_ll_generator_set_action_on_trip_event(mcpwm_dev_t *mcpwm, int operator_id, int generator_id, + mcpwm_timer_direction_t direction, mcpwm_trip_type_t trip, int action) { if (direction == MCPWM_TIMER_DIRECTION_UP) { - mcpwm->channel[operator_id].tz_cfg0.val &= ~(0x03 << (8 + 8 * generator_id + 4 * reaction + 2)); - mcpwm->channel[operator_id].tz_cfg0.val |= action << (8 + 8 * generator_id + 4 * reaction + 2); + mcpwm->channel[operator_id].tz_cfg0.val &= ~(0x03 << (8 + 8 * generator_id + 4 * trip + 2)); + mcpwm->channel[operator_id].tz_cfg0.val |= action << (8 + 8 * generator_id + 4 * trip + 2); } else if (direction == MCPWM_TIMER_DIRECTION_DOWN) { - mcpwm->channel[operator_id].tz_cfg0.val &= ~(0x03 << (8 + 8 * generator_id + 4 * reaction)); - mcpwm->channel[operator_id].tz_cfg0.val |= action << (8 + 8 * generator_id + 4 * reaction); + mcpwm->channel[operator_id].tz_cfg0.val &= ~(0x03 << (8 + 8 * generator_id + 4 * trip)); + mcpwm->channel[operator_id].tz_cfg0.val |= action << (8 + 8 * generator_id + 4 * trip); } } @@ -856,12 +919,12 @@ static inline void mcpwm_ll_capture_enable_channel(mcpwm_dev_t *mcpwm, int chann mcpwm->cap_cfg_ch[channel].en = enable; } -static inline void mcpwm_ll_capture_set_sync_phase(mcpwm_dev_t *mcpwm, uint32_t phase_value) +static inline void mcpwm_ll_capture_set_sync_phase_value(mcpwm_dev_t *mcpwm, uint32_t phase_value) { mcpwm->cap_timer_phase = phase_value; } -static inline uint32_t mcpwm_ll_capture_get_sync_phase(mcpwm_dev_t *mcpwm) +static inline uint32_t mcpwm_ll_capture_get_sync_phase_value(mcpwm_dev_t *mcpwm) { return mcpwm->cap_timer_phase; } @@ -871,14 +934,14 @@ static inline void mcpwm_ll_capture_enable_timer_sync(mcpwm_dev_t *mcpwm, bool e mcpwm->cap_timer_cfg.synci_en = enable; } -static inline void mcpwm_ll_capture_set_internal_timer_syncer(mcpwm_dev_t *mcpwm, int sync_out_timer) +static inline void mcpwm_ll_capture_set_internal_timer_synchro(mcpwm_dev_t *mcpwm, int sync_out_timer) { mcpwm->cap_timer_cfg.synci_sel = sync_out_timer + 1; } -static inline void mcpwm_ll_capture_set_external_syncer(mcpwm_dev_t *mcpwm, int extern_syncer) +static inline void mcpwm_ll_capture_set_external_synchro(mcpwm_dev_t *mcpwm, int extern_synchro) { - mcpwm->cap_timer_cfg.synci_sel = extern_syncer + 4; + mcpwm->cap_timer_cfg.synci_sel = extern_synchro + 4; } static inline void mcpwm_ll_capture_trigger_sw_sync(mcpwm_dev_t *mcpwm) diff --git a/components/hal/esp32s3/include/hal/mcpwm_ll.h b/components/hal/esp32s3/include/hal/mcpwm_ll.h index 132f7a5968..2a8f9a7f74 100644 --- a/components/hal/esp32s3/include/hal/mcpwm_ll.h +++ b/components/hal/esp32s3/include/hal/mcpwm_ll.h @@ -26,6 +26,7 @@ #include "soc/soc_caps.h" #include "soc/mcpwm_struct.h" #include "hal/mcpwm_types.h" +#include "hal/assert.h" #ifdef __cplusplus extern "C" { @@ -33,24 +34,28 @@ extern "C" { /// Get the address of peripheral registers #define MCPWM_LL_GET_HW(ID) (((ID) == 0) ? &MCPWM0 : &MCPWM1) -#define MCPWM_LL_MAX_PRESCALE 255 +#define MCPWM_LL_MAX_CAPTURE_PRESCALE 255 +#define MCPWM_LL_MAX_COMPARE_VALUE 65535 +#define MCPWM_LL_MAX_DEAD_DELAY 65535 +#define MCPWM_LL_MAX_PHASE_VALUE 65535 /********************* Group registers *******************/ // Set/Get group clock: PWM_clk = CLK_160M / (prescale + 1) -static inline void mcpwm_ll_group_set_clock(mcpwm_dev_t *mcpwm, unsigned long long group_clk_hz) +static inline void mcpwm_ll_group_set_clock_prescale(mcpwm_dev_t *mcpwm, int pre_scale) { - mcpwm->clk_cfg.prescale = (SOC_MCPWM_BASE_CLK_HZ / group_clk_hz) - 1; + mcpwm->clk_cfg.prescale = pre_scale - 1; } -static inline unsigned long long mcpwm_ll_group_get_clock(mcpwm_dev_t *mcpwm) +static inline uint32_t mcpwm_ll_group_get_clock_prescale(mcpwm_dev_t *mcpwm) { - return SOC_MCPWM_BASE_CLK_HZ / (mcpwm->clk_cfg.prescale + 1); + return mcpwm->clk_cfg.prescale + 1; } static inline void mcpwm_ll_group_enable_shadow_mode(mcpwm_dev_t *mcpwm) { mcpwm->update_cfg.global_up_en = 1; + // updating of active registers in MCPWM operators should be enabled mcpwm->update_cfg.op0_up_en = 1; mcpwm->update_cfg.op1_up_en = 1; mcpwm->update_cfg.op2_up_en = 1; @@ -58,7 +63,8 @@ static inline void mcpwm_ll_group_enable_shadow_mode(mcpwm_dev_t *mcpwm) static inline void mcpwm_ll_group_flush_shadow(mcpwm_dev_t *mcpwm) { - mcpwm->update_cfg.val ^= (1 << 1); + // a toggle can trigger a forced update of all active registers in MCPWM, i.e. shadow->active + mcpwm->update_cfg.global_force_up = ~mcpwm->update_cfg.global_force_up; } /********************* Interrupt registers *******************/ @@ -129,47 +135,47 @@ static inline uint32_t mcpwm_ll_intr_get_capture_status(mcpwm_dev_t *mcpwm) static inline void mcpwm_ll_intr_clear_timer_stop_status(mcpwm_dev_t *mcpwm, uint32_t timer_mask) { - mcpwm->int_clr.val |= (timer_mask & 0x07) << 0; + mcpwm->int_clr.val = (timer_mask & 0x07) << 0; } static inline void mcpwm_ll_intr_clear_timer_tez_status(mcpwm_dev_t *mcpwm, uint32_t timer_mask) { - mcpwm->int_clr.val |= (timer_mask & 0x07) << 3; + mcpwm->int_clr.val = (timer_mask & 0x07) << 3; } static inline void mcpwm_ll_intr_clear_timer_tep_status(mcpwm_dev_t *mcpwm, uint32_t timer_mask) { - mcpwm->int_clr.val |= (timer_mask & 0x07) << 6; + mcpwm->int_clr.val = (timer_mask & 0x07) << 6; } static inline void mcpwm_ll_intr_clear_fault_enter_status(mcpwm_dev_t *mcpwm, uint32_t fault_mask) { - mcpwm->int_clr.val |= (fault_mask & 0x07) << 9; + mcpwm->int_clr.val = (fault_mask & 0x07) << 9; } static inline void mcpwm_ll_intr_clear_fault_exit_status(mcpwm_dev_t *mcpwm, uint32_t fault_mask) { - mcpwm->int_clr.val |= (fault_mask & 0x07) << 12; + mcpwm->int_clr.val = (fault_mask & 0x07) << 12; } static inline void mcpwm_ll_intr_clear_compare_status(mcpwm_dev_t *mcpwm, uint32_t operator_mask, uint32_t cmp_id) { - mcpwm->int_clr.val |= (operator_mask & 0x07) << (15 + cmp_id * 3); + mcpwm->int_clr.val = (operator_mask & 0x07) << (15 + cmp_id * 3); } static inline void mcpwm_ll_intr_clear_trip_cbc_status(mcpwm_dev_t *mcpwm, uint32_t cbc_mask) { - mcpwm->int_clr.val |= (cbc_mask & 0x07) << 21; + mcpwm->int_clr.val = (cbc_mask & 0x07) << 21; } static inline void mcpwm_ll_intr_clear_trip_ost_status(mcpwm_dev_t *mcpwm, uint32_t ost_mask) { - mcpwm->int_clr.val |= (ost_mask & 0x07) << 24; + mcpwm->int_clr.val = (ost_mask & 0x07) << 24; } static inline void mcpwm_ll_intr_clear_capture_status(mcpwm_dev_t *mcpwm, uint32_t capture_mask) { - mcpwm->int_clr.val |= (capture_mask & 0x07) << 27; + mcpwm->int_clr.val = (capture_mask & 0x07) << 27; } //////////// enable interrupt for each event //////////////// @@ -201,13 +207,20 @@ static inline void mcpwm_ll_intr_enable_timer_tep(mcpwm_dev_t *mcpwm, uint32_t t } } -static inline void mcpwm_ll_intr_enable_fault(mcpwm_dev_t *mcpwm, uint32_t fault_id, bool enable) +static inline void mcpwm_ll_intr_enable_fault_enter(mcpwm_dev_t *mcpwm, uint32_t fault_id, bool enable) { if (enable) { mcpwm->int_ena.val |= 1 << (9 + fault_id); // enter fault interrupt - mcpwm->int_ena.val |= 1 << (12 + fault_id); // exit fault interrupt } else { mcpwm->int_ena.val &= ~(1 << (9 + fault_id)); + } +} + +static inline void mcpwm_ll_intr_enable_fault_exit(mcpwm_dev_t *mcpwm, uint32_t fault_id, bool enable) +{ + if (enable) { + mcpwm->int_ena.val |= 1 << (12 + fault_id); // exit fault interrupt + } else { mcpwm->int_ena.val &= ~(1 << (12 + fault_id)); } } @@ -221,13 +234,20 @@ static inline void mcpwm_ll_intr_enable_compare(mcpwm_dev_t *mcpwm, uint32_t ope } } -static inline void mcpwm_ll_intr_enable_trip(mcpwm_dev_t *mcpwm, uint32_t operator_id, bool enable) +static inline void mcpwm_ll_intr_enable_trip_cbc(mcpwm_dev_t *mcpwm, uint32_t operator_id, bool enable) { if (enable) { mcpwm->int_ena.val |= (1 << (21 + operator_id)); - mcpwm->int_ena.val |= (1 << (24 + operator_id)); } else { mcpwm->int_ena.val &= ~(1 << (21 + operator_id)); + } +} + +static inline void mcpwm_ll_intr_enable_trip_ost(mcpwm_dev_t *mcpwm, uint32_t operator_id, bool enable) +{ + if (enable) { + mcpwm->int_ena.val |= (1 << (24 + operator_id)); + } else { mcpwm->int_ena.val &= ~(1 << (24 + operator_id)); } } @@ -243,14 +263,14 @@ static inline void mcpwm_ll_intr_enable_capture(mcpwm_dev_t *mcpwm, uint32_t cap /********************* Timer registers *******************/ -static inline void mcpwm_ll_timer_set_clock(mcpwm_dev_t *mcpwm, int timer_id, unsigned long long group_clock, unsigned long long timer_clock) +static inline void mcpwm_ll_timer_set_clock_prescale(mcpwm_dev_t *mcpwm, int timer_id, uint32_t prescale) { - mcpwm->timer[timer_id].period.prescale = group_clock / timer_clock - 1; + mcpwm->timer[timer_id].period.prescale = prescale - 1; } -static inline unsigned long long mcpwm_ll_timer_get_clock(mcpwm_dev_t *mcpwm, int timer_id, unsigned long long group_clock) +static inline uint32_t mcpwm_ll_timer_get_clock_prescale(mcpwm_dev_t *mcpwm, int timer_id) { - return group_clock / (mcpwm->timer[timer_id].period.prescale + 1); + return mcpwm->timer[timer_id].period.prescale + 1; } static inline void mcpwm_ll_timer_set_peak(mcpwm_dev_t *mcpwm, int timer_id, uint32_t peak, bool symmetric) @@ -327,9 +347,9 @@ static inline mcpwm_timer_count_mode_t mcpwm_ll_timer_get_count_mode(mcpwm_dev_t } } -static inline void mcpwm_ll_timer_set_operate_command(mcpwm_dev_t *mcpwm, int timer_id, mcpwm_timer_operate_cmd_t mode) +static inline void mcpwm_ll_timer_set_execute_command(mcpwm_dev_t *mcpwm, int timer_id, mcpwm_timer_execute_cmd_t cmd) { - switch (mode) { + switch (cmd) { case MCPWM_TIMER_STOP_AT_ZERO: mcpwm->timer[timer_id].mode.start = 0; break; @@ -348,23 +368,23 @@ static inline void mcpwm_ll_timer_set_operate_command(mcpwm_dev_t *mcpwm, int ti } } -static inline void mcpwm_ll_timer_set_count_value(mcpwm_dev_t *mcpwm, int timer_id, uint32_t value) -{ - // we use software sync to set count value - int previous_phase = mcpwm->timer[timer_id].sync.timer_phase; - mcpwm->timer[timer_id].sync.timer_phase = value; - mcpwm->timer[timer_id].sync.sync_sw = ~mcpwm->timer[timer_id].sync.sync_sw; - mcpwm->timer[timer_id].sync.timer_phase = previous_phase; -} - static inline uint32_t mcpwm_ll_timer_get_count_value(mcpwm_dev_t *mcpwm, int timer_id) { - return mcpwm->timer[timer_id].status.value; + // status.value saves the "next count value", so need an extra round up here to get the current count value according to count mode + // timer is paused + if (mcpwm->timer[timer_id].mode.mode == 0) { + return mcpwm->timer[timer_id].status.value; + } + if (mcpwm->timer[timer_id].status.direction) { // down direction + return (mcpwm->timer[timer_id].status.value + 1) % (mcpwm->timer[timer_id].period.period + 1); + } + // up direction + return (mcpwm->timer[timer_id].status.value + mcpwm->timer[timer_id].period.period) % (mcpwm->timer[timer_id].period.period + 1); } -static inline bool mcpwm_ll_is_timer_decreasing(mcpwm_dev_t *mcpwm, int timer_id) +static inline mcpwm_timer_direction_t mcpwm_ll_timer_get_count_direction(mcpwm_dev_t *mcpwm, int timer_id) { - return mcpwm->timer[timer_id].status.direction; + return mcpwm->timer[timer_id].status.direction ? MCPWM_TIMER_DIRECTION_DOWN : MCPWM_TIMER_DIRECTION_UP; } static inline void mcpwm_ll_timer_enable_sync_input(mcpwm_dev_t *mcpwm, int timer_id, bool enable) @@ -372,8 +392,9 @@ static inline void mcpwm_ll_timer_enable_sync_input(mcpwm_dev_t *mcpwm, int time mcpwm->timer[timer_id].sync.in_en = enable; } -static inline void mcpwm_ll_timer_sync_out_same_in(mcpwm_dev_t *mcpwm, int timer_id) +static inline void mcpwm_ll_timer_sync_out_penetrate(mcpwm_dev_t *mcpwm, int timer_id) { + // sync_out is selected to sync_in mcpwm->timer[timer_id].sync.out_sel = 0; } @@ -383,47 +404,51 @@ static inline void mcpwm_ll_timer_sync_out_on_timer_event(mcpwm_dev_t *mcpwm, in mcpwm->timer[timer_id].sync.out_sel = 1; } else if (event == MCPWM_TIMER_EVENT_PEAK) { mcpwm->timer[timer_id].sync.out_sel = 2; + } else { + HAL_ASSERT(false); } } static inline void mcpwm_ll_timer_disable_sync_out(mcpwm_dev_t *mcpwm, int timer_id) { + // sync_out will always be zero mcpwm->timer[timer_id].sync.out_sel = 3; } -static inline void mcpwm_ll_timer_trigger_sw_sync(mcpwm_dev_t *mcpwm, int timer_id) +static inline void mcpwm_ll_timer_trigger_soft_sync(mcpwm_dev_t *mcpwm, int timer_id) { mcpwm->timer[timer_id].sync.sync_sw = ~mcpwm->timer[timer_id].sync.sync_sw; } -static inline void mcpwm_ll_timer_set_sync_phase_value(mcpwm_dev_t *mcpwm, int timer_id, uint32_t reload_val) +static inline void mcpwm_ll_timer_set_sync_phase_value(mcpwm_dev_t *mcpwm, int timer_id, uint32_t phase_value) { - mcpwm->timer[timer_id].sync.timer_phase = reload_val; + mcpwm->timer[timer_id].sync.timer_phase = phase_value; } -static inline uint32_t mcpwm_ll_timer_get_sync_phase_value(mcpwm_dev_t *mcpwm, int timer_id) +static inline void mcpwm_ll_timer_set_sync_phase_direction(mcpwm_dev_t *mcpwm, int timer_id, mcpwm_timer_direction_t direction) { - return mcpwm->timer[timer_id].sync.timer_phase; + mcpwm->timer[timer_id].sync.phase_direct = direction; } -static inline void mcpwm_ll_timer_set_sync_phase_direction(mcpwm_dev_t *mcpwm, int timer_id, bool decrease) +static inline void mcpwm_ll_timer_set_gpio_synchro(mcpwm_dev_t *mcpwm, int timer, int gpio_sync_id) { - mcpwm->timer[timer_id].sync.phase_direct = decrease; + mcpwm->timer_synci_cfg.val &= ~(0x07 << (timer * 3)); + mcpwm->timer_synci_cfg.val |= (gpio_sync_id + 4) << (timer * 3); } -static inline void mcpwm_ll_timer_enable_sync_from_internal_timer(mcpwm_dev_t *mcpwm, int this_timer, int internal_sync_timer) +static inline void mcpwm_ll_timer_set_timer_synchro(mcpwm_dev_t *mcpwm, int timer, int timer_sync_id) { - mcpwm->timer_synci_cfg.val &= ~(0x07 << (this_timer * 3)); - mcpwm->timer_synci_cfg.val |= (internal_sync_timer + 1) << (this_timer * 3); + mcpwm->timer_synci_cfg.val &= ~(0x07 << (timer * 3)); + mcpwm->timer_synci_cfg.val |= (timer_sync_id + 1) << (timer * 3); } -static inline void mcpwm_ll_timer_enable_sync_from_external(mcpwm_dev_t *mcpwm, int this_timer, int extern_syncer) +static inline void mcpwm_ll_timer_set_soft_synchro(mcpwm_dev_t *mcpwm, int timer) { - mcpwm->timer_synci_cfg.val &= ~(0x07 << (this_timer * 3)); - mcpwm->timer_synci_cfg.val |= (extern_syncer + 4) << (this_timer * 3); + // no sync input is selected, but software sync can still work + mcpwm->timer_synci_cfg.val &= ~(0x07 << (timer * 3)); } -static inline void mcpwm_ll_invert_external_syncer(mcpwm_dev_t *mcpwm, int sync_id, bool invert) +static inline void mcpwm_ll_invert_gpio_synchro(mcpwm_dev_t *mcpwm, int sync_id, bool invert) { if (invert) { mcpwm->timer_synci_cfg.val |= 1 << (sync_id + 9); @@ -524,6 +549,19 @@ static inline void mcpwm_ll_operator_enable_update_action_on_sync(mcpwm_dev_t *m } } +static inline void mcpwm_ll_operator_set_trigger_gpio_fault(mcpwm_dev_t *mcpwm, int operator_id, int trig_id, int fault_id) +{ + mcpwm->channel[operator_id].gen_cfg0.val &= ~(0x07 << (4 + 3 * trig_id)); + mcpwm->channel[operator_id].gen_cfg0.val |= (fault_id << (4 + 3 * trig_id)); +} + +static inline void mcpwm_ll_operator_set_trigger_timer_sync(mcpwm_dev_t *mcpwm, int operator_id, int trig_id) +{ + // the timer here is not selectable, must be the one connected with the operator + mcpwm->channel[operator_id].gen_cfg0.val &= ~(0x07 << (4 + 3 * trig_id)); + mcpwm->channel[operator_id].gen_cfg0.val |= (3 << (4 + 3 * trig_id)); +} + /********************* Generator registers *******************/ static inline void mcpwm_ll_generator_reset_actions(mcpwm_dev_t *mcpwm, int operator_id, int generator_id) @@ -567,31 +605,58 @@ static inline void mcpwm_ll_generator_set_action_on_trigger_event(mcpwm_dev_t *m } } -static inline void mcpwm_ll_gen_set_onetime_force_action(mcpwm_dev_t *mcpwm, int operator_id, int generator_id, int action) +static inline void mcpwm_ll_gen_trigger_noncontinue_force_action(mcpwm_dev_t *mcpwm, int operator_id, int generator_id) { if (generator_id == 0) { - mcpwm->channel[operator_id].gen_force.a_nciforce_mode = action; mcpwm->channel[operator_id].gen_force.a_nciforce = ~mcpwm->channel[operator_id].gen_force.a_nciforce; } else { - mcpwm->channel[operator_id].gen_force.b_nciforce_mode = action; mcpwm->channel[operator_id].gen_force.b_nciforce = ~mcpwm->channel[operator_id].gen_force.b_nciforce; } } -static inline void mcpwm_ll_gen_set_continuous_force_action(mcpwm_dev_t *mcpwm, int operator_id, int generator_id, int action) +static inline void mcpwm_ll_gen_disable_continue_force_action(mcpwm_dev_t *mcpwm, int operator_id, int generator_id) { - mcpwm->channel[operator_id].gen_force.cntu_force_upmethod = 0; // force action immediately + mcpwm->channel[operator_id].gen_force.cntu_force_upmethod = 0; // update force method immediately if (generator_id == 0) { - mcpwm->channel[operator_id].gen_force.a_cntuforce_mode = action; + mcpwm->channel[operator_id].gen_force.a_cntuforce_mode = 0; } else { - mcpwm->channel[operator_id].gen_force.b_cntuforce_mode = action; + mcpwm->channel[operator_id].gen_force.b_cntuforce_mode = 0; + } +} + +static inline void mcpwm_ll_gen_disable_noncontinue_force_action(mcpwm_dev_t *mcpwm, int operator_id, int generator_id) +{ + if (generator_id == 0) { + mcpwm->channel[operator_id].gen_force.a_nciforce_mode = 0; + } else { + mcpwm->channel[operator_id].gen_force.b_nciforce_mode = 0; + } +} + +static inline void mcpwm_ll_gen_set_continue_force_level(mcpwm_dev_t *mcpwm, int operator_id, int generator_id, int level) +{ + mcpwm->channel[operator_id].gen_force.cntu_force_upmethod = 0; // update force method immediately + if (generator_id == 0) { + mcpwm->channel[operator_id].gen_force.a_cntuforce_mode = level + 1; + } else { + mcpwm->channel[operator_id].gen_force.b_cntuforce_mode = level + 1; + } +} + +static inline void mcpwm_ll_gen_set_noncontinue_force_level(mcpwm_dev_t *mcpwm, int operator_id, int generator_id, int level) +{ + if (generator_id == 0) { + mcpwm->channel[operator_id].gen_force.a_nciforce_mode = level + 1; + } else { + mcpwm->channel[operator_id].gen_force.b_nciforce_mode = level + 1; } } /********************* Dead time registers *******************/ -static inline void mcpwm_ll_deadtime_set_resolution_same_to_timer(mcpwm_dev_t *mcpwm, int operator_id, bool same) +static inline void mcpwm_ll_deadtime_resolution_to_timer(mcpwm_dev_t *mcpwm, int operator_id, bool same) { + // whether to make the resolution of dead time delay module the same to the timer connected with operator mcpwm->channel[operator_id].db_cfg.clk_sel = same; } @@ -637,7 +702,7 @@ static inline void mcpwm_ll_deadtime_enable_deb(mcpwm_dev_t *mcpwm, int operator mcpwm->channel[operator_id].db_cfg.deb_mode = enable; } -static inline uint32_t mcpwm_ll_deadtime_get_topology_code(mcpwm_dev_t *mcpwm, int operator_id) +static inline uint32_t mcpwm_ll_deadtime_get_switch_topology(mcpwm_dev_t *mcpwm, int operator_id) { return (mcpwm->channel[operator_id].db_cfg.deb_mode << 8) | (mcpwm->channel[operator_id].db_cfg.b_outswap << 7) | (mcpwm->channel[operator_id].db_cfg.a_outswap << 6) | (mcpwm->channel[operator_id].db_cfg.fed_insel << 5) | @@ -781,25 +846,32 @@ static inline void mcpwm_ll_fault_clear_ost(mcpwm_dev_t *mcpwm, int operator_id) static inline void mcpwm_ll_fault_enable_oneshot_mode(mcpwm_dev_t *mcpwm, int operator_id, int fault_sig, bool enable) { - if (fault_sig == 0) { - mcpwm->channel[operator_id].tz_cfg0.f0_ost = enable; - } else if (fault_sig == 1) { - mcpwm->channel[operator_id].tz_cfg0.f1_ost = enable; - } else { - mcpwm->channel[operator_id].tz_cfg0.f2_ost = enable; - } + mcpwm->channel[operator_id].tz_cfg0.val &= ~(1 << (7 - fault_sig)); + mcpwm->channel[operator_id].tz_cfg0.val |= (enable << (7 - fault_sig)); } static inline void mcpwm_ll_fault_enable_cbc_mode(mcpwm_dev_t *mcpwm, int operator_id, int fault_sig, bool enable) { - if (fault_sig == 0) { - mcpwm->channel[operator_id].tz_cfg0.f0_cbc = enable; - } else if (fault_sig == 1) { - mcpwm->channel[operator_id].tz_cfg0.f1_cbc = enable; + mcpwm->channel[operator_id].tz_cfg0.val &= ~(1 << (3 - fault_sig)); + mcpwm->channel[operator_id].tz_cfg0.val |= (enable << (3 - fault_sig)); +} + +static inline void mcpwm_ll_fault_enable_cbc_refresh_on_tez(mcpwm_dev_t *mcpwm, int operator_id, bool enable) +{ + if (enable) { + mcpwm->channel[operator_id].tz_cfg1.val |= 1 << 1; } else { - mcpwm->channel[operator_id].tz_cfg0.f2_cbc = enable; + mcpwm->channel[operator_id].tz_cfg1.val &= ~(1 << 1); + } +} + +static inline void mcpwm_ll_fault_enable_cbc_refresh_on_tep(mcpwm_dev_t *mcpwm, int operator_id, bool enable) +{ + if (enable) { + mcpwm->channel[operator_id].tz_cfg1.val |= 1 << 2; + } else { + mcpwm->channel[operator_id].tz_cfg1.val &= ~(1 << 2); } - mcpwm->channel[operator_id].tz_cfg1.cbcpulse = 1 << 0; } static inline void mcpwm_ll_fault_enable_sw_cbc(mcpwm_dev_t *mcpwm, int operator_id, bool enable) @@ -817,20 +889,20 @@ static inline void mcpwm_ll_fault_trigger_sw_cbc(mcpwm_dev_t *mcpwm, int operato mcpwm->channel[operator_id].tz_cfg1.force_cbc = ~mcpwm->channel[operator_id].tz_cfg1.force_cbc; } -static inline void mcpwm_ll_fault_trigger_sw_oneshot(mcpwm_dev_t *mcpwm, int operator_id) +static inline void mcpwm_ll_fault_trigger_sw_ost(mcpwm_dev_t *mcpwm, int operator_id) { mcpwm->channel[operator_id].tz_cfg1.force_ost = ~mcpwm->channel[operator_id].tz_cfg1.force_ost; } -static inline void mcpwm_ll_generator_set_action_on_fault_event(mcpwm_dev_t *mcpwm, int operator_id, int generator_id, - mcpwm_timer_direction_t direction, mcpwm_fault_reaction_t reaction, int action) +static inline void mcpwm_ll_generator_set_action_on_trip_event(mcpwm_dev_t *mcpwm, int operator_id, int generator_id, + mcpwm_timer_direction_t direction, mcpwm_trip_type_t trip, int action) { if (direction == MCPWM_TIMER_DIRECTION_UP) { - mcpwm->channel[operator_id].tz_cfg0.val &= ~(0x03 << (8 + 8 * generator_id + 4 * reaction + 2)); - mcpwm->channel[operator_id].tz_cfg0.val |= action << (8 + 8 * generator_id + 4 * reaction + 2); + mcpwm->channel[operator_id].tz_cfg0.val &= ~(0x03 << (8 + 8 * generator_id + 4 * trip + 2)); + mcpwm->channel[operator_id].tz_cfg0.val |= action << (8 + 8 * generator_id + 4 * trip + 2); } else if (direction == MCPWM_TIMER_DIRECTION_DOWN) { - mcpwm->channel[operator_id].tz_cfg0.val &= ~(0x03 << (8 + 8 * generator_id + 4 * reaction)); - mcpwm->channel[operator_id].tz_cfg0.val |= action << (8 + 8 * generator_id + 4 * reaction); + mcpwm->channel[operator_id].tz_cfg0.val &= ~(0x03 << (8 + 8 * generator_id + 4 * trip)); + mcpwm->channel[operator_id].tz_cfg0.val |= action << (8 + 8 * generator_id + 4 * trip); } } @@ -856,12 +928,12 @@ static inline void mcpwm_ll_capture_enable_channel(mcpwm_dev_t *mcpwm, int chann mcpwm->cap_cfg_ch[channel].en = enable; } -static inline void mcpwm_ll_capture_set_sync_phase(mcpwm_dev_t *mcpwm, uint32_t phase_value) +static inline void mcpwm_ll_capture_set_sync_phase_value(mcpwm_dev_t *mcpwm, uint32_t phase_value) { mcpwm->cap_timer_phase = phase_value; } -static inline uint32_t mcpwm_ll_capture_get_sync_phase(mcpwm_dev_t *mcpwm) +static inline uint32_t mcpwm_ll_capture_get_sync_phase_value(mcpwm_dev_t *mcpwm) { return mcpwm->cap_timer_phase; } @@ -871,14 +943,14 @@ static inline void mcpwm_ll_capture_enable_timer_sync(mcpwm_dev_t *mcpwm, bool e mcpwm->cap_timer_cfg.synci_en = enable; } -static inline void mcpwm_ll_capture_set_internal_timer_syncer(mcpwm_dev_t *mcpwm, int sync_out_timer) +static inline void mcpwm_ll_capture_set_internal_timer_synchro(mcpwm_dev_t *mcpwm, int sync_out_timer) { mcpwm->cap_timer_cfg.synci_sel = sync_out_timer + 1; } -static inline void mcpwm_ll_capture_set_external_syncer(mcpwm_dev_t *mcpwm, int extern_syncer) +static inline void mcpwm_ll_capture_set_external_synchro(mcpwm_dev_t *mcpwm, int extern_synchro) { - mcpwm->cap_timer_cfg.synci_sel = extern_syncer + 4; + mcpwm->cap_timer_cfg.synci_sel = extern_synchro + 4; } static inline void mcpwm_ll_capture_trigger_sw_sync(mcpwm_dev_t *mcpwm) diff --git a/components/hal/include/hal/mcpwm_types.h b/components/hal/include/hal/mcpwm_types.h index 77f9c80c12..b9c37315bc 100644 --- a/components/hal/include/hal/mcpwm_types.h +++ b/components/hal/include/hal/mcpwm_types.h @@ -37,7 +37,7 @@ typedef enum { MCPWM_TIMER_START_NO_STOP, /*!< MCPWM timer starts couting */ MCPWM_TIMER_START_STOP_AT_ZERO, /*!< MCPWM timer starts counting and stops when couting to zero */ MCPWM_TIMER_START_STOP_AT_PEAK, /*!< MCPWM timer starts counting and stops when counting to peak */ -} mcpwm_timer_operate_cmd_t; +} mcpwm_timer_execute_cmd_t; typedef enum { MCPWM_GEN_ACTION_KEEP, /*!< Generator action: Keep the same level */ @@ -47,6 +47,6 @@ typedef enum { } mcpwm_generator_action_t; typedef enum { - MCPWM_FAULT_REACTION_CBC, /*!< Reaction on fault signal: recover cycle by cycle */ - MCPWM_FAULT_REACTION_OST, /*!< Reaction on fault signal: one shot trip */ -} mcpwm_fault_reaction_t; + MCPWM_TRIP_TYPE_CBC, /*!< CBC trip type, shut down the operator cycle by cycle*/ + MCPWM_TRIP_TYPE_OST, /*!< OST trip type, shut down the operator in one shot */ +} mcpwm_trip_type_t; diff --git a/components/soc/esp32/include/soc/gpio_sig_map.h b/components/soc/esp32/include/soc/gpio_sig_map.h index b2114d8583..b079c9515e 100644 --- a/components/soc/esp32/include/soc/gpio_sig_map.h +++ b/components/soc/esp32/include/soc/gpio_sig_map.h @@ -236,26 +236,8 @@ #define PWM1_CAP1_IN_IDX 113 #define PWM1_OUT2B_IDX 113 #define PWM1_CAP2_IN_IDX 114 -#define PWM2_OUT1H_IDX 114 -#define PWM2_FLTA_IDX 115 -#define PWM2_OUT1L_IDX 115 -#define PWM2_FLTB_IDX 116 -#define PWM2_OUT2H_IDX 116 -#define PWM2_CAP1_IN_IDX 117 -#define PWM2_OUT2L_IDX 117 -#define PWM2_CAP2_IN_IDX 118 -#define PWM2_OUT3H_IDX 118 -#define PWM2_CAP3_IN_IDX 119 -#define PWM2_OUT3L_IDX 119 -#define PWM3_FLTA_IDX 120 -#define PWM2_OUT4H_IDX 120 -#define PWM3_FLTB_IDX 121 -#define PWM2_OUT4L_IDX 121 -#define PWM3_CAP1_IN_IDX 122 -#define PWM3_CAP2_IN_IDX 123 #define TWAI_TX_IDX 123 #define CAN_TX_IDX TWAI_TX_IDX -#define PWM3_CAP3_IN_IDX 124 #define TWAI_BUS_OFF_ON_IDX 124 #define CAN_BUS_OFF_ON_IDX TWAI_BUS_OFF_ON_IDX #define TWAI_CLKOUT_IDX 125 @@ -369,19 +351,11 @@ #define I2S1O_DATA_OUT22_IDX 188 #define I2S1O_DATA_OUT23_IDX 189 #define I2S0I_H_SYNC_IDX 190 -#define PWM3_OUT1H_IDX 190 #define I2S0I_V_SYNC_IDX 191 -#define PWM3_OUT1L_IDX 191 #define I2S0I_H_ENABLE_IDX 192 -#define PWM3_OUT2H_IDX 192 #define I2S1I_H_SYNC_IDX 193 -#define PWM3_OUT2L_IDX 193 #define I2S1I_V_SYNC_IDX 194 -#define PWM3_OUT3H_IDX 194 #define I2S1I_H_ENABLE_IDX 195 -#define PWM3_OUT3L_IDX 195 -#define PWM3_OUT4H_IDX 196 -#define PWM3_OUT4L_IDX 197 #define U2RXD_IN_IDX 198 #define U2TXD_OUT_IDX 198 #define U2CTS_IN_IDX 199 diff --git a/components/soc/esp32/include/soc/soc_caps.h b/components/soc/esp32/include/soc/soc_caps.h index 1ecedd6557..dd6f994b1a 100644 --- a/components/soc/esp32/include/soc/soc_caps.h +++ b/components/soc/esp32/include/soc/soc_caps.h @@ -158,10 +158,11 @@ #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_GENERATORS_PER_OPERATOR (2) ///< The number of generators that each operator has -#define SOC_MCPWM_FAULT_DETECTORS_PER_GROUP (3) ///< The number of fault signal detectors that each group 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 GPIO fault signals that each group has #define SOC_MCPWM_CAPTURE_TIMERS_PER_GROUP (1) ///< The number of capture timers that each group has #define SOC_MCPWM_CAPTURE_CHANNELS_PER_TIMER (3) ///< The number of capture channels that each capture timer has -#define SOC_MCPWM_EXT_SYNCERS_PER_GROUP (3) ///< The number of external syncers that each group has +#define SOC_MCPWM_GPIO_SYNCHROS_PER_GROUP (3) ///< The number of GPIO synchros that each group has #define SOC_MCPWM_BASE_CLK_HZ (160000000ULL) ///< Base Clock frequency of 160MHz /*-------------------------- MPU CAPS ----------------------------------------*/ diff --git a/components/soc/esp32/mcpwm_periph.c b/components/soc/esp32/mcpwm_periph.c index 0a30191ff1..ae51d75c6a 100644 --- a/components/soc/esp32/mcpwm_periph.c +++ b/components/soc/esp32/mcpwm_periph.c @@ -53,7 +53,7 @@ const mcpwm_signal_conn_t mcpwm_periph_signals = { } } }, - .detectors = { + .gpio_faults = { [0] = { .fault_sig = PWM0_F0_IN_IDX }, @@ -75,7 +75,7 @@ const mcpwm_signal_conn_t mcpwm_periph_signals = { .cap_sig = PWM0_CAP2_IN_IDX } }, - .ext_syncers = { + .gpio_synchros = { [0] = { .sync_sig = PWM0_SYNC0_IN_IDX }, @@ -122,7 +122,7 @@ const mcpwm_signal_conn_t mcpwm_periph_signals = { } } }, - .detectors = { + .gpio_faults = { [0] = { .fault_sig = PWM1_F0_IN_IDX }, @@ -144,7 +144,7 @@ const mcpwm_signal_conn_t mcpwm_periph_signals = { .cap_sig = PWM1_CAP2_IN_IDX } }, - .ext_syncers = { + .gpio_synchros = { [0] = { .sync_sig = PWM1_SYNC0_IN_IDX }, diff --git a/components/soc/esp32s3/include/soc/soc_caps.h b/components/soc/esp32s3/include/soc/soc_caps.h index 9ebf9cd61d..6e9effe5f8 100644 --- a/components/soc/esp32s3/include/soc/soc_caps.h +++ b/components/soc/esp32s3/include/soc/soc_caps.h @@ -71,10 +71,11 @@ #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_GENERATORS_PER_OPERATOR (2) ///< The number of generators that each operator has -#define SOC_MCPWM_FAULT_DETECTORS_PER_GROUP (3) ///< The number of fault signal detectors that each group 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 #define SOC_MCPWM_CAPTURE_TIMERS_PER_GROUP (1) ///< The number of capture timers that each group has #define SOC_MCPWM_CAPTURE_CHANNELS_PER_TIMER (3) ///< The number of capture channels that each capture timer has -#define SOC_MCPWM_EXT_SYNCERS_PER_GROUP (3) ///< The number of external syncers that each group has +#define SOC_MCPWM_GPIO_SYNCHROS_PER_GROUP (3) ///< The number of GPIO synchros that each group has #define SOC_MCPWM_BASE_CLK_HZ (160000000ULL) ///< Base Clock frequency of 160MHz /*-------------------------- MPU CAPS ----------------------------------------*/ diff --git a/components/soc/esp32s3/mcpwm_periph.c b/components/soc/esp32s3/mcpwm_periph.c index 0a30191ff1..ae51d75c6a 100644 --- a/components/soc/esp32s3/mcpwm_periph.c +++ b/components/soc/esp32s3/mcpwm_periph.c @@ -53,7 +53,7 @@ const mcpwm_signal_conn_t mcpwm_periph_signals = { } } }, - .detectors = { + .gpio_faults = { [0] = { .fault_sig = PWM0_F0_IN_IDX }, @@ -75,7 +75,7 @@ const mcpwm_signal_conn_t mcpwm_periph_signals = { .cap_sig = PWM0_CAP2_IN_IDX } }, - .ext_syncers = { + .gpio_synchros = { [0] = { .sync_sig = PWM0_SYNC0_IN_IDX }, @@ -122,7 +122,7 @@ const mcpwm_signal_conn_t mcpwm_periph_signals = { } } }, - .detectors = { + .gpio_faults = { [0] = { .fault_sig = PWM1_F0_IN_IDX }, @@ -144,7 +144,7 @@ const mcpwm_signal_conn_t mcpwm_periph_signals = { .cap_sig = PWM1_CAP2_IN_IDX } }, - .ext_syncers = { + .gpio_synchros = { [0] = { .sync_sig = PWM1_SYNC0_IN_IDX }, diff --git a/components/soc/include/soc/mcpwm_periph.h b/components/soc/include/soc/mcpwm_periph.h index ae5809c91e..d807600c4a 100644 --- a/components/soc/include/soc/mcpwm_periph.h +++ b/components/soc/include/soc/mcpwm_periph.h @@ -34,13 +34,13 @@ typedef struct { } operators[SOC_MCPWM_OPERATORS_PER_GROUP]; struct { const uint32_t fault_sig; - } detectors[SOC_MCPWM_FAULT_DETECTORS_PER_GROUP]; + } gpio_faults[SOC_MCPWM_GPIO_FAULTS_PER_GROUP]; struct { const uint32_t cap_sig; } captures[SOC_MCPWM_CAPTURE_CHANNELS_PER_TIMER]; struct { const uint32_t sync_sig; - } ext_syncers[SOC_MCPWM_EXT_SYNCERS_PER_GROUP]; + } gpio_synchros[SOC_MCPWM_GPIO_SYNCHROS_PER_GROUP]; } groups[SOC_MCPWM_GROUPS]; } mcpwm_signal_conn_t; diff --git a/docs/_static/mcpwm-bldc-control.png b/docs/_static/mcpwm-bldc-control.png index e054033684..d4d83b691c 100644 Binary files a/docs/_static/mcpwm-bldc-control.png and b/docs/_static/mcpwm-bldc-control.png differ diff --git a/docs/en/api-reference/peripherals/mcpwm.rst b/docs/en/api-reference/peripherals/mcpwm.rst index f9bfbb4f9b..dfc1b6fcbd 100644 --- a/docs/en/api-reference/peripherals/mcpwm.rst +++ b/docs/en/api-reference/peripherals/mcpwm.rst @@ -53,7 +53,7 @@ In this case we will describe a simple configuration to control a brushed DC mot Configuration covers the following steps: -1. Selection of a MPWn unit that will be used to drive the motor. There are two units available on-board of {IDF_TARGET_NAME} and enumerated in :cpp:type:`mcpwm_unit_t`. +1. Selection of a MCPWM unit that will be used to drive the motor. There are two units available on-board of {IDF_TARGET_NAME} and enumerated in :cpp:type:`mcpwm_unit_t`. 2. Initialization of two GPIOs as output signals within selected unit by calling :cpp:func:`mcpwm_gpio_init`. The two output signals are typically used to command the motor to rotate right or left. All available signal options are listed in :cpp:type:`mcpwm_io_signals_t`. To set more than a single pin at a time, use function :cpp:func:`mcpwm_set_pin` together with :cpp:type:`mcpwm_pin_config_t`. 3. Selection of a timer. There are three timers available within the unit. The timers are listed in :cpp:type:`mcpwm_timer_t`. 4. Setting of the timer frequency and initial duty within :cpp:type:`mcpwm_config_t` structure. @@ -146,7 +146,7 @@ The MCPWM has a carrier submodule used if galvanic isolation from the motor driv To use the carrier submodule, it should be first initialized by calling :cpp:func:`mcpwm_carrier_init`. The carrier parameters are defined in :cpp:type:`mcpwm_carrier_config_t` structure invoked within the function call. Then the carrier functionality may be enabled by calling :cpp:func:`mcpwm_carrier_enable`. -The carrier parameters may be then alerted at a runtime by calling dedicated functions to change individual fields of the :cpp:type:`mcpwm_carrier_config_t` structure, like :cpp:func:`mcpwm_carrier_set_period`, :cpp:func:`mcpwm_carrier_set_duty_cycle`, :cpp:func:`mcpwm_carrier_output_invert`, etc. +The carrier parameters may be then altered at a runtime by calling dedicated functions to change individual fields of the :cpp:type:`mcpwm_carrier_config_t` structure, like :cpp:func:`mcpwm_carrier_set_period`, :cpp:func:`mcpwm_carrier_set_duty_cycle`, :cpp:func:`mcpwm_carrier_output_invert`, etc. This includes enabling and setting duration of the first pulse of the career with :cpp:func:`mcpwm_carrier_oneshot_mode_enable`. For more details, see *{IDF_TARGET_NAME} Technical Reference Manual* > *Motor Control PWM (MCPWM)* > *PWM Carrier Submodule* [`PDF <{IDF_TARGET_TRM_EN_URL}#mcpwm>`__]. diff --git a/examples/peripherals/mcpwm/mcpwm_bldc_control/README.md b/examples/peripherals/mcpwm/mcpwm_bldc_control/README.md index a5fe0ede27..a4e51e2229 100644 --- a/examples/peripherals/mcpwm/mcpwm_bldc_control/README.md +++ b/examples/peripherals/mcpwm/mcpwm_bldc_control/README.md @@ -1,10 +1,10 @@ -| Supported Targets | ESP32 | -| ----------------- | ----- | +| Supported Targets | ESP32 | ESP32-S3 | +| ----------------- | ----- | -------- | -# MCPWM BLDC motor control(hall sensor feedback) Example +# MCPWM BLDC motor control (hall sensor feedback) Example + +This example will show you how to use MCPWM module to control BLDC motor with hall sensor feedback. -This example will show you how to use MCPWM module to control bldc motor with hall sensor feedback - The following examples uses MCPWM module to control bldc motor and vary its speed continuously The bldc motor used for testing this code had hall sensor capture sequence of 6-->4-->5-->1-->3-->2-->6-->4--> and so on @@ -13,7 +13,7 @@ IR2136 3-ph bridge driver is used for testing this example code User needs to make changes according to the motor and gate driver ic used - + ## Step 1: Pin assignment * The gpio init function initializes: * GPIO15 is assigned as the MCPWM signal for 1H(UH) diff --git a/examples/peripherals/mcpwm/mcpwm_bldc_control/main/mcpwm_bldc_control_hall_sensor_example.c b/examples/peripherals/mcpwm/mcpwm_bldc_control/main/mcpwm_bldc_control_hall_sensor_example.c index b99f926fe2..7e9e0f27ba 100644 --- a/examples/peripherals/mcpwm/mcpwm_bldc_control/main/mcpwm_bldc_control_hall_sensor_example.c +++ b/examples/peripherals/mcpwm/mcpwm_bldc_control/main/mcpwm_bldc_control_hall_sensor_example.c @@ -215,7 +215,7 @@ static void mcpwm_example_bldc_control(void *arg) //2. initial mcpwm configuration printf("Configuring Initial Parameters of mcpwm bldc control...\n"); mcpwm_config_t pwm_config; - pwm_config.frequency = 1000; //frequency = 1000Hz + pwm_config.frequency = 14400; //frequency = 1000Hz pwm_config.cmpr_a = 50.0; //duty cycle of PWMxA = 50.0% pwm_config.cmpr_b = 50.0; //duty cycle of PWMxb = 50.0% pwm_config.counter_mode = MCPWM_UP_COUNTER; diff --git a/examples/peripherals/mcpwm/mcpwm_servo_control/README.md b/examples/peripherals/mcpwm/mcpwm_servo_control/README.md index 262539d6bb..671cb13bff 100644 --- a/examples/peripherals/mcpwm/mcpwm_servo_control/README.md +++ b/examples/peripherals/mcpwm/mcpwm_servo_control/README.md @@ -1,22 +1,59 @@ -| Supported Targets | ESP32 | -| ----------------- | ----- | +| Supported Targets | ESP32 | ESP32-S3 | +| ----------------- | ----- | -------- | +# MCPWM RC Servo Control Example -# MCPWM servo motor control Example +(See the README.md file in the upper level 'examples' directory for more information about examples.) -This example will show you how to use MCPWM module to control servo motor - -Assign pulse width range and the maximum degree, accordingly the servo will move from 0 to maximum degree continuously - +This example illustrates how to drive a typical [RC Servo](https://en.wikipedia.org/wiki/Servo_(radio_control)) by sending a PWM signal using the MCPWM driver. The PWM pulse has a frequency of 50Hz (period of 20ms), and the active-high time (which controls the rotation) ranges from 1ms to 2ms with 1.5ms always being center of range. -## Step 1: Pin assignment -* GPIO18 is assigned as the MCPWM signal for servo motor +## How to Use Example + +### Hardware Required + +* A development board with any Espressif SoC which features MCPWM peripheral (e.g., ESP32-DevKitC, ESP-WROVER-KIT, etc.) +* A USB cable for Power supply and programming +* A RC servo motor, e.g. [SG90](http://www.ee.ic.ac.uk/pcheung/teaching/DE1_EE/stores/sg90_datasheet.pdf) + +Connection : + +``` ++-------+ +-----------------+ +| | | | +| +-+ GPIO18++ PWM(Orange) +----------+ | +| ESP |---5V------+ Vcc(Red) +--------------| Servo Motor | +| +---------+ GND(Brown) +----------+ | +| | | | ++-------+ +-----------------+ +``` + +### Build and Flash + +Run `idf.py -p PORT flash monitor` to build, flash and monitor the project. + +(To exit the serial monitor, type ``Ctrl-]``.) + +See the [Getting Started Guide](https://docs.espressif.com/projects/esp-idf/en/latest/get-started/index.html) for full steps to configure and use ESP-IDF to build projects. -## Step 2: Connection -* connect GPIO18 with servo pwm signal -* other two wires of servo motor are VCC and GND +## Example Output +Run the example, you will see the following output log: -## Step 3: Initialize MCPWM -* You need to set the frequency(generally 50 Hz) and duty cycle of MCPWM timer -* You need to set the MCPWM channel you want to use, and bind the channel with one of the timers +``` +... +I (0) cpu_start: Starting scheduler on APP CPU. +I (349) example: Angle of rotation: -90 +I (449) example: Angle of rotation: -89 +I (549) example: Angle of rotation: -88 +I (649) example: Angle of rotation: -87 +I (749) example: Angle of rotation: -86 +... +``` + +The servo will rotate from -90 degree to 90 degree, and then turn back again. + +## Troubleshooting + +Note that, some kind of servo might need a higher current supply than the development board usually can provide. It's recommended to power the servo separately. + +For any technical queries, please open an [issue] (https://github.com/espressif/esp-idf/issues) on GitHub. We will get back to you soon. diff --git a/examples/peripherals/mcpwm/mcpwm_servo_control/main/CMakeLists.txt b/examples/peripherals/mcpwm/mcpwm_servo_control/main/CMakeLists.txt index fda5dd827e..010d6a1d47 100644 --- a/examples/peripherals/mcpwm/mcpwm_servo_control/main/CMakeLists.txt +++ b/examples/peripherals/mcpwm/mcpwm_servo_control/main/CMakeLists.txt @@ -1,2 +1,2 @@ -idf_component_register(SRCS "mcpwm_servo_control_example.c" +idf_component_register(SRCS "mcpwm_servo_control_example_main.c" INCLUDE_DIRS ".") diff --git a/examples/peripherals/mcpwm/mcpwm_servo_control/main/mcpwm_servo_control_example.c b/examples/peripherals/mcpwm/mcpwm_servo_control/main/mcpwm_servo_control_example.c deleted file mode 100644 index 88841f252f..0000000000 --- a/examples/peripherals/mcpwm/mcpwm_servo_control/main/mcpwm_servo_control_example.c +++ /dev/null @@ -1,77 +0,0 @@ -/* servo motor control example - - This example code is in the Public Domain (or CC0 licensed, at your option.) - - Unless required by applicable law or agreed to in writing, this - software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR - CONDITIONS OF ANY KIND, either express or implied. -*/ -#include - -#include "freertos/FreeRTOS.h" -#include "freertos/task.h" -#include "esp_attr.h" - -#include "driver/mcpwm.h" -#include "soc/mcpwm_periph.h" - -//You can get these value from the datasheet of servo you use, in general pulse width varies between 1000 to 2000 mocrosecond -#define SERVO_MIN_PULSEWIDTH 1000 //Minimum pulse width in microsecond -#define SERVO_MAX_PULSEWIDTH 2000 //Maximum pulse width in microsecond -#define SERVO_MAX_DEGREE 90 //Maximum angle in degree upto which servo can rotate - -static void mcpwm_example_gpio_initialize(void) -{ - printf("initializing mcpwm servo control gpio......\n"); - mcpwm_gpio_init(MCPWM_UNIT_0, MCPWM0A, 18); //Set GPIO 18 as PWM0A, to which servo is connected -} - -/** - * @brief Use this function to calcute pulse width for per degree rotation - * - * @param degree_of_rotation the angle in degree to which servo has to rotate - * - * @return - * - calculated pulse width - */ -static uint32_t servo_per_degree_init(uint32_t degree_of_rotation) -{ - uint32_t cal_pulsewidth = 0; - cal_pulsewidth = (SERVO_MIN_PULSEWIDTH + (((SERVO_MAX_PULSEWIDTH - SERVO_MIN_PULSEWIDTH) * (degree_of_rotation)) / (SERVO_MAX_DEGREE))); - return cal_pulsewidth; -} - -/** - * @brief Configure MCPWM module - */ -void mcpwm_example_servo_control(void *arg) -{ - uint32_t angle, count; - //1. mcpwm gpio initialization - mcpwm_example_gpio_initialize(); - - //2. initial mcpwm configuration - printf("Configuring Initial Parameters of mcpwm......\n"); - mcpwm_config_t pwm_config; - pwm_config.frequency = 50; //frequency = 50Hz, i.e. for every servo motor time period should be 20ms - pwm_config.cmpr_a = 0; //duty cycle of PWMxA = 0 - pwm_config.cmpr_b = 0; //duty cycle of PWMxb = 0 - pwm_config.counter_mode = MCPWM_UP_COUNTER; - pwm_config.duty_mode = MCPWM_DUTY_MODE_0; - mcpwm_init(MCPWM_UNIT_0, MCPWM_TIMER_0, &pwm_config); //Configure PWM0A & PWM0B with above settings - while (1) { - for (count = 0; count < SERVO_MAX_DEGREE; count++) { - printf("Angle of rotation: %d\n", count); - angle = servo_per_degree_init(count); - printf("pulse width: %dus\n", angle); - mcpwm_set_duty_in_us(MCPWM_UNIT_0, MCPWM_TIMER_0, MCPWM_OPR_A, angle); - vTaskDelay(10); //Add delay, since it takes time for servo to rotate, generally 100ms/60degree rotation at 5V - } - } -} - -void app_main(void) -{ - printf("Testing servo motor.......\n"); - xTaskCreate(mcpwm_example_servo_control, "mcpwm_example_servo_control", 4096, NULL, 5, NULL); -} diff --git a/examples/peripherals/mcpwm/mcpwm_servo_control/main/mcpwm_servo_control_example_main.c b/examples/peripherals/mcpwm/mcpwm_servo_control/main/mcpwm_servo_control_example_main.c new file mode 100644 index 0000000000..0c11a6db84 --- /dev/null +++ b/examples/peripherals/mcpwm/mcpwm_servo_control/main/mcpwm_servo_control_example_main.c @@ -0,0 +1,47 @@ +/* Servo Motor control example + + This example code is in the Public Domain (or CC0 licensed, at your option.) + + Unless required by applicable law or agreed to in writing, this + software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + CONDITIONS OF ANY KIND, either express or implied. +*/ +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "esp_log.h" +#include "driver/mcpwm.h" + +static const char *TAG = "example"; + +// You can get these value from the datasheet of servo you use, in general pulse width varies between 1000 to 2000 mocrosecond +#define SERVO_MIN_PULSEWIDTH_US (1000) // Minimum pulse width in microsecond +#define SERVO_MAX_PULSEWIDTH_US (2000) // Maximum pulse width in microsecond +#define SERVO_MAX_DEGREE (90) // Maximum angle in degree upto which servo can rotate + +#define SERVO_PULSE_GPIO (18) // GPIO connects to the PWM signal line + +static inline uint32_t example_convert_servo_angle_to_duty_us(int angle) +{ + return (angle + SERVO_MAX_DEGREE) * (SERVO_MAX_PULSEWIDTH_US - SERVO_MIN_PULSEWIDTH_US) / (2 * SERVO_MAX_DEGREE) + SERVO_MIN_PULSEWIDTH_US; +} + +void app_main(void) +{ + mcpwm_gpio_init(MCPWM_UNIT_0, MCPWM0A, SERVO_PULSE_GPIO); // To drive a RC servo, one MCPWM generator is enough + + mcpwm_config_t pwm_config = { + .frequency = 50, // frequency = 50Hz, i.e. for every servo motor time period should be 20ms + .cmpr_a = 0, // duty cycle of PWMxA = 0 + .counter_mode = MCPWM_UP_COUNTER, + .duty_mode = MCPWM_DUTY_MODE_0, + }; + mcpwm_init(MCPWM_UNIT_0, MCPWM_TIMER_0, &pwm_config); + + while (1) { + for (int angle = -SERVO_MAX_DEGREE; angle < SERVO_MAX_DEGREE; angle++) { + ESP_LOGI(TAG, "Angle of rotation: %d", angle); + ESP_ERROR_CHECK(mcpwm_set_duty_in_us(MCPWM_UNIT_0, MCPWM_TIMER_0, MCPWM_OPR_A, example_convert_servo_angle_to_duty_us(angle))); + vTaskDelay(pdMS_TO_TICKS(100)); //Add delay, since it takes time for servo to rotate, generally 100ms/60degree rotation under 5V power supply + } + } +}