morris 3 лет назад
Родитель
Сommit
15b9352f60
31 измененных файлов с 1876 добавлено и 152 удалено
  1. 10 14
      components/driver/deprecated/driver/mcpwm.h
  2. 79 60
      components/driver/deprecated/mcpwm_legacy.c
  3. 8 23
      components/driver/mcpwm/mcpwm_com.c
  4. 2 2
      components/driver/test_apps/legacy_mcpwm_driver/README.md
  5. 7 3
      components/driver/test_apps/legacy_mcpwm_driver/main/test_legacy_mcpwm.c
  6. 2 1
      components/driver/test_apps/legacy_mcpwm_driver/pytest_legacy_mcpwm.py
  7. 2 2
      components/driver/test_apps/mcpwm/README.md
  8. 2 1
      components/driver/test_apps/mcpwm/pytest_mcpwm.py
  9. 8 0
      components/hal/esp32h2/include/hal/clk_gate_ll.h
  10. 1647 0
      components/hal/esp32h2/include/hal/mcpwm_ll.h
  11. 0 4
      components/soc/esp32/include/soc/Kconfig.soc_caps.in
  12. 0 1
      components/soc/esp32/include/soc/soc_caps.h
  13. 0 8
      components/soc/esp32c6/include/soc/Kconfig.soc_caps.in
  14. 0 2
      components/soc/esp32c6/include/soc/soc_caps.h
  15. 4 8
      components/soc/esp32h2/include/soc/Kconfig.soc_caps.in
  16. 2 1
      components/soc/esp32h2/include/soc/periph_defs.h
  17. 2 5
      components/soc/esp32h2/include/soc/soc_caps.h
  18. 1 1
      components/soc/esp32h2/ld/esp32h2.peripherals.ld
  19. 83 0
      components/soc/esp32h2/mcpwm_periph.c
  20. 0 4
      components/soc/esp32s3/include/soc/Kconfig.soc_caps.in
  21. 0 1
      components/soc/esp32s3/include/soc/soc_caps.h
  22. 0 1
      docs/docs_not_updated/esp32h2.txt
  23. 2 2
      examples/peripherals/mcpwm/mcpwm_bdc_speed_control/README.md
  24. 2 2
      examples/peripherals/mcpwm/mcpwm_bldc_hall_control/README.md
  25. 2 2
      examples/peripherals/mcpwm/mcpwm_capture_hc_sr04/README.md
  26. 1 0
      examples/peripherals/mcpwm/mcpwm_capture_hc_sr04/pytest_hc_sr04.py
  27. 2 2
      examples/peripherals/mcpwm/mcpwm_servo_control/README.md
  28. 1 0
      examples/peripherals/mcpwm/mcpwm_servo_control/pytest_servo_mg996r.py
  29. 2 2
      examples/peripherals/mcpwm/mcpwm_sync/README.md
  30. 1 0
      examples/peripherals/mcpwm/mcpwm_sync/pytest_mcpwm_sync.py
  31. 4 0
      examples/peripherals/rmt/dshot_esc/main/dshot_esc_example_main.c

+ 10 - 14
components/driver/deprecated/driver/mcpwm.h

@@ -51,10 +51,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.
+ * @note The default resolution configured for MCPWM timer is 1M, it can be changed by `mcpwm_timer_set_resolution`.
+ * @note The default resolution configured for MCPWM group can be different on different esp targets (because of different clock source).
+ *       You can change the group resolution by mcpwm_group_set_resolution()
+ * @note If you want to change the preset resolution of MCPWM group and timer, please call them before this function.
  *
  * @param mcpwm_num set MCPWM unit
  * @param timer_num set timer number(0-2) of MCPWM, each MCPWM unit has 3 timers.
@@ -68,11 +68,9 @@ esp_err_t mcpwm_init( mcpwm_unit_t mcpwm_num, mcpwm_timer_t timer_num, const mcp
 
 /**
  * @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.
- *        The group resolution must be an integral multiple of timer resolution.
+ * @note This will override default resolution of MCPWM group.
+ * @note This WILL NOT automatically update PWM frequency and duty. Please call `mcpwm_set_frequency` and `mcpwm_set_duty` manually to reflect the change.
+ * @note The group resolution must be an integral multiple of timer resolution.
  *
  * @param mcpwm_num set MCPWM unit
  * @param resolution set expected frequency resolution
@@ -85,11 +83,9 @@ esp_err_t mcpwm_group_set_resolution(mcpwm_unit_t mcpwm_num, unsigned long int r
 
 /**
  * @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.
- *        The group resolution must be an integral multiple of timer resolution.
+ * @note This will override default resolution of timer(=1,000,000).
+ * @note This WILL NOT automatically update PWM frequency and duty. Please call `mcpwm_set_frequency` and `mcpwm_set_duty` manually to reflect the change.
+ * @note The group resolution must be an integral multiple of timer resolution.
  *
  * @param mcpwm_num set MCPWM unit
  * @param timer_num set timer number(0-2) of MCPWM, each MCPWM unit has 3 timers

+ 79 - 60
components/driver/deprecated/mcpwm_legacy.c

@@ -1,20 +1,17 @@
 /*
- * SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD
+ * SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD
  *
  * SPDX-License-Identifier: Apache-2.0
  */
 
-#include <stdio.h>
 #include "sdkconfig.h"
 #include "freertos/FreeRTOS.h"
-#include "freertos/semphr.h"
 #include "freertos/task.h"
 #include "esp_log.h"
 #include "esp_err.h"
 #include "esp_check.h"
 #include "esp_rom_gpio.h"
 #include "esp_intr_alloc.h"
-#include "soc/gpio_periph.h"
 #include "soc/mcpwm_periph.h"
 #include "hal/mcpwm_hal.h"
 #include "hal/gpio_hal.h"
@@ -22,6 +19,7 @@
 #include "driver/mcpwm_types_legacy.h"
 #include "driver/gpio.h"
 #include "esp_private/periph_ctrl.h"
+#include "clk_tree.h"
 #include "esp_private/esp_clk.h"
 
 static const char *TAG = "mcpwm(legacy)";
@@ -48,14 +46,20 @@ _Static_assert(MCPWM_UNIT_MAX == SOC_MCPWM_GROUPS, "MCPWM unit number not equal
 #define MCPWM_INTR_FLAG  0
 #endif
 
-#define MCPWM_GROUP_CLK_SRC_HZ 160000000 // MCPWM clock source is fixed to `MCPWM_CAPTURE_CLK_SRC_PLL160M`
-#define MCPWM_GROUP_CLK_PRESCALE (16)
-#define MCPWM_GROUP_CLK_HZ (MCPWM_GROUP_CLK_SRC_HZ / MCPWM_GROUP_CLK_PRESCALE)
-#define MCPWM_TIMER_CLK_HZ (MCPWM_GROUP_CLK_HZ / 10)
+// Note: we can't modify the default MCPWM group resolution once it's determined
+// otherwise we may break user's existing code which configures the dead-time based on this resolution, see `mcpwm_deadtime_enable`
+#if CONFIG_IDF_TARGET_ESP32H2
+#define MCPWM_DEFAULT_GROUP_CLK_RESOLUTION_HZ (12 * 1000 * 1000)
+#else
+#define MCPWM_DEFAULT_GROUP_CLK_RESOLUTION_HZ (10 * 1000 * 1000)
+#endif
+
+// Preset MCPWM Timer clock resolution (1MHz)
+#define MCPWM_DEFAULT_TIMER_CLK_RESOLUTION_HZ (1 * 1000 * 1000)
 
-_Static_assert(SOC_MCPWM_OPERATORS_PER_GROUP >= SOC_MCPWM_TIMERS_PER_GROUP, "This driver assumes the timer num equals to the operator num.");
-_Static_assert(SOC_MCPWM_COMPARATORS_PER_OPERATOR >= SOC_MCPWM_GENERATORS_PER_OPERATOR, "This driver assumes the generator num equals to the generator num.");
-_Static_assert(SOC_MCPWM_GENERATORS_PER_OPERATOR == 2, "This driver assumes the generator num equals to 2.");
+ESP_STATIC_ASSERT(SOC_MCPWM_OPERATORS_PER_GROUP >= SOC_MCPWM_TIMERS_PER_GROUP, "This driver assumes the timer num equals to the operator num.");
+ESP_STATIC_ASSERT(SOC_MCPWM_COMPARATORS_PER_OPERATOR >= SOC_MCPWM_GENERATORS_PER_OPERATOR, "This driver assumes the generator num equals to the generator num.");
+ESP_STATIC_ASSERT(SOC_MCPWM_GENERATORS_PER_OPERATOR == 2, "This driver assumes the generator num equals to 2.");
 
 #define MCPWM_TIMER_ID_CHECK(mcpwm_num, timer_num)                                                                  \
     do {                                                                                                            \
@@ -85,8 +89,8 @@ typedef struct {
     portMUX_TYPE spinlock;
     _lock_t mutex_lock;
     const int group_id;
-    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
+    int group_resolution_hz;
+    int timer_resolution_hz[SOC_MCPWM_TIMERS_PER_GROUP];
     intr_handle_t mcpwm_intr_handle;  // handler for ISR register, one per MCPWM group
     cap_isr_func_t cap_isr_func[SOC_MCPWM_CAPTURE_CHANNELS_PER_TIMER]; // handler for ISR callback, one for each cap ch
 } mcpwm_context_t;
@@ -96,27 +100,13 @@ static mcpwm_context_t context[SOC_MCPWM_GROUPS] = {
         .hal = {MCPWM_LL_GET_HW(0)},
         .spinlock = portMUX_INITIALIZER_UNLOCKED,
         .group_id = 0,
-        .group_pre_scale = MCPWM_GROUP_CLK_SRC_HZ / MCPWM_GROUP_CLK_HZ,
-        .timer_pre_scale = {
-            [0 ... SOC_MCPWM_TIMERS_PER_GROUP - 1] =
-            MCPWM_GROUP_CLK_HZ / MCPWM_TIMER_CLK_HZ
-        },
-        .mcpwm_intr_handle = NULL,
-        .cap_isr_func = {[0 ... SOC_MCPWM_CAPTURE_CHANNELS_PER_TIMER - 1] = {NULL, NULL}},
     },
 #if SOC_MCPWM_GROUPS > 1
     [1] = {
         .hal = {MCPWM_LL_GET_HW(1)},
         .spinlock = portMUX_INITIALIZER_UNLOCKED,
         .group_id = 1,
-        .group_pre_scale = MCPWM_GROUP_CLK_SRC_HZ / MCPWM_GROUP_CLK_HZ,
-        .timer_pre_scale = {
-            [0 ... SOC_MCPWM_TIMERS_PER_GROUP - 1] =
-            MCPWM_GROUP_CLK_HZ / MCPWM_TIMER_CLK_HZ
-        },
-        .mcpwm_intr_handle = NULL,
-        .cap_isr_func = {[0 ... SOC_MCPWM_CAPTURE_CHANNELS_PER_TIMER - 1] = {NULL, NULL}},
-    }
+    },
 #endif
 };
 
@@ -215,28 +205,51 @@ 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)
+static inline uint32_t mcpwm_group_get_resolution(mcpwm_unit_t mcpwm_num)
+{
+    if (context[mcpwm_num].group_resolution_hz == 0) {
+        context[mcpwm_num].group_resolution_hz = MCPWM_DEFAULT_GROUP_CLK_RESOLUTION_HZ;
+    }
+    return context[mcpwm_num].group_resolution_hz;
+}
+
+static inline uint32_t mcpwm_timer_get_resolution(mcpwm_unit_t mcpwm_num, mcpwm_timer_t timer_num)
+{
+    if (context[mcpwm_num].timer_resolution_hz[timer_num] == 0) {
+        context[mcpwm_num].timer_resolution_hz[timer_num] = MCPWM_DEFAULT_TIMER_CLK_RESOLUTION_HZ;
+    }
+    return context[mcpwm_num].timer_resolution_hz[timer_num];
+}
+
+esp_err_t mcpwm_group_set_resolution(mcpwm_unit_t mcpwm_num, uint32_t resolution)
 {
     mcpwm_hal_context_t *hal = &context[mcpwm_num].hal;
-    int pre_scale_temp = MCPWM_GROUP_CLK_SRC_HZ / resolution;
+    uint32_t clk_src_hz = 0;
+    clk_tree_src_get_freq_hz(MCPWM_TIMER_CLK_SRC_DEFAULT, CLK_TREE_SRC_FREQ_PRECISION_CACHED, &clk_src_hz);
+
+    int pre_scale_temp = clk_src_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;
+    context[mcpwm_num].group_resolution_hz = clk_src_hz / pre_scale_temp;
+
     mcpwm_critical_enter(mcpwm_num);
-    mcpwm_ll_group_set_clock_prescale(hal->dev, context[mcpwm_num].group_pre_scale);
+    mcpwm_ll_group_set_clock_prescale(hal->dev, pre_scale_temp);
     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)
+esp_err_t mcpwm_timer_set_resolution(mcpwm_unit_t mcpwm_num, mcpwm_timer_t timer_num, uint32_t resolution)
 {
     MCPWM_TIMER_CHECK(mcpwm_num, timer_num);
 
     mcpwm_hal_context_t *hal = &context[mcpwm_num].hal;
-    int pre_scale_temp = MCPWM_GROUP_CLK_SRC_HZ / context[mcpwm_num].group_pre_scale / resolution;
+    uint32_t group_resolution = mcpwm_group_get_resolution(mcpwm_num);
+
+    int pre_scale_temp = group_resolution / 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;
+    context[mcpwm_num].timer_resolution_hz[timer_num] = context[mcpwm_num].group_resolution_hz / 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_ll_timer_set_clock_prescale(hal->dev, timer_num, pre_scale_temp);
     mcpwm_critical_exit(mcpwm_num);
     return ESP_OK;
 }
@@ -252,10 +265,8 @@ 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);
-    int real_group_prescale = mcpwm_ll_group_get_clock_prescale(hal->dev);
-    unsigned long int real_timer_clk_hz =
-        MCPWM_GROUP_CLK_SRC_HZ / real_group_prescale / mcpwm_ll_timer_get_clock_prescale(hal->dev, timer_num);
-    uint32_t new_peak = real_timer_clk_hz / frequency;
+    uint32_t timer_resolution = mcpwm_timer_get_resolution(mcpwm_num, timer_num);
+    uint32_t new_peak = timer_resolution / 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;
@@ -300,11 +311,9 @@ 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);
-    int real_group_prescale = mcpwm_ll_group_get_clock_prescale(hal->dev);
     // to avid multiplication overflow, use uint64_t here
