Bladeren bron

MCPWM: expose API to set timer resolution

Closes https://github.com/espressif/esp-idf/issues/1101
SalimTerryLi 4 jaren geleden
bovenliggende
commit
f4314af913

+ 52 - 3
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)

+ 75 - 13
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);

+ 22 - 14
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);
 

+ 1 - 1
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;

+ 15 - 5
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)

+ 13 - 1
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
 -------------------