diff --git a/components/driver/include/driver/mcpwm.h b/components/driver/include/driver/mcpwm.h index 3e34fac02c..74a7aede8e 100644 --- a/components/driver/include/driver/mcpwm.h +++ b/components/driver/include/driver/mcpwm.h @@ -277,6 +277,10 @@ esp_err_t mcpwm_set_pin(mcpwm_unit_t mcpwm_num, const mcpwm_pin_config_t *mcpwm_ /** * @brief Initialize MCPWM parameters + * @note + * The default resolution configured for MCPWM group and timer are 160M / 16 = 10M and 10M / 10 = 1M + * The default resolution can be changed by calling mcpwm_group_set_resolution() and mcpwm_timer_set_resolution(), + * before calling this function. * * @param mcpwm_num set MCPWM unit(0-1) * @param timer_num set timer number(0-2) of MCPWM, each MCPWM unit has 3 timers. @@ -288,6 +292,39 @@ esp_err_t mcpwm_set_pin(mcpwm_unit_t mcpwm_num, const mcpwm_pin_config_t *mcpwm_ */ esp_err_t mcpwm_init( mcpwm_unit_t mcpwm_num, mcpwm_timer_t timer_num, const mcpwm_config_t *mcpwm_conf); +/** + * @brief Set resolution of the MCPWM group + * @note + * This will override default resolution of group(=10,000,000). + * This WILL NOT automatically update frequency and duty. Call mcpwm_set_frequency() and mcpwm_set_duty() manually + * to set them back. + * + * @param mcpwm_num set MCPWM unit(0-1) + * @param resolution set expected frequency resolution + * + * @return + * - ESP_OK Success + * - ESP_ERR_INVALID_ARG Parameter error + */ +esp_err_t mcpwm_group_set_resolution(mcpwm_unit_t mcpwm_num, unsigned long int resolution); + +/** + * @brief Set resolution of each timer + * @note + * This WILL override default resolution of timer(=1,000,000). + * This WILL NOT automatically update frequency and duty. Call mcpwm_set_frequency() and mcpwm_set_duty() manually + * to set them back. + * + * @param mcpwm_num set MCPWM unit(0-1) + * @param timer_num set timer number(0-2) of MCPWM, each MCPWM unit has 3 timers + * @param resolution set expected frequency resolution + * + * @return + * - ESP_OK Success + * - ESP_ERR_INVALID_ARG Parameter error + */ +esp_err_t mcpwm_timer_set_resolution(mcpwm_unit_t mcpwm_num, mcpwm_timer_t timer_num, unsigned long int resolution); + /** * @brief Set frequency(in Hz) of MCPWM timer * @@ -331,7 +368,7 @@ esp_err_t mcpwm_set_duty_in_us(mcpwm_unit_t mcpwm_num, mcpwm_timer_t timer_num, /** * @brief Set duty either active high or active low(out of phase/inverted) - * @note + * @note * Call this function every time after mcpwm_set_signal_high or mcpwm_set_signal_low to resume with previously set duty cycle * * @param mcpwm_num set MCPWM unit(0-1) @@ -368,6 +405,18 @@ uint32_t mcpwm_get_frequency(mcpwm_unit_t mcpwm_num, mcpwm_timer_t timer_num); */ float mcpwm_get_duty(mcpwm_unit_t mcpwm_num, mcpwm_timer_t timer_num, mcpwm_operator_t gen); +/** + * @brief Get duty cycle of each operator in us + * + * @param mcpwm_num set MCPWM unit(0-1) + * @param timer_num set timer number(0-2) of MCPWM, each MCPWM unit has 3 timers + * @param gen set the generator(MCPWMXA/MCPWMXB), 'x' is operator number selected + * + * @return + * - duty cycle in us of each operator + */ +uint32_t mcpwm_get_duty_in_us(mcpwm_unit_t mcpwm_num, mcpwm_timer_t timer_num, mcpwm_operator_t gen); + /** * @brief Use this function to set MCPWM signal high * @@ -568,7 +617,7 @@ esp_err_t mcpwm_fault_init(mcpwm_unit_t mcpwm_num, mcpwm_fault_input_level_t int /** * @brief Set oneshot mode on fault detection, once fault occur in oneshot mode reset is required to resume MCPWM signals - * @note + * @note * currently low level triggering is not supported * * @param mcpwm_num set MCPWM unit(0-1) @@ -586,7 +635,7 @@ esp_err_t mcpwm_fault_set_oneshot_mode(mcpwm_unit_t mcpwm_num, mcpwm_timer_t tim /** * @brief Set cycle-by-cycle mode on fault detection, once fault occur in cyc mode MCPWM signal resumes as soon as fault signal becomes inactive - * @note + * @note * currently low level triggering is not supported * * @param mcpwm_num set MCPWM unit(0-1) diff --git a/components/driver/mcpwm.c b/components/driver/mcpwm.c index cefc0a851b..4eb884b647 100644 --- a/components/driver/mcpwm.c +++ b/components/driver/mcpwm.c @@ -64,12 +64,25 @@ _Static_assert(SOC_MCPWM_GENERATORS_PER_OPERATOR == 2, "This driver assumes the typedef struct { mcpwm_hal_context_t hal; portMUX_TYPE spinlock; + int group_pre_scale; // starts from 1, not 0. will be subtracted by 1 in ll driver + int timer_pre_scale[SOC_MCPWM_TIMERS_PER_GROUP]; // same as above } mcpwm_context_t; static mcpwm_context_t context[SOC_MCPWM_GROUPS] = { - [0 ... SOC_MCPWM_GROUPS - 1] = { - .spinlock = portMUX_INITIALIZER_UNLOCKED, - } + [0] = { + .hal = {MCPWM_LL_GET_HW(0)}, + .spinlock = portMUX_INITIALIZER_UNLOCKED, + .group_pre_scale = SOC_MCPWM_BASE_CLK_HZ / MCPWM_GROUP_CLK_HZ, + .timer_pre_scale = {[0 ... SOC_MCPWM_TIMERS_PER_GROUP - 1] = + MCPWM_GROUP_CLK_HZ / MCPWM_TIMER_CLK_HZ}, + }, + [1] = { + .hal = {MCPWM_LL_GET_HW(1)}, + .spinlock = portMUX_INITIALIZER_UNLOCKED, + .group_pre_scale = SOC_MCPWM_BASE_CLK_HZ / MCPWM_GROUP_CLK_HZ, + .timer_pre_scale = {[0 ... SOC_MCPWM_TIMERS_PER_GROUP - 1] = + MCPWM_GROUP_CLK_HZ / MCPWM_TIMER_CLK_HZ}, + } }; typedef void (*mcpwm_ll_gen_set_event_action_t)(mcpwm_dev_t *mcpwm, int op, int gen, int action); @@ -157,6 +170,30 @@ esp_err_t mcpwm_stop(mcpwm_unit_t mcpwm_num, mcpwm_timer_t timer_num) return ESP_OK; } +esp_err_t mcpwm_group_set_resolution(mcpwm_unit_t mcpwm_num, unsigned long int resolution) { + mcpwm_hal_context_t *hal = &context[mcpwm_num].hal; + int pre_scale_temp = SOC_MCPWM_BASE_CLK_HZ / resolution; + ESP_RETURN_ON_FALSE(pre_scale_temp >= 1, ESP_ERR_INVALID_ARG, TAG, "invalid resolution"); + context[mcpwm_num].group_pre_scale = pre_scale_temp; + mcpwm_critical_enter(mcpwm_num); + mcpwm_ll_group_set_clock_prescale(hal->dev, context[mcpwm_num].group_pre_scale); + mcpwm_critical_exit(mcpwm_num); + return ESP_OK; +} + +esp_err_t mcpwm_timer_set_resolution(mcpwm_unit_t mcpwm_num, mcpwm_timer_t timer_num, unsigned long int resolution) { + MCPWM_TIMER_CHECK(mcpwm_num, timer_num); + + mcpwm_hal_context_t *hal = &context[mcpwm_num].hal; + int pre_scale_temp = SOC_MCPWM_BASE_CLK_HZ / context[mcpwm_num].group_pre_scale / resolution; + ESP_RETURN_ON_FALSE(pre_scale_temp >= 1, ESP_ERR_INVALID_ARG, TAG, "invalid resolution"); + context[mcpwm_num].timer_pre_scale[timer_num] = pre_scale_temp; + mcpwm_critical_enter(mcpwm_num); + mcpwm_ll_timer_set_clock_prescale(hal->dev, timer_num, context[mcpwm_num].timer_pre_scale[timer_num]); + mcpwm_critical_exit(mcpwm_num); + return ESP_OK; +} + esp_err_t mcpwm_set_frequency(mcpwm_unit_t mcpwm_num, mcpwm_timer_t timer_num, uint32_t frequency) { //the driver currently always use the timer x for operator x @@ -168,7 +205,10 @@ esp_err_t mcpwm_set_frequency(mcpwm_unit_t mcpwm_num, mcpwm_timer_t timer_num, u mcpwm_ll_timer_update_period_at_once(hal->dev, timer_num); uint32_t previous_peak = mcpwm_ll_timer_get_peak(hal->dev, timer_num, false); - uint32_t new_peak = MCPWM_TIMER_CLK_HZ / frequency; + int real_group_prescale = mcpwm_ll_group_get_clock_prescale(hal->dev); + unsigned long int real_timer_clk_hz = + SOC_MCPWM_BASE_CLK_HZ / real_group_prescale / mcpwm_ll_timer_get_clock_prescale(hal->dev, timer_num); + uint32_t new_peak = real_timer_clk_hz / frequency; mcpwm_ll_timer_set_peak(hal->dev, timer_num, new_peak, false); // keep the duty cycle unchanged float scale = ((float)new_peak) / previous_peak; @@ -212,8 +252,10 @@ esp_err_t mcpwm_set_duty_in_us(mcpwm_unit_t mcpwm_num, mcpwm_timer_t timer_num, mcpwm_hal_context_t *hal = &context[mcpwm_num].hal; mcpwm_critical_enter(mcpwm_num); - // the timer resolution is fixed to 1us in the driver, so duty_in_us is the same to compare value - mcpwm_ll_operator_set_compare_value(hal->dev, op, cmp, duty_in_us); + int real_group_prescale = mcpwm_ll_group_get_clock_prescale(hal->dev); + unsigned long int real_timer_clk_hz = + SOC_MCPWM_BASE_CLK_HZ / real_group_prescale / mcpwm_ll_timer_get_clock_prescale(hal->dev, timer_num); + mcpwm_ll_operator_set_compare_value(hal->dev, op, cmp, duty_in_us * real_timer_clk_hz / 1000000); mcpwm_ll_operator_enable_update_compare_on_tez(hal->dev, op, cmp, true); mcpwm_critical_exit(mcpwm_num); return ESP_OK; @@ -313,13 +355,16 @@ 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_prescale(hal->dev, MCPWM_GROUP_CLK_PRESCALE); + mcpwm_ll_group_set_clock_prescale(hal->dev, context[mcpwm_num].group_pre_scale); mcpwm_ll_group_enable_shadow_mode(hal->dev); mcpwm_ll_group_flush_shadow(hal->dev); - mcpwm_ll_timer_set_clock_prescale(hal->dev, timer_num, MCPWM_GROUP_CLK_HZ / MCPWM_TIMER_CLK_HZ); + mcpwm_ll_timer_set_clock_prescale(hal->dev, timer_num, context[mcpwm_num].timer_pre_scale[timer_num]); 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); + int real_group_prescale = mcpwm_ll_group_get_clock_prescale(hal->dev); + unsigned long int real_timer_clk_hz = + SOC_MCPWM_BASE_CLK_HZ / real_group_prescale / mcpwm_ll_timer_get_clock_prescale(hal->dev, timer_num); + mcpwm_ll_timer_set_peak(hal->dev, timer_num, real_timer_clk_hz / mcpwm_conf->frequency, false); mcpwm_ll_operator_select_timer(hal->dev, timer_num, timer_num); //the driver currently always use the timer x for operator x mcpwm_critical_exit(mcpwm_num); @@ -337,10 +382,13 @@ 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 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); + int real_group_prescale = mcpwm_ll_group_get_clock_prescale(hal->dev); + unsigned long int real_timer_clk_hz = + SOC_MCPWM_BASE_CLK_HZ / real_group_prescale / mcpwm_ll_timer_get_clock_prescale(hal->dev, timer_num); + uint32_t peak = mcpwm_ll_timer_get_peak(hal->dev, timer_num, false); + uint32_t freq = real_timer_clk_hz / peak; mcpwm_critical_exit(mcpwm_num); - return (uint32_t)timer_clock; + return freq; } float mcpwm_get_duty(mcpwm_unit_t mcpwm_num, mcpwm_timer_t timer_num, mcpwm_generator_t gen) @@ -355,6 +403,20 @@ float mcpwm_get_duty(mcpwm_unit_t mcpwm_num, mcpwm_timer_t timer_num, mcpwm_gene return duty; } +uint32_t mcpwm_get_duty_in_us(mcpwm_unit_t mcpwm_num, mcpwm_timer_t timer_num, mcpwm_operator_t gen){ + //the driver currently always use the timer x for operator x + const int op = timer_num; + MCPWM_GEN_CHECK(mcpwm_num, timer_num, gen); + mcpwm_hal_context_t *hal = &context[mcpwm_num].hal; + mcpwm_critical_enter(mcpwm_num); + int real_group_prescale = mcpwm_ll_group_get_clock_prescale(hal->dev); + unsigned long int real_timer_clk_hz = + SOC_MCPWM_BASE_CLK_HZ / real_group_prescale / mcpwm_ll_timer_get_clock_prescale(hal->dev, timer_num); + uint32_t duty = mcpwm_ll_operator_get_compare_value(hal->dev, op, gen) * (1000000.0 / real_timer_clk_hz); + mcpwm_critical_exit(mcpwm_num); + return duty; +} + esp_err_t mcpwm_set_signal_high(mcpwm_unit_t mcpwm_num, mcpwm_timer_t timer_num, mcpwm_generator_t gen) { //the driver currently always use the timer x for operator x @@ -657,7 +719,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_prescale(hal->dev, MCPWM_GROUP_CLK_PRESCALE); + mcpwm_ll_group_set_clock_prescale(hal->dev, context[mcpwm_num].group_pre_scale); 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); diff --git a/components/driver/test/test_pwm.c b/components/driver/test/test_pwm.c index 17422ee5c9..4174757daf 100644 --- a/components/driver/test/test_pwm.c +++ b/components/driver/test/test_pwm.c @@ -26,6 +26,10 @@ #define TEST_SYNC_GPIO (21) #define TEST_CAP_GPIO (21) +#define MCPWM_TEST_GROUP_CLK_HZ (SOC_MCPWM_BASE_CLK_HZ / 16) +#define MCPWM_TEST_TIMER_CLK_HZ (MCPWM_TEST_GROUP_CLK_HZ / 10) + + static mcpwm_dev_t *MCPWM[2] = {&MCPWM0, &MCPWM1}; // interrupt handling still lacks API to get/clear pending event, currently we have to read/write interrupt register const static mcpwm_io_signals_t pwma[] = {MCPWM0A, MCPWM1A, MCPWM2A}; const static mcpwm_io_signals_t pwmb[] = {MCPWM0B, MCPWM1B, MCPWM2B}; @@ -67,7 +71,8 @@ static esp_err_t test_mcpwm_gpio_init(mcpwm_unit_t mcpwm_num, mcpwm_io_signals_t return ESP_OK; } -static void mcpwm_setup_testbench(mcpwm_unit_t group, mcpwm_timer_t timer, uint32_t pwm_freq, float pwm_duty) +static void mcpwm_setup_testbench(mcpwm_unit_t group, mcpwm_timer_t timer, uint32_t pwm_freq, float pwm_duty, + unsigned long int group_resolution, unsigned long int timer_resolution) { // PWMA <--> PCNT UNIT0 pcnt_config_t pcnt_config = { @@ -100,6 +105,8 @@ static void mcpwm_setup_testbench(mcpwm_unit_t group, mcpwm_timer_t timer, uint3 .counter_mode = MCPWM_UP_COUNTER, .duty_mode = MCPWM_DUTY_MODE_0, }; + mcpwm_group_set_resolution(group, group_resolution); + mcpwm_timer_set_resolution(group, timer, timer_resolution); TEST_ESP_OK(mcpwm_init(group, timer, &pwm_config)); } @@ -115,24 +122,24 @@ static uint32_t mcpwm_pcnt_get_pulse_number(pcnt_unit_t pwm_pcnt_unit, int captu return (uint32_t)count_value; } -static void mcpwm_timer_duty_test(mcpwm_unit_t unit, mcpwm_timer_t timer) +static void mcpwm_timer_duty_test(mcpwm_unit_t unit, mcpwm_timer_t timer, unsigned long int group_resolution, unsigned long int timer_resolution) { - mcpwm_setup_testbench(unit, timer, 1000, 50.0); + mcpwm_setup_testbench(unit, timer, 1000, 50.0, group_resolution, timer_resolution); vTaskDelay(pdMS_TO_TICKS(100)); TEST_ESP_OK(mcpwm_set_duty(unit, timer, MCPWM_OPR_A, 10.0)); TEST_ESP_OK(mcpwm_set_duty(unit, timer, MCPWM_OPR_B, 20.0)); - TEST_ASSERT_EQUAL_FLOAT(10.0, mcpwm_get_duty(unit, timer, MCPWM_OPR_A)); - TEST_ASSERT_EQUAL_FLOAT(20.0, mcpwm_get_duty(unit, timer, MCPWM_OPR_B)); + TEST_ASSERT_FLOAT_WITHIN(0.1, 10.0, mcpwm_get_duty(unit, timer, MCPWM_OPR_A)); + TEST_ASSERT_FLOAT_WITHIN(0.1, 20.0, mcpwm_get_duty(unit, timer, MCPWM_OPR_B)); vTaskDelay(pdMS_TO_TICKS(100)); TEST_ESP_OK(mcpwm_set_duty(unit, timer, MCPWM_OPR_A, 55.5f)); TEST_ESP_OK(mcpwm_set_duty_type(unit, timer, MCPWM_OPR_A, MCPWM_DUTY_MODE_0)); - TEST_ASSERT_EQUAL_FLOAT(55.5, mcpwm_get_duty(unit, timer, MCPWM_OPR_A)); + TEST_ASSERT_FLOAT_WITHIN(0.1, 55.5, mcpwm_get_duty(unit, timer, MCPWM_OPR_A)); vTaskDelay(pdMS_TO_TICKS(100)); TEST_ESP_OK(mcpwm_set_duty_in_us(unit, timer, MCPWM_OPR_B, 500)); - TEST_ASSERT_EQUAL_FLOAT(50.0, mcpwm_get_duty(unit, timer, MCPWM_OPR_B)); + TEST_ASSERT_INT_WITHIN(5, 500, mcpwm_get_duty_in_us(unit, timer, MCPWM_OPR_B)); vTaskDelay(pdMS_TO_TICKS(100)); TEST_ESP_OK(mcpwm_stop(unit, timer)); @@ -143,7 +150,8 @@ TEST_CASE("MCPWM duty test", "[mcpwm]") { for (int i = 0; i < SOC_MCPWM_GROUPS; i++) { for (int j = 0; j < SOC_MCPWM_TIMERS_PER_GROUP; j++) { - mcpwm_timer_duty_test(i, j); + mcpwm_timer_duty_test(i, j, MCPWM_TEST_GROUP_CLK_HZ, MCPWM_TEST_TIMER_CLK_HZ); + mcpwm_timer_duty_test(i, j, MCPWM_TEST_GROUP_CLK_HZ / 2, MCPWM_TEST_TIMER_CLK_HZ * 2); } } } @@ -154,7 +162,7 @@ static void mcpwm_start_stop_test(mcpwm_unit_t unit, mcpwm_timer_t timer) { uint32_t pulse_number = 0; - mcpwm_setup_testbench(unit, timer, 1000, 50.0); // Period: 1000us, 1ms + mcpwm_setup_testbench(unit, timer, 1000, 50.0, MCPWM_TEST_GROUP_CLK_HZ, MCPWM_TEST_TIMER_CLK_HZ); // Period: 1000us, 1ms // count the pulse number within 100ms pulse_number = mcpwm_pcnt_get_pulse_number(TEST_PWMA_PCNT_UNIT, 100); TEST_ASSERT_INT_WITHIN(2, 100, pulse_number); @@ -187,7 +195,7 @@ TEST_CASE("MCPWM start and stop test", "[mcpwm]") static void mcpwm_deadtime_test(mcpwm_unit_t unit, mcpwm_timer_t timer) { - mcpwm_setup_testbench(unit, timer, 1000, 50.0); // Period: 1000us, 1ms + mcpwm_setup_testbench(unit, timer, 1000, 50.0, MCPWM_TEST_GROUP_CLK_HZ, MCPWM_TEST_TIMER_CLK_HZ); // Period: 1000us, 1ms mcpwm_deadtime_type_t deadtime_type[] = {MCPWM_BYPASS_RED, MCPWM_BYPASS_FED, MCPWM_ACTIVE_HIGH_MODE, MCPWM_ACTIVE_LOW_MODE, MCPWM_ACTIVE_HIGH_COMPLIMENT_MODE, MCPWM_ACTIVE_LOW_COMPLIMENT_MODE, MCPWM_ACTIVE_RED_FED_FROM_PWMXA, MCPWM_ACTIVE_RED_FED_FROM_PWMXB @@ -220,7 +228,7 @@ static void mcpwm_carrier_test(mcpwm_unit_t unit, mcpwm_timer_t timer, mcpwm_car { uint32_t pulse_number = 0; - mcpwm_setup_testbench(unit, timer, 1000, 50.0); + mcpwm_setup_testbench(unit, timer, 1000, 50.0, MCPWM_TEST_GROUP_CLK_HZ, MCPWM_TEST_TIMER_CLK_HZ); mcpwm_set_signal_high(unit, timer, MCPWM_GEN_A); mcpwm_set_signal_high(unit, timer, MCPWM_GEN_B); TEST_ESP_OK(mcpwm_carrier_enable(unit, timer)); @@ -276,7 +284,7 @@ static void mcpwm_fault_cbc_test(mcpwm_unit_t unit, mcpwm_timer_t timer) mcpwm_fault_signal_t fault_sig = fault_sig_array[timer]; mcpwm_io_signals_t fault_io_sig = fault_io_sig_array[timer]; - mcpwm_setup_testbench(unit, timer, 1000, 50.0); + mcpwm_setup_testbench(unit, timer, 1000, 50.0, MCPWM_TEST_GROUP_CLK_HZ, MCPWM_TEST_TIMER_CLK_HZ); TEST_ESP_OK(test_mcpwm_gpio_init(unit, fault_io_sig, TEST_FAULT_GPIO)); gpio_set_level(TEST_FAULT_GPIO, 0); TEST_ESP_OK(mcpwm_fault_init(unit, MCPWM_HIGH_LEVEL_TGR, fault_sig)); @@ -312,7 +320,7 @@ static void mcpwm_fault_ost_test(mcpwm_unit_t unit, mcpwm_timer_t timer) mcpwm_fault_signal_t fault_sig = fault_sig_array[timer]; mcpwm_io_signals_t fault_io_sig = fault_io_sig_array[timer]; - mcpwm_setup_testbench(unit, timer, 1000, 50.0); + mcpwm_setup_testbench(unit, timer, 1000, 50.0, MCPWM_TEST_GROUP_CLK_HZ, MCPWM_TEST_TIMER_CLK_HZ); TEST_ESP_OK(test_mcpwm_gpio_init(unit, fault_io_sig, TEST_FAULT_GPIO)); gpio_set_level(TEST_FAULT_GPIO, 0); TEST_ESP_OK(mcpwm_fault_init(unit, MCPWM_HIGH_LEVEL_TGR, fault_sig)); @@ -345,7 +353,7 @@ static void mcpwm_sync_test(mcpwm_unit_t unit, mcpwm_timer_t timer) mcpwm_sync_signal_t sync_sig = sync_sig_array[timer]; mcpwm_io_signals_t sync_io_sig = sync_io_sig_array[timer]; - mcpwm_setup_testbench(unit, timer, 1000, 50.0); + mcpwm_setup_testbench(unit, timer, 1000, 50.0, MCPWM_TEST_GROUP_CLK_HZ, MCPWM_TEST_TIMER_CLK_HZ); TEST_ESP_OK(test_mcpwm_gpio_init(unit, sync_io_sig, TEST_SYNC_GPIO)); gpio_set_level(TEST_SYNC_GPIO, 0); diff --git a/components/hal/esp32/include/hal/mcpwm_ll.h b/components/hal/esp32/include/hal/mcpwm_ll.h index eff518fc13..f2e3b34183 100644 --- a/components/hal/esp32/include/hal/mcpwm_ll.h +++ b/components/hal/esp32/include/hal/mcpwm_ll.h @@ -41,7 +41,7 @@ extern "C" { /********************* Group registers *******************/ -// Set/Get group clock: PWM_clk = CLK_160M / (prescale + 1) +// Set/Get group clock: PWM_clk = CLK_160M / (clk_cfg.prescale + 1) static inline void mcpwm_ll_group_set_clock_prescale(mcpwm_dev_t *mcpwm, int pre_scale) { mcpwm->clk_cfg.prescale = pre_scale - 1; diff --git a/components/hal/esp32s3/include/hal/mcpwm_ll.h b/components/hal/esp32s3/include/hal/mcpwm_ll.h index 2a8f9a7f74..93f44e2470 100644 --- a/components/hal/esp32s3/include/hal/mcpwm_ll.h +++ b/components/hal/esp32s3/include/hal/mcpwm_ll.h @@ -41,15 +41,20 @@ extern "C" { /********************* Group registers *******************/ -// Set/Get group clock: PWM_clk = CLK_160M / (prescale + 1) +// Set/Get group clock: PWM_clk = CLK_160M / (clk_cfg.prescale + 1) static inline void mcpwm_ll_group_set_clock_prescale(mcpwm_dev_t *mcpwm, int pre_scale) { - mcpwm->clk_cfg.prescale = pre_scale - 1; + // In case the compiler optimise a 32bit instruction (e.g. s32i) into 8bit instruction (e.g. s8i, which is not allowed to access a register) + // We take care of the "read-modify-write" procedure by ourselves. + typeof(mcpwm->clk_cfg) clkcfg = mcpwm->clk_cfg; + clkcfg.prescale = pre_scale - 1; + mcpwm->clk_cfg = clkcfg; } static inline uint32_t mcpwm_ll_group_get_clock_prescale(mcpwm_dev_t *mcpwm) { - return mcpwm->clk_cfg.prescale + 1; + typeof(mcpwm->clk_cfg) clkcfg = mcpwm->clk_cfg; + return clkcfg.prescale + 1; } static inline void mcpwm_ll_group_enable_shadow_mode(mcpwm_dev_t *mcpwm) @@ -265,12 +270,17 @@ static inline void mcpwm_ll_intr_enable_capture(mcpwm_dev_t *mcpwm, uint32_t cap 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 = prescale - 1; + // In case the compiler optimise a 32bit instruction (e.g. s32i) into 8bit instruction (e.g. s8i, which is not allowed to access a register) + // We take care of the "read-modify-write" procedure by ourselves. + typeof(mcpwm->timer[timer_id].period) period = mcpwm->timer[timer_id].period; + period.prescale = prescale - 1; + mcpwm->timer[timer_id].period = period; } static inline uint32_t mcpwm_ll_timer_get_clock_prescale(mcpwm_dev_t *mcpwm, int timer_id) { - return mcpwm->timer[timer_id].period.prescale + 1; + typeof(mcpwm->timer[timer_id].period) period = mcpwm->timer[timer_id].period; + return period.prescale + 1; } static inline void mcpwm_ll_timer_set_peak(mcpwm_dev_t *mcpwm, int timer_id, uint32_t peak, bool symmetric) diff --git a/docs/en/api-reference/peripherals/mcpwm.rst b/docs/en/api-reference/peripherals/mcpwm.rst index dfc1b6fcbd..0e9234a144 100644 --- a/docs/en/api-reference/peripherals/mcpwm.rst +++ b/docs/en/api-reference/peripherals/mcpwm.rst @@ -35,6 +35,7 @@ Contents * Use `Fault Handler`_ to detect and manage faults * Add a higher frequency `Carrier`_, if output signals are passed through an isolation transformer * Configuration and handling of `Interrupts`_. +* Extra configuration of `Resolution`_. Configure @@ -57,7 +58,8 @@ Configuration covers the following steps: 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. -5. Calling of :cpp:func:`mcpwm_init` with the above parameters to make the configuration effective. +5. Setting timer resolution if necessary, by calling :cpp:func:`mcpwm_group_set_resolution` and :cpp:func:`mcpwm_timer_set_resolution` +6. Calling of :cpp:func:`mcpwm_init` with the above parameters to make the configuration effective. Operate @@ -159,6 +161,16 @@ Interrupts Registering of the MCPWM interrupt handler is possible by calling :cpp:func:`mcpwm_isr_register`. +Resolution +---------- + +The default resolution for MCPWM group and MCPWM timer are configured to **10MHz** and **1MHz** in :cpp:func:`mcpwm_init`, which might be not enough for some applications. +The driver also provides two APIs that can be used to override the default resolution: :cpp:func:`mcpwm_group_set_resolution` and :cpp:func:`mcpwm_timer_set_resolution`. + +Note that, these two APIs won't update the frequency and duty automatically, to achieve that, one has to call :cpp:func:`mcpwm_set_frequency` and :cpp:func:`mcpwm_set_duty` accordingly. + +To get PWM pulse that is below 15Hz, please set the resolution to a lower value. For high frequency PWM with limited step range, please set them with higher value. + Application Example -------------------