-    uint64_t real_timer_clk_hz =
-        MCPWM_GROUP_CLK_SRC_HZ / real_group_prescale / mcpwm_ll_timer_get_clock_prescale(hal->dev, timer_num);
-    uint64_t compare_val = real_timer_clk_hz * duty_in_us / 1000000;
+    uint64_t timer_resolution = mcpwm_timer_get_resolution(mcpwm_num, timer_num);
+    uint64_t compare_val = timer_resolution * duty_in_us / 1000000;
     mcpwm_ll_operator_set_compare_value(hal->dev, op, cmp, (uint32_t)compare_val);
     mcpwm_ll_operator_enable_update_compare_on_tez(hal->dev, op, cmp, true);
     mcpwm_ll_operator_enable_update_compare_on_tep(hal->dev, op, cmp, true);
@@ -405,17 +414,21 @@ esp_err_t mcpwm_init(mcpwm_unit_t mcpwm_num, mcpwm_timer_t timer_num, const mcpw
     };
     mcpwm_hal_init(hal, &config);
 
+    uint32_t clk_src_hz = 0;
+    clk_tree_src_get_freq_hz(MCPWM_TIMER_CLK_SRC_DEFAULT, CLK_TREE_SRC_FREQ_PRECISION_CACHED, &clk_src_hz);
+    uint32_t group_resolution = mcpwm_group_get_resolution(mcpwm_num);
+    uint32_t timer_resolution = mcpwm_timer_get_resolution(mcpwm_num, timer_num);
+    uint32_t group_pre_scale = clk_src_hz / group_resolution;
+    uint32_t timer_pre_scale = group_resolution / timer_resolution;
+
     mcpwm_critical_enter(mcpwm_num);
     mcpwm_ll_group_enable_clock(hal->dev, true);
     mcpwm_ll_group_set_clock_source(hal->dev, (soc_module_clk_t)MCPWM_CAPTURE_CLK_SRC_DEFAULT);
-    mcpwm_ll_group_set_clock_prescale(hal->dev, context[mcpwm_num].group_pre_scale);
-    mcpwm_ll_timer_set_clock_prescale(hal->dev, timer_num, context[mcpwm_num].timer_pre_scale[timer_num]);
+    mcpwm_ll_group_set_clock_prescale(hal->dev, group_pre_scale);
+    mcpwm_ll_timer_set_clock_prescale(hal->dev, timer_num, timer_pre_scale);
     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);
-    int real_group_prescale = mcpwm_ll_group_get_clock_prescale(hal->dev);
-    unsigned long int real_timer_clk_hz =
-        MCPWM_GROUP_CLK_SRC_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_timer_set_peak(hal->dev, timer_num, timer_resolution / mcpwm_conf->frequency, false);
     mcpwm_ll_operator_connect_timer(hal->dev, timer_num, timer_num); //the driver currently always use the timer x for operator x
     mcpwm_critical_exit(mcpwm_num);
 
@@ -434,13 +447,13 @@ uint32_t mcpwm_get_frequency(mcpwm_unit_t mcpwm_num, mcpwm_timer_t timer_num)
         ESP_LOGE(TAG, "Invalid MCPWM timer instance");
         return 0;
     }
+
+    uint32_t timer_resolution = mcpwm_timer_get_resolution(mcpwm_num, timer_num);
+
     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 =
-        MCPWM_GROUP_CLK_SRC_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;
+    uint32_t freq = timer_resolution / peak;
     mcpwm_critical_exit(mcpwm_num);
     return freq;
 }
@@ -466,14 +479,14 @@ uint32_t mcpwm_get_duty_in_us(mcpwm_unit_t mcpwm_num, mcpwm_timer_t timer_num, m
         ESP_LOGE(TAG, "Invalid MCPWM generator instance");
         return 0;
     }
+
+    uint32_t timer_resolution = mcpwm_timer_get_resolution(mcpwm_num, timer_num);
+
     //the driver currently always use the timer x for operator x
     const int op = timer_num;
     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 =
-        MCPWM_GROUP_CLK_SRC_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);
+    uint32_t duty = mcpwm_ll_operator_get_compare_value(hal->dev, op, gen) * (1000000.0 / timer_resolution);
     mcpwm_critical_exit(mcpwm_num);
     return duty;
 }
@@ -797,10 +810,16 @@ esp_err_t mcpwm_capture_enable_channel(mcpwm_unit_t mcpwm_num, mcpwm_capture_cha
         .group_id = mcpwm_num
     };
     mcpwm_hal_init(hal, &init_config);
+
+    uint32_t clk_src_hz = 0;
+    clk_tree_src_get_freq_hz(MCPWM_TIMER_CLK_SRC_DEFAULT, CLK_TREE_SRC_FREQ_PRECISION_CACHED, &clk_src_hz);
+    uint32_t group_resolution = mcpwm_group_get_resolution(mcpwm_num);
+    uint32_t group_pre_scale = clk_src_hz / group_resolution;
+
     mcpwm_critical_enter(mcpwm_num);
     mcpwm_ll_group_enable_clock(hal->dev, true);
     mcpwm_ll_group_set_clock_source(hal->dev, (soc_module_clk_t)MCPWM_CAPTURE_CLK_SRC_DEFAULT);
-    mcpwm_ll_group_set_clock_prescale(hal->dev, context[mcpwm_num].group_pre_scale);
+    mcpwm_ll_group_set_clock_prescale(hal->dev, group_pre_scale);
     mcpwm_ll_capture_enable_timer(hal->dev, true);
     mcpwm_ll_capture_enable_channel(hal->dev, cap_channel, true);
     mcpwm_ll_capture_enable_negedge(hal->dev, cap_channel, cap_conf->cap_edge & MCPWM_NEG_EDGE);
@@ -879,7 +898,7 @@ uint32_t mcpwm_capture_get_resolution(mcpwm_unit_t mcpwm_num)
         return 0;
     }
 #if SOC_MCPWM_CAPTURE_CLK_FROM_GROUP
-    return MCPWM_GROUP_CLK_SRC_HZ / context[mcpwm_num].group_pre_scale;
+    return mcpwm_group_get_resolution(mcpwm_num);
 #else
     return esp_clk_apb_freq();
 #endif

+ 8 - 23
components/driver/mcpwm/mcpwm_com.c

@@ -1,5 +1,5 @@
 /*
- * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
+ * SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
  *
  * SPDX-License-Identifier: Apache-2.0
  */
@@ -14,8 +14,8 @@
 #endif
 #include "esp_log.h"
 #include "esp_check.h"
+#include "clk_tree.h"
 #include "esp_private/periph_ctrl.h"
-#include "esp_private/esp_clk.h"
 #include "soc/mcpwm_periph.h"
 #include "hal/mcpwm_ll.h"
 #include "mcpwm_private.h"
@@ -115,29 +115,14 @@ esp_err_t mcpwm_select_periph_clock(mcpwm_group_t *group, soc_module_clk_t clk_s
                         "group clock conflict, already is %d but attempt to %d", group->clk_src, clk_src);
 
     if (do_clock_init) {
-        // [clk_tree] ToDo: replace the following switch-case table by clock_tree APIs
-        switch (clk_src) {
-#if SOC_MCPWM_CLK_SUPPORT_PLL160M
-        case SOC_MOD_CLK_PLL_F160M:
-            periph_src_clk_hz = 160000000;
+        ESP_RETURN_ON_ERROR(clk_tree_src_get_freq_hz(clk_src, CLK_TREE_SRC_FREQ_PRECISION_CACHED, &periph_src_clk_hz), TAG, "get clock source freq failed");
+
 #if CONFIG_PM_ENABLE
-            sprintf(group->pm_lock_name, "mcpwm_%d", group->group_id); // e.g. mcpwm_0
-            ret  = esp_pm_lock_create(ESP_PM_APB_FREQ_MAX, 0, group->pm_lock_name, &group->pm_lock);
-            ESP_RETURN_ON_ERROR(ret, TAG, "create ESP_PM_APB_FREQ_MAX lock failed");
-            ESP_LOGD(TAG, "install ESP_PM_APB_FREQ_MAX lock for MCPWM group(%d)", group->group_id);
+        sprintf(group->pm_lock_name, "mcpwm_%d", group->group_id); // e.g. mcpwm_0
+        ret  = esp_pm_lock_create(ESP_PM_NO_LIGHT_SLEEP, 0, group->pm_lock_name, &group->pm_lock);
+        ESP_RETURN_ON_ERROR(ret, TAG, "create pm lock failed");
+        ESP_LOGD(TAG, "install NO_LIGHT_SLEEP lock for MCPWM group(%d)", group->group_id);
 #endif // CONFIG_PM_ENABLE
-            break;
-#endif // SOC_MCPWM_CLK_SUPPORT_PLL160M
-
-#if SOC_MCPWM_CLK_SUPPORT_XTAL
-        case SOC_MOD_CLK_XTAL:
-            periph_src_clk_hz = esp_clk_xtal_freq();
-            break;
-#endif // SOC_MCPWM_CLK_SUPPORT_XTAL
-        default:
-            ESP_RETURN_ON_FALSE(false, ESP_ERR_NOT_SUPPORTED, TAG, "clock source %d is not supported", clk_src);
-            break;
-        }
 
         mcpwm_ll_group_set_clock_source(group->hal.dev, clk_src);
         mcpwm_ll_group_set_clock_prescale(group->hal.dev, MCPWM_PERIPH_CLOCK_PRE_SCALE);

+ 2 - 2
components/driver/test_apps/legacy_mcpwm_driver/README.md

@@ -1,2 +1,2 @@
-| Supported Targets | ESP32 | ESP32-C6 | ESP32-S3 |
-| ----------------- | ----- | -------- | -------- |
+| Supported Targets | ESP32 | ESP32-C6 | ESP32-H2 | ESP32-S3 |
+| ----------------- | ----- | -------- | -------- | -------- |

+ 7 - 3
components/driver/test_apps/legacy_mcpwm_driver/main/test_legacy_mcpwm.c

@@ -25,9 +25,13 @@
 #define TEST_SYNC_GPIO_2 (19)
 #define TEST_CAP_GPIO (21)
 
-#define MCPWM_GROUP_CLK_SRC_HZ  160000000
-#define MCPWM_TEST_GROUP_CLK_HZ (MCPWM_GROUP_CLK_SRC_HZ / 16)
-#define MCPWM_TEST_TIMER_CLK_HZ (MCPWM_TEST_GROUP_CLK_HZ / 10)
+// MCPWM default resolution
+#if CONFIG_IDF_TARGET_ESP32H2
+#define MCPWM_TEST_GROUP_CLK_HZ (12 * 1000 * 1000)
+#else
+#define MCPWM_TEST_GROUP_CLK_HZ (10 * 1000 * 1000)
+#endif
+#define MCPWM_TEST_TIMER_CLK_HZ (1 * 1000 * 1000)
 
 const static mcpwm_io_signals_t pwma[] = {MCPWM0A, MCPWM1A, MCPWM2A};
 const static mcpwm_io_signals_t pwmb[] = {MCPWM0B, MCPWM1B, MCPWM2B};

+ 2 - 1
components/driver/test_apps/legacy_mcpwm_driver/pytest_legacy_mcpwm.py

@@ -1,4 +1,4 @@
-# SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
+# SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
 # SPDX-License-Identifier: CC0-1.0
 
 import pytest
@@ -8,6 +8,7 @@ from pytest_embedded import Dut
 @pytest.mark.esp32
 @pytest.mark.esp32s3
 @pytest.mark.esp32c6
+@pytest.mark.esp32h2
 @pytest.mark.generic
 @pytest.mark.parametrize(
     'config',

+ 2 - 2
components/driver/test_apps/mcpwm/README.md

@@ -1,2 +1,2 @@
-| Supported Targets | ESP32 | ESP32-C6 | ESP32-S3 |
-| ----------------- | ----- | -------- | -------- |
+| Supported Targets | ESP32 | ESP32-C6 | ESP32-H2 | ESP32-S3 |
+| ----------------- | ----- | -------- | -------- | -------- |

+ 2 - 1
components/driver/test_apps/mcpwm/pytest_mcpwm.py

@@ -1,4 +1,4 @@
-# SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
+# SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
 # SPDX-License-Identifier: CC0-1.0
 
 import pytest
@@ -8,6 +8,7 @@ from pytest_embedded import Dut
 @pytest.mark.esp32
 @pytest.mark.esp32s3
 @pytest.mark.esp32c6
+@pytest.mark.esp32h2
 @pytest.mark.generic
 @pytest.mark.parametrize(
     'config',

+ 8 - 0
components/hal/esp32h2/include/hal/clk_gate_ll.h

@@ -53,6 +53,8 @@ static inline uint32_t periph_ll_get_clk_en_mask(periph_module_t periph)
             return PCR_TWAI0_CLK_EN;
         case PERIPH_GDMA_MODULE:
             return PCR_GDMA_CLK_EN;
+        case PERIPH_MCPWM0_MODULE:
+            return PCR_PWM_CLK_EN;
         case PERIPH_AES_MODULE:
             return PCR_AES_CLK_EN;
         case PERIPH_SHA_MODULE:
@@ -120,6 +122,8 @@ static inline uint32_t periph_ll_get_rst_en_mask(periph_module_t periph, bool en
             return PCR_TWAI0_RST_EN;
         case PERIPH_GDMA_MODULE:
             return PCR_GDMA_RST_EN;
+        case PERIPH_MCPWM0_MODULE:
+            return PCR_PWM_RST_EN;
         case PERIPH_AES_MODULE:
         if (enable == true) {
             // Clear reset on digital signature, otherwise AES unit is held in reset also.
@@ -210,6 +214,8 @@ static uint32_t periph_ll_get_clk_en_reg(periph_module_t periph)
             return PCR_TWAI0_CONF_REG;
         case PERIPH_GDMA_MODULE:
             return PCR_GDMA_CONF_REG;
+        case PERIPH_MCPWM0_MODULE:
+            return PCR_PWM_CONF_REG;
         case PERIPH_AES_MODULE:
             return PCR_AES_CONF_REG;
         case PERIPH_SHA_MODULE:
@@ -263,6 +269,8 @@ static uint32_t periph_ll_get_rst_en_reg(periph_module_t periph)
             return PCR_TWAI0_CONF_REG;
         case PERIPH_GDMA_MODULE:
             return PCR_GDMA_CONF_REG;
+        case PERIPH_MCPWM0_MODULE:
+            return PCR_PWM_CONF_REG;
         case PERIPH_AES_MODULE:
             return PCR_AES_CONF_REG;
         case PERIPH_SHA_MODULE:

+ 1647 - 0
components/hal/esp32h2/include/hal/mcpwm_ll.h

@@ -0,0 +1,1647 @@
+/*
+ * SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+/*******************************************************************************
+ * NOTICE
+ * The hal is not public api, don't use in application code.
+ * See readme.md in hal/include/hal/readme.md
+ ******************************************************************************/
+
+#pragma once
+
+#include <stdbool.h>
+#include "soc/soc_caps.h"
+#include "soc/mcpwm_struct.h"
+#include "soc/clk_tree_defs.h"
+#include "soc/pcr_struct.h"
+#include "hal/mcpwm_types.h"
+#include "hal/misc.h"
+#include "hal/assert.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// Get MCPWM group register base address
+#define MCPWM_LL_GET_HW(ID)                  (((ID) == 0) ? &MCPWM0 : NULL)
+
+// MCPWM interrupt event mask
+#define MCPWM_LL_EVENT_TIMER_STOP(timer)     (1 << (timer))
+#define MCPWM_LL_EVENT_TIMER_EMPTY(timer)    (1 << ((timer) + 3))
+#define MCPWM_LL_EVENT_TIMER_FULL(timer)     (1 << ((timer) + 6))
+#define MCPWM_LL_EVENT_TIMER_MASK(timer)     (MCPWM_LL_EVENT_TIMER_STOP(timer) | MCPWM_LL_EVENT_TIMER_EMPTY(timer) | MCPWM_LL_EVENT_TIMER_FULL(timer))
+#define MCPWM_LL_EVENT_FAULT_ENTER(fault)    (1 << ((fault) + 9))
+#define MCPWM_LL_EVENT_FAULT_EXIT(fault)     (1 << ((fault) + 12))
+#define MCPWM_LL_EVENT_FAULT_MASK(fault)     (MCPWM_LL_EVENT_FAULT_ENTER(fault) | MCPWM_LL_EVENT_FAULT_EXIT(fault))
+#define MCPWM_LL_EVENT_CMP_EQUAL(oper, cmp)  (1 << ((oper) + (cmp) * 3 + 15))
+#define MCPWM_LL_EVENT_OPER_BRAKE_CBC(oper)  (1 << ((oper) + 21))
+#define MCPWM_LL_EVENT_OPER_BRAKE_OST(oper)  (1 << ((oper) + 24))
+#define MCPWM_LL_EVENT_OPER_MASK(oper)       (MCPWM_LL_EVENT_OPER_BRAKE_CBC(oper) | MCPWM_LL_EVENT_OPER_BRAKE_OST(oper))
+#define MCPWM_LL_EVENT_CAPTURE(cap)          (1 << ((cap) + 27))
+
+// Maximum values due to limited register bit width
+#define MCPWM_LL_MAX_CARRIER_ONESHOT         16
+#define MCPWM_LL_MAX_CAPTURE_PRESCALE        256
+#define MCPWM_LL_MAX_DEAD_DELAY              65536
+#define MCPWM_LL_MAX_COUNT_VALUE             65536
+
+// translate the HAL types into register values
+#define MCPWM_LL_TIMER_EVENT_TO_REG_VAL(event) ((uint8_t[]) {0, 1}[(event)])
+#define MCPWM_LL_GEN_ACTION_TO_REG_CAL(action) ((uint8_t[]) {0, 1, 2, 3}[(action)])
+#define MCPWM_LL_BRAKE_MODE_TO_REG_VAL(mode)  ((uint8_t[]) {0, 1}[(mode)])
+
+/**
+ * @brief The dead time module's clock source
+ */
+typedef enum {
+    MCPWM_LL_DEADTIME_CLK_SRC_GROUP,
+    MCPWM_LL_DEADTIME_CLK_SRC_TIMER,
+} mcpwm_ll_deadtime_clock_src_t;
+
+////////////////////////////////////////MCPWM Group Specific////////////////////////////////////////////////////////////
+
+/**
+ * @brief Set the clock source for MCPWM
+ *
+ * @param mcpwm Peripheral instance address
+ * @param clk_src Clock source for the MCPWM peripheral
+ */
+static inline void mcpwm_ll_group_set_clock_source(mcpwm_dev_t *mcpwm, soc_module_clk_t clk_src)
+{
+    (void)mcpwm; // only one MCPWM instance
+    switch (clk_src) {
+    case SOC_MOD_CLK_XTAL:
+        PCR.pwm_clk_conf.pwm_clkm_sel = 0;
+        break;
+    case SOC_MOD_CLK_PLL_F96M:
+        PCR.pwm_clk_conf.pwm_clkm_sel = 2;
+        break;
+    default:
+        HAL_ASSERT(false);
+        break;
+    }
+}
+
+/**
+ * @brief Enable MCPWM module clock
+ *
+ * @param mcpwm Peripheral instance address
+ * @param en true to enable, false to disable
+ */
+static inline void mcpwm_ll_group_enable_clock(mcpwm_dev_t *mcpwm, bool en)
+{
+    (void)mcpwm; // only one MCPWM instance
+    PCR.pwm_clk_conf.pwm_clkm_en = en;
+}
+
+/**
+ * @brief Set the MCPWM group clock prescale
+ *
+ * @param mcpwm Peripheral instance address
+ * @param pre_scale Prescale value
+ */
+static inline void mcpwm_ll_group_set_clock_prescale(mcpwm_dev_t *mcpwm, int pre_scale)
+{
+    (void)mcpwm; // only one MCPWM instance
+    // group clock: PWM_clk = source_clock / (prescale + 1)
+    HAL_FORCE_MODIFY_U32_REG_FIELD(PCR.pwm_clk_conf, pwm_div_num, pre_scale - 1);
+}
+
+/**
+ * @brief Enable update MCPWM active registers from shadow registers
+ *
+ * @param mcpwm Peripheral instance address
+ */
+static inline void mcpwm_ll_group_enable_shadow_mode(mcpwm_dev_t *mcpwm)
+{
+    mcpwm->update_cfg.global_up_en = 1;
+    mcpwm->update_cfg.op0_up_en = 1;
+    mcpwm->update_cfg.op1_up_en = 1;
+    mcpwm->update_cfg.op2_up_en = 1;
+}
+
+/**
+ * @brief Flush shadow registers to active registers
+ *
+ * @param mcpwm Peripheral instance address
+ */
+static inline void mcpwm_ll_group_flush_shadow(mcpwm_dev_t *mcpwm)
+{
+    // a toggle can trigger a forced update of all active registers in MCPWM, i.e. shadow->active
+    mcpwm->update_cfg.global_force_up = 1;
+    mcpwm->update_cfg.global_force_up = 0;
+}
+
+//////////////////////////////////////////Interrupt Specific////////////////////////////////////////////////////////////
+
+/**
+ * @brief Get interrupt status register address
+ *
+ * @param mcpwm Peripheral instance address
+ * @return Register address
+ */
+static inline volatile void *mcpwm_ll_intr_get_status_reg(mcpwm_dev_t *mcpwm)
+{
+    return &mcpwm->int_st;
+}
+
+/**
+ * @brief Enable MCPWM interrupt for specific event mask
+ *
+ * @param mcpwm Peripheral instance address
+ * @param mask Event mask
+ * @param enable True to enable, False to disable
+ */
+static inline void mcpwm_ll_intr_enable(mcpwm_dev_t *mcpwm, uint32_t mask, bool enable)
+{
+    if (enable) {
+        mcpwm->int_ena.val |= mask;
+    } else {
+        mcpwm->int_ena.val &= ~mask;
+    }
+}
+
+/**
+ * @brief Get MCPWM interrupt status
+ *
+ * @param mcpwm Peripheral instance address
+ * @return Interrupt status
+ */
+__attribute__((always_inline))
+static inline uint32_t mcpwm_ll_intr_get_status(mcpwm_dev_t *mcpwm)
+{
+    return mcpwm->int_st.val;
+}
+
+/**
+ * @brief Clear MCPWM interrupt status by mask
+ *
+ * @param mcpwm Peripheral instance address
+ * @param mask Interupt status mask
+ */
+__attribute__((always_inline))
+static inline void mcpwm_ll_intr_clear_status(mcpwm_dev_t *mcpwm, uint32_t mask)
+{
+    mcpwm->int_clr.val = mask;
+}
+
+////////////////////////////////////////MCPWM Timer Specific////////////////////////////////////////////////////////////
+
+/**
+ * @brief Set MCPWM timer prescale
+ *
+ * @param mcpwm Peripheral instance address
+ * @param timer_id Timer ID, index from 0 to 2
+ * @param prescale Prescale value
+ */
+static inline void mcpwm_ll_timer_set_clock_prescale(mcpwm_dev_t *mcpwm, int timer_id, uint32_t prescale)
+{
+    HAL_ASSERT(prescale <= 256 && prescale > 0);
+    HAL_FORCE_MODIFY_U32_REG_FIELD(mcpwm->timer[timer_id].timer_cfg0, timer_prescale, prescale - 1);
+}
+
+/**
+ * @brief Set peak value for MCPWM timer
+ *
+ * @param mcpwm Peripheral instance address
+ * @param timer_id Timer ID, index from 0 to 2
+ * @param peak Peak value
+ * @param symmetric True to set symmetric peak value, False to set asymmetric peak value
+ */
+static inline void mcpwm_ll_timer_set_peak(mcpwm_dev_t *mcpwm, int timer_id, uint32_t peak, bool symmetric)
+{
+    if (!symmetric) { // in asymmetric mode, period = [0,peak-1]
+        HAL_ASSERT(peak > 0 && peak <= MCPWM_LL_MAX_COUNT_VALUE);
+        HAL_FORCE_MODIFY_U32_REG_FIELD(mcpwm->timer[timer_id].timer_cfg0, timer_period, peak - 1);
+    } else { // in symmetric mode, period = [0,peak-1] + [peak,1]
+        HAL_ASSERT(peak < MCPWM_LL_MAX_COUNT_VALUE);
+        HAL_FORCE_MODIFY_U32_REG_FIELD(mcpwm->timer[timer_id].timer_cfg0, timer_period, peak);
+    }
+}
+
+/**
+ * @brief Update MCPWM period immediately
+ * @note When period value is updated in the shadow register, it will be flushed to active register immediately.
+ *
+ * @param mcpwm Peripheral instance address
+ * @param timer_id Timer ID, index from 0 to 2
+ */
+static inline void mcpwm_ll_timer_update_period_at_once(mcpwm_dev_t *mcpwm, int timer_id)
+{
+    mcpwm->timer[timer_id].timer_cfg0.timer_period_upmethod = 0;
+}
+
+/**
+ * @brief Enable to update MCPWM period upon TEZ event
+ *
+ * @param mcpwm Peripheral instance address
+ * @param timer_id Timer ID, index from 0 to 2
+ * @param enable True to enable, False to disable
+ */
+static inline void mcpwm_ll_timer_enable_update_period_on_tez(mcpwm_dev_t *mcpwm, int timer_id, bool enable)
+{
+    if (enable) {
+        mcpwm->timer[timer_id].timer_cfg0.timer_period_upmethod |= 0x01;
+    } else {
+        mcpwm->timer[timer_id].timer_cfg0.timer_period_upmethod &= ~0x01;
+    }
+}
+
+/**
+ * @brief Enable to update MCPWM period upon sync event
+ *
+ * @param mcpwm Peripheral instance address
+ * @param timer_id Timer ID, index from 0 to 2
+ * @param enable True to enable, False to disable
+ */
+static inline void mcpwm_ll_timer_enable_update_period_on_sync(mcpwm_dev_t *mcpwm, int timer_id, bool enable)
+{
+    if (enable) {
+        mcpwm->timer[timer_id].timer_cfg0.timer_period_upmethod |= 0x02;
+    } else {
+        mcpwm->timer[timer_id].timer_cfg0.timer_period_upmethod &= ~0x02;
+    }
+}
+
+/**
+ * @brief Set MCPWM timer count mode
+ *
+ * @param mcpwm Peripheral instance address
+ * @param timer_id Timer ID, index from 0 to 2
+ * @param mode Timer count mode
+ */
+static inline void mcpwm_ll_timer_set_count_mode(mcpwm_dev_t *mcpwm, int timer_id, mcpwm_timer_count_mode_t mode)
+{
+    switch (mode) {
+    case MCPWM_TIMER_COUNT_MODE_PAUSE:
+        mcpwm->timer[timer_id].timer_cfg1.timer_mod = 0;
+        break;
+    case MCPWM_TIMER_COUNT_MODE_UP:
+        mcpwm->timer[timer_id].timer_cfg1.timer_mod = 1;
+        break;
+    case MCPWM_TIMER_COUNT_MODE_DOWN:
+        mcpwm->timer[timer_id].timer_cfg1.timer_mod = 2;
+        break;
+    case MCPWM_TIMER_COUNT_MODE_UP_DOWN:
+        mcpwm->timer[timer_id].timer_cfg1.timer_mod = 3;
+        break;
+    default:
+        HAL_ASSERT(false);
+        break;
+    }
+}
+
+/**
+ * @brief Execute MCPWM timer start/stop command
+ *
+ * @param mcpwm Peripheral instance address
+ * @param timer_id  Timer ID, index from 0 to 2
+ * @param cmd Timer start/stop command
+ */
+static inline void mcpwm_ll_timer_set_start_stop_command(mcpwm_dev_t *mcpwm, int timer_id, mcpwm_timer_start_stop_cmd_t cmd)
+{
+    switch (cmd) {
+    case MCPWM_TIMER_STOP_EMPTY:
+        mcpwm->timer[timer_id].timer_cfg1.timer_start = 0;
+        break;
+    case MCPWM_TIMER_STOP_FULL:
+        mcpwm->timer[timer_id].timer_cfg1.timer_start = 1;
+        break;
+    case MCPWM_TIMER_START_NO_STOP:
+        mcpwm->timer[timer_id].timer_cfg1.timer_start = 2;
+        break;
+    case MCPWM_TIMER_START_STOP_EMPTY:
+        mcpwm->timer[timer_id].timer_cfg1.timer_start = 3;
+        break;
+    case MCPWM_TIMER_START_STOP_FULL:
+        mcpwm->timer[timer_id].timer_cfg1.timer_start = 4;
+        break;
+    default:
+        HAL_ASSERT(false);
+        break;;
+    }
+}
+
+/**
+ * @brief Get timer count value
+ *
+ * @param mcpwm Peripheral instance address
+ * @param timer_id Timer ID, index from 0 to 2
+ * @return Timer count value
+ */
+__attribute__((always_inline))
+static inline uint32_t mcpwm_ll_timer_get_count_value(mcpwm_dev_t *mcpwm, int timer_id)
+{
+    // 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].timer_cfg1.timer_mod == 0) {
+        return HAL_FORCE_READ_U32_REG_FIELD(mcpwm->timer[timer_id].timer_status, timer_value);
+    }
+    if (mcpwm->timer[timer_id].timer_status.timer_direction) { // down direction
+        return (HAL_FORCE_READ_U32_REG_FIELD(mcpwm->timer[timer_id].timer_status, timer_value) + 1) %
+               (HAL_FORCE_READ_U32_REG_FIELD(mcpwm->timer[timer_id].timer_cfg0, timer_period) + 1);
+    }
+    // up direction
+    return (HAL_FORCE_READ_U32_REG_FIELD(mcpwm->timer[timer_id].timer_status, timer_value) +
+            HAL_FORCE_READ_U32_REG_FIELD(mcpwm->timer[timer_id].timer_cfg0, timer_period)) %
+           (HAL_FORCE_READ_U32_REG_FIELD(mcpwm->timer[timer_id].timer_cfg0, timer_period) + 1);
+}
+
+/**
+ * @brief Get timer count direction
+ *
+ * @param mcpwm Peripheral instance address
+ * @param timer_id Timer ID, index from 0 to 2
+ * @return Timer count direction
+ */
+__attribute__((always_inline))
+static inline mcpwm_timer_direction_t mcpwm_ll_timer_get_count_direction(mcpwm_dev_t *mcpwm, int timer_id)
+{
+    return mcpwm->timer[timer_id].timer_status.timer_direction ? MCPWM_TIMER_DIRECTION_DOWN : MCPWM_TIMER_DIRECTION_UP;
+}
+
+/**
+ * @brief Enable sync input for timer
+ *
+ * @param mcpwm Peripheral instance address
+ * @param timer_id Timer ID, index from 0 to 2
+ * @param enable True to enable, False to disable
+ */
+static inline void mcpwm_ll_timer_enable_sync_input(mcpwm_dev_t *mcpwm, int timer_id, bool enable)
+{
+    mcpwm->timer[timer_id].timer_sync.timer_synci_en = enable;
+}
+
+/**
+ * @brief Use the input sync signal as the output sync signal (i.e. propagate the input sync signal)
+ *
+ * @param mcpwm Peripheral instance address
+ * @param timer_id Timer ID, index from 0 to 2
+ */
+static inline void mcpwm_ll_timer_propagate_input_sync(mcpwm_dev_t *mcpwm, int timer_id)
+{
+    // sync_out is selected to sync_in
+    mcpwm->timer[timer_id].timer_sync.timer_synco_sel = 0;
+}
+
+/**
+ * @brief Set the sync output signal to one of the timer event
+ *
+ * @param mcpwm Peripheral instance address
+ * @param timer_id Timer ID, index from 0 to 2
+ * @param event Timer event
+ */
+static inline void mcpwm_ll_timer_sync_out_on_timer_event(mcpwm_dev_t *mcpwm, int timer_id, mcpwm_timer_event_t event)
+{
+    switch (event) {
+    case MCPWM_TIMER_EVENT_EMPTY:
+        mcpwm->timer[timer_id].timer_sync.timer_synco_sel = 1;
+        break;
+    case MCPWM_TIMER_EVENT_FULL:
+        mcpwm->timer[timer_id].timer_sync.timer_synco_sel = 2;
+        break;
+    default:
+        HAL_ASSERT(false);
+        break;
+    }
+}
+
+/**
+ * @brief Disable sync output for MCPWM timer
+ *
+ * @param mcpwm Peripheral instance address
+ * @param timer_id Timer ID, index from 0 to 2
+ */
+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].timer_sync.timer_synco_sel = 3;
+}
+
+/**
+ * @brief Trigger MCPWM timer software sync event
+ *
+ * @param mcpwm Peripheral instance address
+ * @param timer_id Timer ID, index from 0 to 2
+ */
+static inline void mcpwm_ll_timer_trigger_soft_sync(mcpwm_dev_t *mcpwm, int timer_id)
+{
+    mcpwm->timer[timer_id].timer_sync.timer_sync_sw = ~mcpwm->timer[timer_id].timer_sync.timer_sync_sw;
+}
+
+/**
+ * @brief Set sync count value for MCPWM timer
+ *
+ * @param mcpwm Peripheral instance address
+ * @param timer_id Timer ID, index from 0 to 2
+ * @param phase_value Sync phase value
+ */
+static inline void mcpwm_ll_timer_set_sync_phase_value(mcpwm_dev_t *mcpwm, int timer_id, uint32_t phase_value)
+{
+    HAL_FORCE_MODIFY_U32_REG_FIELD(mcpwm->timer[timer_id].timer_sync, timer_phase, phase_value);
+}
+
+/**
+ * @brief Set sync phase direction for MCPWM timer
+ *
+ * @param mcpwm Peripheral instance address
+ * @param timer_id Timer ID, index from 0 to 2
+ * @param direction Sync phase direction
+ */
+static inline void mcpwm_ll_timer_set_sync_phase_direction(mcpwm_dev_t *mcpwm, int timer_id, mcpwm_timer_direction_t direction)
+{
+    mcpwm->timer[timer_id].timer_sync.timer_phase_direction = direction;
+}
+
+/**
+ * @brief Select which GPIO sync input to use
+ *
+ * @param mcpwm Peripheral instance address
+ * @param timer Timer ID, index from 0 to 2
+ * @param gpio_sync_id GPIO sync ID, index from 0 to 2
+ */
+static inline void mcpwm_ll_timer_set_gpio_sync_input(mcpwm_dev_t *mcpwm, int timer, int gpio_sync_id)
+{
+    mcpwm->timer_synci_cfg.val &= ~(0x07 << (timer * 3));
+    mcpwm->timer_synci_cfg.val |= (gpio_sync_id + 4) << (timer * 3);
+}
+
+/**
+ * @brief Select which timer sync input to use
+ *
+ * @param mcpwm Peripheral instance address
+ * @param timer Timer ID, index from 0 to 2
+ * @param timer_sync_id Timer sync ID, index from 0 to 2
+ */
+static inline void mcpwm_ll_timer_set_timer_sync_input(mcpwm_dev_t *mcpwm, int timer, int timer_sync_id)
+{
+    mcpwm->timer_synci_cfg.val &= ~(0x07 << (timer * 3));
+    mcpwm->timer_synci_cfg.val |= (timer_sync_id + 1) << (timer * 3);
+}
+
+/**
+ * @brief Clear timer sync input selection
+ *
+ * @param mcpwm Peripheral instance address
+ * @param timer Timer ID, index from 0 to 2
+ */
+static inline void mcpwm_ll_timer_clear_sync_input(mcpwm_dev_t *mcpwm, int timer)
+{
+    // no sync input is selected, but software sync can still work
+    mcpwm->timer_synci_cfg.val &= ~(0x07 << (timer * 3));
+}
+
+/**
+ * @brief Invert the GPIO sync input signal
+ *
+ * @param mcpwm Peripheral instance address
+ * @param sync_id GPIO sync ID, index from 0 to 2
+ * @param invert True to invert, False to not invert
+ */
+static inline void mcpwm_ll_invert_gpio_sync_input(mcpwm_dev_t *mcpwm, int sync_id, bool invert)
+{
+    if (invert) {
+        mcpwm->timer_synci_cfg.val |= 1 << (sync_id + 9);
+    } else {
+        mcpwm->timer_synci_cfg.val &= ~(1 << (sync_id + 9));
+    }
+}
+
+////////////////////////////////////////MCPWM Operator Specific/////////////////////////////////////////////////////////
+
+/**
+ * @brief Flush operator shadow registers to active registers
+ *
+ * @param mcpwm Peripheral instance address
+ * @param operator_id Operator ID, index from 0 to 2
+ */
+static inline void mcpwm_ll_operator_flush_shadow(mcpwm_dev_t *mcpwm, int operator_id)
+{
+    mcpwm->update_cfg.val ^= (1 << (2 * operator_id + 3));
+}
+
+/**
+ * @brief Connect operator and timer by ID
+ *
+ * @param mcpwm Peripheral instance address
+ * @param operator_id Operator ID, index from 0 to 2
+ * @param timer_id Timer ID, index from 0 to 2
+ */
+static inline void mcpwm_ll_operator_connect_timer(mcpwm_dev_t *mcpwm, int operator_id, int timer_id)
+{
+    mcpwm->operator_timersel.val &= ~(0x03 << (2 * operator_id));
+    mcpwm->operator_timersel.val |= (timer_id << (2 * operator_id));
+}
+
+/**
+ * @brief Update the compare value immediately
+ *
+ * @param mcpwm Peripheral instance address
+ * @param operator_id Operator ID, index from 0 to 2
+ * @param compare_id Compare ID, index from 0 to 1
+ */
+static inline void mcpwm_ll_operator_update_compare_at_once(mcpwm_dev_t *mcpwm, int operator_id, int compare_id)
+{
+    mcpwm->operators[operator_id].gen_stmp_cfg.val &= ~(0x0F << (4 * compare_id));
+}
+
+/**
+ * @brief Enable to update the compare value upon TEZ event
+ *
+ * @param mcpwm Peripheral instance address
+ * @param operator_id Operator ID, index from 0 to 2
+ * @param compare_id Compare ID, index from 0 to 1
+ * @param enable True to enable, False to disable
+ */
+static inline void mcpwm_ll_operator_enable_update_compare_on_tez(mcpwm_dev_t *mcpwm, int operator_id, int compare_id, bool enable)
+{
+    if (enable) {
+        mcpwm->operators[operator_id].gen_stmp_cfg.val |= (1 << 0) << (4 * compare_id);
+    } else {
+        mcpwm->operators[operator_id].gen_stmp_cfg.val &= ~((1 << 0) << (4 * compare_id));
+    }
+}
+
+/**
+ * @brief Enable to update the compare value upon TEP event
+ *
+ * @param mcpwm Peripheral instance address
+ * @param operator_id Operator ID, index from 0 to 2
+ * @param compare_id Compare ID, index from 0 to 1
+ * @param enable True to enable, False to disable
+ */
+static inline void mcpwm_ll_operator_enable_update_compare_on_tep(mcpwm_dev_t *mcpwm, int operator_id, int compare_id, bool enable)
+{
+    if (enable) {
+        mcpwm->operators[operator_id].gen_stmp_cfg.val |= (1 << 1) << (4 * compare_id);
+    } else {
+        mcpwm->operators[operator_id].gen_stmp_cfg.val &= ~((1 << 1) << (4 * compare_id));
+    }
+}
+
+/**
+ * @brief Enable to update the compare value upon sync event
+ *
+ * @param mcpwm Peripheral instance address
+ * @param operator_id Operator ID, index from 0 to 2
+ * @param compare_id Compare ID, index from 0 to 1
+ * @param enable True to enable, False to disable
+ */
+static inline void mcpwm_ll_operator_enable_update_compare_on_sync(mcpwm_dev_t *mcpwm, int operator_id, int compare_id, bool enable)
+{
+    if (enable) {
+        mcpwm->operators[operator_id].gen_stmp_cfg.val |= (1 << 2) << (4 * compare_id);
+    } else {
+        mcpwm->operators[operator_id].gen_stmp_cfg.val &= ~((1 << 2) << (4 * compare_id));
+    }
+}
+
+/**
+ * @brief Stop updating the compare value
+ *
+ * @param mcpwm Peripheral instance address
+ * @param operator_id Operator ID, index from 0 to 2
+ * @param compare_id Compare ID, index from 0 to 1
+ * @param stop_or_not True to stop, False to not stop
+ */
+static inline void mcpwm_ll_operator_stop_update_compare(mcpwm_dev_t *mcpwm, int operator_id, int compare_id, bool stop_or_not)
+{
+    if (stop_or_not) {
+        mcpwm->operators[operator_id].gen_stmp_cfg.val |= (1 << 3) << (4 * compare_id);
+    } else {
+        mcpwm->operators[operator_id].gen_stmp_cfg.val &= ~((1 << 3) << (4 * compare_id));
+    }
+}
+
+/**
+ * @brief Set compare value for comparator
+ *
+ * @param mcpwm Peripheral instance address
+ * @param operator_id Operator ID, index from 0 to 2
+ * @param compare_id Compare ID, index from 0 to 1
+ * @param compare_value Compare value
+ */
+__attribute__((always_inline))
+static inline void mcpwm_ll_operator_set_compare_value(mcpwm_dev_t *mcpwm, int operator_id, int compare_id, uint32_t compare_value)
+{
+    HAL_FORCE_MODIFY_U32_REG_FIELD(mcpwm->operators[operator_id].timestamp[compare_id], cmpr, compare_value);
+}
+
+/**
+ * @brief Update operator actions immediately
+ *
+ * @param mcpwm Peripheral instance address
+ * @param operator_id Operator ID, index from 0 to 2
+ */
+static inline void mcpwm_ll_operator_update_action_at_once(mcpwm_dev_t *mcpwm, int operator_id)
+{
+    mcpwm->operators[operator_id].gen_cfg0.gen_cfg_upmethod = 0;
+}
+
+/**
+ * @brief Enable update actions on TEZ event
+ *
+ * @param mcpwm Peripheral instance address
+ * @param operator_id Operator ID, index from 0 to 2
+ * @param enable True to enable, False to disable
+ */
+static inline void mcpwm_ll_operator_enable_update_action_on_tez(mcpwm_dev_t *mcpwm, int operator_id, bool enable)
+{
+    if (enable) {
+        mcpwm->operators[operator_id].gen_cfg0.gen_cfg_upmethod |= 1 << 0;
+    } else {
+        mcpwm->operators[operator_id].gen_cfg0.gen_cfg_upmethod &= ~(1 << 0);
+    }
+}
+
+/**
+ * @brief Enable update actions on TEP event
+ *
+ * @param mcpwm Peripheral instance address
+ * @param operator_id Operator ID, index from 0 to 2
+ * @param enable True to enable, False to disable
+ */
+static inline void mcpwm_ll_operator_enable_update_action_on_tep(mcpwm_dev_t *mcpwm, int operator_id, bool enable)
+{
+    if (enable) {
+        mcpwm->operators[operator_id].gen_cfg0.gen_cfg_upmethod |= 1 << 1;
+    } else {
+        mcpwm->operators[operator_id].gen_cfg0.gen_cfg_upmethod &= ~(1 << 1);
+    }
+}
+
+/**
+ * @brief Enable update actions on sync event
+ *
+ * @param mcpwm Peripheral instance address
+ * @param operator_id Operator ID, index from 0 to 2
+ * @param enable True to enable, False to disable
+ */
+static inline void mcpwm_ll_operator_enable_update_action_on_sync(mcpwm_dev_t *mcpwm, int operator_id, bool enable)
+{
+    if (enable) {
+        mcpwm->operators[operator_id].gen_cfg0.gen_cfg_upmethod |= 1 << 2;
+    } else {
+        mcpwm->operators[operator_id].gen_cfg0.gen_cfg_upmethod &= ~(1 << 2);
+    }
+}
+
+/**
+ * @brief Stop updating actions
+ *
+ * @param mcpwm Peripheral instance address
+ * @param operator_id Operator ID, index from 0 to 2
+ * @param stop_or_not True to stop, False to not stop
+ */
+static inline void mcpwm_ll_operator_stop_update_action(mcpwm_dev_t *mcpwm, int operator_id, bool stop_or_not)
+{
+    if (stop_or_not) {
+        mcpwm->operators[operator_id].gen_cfg0.gen_cfg_upmethod |= 1 << 3;
+    } else {
+        mcpwm->operators[operator_id].gen_cfg0.gen_cfg_upmethod &= ~(1 << 3);
+    }
+}
+
+/**
+ * @brief Set trigger from GPIO (reuse the fault GPIO)
+ *
+ * @param mcpwm Peripheral instance address
+ * @param operator_id Operator ID, index from 0 to 2
+ * @param trig_id Trigger ID, index from 0 to 1
+ * @param fault_gpio_id Fault GPIO ID, index from 0 to 3
+ */
+static inline void mcpwm_ll_operator_set_trigger_from_gpio(mcpwm_dev_t *mcpwm, int operator_id, int trig_id, int fault_gpio_id)
+{
+    mcpwm->operators[operator_id].gen_cfg0.val &= ~(0x07 << (4 + 3 * trig_id));
+    mcpwm->operators[operator_id].gen_cfg0.val |= (fault_gpio_id << (4 + 3 * trig_id));
+}
+
+/**
+ * @brief Set trigger from timer sync event (when the timer taken the sync signal)
+ *
+ * @param mcpwm Peripheral instance address
+ * @param operator_id Operator ID, index from 0 to 2
+ * @param trig_id Trigger ID, index from 0 to 1
+ */
+static inline void mcpwm_ll_operator_set_trigger_from_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->operators[operator_id].gen_cfg0.val &= ~(0x07 << (4 + 3 * trig_id));
+    mcpwm->operators[operator_id].gen_cfg0.val |= (3 << (4 + 3 * trig_id));
+}
+
+////////////////////////////////////////MCPWM Generator Specific////////////////////////////////////////////////////////
+
+/**
+ * @brief Reset actions for the generator
+ *
+ * @param mcpwm Peripheral instance address
+ * @param operator_id Operator ID, index from 0 to 2
+ * @param generator_id Generator ID, index from 0 to 1
+ */
+static inline void mcpwm_ll_generator_reset_actions(mcpwm_dev_t *mcpwm, int operator_id, int generator_id)
+{
+    mcpwm->operators[operator_id].generator[generator_id].val = 0;
+}
+
+/**
+ * @brief Set generator action on timer event
+ *
+ * @param mcpwm Peripheral instance address
+ * @param operator_id Operator ID, index from 0 to 2
+ * @param generator_id Generator ID, index from 0 to 1
+ * @param direction Timer direction
+ * @param event Timer event
+ * @param action Action to set
+ */
+static inline void mcpwm_ll_generator_set_action_on_timer_event(mcpwm_dev_t *mcpwm, int operator_id, int generator_id,
+        mcpwm_timer_direction_t direction, mcpwm_timer_event_t event, mcpwm_generator_action_t action)
+{
+    // empty: 0, full: 1
+    if (direction == MCPWM_TIMER_DIRECTION_UP) { // utez, utep
+        mcpwm->operators[operator_id].generator[generator_id].val &= ~(0x03 << (MCPWM_LL_TIMER_EVENT_TO_REG_VAL(event) * 2));
+        mcpwm->operators[operator_id].generator[generator_id].val |= MCPWM_LL_GEN_ACTION_TO_REG_CAL(action) << (MCPWM_LL_TIMER_EVENT_TO_REG_VAL(event) * 2);
+    } else if (direction == MCPWM_TIMER_DIRECTION_DOWN) { // dtez, dtep
+        mcpwm->operators[operator_id].generator[generator_id].val &= ~(0x03 << (MCPWM_LL_TIMER_EVENT_TO_REG_VAL(event) * 2 + 12));
+        mcpwm->operators[operator_id].generator[generator_id].val |= MCPWM_LL_GEN_ACTION_TO_REG_CAL(action) << (MCPWM_LL_TIMER_EVENT_TO_REG_VAL(event) * 2 + 12);
+    }
+}
+
+/**
+ * @brief Set generator action on compare event
+ *
+ * @param mcpwm Peripheral instance address
+ * @param operator_id Operator ID, index from 0 to 2
+ * @param generator_id Generator ID, index from 0 to 1
+ * @param direction Timer direction
+ * @param comp_id Compare ID, index from 0 to 1
+ * @param action Action to set
+ */
+static inline void mcpwm_ll_generator_set_action_on_compare_event(mcpwm_dev_t *mcpwm, int operator_id, int generator_id,
+        mcpwm_timer_direction_t direction, int cmp_id, int action)
+{
+    if (direction == MCPWM_TIMER_DIRECTION_UP) { // utea, uteb
+        mcpwm->operators[operator_id].generator[generator_id].val &= ~(0x03 << (cmp_id * 2 + 4));
+        mcpwm->operators[operator_id].generator[generator_id].val |= MCPWM_LL_GEN_ACTION_TO_REG_CAL(action) << (cmp_id * 2 + 4);
+    } else if (direction == MCPWM_TIMER_DIRECTION_DOWN) { // dtea, dteb
+        mcpwm->operators[operator_id].generator[generator_id].val &= ~(0x03 << (cmp_id * 2 + 16));
+        mcpwm->operators[operator_id].generator[generator_id].val |= MCPWM_LL_GEN_ACTION_TO_REG_CAL(action) << (cmp_id * 2 + 16);
+    }
+}
+
+/**
+ * @brief Set generator action on trigger event
+ *
+ * @param mcpwm Peripheral instance address
+ * @param operator_id Operator ID, index from 0 to 2
+ * @param generator_id Generator ID, index from 0 to 1
+ * @param direction Timer direction
+ * @param trig_id Trigger ID, index from 0 to 1
+ * @param action Action to set
+ */
+static inline void mcpwm_ll_generator_set_action_on_trigger_event(mcpwm_dev_t *mcpwm, int operator_id, int generator_id,
+        mcpwm_timer_direction_t direction, int trig_id, int action)
+{
+    if (direction == MCPWM_TIMER_DIRECTION_UP) { // ut0, ut1
+        mcpwm->operators[operator_id].generator[generator_id].val &= ~(0x03 << (trig_id * 2 + 8));
+        mcpwm->operators[operator_id].generator[generator_id].val |= MCPWM_LL_GEN_ACTION_TO_REG_CAL(action) << (trig_id * 2 + 8);
+    } else if (direction == MCPWM_TIMER_DIRECTION_DOWN) { // dt0, dt1
+        mcpwm->operators[operator_id].generator[generator_id].val &= ~(0x03 << (trig_id * 2 + 20));
+        mcpwm->operators[operator_id].generator[generator_id].val |= MCPWM_LL_GEN_ACTION_TO_REG_CAL(action) << (trig_id * 2 + 20);
+    }
+}
+
+/**
+ * @brief Set generator action on brake event
+ *
+ * @param mcpwm Peripheral instance address
+ * @param operator_id Operator ID, index from 0 to 2
+ * @param generator_id Generator ID, index from 0 to 1
+ * @param direction Timer direction
+ * @param brake_mode Brake mode
+ * @param action Action to set
+ */
+static inline void mcpwm_ll_generator_set_action_on_brake_event(mcpwm_dev_t *mcpwm, int operator_id, int generator_id,
+        mcpwm_timer_direction_t direction, mcpwm_operator_brake_mode_t brake_mode, int action)
+{
+    // the following bit operation is highly depend on the register bit layout.
+    // the priority comes: generator ID > brake mode > direction
+    if (direction == MCPWM_TIMER_DIRECTION_UP) {
+        mcpwm->operators[operator_id].fh_cfg0.val &= ~(0x03 << (8 + 8 * generator_id + 4 * MCPWM_LL_BRAKE_MODE_TO_REG_VAL(brake_mode) + 2));
+        mcpwm->operators[operator_id].fh_cfg0.val |= MCPWM_LL_GEN_ACTION_TO_REG_CAL(action) << (8 + 8 * generator_id + 4 * MCPWM_LL_BRAKE_MODE_TO_REG_VAL(brake_mode) + 2);
+    } else if (direction == MCPWM_TIMER_DIRECTION_DOWN) {
+        mcpwm->operators[operator_id].fh_cfg0.val &= ~(0x03 << (8 + 8 * generator_id + 4 * MCPWM_LL_BRAKE_MODE_TO_REG_VAL(brake_mode)));
+        mcpwm->operators[operator_id].fh_cfg0.val |= MCPWM_LL_GEN_ACTION_TO_REG_CAL(action) << (8 + 8 * generator_id + 4 * MCPWM_LL_BRAKE_MODE_TO_REG_VAL(brake_mode));
+    }
+}
+
+/**
+ * @brief Trigger non-continue forced action for generator
+ *
+ * @param mcpwm Peripheral instance address
+ * @param operator_id Operator ID, index from 0 to 2
+ * @param generator_id Generator ID, index from 0 to 1
+ */
+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->operators[operator_id].gen_force.gen_a_nciforce = ~mcpwm->operators[operator_id].gen_force.gen_a_nciforce;
+    } else {
+        mcpwm->operators[operator_id].gen_force.gen_b_nciforce = ~mcpwm->operators[operator_id].gen_force.gen_b_nciforce;
+    }
+}
+
+/**
+ * @brief Disable continue forced action for generator
+ *
+ * @param mcpwm Peripheral instance address
+ * @param operator_id Operator ID, index from 0 to 2
+ * @param generator_id Generator ID, index from 0 to 1
+ */
+static inline void mcpwm_ll_gen_disable_continue_force_action(mcpwm_dev_t *mcpwm, int operator_id, int generator_id)
+{
+    mcpwm->operators[operator_id].gen_force.gen_cntuforce_upmethod = 0; // update force method immediately
+    if (generator_id == 0) {
+        mcpwm->operators[operator_id].gen_force.gen_a_cntuforce_mode = 0;
+    } else {
+        mcpwm->operators[operator_id].gen_force.gen_b_cntuforce_mode = 0;
+    }
+}
+
+/**
+ * @brief Disable non-continue forced action for generator
+ *
+ * @param mcpwm Peripheral instance address
+ * @param operator_id Operator ID, index from 0 to 2
+ * @param generator_id Generator ID, index from 0 to 1
+ */
+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->operators[operator_id].gen_force.gen_a_nciforce_mode = 0;
+    } else {
+        mcpwm->operators[operator_id].gen_force.gen_b_nciforce_mode = 0;
+    }
+}
+
+/**
+ * @brief Set continue force level for generator
+ *
+ * @param mcpwm Peripheral instance address
+ * @param operator_id Operator ID, index from 0 to 2
+ * @param generator_id Generator ID, index from 0 to 1
+ * @param level Force level to set
+ */
+static inline void mcpwm_ll_gen_set_continue_force_level(mcpwm_dev_t *mcpwm, int operator_id, int generator_id, int level)
+{
+    mcpwm->operators[operator_id].gen_force.gen_cntuforce_upmethod = 0; // update force method immediately
+    if (generator_id == 0) {
+        mcpwm->operators[operator_id].gen_force.gen_a_cntuforce_mode = level + 1;
+    } else {
+        mcpwm->operators[operator_id].gen_force.gen_b_cntuforce_mode = level + 1;
+    }
+}
+
+/**
+ * @brief Set non-continue force level for generator
+ *
+ * @param mcpwm Peripheral instance address
+ * @param operator_id Operator ID, index from 0 to 2
+ * @param generator_id Generator ID, index from 0 to 1
+ * @param level Force level to set
+ */
+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->operators[operator_id].gen_force.gen_a_nciforce_mode = level + 1;
+    } else {
+        mcpwm->operators[operator_id].gen_force.gen_b_nciforce_mode = level + 1;
+    }
+}
+
+////////////////////////////////////////MCPWM Dead Time Specific////////////////////////////////////////////////////////
+
+/**
+ * @brief Set clock source for dead time submodule
+ *
+ * @param mcpwm Peripheral instance address
+ * @param operator_id Operator ID, index from 0 to 2
+ * @param src Clock source for dead time submodule
+ */
+static inline void mcpwm_ll_operator_set_deadtime_clock_src(mcpwm_dev_t *mcpwm, int operator_id, mcpwm_ll_deadtime_clock_src_t src)
+{
+    switch (src) {
+    case MCPWM_LL_DEADTIME_CLK_SRC_GROUP:
+        mcpwm->operators[operator_id].dt_cfg.db_clk_sel = 0;
+        break;
+    case MCPWM_LL_DEADTIME_CLK_SRC_TIMER:
+        mcpwm->operators[operator_id].dt_cfg.db_clk_sel = 1;
+        break;;
+    default:
+        HAL_ASSERT(false);
+    }
+}
+
+/**
+ * @brief Select the generator for RED block
+ *
+ * @param mcpwm Peripheral instance address
+ * @param operator_id Operator ID, index from 0 to 2
+ * @param generator Generator ID, index from 0 to 1
+ */
+static inline void mcpwm_ll_deadtime_red_select_generator(mcpwm_dev_t *mcpwm, int operator_id, int generator)
+{
+    mcpwm->operators[operator_id].dt_cfg.db_red_insel = generator;
+}
+
+/**
+ * @brief Select the generator for FED block
+ *
+ * @param mcpwm Peripheral instance address
+ * @param operator_id Operator ID, index from 0 to 2
+ * @param generator Generator ID, index from 0 to 1
+ */
+static inline void mcpwm_ll_deadtime_fed_select_generator(mcpwm_dev_t *mcpwm, int operator_id, int generator)
+{
+    mcpwm->operators[operator_id].dt_cfg.db_fed_insel = generator;
+}
+
+/**
+ * @brief Set which path to bypass in the deadtime submodule
+ *
+ * @param mcpwm Peripheral instance address
+ * @param operator_id Operator ID, index from 0 to 2
+ * @param path Path to bypass, index from 0 to 1
+ * @param bypass True to bypass, False to not bypass
+ */
+static inline void mcpwm_ll_deadtime_bypass_path(mcpwm_dev_t *mcpwm, int operator_id, int path, bool bypass)
+{
+    if (bypass) {
+        mcpwm->operators[operator_id].dt_cfg.val |= 1 << (path + 15);
+    } else {
+        mcpwm->operators[operator_id].dt_cfg.val &= ~(1 << (path + 15));
+    }
+}
+
+/**
+ * @brief Invert the output path
+ *
+ * @param mcpwm Peripheral instance address
+ * @param operator_id Operator ID, index from 0 to 2
+ * @param path Path to invert, index from 0 to 1
+ * @param invert True to invert, False to not invert
+ */
+static inline void mcpwm_ll_deadtime_invert_outpath(mcpwm_dev_t *mcpwm, int operator_id, int path, bool invert)
+{
+    if (invert) {
+        mcpwm->operators[operator_id].dt_cfg.val |= 1 << (path + 13);
+    } else {
+        mcpwm->operators[operator_id].dt_cfg.val &= ~(1 << (path + 13));
+    }
+}
+
+/**
+ * @brief Swap the output path
+ *
+ * @param mcpwm Peripheral instance address
+ * @param operator_id Operator ID, index from 0 to 2
+ * @param path Path to swap, index from 0 to 1
+ * @param swap True to swap, False to not swap
+ */
+static inline void mcpwm_ll_deadtime_swap_out_path(mcpwm_dev_t *mcpwm, int operator_id, int path, bool swap)
+{
+    if (swap) {
+        mcpwm->operators[operator_id].dt_cfg.val |= 1 << (path + 9);
+    } else {
+        mcpwm->operators[operator_id].dt_cfg.val &= ~(1 << (path + 9));
+    }
+}
+
+/**
+ * @brief Enable the DEB block
+ *
+ * @param mcpwm Peripheral instance address
+ * @param operator_id Operator ID, index from 0 to 2
+ * @param enable True to enable, False to disable
+ */
+static inline void mcpwm_ll_deadtime_enable_deb(mcpwm_dev_t *mcpwm, int operator_id, bool enable)
+{
+    mcpwm->operators[operator_id].dt_cfg.db_deb_mode = enable;
+}
+
+/**
+ * @brief Get the deadtime switch topology
+ *
+ * @param mcpwm Peripheral instance address
+ * @param operator_id Operator ID, index from 0 to 2
+ * @return Dead time submodule's switch topology, each bit represents one switch on/off status
+ */
+static inline uint32_t mcpwm_ll_deadtime_get_switch_topology(mcpwm_dev_t *mcpwm, int operator_id)
+{
+    return (mcpwm->operators[operator_id].dt_cfg.db_deb_mode << 8) | (mcpwm->operators[operator_id].dt_cfg.db_b_outswap << 7) |
+           (mcpwm->operators[operator_id].dt_cfg.db_a_outswap << 6) | (mcpwm->operators[operator_id].dt_cfg.db_fed_insel << 5) |
+           (mcpwm->operators[operator_id].dt_cfg.db_red_insel << 4) | (mcpwm->operators[operator_id].dt_cfg.db_fed_outinvert << 3) |
+           (mcpwm->operators[operator_id].dt_cfg.db_red_outinvert << 2) | (mcpwm->operators[operator_id].dt_cfg.db_a_outbypass << 1) |
+           (mcpwm->operators[operator_id].dt_cfg.db_b_outbypass << 0);
+}
+
+/**
+ * @brief Set falling edge delay duration
+ *
+ * @param mcpwm Peripheral instance address
+ * @param operator_id Operator ID, index from 0 to 2
+ * @param fed Delay duration, in deadtime submodule's clock cycles
+ */
+static inline void mcpwm_ll_deadtime_set_falling_delay(mcpwm_dev_t *mcpwm, int operator_id, uint32_t fed)
+{
+    HAL_FORCE_MODIFY_U32_REG_FIELD(mcpwm->operators[operator_id].dt_fed_cfg, db_fed, fed - 1);
+}
+
+/**
+ * @brief Set rising edge delay duration
+ *
+ * @param mcpwm Peripheral instance address
+ * @param operator_id Operator ID, index from 0 to 2
+ * @param red Delay duration, in deadtime submodule's clock cycles
+ */
+static inline void mcpwm_ll_deadtime_set_rising_delay(mcpwm_dev_t *mcpwm, int operator_id, uint32_t red)
+{
+    HAL_FORCE_MODIFY_U32_REG_FIELD(mcpwm->operators[operator_id].dt_red_cfg, db_red, red - 1);
+}
+
+/**
+ * @brief Update deadtime immediately
+ *
+ * @param mcpwm Peripheral instance address
+ * @param operator_id Operator ID, index from 0 to 2
+ */
+static inline void mcpwm_ll_deadtime_update_delay_at_once(mcpwm_dev_t *mcpwm, int operator_id)
+{
+    mcpwm->operators[operator_id].dt_cfg.db_fed_upmethod = 0;
+    mcpwm->operators[operator_id].dt_cfg.db_red_upmethod = 0;
+}
+
+/**
+ * @brief Enable to update deadtime on TEZ event
+ *
+ * @param mcpwm Peripheral instance address
+ * @param operator_id Operator ID, index from 0 to 2
+ * @param enable True to enable, False to disable
+ */
+static inline void mcpwm_ll_deadtime_enable_update_delay_on_tez(mcpwm_dev_t *mcpwm, int operator_id, bool enable)
+{
+    if (enable) {
+        mcpwm->operators[operator_id].dt_cfg.db_fed_upmethod |= 1 << 0;
+        mcpwm->operators[operator_id].dt_cfg.db_red_upmethod |= 1 << 0;
+    } else {
+        mcpwm->operators[operator_id].dt_cfg.db_fed_upmethod &= ~(1 << 0);
+        mcpwm->operators[operator_id].dt_cfg.db_red_upmethod &= ~(1 << 0);
+    }
+}
+
+/**
+ * @brief Enable to update deadtime on TEP event
+ *
+ * @param mcpwm Peripheral instance address
+ * @param operator_id Operator ID, index from 0 to 2
+ * @param enable True to enable, False to disable
+ */
+static inline void mcpwm_ll_deadtime_enable_update_delay_on_tep(mcpwm_dev_t *mcpwm, int operator_id, bool enable)
+{
+    if (enable) {
+        mcpwm->operators[operator_id].dt_cfg.db_fed_upmethod |= 1 << 1;
+        mcpwm->operators[operator_id].dt_cfg.db_red_upmethod |= 1 << 1;
+    } else {
+        mcpwm->operators[operator_id].dt_cfg.db_fed_upmethod &= ~(1 << 1);
+        mcpwm->operators[operator_id].dt_cfg.db_red_upmethod &= ~(1 << 1);
+    }
+}
+
+/**
+ * @brief Enable to update deadtime on sync event
+ *
+ * @param mcpwm Peripheral instance address
+ * @param operator_id Operator ID, index from 0 to 2
+ * @param enable True to enable, False to disable
+ */
+static inline void mcpwm_ll_deadtime_enable_update_delay_on_sync(mcpwm_dev_t *mcpwm, int operator_id, bool enable)
+{
+    if (enable) {
+        mcpwm->operators[operator_id].dt_cfg.db_fed_upmethod |= 1 << 2;
+        mcpwm->operators[operator_id].dt_cfg.db_red_upmethod |= 1 << 2;
+    } else {
+        mcpwm->operators[operator_id].dt_cfg.db_fed_upmethod &= ~(1 << 2);
+        mcpwm->operators[operator_id].dt_cfg.db_red_upmethod &= ~(1 << 2);
+    }
+}
+
+/**
+ * @brief Stop updating deadtime
+ *
+ * @param mcpwm Peripheral instance address
+ * @param operator_id Operator ID, index from 0 to 2
+ * @param stop_or_not True to stop, False to continue
+ */
+static inline void mcpwm_ll_deadtime_stop_update_delay(mcpwm_dev_t *mcpwm, int operator_id, bool stop_or_not)
+{
+    if (stop_or_not) {
+        mcpwm->operators[operator_id].dt_cfg.db_fed_upmethod |= 1 << 3;
+        mcpwm->operators[operator_id].dt_cfg.db_red_upmethod |= 1 << 3;
+    } else {
+        mcpwm->operators[operator_id].dt_cfg.db_fed_upmethod &= ~(1 << 3);
+        mcpwm->operators[operator_id].dt_cfg.db_red_upmethod &= ~(1 << 3);
+    }
+}
+
+////////////////////////////////////////MCPWM Carrier Specific//////////////////////////////////////////////////////////
+
+/**
+ * @brief Enable carrier for MCPWM operator
+ *
+ * @param mcpwm Peripheral instance address
+ * @param operator_id Operator ID, index from 0 to 2
+ * @param enable True to enable, False to disable
+ */
+static inline void mcpwm_ll_carrier_enable(mcpwm_dev_t *mcpwm, int operator_id, bool enable)
+{
+    mcpwm->operators[operator_id].carrier_cfg.chopper_en = enable;
+}
+
+/**
+ * @brief Set prescale for MCPWM carrier source clock
+ *
+ * @param mcpwm Peripheral instance address
+ * @param operator_id Operator ID, index from 0 to 2
+ * @param prescale Prescale value
+ */
+static inline void mcpwm_ll_carrier_set_prescale(mcpwm_dev_t *mcpwm, int operator_id, uint8_t prescale)
+{
+    HAL_ASSERT(prescale > 0 && prescale <= 16);
+    mcpwm->operators[operator_id].carrier_cfg.chopper_prescale = prescale - 1;
+}
+
+/**
+ * @brief Set duty cycle of MCPWM carrier
+ *
+ * @param mcpwm Peripheral instance address
+ * @param operator_id Operator ID, index from 0 to 2
+ * @param carrier_duty Duty cycle value
+ */
+static inline void mcpwm_ll_carrier_set_duty(mcpwm_dev_t *mcpwm, int operator_id, uint8_t carrier_duty)
+{
+    mcpwm->operators[operator_id].carrier_cfg.chopper_duty = carrier_duty;
+}
+
+/**
+ * @brief Invert the signal after the carrier is applied
+ *
+ * @param mcpwm Peripheral instance address
+ * @param operator_id Operator ID, index from 0 to 2
+ * @param invert True to invert, False to not invert
+ */
+static inline void mcpwm_ll_carrier_out_invert(mcpwm_dev_t *mcpwm, int operator_id, bool invert)
+{
+    mcpwm->operators[operator_id].carrier_cfg.chopper_out_invert = invert;
+}
+
+/**
+ * @brief Invert the signal before applying the carrier
+ *
+ * @param mcpwm Peripheral instance address
+ * @param operator_id Operator ID, index from 0 to 2
+ * @param invert True to invert, False to not invert
+ */
+static inline void mcpwm_ll_carrier_in_invert(mcpwm_dev_t *mcpwm, int operator_id, bool invert)
+{
+    mcpwm->operators[operator_id].carrier_cfg.chopper_in_invert = invert;
+}
+
+/**
+ * @brief Set the first pulse width of the carrier
+ *
+ * @param mcpwm Peripheral instance address
+ * @param operator_id Operator ID, index from 0 to 2
+ * @param pulse_width Pulse width
+ */
+static inline void mcpwm_ll_carrier_set_first_pulse_width(mcpwm_dev_t *mcpwm, int operator_id, uint8_t pulse_width)
+{
+    HAL_ASSERT(pulse_width >= 1);
+    mcpwm->operators[operator_id].carrier_cfg.chopper_oshtwth = pulse_width - 1;
+}
+
+////////////////////////////////////////MCPWM Fault Specific////////////////////////////////////////////////////////////
+
+/**
+ * @brief Enable GPIO fault detection
+ *
+ * @param mcpwm Peripheral instance address
+ * @param fault_sig GPIO fault ID, index from 0 to 2
+ * @param enable True to enable, False to disable
+ */
+static inline void mcpwm_ll_fault_enable_detection(mcpwm_dev_t *mcpwm, int fault_sig, bool enable)
+{
+    if (enable) {
+        mcpwm->fault_detect.val |= 1 << fault_sig;
+    } else {
+        mcpwm->fault_detect.val &= ~(1 << fault_sig);
+    }
+}
+
+/**
+ * @brief Set fault polarity (i.e. which level is treated as an active fault)
+ *
+ * @param mcpwm Peripheral instance address
+ * @param fault_sig GPIO fault ID, index from 0 to 2
+ * @param level Active level, 0 for low, 1 for high
+ */
+static inline void mcpwm_ll_fault_set_active_level(mcpwm_dev_t *mcpwm, int fault_sig, bool level)
+{
+    if (level) {
+        mcpwm->fault_detect.val |= 1 << (fault_sig + 3);
+    } else {
+        mcpwm->fault_detect.val &= ~(1 << (fault_sig + 3));
+    }
+}
+
+/**
+ * @brief Clear the OST brake
+ *
+ * @param mcpwm Peripheral instance address
+ * @param operator_id Operator ID, index from 0 to 2
+ */
+static inline void mcpwm_ll_brake_clear_ost(mcpwm_dev_t *mcpwm, int operator_id)
+{
+    // a posedge can clear the ost fault status
+    mcpwm->operators[operator_id].fh_cfg1.tz_clr_ost = 0;
+    mcpwm->operators[operator_id].fh_cfg1.tz_clr_ost = 1;
+}
+
+/**
+ * @brief Enable the OST brake
+ *
+ * @param mcpwm Peripheral instance address
+ * @param operator_id Operator ID, index from 0 to 2
+ * @param fault_sig GPIO fault ID, index from 0 to 2
+ * @param enable True to enable, False to disable
+ */
+static inline void mcpwm_ll_brake_enable_oneshot_mode(mcpwm_dev_t *mcpwm, int operator_id, int fault_sig, bool enable)
+{
+    if (enable) {
+        mcpwm->operators[operator_id].fh_cfg0.val |= (1 << (7 - fault_sig));
+    } else {
+        mcpwm->operators[operator_id].fh_cfg0.val &= ~(1 << (7 - fault_sig));
+    }
+}
+
+/**
+ * @brief Enable the CBC brake
+ *
+ * @param mcpwm Peripheral instance address
+ * @param operator_id Operator ID, index from 0 to 2
+ * @param fault_sig GPIO fault ID, index from 0 to 2
+ * @param enable True to enable, False to disable
+ */
+static inline void mcpwm_ll_brake_enable_cbc_mode(mcpwm_dev_t *mcpwm, int operator_id, int fault_sig, bool enable)
+{
+    if (enable) {
+        mcpwm->operators[operator_id].fh_cfg0.val |= (enable << (3 - fault_sig));
+    } else {
+        mcpwm->operators[operator_id].fh_cfg0.val &= ~(1 << (3 - fault_sig));
+    }
+}
+
+/**
+ * @brief Enable refresh the CBC brake on TEZ event
+ *
+ * @param mcpwm Peripheral instance address
+ * @param operator_id Operator ID, index from 0 to 2
+ * @param enable True to enable, False to disable
+ */
+static inline void mcpwm_ll_brake_enable_cbc_refresh_on_tez(mcpwm_dev_t *mcpwm, int operator_id, bool enable)
+{
+    if (enable) {
+        mcpwm->operators[operator_id].fh_cfg1.val |= 1 << 1;
+    } else {
+        mcpwm->operators[operator_id].fh_cfg1.val &= ~(1 << 1);
+    }
+}
+
+/**
+ * @brief Enable refresh the CBC brake on TEP event
+ *
+ * @param mcpwm Peripheral instance address
+ * @param operator_id Operator ID, index from 0 to 2
+ * @param enable True to enable, False to disable
+ */
+static inline void mcpwm_ll_fault_enable_cbc_refresh_on_tep(mcpwm_dev_t *mcpwm, int operator_id, bool enable)
+{
+    if (enable) {
+        mcpwm->operators[operator_id].fh_cfg1.val |= 1 << 2;
+    } else {
+        mcpwm->operators[operator_id].fh_cfg1.val &= ~(1 << 2);
+    }
+}
+
+/**
+ * @brief Enable software CBC brake
+ *
+ * @param mcpwm Peripheral instance address
+ * @param operator_id Operator ID, index from 0 to 2
+ * @param enable True to enable, False to disable
+ */
+static inline void mcpwm_ll_brake_enable_soft_cbc(mcpwm_dev_t *mcpwm, int operator_id, bool enable)
+{
+    mcpwm->operators[operator_id].fh_cfg0.tz_sw_cbc = enable;
+}
+
+/**
+ * @brief Enable software OST brake
+ *
+ * @param mcpwm Peripheral instance address
+ * @param operator_id Operator ID, index from 0 to 2
+ * @param enable True to enable, False to disable
+ */
+static inline void mcpwm_ll_brake_enable_soft_ost(mcpwm_dev_t *mcpwm, int operator_id, bool enable)
+{
+    mcpwm->operators[operator_id].fh_cfg0.tz_sw_ost = enable;
+}
+
+/**
+ * @brief Trigger software CBC brake for once
+ *
+ * @param mcpwm Peripheral instance address
+ * @param operator_id Operator ID, index from 0 to 2
+ */
+static inline void mcpwm_ll_brake_trigger_soft_cbc(mcpwm_dev_t *mcpwm, int operator_id)
+{
+    mcpwm->operators[operator_id].fh_cfg1.tz_force_cbc = ~mcpwm->operators[operator_id].fh_cfg1.tz_force_cbc;
+}
+
+/**
+ * @brief Trigger software OST brake for once
+ *
+ * @param mcpwm Peripheral instance address
+ * @param operator_id Operator ID, index from 0 to 2
+ */
+static inline void mcpwm_ll_brake_trigger_soft_ost(mcpwm_dev_t *mcpwm, int operator_id)
+{
+    mcpwm->operators[operator_id].fh_cfg1.tz_force_ost = ~mcpwm->operators[operator_id].fh_cfg1.tz_force_ost;
+}
+
+/**
+ * @brief Whether the OST brake is still active
+ *
+ * @param mcpwm Peripheral instance address
+ * @param operator_id Operator ID, index from 0 to 2
+ * @return True if active, False if not
+ */
+static inline bool mcpwm_ll_ost_brake_active(mcpwm_dev_t *mcpwm, int operator_id)
+{
+    return mcpwm->operators[operator_id].fh_status.tz_ost_on;
+}
+
+/**
+ * @brief Whether the CBC brake is still active
+ *
+ * @param mcpwm Peripheral instance address
+ * @param operator_id Operator ID, index from 0 to 2
+ * @return True if active, False if not
+ */
+static inline bool mcpwm_ll_cbc_brake_active(mcpwm_dev_t *mcpwm, int operator_id)
+{
+    return mcpwm->operators[operator_id].fh_status.tz_cbc_on;
+}
+
+////////////////////////////////////////MCPWM Capture Specific//////////////////////////////////////////////////////////
+
+/**
+ * @brief Enable capture timer
+ *
+ * @param mcpwm Peripheral instance address
+ * @param enable True to enable, False to disable
+ */
+static inline void mcpwm_ll_capture_enable_timer(mcpwm_dev_t *mcpwm, bool enable)
+{
+    mcpwm->cap_timer_cfg.cap_timer_en = enable;
+}
+
+/**
+ * @brief Enable capture channel
+ *
+ * @param mcpwm Peripheral instance address
+ * @param channel Channel ID, index from 0 to 2
+ * @param enable True to enable, False to disable
+ */
+static inline void mcpwm_ll_capture_enable_channel(mcpwm_dev_t *mcpwm, int channel, bool enable)
+{
+    mcpwm->cap_chn_cfg[channel].capn_en = enable;
+}
+
+/**
+ * @brief Set sync phase for capture timer
+ *
+ * @param mcpwm Peripheral instance address
+ * @param phase_value Phase value
+ */
+static inline void mcpwm_ll_capture_set_sync_phase_value(mcpwm_dev_t *mcpwm, uint32_t phase_value)
+{
+    mcpwm->cap_timer_phase.cap_phase = phase_value;
+}
+
+/**
+ * @brief Enable sync for capture timer
+ *
+ * @param mcpwm Peripheral instance address
+ * @param enable True to enable, False to disable
+ */
+static inline void mcpwm_ll_capture_enable_timer_sync(mcpwm_dev_t *mcpwm, bool enable)
+{
+    mcpwm->cap_timer_cfg.cap_synci_en = enable;
+}
+
+/**
+ * @brief Set the timer sync source for MCPWM capture timer
+ *
+ * @param mcpwm Peripheral instance address
+ * @param sync_out_timer MCPWM Timer ID, index from 0 to 2
+ */
+static inline void mcpwm_ll_capture_set_timer_sync(mcpwm_dev_t *mcpwm, int sync_out_timer)
+{
+    mcpwm->cap_timer_cfg.cap_synci_sel = sync_out_timer + 1;
+}
+
+/**
+ * @brief Set the GPIO sync source for MCPWM capture timer
+ *
+ * @param mcpwm Peripheral instance address
+ * @param gpio_sync GPIO sync ID, index from 0 to 2
+ */
+static inline void mcpwm_ll_capture_set_gpio_sync(mcpwm_dev_t *mcpwm, int gpio_sync)
+{
+    mcpwm->cap_timer_cfg.cap_synci_sel = gpio_sync + 4;
+}
+
+/**
+ * @brief Trigger a software sync for capture timer
+ *
+ * @param mcpwm Peripheral instance address
+ */
+static inline void mcpwm_ll_capture_trigger_sw_sync(mcpwm_dev_t *mcpwm)
+{
+    mcpwm->cap_timer_cfg.cap_sync_sw = 1; // auto clear
+}
+
+/**
+ * @brief Enable capture on positive edge
+ *
+ * @param mcpwm Peripheral instance address
+ * @param channel Channel ID, index from 0 to 2
+ * @param enable True to enable, False to disable
+ */
+static inline void mcpwm_ll_capture_enable_posedge(mcpwm_dev_t *mcpwm, int channel, bool enable)
+{
+    if (enable) {
+        mcpwm->cap_chn_cfg[channel].val |= 1 << 2;
+    } else {
+        mcpwm->cap_chn_cfg[channel].val &= ~(1 << 2);
+    }
+}
+
+/**
+ * @brief Enable capture on negative edge
+ *
+ * @param mcpwm Peripheral instance address
+ * @param channel Channel ID, index from 0 to 2
+ * @param enable True to enable, False to disable
+ */
+static inline void mcpwm_ll_capture_enable_negedge(mcpwm_dev_t *mcpwm, int channel, bool enable)
+{
+    if (enable) {
+        mcpwm->cap_chn_cfg[channel].val |= 1 << 1;
+    } else {
+        mcpwm->cap_chn_cfg[channel].val &= ~(1 << 1);
+    }
+}
+
+/**
+ * @brief Invert the capture input signal
+ *
+ * @param mcpwm Peripheral instance address
+ * @param channel Channel ID, index from 0 to 2
+ * @param invert True to invert, False to not invert
+ */
+static inline void mcpwm_ll_invert_input(mcpwm_dev_t *mcpwm, int channel, bool invert)
+{
+    mcpwm->cap_chn_cfg[channel].capn_in_invert = invert;
+}
+
+/**
+ * @brief Trigger the software capture for once
+ *
+ * @param mcpwm Peripheral instance address
+ * @param channel Channel ID, index from 0 to 2
+ */
+static inline void mcpwm_ll_trigger_soft_capture(mcpwm_dev_t *mcpwm, int channel)
+{
+    mcpwm->cap_chn_cfg[channel].capn_sw = 1; // auto clear
+}
+
+/**
+ * @brief Get the captured value
+ *
+ * @param mcpwm Peripheral instance address
+ * @param channel Channel ID, index from 0 to 2
+ * @return Captured value
+ */
+__attribute__((always_inline))
+static inline uint32_t mcpwm_ll_capture_get_value(mcpwm_dev_t *mcpwm, int channel)
+{
+    return mcpwm->cap_chn[channel].capn_value;
+}
+
+/**
+ * @brief Get the captured edge
+ *
+ * @param mcpwm Peripheral instance address
+ * @param channel Channel ID, index from 0 to 2
+ * @return Captured edge
+ */
+__attribute__((always_inline))
+static inline mcpwm_capture_edge_t mcpwm_ll_capture_get_edge(mcpwm_dev_t *mcpwm, int channel)
+{
+    return mcpwm->cap_status.val & (1 << channel) ? MCPWM_CAP_EDGE_NEG : MCPWM_CAP_EDGE_POS;
+}
+
+/**
+ * @brief Set the prescale of the input capture signal
+ *
+ * @param mcpwm Peripheral instance address
+ * @param channel Channel ID, index from 0 to 2
+ * @param prescale Prescale value
+ */
+static inline void mcpwm_ll_capture_set_prescale(mcpwm_dev_t *mcpwm, int channel, uint32_t prescale)
+{
+    HAL_ASSERT(prescale > 0);
+    HAL_FORCE_MODIFY_U32_REG_FIELD(mcpwm->cap_chn_cfg[channel], capn_prescale, prescale - 1);
+}
+
+//////////////////////////////////////////Deprecated Functions//////////////////////////////////////////////////////////
+/////////////////////////////The following functions are only used by the legacy driver/////////////////////////////////
+/////////////////////////////They might be removed in the next major release (ESP-IDF 6.0)//////////////////////////////
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+static inline uint32_t mcpwm_ll_group_get_clock_prescale(mcpwm_dev_t *mcpwm)
+{
+    (void)mcpwm; // only one MCPWM instance
+    return HAL_FORCE_READ_U32_REG_FIELD(PCR.pwm_clk_conf, pwm_div_num) + 1;
+}
+
+static inline uint32_t mcpwm_ll_timer_get_clock_prescale(mcpwm_dev_t *mcpwm, int timer_id)
+{
+    mcpwm_timer_cfg0_reg_t cfg0 = mcpwm->timer[timer_id].timer_cfg0;
+    return cfg0.timer_prescale + 1;
+}
+
+static inline uint32_t mcpwm_ll_timer_get_peak(mcpwm_dev_t *mcpwm, int timer_id, bool symmetric)
+{
+    return HAL_FORCE_READ_U32_REG_FIELD(mcpwm->timer[timer_id].timer_cfg0, timer_period) + (symmetric ? 0 : 1);
+}
+
+static inline mcpwm_timer_count_mode_t mcpwm_ll_timer_get_count_mode(mcpwm_dev_t *mcpwm, int timer_id)
+{
+    switch (mcpwm->timer[timer_id].timer_cfg1.timer_mod) {
+    case 1:
+        return MCPWM_TIMER_COUNT_MODE_UP;
+    case 2:
+        return MCPWM_TIMER_COUNT_MODE_DOWN;
+    case 3:
+        return MCPWM_TIMER_COUNT_MODE_UP_DOWN;
+    case 0:
+    default:
+        return MCPWM_TIMER_COUNT_MODE_PAUSE;
+    }
+}
+
+static inline uint32_t mcpwm_ll_operator_get_compare_value(mcpwm_dev_t *mcpwm, int operator_id, int compare_id)
+{
+    return HAL_FORCE_READ_U32_REG_FIELD(mcpwm->operators[operator_id].timestamp[compare_id], cmpr);
+}
+
+__attribute__((always_inline))
+static inline uint32_t mcpwm_ll_intr_get_capture_status(mcpwm_dev_t *mcpwm)
+{
+    return (mcpwm->int_st.val >> 27) & 0x07;
+}
+
+__attribute__((always_inline))
+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;
+}
+
+#ifdef __cplusplus
+}
+#endif

+ 0 - 4
components/soc/esp32/include/soc/Kconfig.soc_caps.in

@@ -439,10 +439,6 @@ config SOC_MCPWM_GPIO_SYNCHROS_PER_GROUP
     int
     default 3
 
-config SOC_MCPWM_CLK_SUPPORT_PLL160M
-    bool
-    default y
-
 config SOC_MPU_CONFIGURABLE_REGIONS_SUPPORTED
     bool
     default n

+ 0 - 1
components/soc/esp32/include/soc/soc_caps.h

@@ -229,7 +229,6 @@
 #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_GPIO_SYNCHROS_PER_GROUP    (3)    ///< The number of GPIO synchros that each group has
-#define SOC_MCPWM_CLK_SUPPORT_PLL160M        (1)    ///< Support PLL160M as clock source
 
 /*-------------------------- MPU CAPS ----------------------------------------*/
 //TODO: correct the caller and remove unsupported lines

+ 0 - 8
components/soc/esp32c6/include/soc/Kconfig.soc_caps.in

@@ -659,14 +659,6 @@ config SOC_MCPWM_CAPTURE_CLK_FROM_GROUP
     bool
     default y
 
-config SOC_MCPWM_CLK_SUPPORT_PLL160M
-    bool
-    default y
-
-config SOC_MCPWM_CLK_SUPPORT_XTAL
-    bool
-    default y
-
 config SOC_RSA_MAX_BIT_LEN
     int
     default 3072

+ 0 - 2
components/soc/esp32c6/include/soc/soc_caps.h

@@ -280,8 +280,6 @@
 #define SOC_MCPWM_SWSYNC_CAN_PROPAGATE       (1)    ///< Software sync event can be routed to its output
 #define SOC_MCPWM_SUPPORT_ETM                (1)    ///< Support ETM (Event Task Matrix)
 #define SOC_MCPWM_CAPTURE_CLK_FROM_GROUP     (1)    ///< Capture timer shares clock with other PWM timers
-#define SOC_MCPWM_CLK_SUPPORT_PLL160M        (1)    ///< Support PLL160M as clock source
-#define SOC_MCPWM_CLK_SUPPORT_XTAL           (1)    ///< Support XTAL as clock source
 
 // TODO: IDF-5359 (Copy from esp32c3, need check)
 /*--------------------------- RSA CAPS ---------------------------------------*/

+ 4 - 8
components/soc/esp32h2/include/soc/Kconfig.soc_caps.in

@@ -15,6 +15,10 @@ config SOC_PCNT_SUPPORTED
     bool
     default y
 
+config SOC_MCPWM_SUPPORTED
+    bool
+    default y
+
 config SOC_GPTIMER_SUPPORTED
     bool
     default y
@@ -507,14 +511,6 @@ config SOC_MCPWM_CAPTURE_CLK_FROM_GROUP
     bool
     default y
 
-config SOC_MCPWM_CLK_SUPPORT_PLL160M
-    bool
-    default y
-
-config SOC_MCPWM_CLK_SUPPORT_XTAL
-    bool
-    default y
-
 config SOC_RTC_CNTL_CPU_PD_DMA_BUS_WIDTH
     int
     default 128

+ 2 - 1
components/soc/esp32h2/include/soc/periph_defs.h

@@ -38,6 +38,7 @@ typedef enum {
     PERIPH_HMAC_MODULE,
     PERIPH_DS_MODULE,
     PERIPH_GDMA_MODULE,
+    PERIPH_MCPWM0_MODULE,
     PERIPH_SYSTIMER_MODULE,
     PERIPH_SARADC_MODULE,
     PERIPH_MODULE_MAX
@@ -93,7 +94,7 @@ typedef enum {
     ETS_SYSTIMER_TARGET1_EDGE_INTR_SOURCE,      /**< interrupt of system timer 1, EDGE*/
     ETS_SYSTIMER_TARGET2_EDGE_INTR_SOURCE,      /**< interrupt of system timer 2, EDGE*/
     ETS_APB_ADC_INTR_SOURCE,                    /**< interrupt of APB ADC, LEVEL*/
-    ETS_PWM_INTR_SOURCE,
+    ETS_MCPWM0_INTR_SOURCE,
     ETS_PCNT_INTR_SOURCE,
     ETS_PARL_IO_TX_INTR_SOURCE,
     ETS_PARL_IO_RX_INTR_SOURCE,

+ 2 - 5
components/soc/esp32h2/include/soc/soc_caps.h

@@ -30,7 +30,7 @@
 #define SOC_GDMA_SUPPORTED              1
 #define SOC_ASYNC_MEMCPY_SUPPORTED      1
 #define SOC_PCNT_SUPPORTED              1
-// #define SOC_MCPWM_SUPPORTED             1 // TODO: IDF-6237
+#define SOC_MCPWM_SUPPORTED             1
 // #define SOC_TWAI_SUPPORTED              1 // TODO: IDF-6217
 // #define SOC_BT_SUPPORTED                1 // TODO: IDF-6416
 // #define SOC_IEEE802154_SUPPORTED        1 // TODO: IDF-6577
@@ -235,7 +235,6 @@
 #define SOC_RMT_SUPPORT_XTAL                  1  /*!< Support set XTAL clock as the RMT clock source */
 // #define SOC_RMT_SUPPORT_RC_FAST               1  /*!< Support set RC_FAST as the RMT clock source */
 
-// TODO: IDF-6237
 /*-------------------------- MCPWM CAPS --------------------------------------*/
 #define SOC_MCPWM_GROUPS                     (1U)   ///< 1 MCPWM groups on the chip (i.e., the number of independent MCPWM peripherals)
 #define SOC_MCPWM_TIMERS_PER_GROUP           (3)    ///< The number of timers that each group has
@@ -249,9 +248,7 @@
 #define SOC_MCPWM_GPIO_SYNCHROS_PER_GROUP    (3)    ///< The number of GPIO synchros that each group has
 #define SOC_MCPWM_SWSYNC_CAN_PROPAGATE       (1)    ///< Software sync event can be routed to its output
 #define SOC_MCPWM_SUPPORT_ETM                (1)    ///< Support ETM (Event Task Matrix)
-#define SOC_MCPWM_CAPTURE_CLK_FROM_GROUP  (1)    ///< Capture timer shares clock with other PWM timers
-#define SOC_MCPWM_CLK_SUPPORT_PLL160M        (1)    ///< Support PLL160M as clock source
-#define SOC_MCPWM_CLK_SUPPORT_XTAL           (1)    ///< Support XTAL as clock source
+#define SOC_MCPWM_CAPTURE_CLK_FROM_GROUP     (1)    ///< Capture timer shares clock with other PWM timers
 
 // TODO: IDF-6267 (Copy from esp32c6, need check)
 /*-------------------------- RTC CAPS --------------------------------------*/

+ 1 - 1
components/soc/esp32h2/ld/esp32h2.peripherals.ld

@@ -27,7 +27,7 @@ PROVIDE ( INTMTX        = 0x60010000 );
 PROVIDE ( ATOMIC_LOCKER = 0x60011000 );
 PROVIDE ( PCNT          = 0x60012000 );
 PROVIDE ( SOC_ETM       = 0x60013000 );
-PROVIDE ( MCPWM         = 0x60014000 );
+PROVIDE ( MCPWM0        = 0x60014000 );
 PROVIDE ( PARL_IO       = 0x60015000 );
 PROVIDE ( PVT_MONITOR   = 0x60019000 );
 

+ 83 - 0
components/soc/esp32h2/mcpwm_periph.c

@@ -0,0 +1,83 @@
+/*
+ * SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include "soc/soc.h"
+#include "soc/mcpwm_periph.h"
+#include "soc/gpio_sig_map.h"
+
+const mcpwm_signal_conn_t mcpwm_periph_signals = {
+    .groups = {
+        [0] = {
+            .module = PERIPH_MCPWM0_MODULE,
+            .irq_id = ETS_MCPWM0_INTR_SOURCE,
+            .operators = {
+                [0] = {
+                    .generators = {
+                        [0] = {
+                            .pwm_sig = PWM0_OUT0A_IDX
+                        },
+                        [1] = {
+                            .pwm_sig = PWM0_OUT0B_IDX
+                        }
+                    }
+                },
+                [1] = {
+                    .generators = {
+                        [0] = {
+                            .pwm_sig = PWM0_OUT1A_IDX
+                        },
+                        [1] = {
+                            .pwm_sig = PWM0_OUT1B_IDX
+                        }
+                    }
+                },
+                [2] = {
+                    .generators = {
+                        [0] = {
+                            .pwm_sig = PWM0_OUT2A_IDX
+                        },
+                        [1] = {
+                            .pwm_sig = PWM0_OUT2B_IDX
+                        }
+                    }
+                }
+            },
+            .gpio_faults = {
+                [0] = {
+                    .fault_sig = PWM0_F0_IN_IDX
+                },
+                [1] = {
+                    .fault_sig = PWM0_F1_IN_IDX
+                },
+                [2] = {
+                    .fault_sig = PWM0_F2_IN_IDX
+                }
+            },
+            .captures = {
+                [0] = {
+                    .cap_sig = PWM0_CAP0_IN_IDX
+                },
+                [1] = {
+                    .cap_sig = PWM0_CAP1_IN_IDX
+                },
+                [2] = {
+                    .cap_sig = PWM0_CAP2_IN_IDX
+                }
+            },
+            .gpio_synchros = {
+                [0] = {
+                    .sync_sig = PWM0_SYNC0_IN_IDX
+                },
+                [1] = {
+                    .sync_sig = PWM0_SYNC1_IN_IDX
+                },
+                [2] = {
+                    .sync_sig = PWM0_SYNC2_IN_IDX
+                }
+            }
+        },
+    }
+};

+ 0 - 4
components/soc/esp32s3/include/soc/Kconfig.soc_caps.in

@@ -535,10 +535,6 @@ config SOC_MCPWM_SWSYNC_CAN_PROPAGATE
     bool
     default y
 
-config SOC_MCPWM_CLK_SUPPORT_PLL160M
-    bool
-    default y
-
 config SOC_PCNT_GROUPS
     int
     default 1

+ 0 - 1
components/soc/esp32s3/include/soc/soc_caps.h

@@ -219,7 +219,6 @@
 #define SOC_MCPWM_CAPTURE_CHANNELS_PER_TIMER (3)    ///< The number of capture channels that each capture timer has
 #define SOC_MCPWM_GPIO_SYNCHROS_PER_GROUP    (3)    ///< The number of GPIO synchros that each group has
 #define SOC_MCPWM_SWSYNC_CAN_PROPAGATE       (1)    ///< Software sync event can be routed to its output
-#define SOC_MCPWM_CLK_SUPPORT_PLL160M        (1)    ///< Support PLL160M as clock source
 
 /*-------------------------- MPU CAPS ----------------------------------------*/
 #include "mpu_caps.h"

+ 0 - 1
docs/docs_not_updated/esp32h2.txt

@@ -81,7 +81,6 @@ api-reference/peripherals/etm
 api-reference/peripherals/i2s
 api-reference/peripherals/touch_element
 api-reference/peripherals/lcd
-api-reference/peripherals/mcpwm
 api-reference/peripherals/secure_element
 api-reference/peripherals/temp_sensor
 api-reference/peripherals/spi_features

+ 2 - 2
examples/peripherals/mcpwm/mcpwm_bdc_speed_control/README.md

@@ -1,5 +1,5 @@
-| Supported Targets | ESP32 | ESP32-C6 | ESP32-S3 |
-| ----------------- | ----- | -------- | -------- |
+| Supported Targets | ESP32 | ESP32-C6 | ESP32-H2 | ESP32-S3 |
+| ----------------- | ----- | -------- | -------- | -------- |
 # MCPWM Brushed DC Motor Example
 
 (See the README.md file in the upper level 'examples' directory for more information about examples.)

+ 2 - 2
examples/peripherals/mcpwm/mcpwm_bldc_hall_control/README.md

@@ -1,5 +1,5 @@
-| Supported Targets | ESP32 | ESP32-C6 | ESP32-S3 |
-| ----------------- | ----- | -------- | -------- |
+| Supported Targets | ESP32 | ESP32-C6 | ESP32-H2 | ESP32-S3 |
+| ----------------- | ----- | -------- | -------- | -------- |
 
 # MCPWM BLDC Motor Control with HALL Sensor Example
 

+ 2 - 2
examples/peripherals/mcpwm/mcpwm_capture_hc_sr04/README.md

@@ -1,5 +1,5 @@
-| Supported Targets | ESP32 | ESP32-C6 | ESP32-S3 |
-| ----------------- | ----- | -------- | -------- |
+| Supported Targets | ESP32 | ESP32-C6 | ESP32-H2 | ESP32-S3 |
+| ----------------- | ----- | -------- | -------- | -------- |
 
 # HC-SR04 Example based on MCPWM Capture
 

+ 1 - 0
examples/peripherals/mcpwm/mcpwm_capture_hc_sr04/pytest_hc_sr04.py

@@ -8,6 +8,7 @@ from pytest_embedded import Dut
 @pytest.mark.esp32
 @pytest.mark.esp32s3
 @pytest.mark.esp32c6
+@pytest.mark.esp32h2
 @pytest.mark.generic
 def test_hc_sr04_example(dut: Dut) -> None:
     dut.expect_exact('example: Install capture timer')

+ 2 - 2
examples/peripherals/mcpwm/mcpwm_servo_control/README.md

@@ -1,5 +1,5 @@
-| Supported Targets | ESP32 | ESP32-C6 | ESP32-S3 |
-| ----------------- | ----- | -------- | -------- |
+| Supported Targets | ESP32 | ESP32-C6 | ESP32-H2 | ESP32-S3 |
+| ----------------- | ----- | -------- | -------- | -------- |
 # MCPWM RC Servo Control Example
 
 (See the README.md file in the upper level 'examples' directory for more information about examples.)

+ 1 - 0
examples/peripherals/mcpwm/mcpwm_servo_control/pytest_servo_mg996r.py

@@ -8,6 +8,7 @@ from pytest_embedded import Dut
 @pytest.mark.esp32
 @pytest.mark.esp32s3
 @pytest.mark.esp32c6
+@pytest.mark.esp32h2
 @pytest.mark.generic
 def test_servo_mg996r_example(dut: Dut) -> None:
     dut.expect_exact('example: Create timer and operator')

+ 2 - 2
examples/peripherals/mcpwm/mcpwm_sync/README.md

@@ -1,5 +1,5 @@
-| Supported Targets | ESP32 | ESP32-C6 | ESP32-S3 |
-| ----------------- | ----- | -------- | -------- |
+| Supported Targets | ESP32 | ESP32-C6 | ESP32-H2 | ESP32-S3 |
+| ----------------- | ----- | -------- | -------- | -------- |
 
 # MCPWM Sync Example
 

+ 1 - 0
examples/peripherals/mcpwm/mcpwm_sync/pytest_mcpwm_sync.py

@@ -8,6 +8,7 @@ from pytest_embedded import Dut
 @pytest.mark.esp32
 @pytest.mark.esp32s3
 @pytest.mark.esp32c6
+@pytest.mark.esp32h2
 @pytest.mark.generic
 @pytest.mark.parametrize('config', [
     pytest.param('gpio', marks=[pytest.mark.esp32, pytest.mark.esp32s3]),

+ 4 - 0
examples/peripherals/rmt/dshot_esc/main/dshot_esc_example_main.c

@@ -10,7 +10,11 @@
 #include "driver/rmt_tx.h"
 #include "dshot_esc_encoder.h"
 
+#if CONFIG_IDF_TARGET_ESP32H2
+#define DSHOT_ESC_RESOLUTION_HZ 32000000 // 32MHz resolution, DSHot protocol needs a relative high resolution
+#else
 #define DSHOT_ESC_RESOLUTION_HZ 40000000 // 40MHz resolution, DSHot protocol needs a relative high resolution
+#endif
 #define DSHOT_ESC_GPIO_NUM      0
 
 static const char *TAG = "example";