Przeglądaj źródła

Merge branch 'feature/mcpwm_refactor_and_support_esp32s3' into 'master'

MCPWM: refactor driver and support ESP32-S3

Closes IDF-1765

See merge request espressif/esp-idf!11907
Michael (XIAO Xufeng) 4 lat temu
rodzic
commit
1322a44588

+ 1 - 15
.gitlab/ci/target-test.yml

@@ -428,7 +428,7 @@ component_ut_test_esp32c3:
 
 UT_001:
   extends: .unit_test_esp32_template
-  parallel: 49
+  parallel: 50
   tags:
     - ESP32_IDF
     - UT_T1_1
@@ -540,20 +540,6 @@ UT_022:
     - UT_T2_I2C
     - psram
 
-UT_023:
-  extends: .unit_test_esp32_template
-  parallel: 2
-  tags:
-    - ESP32_IDF
-    - UT_T1_MCPWM
-
-UT_024:
-  extends: .unit_test_esp32_template
-  tags:
-    - ESP32_IDF
-    - UT_T1_MCPWM
-    - psram
-
 UT_028:
   extends: .unit_test_esp32_template
   tags:

+ 1 - 0
components/driver/CMakeLists.txt

@@ -59,6 +59,7 @@ if(${target} STREQUAL "esp32s3")
                      "gdma.c"
                      "sdmmc_host.c"
                      "sdmmc_transaction.c"
+                     "mcpwm.c"
                      "spi_slave_hd.c"
                      "touch_sensor_common.c"
                     )

+ 68 - 2
components/driver/include/driver/mcpwm.h

@@ -8,7 +8,6 @@
 
 #include "soc/soc_caps.h"
 #if SOC_MCPWM_SUPPORTED
-
 #include "esp_err.h"
 #include "soc/soc.h"
 #include "driver/gpio.h"
@@ -20,7 +19,6 @@
 extern "C" {
 #endif
 
-
 /**
  * @brief IO signals for the MCPWM
  *
@@ -130,6 +128,15 @@ typedef enum {
     MCPWM_SELECT_F2,      /*!<Select F2 as input*/
 } mcpwm_fault_signal_t;
 
+/**
+ * @brief MCPWM select sync signal input
+ */
+typedef enum {
+    MCPWM_SELECT_SYNC0 = 4,  /*!<Select SYNC0 as input*/
+    MCPWM_SELECT_SYNC1,      /*!<Select SYNC1 as input*/
+    MCPWM_SELECT_SYNC2,      /*!<Select SYNC2 as input*/
+} mcpwm_sync_signal_t;
+
 /**
  * @brief MCPWM select triggering level of fault signal
  */
@@ -138,6 +145,62 @@ typedef enum {
     MCPWM_HIGH_LEVEL_TGR,     /*!<Fault condition occurs when fault input signal goes low to high*/
 } mcpwm_fault_input_level_t;
 
+/**
+ * @brief MCPWM select capture starts from which edge
+ */
+typedef enum {
+    MCPWM_NEG_EDGE = BIT(0),           /*!<Capture the negative edge*/
+    MCPWM_POS_EDGE = BIT(1),           /*!<Capture the positive edge*/
+    MCPWM_BOTH_EDGE = BIT(1) | BIT(0), /*!<Capture both edges*/
+} mcpwm_capture_on_edge_t;
+
+/**
+ * @brief Select type of MCPWM counter
+ */
+typedef enum {
+    MCPWM_FREEZE_COUNTER,   /*!<Counter freeze */
+    MCPWM_UP_COUNTER,       /*!<For asymmetric MCPWM*/
+    MCPWM_DOWN_COUNTER,     /*!<For asymmetric MCPWM*/
+    MCPWM_UP_DOWN_COUNTER,  /*!<For symmetric MCPWM, frequency is half of MCPWM frequency set*/
+    MCPWM_COUNTER_MAX,      /*!<Maximum counter mode*/
+} mcpwm_counter_type_t;
+
+/**
+ * @brief Select type of MCPWM duty cycle mode
+ */
+typedef enum {
+    MCPWM_DUTY_MODE_0 = 0, /*!<Active high duty, i.e. duty cycle proportional to high time for asymmetric MCPWM*/
+    MCPWM_DUTY_MODE_1,     /*!<Active low duty,  i.e. duty cycle proportional to low  time for asymmetric MCPWM, out of phase(inverted) MCPWM*/
+    MCPWM_HAL_GENERATOR_MODE_FORCE_LOW,
+    MCPWM_HAL_GENERATOR_MODE_FORCE_HIGH,
+    MCPWM_DUTY_MODE_MAX,   /*!<Num of duty cycle modes*/
+} mcpwm_duty_type_t;
+
+/**
+ * @brief MCPWM deadtime types, used to generate deadtime, RED refers to rising edge delay and FED refers to falling edge delay
+ */
+typedef enum {
+    MCPWM_DEADTIME_BYPASS = 0,          /*!<Bypass the deadtime*/
+    MCPWM_BYPASS_RED,                   /*!<MCPWMXA = no change, MCPWMXB = falling edge delay*/
+    MCPWM_BYPASS_FED,                   /*!<MCPWMXA = rising edge delay, MCPWMXB = no change*/
+    MCPWM_ACTIVE_HIGH_MODE,             /*!<MCPWMXA = rising edge delay,  MCPWMXB = falling edge delay*/
+    MCPWM_ACTIVE_LOW_MODE,              /*!<MCPWMXA = compliment of rising edge delay,  MCPWMXB = compliment of falling edge delay*/
+    MCPWM_ACTIVE_HIGH_COMPLIMENT_MODE,  /*!<MCPWMXA = rising edge delay,  MCPWMXB = compliment of falling edge delay*/
+    MCPWM_ACTIVE_LOW_COMPLIMENT_MODE,   /*!<MCPWMXA = compliment of rising edge delay,  MCPWMXB = falling edge delay*/
+    MCPWM_ACTIVE_RED_FED_FROM_PWMXA,    /*!<MCPWMXA = MCPWMXB = rising edge delay as well as falling edge delay, generated from MCPWMXA*/
+    MCPWM_ACTIVE_RED_FED_FROM_PWMXB,    /*!<MCPWMXA = MCPWMXB = rising edge delay as well as falling edge delay, generated from MCPWMXB*/
+    MCPWM_DEADTIME_TYPE_MAX,
+} mcpwm_deadtime_type_t;
+
+/**
+ * @brief MCPWM select action to be taken on the output when event happens
+ */
+typedef enum {
+    MCPWM_ACTION_NO_CHANGE = 0,  /*!<No change in the output*/
+    MCPWM_ACTION_FORCE_LOW,      /*!<Make output low*/
+    MCPWM_ACTION_FORCE_HIGH,     /*!<Make output high*/
+    MCPWM_ACTION_TOGGLE,         /*!<Make output toggle*/
+} mcpwm_output_action_t;
 
 /// @deprecated MCPWM select action to be taken on MCPWMXA when fault occurs
 typedef mcpwm_output_action_t mcpwm_action_on_pwmxa_t;
@@ -556,6 +619,9 @@ esp_err_t mcpwm_fault_deinit(mcpwm_unit_t mcpwm_num, mcpwm_fault_signal_t fault_
 /**
  * @brief Initialize capture submodule
  *
+ * @note Enabling capture feature could also enable the capture interrupt,
+ *       users have to register an interrupt handler by `mcpwm_isr_register`, and in there, query the capture data.
+ *
  * @param mcpwm_num set MCPWM unit(0-1)
  * @param cap_edge set capture edge, BIT(0) - negative edge, BIT(1) - positive edge
  * @param cap_sig capture pin, which needs to be enabled

+ 320 - 194
components/driver/mcpwm.c

@@ -5,39 +5,24 @@
  */
 
 #include <stdio.h>
-#include "esp_log.h"
-#include "esp_err.h"
+#include "sdkconfig.h"
 #include "freertos/FreeRTOS.h"
 #include "freertos/semphr.h"
 #include "freertos/xtensa_api.h"
 #include "freertos/task.h"
+#include "esp_log.h"
+#include "esp_err.h"
+#include "esp_check.h"
+#include "esp_rom_gpio.h"
 #include "soc/gpio_periph.h"
-#include "driver/mcpwm.h"
-#include "driver/periph_ctrl.h"
-#include "sdkconfig.h"
+#include "soc/mcpwm_periph.h"
 #include "hal/mcpwm_hal.h"
 #include "hal/gpio_hal.h"
-#include "esp_rom_gpio.h"
-
-typedef struct {
-    mcpwm_hal_context_t hal;
-    portMUX_TYPE spinlock;
-} mcpwm_context_t;
-
-#define CONTEXT_INITIALIZER() { \
-    .spinlock = portMUX_INITIALIZER_UNLOCKED, \
-    .hal = { \
-        .prescale = MCPWM_CLK_PRESCL, \
-    }, \
-}
-
+#include "hal/mcpwm_ll.h"
+#include "driver/mcpwm.h"
+#include "driver/periph_ctrl.h"
 
-static const char *MCPWM_TAG = "MCPWM";
-
-#define MCPWM_CHECK(a, str, ret_val) if (!(a)) {                       \
-    ESP_LOGE(MCPWM_TAG,"%s(%d): %s", __FUNCTION__, __LINE__, str);     \
-    return (ret_val);                                                  \
-    }
+static const char *TAG = "mcpwm";
 
 #define MCPWM_DRIVER_INIT_ERROR "MCPWM DRIVER NOT INITIALIZED"
 #define MCPWM_GROUP_NUM_ERROR   "MCPWM GROUP NUM ERROR"
@@ -50,38 +35,43 @@ static const char *MCPWM_TAG = "MCPWM";
 #define MCPWM_GEN_ERROR         "MCPWM GENERATOR ERROR"
 #define MCPWM_DT_ERROR          "MCPWM DEADTIME TYPE ERROR"
 
-#define MCPWM_CLK_PRESCL 15       //MCPWM clock prescale
-#define TIMER_CLK_PRESCALE 9      //MCPWM timer prescales
-#define MCPWM_CLK (MCPWM_BASE_CLK/(MCPWM_CLK_PRESCL +1))
-#define MCPWM_PIN_IGNORE    (-1)
-#define OFFSET_FOR_GPIO_IDX_1  6
-#define OFFSET_FOR_GPIO_IDX_2 75
+#define MCPWM_GROUP_CLK_HZ (SOC_MCPWM_BASE_CLK_HZ / 16)
+#define MCPWM_TIMER_CLK_HZ (MCPWM_GROUP_CLK_HZ / 10)
 
 _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.");
 
-#define MCPWM_TIMER_ID_CHECK(mcpwm_num, timer_num) do {\
-    MCPWM_CHECK((mcpwm_num) < SOC_MCPWM_GROUPS, MCPWM_GROUP_NUM_ERROR, ESP_ERR_INVALID_ARG); \
-    MCPWM_CHECK((timer_num) < SOC_MCPWM_TIMERS_PER_GROUP, MCPWM_TIMER_ERROR, ESP_ERR_INVALID_ARG); \
-} while(0)
+#define MCPWM_TIMER_ID_CHECK(mcpwm_num, timer_num)                                                                  \
+    do {                                                                                                            \
+        ESP_RETURN_ON_FALSE((mcpwm_num) < SOC_MCPWM_GROUPS, ESP_ERR_INVALID_ARG, TAG, MCPWM_GROUP_NUM_ERROR);       \
+        ESP_RETURN_ON_FALSE((timer_num) < SOC_MCPWM_TIMERS_PER_GROUP, ESP_ERR_INVALID_ARG, TAG, MCPWM_TIMER_ERROR); \
+    } while (0)
 
-#define MCPWM_TIMER_CHECK(mcpwm_num, timer_num) do{\
-    MCPWM_TIMER_ID_CHECK(mcpwm_num, timer_num); \
-    MCPWM_CHECK(context[mcpwm_num].hal.dev != NULL, MCPWM_DRIVER_INIT_ERROR, ESP_ERR_INVALID_STATE); \
-} while(0)
+#define MCPWM_TIMER_CHECK(mcpwm_num, timer_num)                                                               \
+    do {                                                                                                      \
+        MCPWM_TIMER_ID_CHECK(mcpwm_num, timer_num);                                                           \
+        ESP_RETURN_ON_FALSE(context[mcpwm_num].hal.dev, ESP_ERR_INVALID_STATE, TAG, MCPWM_DRIVER_INIT_ERROR); \
+    } while (0)
 
-#define MCPWM_GEN_CHECK(mcpwm_num, timer_num, gen) do{ \
-    MCPWM_TIMER_CHECK(mcpwm_num, timer_num); \
-    MCPWM_CHECK(gen < MCPWM_GEN_MAX, MCPWM_GEN_ERROR, ESP_ERR_INVALID_ARG); \
-} while(0)
+#define MCPWM_GEN_CHECK(mcpwm_num, timer_num, gen)                                             \
+    do {                                                                                       \
+        MCPWM_TIMER_CHECK(mcpwm_num, timer_num);                                               \
+        ESP_RETURN_ON_FALSE((gen) < MCPWM_GEN_MAX, ESP_ERR_INVALID_ARG, TAG, MCPWM_GEN_ERROR); \
+    } while (0)
 
+typedef struct {
+    mcpwm_hal_context_t hal;
+    portMUX_TYPE spinlock;
+} mcpwm_context_t;
 
 static mcpwm_context_t context[SOC_MCPWM_GROUPS] = {
-    CONTEXT_INITIALIZER(),
-    CONTEXT_INITIALIZER(),
+    [0 ... SOC_MCPWM_GROUPS - 1] = {
+        .spinlock = portMUX_INITIALIZER_UNLOCKED,
+    }
 };
 
+typedef void (*mcpwm_ll_gen_set_event_action_t)(mcpwm_dev_t *mcpwm, int op, int gen, int action);
 
 static inline void mcpwm_critical_enter(mcpwm_unit_t mcpwm_num)
 {
@@ -99,36 +89,35 @@ esp_err_t mcpwm_gpio_init(mcpwm_unit_t mcpwm_num, mcpwm_io_signals_t io_signal,
         return ESP_OK;
     }
 
-    MCPWM_CHECK(mcpwm_num < SOC_MCPWM_GROUPS, MCPWM_GROUP_NUM_ERROR, ESP_ERR_INVALID_ARG);
-    MCPWM_CHECK((GPIO_IS_VALID_GPIO(gpio_num)), MCPWM_GPIO_ERROR, ESP_ERR_INVALID_ARG);
+    ESP_RETURN_ON_FALSE(mcpwm_num < SOC_MCPWM_GROUPS, ESP_ERR_INVALID_ARG, TAG, MCPWM_GROUP_NUM_ERROR);
+    ESP_RETURN_ON_FALSE(GPIO_IS_VALID_GPIO(gpio_num), ESP_ERR_INVALID_ARG, TAG, MCPWM_GPIO_ERROR);
 
-    // we enabled both input and output mode for GPIO used here, which can help to simulate trigger source especially in test code
-    gpio_hal_iomux_func_sel(GPIO_PIN_MUX_REG[gpio_num], PIN_FUNC_GPIO);
     if (io_signal <= MCPWM2B) { // Generator output signal
-        MCPWM_CHECK((GPIO_IS_VALID_OUTPUT_GPIO(gpio_num)), MCPWM_GPIO_ERROR, ESP_ERR_INVALID_ARG);
-        gpio_set_direction(gpio_num, GPIO_MODE_INPUT_OUTPUT);
+        ESP_RETURN_ON_FALSE(GPIO_IS_VALID_OUTPUT_GPIO(gpio_num), ESP_ERR_INVALID_ARG, TAG, MCPWM_GPIO_ERROR);
+        gpio_set_direction(gpio_num, GPIO_MODE_OUTPUT);
         int operator_id = io_signal / 2;
         int generator_id = io_signal % 2;
         esp_rom_gpio_connect_out_signal(gpio_num, mcpwm_periph_signals.groups[mcpwm_num].operators[operator_id].generators[generator_id].pwm_sig, 0, 0);
     } else if (io_signal <= MCPWM_SYNC_2) { // External sync input signal
-        gpio_set_direction(gpio_num, GPIO_MODE_INPUT_OUTPUT);
+        gpio_set_direction(gpio_num, GPIO_MODE_INPUT);
         int ext_sync_id = io_signal - MCPWM_SYNC_0;
         esp_rom_gpio_connect_in_signal(gpio_num, mcpwm_periph_signals.groups[mcpwm_num].ext_syncers[ext_sync_id].sync_sig, 0);
     } else if (io_signal <= MCPWM_FAULT_2) { // Fault input signal
-        gpio_set_direction(gpio_num, GPIO_MODE_INPUT_OUTPUT);
+        gpio_set_direction(gpio_num, GPIO_MODE_INPUT);
         int fault_id = io_signal - MCPWM_FAULT_0;
         esp_rom_gpio_connect_in_signal(gpio_num, mcpwm_periph_signals.groups[mcpwm_num].detectors[fault_id].fault_sig, 0);
     } else if (io_signal >= MCPWM_CAP_0 && io_signal <= MCPWM_CAP_2) { // Capture input signal
-        gpio_set_direction(gpio_num, GPIO_MODE_INPUT_OUTPUT);
+        gpio_set_direction(gpio_num, GPIO_MODE_INPUT);
         int capture_id = io_signal - MCPWM_CAP_0;
         esp_rom_gpio_connect_in_signal(gpio_num, mcpwm_periph_signals.groups[mcpwm_num].captures[capture_id].cap_sig, 0);
     }
+    gpio_hal_iomux_func_sel(GPIO_PIN_MUX_REG[gpio_num], PIN_FUNC_GPIO);
     return ESP_OK;
 }
 
 esp_err_t mcpwm_set_pin(mcpwm_unit_t mcpwm_num, const mcpwm_pin_config_t *mcpwm_pin)
 {
-    MCPWM_CHECK(mcpwm_num < SOC_MCPWM_GROUPS, MCPWM_GROUP_NUM_ERROR, ESP_ERR_INVALID_ARG);
+    ESP_RETURN_ON_FALSE(mcpwm_num < SOC_MCPWM_GROUPS, ESP_ERR_INVALID_ARG, TAG, MCPWM_GROUP_NUM_ERROR);
     mcpwm_gpio_init(mcpwm_num, MCPWM0A, mcpwm_pin->mcpwm0a_out_num);    //MCPWM0A
     mcpwm_gpio_init(mcpwm_num, MCPWM0B, mcpwm_pin->mcpwm0b_out_num);    //MCPWM0B
     mcpwm_gpio_init(mcpwm_num, MCPWM1A, mcpwm_pin->mcpwm1a_out_num);    //MCPWM1A
@@ -152,7 +141,7 @@ esp_err_t mcpwm_start(mcpwm_unit_t mcpwm_num, mcpwm_timer_t timer_num)
     MCPWM_TIMER_CHECK(mcpwm_num, timer_num);
 
     mcpwm_critical_enter(mcpwm_num);
-    mcpwm_hal_timer_start(&context[mcpwm_num].hal, timer_num);
+    mcpwm_ll_timer_set_operate_command(context[mcpwm_num].hal.dev, timer_num, MCPWM_TIMER_START_NO_STOP);
     mcpwm_critical_exit(mcpwm_num);
     return ESP_OK;
 }
@@ -162,7 +151,7 @@ esp_err_t mcpwm_stop(mcpwm_unit_t mcpwm_num, mcpwm_timer_t timer_num)
     MCPWM_TIMER_CHECK(mcpwm_num, timer_num);
 
     mcpwm_critical_enter(mcpwm_num);
-    mcpwm_hal_timer_stop(&context[mcpwm_num].hal, timer_num);
+    mcpwm_ll_timer_set_operate_command(context[mcpwm_num].hal.dev, timer_num, MCPWM_TIMER_STOP_AT_ZERO);
     mcpwm_critical_exit(mcpwm_num);
     return ESP_OK;
 }
@@ -175,10 +164,22 @@ esp_err_t mcpwm_set_frequency(mcpwm_unit_t mcpwm_num, mcpwm_timer_t timer_num, u
     mcpwm_hal_context_t *hal = &context[mcpwm_num].hal;
 
     mcpwm_critical_enter(mcpwm_num);
-    hal->timer[timer_num].freq = frequency;
-    mcpwm_hal_timer_update_basic(hal, timer_num);
-    //update the operator to update the duty
-    mcpwm_hal_operator_update_basic(hal, op);
+
+    mcpwm_ll_timer_update_period_at_once(hal->dev, timer_num);
+    uint32_t previous_peak = mcpwm_ll_timer_get_peak(hal->dev, timer_num, false);
+    uint32_t new_peak = MCPWM_TIMER_CLK_HZ / frequency;
+    mcpwm_ll_timer_set_peak(hal->dev, timer_num, new_peak, false);
+    // keep the duty cycle unchanged
+    float scale = ((float)new_peak) / previous_peak;
+    // the driver currently always use the comparator A for PWMxA output, and comparator B for PWMxB output
+    uint32_t previous_cmp_a = mcpwm_ll_operator_get_compare_value(hal->dev, op, 0);
+    uint32_t previous_cmp_b = mcpwm_ll_operator_get_compare_value(hal->dev, op, 1);
+    // update compare value immediately
+    mcpwm_ll_operator_update_compare_at_once(hal->dev, op, 0);
+    mcpwm_ll_operator_update_compare_at_once(hal->dev, op, 1);
+    mcpwm_ll_operator_set_compare_value(hal->dev, op, 0, (uint32_t)(previous_cmp_a * scale));
+    mcpwm_ll_operator_set_compare_value(hal->dev, op, 1, (uint32_t)(previous_cmp_b * scale));
+
     mcpwm_critical_exit(mcpwm_num);
     return ESP_OK;
 }
@@ -193,8 +194,9 @@ esp_err_t mcpwm_set_duty(mcpwm_unit_t mcpwm_num, mcpwm_timer_t timer_num, mcpwm_
     mcpwm_hal_context_t *hal = &context[mcpwm_num].hal;
 
     mcpwm_critical_enter(mcpwm_num);
-    hal->op[op].duty[cmp] = duty;
-    mcpwm_hal_operator_update_comparator(hal, op, gen);
+    uint32_t set_duty = mcpwm_ll_timer_get_peak(hal->dev, timer_num, false) * duty / 100;
+    mcpwm_ll_operator_set_compare_value(hal->dev, op, cmp, set_duty);
+    mcpwm_ll_operator_enable_update_compare_on_tez(hal->dev, op, cmp, true);
     mcpwm_critical_exit(mcpwm_num);
     return ESP_OK;
 }
@@ -209,8 +211,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);
-    hal->op[op].duty[cmp] = (100 * duty_in_us * hal->timer[timer_num].freq) / (1000 * 1000.);
-    mcpwm_hal_operator_update_comparator(hal, op, gen);
+    // the timer resolution is fixed to 1us in the driver, so duty_in_us is the same to compare value
+    mcpwm_ll_operator_set_compare_value(hal->dev, op, cmp, duty_in_us);
+    mcpwm_ll_operator_enable_update_compare_on_tez(hal->dev, op, cmp, true);
     mcpwm_critical_exit(mcpwm_num);
     return ESP_OK;
 }
@@ -221,67 +224,122 @@ esp_err_t mcpwm_set_duty_type(mcpwm_unit_t mcpwm_num, mcpwm_timer_t timer_num, m
     //the driver currently always use the timer x for operator x
     const int op = timer_num;
     MCPWM_GEN_CHECK(mcpwm_num, timer_num, gen);
-    MCPWM_CHECK(duty_type < MCPWM_DUTY_MODE_MAX, MCPWM_DUTY_TYPE_ERROR, ESP_ERR_INVALID_ARG);
+    ESP_RETURN_ON_FALSE(duty_type < MCPWM_DUTY_MODE_MAX, ESP_ERR_INVALID_ARG, TAG, MCPWM_DUTY_TYPE_ERROR);
     mcpwm_hal_context_t *hal = &context[mcpwm_num].hal;
 
+    //the driver currently always use the comparator A for PWMxA output, and comparator B for PWMxB output
     mcpwm_critical_enter(mcpwm_num);
-    hal->op[op].gen[gen] = (mcpwm_hal_generator_config_t) {
-        .comparator = gen, //the driver currently always use the comparator A for PWMxA output, and comparator B for PWMxB output
-        .duty_type = duty_type,
-    };
-    mcpwm_hal_operator_update_generator(hal, op, gen);
+    switch (mcpwm_ll_timer_get_count_mode(hal->dev, timer_num)) {
+    case MCPWM_TIMER_COUNT_MODE_UP:
+        if (duty_type == MCPWM_DUTY_MODE_0) {
+            mcpwm_ll_generator_set_action_on_timer_event(hal->dev, op, gen, MCPWM_TIMER_DIRECTION_UP, MCPWM_TIMER_EVENT_ZERO, MCPWM_GEN_ACTION_HIGH);
+            mcpwm_ll_generator_set_action_on_timer_event(hal->dev, op, gen, MCPWM_TIMER_DIRECTION_UP, MCPWM_TIMER_EVENT_PEAK, MCPWM_GEN_ACTION_KEEP);
+            mcpwm_ll_generator_set_action_on_compare_event(hal->dev, op, gen, MCPWM_TIMER_DIRECTION_UP, gen, MCPWM_ACTION_FORCE_LOW);
+        } else if (duty_type == MCPWM_DUTY_MODE_1) {
+            mcpwm_ll_generator_set_action_on_timer_event(hal->dev, op, gen, MCPWM_TIMER_DIRECTION_UP, MCPWM_TIMER_EVENT_ZERO, MCPWM_GEN_ACTION_LOW);
+            mcpwm_ll_generator_set_action_on_timer_event(hal->dev, op, gen, MCPWM_TIMER_DIRECTION_UP, MCPWM_TIMER_EVENT_PEAK, MCPWM_ACTION_NO_CHANGE);
+            mcpwm_ll_generator_set_action_on_compare_event(hal->dev, op, gen, MCPWM_TIMER_DIRECTION_UP, gen, MCPWM_ACTION_FORCE_HIGH);
+        } else if (duty_type == MCPWM_HAL_GENERATOR_MODE_FORCE_LOW) {
+            mcpwm_ll_generator_set_action_on_timer_event(hal->dev, op, gen, MCPWM_TIMER_DIRECTION_UP, MCPWM_TIMER_EVENT_ZERO, MCPWM_ACTION_FORCE_LOW);
+            mcpwm_ll_generator_set_action_on_timer_event(hal->dev, op, gen, MCPWM_TIMER_DIRECTION_UP, MCPWM_TIMER_EVENT_PEAK, MCPWM_ACTION_FORCE_LOW);
+            mcpwm_ll_generator_set_action_on_compare_event(hal->dev, op, gen, MCPWM_TIMER_DIRECTION_UP, gen, MCPWM_ACTION_FORCE_LOW);
+        } else if (duty_type == MCPWM_HAL_GENERATOR_MODE_FORCE_HIGH) {
+            mcpwm_ll_generator_set_action_on_timer_event(hal->dev, op, gen, MCPWM_TIMER_DIRECTION_UP, MCPWM_TIMER_EVENT_ZERO, MCPWM_ACTION_FORCE_HIGH);
+            mcpwm_ll_generator_set_action_on_timer_event(hal->dev, op, gen, MCPWM_TIMER_DIRECTION_UP, MCPWM_TIMER_EVENT_PEAK, MCPWM_ACTION_FORCE_HIGH);
+            mcpwm_ll_generator_set_action_on_compare_event(hal->dev, op, gen, MCPWM_TIMER_DIRECTION_UP, gen, MCPWM_ACTION_FORCE_HIGH);
+        }
+        break;
+    case MCPWM_TIMER_COUNT_MODE_DOWN:
+        if (duty_type == MCPWM_DUTY_MODE_0) {
+            mcpwm_ll_generator_set_action_on_timer_event(hal->dev, op, gen, MCPWM_TIMER_DIRECTION_DOWN, MCPWM_TIMER_EVENT_PEAK, MCPWM_ACTION_FORCE_LOW);
+            mcpwm_ll_generator_set_action_on_timer_event(hal->dev, op, gen, MCPWM_TIMER_DIRECTION_DOWN, MCPWM_TIMER_EVENT_ZERO, MCPWM_ACTION_NO_CHANGE);
+            mcpwm_ll_generator_set_action_on_compare_event(hal->dev, op, gen, MCPWM_TIMER_DIRECTION_DOWN, gen, MCPWM_ACTION_FORCE_HIGH);
+        } else if (duty_type == MCPWM_DUTY_MODE_1) {
+            mcpwm_ll_generator_set_action_on_timer_event(hal->dev, op, gen, MCPWM_TIMER_DIRECTION_DOWN, MCPWM_TIMER_EVENT_PEAK, MCPWM_ACTION_FORCE_HIGH);
+            mcpwm_ll_generator_set_action_on_timer_event(hal->dev, op, gen, MCPWM_TIMER_DIRECTION_DOWN, MCPWM_TIMER_EVENT_ZERO, MCPWM_ACTION_NO_CHANGE);
+            mcpwm_ll_generator_set_action_on_compare_event(hal->dev, op, gen, MCPWM_TIMER_DIRECTION_DOWN, gen, MCPWM_ACTION_FORCE_LOW);
+        } else if (duty_type == MCPWM_HAL_GENERATOR_MODE_FORCE_LOW) {
+            mcpwm_ll_generator_set_action_on_timer_event(hal->dev, op, gen, MCPWM_TIMER_DIRECTION_DOWN, MCPWM_TIMER_EVENT_PEAK, MCPWM_ACTION_FORCE_LOW);
+            mcpwm_ll_generator_set_action_on_timer_event(hal->dev, op, gen, MCPWM_TIMER_DIRECTION_DOWN, MCPWM_TIMER_EVENT_ZERO, MCPWM_ACTION_FORCE_LOW);
+            mcpwm_ll_generator_set_action_on_compare_event(hal->dev, op, gen, MCPWM_TIMER_DIRECTION_DOWN, gen, MCPWM_ACTION_FORCE_LOW);
+        } else if (duty_type == MCPWM_HAL_GENERATOR_MODE_FORCE_HIGH) {
+            mcpwm_ll_generator_set_action_on_timer_event(hal->dev, op, gen, MCPWM_TIMER_DIRECTION_DOWN, MCPWM_TIMER_EVENT_PEAK, MCPWM_ACTION_FORCE_HIGH);
+            mcpwm_ll_generator_set_action_on_timer_event(hal->dev, op, gen, MCPWM_TIMER_DIRECTION_DOWN, MCPWM_TIMER_EVENT_ZERO, MCPWM_ACTION_FORCE_HIGH);
+            mcpwm_ll_generator_set_action_on_compare_event(hal->dev, op, gen, MCPWM_TIMER_DIRECTION_DOWN, gen, MCPWM_ACTION_FORCE_HIGH);
+        }
+        break;
+    case MCPWM_TIMER_COUNT_MODE_UP_DOWN:
+        if (duty_type == MCPWM_DUTY_MODE_0) {
+            mcpwm_ll_generator_set_action_on_timer_event(hal->dev, op, gen, MCPWM_TIMER_DIRECTION_UP, MCPWM_TIMER_EVENT_ZERO, MCPWM_ACTION_FORCE_HIGH);
+            mcpwm_ll_generator_set_action_on_compare_event(hal->dev, op, gen, MCPWM_TIMER_DIRECTION_UP, gen, MCPWM_ACTION_FORCE_LOW);
+            mcpwm_ll_generator_set_action_on_compare_event(hal->dev, op, gen, MCPWM_TIMER_DIRECTION_DOWN, gen, MCPWM_ACTION_FORCE_HIGH);
+        } else if (duty_type == MCPWM_DUTY_MODE_1) {
+            mcpwm_ll_generator_set_action_on_timer_event(hal->dev, op, gen, MCPWM_TIMER_DIRECTION_UP, MCPWM_TIMER_EVENT_ZERO, MCPWM_ACTION_FORCE_LOW);
+            mcpwm_ll_generator_set_action_on_compare_event(hal->dev, op, gen, MCPWM_TIMER_DIRECTION_UP, gen, MCPWM_ACTION_FORCE_HIGH);
+            mcpwm_ll_generator_set_action_on_compare_event(hal->dev, op, gen, MCPWM_TIMER_DIRECTION_DOWN, gen, MCPWM_ACTION_FORCE_LOW);
+        } else if (duty_type == MCPWM_HAL_GENERATOR_MODE_FORCE_LOW) {
+            mcpwm_ll_generator_set_action_on_timer_event(hal->dev, op, gen, MCPWM_TIMER_DIRECTION_DOWN, MCPWM_TIMER_EVENT_PEAK, MCPWM_ACTION_FORCE_LOW);
+            mcpwm_ll_generator_set_action_on_timer_event(hal->dev, op, gen, MCPWM_TIMER_DIRECTION_DOWN, MCPWM_TIMER_EVENT_ZERO, MCPWM_ACTION_FORCE_LOW);
+            mcpwm_ll_generator_set_action_on_timer_event(hal->dev, op, gen, MCPWM_TIMER_DIRECTION_UP, MCPWM_TIMER_EVENT_ZERO, MCPWM_ACTION_FORCE_LOW);
+            mcpwm_ll_generator_set_action_on_timer_event(hal->dev, op, gen, MCPWM_TIMER_DIRECTION_UP, MCPWM_TIMER_EVENT_PEAK, MCPWM_ACTION_FORCE_LOW);
+            mcpwm_ll_generator_set_action_on_compare_event(hal->dev, op, gen, MCPWM_TIMER_DIRECTION_DOWN, gen, MCPWM_ACTION_FORCE_LOW);
+            mcpwm_ll_generator_set_action_on_compare_event(hal->dev, op, gen, MCPWM_TIMER_DIRECTION_UP, gen, MCPWM_ACTION_FORCE_LOW);
+        } else if (duty_type == MCPWM_HAL_GENERATOR_MODE_FORCE_HIGH) {
+            mcpwm_ll_generator_set_action_on_timer_event(hal->dev, op, gen, MCPWM_TIMER_DIRECTION_DOWN, MCPWM_TIMER_EVENT_PEAK, MCPWM_ACTION_FORCE_HIGH);
+            mcpwm_ll_generator_set_action_on_timer_event(hal->dev, op, gen, MCPWM_TIMER_DIRECTION_DOWN, MCPWM_TIMER_EVENT_ZERO, MCPWM_ACTION_FORCE_HIGH);
+            mcpwm_ll_generator_set_action_on_timer_event(hal->dev, op, gen, MCPWM_TIMER_DIRECTION_UP, MCPWM_TIMER_EVENT_ZERO, MCPWM_ACTION_FORCE_HIGH);
+            mcpwm_ll_generator_set_action_on_timer_event(hal->dev, op, gen, MCPWM_TIMER_DIRECTION_UP, MCPWM_TIMER_EVENT_PEAK, MCPWM_ACTION_FORCE_HIGH);
+            mcpwm_ll_generator_set_action_on_compare_event(hal->dev, op, gen, MCPWM_TIMER_DIRECTION_DOWN, gen, MCPWM_ACTION_FORCE_HIGH);
+            mcpwm_ll_generator_set_action_on_compare_event(hal->dev, op, gen, MCPWM_TIMER_DIRECTION_UP, gen, MCPWM_ACTION_FORCE_HIGH);
+        }
+        break;
+    default:
+        break;
+    }
     mcpwm_critical_exit(mcpwm_num);
     return ESP_OK;
 }
 
-
 esp_err_t mcpwm_init(mcpwm_unit_t mcpwm_num, mcpwm_timer_t timer_num, const mcpwm_config_t *mcpwm_conf)
 {
-    //the driver currently always use the timer x for operator x
     const int op = timer_num;
     MCPWM_TIMER_ID_CHECK(mcpwm_num, op);
-    periph_module_enable(mcpwm_periph_signals.groups[mcpwm_num].module);
-
     mcpwm_hal_context_t *hal = &context[mcpwm_num].hal;
-    mcpwm_hal_init_config_t init_config = {
-        .host_id = mcpwm_num,
+    periph_module_enable(mcpwm_periph_signals.groups[mcpwm_num].module);
+    mcpwm_hal_init_config_t config = {
+        .host_id = mcpwm_num
     };
+    mcpwm_hal_init(hal, &config);
 
     mcpwm_critical_enter(mcpwm_num);
-    mcpwm_hal_init(hal, &init_config);
-    mcpwm_hal_hw_init(hal);
-
-    hal->timer[timer_num].timer_prescale = TIMER_CLK_PRESCALE;
-    hal->timer[timer_num].freq = mcpwm_conf->frequency;
-    hal->timer[timer_num].count_mode = mcpwm_conf->counter_mode;
-
-    //the driver currently always use the timer x for operator x
-    hal->op[op].timer = timer_num;
-    hal->op[op].duty[0] = mcpwm_conf->cmpr_a;
-    hal->op[op].duty[1] = mcpwm_conf->cmpr_b;
-
-    mcpwm_hal_timer_update_basic(hal, timer_num);
-    //update the comparer to keep the same duty rate
-    mcpwm_hal_operator_update_basic(hal, op);
-
-    for (int gen = 0; gen < SOC_MCPWM_GENERATORS_PER_OPERATOR; gen++) {
-        hal->op[op].gen[gen] = (mcpwm_hal_generator_config_t) {
-            .comparator = gen, //the driver currently always use the comparator A for PWMxA output, and comparator B for PWMxB output
-            .duty_type = mcpwm_conf->duty_mode,
-        };
-        mcpwm_hal_operator_update_generator(hal, op, gen);
-    }
-
-    mcpwm_hal_timer_start(hal, timer_num);
+    mcpwm_ll_group_set_clock(hal->dev, MCPWM_GROUP_CLK_HZ);
+    mcpwm_ll_group_enable_shadow_mode(hal->dev);
+    mcpwm_ll_group_flush_shadow(hal->dev);
+    mcpwm_ll_timer_set_clock(hal->dev, timer_num, MCPWM_GROUP_CLK_HZ, MCPWM_TIMER_CLK_HZ);
+    mcpwm_ll_timer_set_count_mode(hal->dev, timer_num, mcpwm_conf->counter_mode);
+    mcpwm_ll_timer_update_period_at_once(hal->dev, timer_num);
+    mcpwm_ll_timer_set_peak(hal->dev, timer_num, MCPWM_TIMER_CLK_HZ / mcpwm_conf->frequency, false);
+    mcpwm_ll_operator_select_timer(hal->dev, timer_num, timer_num); //the driver currently always use the timer x for operator x
     mcpwm_critical_exit(mcpwm_num);
 
+    mcpwm_set_duty(mcpwm_num, timer_num, 0, mcpwm_conf->cmpr_a);
+    mcpwm_set_duty(mcpwm_num, timer_num, 1, mcpwm_conf->cmpr_b);
+    mcpwm_set_duty_type(mcpwm_num, timer_num, 0, mcpwm_conf->duty_mode);
+    mcpwm_set_duty_type(mcpwm_num, timer_num, 1, mcpwm_conf->duty_mode);
+    mcpwm_start(mcpwm_num, timer_num);
+
     return ESP_OK;
 }
 
 uint32_t mcpwm_get_frequency(mcpwm_unit_t mcpwm_num, mcpwm_timer_t timer_num)
 {
     MCPWM_TIMER_CHECK(mcpwm_num, timer_num);
-    return context[mcpwm_num].hal.timer[timer_num].freq;
+    mcpwm_hal_context_t *hal = &context[mcpwm_num].hal;
+    mcpwm_critical_enter(mcpwm_num);
+    unsigned long long group_clock = mcpwm_ll_group_get_clock(hal->dev);
+    unsigned long long timer_clock = mcpwm_ll_timer_get_clock(hal->dev, timer_num, group_clock);
+    mcpwm_critical_exit(mcpwm_num);
+    return (uint32_t)timer_clock;
 }
 
 float mcpwm_get_duty(mcpwm_unit_t mcpwm_num, mcpwm_timer_t timer_num, mcpwm_generator_t gen)
@@ -289,44 +347,23 @@ float mcpwm_get_duty(mcpwm_unit_t mcpwm_num, mcpwm_timer_t timer_num, mcpwm_gene
     //the driver currently always use the timer x for operator x
     const int op = timer_num;
     MCPWM_GEN_CHECK(mcpwm_num, timer_num, gen);
-    return context[mcpwm_num].hal.op[op].duty[gen];
+    mcpwm_hal_context_t *hal = &context[mcpwm_num].hal;
+    mcpwm_critical_enter(mcpwm_num);
+    float duty = 100.0 * mcpwm_ll_operator_get_compare_value(hal->dev, op, gen) / mcpwm_ll_timer_get_peak(hal->dev, timer_num, false);
+    mcpwm_critical_exit(mcpwm_num);
+    return duty;
 }
 
-STATIC_HAL_REG_CHECK(MCPWM, MCPWM_GEN_A, 0);
-STATIC_HAL_REG_CHECK(MCPWM, MCPWM_GEN_B, 1);
-
 esp_err_t mcpwm_set_signal_high(mcpwm_unit_t mcpwm_num, mcpwm_timer_t timer_num, mcpwm_generator_t gen)
 {
     //the driver currently always use the timer x for operator x
-    const int op = timer_num;
-    MCPWM_GEN_CHECK(mcpwm_num, timer_num, gen);
-    mcpwm_hal_context_t *hal = &context[mcpwm_num].hal;
-
-    mcpwm_critical_enter(mcpwm_num);
-    hal->op[op].gen[gen] = (mcpwm_hal_generator_config_t) {
-        .comparator = gen, //the driver currently always use the comparator A for PWMxA output, and comparator B for PWMxB output
-        .duty_type = MCPWM_HAL_GENERATOR_MODE_FORCE_HIGH,
-    };
-    mcpwm_hal_operator_update_generator(hal, op, gen);
-    mcpwm_critical_exit(mcpwm_num);
-    return ESP_OK;
+    return mcpwm_set_duty_type(mcpwm_num, timer_num, gen, MCPWM_HAL_GENERATOR_MODE_FORCE_HIGH);
 }
 
 esp_err_t mcpwm_set_signal_low(mcpwm_unit_t mcpwm_num, mcpwm_timer_t timer_num, mcpwm_generator_t gen)
 {
     //the driver currently always use the timer x for operator x
-    const int op = timer_num;
-    MCPWM_GEN_CHECK(mcpwm_num, timer_num, gen);
-    mcpwm_hal_context_t *hal = &context[mcpwm_num].hal;
-
-    mcpwm_critical_enter(mcpwm_num);
-    hal->op[op].gen[gen] = (mcpwm_hal_generator_config_t) {
-        .comparator = gen, //the driver currently always use the comparator A for PWMxA output, and comparator B for PWMxB output
-        .duty_type = MCPWM_HAL_GENERATOR_MODE_FORCE_LOW,
-    };
-    mcpwm_hal_operator_update_generator(hal, op, gen);
-    mcpwm_critical_exit(mcpwm_num);
-    return ESP_OK;
+    return mcpwm_set_duty_type(mcpwm_num, timer_num, gen, MCPWM_HAL_GENERATOR_MODE_FORCE_LOW);
 }
 
 esp_err_t mcpwm_carrier_enable(mcpwm_unit_t mcpwm_num, mcpwm_timer_t timer_num)
@@ -360,7 +397,7 @@ esp_err_t mcpwm_carrier_set_period(mcpwm_unit_t mcpwm_num, mcpwm_timer_t timer_n
     MCPWM_TIMER_CHECK(mcpwm_num, timer_num);
 
     mcpwm_critical_enter(mcpwm_num);
-    mcpwm_ll_carrier_set_prescale(context[mcpwm_num].hal.dev, op, carrier_period);
+    mcpwm_ll_carrier_set_prescale(context[mcpwm_num].hal.dev, op, carrier_period + 1);
     mcpwm_critical_exit(mcpwm_num);
     return ESP_OK;
 }
@@ -384,7 +421,7 @@ esp_err_t mcpwm_carrier_oneshot_mode_enable(mcpwm_unit_t mcpwm_num, mcpwm_timer_
     MCPWM_TIMER_CHECK(mcpwm_num, timer_num);
 
     mcpwm_critical_enter(mcpwm_num);
-    mcpwm_ll_carrier_set_oneshot_width(context[mcpwm_num].hal.dev, op, pulse_width);
+    mcpwm_ll_carrier_set_oneshot_width(context[mcpwm_num].hal.dev, op, pulse_width + 1);
     mcpwm_critical_exit(mcpwm_num);
     return ESP_OK;
 }
@@ -414,20 +451,20 @@ esp_err_t mcpwm_carrier_init(mcpwm_unit_t mcpwm_num, mcpwm_timer_t timer_num, co
     MCPWM_TIMER_CHECK(mcpwm_num, timer_num);
     mcpwm_hal_context_t *hal = &context[mcpwm_num].hal;
 
-    mcpwm_hal_carrier_conf_t carrier = {
-        .period = carrier_conf->carrier_period,
-        .duty = carrier_conf->carrier_duty,
-        .inverted = carrier_conf->carrier_ivt_mode,
-    };
+    mcpwm_carrier_enable(mcpwm_num, timer_num);
+    mcpwm_carrier_set_period(mcpwm_num, timer_num, carrier_conf->carrier_period);
+    mcpwm_carrier_set_duty_cycle(mcpwm_num, timer_num, carrier_conf->carrier_duty);
     if (carrier_conf->carrier_os_mode == MCPWM_ONESHOT_MODE_EN) {
-        carrier.oneshot_pulse_width = carrier_conf->pulse_width_in_os;
+        mcpwm_carrier_oneshot_mode_enable(mcpwm_num, timer_num, carrier_conf->pulse_width_in_os);
     } else {
-        carrier.oneshot_pulse_width = 0;
+        mcpwm_carrier_oneshot_mode_disable(mcpwm_num, timer_num);
     }
+    mcpwm_carrier_output_invert(mcpwm_num, timer_num, carrier_conf->carrier_ivt_mode);
 
     mcpwm_critical_enter(mcpwm_num);
-    mcpwm_hal_operator_enable_carrier(hal, op, &carrier);
+    mcpwm_ll_carrier_in_invert(hal->dev, op, false);
     mcpwm_critical_exit(mcpwm_num);
+
     return ESP_OK;
 }
 
@@ -437,16 +474,82 @@ esp_err_t mcpwm_deadtime_enable(mcpwm_unit_t mcpwm_num, mcpwm_timer_t timer_num,
     //the driver currently always use the timer x for operator x
     const int op = timer_num;
     MCPWM_TIMER_CHECK(mcpwm_num, timer_num);
-    MCPWM_CHECK(dt_mode < MCPWM_DEADTIME_TYPE_MAX, MCPWM_DT_ERROR, ESP_ERR_INVALID_ARG);
+    ESP_RETURN_ON_FALSE(dt_mode < MCPWM_DEADTIME_TYPE_MAX, ESP_ERR_INVALID_ARG, TAG, MCPWM_DT_ERROR);
     mcpwm_hal_context_t *hal = &context[mcpwm_num].hal;
-    mcpwm_hal_deadzone_conf_t deadzone = {
-        .red = red,
-        .fed = fed,
-        .mode = dt_mode,
-    };
 
     mcpwm_critical_enter(mcpwm_num);
-    mcpwm_hal_operator_update_deadzone(hal, op, &deadzone);
+    mcpwm_ll_deadtime_enable_update_delay_on_tez(hal->dev, op, true);
+    mcpwm_ll_deadtime_set_resolution_same_to_timer(hal->dev, op, false);
+    mcpwm_ll_deadtime_set_rising_delay(hal->dev, op, red + 1);
+    mcpwm_ll_deadtime_set_falling_delay(hal->dev, op, fed + 1);
+    switch (dt_mode) {
+    case MCPWM_BYPASS_RED:
+        mcpwm_ll_deadtime_bypass_path(hal->dev, op, 1, false);    // S0
+        mcpwm_ll_deadtime_bypass_path(hal->dev, op, 0, true);     // S1
+        mcpwm_ll_deadtime_invert_outpath(hal->dev, op, 0, false); // S2
+        mcpwm_ll_deadtime_invert_outpath(hal->dev, op, 1, false); // S3
+        mcpwm_ll_deadtime_red_select_generator(hal->dev, op, 0);  // S4
+        mcpwm_ll_deadtime_fed_select_generator(hal->dev, op, 1);  // S5
+        break;
+    case MCPWM_BYPASS_FED:
+        mcpwm_ll_deadtime_bypass_path(hal->dev, op, 1, true);     // S0
+        mcpwm_ll_deadtime_bypass_path(hal->dev, op, 0, false);    // S1
+        mcpwm_ll_deadtime_invert_outpath(hal->dev, op, 0, false); // S2
+        mcpwm_ll_deadtime_invert_outpath(hal->dev, op, 1, false); // S3
+        mcpwm_ll_deadtime_red_select_generator(hal->dev, op, 0);  // S4
+        mcpwm_ll_deadtime_fed_select_generator(hal->dev, op, 0);  // S5
+        break;
+    case MCPWM_ACTIVE_HIGH_MODE:
+        mcpwm_ll_deadtime_bypass_path(hal->dev, op, 1, false);    // S0
+        mcpwm_ll_deadtime_bypass_path(hal->dev, op, 0, false);    // S1
+        mcpwm_ll_deadtime_invert_outpath(hal->dev, op, 0, false); // S2
+        mcpwm_ll_deadtime_invert_outpath(hal->dev, op, 1, false); // S3
+        mcpwm_ll_deadtime_red_select_generator(hal->dev, op, 0);  // S4
+        mcpwm_ll_deadtime_fed_select_generator(hal->dev, op, 1);  // S5
+        break;
+    case MCPWM_ACTIVE_LOW_MODE:
+        mcpwm_ll_deadtime_bypass_path(hal->dev, op, 1, false);   // S0
+        mcpwm_ll_deadtime_bypass_path(hal->dev, op, 0, false);   // S1
+        mcpwm_ll_deadtime_invert_outpath(hal->dev, op, 0, true); // S2
+        mcpwm_ll_deadtime_invert_outpath(hal->dev, op, 1, true); // S3
+        mcpwm_ll_deadtime_red_select_generator(hal->dev, op, 0); // S4
+        mcpwm_ll_deadtime_fed_select_generator(hal->dev, op, 1); // S5
+        break;
+    case MCPWM_ACTIVE_HIGH_COMPLIMENT_MODE:
+        mcpwm_ll_deadtime_bypass_path(hal->dev, op, 1, false);    // S0
+        mcpwm_ll_deadtime_bypass_path(hal->dev, op, 0, false);    // S1
+        mcpwm_ll_deadtime_invert_outpath(hal->dev, op, 0, false); // S2
+        mcpwm_ll_deadtime_invert_outpath(hal->dev, op, 1, true);  // S3
+        mcpwm_ll_deadtime_red_select_generator(hal->dev, op, 0);  // S4
+        mcpwm_ll_deadtime_fed_select_generator(hal->dev, op, 1);  // S5
+        break;
+    case MCPWM_ACTIVE_LOW_COMPLIMENT_MODE:
+        mcpwm_ll_deadtime_bypass_path(hal->dev, op, 1, false);    // S0
+        mcpwm_ll_deadtime_bypass_path(hal->dev, op, 0, false);    // S1
+        mcpwm_ll_deadtime_invert_outpath(hal->dev, op, 0, true);  // S2
+        mcpwm_ll_deadtime_invert_outpath(hal->dev, op, 1, false); // S3
+        mcpwm_ll_deadtime_red_select_generator(hal->dev, op, 1);  // S4
+        mcpwm_ll_deadtime_fed_select_generator(hal->dev, op, 0);  // S5
+        break;
+    case MCPWM_ACTIVE_RED_FED_FROM_PWMXA:
+        mcpwm_ll_deadtime_bypass_path(hal->dev, op, 1, false);    // S0
+        mcpwm_ll_deadtime_invert_outpath(hal->dev, op, 1, false); // S3
+        mcpwm_ll_deadtime_red_select_generator(hal->dev, op, 1);  // S4
+        mcpwm_ll_deadtime_swap_out_path(hal->dev, op, 0, true);   // S6
+        mcpwm_ll_deadtime_swap_out_path(hal->dev, op, 1, false);  // S7
+        mcpwm_ll_deadtime_enable_deb(hal->dev, op, true);         // S8
+        break;
+    case MCPWM_ACTIVE_RED_FED_FROM_PWMXB:
+        mcpwm_ll_deadtime_bypass_path(hal->dev, op, 1, false);    // S0
+        mcpwm_ll_deadtime_invert_outpath(hal->dev, op, 1, false); // S3
+        mcpwm_ll_deadtime_red_select_generator(hal->dev, op, 0);  // S4
+        mcpwm_ll_deadtime_swap_out_path(hal->dev, op, 0, true);   // S6
+        mcpwm_ll_deadtime_swap_out_path(hal->dev, op, 1, false);  // S7
+        mcpwm_ll_deadtime_enable_deb(hal->dev, op, true);         // S8
+        break;
+    default :
+        break;
+    }
     mcpwm_critical_exit(mcpwm_num);
     return ESP_OK;
 }
@@ -457,30 +560,43 @@ esp_err_t mcpwm_deadtime_disable(mcpwm_unit_t mcpwm_num, mcpwm_timer_t timer_num
     const int op = timer_num;
     MCPWM_TIMER_CHECK(mcpwm_num, timer_num);
     mcpwm_hal_context_t *hal = &context[mcpwm_num].hal;
-    mcpwm_hal_deadzone_conf_t deadzone = { .mode = MCPWM_DEADTIME_BYPASS };
 
     mcpwm_critical_enter(mcpwm_num);
-    mcpwm_hal_operator_update_deadzone(hal, op, &deadzone);
+    mcpwm_ll_deadtime_bypass_path(hal->dev, op, 1, true);     // S0
+    mcpwm_ll_deadtime_bypass_path(hal->dev, op, 0, true);     // S1
+    mcpwm_ll_deadtime_invert_outpath(hal->dev, op, 0, false); // S2
+    mcpwm_ll_deadtime_invert_outpath(hal->dev, op, 1, false); // S3
+    mcpwm_ll_deadtime_red_select_generator(hal->dev, op, 0);  // S4
+    mcpwm_ll_deadtime_fed_select_generator(hal->dev, op, 0);  // S5
+    mcpwm_ll_deadtime_swap_out_path(hal->dev, op, 0, false);  // S6
+    mcpwm_ll_deadtime_swap_out_path(hal->dev, op, 1, false);  // S7
+    mcpwm_ll_deadtime_enable_deb(hal->dev, op, false);        // S8
     mcpwm_critical_exit(mcpwm_num);
     return ESP_OK;
 }
 
 esp_err_t mcpwm_fault_init(mcpwm_unit_t mcpwm_num, mcpwm_fault_input_level_t intput_level, mcpwm_fault_signal_t fault_sig)
 {
-    MCPWM_CHECK(mcpwm_num < SOC_MCPWM_GROUPS, MCPWM_GROUP_NUM_ERROR, ESP_ERR_INVALID_ARG);
+    ESP_RETURN_ON_FALSE(mcpwm_num < SOC_MCPWM_GROUPS, ESP_ERR_INVALID_ARG, TAG, MCPWM_GROUP_NUM_ERROR);
+    mcpwm_hal_context_t *hal = &context[mcpwm_num].hal;
 
     mcpwm_critical_enter(mcpwm_num);
-    mcpwm_hal_fault_init(&context[mcpwm_num].hal, fault_sig, intput_level);
+    mcpwm_ll_fault_enable_detection(hal->dev, fault_sig, true);
+    mcpwm_ll_fault_set_active_level(hal->dev, fault_sig, intput_level);
     mcpwm_critical_exit(mcpwm_num);
     return ESP_OK;
 }
 
 esp_err_t mcpwm_fault_deinit(mcpwm_unit_t mcpwm_num, mcpwm_fault_signal_t fault_sig)
 {
-    MCPWM_CHECK(mcpwm_num < SOC_MCPWM_GROUPS, MCPWM_GROUP_NUM_ERROR, ESP_ERR_INVALID_ARG);
+    ESP_RETURN_ON_FALSE(mcpwm_num < SOC_MCPWM_GROUPS, ESP_ERR_INVALID_ARG, TAG, MCPWM_GROUP_NUM_ERROR);
+    mcpwm_hal_context_t *hal = &context[mcpwm_num].hal;
 
     mcpwm_critical_enter(mcpwm_num);
-    mcpwm_hal_fault_disable(&context[mcpwm_num].hal, fault_sig);
+    mcpwm_ll_fault_enable_detection(hal->dev, fault_sig, false);
+    for (int i = 0; i < SOC_MCPWM_OPERATORS_PER_GROUP; i++) {
+        mcpwm_ll_fault_clear_ost(hal->dev, i); // make sure operator has exit the ost fault state totally
+    }
     mcpwm_critical_exit(mcpwm_num);
     return ESP_OK;
 }
@@ -494,10 +610,12 @@ esp_err_t mcpwm_fault_set_cyc_mode(mcpwm_unit_t mcpwm_num, mcpwm_timer_t timer_n
     mcpwm_hal_context_t *hal = &context[mcpwm_num].hal;
 
     mcpwm_critical_enter(mcpwm_num);
-    mcpwm_ll_fault_cbc_enable_signal(hal->dev, op, fault_sig, true);
-    mcpwm_ll_fault_oneshot_enable_signal(hal->dev, op, fault_sig, false);
-    mcpwm_ll_fault_set_cyc_action(hal->dev, op, 0, action_on_pwmxa, action_on_pwmxa);
-    mcpwm_ll_fault_set_cyc_action(hal->dev, op, 1, action_on_pwmxb, action_on_pwmxb);
+    mcpwm_ll_fault_enable_cbc_mode(hal->dev, op, fault_sig, true);
+    mcpwm_ll_fault_enable_oneshot_mode(hal->dev, op, fault_sig, false);
+    mcpwm_ll_generator_set_action_on_fault_event(hal->dev, op, 0, MCPWM_TIMER_DIRECTION_DOWN, MCPWM_FAULT_REACTION_CBC, action_on_pwmxa);
+    mcpwm_ll_generator_set_action_on_fault_event(hal->dev, op, 0, MCPWM_TIMER_DIRECTION_UP, MCPWM_FAULT_REACTION_CBC, action_on_pwmxa);
+    mcpwm_ll_generator_set_action_on_fault_event(hal->dev, op, 1, MCPWM_TIMER_DIRECTION_DOWN, MCPWM_FAULT_REACTION_CBC, action_on_pwmxb);
+    mcpwm_ll_generator_set_action_on_fault_event(hal->dev, op, 1, MCPWM_TIMER_DIRECTION_UP, MCPWM_FAULT_REACTION_CBC, action_on_pwmxb);
     mcpwm_critical_exit(mcpwm_num);
     return ESP_OK;
 }
@@ -511,11 +629,13 @@ esp_err_t mcpwm_fault_set_oneshot_mode(mcpwm_unit_t mcpwm_num, mcpwm_timer_t tim
     mcpwm_hal_context_t *hal = &context[mcpwm_num].hal;
 
     mcpwm_critical_enter(mcpwm_num);
-    mcpwm_hal_fault_oneshot_clear(hal, op);
-    mcpwm_ll_fault_cbc_enable_signal(hal->dev, op, fault_sig, false);
-    mcpwm_ll_fault_oneshot_enable_signal(hal->dev, op, fault_sig, true);
-    mcpwm_ll_fault_set_oneshot_action(hal->dev, op, 0, action_on_pwmxa, action_on_pwmxa);
-    mcpwm_ll_fault_set_oneshot_action(hal->dev, op, 1, action_on_pwmxb, action_on_pwmxb);
+    mcpwm_ll_fault_clear_ost(hal->dev, op);
+    mcpwm_ll_fault_enable_oneshot_mode(hal->dev, op, fault_sig, true);
+    mcpwm_ll_fault_enable_cbc_mode(hal->dev, op, fault_sig, false);
+    mcpwm_ll_generator_set_action_on_fault_event(hal->dev, op, 0, MCPWM_TIMER_DIRECTION_DOWN, MCPWM_FAULT_REACTION_OST, action_on_pwmxa);
+    mcpwm_ll_generator_set_action_on_fault_event(hal->dev, op, 0, MCPWM_TIMER_DIRECTION_UP, MCPWM_FAULT_REACTION_OST, action_on_pwmxa);
+    mcpwm_ll_generator_set_action_on_fault_event(hal->dev, op, 1, MCPWM_TIMER_DIRECTION_DOWN, MCPWM_FAULT_REACTION_OST, action_on_pwmxb);
+    mcpwm_ll_generator_set_action_on_fault_event(hal->dev, op, 1, MCPWM_TIMER_DIRECTION_UP, MCPWM_FAULT_REACTION_OST, action_on_pwmxb);
     mcpwm_critical_exit(mcpwm_num);
     return ESP_OK;
 }
@@ -523,35 +643,39 @@ esp_err_t mcpwm_fault_set_oneshot_mode(mcpwm_unit_t mcpwm_num, mcpwm_timer_t tim
 esp_err_t mcpwm_capture_enable(mcpwm_unit_t mcpwm_num, mcpwm_capture_signal_t cap_sig, mcpwm_capture_on_edge_t cap_edge,
                                uint32_t num_of_pulse)
 {
-    MCPWM_CHECK(mcpwm_num < SOC_MCPWM_GROUPS, MCPWM_GROUP_NUM_ERROR, ESP_ERR_INVALID_ARG);
-    MCPWM_CHECK(num_of_pulse <= MCPWM_LL_MAX_PRESCALE, MCPWM_PRESCALE_ERROR, ESP_ERR_INVALID_ARG);
+    ESP_RETURN_ON_FALSE(mcpwm_num < SOC_MCPWM_GROUPS, ESP_ERR_INVALID_ARG, TAG, MCPWM_GROUP_NUM_ERROR);
+    ESP_RETURN_ON_FALSE(num_of_pulse <= MCPWM_LL_MAX_PRESCALE, ESP_ERR_INVALID_ARG, TAG, MCPWM_PRESCALE_ERROR);
+    ESP_RETURN_ON_FALSE(cap_sig < SOC_MCPWM_CAPTURE_CHANNELS_PER_TIMER, ESP_ERR_INVALID_ARG, TAG, MCPWM_CAPTURE_ERROR);
+    mcpwm_hal_context_t *hal = &context[mcpwm_num].hal;
     // enable MCPWM module incase user don't use `mcpwm_init` at all
     periph_module_enable(mcpwm_periph_signals.groups[mcpwm_num].module);
     mcpwm_hal_init_config_t init_config = {
         .host_id = mcpwm_num,
     };
-    mcpwm_hal_capture_config_t cap_conf = {
-        .cap_edge = cap_edge,
-        .prescale = num_of_pulse,
-    };
-    mcpwm_hal_context_t *hal = &context[mcpwm_num].hal;
-
     mcpwm_critical_enter(mcpwm_num);
-    //We have to do this here, since there is no standalone init function
-    //without enabling any PWM channels.
     mcpwm_hal_init(hal, &init_config);
-    mcpwm_hal_hw_init(hal);
-    mcpwm_hal_capture_enable(hal, cap_sig, &cap_conf);
+    mcpwm_ll_group_set_clock(hal->dev, MCPWM_GROUP_CLK_HZ);
+    mcpwm_ll_capture_enable_timer(hal->dev, true);
+    mcpwm_ll_capture_enable_channel(hal->dev, cap_sig, true);
+    mcpwm_ll_capture_enable_negedge(hal->dev, cap_sig, cap_edge & MCPWM_NEG_EDGE);
+    mcpwm_ll_capture_enable_posedge(hal->dev, cap_sig, cap_edge & MCPWM_POS_EDGE);
+    mcpwm_ll_capture_set_prescale(hal->dev, cap_sig, num_of_pulse + 1);
+    // capture feature should be used with interupt, so enable it by default
+    mcpwm_ll_intr_enable_capture(hal->dev, cap_sig, true);
+    mcpwm_ll_intr_clear_capture_status(hal->dev, 1 << cap_sig);
     mcpwm_critical_exit(mcpwm_num);
     return ESP_OK;
 }
 
 esp_err_t mcpwm_capture_disable(mcpwm_unit_t mcpwm_num, mcpwm_capture_signal_t cap_sig)
 {
-    MCPWM_CHECK(mcpwm_num < SOC_MCPWM_GROUPS, MCPWM_GROUP_NUM_ERROR, ESP_ERR_INVALID_ARG);
+    ESP_RETURN_ON_FALSE(mcpwm_num < SOC_MCPWM_GROUPS, ESP_ERR_INVALID_ARG, TAG, MCPWM_GROUP_NUM_ERROR);
+    ESP_RETURN_ON_FALSE(cap_sig < SOC_MCPWM_CAPTURE_CHANNELS_PER_TIMER, ESP_ERR_INVALID_ARG, TAG, MCPWM_CAPTURE_ERROR);
+    mcpwm_hal_context_t *hal = &context[mcpwm_num].hal;
 
     mcpwm_critical_enter(mcpwm_num);
-    mcpwm_hal_capture_disable(&context[mcpwm_num].hal, cap_sig);
+    mcpwm_ll_capture_enable_channel(hal->dev, cap_sig, false);
+    mcpwm_ll_intr_enable_capture(hal->dev, cap_sig, false);
     mcpwm_critical_exit(mcpwm_num);
     periph_module_disable(mcpwm_periph_signals.groups[mcpwm_num].module);
     return ESP_OK;
@@ -559,19 +683,18 @@ esp_err_t mcpwm_capture_disable(mcpwm_unit_t mcpwm_num, mcpwm_capture_signal_t c
 
 uint32_t mcpwm_capture_signal_get_value(mcpwm_unit_t mcpwm_num, mcpwm_capture_signal_t cap_sig)
 {
-    MCPWM_CHECK(mcpwm_num < SOC_MCPWM_GROUPS, MCPWM_GROUP_NUM_ERROR, ESP_ERR_INVALID_ARG);
-
-    uint32_t captured_value;
-    mcpwm_hal_capture_get_result(&context[mcpwm_num].hal, cap_sig, &captured_value, NULL);
-    return captured_value;
+    ESP_RETURN_ON_FALSE(mcpwm_num < SOC_MCPWM_GROUPS, ESP_ERR_INVALID_ARG, TAG, MCPWM_GROUP_NUM_ERROR);
+    ESP_RETURN_ON_FALSE(cap_sig < SOC_MCPWM_CAPTURE_CHANNELS_PER_TIMER, ESP_ERR_INVALID_ARG, TAG, MCPWM_CAPTURE_ERROR);
+    mcpwm_hal_context_t *hal = &context[mcpwm_num].hal;
+    return mcpwm_ll_capture_get_value(hal->dev, cap_sig);
 }
 
 uint32_t mcpwm_capture_signal_get_edge(mcpwm_unit_t mcpwm_num, mcpwm_capture_signal_t cap_sig)
 {
-    MCPWM_CHECK(mcpwm_num < SOC_MCPWM_GROUPS, MCPWM_GROUP_NUM_ERROR, ESP_ERR_INVALID_ARG);
-    mcpwm_capture_on_edge_t edge;
-    mcpwm_hal_capture_get_result(&context[mcpwm_num].hal, cap_sig, NULL, &edge);
-    return (edge == MCPWM_NEG_EDGE ? 2 : 1);
+    ESP_RETURN_ON_FALSE(mcpwm_num < SOC_MCPWM_GROUPS, ESP_ERR_INVALID_ARG, TAG, MCPWM_GROUP_NUM_ERROR);
+    ESP_RETURN_ON_FALSE(cap_sig < SOC_MCPWM_CAPTURE_CHANNELS_PER_TIMER, ESP_ERR_INVALID_ARG, TAG, MCPWM_CAPTURE_ERROR);
+    mcpwm_hal_context_t *hal = &context[mcpwm_num].hal;
+    return mcpwm_ll_capture_is_negedge(hal->dev, cap_sig) ? 2 : 1;
 }
 
 esp_err_t mcpwm_sync_enable(mcpwm_unit_t mcpwm_num, mcpwm_timer_t timer_num, mcpwm_sync_signal_t sync_sig,
@@ -579,13 +702,15 @@ esp_err_t mcpwm_sync_enable(mcpwm_unit_t mcpwm_num, mcpwm_timer_t timer_num, mcp
 {
     MCPWM_TIMER_CHECK(mcpwm_num, timer_num);
     mcpwm_hal_context_t *hal = &context[mcpwm_num].hal;
-    mcpwm_hal_sync_config_t sync_config = {
-        .reload_permillage = phase_val,
-        .sync_sig = sync_sig,
-    };
 
     mcpwm_critical_enter(mcpwm_num);
-    mcpwm_hal_timer_enable_sync(hal, timer_num, &sync_config);
+    uint32_t set_phase = mcpwm_ll_timer_get_peak(hal->dev, timer_num, false) * phase_val / 1000;
+    mcpwm_ll_timer_set_sync_phase_value(hal->dev, timer_num, set_phase);
+    if (sync_sig >= MCPWM_SELECT_SYNC0) {
+        mcpwm_ll_timer_enable_sync_from_external(hal->dev, timer_num, sync_sig - MCPWM_SELECT_SYNC0);
+    }
+    mcpwm_ll_timer_sync_out_same_in(hal->dev, timer_num);
+    mcpwm_ll_timer_enable_sync_input(hal->dev, timer_num, true);
     mcpwm_critical_exit(mcpwm_num);
     return ESP_OK;
 }
@@ -593,9 +718,10 @@ esp_err_t mcpwm_sync_enable(mcpwm_unit_t mcpwm_num, mcpwm_timer_t timer_num, mcp
 esp_err_t mcpwm_sync_disable(mcpwm_unit_t mcpwm_num, mcpwm_timer_t timer_num)
 {
     MCPWM_TIMER_CHECK(mcpwm_num, timer_num);
+    mcpwm_hal_context_t *hal = &context[mcpwm_num].hal;
 
     mcpwm_critical_enter(mcpwm_num);
-    mcpwm_hal_timer_disable_sync(&context[mcpwm_num].hal, timer_num);
+    mcpwm_ll_timer_enable_sync_input(hal->dev, timer_num, false);
     mcpwm_critical_exit(mcpwm_num);
     return ESP_OK;
 }
@@ -603,8 +729,8 @@ esp_err_t mcpwm_sync_disable(mcpwm_unit_t mcpwm_num, mcpwm_timer_t timer_num)
 esp_err_t mcpwm_isr_register(mcpwm_unit_t mcpwm_num, void (*fn)(void *), void *arg, int intr_alloc_flags, intr_handle_t *handle)
 {
     esp_err_t ret;
-    MCPWM_CHECK(mcpwm_num < SOC_MCPWM_GROUPS, MCPWM_GROUP_NUM_ERROR, ESP_ERR_INVALID_ARG);
-    MCPWM_CHECK(fn != NULL, MCPWM_PARAM_ADDR_ERROR, ESP_ERR_INVALID_ARG);
+    ESP_RETURN_ON_FALSE(mcpwm_num < SOC_MCPWM_GROUPS, ESP_ERR_INVALID_ARG, TAG, MCPWM_GROUP_NUM_ERROR);
+    ESP_RETURN_ON_FALSE(fn, ESP_ERR_INVALID_ARG, TAG, MCPWM_PARAM_ADDR_ERROR);
     ret = esp_intr_alloc(mcpwm_periph_signals.groups[mcpwm_num].irq_id, intr_alloc_flags, fn, arg, handle);
     return ret;
 }

+ 293 - 659
components/driver/test/test_pwm.c

@@ -3,80 +3,30 @@
  *
  * SPDX-License-Identifier: Apache-2.0
  */
-/**
- * To test PWM, use the PCNT to calculateit to judge it work right or not.
- * e.g: judge the start and stop.
- *      If started right, the PCNT will count the pulse.
- *      If stopped right, the PCNT will count no pulse.
- *
- *
- * test environment UT_T1_MCPWM:
- * 1. connect GPIO4 to GPIO5
- * 2. connect GPIO13 to GPIO12
- * 3. connect GPIO27 to GPIO14
- *
- * all of case separate different timer to test in case that one case cost too much time
- */
 #include <stdio.h>
-#include "esp_system.h"
+#include <unistd.h>
 #include "unity.h"
 #include "test_utils.h"
 #include "freertos/FreeRTOS.h"
 #include "freertos/task.h"
-#include "freertos/queue.h"
-#include "esp_attr.h"
-#include "esp_log.h"
-#include "soc/rtc.h"
 #include "soc/soc_caps.h"
-
+#include "hal/gpio_hal.h"
+#include "esp_rom_gpio.h"
 #if SOC_MCPWM_SUPPORTED
-#if !TEMPORARY_DISABLED_FOR_TARGETS(ESP32S3)
 #include "soc/mcpwm_periph.h"
 #include "driver/pcnt.h"
 #include "driver/mcpwm.h"
+#include "driver/gpio.h"
 
+#define TEST_PWMA_PCNT_UNIT (0)
+#define TEST_PWMB_PCNT_UNIT (1)
+#define TEST_PWMA_GPIO (2)
+#define TEST_PWMB_GPIO (4)
+#define TEST_FAULT_GPIO (21)
+#define TEST_SYNC_GPIO (21)
+#define TEST_CAP_GPIO (21)
 
-#define GPIO_PWMA_OUT  4
-#define GPIO_PWMB_OUT  13
-#define GPIO_CAP_IN   27
-#define GPIO_SYNC_IN   27
-#define GPIO_FAULT_IN 27
-
-#define CAP_SIG_NUM 14
-#define SYN_SIG_NUM 14
-#define FAULT_SIG_NUM 14
-
-#define GPIO_PWMA_PCNT_INPUT 5
-#define GPIO_PWMB_PCNT_INPUT 12
-
-#define PCNT_CTRL_FLOATING_IO1 25
-#define PCNT_CTRL_FLOATING_IO2 26
-
-#define CAP0_INT_EN BIT(27)
-#define CAP1_INT_EN BIT(28)
-#define CAP2_INT_EN BIT(29)
-
-#define INITIAL_DUTY 10.0
-#define MCPWM_GPIO_INIT 0
-
-
-#define HIGHEST_LIMIT 10000
-#define LOWEST_LIMIT -10000
-
-static mcpwm_dev_t *MCPWM[2] = {&MCPWM0, &MCPWM1};
-
-static xQueueHandle cap_queue;
-static volatile int cap0_times = 0;
-static volatile int cap1_times = 0;
-static volatile int cap2_times = 0;
-
-typedef struct {
-    uint32_t capture_signal;
-    mcpwm_capture_signal_t sel_cap_signal;
-} capture;
-
-static const char TAG[] = "test_pwm";
-
+static mcpwm_dev_t *MCPWM[2] = {&MCPWM0, &MCPWM1}; // interrupt handling still lacks API to get/clear pending event, currently we have to read/write interrupt register
 const static mcpwm_io_signals_t pwma[] = {MCPWM0A, MCPWM1A, MCPWM2A};
 const static mcpwm_io_signals_t pwmb[] = {MCPWM0B, MCPWM1B, MCPWM2B};
 const static mcpwm_fault_signal_t fault_sig_array[] = {MCPWM_SELECT_F0, MCPWM_SELECT_F1, MCPWM_SELECT_F2};
@@ -86,711 +36,395 @@ const static mcpwm_io_signals_t sync_io_sig_array[] = {MCPWM_SYNC_0, MCPWM_SYNC_
 const static mcpwm_capture_signal_t cap_sig_array[] = {MCPWM_SELECT_CAP0, MCPWM_SELECT_CAP1, MCPWM_SELECT_CAP2};
 const static mcpwm_io_signals_t cap_io_sig_array[] = {MCPWM_CAP_0, MCPWM_CAP_1, MCPWM_CAP_2};
 
-
-// universal settings of mcpwm
-static void mcpwm_basic_config(mcpwm_unit_t unit, mcpwm_timer_t timer)
+// This GPIO init function is almost the same to public API `mcpwm_gpio_init()`, except that
+// this function will configure all MCPWM GPIOs into output and input capable
+// which is useful to simulate a trigger source
+static esp_err_t test_mcpwm_gpio_init(mcpwm_unit_t mcpwm_num, mcpwm_io_signals_t io_signal, int gpio_num)
 {
-    mcpwm_io_signals_t mcpwm_a = pwma[timer];
-    mcpwm_io_signals_t mcpwm_b = pwmb[timer];
-    mcpwm_gpio_init(unit, mcpwm_a, GPIO_PWMA_OUT);
-    mcpwm_gpio_init(unit, mcpwm_b, GPIO_PWMB_OUT);
-    mcpwm_config_t pwm_config = {
-        .frequency = 1000,
-        .cmpr_a = 50.0,  //duty cycle of PWMxA = 50.0%
-        .cmpr_b = 50.0,  //duty cycle of PWMxb = 50.0%
-        .counter_mode = MCPWM_UP_COUNTER,
-        .duty_mode = MCPWM_DUTY_MODE_0,
-    };
-    mcpwm_init(unit, timer, &pwm_config);
-}
+    if (gpio_num < 0) { // ignore on minus gpio number
+        return ESP_OK;
+    }
 
-static void pcnt_init(int pulse_gpio_num, int ctrl_gpio_num)
-{
-    pcnt_config_t pcnt_config = {
-       .pulse_gpio_num = pulse_gpio_num,
-       .ctrl_gpio_num = ctrl_gpio_num,
-       .channel = PCNT_CHANNEL_0,
-       .unit = PCNT_UNIT_0,
-       .pos_mode = PCNT_COUNT_INC,
-       .neg_mode = PCNT_COUNT_DIS,
-       .lctrl_mode = PCNT_MODE_REVERSE,
-       .hctrl_mode = PCNT_MODE_KEEP,
-       .counter_h_lim = HIGHEST_LIMIT,
-       .counter_l_lim = LOWEST_LIMIT,
-   };
-   TEST_ESP_OK(pcnt_unit_config(&pcnt_config));
+    if (io_signal <= MCPWM2B) { // Generator output signal
+        gpio_set_direction(gpio_num, GPIO_MODE_INPUT_OUTPUT);
+        int operator_id = io_signal / 2;
+        int generator_id = io_signal % 2;
+        esp_rom_gpio_connect_out_signal(gpio_num, mcpwm_periph_signals.groups[mcpwm_num].operators[operator_id].generators[generator_id].pwm_sig, 0, 0);
+    } else if (io_signal <= MCPWM_SYNC_2) { // External sync input signal
+        gpio_set_direction(gpio_num, GPIO_MODE_INPUT_OUTPUT);
+        int ext_sync_id = io_signal - MCPWM_SYNC_0;
+        esp_rom_gpio_connect_in_signal(gpio_num, mcpwm_periph_signals.groups[mcpwm_num].ext_syncers[ext_sync_id].sync_sig, 0);
+    } else if (io_signal <= MCPWM_FAULT_2) { // Fault input signal
+        gpio_set_direction(gpio_num, GPIO_MODE_INPUT_OUTPUT);
+        int fault_id = io_signal - MCPWM_FAULT_0;
+        esp_rom_gpio_connect_in_signal(gpio_num, mcpwm_periph_signals.groups[mcpwm_num].detectors[fault_id].fault_sig, 0);
+    } else if (io_signal >= MCPWM_CAP_0 && io_signal <= MCPWM_CAP_2) { // Capture input signal
+        gpio_set_direction(gpio_num, GPIO_MODE_INPUT_OUTPUT);
+        int capture_id = io_signal - MCPWM_CAP_0;
+        esp_rom_gpio_connect_in_signal(gpio_num, mcpwm_periph_signals.groups[mcpwm_num].captures[capture_id].cap_sig, 0);
+    }
+    gpio_hal_iomux_func_sel(GPIO_PIN_MUX_REG[gpio_num], PIN_FUNC_GPIO);
+    return ESP_OK;
 }
 
-// initialize the PCNT
-// PCNT is used to count the MCPWM pulse
-static int16_t pcnt_count(int pulse_gpio_num, int ctrl_gpio_num, int last_time)
+static void mcpwm_setup_testbench(mcpwm_unit_t group, mcpwm_timer_t timer, uint32_t pwm_freq, float pwm_duty)
 {
+    // PWMA <--> PCNT UNIT0
     pcnt_config_t pcnt_config = {
-        .pulse_gpio_num = pulse_gpio_num,
-        .ctrl_gpio_num = ctrl_gpio_num,
+        .pulse_gpio_num = TEST_PWMA_GPIO,
+        .ctrl_gpio_num = -1,       // don't care level signal
         .channel = PCNT_CHANNEL_0,
-        .unit = PCNT_UNIT_0,
+        .unit = TEST_PWMA_PCNT_UNIT,
         .pos_mode = PCNT_COUNT_INC,
         .neg_mode = PCNT_COUNT_DIS,
-        .lctrl_mode = PCNT_MODE_REVERSE,
+        .lctrl_mode = PCNT_MODE_KEEP,
         .hctrl_mode = PCNT_MODE_KEEP,
-        .counter_h_lim = HIGHEST_LIMIT,
-        .counter_l_lim = LOWEST_LIMIT,
+        .counter_h_lim = 10000,
+        .counter_l_lim = -10000,
     };
     TEST_ESP_OK(pcnt_unit_config(&pcnt_config));
-    int16_t test_counter;
-    TEST_ESP_OK(pcnt_counter_pause(PCNT_UNIT_0));
-    TEST_ESP_OK(pcnt_counter_clear(PCNT_UNIT_0));
-    TEST_ESP_OK(pcnt_counter_resume(PCNT_UNIT_0));
-    TEST_ESP_OK(pcnt_get_counter_value(PCNT_UNIT_0, &test_counter));
-    printf("COUNT (before): %d\n", test_counter);
-    vTaskDelay(last_time / portTICK_RATE_MS);
-    TEST_ESP_OK(pcnt_get_counter_value(PCNT_UNIT_0, &test_counter));
-    printf("COUNT (after): %d\n", test_counter);
-    return test_counter;
-}
-
-// judge the counting value right or not in specific error
-static void judge_count_value(int allow_error ,int expect_freq)
-{
-    int16_t countA, countB;
-
-    countA = pcnt_count(GPIO_PWMA_PCNT_INPUT, PCNT_CTRL_FLOATING_IO1, 1000);
-    countB = pcnt_count(GPIO_PWMB_PCNT_INPUT, PCNT_CTRL_FLOATING_IO2, 1000);
+    mcpwm_io_signals_t mcpwm_a = pwma[timer];
+    TEST_ESP_OK(test_mcpwm_gpio_init(group, mcpwm_a, TEST_PWMA_GPIO));
+    // PWMB <--> PCNT UNIT1
+    pcnt_config.pulse_gpio_num = TEST_PWMB_GPIO;
+    pcnt_config.unit = TEST_PWMB_PCNT_UNIT;
+    TEST_ESP_OK(pcnt_unit_config(&pcnt_config));
+    mcpwm_io_signals_t mcpwm_b = pwmb[timer];
+    TEST_ESP_OK(test_mcpwm_gpio_init(group, mcpwm_b, TEST_PWMB_GPIO));
 
-    TEST_ASSERT_INT16_WITHIN(allow_error, countA, expect_freq);
-    TEST_ASSERT_INT16_WITHIN(allow_error, countB, expect_freq);
+    // Set PWM freq and duty, start immediately
+    mcpwm_config_t pwm_config = {
+        .frequency = pwm_freq,
+        .cmpr_a = pwm_duty,
+        .cmpr_b = pwm_duty,
+        .counter_mode = MCPWM_UP_COUNTER,
+        .duty_mode = MCPWM_DUTY_MODE_0,
+    };
+    TEST_ESP_OK(mcpwm_init(group, timer, &pwm_config));
 }
 
-// test the duty configuration
-static void timer_duty_test(mcpwm_unit_t unit, mcpwm_timer_t timer)
+static uint32_t mcpwm_pcnt_get_pulse_number(pcnt_unit_t pwm_pcnt_unit, int capture_window_ms)
 {
-    mcpwm_basic_config(unit, timer);
-    vTaskDelay(1000 / portTICK_RATE_MS); // stay this status for a while so that can view its waveform by logic anylyzer
-
-    TEST_ESP_OK(mcpwm_set_duty(unit, timer, MCPWM_OPR_A, (INITIAL_DUTY * 1)));
-    TEST_ESP_OK(mcpwm_set_duty(unit, timer, MCPWM_OPR_B, (INITIAL_DUTY * 2)));
-
-    TEST_ASSERT_EQUAL_INT(mcpwm_get_duty(unit, timer, MCPWM_OPR_A), INITIAL_DUTY * 1);
-    TEST_ASSERT_EQUAL_INT(mcpwm_get_duty(unit, timer, MCPWM_OPR_B), INITIAL_DUTY * 2);
-    vTaskDelay(100 / portTICK_RATE_MS);  // stay this status for a while so that can view its waveform by logic anylyzer
-
-    mcpwm_set_duty(unit, timer, MCPWM_OPR_A, 55.5f);
-    mcpwm_set_duty_type(unit, timer, MCPWM_OPR_A, MCPWM_DUTY_MODE_0);
-    printf("mcpwm check = %f\n", mcpwm_get_duty(unit, timer, MCPWM_OPR_A));
-
-    mcpwm_set_duty_in_us(unit, timer, MCPWM_OPR_B, 500);
-    printf("mcpwm check = %f\n", mcpwm_get_duty(unit, timer, MCPWM_OPR_B));
-    vTaskDelay(100 / portTICK_RATE_MS);  // stay this status for a while so that can view its waveform by logic anylyzer
-
-    mcpwm_stop(unit, timer);
+    int16_t count_value = 0;
+    TEST_ESP_OK(pcnt_counter_pause(pwm_pcnt_unit));
+    TEST_ESP_OK(pcnt_counter_clear(pwm_pcnt_unit));
+    TEST_ESP_OK(pcnt_counter_resume(pwm_pcnt_unit));
+    usleep(capture_window_ms * 1000);
+    TEST_ESP_OK(pcnt_get_counter_value(pwm_pcnt_unit, &count_value));
+    printf("count value: %d\r\n", count_value);
+    return (uint32_t)count_value;
 }
 
-// test the start and stop function work or not
-static void start_stop_test(mcpwm_unit_t unit, mcpwm_timer_t timer)
+static void mcpwm_timer_duty_test(mcpwm_unit_t unit, mcpwm_timer_t timer)
 {
-    mcpwm_basic_config(unit, timer);
-    judge_count_value(2, 1000);
-    TEST_ESP_OK(mcpwm_stop(unit, timer));
-    vTaskDelay(10 / portTICK_RATE_MS); // wait for a while, stop totally
-    judge_count_value(0, 0);
-    TEST_ESP_OK(mcpwm_start(unit, timer));
-    vTaskDelay(10 / portTICK_RATE_MS); // wait for a while, start totally
-    judge_count_value(2, 1000);
-}
+    mcpwm_setup_testbench(unit, timer, 1000, 50.0);
+    vTaskDelay(pdMS_TO_TICKS(100));
 
-// test the deadtime
-static void deadtime_test(mcpwm_unit_t unit, mcpwm_timer_t timer)
-{
-    mcpwm_basic_config(unit, timer);
-    mcpwm_deadtime_type_t deadtime_type[8] = {MCPWM_BYPASS_RED, MCPWM_BYPASS_FED, MCPWM_ACTIVE_HIGH_MODE,
-            MCPWM_ACTIVE_LOW_MODE, MCPWM_ACTIVE_HIGH_COMPLIMENT_MODE, MCPWM_ACTIVE_LOW_COMPLIMENT_MODE,
-            MCPWM_ACTIVE_RED_FED_FROM_PWMXA, MCPWM_ACTIVE_RED_FED_FROM_PWMXB};
+    TEST_ESP_OK(mcpwm_set_duty(unit, timer, MCPWM_OPR_A, 10.0));
+    TEST_ESP_OK(mcpwm_set_duty(unit, timer, MCPWM_OPR_B, 20.0));
+    TEST_ASSERT_EQUAL_FLOAT(10.0, mcpwm_get_duty(unit, timer, MCPWM_OPR_A));
+    TEST_ASSERT_EQUAL_FLOAT(20.0, mcpwm_get_duty(unit, timer, MCPWM_OPR_B));
+    vTaskDelay(pdMS_TO_TICKS(100));
 
-    for(int i=0; i<8; i++) {
-        mcpwm_deadtime_enable(unit, timer, deadtime_type[i], 1000, 1000);
-        vTaskDelay(100 / portTICK_RATE_MS);
-        mcpwm_deadtime_disable(unit, timer);
-        //add a small gap between tests to make the waveform more clear
-        mcpwm_stop(unit, timer);
-        vTaskDelay(10);
-        mcpwm_start(unit, timer);
-    }
-}
+    TEST_ESP_OK(mcpwm_set_duty(unit, timer, MCPWM_OPR_A, 55.5f));
+    TEST_ESP_OK(mcpwm_set_duty_type(unit, timer, MCPWM_OPR_A, MCPWM_DUTY_MODE_0));
+    TEST_ASSERT_EQUAL_FLOAT(55.5, mcpwm_get_duty(unit, timer, MCPWM_OPR_A));
+    vTaskDelay(pdMS_TO_TICKS(100));
 
-/**
- * there are two kind of methods to set the carrier:
- * 1. by mcpwm_carrier_init
- * 2. by different single setting function
- */
-static void carrier_with_set_function_test(mcpwm_unit_t unit, mcpwm_timer_t timer, mcpwm_carrier_out_ivt_t invert_or_not,
-                                           uint8_t period, uint8_t duty, uint8_t os_width)
-{
-    // no inversion and no one shot
-    mcpwm_basic_config(unit, timer);
-    TEST_ESP_OK(mcpwm_carrier_enable(unit, timer));
-    TEST_ESP_OK(mcpwm_carrier_set_period(unit, timer, period)); //carrier revolution
-    TEST_ESP_OK(mcpwm_carrier_set_duty_cycle(unit, timer, duty)); // carrier duty
-    judge_count_value(500, 50000/5.6);
+    TEST_ESP_OK(mcpwm_set_duty_in_us(unit, timer, MCPWM_OPR_B, 500));
+    TEST_ASSERT_EQUAL_FLOAT(50.0, mcpwm_get_duty(unit, timer, MCPWM_OPR_B));
+    vTaskDelay(pdMS_TO_TICKS(100));
 
-    // with invert
-    TEST_ESP_OK(mcpwm_carrier_output_invert(unit, timer, invert_or_not));
-    vTaskDelay(2000 / portTICK_RATE_MS);
+    TEST_ESP_OK(mcpwm_stop(unit, timer));
+    vTaskDelay(pdMS_TO_TICKS(100));
 }
 
-static void carrier_with_configuration_test(mcpwm_unit_t unit, mcpwm_timer_t timer, mcpwm_carrier_os_t oneshot_or_not,
-                                           mcpwm_carrier_out_ivt_t invert_or_not, uint8_t period, uint8_t duty,
-                                           uint8_t os_width)
+TEST_CASE("MCPWM duty test", "[mcpwm]")
 {
-    mcpwm_basic_config(unit, timer);
-
-    mcpwm_carrier_config_t chop_config;
-    chop_config.carrier_period = period;         //carrier period = (period + 1)*800ns
-    chop_config.carrier_duty = duty;           // carrier duty cycle, carrier_duty should be less then 8(increment every 12.5%).  carrier duty = (3)*12.5%
-    chop_config.carrier_os_mode = oneshot_or_not; //If one shot mode is enabled then set pulse width, if disabled no need to set pulse width
-    chop_config.pulse_width_in_os = os_width;      //pulse width of first pulse in one shot mode = (carrier period)*(pulse_width_in_os + 1), should be less then 16.first pulse width = (3 + 1)*carrier_period
-    chop_config.carrier_ivt_mode = invert_or_not; //output signal inversion enable
-    mcpwm_carrier_init(unit, timer, &chop_config);
-
-    if(!oneshot_or_not) {
-        // the pwm frequency is 1000
-        // the carrrier duration in one second is 500ms
-        // the carrier wave count is: 500ms/carrier_period = 500ms/(period + 1)*800ns
-        //                                                 = 62500/(period + 1)
-        judge_count_value(500, 62500/(period + 1));
-    } else {
-        judge_count_value(500, 40000/((period + 1)));  // (500-500*0.125*3)/((period + 1)*800)
+    for (int i = 0; i < SOC_MCPWM_GROUPS; i++) {
+        for (int j = 0; j < SOC_MCPWM_TIMERS_PER_GROUP; j++) {
+            mcpwm_timer_duty_test(i, j);
+        }
     }
-
-    TEST_ESP_OK(mcpwm_carrier_disable(unit, timer));
-    judge_count_value(2, 1000);
 }
 
-static void get_action_level(mcpwm_fault_input_level_t input_sig, mcpwm_action_on_pwmxa_t action_a, mcpwm_action_on_pwmxb_t action_b, int freq, int allow_err)
-{
-    if(action_a == MCPWM_NO_CHANGE_IN_MCPWMXA) {
-        TEST_ASSERT_INT16_WITHIN(allow_err, pcnt_count(GPIO_PWMA_PCNT_INPUT, PCNT_CTRL_FLOATING_IO1, 1000), freq);
-    } else if(action_a == MCPWM_FORCE_MCPWMXA_LOW) {
-        TEST_ASSERT(gpio_get_level(GPIO_PWMA_PCNT_INPUT) == 0);
-    } else if(action_a == MCPWM_FORCE_MCPWMXA_HIGH) {
-        TEST_ASSERT(gpio_get_level(GPIO_PWMA_PCNT_INPUT) == 1);
-    }else {
-        int level =  gpio_get_level(GPIO_PWMA_PCNT_INPUT);
-        vTaskDelay(100 / portTICK_RATE_MS);
-        TEST_ASSERT(gpio_get_level(GPIO_PWMA_PCNT_INPUT) == level);
-    }
-
-    if(action_b == MCPWM_NO_CHANGE_IN_MCPWMXB) {
-        TEST_ASSERT_INT16_WITHIN(allow_err, pcnt_count(GPIO_PWMB_PCNT_INPUT, PCNT_CTRL_FLOATING_IO1, 1000), freq);
-    } else if(action_b == MCPWM_FORCE_MCPWMXB_LOW) {
-        TEST_ASSERT(gpio_get_level(GPIO_PWMB_PCNT_INPUT) == 0);
-    } else if(action_b == MCPWM_FORCE_MCPWMXB_HIGH) {
-        TEST_ASSERT(gpio_get_level(GPIO_PWMB_PCNT_INPUT) == 1);
-    }else {
-        int level =  gpio_get_level(GPIO_PWMB_PCNT_INPUT);
-        vTaskDelay(100 / portTICK_RATE_MS);
-        TEST_ASSERT(gpio_get_level(GPIO_PWMB_PCNT_INPUT) == level);
-    }
-}
+// -------------------------------------------------------------------------------------
 
-// test the fault event
-static void cycle_fault_test(mcpwm_unit_t unit, mcpwm_timer_t timer, mcpwm_fault_signal_t fault_sig,
-                             mcpwm_fault_input_level_t input_sig, mcpwm_io_signals_t fault_io,
-                             mcpwm_action_on_pwmxa_t action_a, mcpwm_action_on_pwmxb_t action_b)
+static void mcpwm_start_stop_test(mcpwm_unit_t unit, mcpwm_timer_t timer)
 {
-    gpio_config_t gp;
-    gp.intr_type = GPIO_INTR_DISABLE;
-    gp.mode = GPIO_MODE_OUTPUT;
-    gp.pin_bit_mask = (1ULL << FAULT_SIG_NUM);
-    gpio_config(&gp); // gpio configure should be more previous than mcpwm configuration
-    gpio_set_level(FAULT_SIG_NUM, !input_sig);
-
-    pcnt_init(GPIO_PWMA_PCNT_INPUT, PCNT_CTRL_FLOATING_IO1);
-    pcnt_init(GPIO_PWMB_PCNT_INPUT, PCNT_CTRL_FLOATING_IO2);
-
-    mcpwm_basic_config(unit, timer);
-    mcpwm_gpio_init(unit, fault_io, GPIO_FAULT_IN);
-
-    // cycle mode, it can be triggered more than once
-    printf("cyc test:\n");
-    gpio_set_level(FAULT_SIG_NUM, !input_sig);
-    TEST_ESP_OK(mcpwm_fault_init(unit, input_sig, fault_sig));
-    TEST_ESP_OK(mcpwm_fault_set_cyc_mode(unit, timer, fault_sig, action_a, action_b));
-    vTaskDelay(1000 / portTICK_RATE_MS);
-    gpio_set_level(FAULT_SIG_NUM, input_sig); // trigger the fault event
-    vTaskDelay(1000 / portTICK_RATE_MS);
-    get_action_level(input_sig, action_a, action_b, 1000, 5);
-    TEST_ESP_OK(mcpwm_fault_deinit(unit, fault_sig));
-}
+    uint32_t pulse_number = 0;
 
-static void oneshot_fault_test(mcpwm_unit_t unit, mcpwm_timer_t timer, mcpwm_fault_signal_t fault_sig,
-                              mcpwm_fault_input_level_t input_sig, mcpwm_io_signals_t fault_io,
-                              mcpwm_action_on_pwmxa_t action_a, mcpwm_action_on_pwmxb_t action_b)
-{
-    gpio_config_t gp;
-    gp.intr_type = GPIO_INTR_DISABLE;
-    gp.mode = GPIO_MODE_OUTPUT;
-    gp.pin_bit_mask = (1ULL << FAULT_SIG_NUM);
-    gpio_config(&gp); // gpio configure should be more previous than mcpwm configuration
-    gpio_set_level(FAULT_SIG_NUM, !input_sig);
-
-    pcnt_init(GPIO_PWMA_PCNT_INPUT, PCNT_CTRL_FLOATING_IO1);
-    pcnt_init(GPIO_PWMB_PCNT_INPUT, PCNT_CTRL_FLOATING_IO2);
-
-    mcpwm_basic_config(unit, timer);
-    mcpwm_gpio_init(unit, fault_io, GPIO_FAULT_IN);
-
-    // one shot mode, it just can be triggered once
-    TEST_ESP_OK(mcpwm_fault_init(unit, input_sig, fault_sig));
-    TEST_ESP_OK(mcpwm_fault_set_oneshot_mode(unit, timer, fault_sig, action_a, action_b));
-    vTaskDelay(10 / portTICK_RATE_MS);
-    // trigger it
-    gpio_set_level(FAULT_SIG_NUM, input_sig);
-    vTaskDelay(10 / portTICK_RATE_MS);
-    get_action_level(input_sig, action_a, action_b, 1000, 5);
-    TEST_ESP_OK(mcpwm_fault_deinit(unit, fault_sig));
-}
+    mcpwm_setup_testbench(unit, timer, 1000, 50.0); // Period: 1000us, 1ms
+    // count the pulse number within 100ms
+    pulse_number = mcpwm_pcnt_get_pulse_number(TEST_PWMA_PCNT_UNIT, 100);
+    TEST_ASSERT_INT_WITHIN(2, 100, pulse_number);
+    pulse_number = mcpwm_pcnt_get_pulse_number(TEST_PWMB_PCNT_UNIT, 100);
+    TEST_ASSERT_INT_WITHIN(2, 100, pulse_number);
 
-// test the sync event
-static void sync_test(mcpwm_unit_t unit, mcpwm_timer_t timer, mcpwm_sync_signal_t sync_sig, mcpwm_io_signals_t sync_io)
-{
-    gpio_config_t gp;
-    gp.intr_type = GPIO_INTR_DISABLE;
-    gp.mode = GPIO_MODE_OUTPUT;
-    gp.pin_bit_mask = (1ULL << SYN_SIG_NUM);
-    gpio_config(&gp);
-    gpio_set_level(SYN_SIG_NUM, 0);
+    TEST_ESP_OK(mcpwm_set_frequency(unit, timer, 100));
+    pulse_number = mcpwm_pcnt_get_pulse_number(TEST_PWMB_PCNT_UNIT, 100);
+    TEST_ASSERT_INT_WITHIN(2, 10, pulse_number);
 
-    mcpwm_io_signals_t mcpwm_a = pwma[timer];
-    mcpwm_io_signals_t mcpwm_b = pwmb[timer];
-    mcpwm_gpio_init(unit, mcpwm_a, GPIO_PWMA_OUT);
-    mcpwm_gpio_init(unit, mcpwm_b, GPIO_PWMB_OUT);
-    mcpwm_gpio_init(unit, sync_io, GPIO_SYNC_IN);
-    mcpwm_config_t pwm_config = {
-        .frequency = 1000,
-        .cmpr_a = 50.0,  //duty cycle of PWMxA = 50.0%
-        .cmpr_b = 50.0,  //duty cycle of PWMxb = 50.0%
-        .counter_mode = MCPWM_UP_COUNTER,
-        .duty_mode = MCPWM_DUTY_MODE_0,
-    };
-    mcpwm_init(unit, timer, &pwm_config);
-    gpio_pulldown_en(GPIO_SYNC_IN);
-
-    mcpwm_sync_enable(unit, timer, sync_sig, 200);
-    //wait for some pulses before sync
-    vTaskDelay(10);
-    gpio_set_level(SYN_SIG_NUM, 1);
-    vTaskDelay(100 / portTICK_RATE_MS);
-    gpio_set_level(SYN_SIG_NUM, 0);
-    mcpwm_sync_disable(unit, timer);
-    vTaskDelay(100 / portTICK_RATE_MS);
+    // stop timer, then no pwm pulse should be generating
+    TEST_ESP_OK(mcpwm_stop(unit, timer));
+    usleep(10000); // wait until timer stopped
+    pulse_number = mcpwm_pcnt_get_pulse_number(TEST_PWMA_PCNT_UNIT, 100);
+    TEST_ASSERT_INT_WITHIN(0, 0, pulse_number);
+    pulse_number = mcpwm_pcnt_get_pulse_number(TEST_PWMB_PCNT_UNIT, 100);
+    TEST_ASSERT_INT_WITHIN(0, 0, pulse_number);
 }
 
-/**
- * use interruption to test the capture event
- * there are two kinds of methods to trigger the capture event:
- * 1. high level trigger
- * 2. low level trigger
- */
-static volatile int flag = 0;
-
-// once capture event happens, will show it
-static void disp_captured_signal(void *arg)
+TEST_CASE("MCPWM start and stop test", "[mcpwm]")
 {
-
-    uint32_t *current_cap_value = (uint32_t *)malloc(sizeof(uint32_t) * CAP_SIG_NUM);
-    uint32_t *previous_cap_value = (uint32_t *)malloc(sizeof(uint32_t) * CAP_SIG_NUM);
-    capture evt;
-    for (int i=0; i<1000; i++) {
-        xQueueReceive(cap_queue, &evt, portMAX_DELAY);
-        if (evt.sel_cap_signal == MCPWM_SELECT_CAP0) {
-            current_cap_value[0] = evt.capture_signal - previous_cap_value[0];
-            previous_cap_value[0] = evt.capture_signal;
-            current_cap_value[0] = (current_cap_value[0] / 10000) * (10000000000 / rtc_clk_apb_freq_get());
-            printf("CAP0 : %d us\n", current_cap_value[0]);
-            cap0_times++;
-        }
-        if (evt.sel_cap_signal == MCPWM_SELECT_CAP1) {
-            current_cap_value[1] = evt.capture_signal - previous_cap_value[1];
-            previous_cap_value[1] = evt.capture_signal;
-            current_cap_value[1] = (current_cap_value[1] / 10000) * (10000000000 / rtc_clk_apb_freq_get());
-            printf("CAP1 : %d us\n", current_cap_value[1]);
-            cap1_times++;
-        }
-        if (evt.sel_cap_signal == MCPWM_SELECT_CAP2) {
-            current_cap_value[2] = evt.capture_signal -  previous_cap_value[2];
-            previous_cap_value[2] = evt.capture_signal;
-            current_cap_value[2] = (current_cap_value[2] / 10000) * (10000000000 / rtc_clk_apb_freq_get());
-            printf("CAP2 : %d us\n", current_cap_value[2]);
-            cap2_times++;
+    for (int i = 0; i < SOC_MCPWM_GROUPS; i++) {
+        for (int j = 0; j < SOC_MCPWM_TIMERS_PER_GROUP; j++) {
+            mcpwm_start_stop_test(i, j);
         }
     }
-    free(current_cap_value);
-    free(previous_cap_value);
-    vTaskDelete(NULL);
 }
 
-// mcpwm event
-static void IRAM_ATTR isr_handler(void *arg)
-{
-    mcpwm_unit_t unit = (mcpwm_unit_t)arg;
-    uint32_t mcpwm_intr_status;
-    capture evt;
-    mcpwm_intr_status = MCPWM[unit]->int_st.val; //Read interrupt status
-    if (mcpwm_intr_status & CAP0_INT_EN) { //Check for interrupt on rising edge on CAP0 signal
-        evt.capture_signal = mcpwm_capture_signal_get_value(unit, MCPWM_SELECT_CAP0); //get capture signal counter value
-        evt.sel_cap_signal = MCPWM_SELECT_CAP0;
-        xQueueSendFromISR(cap_queue, &evt, NULL);
-    }
-    if (mcpwm_intr_status & CAP1_INT_EN) { //Check for interrupt on rising edge on CAP0 signal
-        evt.capture_signal = mcpwm_capture_signal_get_value(unit, MCPWM_SELECT_CAP1); //get capture signal counter value
-        evt.sel_cap_signal = MCPWM_SELECT_CAP1;
-        xQueueSendFromISR(cap_queue, &evt, NULL);
-    }
-    if (mcpwm_intr_status & CAP2_INT_EN) { //Check for interrupt on rising edge on CAP0 signal
-        evt.capture_signal = mcpwm_capture_signal_get_value(unit, MCPWM_SELECT_CAP2); //get capture signal counter value
-        evt.sel_cap_signal = MCPWM_SELECT_CAP2;
-        xQueueSendFromISR(cap_queue, &evt, NULL);
-    }
-    MCPWM[unit]->int_clr.val = mcpwm_intr_status;
-}
+// -------------------------------------------------------------------------------------
 
-// the produce the capture triggering signal to trigger the capture event
-static void gpio_test_signal(void *arg)
+static void mcpwm_deadtime_test(mcpwm_unit_t unit, mcpwm_timer_t timer)
 {
+    mcpwm_setup_testbench(unit, timer, 1000, 50.0); // Period: 1000us, 1ms
+    mcpwm_deadtime_type_t deadtime_type[] = {MCPWM_BYPASS_RED, MCPWM_BYPASS_FED, MCPWM_ACTIVE_HIGH_MODE,
+                                             MCPWM_ACTIVE_LOW_MODE, MCPWM_ACTIVE_HIGH_COMPLIMENT_MODE, MCPWM_ACTIVE_LOW_COMPLIMENT_MODE,
+                                             MCPWM_ACTIVE_RED_FED_FROM_PWMXA, MCPWM_ACTIVE_RED_FED_FROM_PWMXB
+                                            };
 
-    printf("intializing test signal...\n");
-    gpio_config_t gp = {};
-    gp.intr_type = GPIO_INTR_DISABLE;
-    gp.mode = GPIO_MODE_OUTPUT;
-    gp.pin_bit_mask = 1ULL << CAP_SIG_NUM;
-    gpio_config(&gp);
-    for (int i=0; i<1000; i++) {
-        //here the period of test signal is 20ms
-        gpio_set_level(CAP_SIG_NUM, 1); //Set high
-        vTaskDelay(10);             //delay of 10ms
-        gpio_set_level(CAP_SIG_NUM, 0); //Set low
-        vTaskDelay(10);         //delay of 10ms
+    for (size_t i = 0; i < sizeof(deadtime_type) / sizeof(deadtime_type[0]); i++) {
+        mcpwm_stop(unit, timer);
+        usleep(10000);
+        mcpwm_deadtime_enable(unit, timer, deadtime_type[i], 1000, 1000);
+        mcpwm_start(unit, timer);
+        vTaskDelay(pdMS_TO_TICKS(100));
+        mcpwm_deadtime_disable(unit, timer);
     }
-    flag = 1;
-    vTaskDelete(NULL);
+    mcpwm_stop(unit, timer);
 }
 
-
-// capture event test function
-static void capture_test(mcpwm_unit_t unit, mcpwm_timer_t timer, mcpwm_capture_on_edge_t cap_edge)
+TEST_CASE("MCPWM deadtime test", "[mcpwm]")
 {
-    // initialize the capture times
-    cap0_times = 0;
-    cap1_times = 0;
-    cap2_times = 0;
-
-    //each timer test the capture sig with the same id with it.
-    mcpwm_io_signals_t cap_io = cap_io_sig_array[timer];
-    mcpwm_capture_signal_t cap_sig = cap_sig_array[timer];
-
-    mcpwm_gpio_init(unit, cap_io, GPIO_CAP_IN);
-
-    cap_queue = xQueueCreate(1, sizeof(capture));
-    xTaskCreate(disp_captured_signal, "mcpwm_config", 4096, (void *)unit, 5, NULL);
-    xTaskCreate(gpio_test_signal, "gpio_test_signal", 4096, NULL, 5, NULL);
-    mcpwm_capture_enable(unit, cap_sig, cap_edge, 0);
-    MCPWM[unit]->int_ena.val = CAP0_INT_EN | CAP1_INT_EN | CAP2_INT_EN;  //Enable interrupt on  CAP0, CAP1 and CAP2 signal
-    mcpwm_isr_register(unit, isr_handler, (void *)unit, ESP_INTR_FLAG_IRAM, NULL);
-
-    while(flag != 1) {
-        vTaskDelay(10 / portTICK_RATE_MS);
-    }
-    if(cap_sig == MCPWM_SELECT_CAP0) {
-        TEST_ASSERT(1000 == cap0_times);
-    } else if(cap_sig == MCPWM_SELECT_CAP1) {
-        TEST_ASSERT(1000 == cap1_times);
-    }else {
-        TEST_ASSERT(1000 == cap2_times);
+    for (int i = 0; i < SOC_MCPWM_GROUPS; i++) {
+        for (int j = 0; j < SOC_MCPWM_TIMERS_PER_GROUP; j++) {
+            mcpwm_deadtime_test(i, j);
+        }
     }
-    flag = 0; // set flag to 0 that it can be used in other case
-    mcpwm_capture_disable(unit, cap_sig);
-}
-
-/**
- *  duty test:
- *  1. mcpwm_set_duty
- *  2. mcpwm_get_duty
- *
- *  This case's phenomenon should be viewed by logic analyzer
- *  so set it ignore
- */
-TEST_CASE("MCPWM timer0 duty test and each timer works or not test(logic analyzer)", "[mcpwm][ignore]")
-{
-    timer_duty_test(MCPWM_UNIT_0, MCPWM_TIMER_0);
-    timer_duty_test(MCPWM_UNIT_1, MCPWM_TIMER_0);
-}
-
-TEST_CASE("MCPWM timer1 duty test and each timer works or not test(logic analyzer)", "[mcpwm][ignore]")
-{
-    timer_duty_test(MCPWM_UNIT_0, MCPWM_TIMER_1);
-    timer_duty_test(MCPWM_UNIT_1, MCPWM_TIMER_1);
-}
-TEST_CASE("MCPWM timer2 duty test and each timer works or not test(logic analyzer)", "[mcpwm][ignore]")
-{
-    timer_duty_test(MCPWM_UNIT_0, MCPWM_TIMER_2);
-    timer_duty_test(MCPWM_UNIT_1, MCPWM_TIMER_2);
-}
-
-// the deadtime configuration test
-// use the logic analyzer to make sure it goes right
-TEST_CASE("MCPWM timer0 deadtime configuration(logic analyzer)", "[mcpwm][ignore]")
-{
-    deadtime_test(MCPWM_UNIT_0, MCPWM_TIMER_0);
-    deadtime_test(MCPWM_UNIT_1, MCPWM_TIMER_0);
-}
-
-TEST_CASE("MCPWM timer1 deadtime configuration(logic analyzer)", "[mcpwm][ignore]")
-{
-    deadtime_test(MCPWM_UNIT_0, MCPWM_TIMER_1);
-    deadtime_test(MCPWM_UNIT_1, MCPWM_TIMER_1);
-}
-
-TEST_CASE("MCPWM timer2 deadtime configuration(logic analyzer)", "[mcpwm][ignore]")
-{
-    deadtime_test(MCPWM_UNIT_0, MCPWM_TIMER_2);
-    deadtime_test(MCPWM_UNIT_1, MCPWM_TIMER_2);
-}
-
-TEST_CASE("MCPWM timer0 start and stop test", "[mcpwm][test_env=UT_T1_MCPWM]")
-{
-    start_stop_test(MCPWM_UNIT_0, MCPWM_TIMER_0);
-    start_stop_test(MCPWM_UNIT_1, MCPWM_TIMER_0);
 }
 
-// mcpwm start and stop test
-TEST_CASE("MCPWM timer1 start and stop test", "[mcpwm][test_env=UT_T1_MCPWM]")
-{
-    start_stop_test(MCPWM_UNIT_0, MCPWM_TIMER_1);
-    start_stop_test(MCPWM_UNIT_1, MCPWM_TIMER_1);
-}
+// -------------------------------------------------------------------------------------
 
-TEST_CASE("MCPWM timer2 start and stop test", "[mcpwm][test_env=UT_T1_MCPWM]")
+static void mcpwm_carrier_test(mcpwm_unit_t unit, mcpwm_timer_t timer, mcpwm_carrier_out_ivt_t invert_or_not,
+                               uint8_t period, uint8_t duty, uint8_t os_width)
 {
-    start_stop_test(MCPWM_UNIT_0, MCPWM_TIMER_2);
-    start_stop_test(MCPWM_UNIT_1, MCPWM_TIMER_2);
-}
+    uint32_t pulse_number = 0;
 
-TEST_CASE("MCPWM timer0 carrier test with set function", "[mcpwm][test_env=UT_T1_MCPWM]")
-{
-    carrier_with_set_function_test(MCPWM_UNIT_0, MCPWM_TIMER_0,
-                                   MCPWM_CARRIER_OUT_IVT_DIS, 6, 3, 3);
-    carrier_with_set_function_test(MCPWM_UNIT_0, MCPWM_TIMER_0,
-                                   MCPWM_CARRIER_OUT_IVT_EN, 6, 3, 3);
-    carrier_with_set_function_test(MCPWM_UNIT_1, MCPWM_TIMER_0,
-                                   MCPWM_CARRIER_OUT_IVT_DIS, 6, 3, 3);
-    carrier_with_set_function_test(MCPWM_UNIT_1, MCPWM_TIMER_0,
-                                   MCPWM_CARRIER_OUT_IVT_EN, 6, 3, 3);
-}
+    mcpwm_setup_testbench(unit, timer, 1000, 50.0);
+    mcpwm_set_signal_high(unit, timer, MCPWM_GEN_A);
+    mcpwm_set_signal_high(unit, timer, MCPWM_GEN_B);
+    TEST_ESP_OK(mcpwm_carrier_enable(unit, timer));
+    TEST_ESP_OK(mcpwm_carrier_set_period(unit, timer, period));   //carrier revolution
+    TEST_ESP_OK(mcpwm_carrier_set_duty_cycle(unit, timer, duty)); // carrier duty
+    TEST_ESP_OK(mcpwm_carrier_output_invert(unit, timer, invert_or_not));
+    TEST_ESP_OK(mcpwm_carrier_oneshot_mode_enable(unit, timer, os_width));
+    vTaskDelay(pdMS_TO_TICKS(100));
 
-TEST_CASE("MCPWM timer1 carrier test with set function", "[mcpwm][test_env=UT_T1_MCPWM]")
-{
-    carrier_with_set_function_test(MCPWM_UNIT_0, MCPWM_TIMER_1,
-                                   MCPWM_CARRIER_OUT_IVT_DIS, 6, 3, 3);
-    carrier_with_set_function_test(MCPWM_UNIT_0, MCPWM_TIMER_1,
-                                   MCPWM_CARRIER_OUT_IVT_EN, 6, 3, 3);
-    carrier_with_set_function_test(MCPWM_UNIT_1, MCPWM_TIMER_1,
-                                   MCPWM_CARRIER_OUT_IVT_DIS, 6, 3, 3);
-    carrier_with_set_function_test(MCPWM_UNIT_1, MCPWM_TIMER_1,
-                                   MCPWM_CARRIER_OUT_IVT_EN, 6, 3, 3);
-}
+    pulse_number = mcpwm_pcnt_get_pulse_number(TEST_PWMA_PCNT_UNIT, 10);
+    TEST_ASSERT_INT_WITHIN(50, 2500, pulse_number);
+    usleep(10000);
+    pulse_number = mcpwm_pcnt_get_pulse_number(TEST_PWMB_PCNT_UNIT, 10);
+    TEST_ASSERT_INT_WITHIN(50, 2500, pulse_number);
 
-TEST_CASE("MCPWM timer2 carrier test with set function", "[mcpwm][test_env=UT_T1_MCPWM]")
-{
-    carrier_with_set_function_test(MCPWM_UNIT_0, MCPWM_TIMER_2,
-                                   MCPWM_CARRIER_OUT_IVT_DIS, 6, 3, 3);
-    carrier_with_set_function_test(MCPWM_UNIT_0, MCPWM_TIMER_2,
-                                   MCPWM_CARRIER_OUT_IVT_EN, 6, 3, 3);
-    carrier_with_set_function_test(MCPWM_UNIT_1, MCPWM_TIMER_2,
-                                   MCPWM_CARRIER_OUT_IVT_DIS, 6, 3, 3);
-    carrier_with_set_function_test(MCPWM_UNIT_1, MCPWM_TIMER_2,
-                                   MCPWM_CARRIER_OUT_IVT_EN, 6, 3, 3);
+    TEST_ESP_OK(mcpwm_carrier_disable(unit, timer));
+    TEST_ESP_OK(mcpwm_stop(unit, timer));
 }
 
-
-static void test_carrier_with_config_func(mcpwm_unit_t unit, mcpwm_timer_t timer)
+TEST_CASE("MCPWM carrier test", "[mcpwm]")
 {
-    mcpwm_carrier_os_t oneshot[2] = {MCPWM_ONESHOT_MODE_DIS, MCPWM_ONESHOT_MODE_EN};
-    mcpwm_carrier_out_ivt_t invert[2] = {MCPWM_CARRIER_OUT_IVT_DIS, MCPWM_CARRIER_OUT_IVT_EN};
-    ESP_LOGI(TAG, "test unit%d timer%d", unit, timer);
-
-    for(int i=0; i<2; i++){
-        for(int j=0; j<2; j++) {
-            printf("i=%d, j=%d\n", i, j);
-            carrier_with_configuration_test(unit, timer, oneshot[i], invert[j], 6, 3, 3);
+    for (int i = 0; i < SOC_MCPWM_GROUPS; i++) {
+        for (int j = 0; j < SOC_MCPWM_TIMERS_PER_GROUP; j++) {
+            // carrier should be 10MHz/8/(4+1) = 250KHz, (10MHz is the group resolution, it's fixed in the driver), carrier duty cycle is 4/8 = 50%
+            mcpwm_carrier_test(i, j, MCPWM_CARRIER_OUT_IVT_DIS, 4, 4, 3);
+            mcpwm_carrier_test(i, j, MCPWM_CARRIER_OUT_IVT_EN, 4, 4, 3);
         }
     }
 }
 
-TEST_CASE("MCPWM timer0 carrier test with configuration function", "[mcpwm][test_env=UT_T1_MCPWM][timeout=120]")
-{
-    test_carrier_with_config_func(MCPWM_UNIT_0, MCPWM_TIMER_0);
-    test_carrier_with_config_func(MCPWM_UNIT_1, MCPWM_TIMER_0);
-}
+// -------------------------------------------------------------------------------------
 
-TEST_CASE("MCPWM timer1 carrier test with configuration function", "[mcpwm][test_env=UT_T1_MCPWM][timeout=120]") {
-    test_carrier_with_config_func(MCPWM_UNIT_0, MCPWM_TIMER_1);
-    test_carrier_with_config_func(MCPWM_UNIT_1, MCPWM_TIMER_1);
-}
-
-TEST_CASE("MCPWM timer2 carrier test with configuration function", "[mcpwm][test_env=UT_T1_MCPWM][timeout=120]")
+static void mcpwm_check_generator_level_on_fault(mcpwm_action_on_pwmxa_t action_a, mcpwm_action_on_pwmxb_t action_b)
 {
-    test_carrier_with_config_func(MCPWM_UNIT_0, MCPWM_TIMER_2);
-    test_carrier_with_config_func(MCPWM_UNIT_1, MCPWM_TIMER_2);
+    if (action_a == MCPWM_ACTION_FORCE_LOW) {
+        TEST_ASSERT_EQUAL(0, gpio_get_level(TEST_PWMA_GPIO));
+    } else if (action_a == MCPWM_ACTION_FORCE_HIGH) {
+        TEST_ASSERT_EQUAL(1, gpio_get_level(TEST_PWMA_GPIO));
+    }
+
+    if (action_b == MCPWM_ACTION_FORCE_LOW) {
+        TEST_ASSERT_EQUAL(0, gpio_get_level(TEST_PWMB_GPIO));
+    } else if (action_b == MCPWM_ACTION_FORCE_HIGH) {
+        TEST_ASSERT_EQUAL(1, gpio_get_level(TEST_PWMB_GPIO));
+    }
 }
 
-/**
- * Fault event:
- * Just support high level triggering
- * There are two types fault event:
- * 1. one-shot: it just can be triggered once, its effect is forever and it will never be changed although the fault signal change
- * 2. cycle: it can be triggered more than once, it will changed just as the fault signal changes. If set it triggered by high level,
- *           when the fault signal is high level, the event will be triggered. But the event will disappear as the fault signal disappears
- */
-void test_cycle_fault(mcpwm_unit_t unit, mcpwm_timer_t timer)
+static void mcpwm_fault_cbc_test(mcpwm_unit_t unit, mcpwm_timer_t timer)
 {
-    // API just supports the high level trigger now, so comment it
-    //    mcpwm_fault_input_level_t fault_input[2] = {MCPWM_LOW_LEVEL_TGR, MCPWM_HIGH_LEVEL_TGR};
-    mcpwm_action_on_pwmxa_t action_a[4] = {MCPWM_NO_CHANGE_IN_MCPWMXA, MCPWM_FORCE_MCPWMXA_LOW, MCPWM_FORCE_MCPWMXA_HIGH, MCPWM_TOG_MCPWMXA};
-    mcpwm_action_on_pwmxb_t action_b[4] = {MCPWM_NO_CHANGE_IN_MCPWMXB, MCPWM_FORCE_MCPWMXB_LOW, MCPWM_FORCE_MCPWMXB_HIGH, MCPWM_TOG_MCPWMXB};
-    ESP_LOGI(TAG, "test unit%d timer%d", unit, timer);
+    mcpwm_action_on_pwmxa_t action_a[] = {MCPWM_ACTION_FORCE_LOW, MCPWM_ACTION_FORCE_HIGH};
+    mcpwm_action_on_pwmxb_t action_b[] = {MCPWM_ACTION_FORCE_LOW, MCPWM_ACTION_FORCE_HIGH};
 
-    //each timer test the fault sig with the same id with it.
     mcpwm_fault_signal_t fault_sig = fault_sig_array[timer];
     mcpwm_io_signals_t fault_io_sig = fault_io_sig_array[timer];
 
-    for(int i=0; i<4; i++){
-        for(int j=0; j<4; j++) {
-            printf("i=%d, j=%d\n",i, j);
-            cycle_fault_test(unit, timer, fault_sig, MCPWM_HIGH_LEVEL_TGR, fault_io_sig, action_a[i], action_b[j]);
+    mcpwm_setup_testbench(unit, timer, 1000, 50.0);
+    TEST_ESP_OK(test_mcpwm_gpio_init(unit, fault_io_sig, TEST_FAULT_GPIO));
+    gpio_set_level(TEST_FAULT_GPIO, 0);
+    TEST_ESP_OK(mcpwm_fault_init(unit, MCPWM_HIGH_LEVEL_TGR, fault_sig));
+    for (int i = 0; i < sizeof(action_a) / sizeof(action_a[0]); i++) {
+        for (int j = 0; j < sizeof(action_b) / sizeof(action_b[0]); j++) {
+            TEST_ESP_OK(mcpwm_fault_set_cyc_mode(unit, timer, fault_sig, action_a[i], action_b[j]));
+            gpio_set_level(TEST_FAULT_GPIO, 1); // trigger the fault event
+            usleep(10000);
+            mcpwm_check_generator_level_on_fault(action_a[i], action_b[j]);
+            gpio_set_level(TEST_FAULT_GPIO, 0); // remove the fault signal
+            usleep(10000);
         }
     }
+    TEST_ESP_OK(mcpwm_fault_deinit(unit, fault_sig));
 }
 
-TEST_CASE("MCPWM timer0 cycle fault test", "[mcpwm][test_env=UT_T1_MCPWM][timeout=180]")
-{
-    test_cycle_fault(MCPWM_UNIT_0, MCPWM_TIMER_0);
-    test_cycle_fault(MCPWM_UNIT_1, MCPWM_TIMER_0);
-}
-
-TEST_CASE("MCPWM timer1 cycle fault test", "[mcpwm][test_env=UT_T1_MCPWM][timeout=180]")
+TEST_CASE("MCPWM fault cbc test", "[mcpwm]")
 {
-    test_cycle_fault(MCPWM_UNIT_0, MCPWM_TIMER_1);
-    test_cycle_fault(MCPWM_UNIT_1, MCPWM_TIMER_1);
+    for (int i = 0; i < SOC_MCPWM_GROUPS; i++) {
+        for (int j = 0; j < SOC_MCPWM_TIMERS_PER_GROUP; j++) {
+            mcpwm_fault_cbc_test(i, j);
+        }
+    }
 }
 
-TEST_CASE("MCPWM timer2 cycle fault test", "[mcpwm][test_env=UT_T1_MCPWM][timeout=180]")
-{
-    test_cycle_fault(MCPWM_UNIT_0, MCPWM_TIMER_2);
-    test_cycle_fault(MCPWM_UNIT_1, MCPWM_TIMER_2);
-}
+// -------------------------------------------------------------------------------------
 
-static void test_oneshot_fault(mcpwm_unit_t unit, mcpwm_timer_t timer)
+static void mcpwm_fault_ost_test(mcpwm_unit_t unit, mcpwm_timer_t timer)
 {
-//    API just supports the high level trigger now, so comment it
-//    mcpwm_fault_input_level_t fault_input[2] = {MCPWM_LOW_LEVEL_TGR, MCPWM_HIGH_LEVEL_TGR};
-    mcpwm_action_on_pwmxa_t action_a[4] = {MCPWM_NO_CHANGE_IN_MCPWMXA, MCPWM_FORCE_MCPWMXA_LOW, MCPWM_FORCE_MCPWMXA_HIGH, MCPWM_TOG_MCPWMXA};
-    mcpwm_action_on_pwmxb_t action_b[4] = {MCPWM_NO_CHANGE_IN_MCPWMXB, MCPWM_FORCE_MCPWMXB_LOW, MCPWM_FORCE_MCPWMXB_HIGH, MCPWM_TOG_MCPWMXB};
+    mcpwm_action_on_pwmxa_t action_a[] = {MCPWM_ACTION_FORCE_LOW, MCPWM_ACTION_FORCE_HIGH};
+    mcpwm_action_on_pwmxb_t action_b[] = {MCPWM_ACTION_FORCE_LOW, MCPWM_ACTION_FORCE_HIGH};
 
-    //each timer test the fault sig with the same id with it.
     mcpwm_fault_signal_t fault_sig = fault_sig_array[timer];
     mcpwm_io_signals_t fault_io_sig = fault_io_sig_array[timer];
 
-    ESP_LOGI(TAG, "test pwm unit%d, timer%d fault_sig%d", unit, timer, fault_sig);
-    for(int i=0; i<4; i++){
-        for(int j=0; j<4; j++) {
-            printf("action (%d, %d)\n", i, j);
-            oneshot_fault_test(unit, timer, fault_sig, MCPWM_HIGH_LEVEL_TGR, fault_io_sig, action_a[i], action_b[j]);
+    mcpwm_setup_testbench(unit, timer, 1000, 50.0);
+    TEST_ESP_OK(test_mcpwm_gpio_init(unit, fault_io_sig, TEST_FAULT_GPIO));
+    gpio_set_level(TEST_FAULT_GPIO, 0);
+    TEST_ESP_OK(mcpwm_fault_init(unit, MCPWM_HIGH_LEVEL_TGR, fault_sig));
+    for (int i = 0; i < sizeof(action_a) / sizeof(action_a[0]); i++) {
+        for (int j = 0; j < sizeof(action_b) / sizeof(action_b[0]); j++) {
+            TEST_ESP_OK(mcpwm_fault_set_oneshot_mode(unit, timer, fault_sig, action_a[i], action_b[j]));
+            gpio_set_level(TEST_FAULT_GPIO, 1); // trigger the fault event
+            usleep(10000);
+            mcpwm_check_generator_level_on_fault(action_a[i], action_b[j]);
+            gpio_set_level(TEST_FAULT_GPIO, 0); // remove the fault signal
+            usleep(10000);
         }
     }
+    TEST_ESP_OK(mcpwm_fault_deinit(unit, fault_sig));
 }
 
-TEST_CASE("MCPWM timer0 one shot fault test", "[mcpwm][test_env=UT_T1_MCPWM][timeout=60]")
-{
-    test_oneshot_fault(MCPWM_UNIT_0, MCPWM_TIMER_0);
-    test_oneshot_fault(MCPWM_UNIT_1, MCPWM_TIMER_0);
-}
-
-TEST_CASE("MCPWM timer1 one shot fault test", "[mcpwm][test_env=UT_T1_MCPWM][timeout=60]")
+TEST_CASE("MCPWM fault ost test", "[mcpwm]")
 {
-    test_oneshot_fault(MCPWM_UNIT_0, MCPWM_TIMER_1);
-    test_oneshot_fault(MCPWM_UNIT_1, MCPWM_TIMER_1);
+    for (int i = 0; i < SOC_MCPWM_GROUPS; i++) {
+        for (int j = 0; j < SOC_MCPWM_TIMERS_PER_GROUP; j++) {
+            mcpwm_fault_ost_test(i, j);
+        }
+    }
 }
 
-TEST_CASE("MCPWM timer2 one shot fault test", "[mcpwm][test_env=UT_T1_MCPWM][timeout=60]")
-{
-    test_oneshot_fault(MCPWM_UNIT_0, MCPWM_TIMER_2);
-    test_oneshot_fault(MCPWM_UNIT_1, MCPWM_TIMER_2);
-}
+// -------------------------------------------------------------------------------------
 
-static void test_sync(mcpwm_timer_t timer)
+static void mcpwm_sync_test(mcpwm_unit_t unit, mcpwm_timer_t timer)
 {
-    //each timer test the sync sig with the same id with it.
     mcpwm_sync_signal_t sync_sig = sync_sig_array[timer];
     mcpwm_io_signals_t sync_io_sig = sync_io_sig_array[timer];
 
-    sync_test(MCPWM_UNIT_0, timer, sync_sig, sync_io_sig);
-    TEST_ESP_OK(mcpwm_stop(MCPWM_UNIT_0, timer)); // make sure can view the next sync signal clearly
-    vTaskDelay(100 / portTICK_RATE_MS);
-    TEST_ESP_OK(mcpwm_start(MCPWM_UNIT_0, timer));
-    sync_test(MCPWM_UNIT_1, timer, sync_sig, sync_io_sig);
-}
+    mcpwm_setup_testbench(unit, timer, 1000, 50.0);
+    TEST_ESP_OK(test_mcpwm_gpio_init(unit, sync_io_sig, TEST_SYNC_GPIO));
+    gpio_set_level(TEST_SYNC_GPIO, 0);
 
-// need to view its phenomenon in logic analyzer
-// set it ignore
-TEST_CASE("MCPWM timer0 sync test(logic analyzer)", "[mcpwm][ignore]")
-{
-    test_sync(MCPWM_TIMER_0);
-}
-
-// need to view its phenomenon in logic analyzer
-// set it ignore
-TEST_CASE("MCPWM timer1 sync test(logic analyzer)", "[mcpwm][ignore]")
-{
-    test_sync(MCPWM_TIMER_1);
-}
-
-// need to view its phenomenon in logic analyzer
-// set it ignore
-TEST_CASE("MCPWM timer2 sync test(logic analyzer)", "[mcpwm][ignore]")
-{
-    test_sync(MCPWM_TIMER_2);
+    TEST_ESP_OK(mcpwm_sync_enable(unit, timer, sync_sig, 200));
+    vTaskDelay(pdMS_TO_TICKS(50));
+    gpio_set_level(TEST_SYNC_GPIO, 1); // trigger an external sync event
+    vTaskDelay(pdMS_TO_TICKS(50));
+    TEST_ESP_OK(mcpwm_sync_disable(unit, timer));
+    TEST_ESP_OK(mcpwm_stop(unit, timer));
 }
 
-TEST_CASE("MCPWM unit0, timer0 capture test", "[mcpwm][test_env=UT_T1_MCPWM][timeout=60]")
+TEST_CASE("MCPWM timer sync test", "[mcpwm]")
 {
-    capture_test(MCPWM_UNIT_0, MCPWM_TIMER_0, MCPWM_POS_EDGE);
+    for (int i = 0; i < SOC_MCPWM_GROUPS; i++) {
+        for (int j = 0; j < SOC_MCPWM_TIMERS_PER_GROUP; j++) {
+            mcpwm_sync_test(i, j);
+        }
+    }
 }
 
-TEST_CASE("MCPWM unit0, timer1 capture test", "[mcpwm][test_env=UT_T1_MCPWM][timeout=60]")
-{
-    capture_test(MCPWM_UNIT_0, MCPWM_TIMER_1, MCPWM_POS_EDGE);
-}
+// -------------------------------------------------------------------------------------
 
-TEST_CASE("MCPWM unit0, timer2 capture test", "[mcpwm][test_env=UT_T1_MCPWM][timeout=60]")
+static void mcpwm_capture_test(mcpwm_unit_t unit, mcpwm_capture_signal_t cap_chan)
 {
-    capture_test(MCPWM_UNIT_0, MCPWM_TIMER_2, MCPWM_POS_EDGE);
-}
+    typedef struct {
+        mcpwm_unit_t unit;
+        TaskHandle_t task_hdl;
+    } test_capture_callback_data_t;
 
-TEST_CASE("MCPWM unit1, timer0 capture test", "[mcpwm][test_env=UT_T1_MCPWM][timeout=60]")
-{
-    capture_test(MCPWM_UNIT_1, MCPWM_TIMER_0, MCPWM_NEG_EDGE);
-}
+    void test_mcpwm_intr_handler(void *arg) {
+        BaseType_t high_task_wakeup = pdFALSE;
+        test_capture_callback_data_t *cb_data = (test_capture_callback_data_t *)arg;
+        uint32_t status = MCPWM[cb_data->unit]->int_st.val;
+        MCPWM[cb_data->unit]->int_clr.val = status;
+        vTaskNotifyGiveFromISR(cb_data->task_hdl, &high_task_wakeup);
+        if (high_task_wakeup == pdTRUE) {
+            portYIELD_FROM_ISR();
+        }
+    }
 
-TEST_CASE("MCPWM unit1, timer1 capture test", "[mcpwm][test_env=UT_T1_MCPWM][timeout=60]")
-{
-    capture_test(MCPWM_UNIT_1, MCPWM_TIMER_1, MCPWM_POS_EDGE);
-}
+    intr_handle_t mcpwm_intr = NULL;
+    test_capture_callback_data_t callback_data = {
+        .unit = unit,
+        .task_hdl = xTaskGetCurrentTaskHandle(),
+    };
 
-TEST_CASE("MCPWM unit1, timer2 capture test", "[mcpwm][test_env=UT_T1_MCPWM][timeout=60]")
-{
-    capture_test(MCPWM_UNIT_1, MCPWM_TIMER_2, MCPWM_POS_EDGE);
+    //each timer test the capture sig with the same id with it.
+    mcpwm_io_signals_t cap_io = cap_io_sig_array[cap_chan];
+    mcpwm_capture_signal_t cap_sig = cap_sig_array[cap_chan];
+
+    TEST_ESP_OK(test_mcpwm_gpio_init(unit, cap_io, TEST_CAP_GPIO));
+    TEST_ESP_OK(mcpwm_capture_enable(unit, cap_sig, MCPWM_POS_EDGE, 0));
+    TEST_ESP_OK(mcpwm_isr_register(unit, test_mcpwm_intr_handler, &callback_data, 0, &mcpwm_intr));
+    // generate an posage
+    gpio_set_level(TEST_CAP_GPIO, 0);
+    gpio_set_level(TEST_CAP_GPIO, 1);
+    vTaskDelay(pdMS_TO_TICKS(100));
+    TEST_ASSERT_NOT_EQUAL(0, ulTaskNotifyTake(pdFALSE, pdMS_TO_TICKS(40)));
+    uint32_t cap_val0 = mcpwm_capture_signal_get_value(unit, cap_chan);
+    // generate another posage
+    gpio_set_level(TEST_CAP_GPIO, 0);
+    gpio_set_level(TEST_CAP_GPIO, 1);
+    TEST_ASSERT_NOT_EQUAL(0, ulTaskNotifyTake(pdFALSE, pdMS_TO_TICKS(40)));
+    uint32_t cap_val1 = mcpwm_capture_signal_get_value(unit, cap_chan);
+    // capture clock source is APB (80MHz), 100ms means 8000000 ticks
+    TEST_ASSERT_UINT_WITHIN(100000, 8000000, cap_val1 - cap_val0);
+
+    TEST_ESP_OK(mcpwm_capture_disable(unit, cap_sig));
+    TEST_ESP_OK(esp_intr_free(mcpwm_intr));
+}
+
+TEST_CASE("MCPWM capture test", "[mcpwm]")
+{
+    // we assume each group has one capture timer
+    for (int i = 0; i < SOC_MCPWM_GROUPS; i++) {
+        for (int j = 0; j < SOC_MCPWM_CAPTURE_CHANNELS_PER_TIMER; j++) {
+            mcpwm_capture_test(i, j);
+        }
+    }
 }
 
-#endif // !TEMPORARY_DISABLED_FOR_TARGETS(ESP32S3)
 #endif // SOC_MCPWM_SUPPORTED

+ 1 - 0
components/hal/CMakeLists.txt

@@ -73,6 +73,7 @@ if(NOT BOOTLOADER_BUILD)
         list(APPEND srcs
             "gdma_hal.c"
             "lcd_hal.c"
+            "mcpwm_hal.c"
             "pcnt_hal.c"
             "spi_flash_hal_gpspi.c"
             "spi_slave_hd_hal.c"

+ 823 - 619
components/hal/esp32/include/hal/mcpwm_ll.h

@@ -1,9 +1,9 @@
-// Copyright 2015-2019 Espressif Systems (Shanghai) PTE LTD
+// Copyright 2015-2021 Espressif Systems (Shanghai) PTE LTD
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
 // You may obtain a copy of the License at
-
+//
 //     http://www.apache.org/licenses/LICENSE-2.0
 //
 // Unless required by applicable law or agreed to in writing, software
@@ -22,712 +22,916 @@
 
 #pragma once
 
+#include <stdbool.h>
 #include "soc/soc_caps.h"
-#include <soc/mcpwm_periph.h>
-#include "soc/mcpwm_periph.h"
+#include "soc/mcpwm_struct.h"
 #include "hal/mcpwm_types.h"
-#include "hal/hal_defs.h"
-
-#include "esp_types.h"
 
 #ifdef __cplusplus
 extern "C" {
 #endif
 
 /// Get the address of peripheral registers
-#define MCPWM_LL_GET_HW(ID) (((ID)==0)? &MCPWM0: &MCPWM1)
+#define MCPWM_LL_GET_HW(ID)     (((ID) == 0) ? &MCPWM0 : &MCPWM1)
 #define MCPWM_LL_MAX_PRESCALE   255
 
-/********************* Global *******************/
-/**
- * Initialize common registers.
- *
- * @param mcpwm Address of the MCPWM peripheral registers.
- */
-static inline void mcpwm_ll_init(mcpwm_dev_t *mcpwm)
+/********************* Group registers *******************/
+
+// Set/Get group clock: PWM_clk = CLK_160M / (prescale + 1)
+static inline void mcpwm_ll_group_set_clock(mcpwm_dev_t *mcpwm, unsigned long long group_clk_hz)
+{
+    mcpwm->clk_cfg.prescale = (SOC_MCPWM_BASE_CLK_HZ / group_clk_hz) - 1;
+}
+
+static inline unsigned long long mcpwm_ll_group_get_clock(mcpwm_dev_t *mcpwm)
+{
+    return SOC_MCPWM_BASE_CLK_HZ / (mcpwm->clk_cfg.prescale + 1);
+}
+
+static inline void mcpwm_ll_group_enable_shadow_mode(mcpwm_dev_t *mcpwm)
 {
     mcpwm->update_cfg.global_up_en = 1;
-    mcpwm->update_cfg.global_force_up = 1;
-    mcpwm->update_cfg.global_force_up = 0;
+    mcpwm->update_cfg.op0_up_en = 1;
+    mcpwm->update_cfg.op1_up_en = 1;
+    mcpwm->update_cfg.op2_up_en = 1;
+}
+
+static inline void mcpwm_ll_group_flush_shadow(mcpwm_dev_t *mcpwm)
+{
+    mcpwm->update_cfg.val ^= (1 << 1);
+}
+
+/********************* Interrupt registers *******************/
+
+static inline uint32_t mcpwm_ll_intr_get_status(mcpwm_dev_t *mcpwm)
+{
+    return mcpwm->int_st.val;
+}
+
+static inline void mcpwm_ll_intr_clear_status(mcpwm_dev_t *mcpwm, uint32_t intr_mask)
+{
+    mcpwm->int_clr.val = intr_mask;
+}
+
+static inline void mcpwm_ll_intr_disable_all(mcpwm_dev_t *mcpwm)
+{
+    mcpwm->int_ena.val = 0;
+}
+
+//////////// get interrupt status for each event ////////////////
+
+static inline uint32_t mcpwm_ll_intr_get_timer_stop_status(mcpwm_dev_t *mcpwm)
+{
+    return (mcpwm->int_st.val >> 0) & 0x07;
+}
+
+static inline uint32_t mcpwm_ll_intr_get_timer_tez_status(mcpwm_dev_t *mcpwm)
+{
+    return (mcpwm->int_st.val >> 3) & 0x07;
+}
+
+static inline uint32_t mcpwm_ll_intr_get_timer_tep_status(mcpwm_dev_t *mcpwm)
+{
+    return (mcpwm->int_st.val >> 6) & 0x07;
+}
+
+static inline uint32_t mcpwm_ll_intr_get_fault_enter_status(mcpwm_dev_t *mcpwm)
+{
+    return (mcpwm->int_st.val >> 9) & 0x07;
+}
+
+static inline uint32_t mcpwm_ll_intr_get_fault_exit_status(mcpwm_dev_t *mcpwm)
+{
+    return (mcpwm->int_st.val >> 12) & 0x07;
 }
 
-/**
- * Set the prescale of the PWM main clock to the input clock.
- *
- * Input clock is 160MHz, PWM main clock cycle = 6.25ns*(prescale + 1).
- *
- * @param mcpwm Address of the MCPWM peripheral registers.
- * @param prescale Prescale factor, 0-255.
- */
-static inline void mcpwm_ll_set_clock_prescale(mcpwm_dev_t *mcpwm, int prescale)
+static inline uint32_t mcpwm_ll_intr_get_compare_status(mcpwm_dev_t *mcpwm, uint32_t cmp_id)
+{
+    return (mcpwm->int_st.val >> (15 + cmp_id * 3)) & 0x07;
+}
+
+static inline uint32_t mcpwm_ll_intr_get_trip_cbc_status(mcpwm_dev_t *mcpwm)
+{
+    return (mcpwm->int_st.val >> 21) & 0x07;
+}
+
+static inline uint32_t mcpwm_ll_intr_get_trip_ost_status(mcpwm_dev_t *mcpwm)
+{
+    return (mcpwm->int_st.val >> 24) & 0x07;
+}
+
+static inline uint32_t mcpwm_ll_intr_get_capture_status(mcpwm_dev_t *mcpwm)
+{
+    return (mcpwm->int_st.val >> 27) & 0x07;
+}
+
+//////////// clear interrupt status for each event ////////////////
+
+static inline void mcpwm_ll_intr_clear_timer_stop_status(mcpwm_dev_t *mcpwm, uint32_t timer_mask)
 {
-    mcpwm->clk_cfg.prescale = prescale;
+    mcpwm->int_clr.val |= (timer_mask & 0x07) << 0;
 }
 
-STATIC_HAL_REG_CHECK(MCPWM, MCPWM_LL_INTR_CAP0, MCPWM_CAP0_INT_RAW);
-STATIC_HAL_REG_CHECK(MCPWM, MCPWM_LL_INTR_CAP1, MCPWM_CAP1_INT_RAW);
-STATIC_HAL_REG_CHECK(MCPWM, MCPWM_LL_INTR_CAP2, MCPWM_CAP2_INT_RAW);
+static inline void mcpwm_ll_intr_clear_timer_tez_status(mcpwm_dev_t *mcpwm, uint32_t timer_mask)
+{
+    mcpwm->int_clr.val |= (timer_mask & 0x07) << 3;
+}
 
-/**
- * Get raw interrupt status.
- *
- * @param mcpwm Address of the MCPWM peripheral registers.
- * @return The triggered interrupts, ORed by active interrupts.
- */
-static inline mcpwm_intr_t mcpwm_ll_get_intr(mcpwm_dev_t *mcpwm)
+static inline void mcpwm_ll_intr_clear_timer_tep_status(mcpwm_dev_t *mcpwm, uint32_t timer_mask)
 {
-    return mcpwm->int_raw.val;
+    mcpwm->int_clr.val |= (timer_mask & 0x07) << 6;
 }
 
-/**
- * Clear the interrupts.
- *
- * @param mcpwm Address of the MCPWM peripheral registers.
- * @param intr Bitwise ORed interrupts to clear.
- */
-static inline void mcpwm_ll_clear_intr(mcpwm_dev_t* mcpwm, mcpwm_intr_t intr)
+static inline void mcpwm_ll_intr_clear_fault_enter_status(mcpwm_dev_t *mcpwm, uint32_t fault_mask)
 {
-    mcpwm->int_clr.val = intr;
+    mcpwm->int_clr.val |= (fault_mask & 0x07) << 9;
 }
 
-/********************* Timer *******************/
-/**
- * Set the prescale of the Timer_x clock to the PWM main clock.
- *
- * Timer clock frequency = PWM main clock frequency/(prescale + 1).
- *
- * @param mcpwm Address of the MCPWM peripheral registers.
- * @param timer The timer to set the prescale, 0-2.
- * @param prescale Prescale factor, 0-255.
- */
-static inline void mcpwm_ll_timer_set_prescale(mcpwm_dev_t* mcpwm, int timer, uint32_t prescale)
+static inline void mcpwm_ll_intr_clear_fault_exit_status(mcpwm_dev_t *mcpwm, uint32_t fault_mask)
 {
-    mcpwm->timer[timer].period.prescale = prescale;
+    mcpwm->int_clr.val |= (fault_mask & 0x07) << 12;
 }
 
+static inline void mcpwm_ll_intr_clear_compare_status(mcpwm_dev_t *mcpwm, uint32_t operator_mask, uint32_t cmp_id)
+{
+    mcpwm->int_clr.val |= (operator_mask & 0x07) << (15 + cmp_id * 3);
+}
 
-STATIC_HAL_REG_CHECK(MCPWM, MCPWM_UP_COUNTER,        1);
-STATIC_HAL_REG_CHECK(MCPWM, MCPWM_DOWN_COUNTER,      2);
-STATIC_HAL_REG_CHECK(MCPWM, MCPWM_UP_DOWN_COUNTER,   3);
+static inline void mcpwm_ll_intr_clear_trip_cbc_status(mcpwm_dev_t *mcpwm, uint32_t cbc_mask)
+{
+    mcpwm->int_clr.val |= (cbc_mask & 0x07) << 21;
+}
 
-/**
- * Set the counting mode for the PWM timer.
- *
- * @param mcpwm Address of the MCPWM peripheral registers.
- * @param timer The timer to change counting mode, 0-2.
- * @param mode Counting mode to use.
- */
-static inline void mcpwm_ll_timer_set_count_mode(mcpwm_dev_t *mcpwm, int timer, mcpwm_counter_type_t mode)
+static inline void mcpwm_ll_intr_clear_trip_ost_status(mcpwm_dev_t *mcpwm, uint32_t ost_mask)
 {
-    mcpwm->timer[timer].mode.mode = mode;
+    mcpwm->int_clr.val |= (ost_mask & 0x07) << 24;
 }
 
-/**
- * Start a timer.
- *
- * @param mcpwm Address of the MCPWM peripheral registers.
- * @param timer The timer to start, 0-2.
- */
-static inline void mcpwm_ll_timer_start(mcpwm_dev_t *mcpwm, int timer)
+static inline void mcpwm_ll_intr_clear_capture_status(mcpwm_dev_t *mcpwm, uint32_t capture_mask)
 {
-    mcpwm->timer[timer].mode.start = 2;
+    mcpwm->int_clr.val |= (capture_mask & 0x07) << 27;
 }
 
-/**
- * Stop a timer.
- *
- * @param mcpwm Address of the MCPWM peripheral registers.
- * @param timer The timer to stop, 0-2.
- */
-static inline void mcpwm_ll_timer_stop(mcpwm_dev_t *mcpwm, int timer)
+//////////// enable interrupt for each event ////////////////
+
+static inline void mcpwm_ll_intr_enable_timer_stop(mcpwm_dev_t *mcpwm, uint32_t timer_id, bool enable)
 {
-    mcpwm->timer[timer].mode.start = 0;
+    if (enable) {
+        mcpwm->int_ena.val |= 1 << (timer_id + 0);
+    } else {
+        mcpwm->int_ena.val &= ~(1 << (timer_id + 0));
+    }
 }
 
-/**
- * Set the overflow period of a timer.
- *
- * The overflow rate will be Frequency of timer / period.
- *
- * @param mcpwm Address of the MCPWM peripheral registers.
- * @param timer The timer to set period, 0-2.
- * @param period Total timer count of each period, 0-65535.
- */
-static inline void mcpwm_ll_timer_set_period(mcpwm_dev_t *mcpwm, int timer, uint32_t period)
+static inline void mcpwm_ll_intr_enable_timer_tez(mcpwm_dev_t *mcpwm, uint32_t timer_id, bool enable)
 {
+    if (enable) {
+        mcpwm->int_ena.val |= 1 << (timer_id + 3);
+    } else {
+        mcpwm->int_ena.val &= ~(1 << (timer_id + 3));
+    }
+}
 
-    mcpwm->timer[timer].period.period = period - 1;
-    mcpwm->timer[timer].period.upmethod = 0;
+static inline void mcpwm_ll_intr_enable_timer_tep(mcpwm_dev_t *mcpwm, uint32_t timer_id, bool enable)
+{
+    if (enable) {
+        mcpwm->int_ena.val |= 1 << (timer_id + 6);
+    } else {
+        mcpwm->int_ena.val &= ~(1 << (timer_id + 6));
+    }
 }
 
-/**
- * Get the period setting of a timer.
- *
- * @param mcpwm Address of the MCPWM peripheral registers.
- * @param timer The timer to get the period, 0-2.
- * @return Period setting value.
- */
-static inline uint32_t mcpwm_ll_timer_get_period(mcpwm_dev_t *mcpwm, int timer)
+static inline void mcpwm_ll_intr_enable_fault(mcpwm_dev_t *mcpwm, uint32_t fault_id, bool enable)
 {
-    return mcpwm->timer[timer].period.period + 1;
+    if (enable) {
+        mcpwm->int_ena.val |= 1 << (9 + fault_id);  // enter fault interrupt
+        mcpwm->int_ena.val |= 1 << (12 + fault_id); // exit fault interrupt
+    } else {
+        mcpwm->int_ena.val &= ~(1 << (9 + fault_id));
+        mcpwm->int_ena.val &= ~(1 << (12 + fault_id));
+    }
 }
 
-/********************* Sync *******************/
-/**
- * Enable the synchronization feature for a timer.
- *
- * @param mcpwm Address of the MCPWM peripheral registers.
- * @param timer Timer to set, 0-2.
- * @param enable true to enable, otherwise false.
- */
-static inline void mcpwm_ll_sync_enable(mcpwm_dev_t *mcpwm, int timer, bool enable)
+static inline void mcpwm_ll_intr_enable_compare(mcpwm_dev_t *mcpwm, uint32_t operator_id, uint32_t cmp_id, bool enable)
 {
     if (enable) {
-        mcpwm->timer[timer].sync.out_sel = 0;
-        mcpwm->timer[timer].sync.in_en = 1;
+        mcpwm->int_ena.val |= (1 << (15 + cmp_id * 3 + operator_id));
     } else {
-        mcpwm->timer[timer].sync.in_en = 0;
-    }
-}
-
-/**
- * Set the phase (counter value) to reload when the sync signal triggers.
- *
- * @param mcpwm Address of the MCPWM peripheral registers.
- * @param timer Timer to set, 0-2.
- * @param reload_val The reloaded value.
- */
-static inline void mcpwm_ll_sync_set_phase(mcpwm_dev_t *mcpwm, int timer, uint32_t reload_val)
-{
-    mcpwm->timer[timer].sync.timer_phase = reload_val;
-}
-
-STATIC_HAL_REG_CHECK(MCPWM, MCPWM_SELECT_SYNC0, 4);
-STATIC_HAL_REG_CHECK(MCPWM, MCPWM_SELECT_SYNC1, 5);
-STATIC_HAL_REG_CHECK(MCPWM, MCPWM_SELECT_SYNC2, 6);
-/**
- * Set the sync signal source for a timer.
- *
- * @param mcpwm Address of the MCPWM peripheral registers.
- * @param timer The timer to set, 0-2.
- * @param sync_sig The synchronization signal to use.
- */
-static inline void mcpwm_ll_sync_set_input(mcpwm_dev_t *mcpwm, int timer, mcpwm_sync_signal_t sync_sig)
-{
-    if (timer == 0) {
-        mcpwm->timer_synci_cfg.t0_in_sel = sync_sig;
-    } else if (timer == 1) {
-        mcpwm->timer_synci_cfg.t1_in_sel = sync_sig;
-    } else {   //MCPWM_TIMER_2
-        mcpwm->timer_synci_cfg.t2_in_sel = sync_sig;
-    }
-}
-
-/********************* Comparator *******************/
-/**
- * Select a timer for the specified operator to use.
- *
- * @param mcpwm Address of the MCPWM peripheral registers.
- * @param op    The operator to choose timer, 0-2.
- * @param timer The timer to use, 0-2.
- */
-static inline void mcpwm_ll_operator_select_timer(mcpwm_dev_t *mcpwm, int op, int timer)
-{
-    if (op == 0) {
-        mcpwm->timer_sel.operator0_sel = timer;
-    } else if (op == 1) {
-        mcpwm->timer_sel.operator1_sel = timer;
+        mcpwm->int_ena.val &= ~(1 << (15 + cmp_id * 3 + operator_id));
+    }
+}
+
+static inline void mcpwm_ll_intr_enable_trip(mcpwm_dev_t *mcpwm, uint32_t operator_id, bool enable)
+{
+    if (enable) {
+        mcpwm->int_ena.val |= (1 << (21 + operator_id));
+        mcpwm->int_ena.val |= (1 << (24 + operator_id));
     } else {
-        mcpwm->timer_sel.operator2_sel = timer;
-    }
-}
-
-/**
- * Set the update method of the compare value of a timer
- *
- * @param mcpwm Address of the MCPWM peripheral registers.
- * @param op    Operator to set, 0-2.
- */
-static inline void mcpwm_ll_operator_set_compare_upmethod(mcpwm_dev_t *mcpwm, int op)
-{
-    mcpwm->channel[op].cmpr_cfg.a_upmethod = BIT(0);
-    mcpwm->channel[op].cmpr_cfg.b_upmethod = BIT(0);
-}
-
-/**
- * Get one of the compare value of a timer.
- *
- * @param mcpwm Address of the MCPWM peripheral registers.
- * @param op    The operator to get, 0-2.
- * @param cmp_n Comparer id to get, 0-1.
- * @return The set compare value.
- */
-static inline uint32_t mcpwm_ll_operator_get_compare(mcpwm_dev_t *mcpwm, int op, int cmp_n)
-{
-    return (mcpwm->channel[op].cmpr_value[cmp_n].cmpr_val);
-}
-
-/**
- * Set one of the compare value of a timer.
- *
- * @param mcpwm Address of the MCPWM peripheral registers.
- * @param op    The operator to set, 0-2.
- * @param cmp_n The comparer to set value, 0-1.
- * @param compare The compare value, 0-65535.
- */
-static inline void mcpwm_ll_operator_set_compare(mcpwm_dev_t *mcpwm, int op, int cmp_n, uint32_t compare)
-{
-    mcpwm->channel[op].cmpr_value[cmp_n].cmpr_val = compare;
-}
-
-/********************* Generator *******************/
-
-STATIC_HAL_REG_CHECK(MCPWM, MCPWM_ACTION_NO_CHANGE, 0);
-STATIC_HAL_REG_CHECK(MCPWM, MCPWM_ACTION_FORCE_LOW, 1);
-STATIC_HAL_REG_CHECK(MCPWM, MCPWM_ACTION_FORCE_HIGH, 2);
-STATIC_HAL_REG_CHECK(MCPWM, MCPWM_ACTION_TOGGLE,     3);
-/**
- * Set the action will be taken by a operator when its timer counts to zero.
- *
- * @param mcpwm Address of the MCPWM peripheral registers.
- * @param op     The operator to set action, 0-2.
- * @param gen    One generator of the operator to take the action, 0-1.
- * @param action Action to take.
- */
-static inline void mcpwm_ll_gen_set_zero_action(mcpwm_dev_t *mcpwm, int op, int gen, mcpwm_output_action_t action)
-{
-    mcpwm->channel[op].generator[gen].utez = action;
-    mcpwm->channel[op].generator[gen].dtez = action;
-}
-
-/**
- * Set the action will be taken by a operator when its timer counts to the period value.
- *
- * @param mcpwm Address of the MCPWM peripheral registers.
- * @param op     The operator to set action, 0-2.
- * @param gen    One generator of the operator to take the action, 0-1.
- * @param action Action to take.
- */
-static inline void mcpwm_ll_gen_set_period_action(mcpwm_dev_t *mcpwm, int op, int gen, mcpwm_output_action_t action)
-{
-    mcpwm->channel[op].generator[gen].utep = action;
-    mcpwm->channel[op].generator[gen].dtep = action;
-}
-
-/**
- * Set the action will be taken by a operator when its timer counts to the compare value.
- *
- * @param mcpwm Address of the MCPWM peripheral registers.
- * @param op        The operator to set action, 0-2.
- * @param gen       One generator of the operator to take the action, 0-1.
- * @param cmp_n     The comparer to use.
- * @param up_action The action to take when the counter is counting up.
- * @param down_action The action to take when the counter is counting down.
- */
-static inline void mcpwm_ll_gen_set_cmp_action(mcpwm_dev_t *mcpwm, int op, int gen,
-               int cmp_n, mcpwm_output_action_t up_action, mcpwm_output_action_t down_action)
-{
-    if (cmp_n == 0) {
-        mcpwm->channel[op].generator[gen].utea = up_action;
-        mcpwm->channel[op].generator[gen].dtea = down_action;
+        mcpwm->int_ena.val &= ~(1 << (21 + operator_id));
+        mcpwm->int_ena.val &= ~(1 << (24 + operator_id));
+    }
+}
+
+static inline void mcpwm_ll_intr_enable_capture(mcpwm_dev_t *mcpwm, uint32_t capture_id, bool enable)
+{
+    if (enable) {
+        mcpwm->int_ena.val |= 1 << (27 + capture_id);
     } else {
-        mcpwm->channel[op].generator[gen].uteb = up_action;
-        mcpwm->channel[op].generator[gen].dteb = down_action;
+        mcpwm->int_ena.val &= ~(1 << (27 + capture_id));
+    }
+}
+
+/********************* Timer registers *******************/
+
+static inline void mcpwm_ll_timer_set_clock(mcpwm_dev_t *mcpwm, int timer_id, unsigned long long group_clock, unsigned long long timer_clock)
+{
+    mcpwm->timer[timer_id].period.prescale = group_clock / timer_clock - 1;
+}
+
+static inline unsigned long long mcpwm_ll_timer_get_clock(mcpwm_dev_t *mcpwm, int timer_id, unsigned long long group_clock)
+{
+    return group_clock / (mcpwm->timer[timer_id].period.prescale + 1);
+}
+
+static inline void mcpwm_ll_timer_set_peak(mcpwm_dev_t *mcpwm, int timer_id, uint32_t peak, bool symmetric)
+{
+    if (!symmetric) { // in asymmetric mode, period = [0,peak-1]
+        mcpwm->timer[timer_id].period.period = peak - 1;
+    } else { // in symmetric mode, period = [0,peak-1] + [peak,1]
+        mcpwm->timer[timer_id].period.period = peak;
+    }
+}
+
+static inline uint32_t mcpwm_ll_timer_get_peak(mcpwm_dev_t *mcpwm, int timer_id, bool symmetric)
+{
+    // asymmetric mode
+    if (!symmetric) {
+        return mcpwm->timer[timer_id].period.period + 1;
     }
+    // symmetric mode
+    return mcpwm->timer[timer_id].period.period;
+}
+
+static inline void mcpwm_ll_timer_update_period_at_once(mcpwm_dev_t *mcpwm, int timer_id)
+{
+    mcpwm->timer[timer_id].period.upmethod = 0;
+}
+
+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].period.upmethod |= 0x01;
+    } else {
+        mcpwm->timer[timer_id].period.upmethod &= ~0x01;
+    }
+}
+
+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].period.upmethod |= 0x02;
+    } else {
+        mcpwm->timer[timer_id].period.upmethod &= ~0x02;
+    }
+}
+
+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].mode.mode = 0;
+        break;
+    case MCPWM_TIMER_COUNT_MODE_UP:
+        mcpwm->timer[timer_id].mode.mode = 1;
+        break;
+    case MCPWM_TIMER_COUNT_MODE_DOWN:
+        mcpwm->timer[timer_id].mode.mode = 2;
+        break;
+    case MCPWM_TIMER_COUNT_MODE_UP_DOWN:
+        mcpwm->timer[timer_id].mode.mode = 3;
+        break;
+    }
+}
+
+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].mode.mode) {
+    case 0:
+        return MCPWM_TIMER_COUNT_MODE_PAUSE;
+    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;
+    }
+}
+
+static inline void mcpwm_ll_timer_set_operate_command(mcpwm_dev_t *mcpwm, int timer_id, mcpwm_timer_operate_cmd_t mode)
+{
+    switch (mode) {
+    case MCPWM_TIMER_STOP_AT_ZERO:
+        mcpwm->timer[timer_id].mode.start = 0;
+        break;
+    case MCPWM_TIMER_STOP_AT_PEAK:
+        mcpwm->timer[timer_id].mode.start = 1;
+        break;
+    case MCPWM_TIMER_START_NO_STOP:
+        mcpwm->timer[timer_id].mode.start = 2;
+        break;
+    case MCPWM_TIMER_START_STOP_AT_ZERO:
+        mcpwm->timer[timer_id].mode.start = 3;
+        break;
+    case MCPWM_TIMER_START_STOP_AT_PEAK:
+        mcpwm->timer[timer_id].mode.start = 4;
+        break;
+    }
+}
+
+static inline void mcpwm_ll_timer_set_count_value(mcpwm_dev_t *mcpwm, int timer_id, uint32_t value)
+{
+    // we use software sync to set count value
+    int previous_phase = mcpwm->timer[timer_id].sync.timer_phase;
+    mcpwm->timer[timer_id].sync.timer_phase = value;
+    mcpwm->timer[timer_id].sync.sync_sw = ~mcpwm->timer[timer_id].sync.sync_sw;
+    mcpwm->timer[timer_id].sync.timer_phase = previous_phase;
+}
+
+static inline uint32_t mcpwm_ll_timer_get_count_value(mcpwm_dev_t *mcpwm, int timer_id)
+{
+    return mcpwm->timer[timer_id].status.value;
+}
+
+static inline bool mcpwm_ll_is_timer_decreasing(mcpwm_dev_t *mcpwm, int timer_id)
+{
+    return mcpwm->timer[timer_id].status.direction;
+}
+
+static inline void mcpwm_ll_timer_enable_sync_input(mcpwm_dev_t *mcpwm, int timer_id, bool enable)
+{
+    mcpwm->timer[timer_id].sync.in_en = enable;
+}
+
+static inline void mcpwm_ll_timer_sync_out_same_in(mcpwm_dev_t *mcpwm, int timer_id)
+{
+    mcpwm->timer[timer_id].sync.out_sel = 0;
+}
+
+static inline void mcpwm_ll_timer_sync_out_on_timer_event(mcpwm_dev_t *mcpwm, int timer_id, mcpwm_timer_event_t event)
+{
+    if (event == MCPWM_TIMER_EVENT_ZERO) {
+        mcpwm->timer[timer_id].sync.out_sel = 1;
+    } else if (event == MCPWM_TIMER_EVENT_PEAK) {
+        mcpwm->timer[timer_id].sync.out_sel = 2;
+    }
+}
+
+static inline void mcpwm_ll_timer_disable_sync_out(mcpwm_dev_t *mcpwm, int timer_id)
+{
+    mcpwm->timer[timer_id].sync.out_sel = 3;
+}
+
+static inline void mcpwm_ll_timer_trigger_sw_sync(mcpwm_dev_t *mcpwm, int timer_id)
+{
+    mcpwm->timer[timer_id].sync.sync_sw = ~mcpwm->timer[timer_id].sync.sync_sw;
+}
+
+static inline void mcpwm_ll_timer_set_sync_phase_value(mcpwm_dev_t *mcpwm, int timer_id, uint32_t reload_val)
+{
+    mcpwm->timer[timer_id].sync.timer_phase = reload_val;
+}
+
+static inline uint32_t mcpwm_ll_timer_get_sync_phase_value(mcpwm_dev_t *mcpwm, int timer_id)
+{
+    return mcpwm->timer[timer_id].sync.timer_phase;
+}
+
+static inline void mcpwm_ll_timer_set_sync_phase_direction(mcpwm_dev_t *mcpwm, int timer_id, bool decrease)
+{
+    mcpwm->timer[timer_id].sync.phase_direct = decrease;
+}
+
+static inline void mcpwm_ll_timer_enable_sync_from_internal_timer(mcpwm_dev_t *mcpwm, int this_timer, int internal_sync_timer)
+{
+    mcpwm->timer_synci_cfg.val &= ~(0x07 << (this_timer * 3));
+    mcpwm->timer_synci_cfg.val |= (internal_sync_timer + 1) << (this_timer * 3);
+}
+
+static inline void mcpwm_ll_timer_enable_sync_from_external(mcpwm_dev_t *mcpwm, int this_timer, int extern_syncer)
+{
+    mcpwm->timer_synci_cfg.val &= ~(0x07 << (this_timer * 3));
+    mcpwm->timer_synci_cfg.val |= (extern_syncer + 4) << (this_timer * 3);
+}
+
+static inline void mcpwm_ll_invert_external_syncer(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));
+    }
+}
+
+/********************* Operator registers *******************/
+
+static inline void mcpwm_ll_operator_flush_shadow(mcpwm_dev_t *mcpwm, int operator_id)
+{
+    mcpwm->update_cfg.val ^= (1 << (2 * operator_id + 3));
+}
+
+static inline void mcpwm_ll_operator_select_timer(mcpwm_dev_t *mcpwm, int operator_id, int timer_id)
+{
+    if (operator_id == 0) {
+        mcpwm->timer_sel.operator0_sel = timer_id;
+    } else if (operator_id == 1) {
+        mcpwm->timer_sel.operator1_sel = timer_id;
+    } else {
+        mcpwm->timer_sel.operator2_sel = timer_id;
+    }
+}
+
+static inline void mcpwm_ll_operator_update_compare_at_once(mcpwm_dev_t *mcpwm, int operator_id, int compare_id)
+{
+    mcpwm->channel[operator_id].cmpr_cfg.val &= ~(0x0F << (4 * compare_id));
+}
+
+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->channel[operator_id].cmpr_cfg.val |= (1 << 0) << (4 * compare_id);
+    } else {
+        mcpwm->channel[operator_id].cmpr_cfg.val &= ~((1 << 0) << (4 * compare_id));
+    }
+}
+
+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->channel[operator_id].cmpr_cfg.val |= (1 << 1) << (4 * compare_id);
+    } else {
+        mcpwm->channel[operator_id].cmpr_cfg.val &= ~((1 << 1) << (4 * compare_id));
+    }
+}
+
+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->channel[operator_id].cmpr_cfg.val |= (1 << 2) << (4 * compare_id);
+    } else {
+        mcpwm->channel[operator_id].cmpr_cfg.val &= ~((1 << 2) << (4 * compare_id));
+    }
+}
+
+static inline void mcpwm_ll_operator_set_compare_value(mcpwm_dev_t *mcpwm, int operator_id, int compare_id, uint32_t compare_value)
+{
+    mcpwm->channel[operator_id].cmpr_value[compare_id].cmpr_val = compare_value;
+}
+
+static inline uint32_t mcpwm_ll_operator_get_compare_value(mcpwm_dev_t *mcpwm, int operator_id, int compare_id)
+{
+    return mcpwm->channel[operator_id].cmpr_value[compare_id].cmpr_val;
+}
+
+static inline void mcpwm_ll_operator_update_action_at_once(mcpwm_dev_t *mcpwm, int operator_id)
+{
+    mcpwm->channel[operator_id].gen_cfg0.upmethod = 0;
 }
 
-/********************* Fault *******************/
-/**
- * Enable the fault detection feature for an input signal.
- *
- * @param mcpwm Address of the MCPWM peripheral registers.
- * @param fault_sig One of the signals to select, 0-2.
- * @param level The active level of the fault-detection signal.
- */
-static inline void mcpwm_ll_fault_enable(mcpwm_dev_t *mcpwm, int fault_sig, bool level)
+static inline void mcpwm_ll_operator_enable_update_action_on_tez(mcpwm_dev_t *mcpwm, int operator_id, bool enable)
+{
+    if (enable) {
+        mcpwm->channel[operator_id].gen_cfg0.upmethod |= 1 << 0;
+    } else {
+        mcpwm->channel[operator_id].gen_cfg0.upmethod &= ~(1 << 0);
+    }
+}
+
+static inline void mcpwm_ll_operator_enable_update_action_on_tep(mcpwm_dev_t *mcpwm, int operator_id, bool enable)
+{
+    if (enable) {
+        mcpwm->channel[operator_id].gen_cfg0.upmethod |= 1 << 1;
+    } else {
+        mcpwm->channel[operator_id].gen_cfg0.upmethod &= ~(1 << 1);
+    }
+}
+
+static inline void mcpwm_ll_operator_enable_update_action_on_sync(mcpwm_dev_t *mcpwm, int operator_id, bool enable)
+{
+    if (enable) {
+        mcpwm->channel[operator_id].gen_cfg0.upmethod |= 1 << 2;
+    } else {
+        mcpwm->channel[operator_id].gen_cfg0.upmethod &= ~(1 << 2);
+    }
+}
+
+/********************* Generator registers *******************/
+
+static inline void mcpwm_ll_generator_reset_actions(mcpwm_dev_t *mcpwm, int operator_id, int generator_id)
+{
+    mcpwm->channel[operator_id].generator[generator_id].val = 0;
+}
+
+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)
+{
+    if (direction == MCPWM_TIMER_DIRECTION_UP) { // utez, utep
+        mcpwm->channel[operator_id].generator[generator_id].val &= ~(0x03 << (event * 2));
+        mcpwm->channel[operator_id].generator[generator_id].val |= action << (event * 2);
+    } else if (direction == MCPWM_TIMER_DIRECTION_DOWN) { // dtez, dtep
+        mcpwm->channel[operator_id].generator[generator_id].val &= ~(0x03 << (event * 2 + 12));
+        mcpwm->channel[operator_id].generator[generator_id].val |= action << (event * 2 + 12);
+    }
+}
+
+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->channel[operator_id].generator[generator_id].val &= ~(0x03 << (cmp_id * 2 + 4));
+        mcpwm->channel[operator_id].generator[generator_id].val |= action << (cmp_id * 2 + 4);
+    } else if (direction == MCPWM_TIMER_DIRECTION_DOWN) { // dtea, dteb
+        mcpwm->channel[operator_id].generator[generator_id].val &= ~(0x03 << (cmp_id * 2 + 16));
+        mcpwm->channel[operator_id].generator[generator_id].val |= action << (cmp_id * 2 + 16);
+    }
+}
+
+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->channel[operator_id].generator[generator_id].val &= ~(0x03 << (trig_id * 2 + 8));
+        mcpwm->channel[operator_id].generator[generator_id].val |= action << (trig_id * 2 + 8);
+    } else if (direction == MCPWM_TIMER_DIRECTION_DOWN) { // dt0, dt1
+        mcpwm->channel[operator_id].generator[generator_id].val &= ~(0x03 << (trig_id * 2 + 20));
+        mcpwm->channel[operator_id].generator[generator_id].val |= action << (trig_id * 2 + 20);
+    }
+}
+
+static inline void mcpwm_ll_gen_set_onetime_force_action(mcpwm_dev_t *mcpwm, int operator_id, int generator_id, int action)
+{
+    if (generator_id == 0) {
+        mcpwm->channel[operator_id].gen_force.a_nciforce_mode = action;
+        mcpwm->channel[operator_id].gen_force.a_nciforce = ~mcpwm->channel[operator_id].gen_force.a_nciforce;
+    } else {
+        mcpwm->channel[operator_id].gen_force.b_nciforce_mode = action;
+        mcpwm->channel[operator_id].gen_force.b_nciforce = ~mcpwm->channel[operator_id].gen_force.b_nciforce;
+    }
+}
+
+static inline void mcpwm_ll_gen_set_continuous_force_action(mcpwm_dev_t *mcpwm, int operator_id, int generator_id, int action)
+{
+    mcpwm->channel[operator_id].gen_force.cntu_force_upmethod = 0; // force action immediately
+    if (generator_id == 0) {
+        mcpwm->channel[operator_id].gen_force.a_cntuforce_mode = action;
+    } else {
+        mcpwm->channel[operator_id].gen_force.b_cntuforce_mode = action;
+    }
+}
+
+/********************* Dead time registers *******************/
+
+static inline void mcpwm_ll_deadtime_set_resolution_same_to_timer(mcpwm_dev_t *mcpwm, int operator_id, bool same)
+{
+    mcpwm->channel[operator_id].db_cfg.clk_sel = same;
+}
+
+static inline void mcpwm_ll_deadtime_red_select_generator(mcpwm_dev_t *mcpwm, int operator_id, int generator)
+{
+    mcpwm->channel[operator_id].db_cfg.red_insel = generator;
+}
+
+static inline void mcpwm_ll_deadtime_fed_select_generator(mcpwm_dev_t *mcpwm, int operator_id, int generator)
+{
+    mcpwm->channel[operator_id].db_cfg.fed_insel = generator;
+}
+
+static inline void mcpwm_ll_deadtime_bypass_path(mcpwm_dev_t *mcpwm, int operator_id, int path, bool bypass)
+{
+    if (bypass) {
+        mcpwm->channel[operator_id].db_cfg.val |= 1 << (path + 15);
+    } else {
+        mcpwm->channel[operator_id].db_cfg.val &= ~(1 << (path + 15));
+    }
+}
+
+static inline void mcpwm_ll_deadtime_invert_outpath(mcpwm_dev_t *mcpwm, int operator_id, int path, bool invert)
+{
+    if (invert) {
+        mcpwm->channel[operator_id].db_cfg.val |= 1 << (path + 13);
+    } else {
+        mcpwm->channel[operator_id].db_cfg.val &= ~(1 << (path + 13));
+    }
+}
+
+static inline void mcpwm_ll_deadtime_swap_out_path(mcpwm_dev_t *mcpwm, int operator_id, int path, bool swap)
+{
+    if (swap) {
+        mcpwm->channel[operator_id].db_cfg.val |= 1 << (path + 9);
+    } else {
+        mcpwm->channel[operator_id].db_cfg.val &= ~(1 << (path + 9));
+    }
+}
+
+static inline void mcpwm_ll_deadtime_enable_deb(mcpwm_dev_t *mcpwm, int operator_id, bool enable)
+{
+    mcpwm->channel[operator_id].db_cfg.deb_mode = enable;
+}
+
+static inline uint32_t mcpwm_ll_deadtime_get_topology_code(mcpwm_dev_t *mcpwm, int operator_id)
+{
+    return (mcpwm->channel[operator_id].db_cfg.deb_mode << 8) | (mcpwm->channel[operator_id].db_cfg.b_outswap << 7) |
+           (mcpwm->channel[operator_id].db_cfg.a_outswap << 6) | (mcpwm->channel[operator_id].db_cfg.fed_insel << 5) |
+           (mcpwm->channel[operator_id].db_cfg.red_insel << 4) | (mcpwm->channel[operator_id].db_cfg.fed_outinvert << 3) |
+           (mcpwm->channel[operator_id].db_cfg.red_outinvert << 2) | (mcpwm->channel[operator_id].db_cfg.a_outbypass << 1) |
+           (mcpwm->channel[operator_id].db_cfg.b_outbypass << 0);
+}
+
+static inline void mcpwm_ll_deadtime_set_falling_delay(mcpwm_dev_t *mcpwm, int operator_id, uint32_t fed)
+{
+    mcpwm->channel[operator_id].db_fed_cfg.fed = fed - 1;
+}
+
+static inline uint32_t mcpwm_ll_deadtime_get_falling_delay(mcpwm_dev_t *mcpwm, int operator_id)
+{
+    return mcpwm->channel[operator_id].db_fed_cfg.fed + 1;
+}
+
+static inline void mcpwm_ll_deadtime_set_rising_delay(mcpwm_dev_t *mcpwm, int operator_id, uint32_t red)
+{
+    mcpwm->channel[operator_id].db_red_cfg.red = red - 1;
+}
+
+static inline uint32_t mcpwm_ll_deadtime_get_rising_delay(mcpwm_dev_t *mcpwm, int operator_id)
+{
+    return mcpwm->channel[operator_id].db_red_cfg.red + 1;
+}
+
+static inline void mcpwm_ll_deadtime_update_delay_at_once(mcpwm_dev_t *mcpwm, int operator_id)
+{
+    mcpwm->channel[operator_id].db_cfg.fed_upmethod = 0;
+    mcpwm->channel[operator_id].db_cfg.red_upmethod = 0;
+}
+
+static inline void mcpwm_ll_deadtime_enable_update_delay_on_tez(mcpwm_dev_t *mcpwm, int operator_id, bool enable)
+{
+    if (enable) {
+        mcpwm->channel[operator_id].db_cfg.fed_upmethod |= 1 << 0;
+        mcpwm->channel[operator_id].db_cfg.red_upmethod |= 1 << 0;
+    } else {
+        mcpwm->channel[operator_id].db_cfg.fed_upmethod &= ~(1 << 0);
+        mcpwm->channel[operator_id].db_cfg.red_upmethod &= ~(1 << 0);
+    }
+}
+
+static inline void mcpwm_ll_deadtime_enable_update_delay_on_tep(mcpwm_dev_t *mcpwm, int operator_id, bool enable)
+{
+    if (enable) {
+        mcpwm->channel[operator_id].db_cfg.fed_upmethod |= 1 << 1;
+        mcpwm->channel[operator_id].db_cfg.red_upmethod |= 1 << 1;
+    } else {
+        mcpwm->channel[operator_id].db_cfg.fed_upmethod &= ~(1 << 1);
+        mcpwm->channel[operator_id].db_cfg.red_upmethod &= ~(1 << 1);
+    }
+}
+
+static inline void mcpwm_ll_deadtime_enable_update_delay_on_sync(mcpwm_dev_t *mcpwm, int operator_id, bool enable)
+{
+    if (enable) {
+        mcpwm->channel[operator_id].db_cfg.fed_upmethod |= 1 << 2;
+        mcpwm->channel[operator_id].db_cfg.red_upmethod |= 1 << 2;
+    } else {
+        mcpwm->channel[operator_id].db_cfg.fed_upmethod &= ~(1 << 2);
+        mcpwm->channel[operator_id].db_cfg.red_upmethod &= ~(1 << 2);
+    }
+}
+
+/********************* Carrier registers *******************/
+
+static inline void mcpwm_ll_carrier_enable(mcpwm_dev_t *mcpwm, int operator_id, bool enable)
+{
+    mcpwm->channel[operator_id].carrier_cfg.en = enable;
+}
+
+static inline void mcpwm_ll_carrier_set_prescale(mcpwm_dev_t *mcpwm, int operator_id, uint8_t prescale)
+{
+    mcpwm->channel[operator_id].carrier_cfg.prescale = prescale - 1;
+}
+
+static inline uint8_t mcpwm_ll_carrier_get_prescale(mcpwm_dev_t *mcpwm, int operator_id)
+{
+    return mcpwm->channel[operator_id].carrier_cfg.prescale + 1;
+}
+
+static inline void mcpwm_ll_carrier_set_duty(mcpwm_dev_t *mcpwm, int operator_id, uint8_t carrier_duty)
+{
+    mcpwm->channel[operator_id].carrier_cfg.duty = carrier_duty;
+}
+
+static inline uint8_t mcpwm_ll_carrier_get_duty(mcpwm_dev_t *mcpwm, int operator_id)
+{
+    return mcpwm->channel[operator_id].carrier_cfg.duty;
+}
+
+static inline void mcpwm_ll_carrier_out_invert(mcpwm_dev_t *mcpwm, int operator_id, bool invert)
+{
+    mcpwm->channel[operator_id].carrier_cfg.out_invert = invert;
+}
+
+static inline void mcpwm_ll_carrier_in_invert(mcpwm_dev_t *mcpwm, int operator_id, bool invert)
+{
+    mcpwm->channel[operator_id].carrier_cfg.in_invert = invert;
+}
+
+static inline void mcpwm_ll_carrier_set_oneshot_width(mcpwm_dev_t *mcpwm, int operator_id, uint8_t pulse_width)
+{
+    mcpwm->channel[operator_id].carrier_cfg.oshtwth = pulse_width - 1;
+}
+
+static inline uint8_t mcpwm_ll_carrier_get_oneshot_width(mcpwm_dev_t *mcpwm, int operator_id)
+{
+    return mcpwm->channel[operator_id].carrier_cfg.oshtwth + 1;
+}
+
+/********************* Fault detector registers *******************/
+
+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);
+    }
+}
+
+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));
+    }
+}
+
+static inline void mcpwm_ll_fault_clear_ost(mcpwm_dev_t *mcpwm, int operator_id)
+{
+    // a posedge can clear the ost fault status
+    mcpwm->channel[operator_id].tz_cfg1.clr_ost = 0;
+    mcpwm->channel[operator_id].tz_cfg1.clr_ost = 1;
+}
+
+static inline void mcpwm_ll_fault_enable_oneshot_mode(mcpwm_dev_t *mcpwm, int operator_id, int fault_sig, bool enable)
 {
     if (fault_sig == 0) {
-        mcpwm->fault_detect.f0_en = 1;
-        mcpwm->fault_detect.f0_pole = level;
+        mcpwm->channel[operator_id].tz_cfg0.f0_ost = enable;
     } else if (fault_sig == 1) {
-        mcpwm->fault_detect.f1_en = 1;
-        mcpwm->fault_detect.f1_pole = level;
-    } else {   //MCPWM_SELECT_F2
-        mcpwm->fault_detect.f2_en = 1;
-        mcpwm->fault_detect.f2_pole = level;
+        mcpwm->channel[operator_id].tz_cfg0.f1_ost = enable;
+    } else {
+        mcpwm->channel[operator_id].tz_cfg0.f2_ost = enable;
     }
 }
 
-/**
- * Disable the fault detection of an input signal.
- * @param mcpwm Address of the MCPWM peripheral registers.
- * @param fault_sig The signal to disable, 0-2.
- */
-static inline void mcpwm_ll_fault_disable(mcpwm_dev_t *mcpwm, int fault_sig)
+static inline void mcpwm_ll_fault_enable_cbc_mode(mcpwm_dev_t *mcpwm, int operator_id, int fault_sig, bool enable)
 {
     if (fault_sig == 0) {
-        mcpwm->fault_detect.f0_en = 0;
+        mcpwm->channel[operator_id].tz_cfg0.f0_cbc = enable;
     } else if (fault_sig == 1) {
-        mcpwm->fault_detect.f1_en = 0;
-    } else {   //MCPWM_SELECT_F2
-        mcpwm->fault_detect.f2_en = 0;
-    }
-}
-
-/**
- * Clear the oneshot fault status of an operator.
- *
- * @param mcpwm Address of the MCPWM peripheral registers.
- * @param op The operator to clear, 0-2.
- */
-static inline void mcpwm_ll_fault_clear_ost(mcpwm_dev_t *mcpwm, int op)
-{
-    mcpwm->channel[op].tz_cfg1.clr_ost = 1;
-    mcpwm->channel[op].tz_cfg1.clr_ost = 0;
-}
-
-/**
- * Use the oneshot mode to handle the fault when it occurs
- *
- * @param mcpwm Address of the MCPWM peripheral registers.
- * @param op     The operator to handle the fault signal, 0-2.
- * @param signal The fault signal to set, 0-2.
- * @param enable true to enable oneshot, otherwise false.
- */
-static inline void mcpwm_ll_fault_oneshot_enable_signal(mcpwm_dev_t *mcpwm, int op, int signal, bool enable)
-{
-    if (signal == 0) {
-        mcpwm->channel[op].tz_cfg0.f0_ost = enable;
-    } else if (signal == 1) {
-        mcpwm->channel[op].tz_cfg0.f1_ost = enable;
-    } else {   //MCPWM_SELECT_F2
-        mcpwm->channel[op].tz_cfg0.f2_ost = enable;
-    }
-}
-
-/**
- * @brief Get the oneshot enabled status of the operator
- *
- * @param mcpwm Address of the MCPWM peripheral registers.
- * @param op     The operator to check, 0-2.
- * @param signal The fault signal to get, 0-2.
- */
-static inline bool mcpwm_ll_fault_oneshot_signal_enabled(mcpwm_dev_t *mcpwm, int op, int signal)
-{
-    if (signal == 0) {
-        return mcpwm->channel[op].tz_cfg0.f0_ost;
-    } else if (signal == 1) {
-        return mcpwm->channel[op].tz_cfg0.f1_ost;
-    } else {   //MCPWM_SELECT_F2
-        return mcpwm->channel[op].tz_cfg0.f2_ost;
-    }
-}
-
-/**
- * Use the CBC (cycle-by-cycle) mode to handle the fault when it occurs.
- *
- * @param mcpwm Address of the MCPWM peripheral registers.
- * @param op     The operator to handle the fault signal, 0-2.
- * @param signal The fault signal to set, 0-2.
- * @param enable true to enable cbc mode, otherwise false.
- */
-static inline void mcpwm_ll_fault_cbc_enable_signal(mcpwm_dev_t *mcpwm, int op, int signal, bool enable)
-{
-    if (signal == 0) {
-        mcpwm->channel[op].tz_cfg0.f0_cbc = enable;
-    } else if (signal == 1) {
-        mcpwm->channel[op].tz_cfg0.f1_cbc = enable;
-    } else {   //MCPWM_SELECT_F2
-        mcpwm->channel[op].tz_cfg0.f2_cbc = enable;
-    }
-}
-
-/**
- * Set the action that will be taken when the fault is handled by oneshot.
- *
- * @param mcpwm Address of the MCPWM peripheral registers.
- * @param op     The operator to handle the fault signal, 0-2.
- * @param gen    The generator to take the action, 0-1.
- * @param up_action     Action to take when fault happens when counting up.
- * @param down_action   Action to take when fault happens when counting down.
- */
-static inline void mcpwm_ll_fault_set_oneshot_action(mcpwm_dev_t *mcpwm, int op, int gen,
-        mcpwm_output_action_t up_action, mcpwm_output_action_t down_action)
-{
-    if (gen == 0) {
-        mcpwm->channel[op].tz_cfg0.a_ost_u = up_action;
-        mcpwm->channel[op].tz_cfg0.a_ost_d = down_action;
+        mcpwm->channel[operator_id].tz_cfg0.f1_cbc = enable;
     } else {
-        mcpwm->channel[op].tz_cfg0.b_ost_u = up_action;
-        mcpwm->channel[op].tz_cfg0.b_ost_d = down_action;
-    }
-}
-
-/**
- * Set the action that will be taken when the fault is handled cycle by cycle.
- *
- * @param mcpwm Address of the MCPWM peripheral registers.
- * @param op     The operator to handle the fault signal, 0-2.
- * @param gen    The generator to take the action, 0-1.
- * @param up_action     Action to take when fault happens when counting up.
- * @param down_action   Action to take when fault happens when counting down.
- */
-static inline void mcpwm_ll_fault_set_cyc_action(mcpwm_dev_t *mcpwm, int op, int gen,
-                  mcpwm_output_action_t up_action, mcpwm_output_action_t down_action)
-{
-    mcpwm->channel[op].tz_cfg1.cbcpulse = BIT(0);    //immediately
-    if (gen == 0) {
-        mcpwm->channel[op].tz_cfg0.a_cbc_u = up_action;
-        mcpwm->channel[op].tz_cfg0.a_cbc_d = down_action;
-    } else {
-        mcpwm->channel[op].tz_cfg0.b_cbc_u = up_action;
-        mcpwm->channel[op].tz_cfg0.b_cbc_d = down_action;
-    }
-}
-
-/********************* Dead Zone (deadtime) *******************/
-/**
- * Initialize the dead zone feature.
- *
- * @param mcpwm Address of the MCPWM peripheral registers.
- * @param op The operator to initialize, 0-2.
- */
-static inline void mcpwm_ll_deadtime_init(mcpwm_dev_t *mcpwm, int op)
-{
-    mcpwm->channel[op].db_cfg.fed_upmethod = BIT(0);
-    mcpwm->channel[op].db_cfg.red_upmethod = BIT(0);
-    mcpwm->channel[op].db_cfg.clk_sel = 0;
-}
-
-/**
- * Set the output dead zone mode applying to the outputs of a timer.
- *
- * If the desired internal connection is not provided, you can write your own inside this function.
- *
- * @param mcpwm Address of the MCPWM peripheral registers.
- * @param op    The operator to set, 0-2.
- * @param mode  Dead zone mode to use.
- */
-static inline void mcpwm_ll_set_deadtime_mode(mcpwm_dev_t *mcpwm,
-    int op, mcpwm_deadtime_type_t mode)
-{
-#define MCPWM_LL_DEADTIME_REG_MASK (MCPWM_DT0_DEB_MODE_M | MCPWM_DT0_A_OUTSWAP_M | MCPWM_DT0_B_OUTSWAP_M | \
-    MCPWM_DT0_RED_INSEL_M | MCPWM_DT0_FED_INSEL_M | MCPWM_DT0_RED_OUTINVERT_M | MCPWM_DT0_FED_OUTINVERT_M | \
-    MCPWM_DT0_A_OUTBYPASS_M | MCPWM_DT0_B_OUTBYPASS_M)
-
-    static uint32_t deadtime_mode_settings[MCPWM_DEADTIME_TYPE_MAX] = {
-        [MCPWM_BYPASS_RED] =                    0b010010000 << MCPWM_DT0_DEB_MODE_S,
-        [MCPWM_BYPASS_FED] =                    0b100000000 << MCPWM_DT0_DEB_MODE_S,
-        [MCPWM_ACTIVE_HIGH_MODE] =              0b000010000 << MCPWM_DT0_DEB_MODE_S,
-        [MCPWM_ACTIVE_LOW_MODE] =               0b001110000 << MCPWM_DT0_DEB_MODE_S,
-        [MCPWM_ACTIVE_HIGH_COMPLIMENT_MODE] =   0b001010000 << MCPWM_DT0_DEB_MODE_S,
-        [MCPWM_ACTIVE_LOW_COMPLIMENT_MODE] =    0b000101000 << MCPWM_DT0_DEB_MODE_S,
-        [MCPWM_ACTIVE_RED_FED_FROM_PWMXA] =     0b000000011 << MCPWM_DT0_DEB_MODE_S,
-        [MCPWM_ACTIVE_RED_FED_FROM_PWMXB] =     0b000001011 << MCPWM_DT0_DEB_MODE_S,
-        [MCPWM_DEADTIME_BYPASS] =               0b110000000 << MCPWM_DT0_DEB_MODE_S,
-    };
-    mcpwm->channel[op].db_cfg.val =
-        (mcpwm->channel[op].db_cfg.val & (~MCPWM_LL_DEADTIME_REG_MASK)) | deadtime_mode_settings[mode];
-
-#undef MCPWM_LL_DEADTIME_REG_MASK
-}
-
-/**
- * Set the delay of the falling edge on the output.
- *
- * @param mcpwm Address of the MCPWM peripheral registers.
- * @param op    The operator to set, 0-2.
- * @param fed   Falling delay, by PWM main clock.
- */
-static inline void mcpwm_ll_deadtime_set_falling_delay(mcpwm_dev_t *mcpwm, int op, uint32_t fed)
-{
-    mcpwm->channel[op].db_fed_cfg.fed = fed;
-}
-
-/**
- * Set the delay of the rising edge on the output.
- *
- * @param mcpwm Address of the MCPWM peripheral registers.
- * @param op    The operator to set, 0-2.
- * @param fed   Rising delay, by PWM main clock.
- */
-static inline void mcpwm_ll_deadtime_set_rising_delay(mcpwm_dev_t *mcpwm, int op, uint32_t red)
-{
-    mcpwm->channel[op].db_red_cfg.red = red;
-}
-
-/**
- * Disable (bypass) the dead zone feature.
- *
- * @param mcpwm Address of the MCPWM peripheral registers.
- * @param op The operator to set, 0-2.
- */
-static inline void mcpwm_ll_deadtime_bypass(mcpwm_dev_t *mcpwm, int op)
-{
-    mcpwm_ll_set_deadtime_mode(mcpwm, op, MCPWM_DEADTIME_BYPASS);
-}
-
-/********************* Carrier *******************/
-/**
- * Initialize the carrier feature.
- *
- * @param mcpwm Address of the MCPWM peripheral registers.
- * @param op The operator to set, 0-2.
- */
-static inline void mcpwm_ll_carrier_init(mcpwm_dev_t *mcpwm, int op)
-{
-    mcpwm->channel[op].carrier_cfg.in_invert = 0;
-}
-
-/**
- * Enable the carrier feature for a timer.
- *
- * @param mcpwm Address of the MCPWM peripheral registers.
- * @param op The operator to set, 0-2.
- * @param enable true to enable, otherwise false.
- */
-static inline void mcpwm_ll_carrier_enable(mcpwm_dev_t *mcpwm, int op, bool enable)
-{
-    mcpwm->channel[op].carrier_cfg.en = enable;
-}
-
-/**
- * Set the prescale of the carrier timer.
- *
- * The carrier period will be Frequency of PWM main clock/(carrier_period+1).
- *
- * @param mcpwm Address of the MCPWM peripheral registers.
- * @param op The operator to set, 0-2.
- * @param carrier_period The prescale of the carrier clock, 0-15.
- */
-static inline void mcpwm_ll_carrier_set_prescale(mcpwm_dev_t *mcpwm, int op, uint8_t carrier_period)
-{
-    mcpwm->channel[op].carrier_cfg.prescale = carrier_period;
-}
-
-/**
- * Set the duty rate of the carrier.
- *
- * @param mcpwm Address of the MCPWM peripheral registers.
- * @param op The operator to set, 0-2.
- * @param carrier_duty Duty rate will be (carrier_duty/8)*100%. 0-7.
- */
-static inline void mcpwm_ll_carrier_set_duty(mcpwm_dev_t *mcpwm, int op, uint8_t carrier_duty)
-{
-    mcpwm->channel[op].carrier_cfg.duty = carrier_duty;
-}
-
-/**
- * Invert output of the carrier.
- *
- * @param mcpwm Address of the MCPWM peripheral registers.
- * @param op The operator to set, 0-2.
- * @param invert true to invert, otherwise false.
- */
-static inline void mcpwm_ll_carrier_out_invert(mcpwm_dev_t *mcpwm, int op, bool invert)
+        mcpwm->channel[operator_id].tz_cfg0.f2_cbc = enable;
+    }
+    mcpwm->channel[operator_id].tz_cfg1.cbcpulse = 1 << 0;
+}
+
+static inline void mcpwm_ll_fault_enable_sw_cbc(mcpwm_dev_t *mcpwm, int operator_id, bool enable)
+{
+    mcpwm->channel[operator_id].tz_cfg0.sw_cbc = enable;
+}
+
+static inline void mcpwm_ll_fault_enable_sw_oneshot(mcpwm_dev_t *mcpwm, int operator_id, bool enable)
+{
+    mcpwm->channel[operator_id].tz_cfg0.sw_ost = enable;
+}
+
+static inline void mcpwm_ll_fault_trigger_sw_cbc(mcpwm_dev_t *mcpwm, int operator_id)
+{
+    mcpwm->channel[operator_id].tz_cfg1.force_cbc = ~mcpwm->channel[operator_id].tz_cfg1.force_cbc;
+}
+
+static inline void mcpwm_ll_fault_trigger_sw_oneshot(mcpwm_dev_t *mcpwm, int operator_id)
+{
+    mcpwm->channel[operator_id].tz_cfg1.force_ost = ~mcpwm->channel[operator_id].tz_cfg1.force_ost;
+}
+
+static inline void mcpwm_ll_generator_set_action_on_fault_event(mcpwm_dev_t *mcpwm, int operator_id, int generator_id,
+        mcpwm_timer_direction_t direction, mcpwm_fault_reaction_t reaction, int action)
 {
-    mcpwm->channel[op].carrier_cfg.out_invert = invert;
+    if (direction == MCPWM_TIMER_DIRECTION_UP) {
+        mcpwm->channel[operator_id].tz_cfg0.val &= ~(0x03 << (8 + 8 * generator_id + 4 * reaction + 2));
+        mcpwm->channel[operator_id].tz_cfg0.val |= action << (8 + 8 * generator_id + 4 * reaction + 2);
+    } else if (direction == MCPWM_TIMER_DIRECTION_DOWN) {
+        mcpwm->channel[operator_id].tz_cfg0.val &= ~(0x03 << (8 + 8 * generator_id + 4 * reaction));
+        mcpwm->channel[operator_id].tz_cfg0.val |= action << (8 + 8 * generator_id + 4 * reaction);
+    }
 }
 
-/**
- * Set the oneshot pulse width of the carrier.
- *
- * @param mcpwm Address of the MCPWM peripheral registers.
- * @param op The operator to set, 0-2.
- * @param pulse_width The width of the oneshot pulse, by carrier period. 0 to disable the oneshot pulse.
- */
-static inline void mcpwm_ll_carrier_set_oneshot_width(mcpwm_dev_t *mcpwm, int op, uint8_t pulse_width)
+static inline bool mcpwm_ll_fault_is_ost_on(mcpwm_dev_t *mcpwm, int op)
 {
-    mcpwm->channel[op].carrier_cfg.oshtwth = pulse_width;
+    return mcpwm->channel[op].tz_status.ost_on;
 }
 
-/********************* Capture *******************/
-/**
- * Enable the capture feature for a signal
- *
- * @param mcpwm Address of the MCPWM peripheral registers.
- * @param cap_sig Signal to enable, 0-2.
- * @param enable true to enable, otherwise false.
- */
-static inline void mcpwm_ll_capture_enable(mcpwm_dev_t *mcpwm, int cap_sig, int enable)
+static inline bool mcpwm_ll_fault_is_cbc_on(mcpwm_dev_t *mcpwm, int op)
+{
+    return mcpwm->channel[op].tz_status.cbc_on;
+}
+
+/********************* Capture registers *******************/
+
+static inline void mcpwm_ll_capture_enable_timer(mcpwm_dev_t *mcpwm, bool enable)
+{
+    mcpwm->cap_timer_cfg.timer_en = enable;
+}
+
+static inline void mcpwm_ll_capture_enable_channel(mcpwm_dev_t *mcpwm, int channel, bool enable)
+{
+    mcpwm->cap_cfg_ch[channel].en = enable;
+}
+
+static inline void mcpwm_ll_capture_set_sync_phase(mcpwm_dev_t *mcpwm, uint32_t phase_value)
+{
+    mcpwm->cap_timer_phase = phase_value;
+}
+
+static inline uint32_t mcpwm_ll_capture_get_sync_phase(mcpwm_dev_t *mcpwm)
+{
+    return mcpwm->cap_timer_phase;
+}
+
+static inline void mcpwm_ll_capture_enable_timer_sync(mcpwm_dev_t *mcpwm, bool enable)
+{
+    mcpwm->cap_timer_cfg.synci_en = enable;
+}
+
+static inline void mcpwm_ll_capture_set_internal_timer_syncer(mcpwm_dev_t *mcpwm, int sync_out_timer)
+{
+    mcpwm->cap_timer_cfg.synci_sel = sync_out_timer + 1;
+}
+
+static inline void mcpwm_ll_capture_set_external_syncer(mcpwm_dev_t *mcpwm, int extern_syncer)
+{
+    mcpwm->cap_timer_cfg.synci_sel = extern_syncer + 4;
+}
+
+static inline void mcpwm_ll_capture_trigger_sw_sync(mcpwm_dev_t *mcpwm)
+{
+    mcpwm->cap_timer_cfg.sync_sw = 1; // auto clear
+}
+
+static inline void mcpwm_ll_capture_enable_posedge(mcpwm_dev_t *mcpwm, int channel, bool enable)
+{
+    if (enable) {
+        mcpwm->cap_cfg_ch[channel].val |= 1 << 2;
+    } else {
+        mcpwm->cap_cfg_ch[channel].val &= ~(1 << 2);
+    }
+}
+
+static inline void mcpwm_ll_capture_enable_negedge(mcpwm_dev_t *mcpwm, int channel, bool enable)
 {
     if (enable) {
-        mcpwm->cap_timer_cfg.timer_en = 1;
-        mcpwm->cap_cfg_ch[cap_sig].en = 1;
+        mcpwm->cap_cfg_ch[channel].val |= 1 << 1;
     } else {
-        mcpwm->cap_cfg_ch[cap_sig].en = 0;
+        mcpwm->cap_cfg_ch[channel].val &= ~(1 << 1);
     }
 }
 
-/**
- * Get the captured value.
- *
- * @param mcpwm Address of the MCPWM peripheral registers.
- * @param cap_sig Of which signal to get the captured value.
- * @return The captured value
- */
-static inline uint32_t mcpwm_ll_get_capture_val(mcpwm_dev_t *mcpwm, int cap_sig)
+static inline void mcpwm_ll_invert_input(mcpwm_dev_t *mcpwm, int channel, bool invert)
 {
-    return mcpwm->cap_val_ch[cap_sig];
+    mcpwm->cap_cfg_ch[channel].in_invert = invert;
 }
 
-/**
- * Get the set capture edge.
- *
- * @param mcpwm Address of the MCPWM peripheral registers.
- * @param cap_sig Which signal the edge capture is applied.
- * @return Capture signal edge: 1 - positive edge, 2 - negtive edge
- */
-static inline mcpwm_capture_on_edge_t mcpwm_ll_get_captured_edge(mcpwm_dev_t *mcpwm, int cap_sig)
+static inline void mcpwm_ll_trigger_soft_capture(mcpwm_dev_t *mcpwm, int channel)
 {
-    bool edge;
-    if (cap_sig == 0) {
-        edge = mcpwm->cap_status.cap0_edge;
-    } else if (cap_sig == 1) {
-        edge = mcpwm->cap_status.cap1_edge;
-    } else {   //2
-        edge = mcpwm->cap_status.cap2_edge;
-    }
-    return (edge? MCPWM_NEG_EDGE: MCPWM_POS_EDGE);
+    mcpwm->cap_cfg_ch[channel].sw = 1; // auto clear
 }
 
-STATIC_HAL_REG_CHECK(MCPWM, MCPWM_NEG_EDGE, BIT(0));
-STATIC_HAL_REG_CHECK(MCPWM, MCPWM_POS_EDGE, BIT(1));
+static inline uint32_t mcpwm_ll_capture_get_value(mcpwm_dev_t *mcpwm, int channel)
+{
+    return mcpwm->cap_val_ch[channel];
+}
 
-/**
- * Select the edge to capture.
- *
- * @param mcpwm Address of the MCPWM peripheral registers.
- * @param cap_sig   The signal to capture, 0-2.
- * @param cap_edge  The edge to capture, bitwise.
- */
-static inline void mcpwm_ll_capture_select_edge(mcpwm_dev_t *mcpwm, int cap_sig,
-                                  mcpwm_capture_on_edge_t cap_edge)
+static inline bool mcpwm_ll_capture_is_negedge(mcpwm_dev_t *mcpwm, int channel)
 {
-    mcpwm->cap_cfg_ch[cap_sig].mode = cap_edge;
+    return mcpwm->cap_status.val & (1 << channel) ? true : false;
 }
 
-/**
- * Set the prescale of the input signal to capture.
- *
- * @param mcpwm Address of the MCPWM peripheral registers.
- * @param cap_sig   The prescaled signal to capture, 0-2.
- * @param prescale  Prescal value, 0 to disable.
- */
-static inline void mcpwm_ll_capture_set_prescale(mcpwm_dev_t *mcpwm, int cap_sig, uint32_t prescale)
+static inline void mcpwm_ll_capture_set_prescale(mcpwm_dev_t *mcpwm, int channel, uint32_t prescale)
 {
-    mcpwm->cap_cfg_ch[cap_sig].prescale = prescale;
+    mcpwm->cap_cfg_ch[channel].prescale = prescale - 1;
 }
 
-/**
- * Utility function, get the `mcpwm_intr_t` interrupt enum of a specific capture signal.
- *
- * @param bit x for CAPx.
- * @return the corresponding `mcpwm_intr_t`.
- */
-static inline mcpwm_intr_t mcpwm_ll_get_cap_intr_def(int bit)
+static inline uint32_t mcpwm_ll_capture_get_prescale(mcpwm_dev_t *mcpwm, int channel)
 {
-    return BIT(bit+MCPWM_CAP0_INT_RAW_S);
+    return mcpwm->cap_cfg_ch[channel].prescale + 1;
 }
 
 #ifdef __cplusplus

+ 824 - 620
components/hal/esp32s3/include/hal/mcpwm_ll.h

@@ -1,9 +1,9 @@
-// Copyright 2015-2019 Espressif Systems (Shanghai) PTE LTD
+// Copyright 2015-2021 Espressif Systems (Shanghai) PTE LTD
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
 // You may obtain a copy of the License at
-
+//
 //     http://www.apache.org/licenses/LICENSE-2.0
 //
 // Unless required by applicable law or agreed to in writing, software
@@ -18,716 +18,920 @@
  * See readme.md in hal/include/hal/readme.md
  ******************************************************************************/
 
-// The LL layer for ESP32 MCPWM register operations
+// The LL layer for ESP32-S3 MCPWM register operations
 
 #pragma once
 
+#include <stdbool.h>
 #include "soc/soc_caps.h"
-#include <soc/mcpwm_periph.h>
-#include "soc/mcpwm_periph.h"
+#include "soc/mcpwm_struct.h"
 #include "hal/mcpwm_types.h"
-#include "hal/hal_defs.h"
-
-#include "esp_types.h"
 
 #ifdef __cplusplus
 extern "C" {
 #endif
 
 /// Get the address of peripheral registers
-#define MCPWM_LL_GET_HW(ID) (((ID)==0)? &MCPWM0: &MCPWM1)
+#define MCPWM_LL_GET_HW(ID) (((ID) == 0) ? &MCPWM0 : &MCPWM1)
 #define MCPWM_LL_MAX_PRESCALE   255
 
-/********************* Global *******************/
-/**
- * Initialize common registers.
- *
- * @param mcpwm Address of the MCPWM peripheral registers.
- */
-static inline void mcpwm_ll_init(mcpwm_dev_t *mcpwm)
+/********************* Group registers *******************/
+
+// Set/Get group clock: PWM_clk = CLK_160M / (prescale + 1)
+static inline void mcpwm_ll_group_set_clock(mcpwm_dev_t *mcpwm, unsigned long long group_clk_hz)
+{
+    mcpwm->clk_cfg.prescale = (SOC_MCPWM_BASE_CLK_HZ / group_clk_hz) - 1;
+}
+
+static inline unsigned long long mcpwm_ll_group_get_clock(mcpwm_dev_t *mcpwm)
+{
+    return SOC_MCPWM_BASE_CLK_HZ / (mcpwm->clk_cfg.prescale + 1);
+}
+
+static inline void mcpwm_ll_group_enable_shadow_mode(mcpwm_dev_t *mcpwm)
 {
     mcpwm->update_cfg.global_up_en = 1;
-    mcpwm->update_cfg.global_force_up = 1;
-    mcpwm->update_cfg.global_force_up = 0;
+    mcpwm->update_cfg.op0_up_en = 1;
+    mcpwm->update_cfg.op1_up_en = 1;
+    mcpwm->update_cfg.op2_up_en = 1;
+}
+
+static inline void mcpwm_ll_group_flush_shadow(mcpwm_dev_t *mcpwm)
+{
+    mcpwm->update_cfg.val ^= (1 << 1);
+}
+
+/********************* Interrupt registers *******************/
+
+static inline uint32_t mcpwm_ll_intr_get_status(mcpwm_dev_t *mcpwm)
+{
+    return mcpwm->int_st.val;
+}
+
+static inline void mcpwm_ll_intr_clear_status(mcpwm_dev_t *mcpwm, uint32_t intr_mask)
+{
+    mcpwm->int_clr.val = intr_mask;
+}
+
+static inline void mcpwm_ll_intr_disable_all(mcpwm_dev_t *mcpwm)
+{
+    mcpwm->int_ena.val = 0;
+}
+
+//////////// get interrupt status for each event ////////////////
+
+static inline uint32_t mcpwm_ll_intr_get_timer_stop_status(mcpwm_dev_t *mcpwm)
+{
+    return (mcpwm->int_st.val >> 0) & 0x07;
+}
+
+static inline uint32_t mcpwm_ll_intr_get_timer_tez_status(mcpwm_dev_t *mcpwm)
+{
+    return (mcpwm->int_st.val >> 3) & 0x07;
+}
+
+static inline uint32_t mcpwm_ll_intr_get_timer_tep_status(mcpwm_dev_t *mcpwm)
+{
+    return (mcpwm->int_st.val >> 6) & 0x07;
+}
+
+static inline uint32_t mcpwm_ll_intr_get_fault_enter_status(mcpwm_dev_t *mcpwm)
+{
+    return (mcpwm->int_st.val >> 9) & 0x07;
+}
+
+static inline uint32_t mcpwm_ll_intr_get_fault_exit_status(mcpwm_dev_t *mcpwm)
+{
+    return (mcpwm->int_st.val >> 12) & 0x07;
 }
 
-/**
- * Set the prescale of the PWM main clock to the input clock.
- *
- * Input clock is 160MHz, PWM main clock cycle = 6.25ns*(prescale + 1).
- *
- * @param mcpwm Address of the MCPWM peripheral registers.
- * @param prescale Prescale factor, 0-255.
- */
-static inline void mcpwm_ll_set_clock_prescale(mcpwm_dev_t *mcpwm, int prescale)
+static inline uint32_t mcpwm_ll_intr_get_compare_status(mcpwm_dev_t *mcpwm, uint32_t cmp_id)
+{
+    return (mcpwm->int_st.val >> (15 + cmp_id * 3)) & 0x07;
+}
+
+static inline uint32_t mcpwm_ll_intr_get_trip_cbc_status(mcpwm_dev_t *mcpwm)
+{
+    return (mcpwm->int_st.val >> 21) & 0x07;
+}
+
+static inline uint32_t mcpwm_ll_intr_get_trip_ost_status(mcpwm_dev_t *mcpwm)
+{
+    return (mcpwm->int_st.val >> 24) & 0x07;
+}
+
+static inline uint32_t mcpwm_ll_intr_get_capture_status(mcpwm_dev_t *mcpwm)
+{
+    return (mcpwm->int_st.val >> 27) & 0x07;
+}
+
+//////////// clear interrupt status for each event ////////////////
+
+static inline void mcpwm_ll_intr_clear_timer_stop_status(mcpwm_dev_t *mcpwm, uint32_t timer_mask)
 {
-    mcpwm->clk_cfg.prescale = prescale;
+    mcpwm->int_clr.val |= (timer_mask & 0x07) << 0;
 }
 
-STATIC_HAL_REG_CHECK(MCPWM, MCPWM_LL_INTR_CAP0, MCPWM_CAP0_INT_RAW);
-STATIC_HAL_REG_CHECK(MCPWM, MCPWM_LL_INTR_CAP1, MCPWM_CAP1_INT_RAW);
-STATIC_HAL_REG_CHECK(MCPWM, MCPWM_LL_INTR_CAP2, MCPWM_CAP2_INT_RAW);
+static inline void mcpwm_ll_intr_clear_timer_tez_status(mcpwm_dev_t *mcpwm, uint32_t timer_mask)
+{
+    mcpwm->int_clr.val |= (timer_mask & 0x07) << 3;
+}
 
-/**
- * Get raw interrupt status.
- *
- * @param mcpwm Address of the MCPWM peripheral registers.
- * @return The triggered interrupts, ORed by active interrupts.
- */
-static inline mcpwm_intr_t mcpwm_ll_get_intr(mcpwm_dev_t *mcpwm)
+static inline void mcpwm_ll_intr_clear_timer_tep_status(mcpwm_dev_t *mcpwm, uint32_t timer_mask)
 {
-    return mcpwm->int_raw.val;
+    mcpwm->int_clr.val |= (timer_mask & 0x07) << 6;
 }
 
-/**
- * Clear the interrupts.
- *
- * @param mcpwm Address of the MCPWM peripheral registers.
- * @param intr Bitwise ORed interrupts to clear.
- */
-static inline void mcpwm_ll_clear_intr(mcpwm_dev_t* mcpwm, mcpwm_intr_t intr)
+static inline void mcpwm_ll_intr_clear_fault_enter_status(mcpwm_dev_t *mcpwm, uint32_t fault_mask)
 {
-    mcpwm->int_clr.val = intr;
+    mcpwm->int_clr.val |= (fault_mask & 0x07) << 9;
 }
 
-/********************* Timer *******************/
-/**
- * Set the prescale of the Timer_x clock to the PWM main clock.
- *
- * Timer clock frequency = PWM main clock frequency/(prescale + 1).
- *
- * @param mcpwm Address of the MCPWM peripheral registers.
- * @param timer The timer to set the prescale, 0-2.
- * @param prescale Prescale factor, 0-255.
- */
-static inline void mcpwm_ll_timer_set_prescale(mcpwm_dev_t* mcpwm, int timer, uint32_t prescale)
+static inline void mcpwm_ll_intr_clear_fault_exit_status(mcpwm_dev_t *mcpwm, uint32_t fault_mask)
 {
-    mcpwm->timer[timer].period.prescale = prescale;
+    mcpwm->int_clr.val |= (fault_mask & 0x07) << 12;
 }
 
+static inline void mcpwm_ll_intr_clear_compare_status(mcpwm_dev_t *mcpwm, uint32_t operator_mask, uint32_t cmp_id)
+{
+    mcpwm->int_clr.val |= (operator_mask & 0x07) << (15 + cmp_id * 3);
+}
 
-STATIC_HAL_REG_CHECK(MCPWM, MCPWM_UP_COUNTER,        1);
-STATIC_HAL_REG_CHECK(MCPWM, MCPWM_DOWN_COUNTER,      2);
-STATIC_HAL_REG_CHECK(MCPWM, MCPWM_UP_DOWN_COUNTER,   3);
+static inline void mcpwm_ll_intr_clear_trip_cbc_status(mcpwm_dev_t *mcpwm, uint32_t cbc_mask)
+{
+    mcpwm->int_clr.val |= (cbc_mask & 0x07) << 21;
+}
 
-/**
- * Set the counting mode for the PWM timer.
- *
- * @param mcpwm Address of the MCPWM peripheral registers.
- * @param timer The timer to change counting mode, 0-2.
- * @param mode Counting mode to use.
- */
-static inline void mcpwm_ll_timer_set_count_mode(mcpwm_dev_t *mcpwm, int timer, mcpwm_counter_type_t mode)
+static inline void mcpwm_ll_intr_clear_trip_ost_status(mcpwm_dev_t *mcpwm, uint32_t ost_mask)
 {
-    mcpwm->timer[timer].mode.mode = mode;
+    mcpwm->int_clr.val |= (ost_mask & 0x07) << 24;
 }
 
-/**
- * Start a timer.
- *
- * @param mcpwm Address of the MCPWM peripheral registers.
- * @param timer The timer to start, 0-2.
- */
-static inline void mcpwm_ll_timer_start(mcpwm_dev_t *mcpwm, int timer)
+static inline void mcpwm_ll_intr_clear_capture_status(mcpwm_dev_t *mcpwm, uint32_t capture_mask)
 {
-    mcpwm->timer[timer].mode.start = 2;
+    mcpwm->int_clr.val |= (capture_mask & 0x07) << 27;
 }
 
-/**
- * Stop a timer.
- *
- * @param mcpwm Address of the MCPWM peripheral registers.
- * @param timer The timer to stop, 0-2.
- */
-static inline void mcpwm_ll_timer_stop(mcpwm_dev_t *mcpwm, int timer)
+//////////// enable interrupt for each event ////////////////
+
+static inline void mcpwm_ll_intr_enable_timer_stop(mcpwm_dev_t *mcpwm, uint32_t timer_id, bool enable)
 {
-    mcpwm->timer[timer].mode.start = 0;
+    if (enable) {
+        mcpwm->int_ena.val |= 1 << (timer_id + 0);
+    } else {
+        mcpwm->int_ena.val &= ~(1 << (timer_id + 0));
+    }
 }
 
-/**
- * Set the overflow period of a timer.
- *
- * The overflow rate will be Frequency of timer / period.
- *
- * @param mcpwm Address of the MCPWM peripheral registers.
- * @param timer The timer to set period, 0-2.
- * @param period Total timer count of each period, 0-65535.
- */
-static inline void mcpwm_ll_timer_set_period(mcpwm_dev_t *mcpwm, int timer, uint32_t period)
+static inline void mcpwm_ll_intr_enable_timer_tez(mcpwm_dev_t *mcpwm, uint32_t timer_id, bool enable)
 {
+    if (enable) {
+        mcpwm->int_ena.val |= 1 << (timer_id + 3);
+    } else {
+        mcpwm->int_ena.val &= ~(1 << (timer_id + 3));
+    }
+}
 
-    mcpwm->timer[timer].period.period = period;
-    mcpwm->timer[timer].period.upmethod = 0;
+static inline void mcpwm_ll_intr_enable_timer_tep(mcpwm_dev_t *mcpwm, uint32_t timer_id, bool enable)
+{
+    if (enable) {
+        mcpwm->int_ena.val |= 1 << (timer_id + 6);
+    } else {
+        mcpwm->int_ena.val &= ~(1 << (timer_id + 6));
+    }
 }
 
-/**
- * Get the period setting of a timer.
- *
- * @param mcpwm Address of the MCPWM peripheral registers.
- * @param timer The timer to get the period, 0-2.
- * @return Period setting value.
- */
-static inline uint32_t mcpwm_ll_timer_get_period(mcpwm_dev_t *mcpwm, int timer)
+static inline void mcpwm_ll_intr_enable_fault(mcpwm_dev_t *mcpwm, uint32_t fault_id, bool enable)
 {
-    return mcpwm->timer[timer].period.period;
+    if (enable) {
+        mcpwm->int_ena.val |= 1 << (9 + fault_id);  // enter fault interrupt
+        mcpwm->int_ena.val |= 1 << (12 + fault_id); // exit fault interrupt
+    } else {
+        mcpwm->int_ena.val &= ~(1 << (9 + fault_id));
+        mcpwm->int_ena.val &= ~(1 << (12 + fault_id));
+    }
 }
 
-/********************* Sync *******************/
-/**
- * Enable the synchronization feature for a timer.
- *
- * @param mcpwm Address of the MCPWM peripheral registers.
- * @param timer Timer to set, 0-2.
- * @param enable true to enable, otherwise false.
- */
-static inline void mcpwm_ll_sync_enable(mcpwm_dev_t *mcpwm, int timer, bool enable)
+static inline void mcpwm_ll_intr_enable_compare(mcpwm_dev_t *mcpwm, uint32_t operator_id, uint32_t cmp_id, bool enable)
 {
     if (enable) {
-        mcpwm->timer[timer].sync.out_sel = 0;
-        mcpwm->timer[timer].sync.in_en = 1;
+        mcpwm->int_ena.val |= (1 << (15 + cmp_id * 3 + operator_id));
     } else {
-        mcpwm->timer[timer].sync.in_en = 0;
-    }
-}
-
-/**
- * Set the phase (counter value) to reload when the sync signal triggers.
- *
- * @param mcpwm Address of the MCPWM peripheral registers.
- * @param timer Timer to set, 0-2.
- * @param reload_val The reloaded value.
- */
-static inline void mcpwm_ll_sync_set_phase(mcpwm_dev_t *mcpwm, int timer, uint32_t reload_val)
-{
-    mcpwm->timer[timer].sync.timer_phase = reload_val;
-}
-
-STATIC_HAL_REG_CHECK(MCPWM, MCPWM_SELECT_SYNC0, 4);
-STATIC_HAL_REG_CHECK(MCPWM, MCPWM_SELECT_SYNC1, 5);
-STATIC_HAL_REG_CHECK(MCPWM, MCPWM_SELECT_SYNC2, 6);
-/**
- * Set the sync signal source for a timer.
- *
- * @param mcpwm Address of the MCPWM peripheral registers.
- * @param timer The timer to set, 0-2.
- * @param sync_sig The synchronization signal to use.
- */
-static inline void mcpwm_ll_sync_set_input(mcpwm_dev_t *mcpwm, int timer, mcpwm_sync_signal_t sync_sig)
-{
-    if (timer == 0) {
-        mcpwm->timer_synci_cfg.t0_in_sel = sync_sig;
-    } else if (timer == 1) {
-        mcpwm->timer_synci_cfg.t1_in_sel = sync_sig;
-    } else {   //MCPWM_TIMER_2
-        mcpwm->timer_synci_cfg.t2_in_sel = sync_sig;
-    }
-}
-
-/********************* Comparator *******************/
-/**
- * Select a timer for the specified operator to use.
- *
- * @param mcpwm Address of the MCPWM peripheral registers.
- * @param op    The operator to choose timer, 0-2.
- * @param timer The timer to use, 0-2.
- */
-static inline void mcpwm_ll_operator_select_timer(mcpwm_dev_t *mcpwm, int op, int timer)
-{
-    if (op == 0) {
-        mcpwm->timer_sel.operator0_sel = timer;
-    } else if (op == 1) {
-        mcpwm->timer_sel.operator1_sel = timer;
+        mcpwm->int_ena.val &= ~(1 << (15 + cmp_id * 3 + operator_id));
+    }
+}
+
+static inline void mcpwm_ll_intr_enable_trip(mcpwm_dev_t *mcpwm, uint32_t operator_id, bool enable)
+{
+    if (enable) {
+        mcpwm->int_ena.val |= (1 << (21 + operator_id));
+        mcpwm->int_ena.val |= (1 << (24 + operator_id));
     } else {
-        mcpwm->timer_sel.operator2_sel = timer;
-    }
-}
-
-/**
- * Set the update method of the compare value of a timer
- *
- * @param mcpwm Address of the MCPWM peripheral registers.
- * @param op    Operator to set, 0-2.
- */
-static inline void mcpwm_ll_operator_set_compare_upmethod(mcpwm_dev_t *mcpwm, int op)
-{
-    mcpwm->channel[op].cmpr_cfg.a_upmethod = BIT(0);
-    mcpwm->channel[op].cmpr_cfg.b_upmethod = BIT(0);
-}
-
-/**
- * Get one of the compare value of a timer.
- *
- * @param mcpwm Address of the MCPWM peripheral registers.
- * @param op    The operator to get, 0-2.
- * @param cmp_n Comparer id to get, 0-1.
- * @return The set compare value.
- */
-static inline uint32_t mcpwm_ll_operator_get_compare(mcpwm_dev_t *mcpwm, int op, int cmp_n)
-{
-    return (mcpwm->channel[op].cmpr_value[cmp_n].cmpr_val);
-}
-
-/**
- * Set one of the compare value of a timer.
- *
- * @param mcpwm Address of the MCPWM peripheral registers.
- * @param op    The operator to set, 0-2.
- * @param cmp_n The comparer to set value, 0-1.
- * @param compare The compare value, 0-65535.
- */
-static inline void mcpwm_ll_operator_set_compare(mcpwm_dev_t *mcpwm, int op, int cmp_n, uint32_t compare)
-{
-    mcpwm->channel[op].cmpr_value[cmp_n].cmpr_val = compare;
-}
-
-/********************* Generator *******************/
-
-STATIC_HAL_REG_CHECK(MCPWM, MCPWM_ACTION_NO_CHANGE, 0);
-STATIC_HAL_REG_CHECK(MCPWM, MCPWM_ACTION_FORCE_LOW, 1);
-STATIC_HAL_REG_CHECK(MCPWM, MCPWM_ACTION_FORCE_HIGH, 2);
-STATIC_HAL_REG_CHECK(MCPWM, MCPWM_ACTION_TOGGLE,     3);
-/**
- * Set the action will be taken by a operator when its timer counts to zero.
- *
- * @param mcpwm Address of the MCPWM peripheral registers.
- * @param op     The operator to set action, 0-2.
- * @param gen    One generator of the operator to take the action, 0-1.
- * @param action Action to take.
- */
-static inline void mcpwm_ll_gen_set_zero_action(mcpwm_dev_t *mcpwm, int op, int gen, mcpwm_output_action_t action)
-{
-    mcpwm->channel[op].generator[gen].utez = action;
-    mcpwm->channel[op].generator[gen].dtez = action;
-}
-
-/**
- * Set the action will be taken by a operator when its timer counts to the period value.
- *
- * @param mcpwm Address of the MCPWM peripheral registers.
- * @param op     The operator to set action, 0-2.
- * @param gen    One generator of the operator to take the action, 0-1.
- * @param action Action to take.
- */
-static inline void mcpwm_ll_gen_set_period_action(mcpwm_dev_t *mcpwm, int op, int gen, mcpwm_output_action_t action)
-{
-    mcpwm->channel[op].generator[gen].utep = action;
-    mcpwm->channel[op].generator[gen].dtep = action;
-}
-
-/**
- * Set the action will be taken by a operator when its timer counts to the compare value.
- *
- * @param mcpwm Address of the MCPWM peripheral registers.
- * @param op        The operator to set action, 0-2.
- * @param gen       One generator of the operator to take the action, 0-1.
- * @param cmp_n     The comparer to use.
- * @param up_action The action to take when the counter is counting up.
- * @param down_action The action to take when the counter is counting down.
- */
-static inline void mcpwm_ll_gen_set_cmp_action(mcpwm_dev_t *mcpwm, int op, int gen,
-               int cmp_n, mcpwm_output_action_t up_action, mcpwm_output_action_t down_action)
-{
-    if (cmp_n == 0) {
-        mcpwm->channel[op].generator[gen].utea = up_action;
-        mcpwm->channel[op].generator[gen].dtea = down_action;
+        mcpwm->int_ena.val &= ~(1 << (21 + operator_id));
+        mcpwm->int_ena.val &= ~(1 << (24 + operator_id));
+    }
+}
+
+static inline void mcpwm_ll_intr_enable_capture(mcpwm_dev_t *mcpwm, uint32_t capture_id, bool enable)
+{
+    if (enable) {
+        mcpwm->int_ena.val |= 1 << (27 + capture_id);
     } else {
-        mcpwm->channel[op].generator[gen].uteb = up_action;
-        mcpwm->channel[op].generator[gen].dteb = down_action;
+        mcpwm->int_ena.val &= ~(1 << (27 + capture_id));
+    }
+}
+
+/********************* Timer registers *******************/
+
+static inline void mcpwm_ll_timer_set_clock(mcpwm_dev_t *mcpwm, int timer_id, unsigned long long group_clock, unsigned long long timer_clock)
+{
+    mcpwm->timer[timer_id].period.prescale = group_clock / timer_clock - 1;
+}
+
+static inline unsigned long long mcpwm_ll_timer_get_clock(mcpwm_dev_t *mcpwm, int timer_id, unsigned long long group_clock)
+{
+    return group_clock / (mcpwm->timer[timer_id].period.prescale + 1);
+}
+
+static inline void mcpwm_ll_timer_set_peak(mcpwm_dev_t *mcpwm, int timer_id, uint32_t peak, bool symmetric)
+{
+    if (!symmetric) { // in asymmetric mode, period = [0,peak-1]
+        mcpwm->timer[timer_id].period.period = peak - 1;
+    } else { // in symmetric mode, period = [0,peak-1] + [peak,1]
+        mcpwm->timer[timer_id].period.period = peak;
+    }
+}
+
+static inline uint32_t mcpwm_ll_timer_get_peak(mcpwm_dev_t *mcpwm, int timer_id, bool symmetric)
+{
+    // asymmetric mode
+    if (!symmetric) {
+        return mcpwm->timer[timer_id].period.period + 1;
     }
+    // symmetric mode
+    return mcpwm->timer[timer_id].period.period;
+}
+
+static inline void mcpwm_ll_timer_update_period_at_once(mcpwm_dev_t *mcpwm, int timer_id)
+{
+    mcpwm->timer[timer_id].period.upmethod = 0;
+}
+
+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].period.upmethod |= 0x01;
+    } else {
+        mcpwm->timer[timer_id].period.upmethod &= ~0x01;
+    }
+}
+
+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].period.upmethod |= 0x02;
+    } else {
+        mcpwm->timer[timer_id].period.upmethod &= ~0x02;
+    }
+}
+
+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].mode.mode = 0;
+        break;
+    case MCPWM_TIMER_COUNT_MODE_UP:
+        mcpwm->timer[timer_id].mode.mode = 1;
+        break;
+    case MCPWM_TIMER_COUNT_MODE_DOWN:
+        mcpwm->timer[timer_id].mode.mode = 2;
+        break;
+    case MCPWM_TIMER_COUNT_MODE_UP_DOWN:
+        mcpwm->timer[timer_id].mode.mode = 3;
+        break;
+    }
+}
+
+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].mode.mode) {
+    case 0:
+        return MCPWM_TIMER_COUNT_MODE_PAUSE;
+    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;
+    }
+}
+
+static inline void mcpwm_ll_timer_set_operate_command(mcpwm_dev_t *mcpwm, int timer_id, mcpwm_timer_operate_cmd_t mode)
+{
+    switch (mode) {
+    case MCPWM_TIMER_STOP_AT_ZERO:
+        mcpwm->timer[timer_id].mode.start = 0;
+        break;
+    case MCPWM_TIMER_STOP_AT_PEAK:
+        mcpwm->timer[timer_id].mode.start = 1;
+        break;
+    case MCPWM_TIMER_START_NO_STOP:
+        mcpwm->timer[timer_id].mode.start = 2;
+        break;
+    case MCPWM_TIMER_START_STOP_AT_ZERO:
+        mcpwm->timer[timer_id].mode.start = 3;
+        break;
+    case MCPWM_TIMER_START_STOP_AT_PEAK:
+        mcpwm->timer[timer_id].mode.start = 4;
+        break;
+    }
+}
+
+static inline void mcpwm_ll_timer_set_count_value(mcpwm_dev_t *mcpwm, int timer_id, uint32_t value)
+{
+    // we use software sync to set count value
+    int previous_phase = mcpwm->timer[timer_id].sync.timer_phase;
+    mcpwm->timer[timer_id].sync.timer_phase = value;
+    mcpwm->timer[timer_id].sync.sync_sw = ~mcpwm->timer[timer_id].sync.sync_sw;
+    mcpwm->timer[timer_id].sync.timer_phase = previous_phase;
+}
+
+static inline uint32_t mcpwm_ll_timer_get_count_value(mcpwm_dev_t *mcpwm, int timer_id)
+{
+    return mcpwm->timer[timer_id].status.value;
+}
+
+static inline bool mcpwm_ll_is_timer_decreasing(mcpwm_dev_t *mcpwm, int timer_id)
+{
+    return mcpwm->timer[timer_id].status.direction;
+}
+
+static inline void mcpwm_ll_timer_enable_sync_input(mcpwm_dev_t *mcpwm, int timer_id, bool enable)
+{
+    mcpwm->timer[timer_id].sync.in_en = enable;
+}
+
+static inline void mcpwm_ll_timer_sync_out_same_in(mcpwm_dev_t *mcpwm, int timer_id)
+{
+    mcpwm->timer[timer_id].sync.out_sel = 0;
+}
+
+static inline void mcpwm_ll_timer_sync_out_on_timer_event(mcpwm_dev_t *mcpwm, int timer_id, mcpwm_timer_event_t event)
+{
+    if (event == MCPWM_TIMER_EVENT_ZERO) {
+        mcpwm->timer[timer_id].sync.out_sel = 1;
+    } else if (event == MCPWM_TIMER_EVENT_PEAK) {
+        mcpwm->timer[timer_id].sync.out_sel = 2;
+    }
+}
+
+static inline void mcpwm_ll_timer_disable_sync_out(mcpwm_dev_t *mcpwm, int timer_id)
+{
+    mcpwm->timer[timer_id].sync.out_sel = 3;
+}
+
+static inline void mcpwm_ll_timer_trigger_sw_sync(mcpwm_dev_t *mcpwm, int timer_id)
+{
+    mcpwm->timer[timer_id].sync.sync_sw = ~mcpwm->timer[timer_id].sync.sync_sw;
+}
+
+static inline void mcpwm_ll_timer_set_sync_phase_value(mcpwm_dev_t *mcpwm, int timer_id, uint32_t reload_val)
+{
+    mcpwm->timer[timer_id].sync.timer_phase = reload_val;
+}
+
+static inline uint32_t mcpwm_ll_timer_get_sync_phase_value(mcpwm_dev_t *mcpwm, int timer_id)
+{
+    return mcpwm->timer[timer_id].sync.timer_phase;
+}
+
+static inline void mcpwm_ll_timer_set_sync_phase_direction(mcpwm_dev_t *mcpwm, int timer_id, bool decrease)
+{
+    mcpwm->timer[timer_id].sync.phase_direct = decrease;
+}
+
+static inline void mcpwm_ll_timer_enable_sync_from_internal_timer(mcpwm_dev_t *mcpwm, int this_timer, int internal_sync_timer)
+{
+    mcpwm->timer_synci_cfg.val &= ~(0x07 << (this_timer * 3));
+    mcpwm->timer_synci_cfg.val |= (internal_sync_timer + 1) << (this_timer * 3);
+}
+
+static inline void mcpwm_ll_timer_enable_sync_from_external(mcpwm_dev_t *mcpwm, int this_timer, int extern_syncer)
+{
+    mcpwm->timer_synci_cfg.val &= ~(0x07 << (this_timer * 3));
+    mcpwm->timer_synci_cfg.val |= (extern_syncer + 4) << (this_timer * 3);
+}
+
+static inline void mcpwm_ll_invert_external_syncer(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));
+    }
+}
+
+/********************* Operator registers *******************/
+
+static inline void mcpwm_ll_operator_flush_shadow(mcpwm_dev_t *mcpwm, int operator_id)
+{
+    mcpwm->update_cfg.val ^= (1 << (2 * operator_id + 3));
+}
+
+static inline void mcpwm_ll_operator_select_timer(mcpwm_dev_t *mcpwm, int operator_id, int timer_id)
+{
+    if (operator_id == 0) {
+        mcpwm->timer_sel.operator0_sel = timer_id;
+    } else if (operator_id == 1) {
+        mcpwm->timer_sel.operator1_sel = timer_id;
+    } else {
+        mcpwm->timer_sel.operator2_sel = timer_id;
+    }
+}
+
+static inline void mcpwm_ll_operator_update_compare_at_once(mcpwm_dev_t *mcpwm, int operator_id, int compare_id)
+{
+    mcpwm->channel[operator_id].cmpr_cfg.val &= ~(0x0F << (4 * compare_id));
+}
+
+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->channel[operator_id].cmpr_cfg.val |= (1 << 0) << (4 * compare_id);
+    } else {
+        mcpwm->channel[operator_id].cmpr_cfg.val &= ~((1 << 0) << (4 * compare_id));
+    }
+}
+
+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->channel[operator_id].cmpr_cfg.val |= (1 << 1) << (4 * compare_id);
+    } else {
+        mcpwm->channel[operator_id].cmpr_cfg.val &= ~((1 << 1) << (4 * compare_id));
+    }
+}
+
+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->channel[operator_id].cmpr_cfg.val |= (1 << 2) << (4 * compare_id);
+    } else {
+        mcpwm->channel[operator_id].cmpr_cfg.val &= ~((1 << 2) << (4 * compare_id));
+    }
+}
+
+static inline void mcpwm_ll_operator_set_compare_value(mcpwm_dev_t *mcpwm, int operator_id, int compare_id, uint32_t compare_value)
+{
+    mcpwm->channel[operator_id].cmpr_value[compare_id].cmpr_val = compare_value;
+}
+
+static inline uint32_t mcpwm_ll_operator_get_compare_value(mcpwm_dev_t *mcpwm, int operator_id, int compare_id)
+{
+    return mcpwm->channel[operator_id].cmpr_value[compare_id].cmpr_val;
+}
+
+static inline void mcpwm_ll_operator_update_action_at_once(mcpwm_dev_t *mcpwm, int operator_id)
+{
+    mcpwm->channel[operator_id].gen_cfg0.upmethod = 0;
 }
 
-/********************* Fault *******************/
-/**
- * Enable the fault detection feature for an input signal.
- *
- * @param mcpwm Address of the MCPWM peripheral registers.
- * @param fault_sig One of the signals to select, 0-2.
- * @param level The active level of the fault-detection signal.
- */
-static inline void mcpwm_ll_fault_enable(mcpwm_dev_t *mcpwm, int fault_sig, bool level)
+static inline void mcpwm_ll_operator_enable_update_action_on_tez(mcpwm_dev_t *mcpwm, int operator_id, bool enable)
+{
+    if (enable) {
+        mcpwm->channel[operator_id].gen_cfg0.upmethod |= 1 << 0;
+    } else {
+        mcpwm->channel[operator_id].gen_cfg0.upmethod &= ~(1 << 0);
+    }
+}
+
+static inline void mcpwm_ll_operator_enable_update_action_on_tep(mcpwm_dev_t *mcpwm, int operator_id, bool enable)
+{
+    if (enable) {
+        mcpwm->channel[operator_id].gen_cfg0.upmethod |= 1 << 1;
+    } else {
+        mcpwm->channel[operator_id].gen_cfg0.upmethod &= ~(1 << 1);
+    }
+}
+
+static inline void mcpwm_ll_operator_enable_update_action_on_sync(mcpwm_dev_t *mcpwm, int operator_id, bool enable)
+{
+    if (enable) {
+        mcpwm->channel[operator_id].gen_cfg0.upmethod |= 1 << 2;
+    } else {
+        mcpwm->channel[operator_id].gen_cfg0.upmethod &= ~(1 << 2);
+    }
+}
+
+/********************* Generator registers *******************/
+
+static inline void mcpwm_ll_generator_reset_actions(mcpwm_dev_t *mcpwm, int operator_id, int generator_id)
+{
+    mcpwm->channel[operator_id].generator[generator_id].val = 0;
+}
+
+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)
+{
+    if (direction == MCPWM_TIMER_DIRECTION_UP) { // utez, utep
+        mcpwm->channel[operator_id].generator[generator_id].val &= ~(0x03 << (event * 2));
+        mcpwm->channel[operator_id].generator[generator_id].val |= action << (event * 2);
+    } else if (direction == MCPWM_TIMER_DIRECTION_DOWN) { // dtez, dtep
+        mcpwm->channel[operator_id].generator[generator_id].val &= ~(0x03 << (event * 2 + 12));
+        mcpwm->channel[operator_id].generator[generator_id].val |= action << (event * 2 + 12);
+    }
+}
+
+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->channel[operator_id].generator[generator_id].val &= ~(0x03 << (cmp_id * 2 + 4));
+        mcpwm->channel[operator_id].generator[generator_id].val |= action << (cmp_id * 2 + 4);
+    } else if (direction == MCPWM_TIMER_DIRECTION_DOWN) { // dtea, dteb
+        mcpwm->channel[operator_id].generator[generator_id].val &= ~(0x03 << (cmp_id * 2 + 16));
+        mcpwm->channel[operator_id].generator[generator_id].val |= action << (cmp_id * 2 + 16);
+    }
+}
+
+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->channel[operator_id].generator[generator_id].val &= ~(0x03 << (trig_id * 2 + 8));
+        mcpwm->channel[operator_id].generator[generator_id].val |= action << (trig_id * 2 + 8);
+    } else if (direction == MCPWM_TIMER_DIRECTION_DOWN) { // dt0, dt1
+        mcpwm->channel[operator_id].generator[generator_id].val &= ~(0x03 << (trig_id * 2 + 20));
+        mcpwm->channel[operator_id].generator[generator_id].val |= action << (trig_id * 2 + 20);
+    }
+}
+
+static inline void mcpwm_ll_gen_set_onetime_force_action(mcpwm_dev_t *mcpwm, int operator_id, int generator_id, int action)
+{
+    if (generator_id == 0) {
+        mcpwm->channel[operator_id].gen_force.a_nciforce_mode = action;
+        mcpwm->channel[operator_id].gen_force.a_nciforce = ~mcpwm->channel[operator_id].gen_force.a_nciforce;
+    } else {
+        mcpwm->channel[operator_id].gen_force.b_nciforce_mode = action;
+        mcpwm->channel[operator_id].gen_force.b_nciforce = ~mcpwm->channel[operator_id].gen_force.b_nciforce;
+    }
+}
+
+static inline void mcpwm_ll_gen_set_continuous_force_action(mcpwm_dev_t *mcpwm, int operator_id, int generator_id, int action)
+{
+    mcpwm->channel[operator_id].gen_force.cntu_force_upmethod = 0; // force action immediately
+    if (generator_id == 0) {
+        mcpwm->channel[operator_id].gen_force.a_cntuforce_mode = action;
+    } else {
+        mcpwm->channel[operator_id].gen_force.b_cntuforce_mode = action;
+    }
+}
+
+/********************* Dead time registers *******************/
+
+static inline void mcpwm_ll_deadtime_set_resolution_same_to_timer(mcpwm_dev_t *mcpwm, int operator_id, bool same)
+{
+    mcpwm->channel[operator_id].db_cfg.clk_sel = same;
+}
+
+static inline void mcpwm_ll_deadtime_red_select_generator(mcpwm_dev_t *mcpwm, int operator_id, int generator)
+{
+    mcpwm->channel[operator_id].db_cfg.red_insel = generator;
+}
+
+static inline void mcpwm_ll_deadtime_fed_select_generator(mcpwm_dev_t *mcpwm, int operator_id, int generator)
+{
+    mcpwm->channel[operator_id].db_cfg.fed_insel = generator;
+}
+
+static inline void mcpwm_ll_deadtime_bypass_path(mcpwm_dev_t *mcpwm, int operator_id, int path, bool bypass)
+{
+    if (bypass) {
+        mcpwm->channel[operator_id].db_cfg.val |= 1 << (path + 15);
+    } else {
+        mcpwm->channel[operator_id].db_cfg.val &= ~(1 << (path + 15));
+    }
+}
+
+static inline void mcpwm_ll_deadtime_invert_outpath(mcpwm_dev_t *mcpwm, int operator_id, int path, bool invert)
+{
+    if (invert) {
+        mcpwm->channel[operator_id].db_cfg.val |= 1 << (path + 13);
+    } else {
+        mcpwm->channel[operator_id].db_cfg.val &= ~(1 << (path + 13));
+    }
+}
+
+static inline void mcpwm_ll_deadtime_swap_out_path(mcpwm_dev_t *mcpwm, int operator_id, int path, bool swap)
+{
+    if (swap) {
+        mcpwm->channel[operator_id].db_cfg.val |= 1 << (path + 9);
+    } else {
+        mcpwm->channel[operator_id].db_cfg.val &= ~(1 << (path + 9));
+    }
+}
+
+static inline void mcpwm_ll_deadtime_enable_deb(mcpwm_dev_t *mcpwm, int operator_id, bool enable)
+{
+    mcpwm->channel[operator_id].db_cfg.deb_mode = enable;
+}
+
+static inline uint32_t mcpwm_ll_deadtime_get_topology_code(mcpwm_dev_t *mcpwm, int operator_id)
+{
+    return (mcpwm->channel[operator_id].db_cfg.deb_mode << 8) | (mcpwm->channel[operator_id].db_cfg.b_outswap << 7) |
+           (mcpwm->channel[operator_id].db_cfg.a_outswap << 6) | (mcpwm->channel[operator_id].db_cfg.fed_insel << 5) |
+           (mcpwm->channel[operator_id].db_cfg.red_insel << 4) | (mcpwm->channel[operator_id].db_cfg.fed_outinvert << 3) |
+           (mcpwm->channel[operator_id].db_cfg.red_outinvert << 2) | (mcpwm->channel[operator_id].db_cfg.a_outbypass << 1) |
+           (mcpwm->channel[operator_id].db_cfg.b_outbypass << 0);
+}
+
+static inline void mcpwm_ll_deadtime_set_falling_delay(mcpwm_dev_t *mcpwm, int operator_id, uint32_t fed)
+{
+    mcpwm->channel[operator_id].db_fed_cfg.fed = fed - 1;
+}
+
+static inline uint32_t mcpwm_ll_deadtime_get_falling_delay(mcpwm_dev_t *mcpwm, int operator_id)
+{
+    return mcpwm->channel[operator_id].db_fed_cfg.fed + 1;
+}
+
+static inline void mcpwm_ll_deadtime_set_rising_delay(mcpwm_dev_t *mcpwm, int operator_id, uint32_t red)
+{
+    mcpwm->channel[operator_id].db_red_cfg.red = red - 1;
+}
+
+static inline uint32_t mcpwm_ll_deadtime_get_rising_delay(mcpwm_dev_t *mcpwm, int operator_id)
+{
+    return mcpwm->channel[operator_id].db_red_cfg.red + 1;
+}
+
+static inline void mcpwm_ll_deadtime_update_delay_at_once(mcpwm_dev_t *mcpwm, int operator_id)
+{
+    mcpwm->channel[operator_id].db_cfg.fed_upmethod = 0;
+    mcpwm->channel[operator_id].db_cfg.red_upmethod = 0;
+}
+
+static inline void mcpwm_ll_deadtime_enable_update_delay_on_tez(mcpwm_dev_t *mcpwm, int operator_id, bool enable)
+{
+    if (enable) {
+        mcpwm->channel[operator_id].db_cfg.fed_upmethod |= 1 << 0;
+        mcpwm->channel[operator_id].db_cfg.red_upmethod |= 1 << 0;
+    } else {
+        mcpwm->channel[operator_id].db_cfg.fed_upmethod &= ~(1 << 0);
+        mcpwm->channel[operator_id].db_cfg.red_upmethod &= ~(1 << 0);
+    }
+}
+
+static inline void mcpwm_ll_deadtime_enable_update_delay_on_tep(mcpwm_dev_t *mcpwm, int operator_id, bool enable)
+{
+    if (enable) {
+        mcpwm->channel[operator_id].db_cfg.fed_upmethod |= 1 << 1;
+        mcpwm->channel[operator_id].db_cfg.red_upmethod |= 1 << 1;
+    } else {
+        mcpwm->channel[operator_id].db_cfg.fed_upmethod &= ~(1 << 1);
+        mcpwm->channel[operator_id].db_cfg.red_upmethod &= ~(1 << 1);
+    }
+}
+
+static inline void mcpwm_ll_deadtime_enable_update_delay_on_sync(mcpwm_dev_t *mcpwm, int operator_id, bool enable)
+{
+    if (enable) {
+        mcpwm->channel[operator_id].db_cfg.fed_upmethod |= 1 << 2;
+        mcpwm->channel[operator_id].db_cfg.red_upmethod |= 1 << 2;
+    } else {
+        mcpwm->channel[operator_id].db_cfg.fed_upmethod &= ~(1 << 2);
+        mcpwm->channel[operator_id].db_cfg.red_upmethod &= ~(1 << 2);
+    }
+}
+
+/********************* Carrier registers *******************/
+
+static inline void mcpwm_ll_carrier_enable(mcpwm_dev_t *mcpwm, int operator_id, bool enable)
+{
+    mcpwm->channel[operator_id].carrier_cfg.en = enable;
+}
+
+static inline void mcpwm_ll_carrier_set_prescale(mcpwm_dev_t *mcpwm, int operator_id, uint8_t prescale)
+{
+    mcpwm->channel[operator_id].carrier_cfg.prescale = prescale - 1;
+}
+
+static inline uint8_t mcpwm_ll_carrier_get_prescale(mcpwm_dev_t *mcpwm, int operator_id)
+{
+    return mcpwm->channel[operator_id].carrier_cfg.prescale + 1;
+}
+
+static inline void mcpwm_ll_carrier_set_duty(mcpwm_dev_t *mcpwm, int operator_id, uint8_t carrier_duty)
+{
+    mcpwm->channel[operator_id].carrier_cfg.duty = carrier_duty;
+}
+
+static inline uint8_t mcpwm_ll_carrier_get_duty(mcpwm_dev_t *mcpwm, int operator_id)
+{
+    return mcpwm->channel[operator_id].carrier_cfg.duty;
+}
+
+static inline void mcpwm_ll_carrier_out_invert(mcpwm_dev_t *mcpwm, int operator_id, bool invert)
+{
+    mcpwm->channel[operator_id].carrier_cfg.out_invert = invert;
+}
+
+static inline void mcpwm_ll_carrier_in_invert(mcpwm_dev_t *mcpwm, int operator_id, bool invert)
+{
+    mcpwm->channel[operator_id].carrier_cfg.in_invert = invert;
+}
+
+static inline void mcpwm_ll_carrier_set_oneshot_width(mcpwm_dev_t *mcpwm, int operator_id, uint8_t pulse_width)
+{
+    mcpwm->channel[operator_id].carrier_cfg.oshtwth = pulse_width - 1;
+}
+
+static inline uint8_t mcpwm_ll_carrier_get_oneshot_width(mcpwm_dev_t *mcpwm, int operator_id)
+{
+    return mcpwm->channel[operator_id].carrier_cfg.oshtwth + 1;
+}
+
+/********************* Fault detector registers *******************/
+
+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);
+    }
+}
+
+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));
+    }
+}
+
+static inline void mcpwm_ll_fault_clear_ost(mcpwm_dev_t *mcpwm, int operator_id)
+{
+    // a posedge can clear the ost fault status
+    mcpwm->channel[operator_id].tz_cfg1.clr_ost = 0;
+    mcpwm->channel[operator_id].tz_cfg1.clr_ost = 1;
+}
+
+static inline void mcpwm_ll_fault_enable_oneshot_mode(mcpwm_dev_t *mcpwm, int operator_id, int fault_sig, bool enable)
 {
     if (fault_sig == 0) {
-        mcpwm->fault_detect.f0_en = 1;
-        mcpwm->fault_detect.f0_pole = level;
+        mcpwm->channel[operator_id].tz_cfg0.f0_ost = enable;
     } else if (fault_sig == 1) {
-        mcpwm->fault_detect.f1_en = 1;
-        mcpwm->fault_detect.f1_pole = level;
-    } else {   //MCPWM_SELECT_F2
-        mcpwm->fault_detect.f2_en = 1;
-        mcpwm->fault_detect.f2_pole = level;
+        mcpwm->channel[operator_id].tz_cfg0.f1_ost = enable;
+    } else {
+        mcpwm->channel[operator_id].tz_cfg0.f2_ost = enable;
     }
 }
 
-/**
- * Disable the fault detection of an input signal.
- * @param mcpwm Address of the MCPWM peripheral registers.
- * @param fault_sig The signal to disable, 0-2.
- */
-static inline void mcpwm_ll_fault_disable(mcpwm_dev_t *mcpwm, int fault_sig)
+static inline void mcpwm_ll_fault_enable_cbc_mode(mcpwm_dev_t *mcpwm, int operator_id, int fault_sig, bool enable)
 {
     if (fault_sig == 0) {
-        mcpwm->fault_detect.f0_en = 0;
+        mcpwm->channel[operator_id].tz_cfg0.f0_cbc = enable;
     } else if (fault_sig == 1) {
-        mcpwm->fault_detect.f1_en = 0;
-    } else {   //MCPWM_SELECT_F2
-        mcpwm->fault_detect.f2_en = 0;
-    }
-}
-
-/**
- * Clear the oneshot fault status of an operator.
- *
- * @param mcpwm Address of the MCPWM peripheral registers.
- * @param op The operator to clear, 0-2.
- */
-static inline void mcpwm_ll_fault_clear_ost(mcpwm_dev_t *mcpwm, int op)
-{
-    mcpwm->channel[op].tz_cfg1.clr_ost = 1;
-    mcpwm->channel[op].tz_cfg1.clr_ost = 0;
-}
-
-/**
- * Use the oneshot mode to handle the fault when it occurs
- *
- * @param mcpwm Address of the MCPWM peripheral registers.
- * @param op     The operator to handle the fault signal, 0-2.
- * @param signal The fault signal to set, 0-2.
- * @param enable true to enable oneshot, otherwise false.
- */
-static inline void mcpwm_ll_fault_oneshot_enable_signal(mcpwm_dev_t *mcpwm, int op, int signal, bool enable)
-{
-    if (signal == 0) {
-        mcpwm->channel[op].tz_cfg0.f0_ost = enable;
-    } else if (signal == 1) {
-        mcpwm->channel[op].tz_cfg0.f1_ost = enable;
-    } else {   //MCPWM_SELECT_F2
-        mcpwm->channel[op].tz_cfg0.f2_ost = enable;
-    }
-}
-
-/**
- * @brief Get the oneshot enabled status of the operator
- *
- * @param mcpwm Address of the MCPWM peripheral registers.
- * @param op     The operator to check, 0-2.
- * @param signal The fault signal to get, 0-2.
- */
-static inline bool mcpwm_ll_fault_oneshot_signal_enabled(mcpwm_dev_t *mcpwm, int op, int signal)
-{
-    if (signal == 0) {
-        return mcpwm->channel[op].tz_cfg0.f0_ost;
-    } else if (signal == 1) {
-        return mcpwm->channel[op].tz_cfg0.f1_ost;
-    } else {   //MCPWM_SELECT_F2
-        return mcpwm->channel[op].tz_cfg0.f2_ost;
-    }
-}
-
-/**
- * Use the CBC (cycle-by-cycle) mode to handle the fault when it occurs.
- *
- * @param mcpwm Address of the MCPWM peripheral registers.
- * @param op     The operator to handle the fault signal, 0-2.
- * @param signal The fault signal to set, 0-2.
- * @param enable true to enable cbc mode, otherwise false.
- */
-static inline void mcpwm_ll_fault_cbc_enable_signal(mcpwm_dev_t *mcpwm, int op, int signal, bool enable)
-{
-    if (signal == 0) {
-        mcpwm->channel[op].tz_cfg0.f0_cbc = enable;
-    } else if (signal == 1) {
-        mcpwm->channel[op].tz_cfg0.f1_cbc = enable;
-    } else {   //MCPWM_SELECT_F2
-        mcpwm->channel[op].tz_cfg0.f2_cbc = enable;
-    }
-}
-
-/**
- * Set the action that will be taken when the fault is handled by oneshot.
- *
- * @param mcpwm Address of the MCPWM peripheral registers.
- * @param op     The operator to handle the fault signal, 0-2.
- * @param gen    The generator to take the action, 0-1.
- * @param up_action     Action to take when fault happens when counting up.
- * @param down_action   Action to take when fault happens when counting down.
- */
-static inline void mcpwm_ll_fault_set_oneshot_action(mcpwm_dev_t *mcpwm, int op, int gen,
-        mcpwm_output_action_t up_action, mcpwm_output_action_t down_action)
-{
-    if (gen == 0) {
-        mcpwm->channel[op].tz_cfg0.a_ost_u = up_action;
-        mcpwm->channel[op].tz_cfg0.a_ost_d = down_action;
+        mcpwm->channel[operator_id].tz_cfg0.f1_cbc = enable;
     } else {
-        mcpwm->channel[op].tz_cfg0.b_ost_u = up_action;
-        mcpwm->channel[op].tz_cfg0.b_ost_d = down_action;
-    }
-}
-
-/**
- * Set the action that will be taken when the fault is handled cycle by cycle.
- *
- * @param mcpwm Address of the MCPWM peripheral registers.
- * @param op     The operator to handle the fault signal, 0-2.
- * @param gen    The generator to take the action, 0-1.
- * @param up_action     Action to take when fault happens when counting up.
- * @param down_action   Action to take when fault happens when counting down.
- */
-static inline void mcpwm_ll_fault_set_cyc_action(mcpwm_dev_t *mcpwm, int op, int gen,
-                  mcpwm_output_action_t up_action, mcpwm_output_action_t down_action)
-{
-    mcpwm->channel[op].tz_cfg1.cbcpulse = BIT(0);    //immediately
-    if (gen == 0) {
-        mcpwm->channel[op].tz_cfg0.a_cbc_u = up_action;
-        mcpwm->channel[op].tz_cfg0.a_cbc_d = down_action;
-    } else {
-        mcpwm->channel[op].tz_cfg0.b_cbc_u = up_action;
-        mcpwm->channel[op].tz_cfg0.b_cbc_d = down_action;
-    }
-}
-
-/********************* Dead Zone (deadtime) *******************/
-/**
- * Initialize the dead zone feature.
- *
- * @param mcpwm Address of the MCPWM peripheral registers.
- * @param op The operator to initialize, 0-2.
- */
-static inline void mcpwm_ll_deadtime_init(mcpwm_dev_t *mcpwm, int op)
-{
-    mcpwm->channel[op].db_cfg.fed_upmethod = BIT(0);
-    mcpwm->channel[op].db_cfg.red_upmethod = BIT(0);
-    mcpwm->channel[op].db_cfg.clk_sel = 0;
-}
-
-/**
- * Set the output dead zone mode applying to the outputs of a timer.
- *
- * If the desired internal connection is not provided, you can write your own inside this function.
- *
- * @param mcpwm Address of the MCPWM peripheral registers.
- * @param op    The operator to set, 0-2.
- * @param mode  Dead zone mode to use.
- */
-static inline void mcpwm_ll_set_deadtime_mode(mcpwm_dev_t *mcpwm,
-    int op, mcpwm_deadtime_type_t mode)
-{
-#define MCPWM_LL_DEADTIME_REG_MASK (MCPWM_DT0_DEB_MODE_M | MCPWM_DT0_A_OUTSWAP_M | MCPWM_DT0_B_OUTSWAP_M | \
-    MCPWM_DT0_RED_INSEL_M | MCPWM_DT0_FED_INSEL_M | MCPWM_DT0_RED_OUTINVERT_M | MCPWM_DT0_FED_OUTINVERT_M | \
-    MCPWM_DT0_A_OUTBYPASS_M | MCPWM_DT0_B_OUTBYPASS_M)
-
-    static uint32_t deadtime_mode_settings[MCPWM_DEADTIME_TYPE_MAX] = {
-        [MCPWM_BYPASS_RED] =                    0b010010000 << MCPWM_DT0_DEB_MODE_S,
-        [MCPWM_BYPASS_FED] =                    0b100000000 << MCPWM_DT0_DEB_MODE_S,
-        [MCPWM_ACTIVE_HIGH_MODE] =              0b000010000 << MCPWM_DT0_DEB_MODE_S,
-        [MCPWM_ACTIVE_LOW_MODE] =               0b001110000 << MCPWM_DT0_DEB_MODE_S,
-        [MCPWM_ACTIVE_HIGH_COMPLIMENT_MODE] =   0b001010000 << MCPWM_DT0_DEB_MODE_S,
-        [MCPWM_ACTIVE_LOW_COMPLIMENT_MODE] =    0b000101000 << MCPWM_DT0_DEB_MODE_S,
-        [MCPWM_ACTIVE_RED_FED_FROM_PWMXA] =     0b000000011 << MCPWM_DT0_DEB_MODE_S,
-        [MCPWM_ACTIVE_RED_FED_FROM_PWMXB] =     0b000001011 << MCPWM_DT0_DEB_MODE_S,
-        [MCPWM_DEADTIME_BYPASS] =               0b110000000 << MCPWM_DT0_DEB_MODE_S,
-    };
-    mcpwm->channel[op].db_cfg.val =
-        (mcpwm->channel[op].db_cfg.val & (~MCPWM_LL_DEADTIME_REG_MASK)) | deadtime_mode_settings[mode];
-
-#undef MCPWM_LL_DEADTIME_REG_MASK
-}
-
-/**
- * Set the delay of the falling edge on the output.
- *
- * @param mcpwm Address of the MCPWM peripheral registers.
- * @param op    The operator to set, 0-2.
- * @param fed   Falling delay, by PWM main clock.
- */
-static inline void mcpwm_ll_deadtime_set_falling_delay(mcpwm_dev_t *mcpwm, int op, uint32_t fed)
-{
-    mcpwm->channel[op].db_fed_cfg.fed = fed;
-}
-
-/**
- * Set the delay of the rising edge on the output.
- *
- * @param mcpwm Address of the MCPWM peripheral registers.
- * @param op    The operator to set, 0-2.
- * @param fed   Rising delay, by PWM main clock.
- */
-static inline void mcpwm_ll_deadtime_set_rising_delay(mcpwm_dev_t *mcpwm, int op, uint32_t red)
-{
-    mcpwm->channel[op].db_red_cfg.red = red;
-}
-
-/**
- * Disable (bypass) the dead zone feature.
- *
- * @param mcpwm Address of the MCPWM peripheral registers.
- * @param op The operator to set, 0-2.
- */
-static inline void mcpwm_ll_deadtime_bypass(mcpwm_dev_t *mcpwm, int op)
-{
-    mcpwm_ll_set_deadtime_mode(mcpwm, op, MCPWM_DEADTIME_BYPASS);
-}
-
-/********************* Carrier *******************/
-/**
- * Initialize the carrier feature.
- *
- * @param mcpwm Address of the MCPWM peripheral registers.
- * @param op The operator to set, 0-2.
- */
-static inline void mcpwm_ll_carrier_init(mcpwm_dev_t *mcpwm, int op)
-{
-    mcpwm->channel[op].carrier_cfg.in_invert = 0;
-}
-
-/**
- * Enable the carrier feature for a timer.
- *
- * @param mcpwm Address of the MCPWM peripheral registers.
- * @param op The operator to set, 0-2.
- * @param enable true to enable, otherwise false.
- */
-static inline void mcpwm_ll_carrier_enable(mcpwm_dev_t *mcpwm, int op, bool enable)
-{
-    mcpwm->channel[op].carrier_cfg.en = enable;
-}
-
-/**
- * Set the prescale of the carrier timer.
- *
- * The carrier period will be Frequency of PWM main clock/(carrier_period+1).
- *
- * @param mcpwm Address of the MCPWM peripheral registers.
- * @param op The operator to set, 0-2.
- * @param carrier_period The prescale of the carrier clock, 0-15.
- */
-static inline void mcpwm_ll_carrier_set_prescale(mcpwm_dev_t *mcpwm, int op, uint8_t carrier_period)
-{
-    mcpwm->channel[op].carrier_cfg.prescale = carrier_period;
-}
-
-/**
- * Set the duty rate of the carrier.
- *
- * @param mcpwm Address of the MCPWM peripheral registers.
- * @param op The operator to set, 0-2.
- * @param carrier_duty Duty rate will be (carrier_duty/8)*100%. 0-7.
- */
-static inline void mcpwm_ll_carrier_set_duty(mcpwm_dev_t *mcpwm, int op, uint8_t carrier_duty)
-{
-    mcpwm->channel[op].carrier_cfg.duty = carrier_duty;
-}
-
-/**
- * Invert output of the carrier.
- *
- * @param mcpwm Address of the MCPWM peripheral registers.
- * @param op The operator to set, 0-2.
- * @param invert true to invert, otherwise false.
- */
-static inline void mcpwm_ll_carrier_out_invert(mcpwm_dev_t *mcpwm, int op, bool invert)
+        mcpwm->channel[operator_id].tz_cfg0.f2_cbc = enable;
+    }
+    mcpwm->channel[operator_id].tz_cfg1.cbcpulse = 1 << 0;
+}
+
+static inline void mcpwm_ll_fault_enable_sw_cbc(mcpwm_dev_t *mcpwm, int operator_id, bool enable)
+{
+    mcpwm->channel[operator_id].tz_cfg0.sw_cbc = enable;
+}
+
+static inline void mcpwm_ll_fault_enable_sw_oneshot(mcpwm_dev_t *mcpwm, int operator_id, bool enable)
+{
+    mcpwm->channel[operator_id].tz_cfg0.sw_ost = enable;
+}
+
+static inline void mcpwm_ll_fault_trigger_sw_cbc(mcpwm_dev_t *mcpwm, int operator_id)
+{
+    mcpwm->channel[operator_id].tz_cfg1.force_cbc = ~mcpwm->channel[operator_id].tz_cfg1.force_cbc;
+}
+
+static inline void mcpwm_ll_fault_trigger_sw_oneshot(mcpwm_dev_t *mcpwm, int operator_id)
+{
+    mcpwm->channel[operator_id].tz_cfg1.force_ost = ~mcpwm->channel[operator_id].tz_cfg1.force_ost;
+}
+
+static inline void mcpwm_ll_generator_set_action_on_fault_event(mcpwm_dev_t *mcpwm, int operator_id, int generator_id,
+        mcpwm_timer_direction_t direction, mcpwm_fault_reaction_t reaction, int action)
 {
-    mcpwm->channel[op].carrier_cfg.out_invert = invert;
+    if (direction == MCPWM_TIMER_DIRECTION_UP) {
+        mcpwm->channel[operator_id].tz_cfg0.val &= ~(0x03 << (8 + 8 * generator_id + 4 * reaction + 2));
+        mcpwm->channel[operator_id].tz_cfg0.val |= action << (8 + 8 * generator_id + 4 * reaction + 2);
+    } else if (direction == MCPWM_TIMER_DIRECTION_DOWN) {
+        mcpwm->channel[operator_id].tz_cfg0.val &= ~(0x03 << (8 + 8 * generator_id + 4 * reaction));
+        mcpwm->channel[operator_id].tz_cfg0.val |= action << (8 + 8 * generator_id + 4 * reaction);
+    }
 }
 
-/**
- * Set the oneshot pulse width of the carrier.
- *
- * @param mcpwm Address of the MCPWM peripheral registers.
- * @param op The operator to set, 0-2.
- * @param pulse_width The width of the oneshot pulse, by carrier period. 0 to disable the oneshot pulse.
- */
-static inline void mcpwm_ll_carrier_set_oneshot_width(mcpwm_dev_t *mcpwm, int op, uint8_t pulse_width)
+static inline bool mcpwm_ll_fault_is_ost_on(mcpwm_dev_t *mcpwm, int op)
 {
-    mcpwm->channel[op].carrier_cfg.oshtwth = pulse_width;
+    return mcpwm->channel[op].tz_status.ost_on;
 }
 
-/********************* Capture *******************/
-/**
- * Enable the capture feature for a signal
- *
- * @param mcpwm Address of the MCPWM peripheral registers.
- * @param cap_sig Signal to enable, 0-2.
- * @param enable true to enable, otherwise false.
- */
-static inline void mcpwm_ll_capture_enable(mcpwm_dev_t *mcpwm, int cap_sig, int enable)
+static inline bool mcpwm_ll_fault_is_cbc_on(mcpwm_dev_t *mcpwm, int op)
+{
+    return mcpwm->channel[op].tz_status.cbc_on;
+}
+
+/********************* Capture registers *******************/
+
+static inline void mcpwm_ll_capture_enable_timer(mcpwm_dev_t *mcpwm, bool enable)
+{
+    mcpwm->cap_timer_cfg.timer_en = enable;
+}
+
+static inline void mcpwm_ll_capture_enable_channel(mcpwm_dev_t *mcpwm, int channel, bool enable)
+{
+    mcpwm->cap_cfg_ch[channel].en = enable;
+}
+
+static inline void mcpwm_ll_capture_set_sync_phase(mcpwm_dev_t *mcpwm, uint32_t phase_value)
+{
+    mcpwm->cap_timer_phase = phase_value;
+}
+
+static inline uint32_t mcpwm_ll_capture_get_sync_phase(mcpwm_dev_t *mcpwm)
+{
+    return mcpwm->cap_timer_phase;
+}
+
+static inline void mcpwm_ll_capture_enable_timer_sync(mcpwm_dev_t *mcpwm, bool enable)
+{
+    mcpwm->cap_timer_cfg.synci_en = enable;
+}
+
+static inline void mcpwm_ll_capture_set_internal_timer_syncer(mcpwm_dev_t *mcpwm, int sync_out_timer)
+{
+    mcpwm->cap_timer_cfg.synci_sel = sync_out_timer + 1;
+}
+
+static inline void mcpwm_ll_capture_set_external_syncer(mcpwm_dev_t *mcpwm, int extern_syncer)
+{
+    mcpwm->cap_timer_cfg.synci_sel = extern_syncer + 4;
+}
+
+static inline void mcpwm_ll_capture_trigger_sw_sync(mcpwm_dev_t *mcpwm)
+{
+    mcpwm->cap_timer_cfg.sync_sw = 1; // auto clear
+}
+
+static inline void mcpwm_ll_capture_enable_posedge(mcpwm_dev_t *mcpwm, int channel, bool enable)
+{
+    if (enable) {
+        mcpwm->cap_cfg_ch[channel].val |= 1 << 2;
+    } else {
+        mcpwm->cap_cfg_ch[channel].val &= ~(1 << 2);
+    }
+}
+
+static inline void mcpwm_ll_capture_enable_negedge(mcpwm_dev_t *mcpwm, int channel, bool enable)
 {
     if (enable) {
-        mcpwm->cap_timer_cfg.timer_en = 1;
-        mcpwm->cap_cfg_ch[cap_sig].en = 1;
+        mcpwm->cap_cfg_ch[channel].val |= 1 << 1;
     } else {
-        mcpwm->cap_cfg_ch[cap_sig].en = 0;
+        mcpwm->cap_cfg_ch[channel].val &= ~(1 << 1);
     }
 }
 
-/**
- * Get the captured value.
- *
- * @param mcpwm Address of the MCPWM peripheral registers.
- * @param cap_sig Of which signal to get the captured value.
- * @return The captured value
- */
-static inline uint32_t mcpwm_ll_get_capture_val(mcpwm_dev_t *mcpwm, int cap_sig)
+static inline void mcpwm_ll_invert_input(mcpwm_dev_t *mcpwm, int channel, bool invert)
 {
-    return mcpwm->cap_val_ch[cap_sig];
+    mcpwm->cap_cfg_ch[channel].in_invert = invert;
 }
 
-/**
- * Get the set capture edge.
- *
- * @param mcpwm Address of the MCPWM peripheral registers.
- * @param cap_sig Which signal the edge capture is applied.
- * @return Capture signal edge: 1 - positive edge, 2 - negtive edge
- */
-static inline mcpwm_capture_on_edge_t mcpwm_ll_get_captured_edge(mcpwm_dev_t *mcpwm, int cap_sig)
+static inline void mcpwm_ll_trigger_soft_capture(mcpwm_dev_t *mcpwm, int channel)
 {
-    bool edge;
-    if (cap_sig == 0) {
-        edge = mcpwm->cap_status.cap0_edge;
-    } else if (cap_sig == 1) {
-        edge = mcpwm->cap_status.cap0_edge;
-    } else {   //2
-        edge = mcpwm->cap_status.cap0_edge;
-    }
-    return (edge? MCPWM_NEG_EDGE: MCPWM_POS_EDGE);
+    mcpwm->cap_cfg_ch[channel].sw = 1; // auto clear
 }
 
-STATIC_HAL_REG_CHECK(MCPWM, MCPWM_NEG_EDGE, BIT(0));
-STATIC_HAL_REG_CHECK(MCPWM, MCPWM_POS_EDGE, BIT(1));
+static inline uint32_t mcpwm_ll_capture_get_value(mcpwm_dev_t *mcpwm, int channel)
+{
+    return mcpwm->cap_val_ch[channel];
+}
 
-/**
- * Select the edge to capture.
- *
- * @param mcpwm Address of the MCPWM peripheral registers.
- * @param cap_sig   The signal to capture, 0-2.
- * @param cap_edge  The edge to capture, bitwise.
- */
-static inline void mcpwm_ll_capture_select_edge(mcpwm_dev_t *mcpwm, int cap_sig,
-                                  mcpwm_capture_on_edge_t cap_edge)
+static inline bool mcpwm_ll_capture_is_negedge(mcpwm_dev_t *mcpwm, int channel)
 {
-    mcpwm->cap_cfg_ch[cap_sig].mode = cap_edge;
+    return mcpwm->cap_status.val & (1 << channel) ? true : false;
 }
 
-/**
- * Set the prescale of the input signal to capture.
- *
- * @param mcpwm Address of the MCPWM peripheral registers.
- * @param cap_sig   The prescaled signal to capture, 0-2.
- * @param prescale  Prescal value, 0 to disable.
- */
-static inline void mcpwm_ll_capture_set_prescale(mcpwm_dev_t *mcpwm, int cap_sig, uint32_t prescale)
+static inline void mcpwm_ll_capture_set_prescale(mcpwm_dev_t *mcpwm, int channel, uint32_t prescale)
 {
-    mcpwm->cap_cfg_ch[cap_sig].prescale = prescale;
+    mcpwm->cap_cfg_ch[channel].prescale = prescale - 1;
 }
 
-/**
- * Utility function, get the `mcpwm_intr_t` interrupt enum of a specific capture signal.
- *
- * @param bit x for CAPx.
- * @return the corresponding `mcpwm_intr_t`.
- */
-static inline mcpwm_intr_t mcpwm_ll_get_cap_intr_def(int bit)
+static inline uint32_t mcpwm_ll_capture_get_prescale(mcpwm_dev_t *mcpwm, int channel)
 {
-    return BIT(bit+MCPWM_CAP0_INT_RAW_S);
+    return mcpwm->cap_cfg_ch[channel].prescale + 1;
 }
 
 #ifdef __cplusplus

+ 5 - 292
components/hal/include/hal/mcpwm_hal.h

@@ -1,4 +1,4 @@
-// Copyright 2015-2019 Espressif Systems (Shanghai) PTE LTD
+// Copyright 2015-2021 Espressif Systems (Shanghai) PTE LTD
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -20,309 +20,22 @@
 
 // The HAL layer for MCPWM (common part)
 
-/*
- * MCPWM HAL usages:
- *
- * Initialization:
- *  1. Fill the parameters in `mcpwm_hal_context_t`.
- *  2. Call `mcpwm_hal_init` to initialize the context.
- *  3. Call `mcpwm_hal_hw_init` to initialize the hardware.
- *
- * Basic PWM:
- *  1. Update parameters for the timers, comparators and generators.
- *  2. Call `mcpwm_hal_timer_update_basic` to update the timer used.
- *  3. Call `mcpwm_hal_operator_update_basic` to update all the parameters of a operator.
- *
- *     Alternatively, if only the comparator is updated (duty rate), call
- *     `mcpwm_hal_operator_update_comparator` to update the comparator parameters; if only the
- *     generator is updated (output style), call `mcpwm_hal_operator_update_generator` to update the
- *     generator parameters.
- *
- *  4. At any time, call `mcpwm_hal_timer_start` to start the timer (so that PWM output will toggle
- *    according to settings), or call `mcpwm_hal_timer_stop` to stop the timer (so that the PWM output
- *    will be kept as called).
- *
- * Timer settings:
- *  - Sync: Call `mcpwm_hal_timer_enable_sync` to enable the sync for the timer, and call
- *  `mcpwm_hal_timer_disable_sync` to disable it.
- *
- * Operator settings:
- *  - Carrier: Call `mcpwm_hal_operator_enable_carrier` to enable carrier for an operator, and call
- *  `mcpwm_hal_operator_disable_carrier` to disable it.
- *
- *  - Deadzone: Call `mcpwm_hal_operator_update_deadzone` to update settings of deadzone for an operator.
- *
- * Fault handling settings:
- *  1. Call `mcpwm_hal_fault_init` to initialize an fault signal to be detected.
- *  2. Call `mcpwm_hal_operator_update_fault` to update the behavior of an operator when fault is
- *     detected.
- *  3. If the operator selects oneshot mode to handle the fault event, call
- *     `mcpwm_hal_fault_oneshot_clear` to clear that fault event after the fault is handled properly.
- *  4. Call `mcpwm_hal_fault_disable` to deinitialize the fault signal when it's no longer used.
- *
- * Capture:
- *  1. Call `mcpwm_hal_capture_enable` to enable the capture for one capture signal.
- *  2. Call `mcpwm_hal_capture_get_result` to get the last captured result.
- *  3. Call `mcpwm_hal_capture_disable` to disable the capture for a signal.
- */
-
-
 #pragma once
 
-#include <esp_err.h>
-#include "hal/mcpwm_ll.h"
-
-#define MCPWM_BASE_CLK (2 * APB_CLK_FREQ)   //2*APB_CLK_FREQ 160Mhz
+#include "soc/mcpwm_struct.h"
 
-/// Configuration of HAL that used only once.
 typedef struct {
-    int host_id;    ///< Which MCPWM peripheral to use, 0-1.
+    int host_id; ///< Which MCPWM peripheral to use, 0-1.
 } mcpwm_hal_init_config_t;
 
-/// Configuration of each generator (output of operator)
-typedef struct {
-    mcpwm_duty_type_t   duty_type;  ///< How the generator output
-    int comparator; ///< for mode `MCPWM_DUTY_MODE_*`, which comparator it refers to.
-} mcpwm_hal_generator_config_t;
-
-/// Configuration of each operator
-typedef struct {
-    mcpwm_hal_generator_config_t gen[SOC_MCPWM_GENERATORS_PER_OPERATOR];  ///< Configuration of the generators
-    float duty[SOC_MCPWM_COMPARATORS_PER_OPERATOR];   ///< Duty rate for each comparator, 10 means 10%.
-    int timer;      ///< The timer this operator is using
-} mcpwm_hal_operator_config_t;
-
-/// Configuration of each timer
 typedef struct {
-    uint32_t timer_prescale;    ///< The prescale from the MCPWM main clock to the timer clock, TIMER_FREQ=(MCPWM_FREQ/(timer_prescale+1))
-    uint32_t freq;              ///< Frequency desired, will be updated to actual value after the `mcpwm_hal_timer_update_freq` is called.
-    mcpwm_counter_type_t count_mode;    ///< Counting mode
-} mcpwm_hal_timer_config_t;
-
-typedef struct {
-    mcpwm_dev_t *dev;           ///< Beginning address of the MCPWM peripheral registers. Call `mcpwm_hal_init` to initialize it.
-    uint32_t    prescale;       ///< Prescale from the 160M clock to MCPWM main clock.
-    mcpwm_hal_timer_config_t    timer[SOC_MCPWM_TIMERS_PER_GROUP]; ///< Configuration of the timers
-    mcpwm_hal_operator_config_t op[SOC_MCPWM_OPERATORS_PER_GROUP];       ///< Configuration of the operators
+    mcpwm_dev_t *dev; ///< Beginning address of the peripheral registers of a single MCPWM unit. Call `mcpwm_hal_init` to initialize it.
 } mcpwm_hal_context_t;
 
-/// Configuration of the carrier
-typedef struct {
-    bool inverted;  ///< Whether to invert the output
-    uint8_t  duty;  ///< Duty of the carrier, 0-7. Duty rate = duty/8.
-    uint8_t oneshot_pulse_width;    ///< oneshot pulse width, in carrier periods. 0 to disable. 0-15.
-    uint32_t period; ///< Prescale from the MCPWM main clock to the carrier clock. CARRIER_FREQ=(MCPWM_FREQ/(period+1)/8.)
-} mcpwm_hal_carrier_conf_t;
-
-/// Configuration of the deadzone
-typedef struct {
-    mcpwm_deadtime_type_t mode; ///< Deadzone mode, `MCPWM_DEADTIME_BYPASS` to disable.
-    uint32_t fed;   ///< Delay on falling edge. By MCPWM main clock.
-    uint32_t red;   ///< Delay on rising edge. By MCPWM main clock.
-} mcpwm_hal_deadzone_conf_t;
-
-/// Configuration of the fault handling for each operator
-typedef struct {
-    uint32_t cbc_enabled_mask;  ///< Whether the cycle-by-cycle fault handling is enabled on each fault signal. BIT(n) stands for signal n.
-    uint32_t ost_enabled_mask;  ///< Whether the oneshot fault handling is enabled on each on each fault signal. BIT(n) stands for signal n.
-    mcpwm_output_action_t   action_on_fault[SOC_MCPWM_GENERATORS_PER_OPERATOR];   ///< Action to perform on each generator when any one of the fault signal triggers.
-} mcpwm_hal_fault_conf_t;
-
-/// Configuration of the synchronization of each clock
-typedef struct {
-    mcpwm_sync_signal_t sync_sig;   ///< Sync signal to use
-    uint32_t reload_permillage;     ///< Reload permillage when the sync is triggered. 100 means the timer will be reload to (period * 100)/1000=10% period value.
-} mcpwm_hal_sync_config_t;
-
-/// Configuration of the capture feature on each capture signal
-typedef struct {
-    mcpwm_capture_on_edge_t cap_edge;   ///< Whether the edges is captured, bitwise.
-    uint32_t prescale;          ///< Prescale of the input signal.
-} mcpwm_hal_capture_config_t;
-
 /**
- * @brief Initialize the internal state of the HAL. Call after settings are set and before other functions are called.
- *
- * @note Since There are several individual parts (timers + operators, captures), this funciton is
- *  allowed to called several times.
+ * @brief Initialize the internal state of the HAL.
  *
  * @param hal Context of the HAL layer.
  * @param init_config Configuration for the HAL to be used only once.
  */
 void mcpwm_hal_init(mcpwm_hal_context_t *hal, const mcpwm_hal_init_config_t *init_config);
-
-/**
- * @brief Initialize the hardware, call after `mcpwm_hal_init` and before other functions.
- *
- * @param hal Context of the HAL layer.
- */
-void mcpwm_hal_hw_init(mcpwm_hal_context_t *hal);
-
-/**
- * @brief Start a timer
- *
- * @param hal Context of the HAL layer.
- * @param timer Timer to start, 0-2.
- */
-void mcpwm_hal_timer_start(mcpwm_hal_context_t *hal, int timer);
-
-/**
- * @brief Stop a timer.
- *
- * @param hal Context of the HAL layer.
- * @param timer Timer to stop, 0-2.
- */
-void mcpwm_hal_timer_stop(mcpwm_hal_context_t *hal, int timer);
-
-/**
- * @brief Update the basic parameters of a timer.
- *
- * @note This will influence the duty rate and count mode of each operator relies on this timer.
- *       Call `mcpwm_hal_operator_update_basic` for each of the operator that relies on this timer after
- *       to update the duty rate and generator output.
- *
- * @param hal Context of the HAL layer.
- * @param timer Timer to update, 0-2.
- */
-void mcpwm_hal_timer_update_basic(mcpwm_hal_context_t *hal, int timer);
-
-/**
- * @brief Start the synchronization for a timer.
- *
- * @param hal Context of the HAL layer.
- * @param timer Timer to enable, 0-2.
- * @param sync_conf Configuration of the sync operation.
- */
-void mcpwm_hal_timer_enable_sync(mcpwm_hal_context_t *hal, int timer, const mcpwm_hal_sync_config_t *sync_conf);
-
-/**
- * @brief Stop the synchronization for a timer.
- *
- * @param hal Context of the HAL layer.
- * @param timer Timer to disable sync, 0-2.
- */
-void mcpwm_hal_timer_disable_sync(mcpwm_hal_context_t *hal, int timer);
-
-/**
- * @brief Update the basic settings (duty, output mode) for an operator.
- *
- * Will call `mcpwm_hal_operator_update_comparator` and `mcpwm_hal_operator_update_generator`
- * recursively to update each of their duty and output mode.
- *
- * @param hal Context of the HAL layer.
- * @param op Operator to update, 0-2.
- */
-void mcpwm_hal_operator_update_basic(mcpwm_hal_context_t *hal, int op);
-
-/**
- * @brief Update a comparator (duty) for an operator.
- *
- * @param hal Context of the HAL layer.
- * @param op Operator to update, 0-2.
- * @param cmp Comparator to update, 0-1.
- */
-void mcpwm_hal_operator_update_comparator(mcpwm_hal_context_t *hal, int op, int cmp);
-
-/**
- * @brief Update a generator (output mode) for an operator.
- *
- * @param hal Context of the HAL layer.
- * @param op Operator to update, 0-2.
- * @param cmp Comparator to update, 0-1.
- */
-void mcpwm_hal_operator_update_generator(mcpwm_hal_context_t *hal, int op, int gen_num);
-
-/**
- * @brief Enable the carrier for an operator.
- *
- * @param hal Context of the HAL layer.
- * @param op Operator to enable carrier, 0-2.
- * @param carrier_conf Configuration of the carrier.
- */
-void mcpwm_hal_operator_enable_carrier(mcpwm_hal_context_t *hal, int op, const mcpwm_hal_carrier_conf_t *carrier_conf);
-
-/**
- * @brief Disable the carrier for an operator.
- *
- * @param hal Context of the HAL layer.
- * @param op  Operator to disable carrier, 0-2.
- */
-void mcpwm_hal_operator_disable_carrier(mcpwm_hal_context_t *hal, int op);
-
-/**
- * @brief Update the deadzone for an operator.
- *
- * @param hal Context of the HAL layer.
- * @param op Operator to update the deadzone, 0-2.
- * @param deadzone Configuration of the deadzone. Set member `mode` to `MCPWM_DEADTIME_BYPASS` will bypass the deadzone.
- */
-void mcpwm_hal_operator_update_deadzone(mcpwm_hal_context_t *hal, int op, const mcpwm_hal_deadzone_conf_t *deadzone);
-
-/**
- * @brief Enable one of the fault signal.
- *
- * @param hal Context of the HAL layer.
- * @param fault_sig The signal to enable, 0-2.
- * @param level The active level for the fault signal, true for high and false for low.
- */
-void mcpwm_hal_fault_init(mcpwm_hal_context_t *hal, int fault_sig, bool level);
-
-/**
- * @brief Configure how the operator behave to the fault signals.
- *
- * Call after the fault signal is enabled by `mcpwm_hal_fault_init`.
- *
- * @param hal Context of the HAL layer.
- * @param op Operator to configure, 0-2.
- * @param fault_conf Configuration of the behavior of the operator when fault. Clear member `cbc_enabled_mask` and `ost_enabled_mask` will disable the fault detection of this operator.
- */
-void mcpwm_hal_operator_update_fault(mcpwm_hal_context_t *hal, int op, const mcpwm_hal_fault_conf_t *fault_conf);
-
-/**
- * @brief Clear the oneshot fault status for an operator.
- *
- * @param hal Context of the HAL layer.
- * @param op The operator to clear oneshot fault status, 0-2.
- */
-void mcpwm_hal_fault_oneshot_clear(mcpwm_hal_context_t *hal, int op);
-
-/**
- * @brief Disable one of the fault signal.
- *
- * @param hal Context of the HAL layer.
- * @param fault_sig The fault signal to disable, 0-2.
- */
-void mcpwm_hal_fault_disable(mcpwm_hal_context_t *hal, int fault_sig);
-
-/**
- * @brief Enable one of the capture signal.
- *
- * @param hal Context of the HAL layer.
- * @param cap_sig Capture signal to enable, 0-2.
- * @param conf Configuration on how to capture the signal.
- */
-void mcpwm_hal_capture_enable(mcpwm_hal_context_t *hal, int cap_sig, const mcpwm_hal_capture_config_t *conf);
-
-/**
- * @brief Get the capture result.
- *
- * @note The output value will always be updated with the register value, no matter event triggered or not.
- *
- * @param hal Context of the HAL layer.
- * @param cap_sig Signal to get capture result, 0-2.
- * @param out_count Output of the captured counter.
- * @param out_edge  Output of the captured edge.
- * @return
- *  - ESP_OK: if a signal is captured
- *  - ESP_ERR_NOT_FOUND: if no capture event happened.
- */
-esp_err_t mcpwm_hal_capture_get_result(mcpwm_hal_context_t *hal, int cap_sig, uint32_t *out_count,
-                                       mcpwm_capture_on_edge_t *out_edge);
-
-/**
- * @brief Disable one of the capture signal.
- *
- * @param hal Context of the HAL layer.
- * @param cap_sig The signal to capture, 0-2.
- */
-void mcpwm_hal_capture_disable(mcpwm_hal_context_t *hal, int cap_sig);

+ 26 - 61
components/hal/include/hal/mcpwm_types.h

@@ -1,4 +1,4 @@
-// Copyright 2015-2019 Espressif Systems (Shanghai) PTE LTD
+// Copyright 2015-2021 Espressif Systems (Shanghai) PTE LTD
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -14,74 +14,39 @@
 
 #pragma once
 
-/// Interrupts for MCPWM
 typedef enum {
-    MCPWM_LL_INTR_CAP0 = BIT(27), ///< Capture 0 happened
-    MCPWM_LL_INTR_CAP1 = BIT(28), ///< Capture 1 happened
-    MCPWM_LL_INTR_CAP2 = BIT(29), ///< Capture 2 happened
-} mcpwm_intr_t;
+    MCPWM_TIMER_DIRECTION_UP,   /*!< Counting direction: Increase */
+    MCPWM_TIMER_DIRECTION_DOWN, /*!< Counting direction: Decrease */
+} mcpwm_timer_direction_t;
 
-/**
- * @brief Select type of MCPWM counter
- */
 typedef enum {
-    MCPWM_UP_COUNTER = 1,   /*!<For asymmetric MCPWM*/
-    MCPWM_DOWN_COUNTER,     /*!<For asymmetric MCPWM*/
-    MCPWM_UP_DOWN_COUNTER,  /*!<For symmetric MCPWM, frequency is half of MCPWM frequency set*/
-    MCPWM_COUNTER_MAX,      /*!<Maximum counter mode*/
-} mcpwm_counter_type_t;
+    MCPWM_TIMER_EVENT_ZERO, /*!< MCPWM timer counts to zero */
+    MCPWM_TIMER_EVENT_PEAK, /*!< MCPWM timer counts to peak */
+} mcpwm_timer_event_t;
 
-/**
- * @brief Select type of MCPWM duty cycle mode
- */
 typedef enum {
-    MCPWM_DUTY_MODE_0 = 0, /*!<Active high duty, i.e. duty cycle proportional to high time for asymmetric MCPWM*/
-    MCPWM_DUTY_MODE_1,     /*!<Active low duty,  i.e. duty cycle proportional to low  time for asymmetric MCPWM, out of phase(inverted) MCPWM*/
-    MCPWM_HAL_GENERATOR_MODE_FORCE_LOW,
-    MCPWM_HAL_GENERATOR_MODE_FORCE_HIGH,
-    MCPWM_DUTY_MODE_MAX,   /*!<Num of duty cycle modes*/
-} mcpwm_duty_type_t;
+    MCPWM_TIMER_COUNT_MODE_PAUSE,   /*!< MCPWM timer paused */
+    MCPWM_TIMER_COUNT_MODE_UP,      /*!< MCPWM timer counting up */
+    MCPWM_TIMER_COUNT_MODE_DOWN,    /*!< MCPWM timer counting down */
+    MCPWM_TIMER_COUNT_MODE_UP_DOWN, /*!< MCPWM timer counting up and down */
+} mcpwm_timer_count_mode_t;
 
-/**
- * @brief MCPWM select action to be taken on the output when event happens
- */
 typedef enum {
-    MCPWM_ACTION_NO_CHANGE = 0,  /*!<No change in the output*/
-    MCPWM_ACTION_FORCE_LOW,      /*!<Make output low*/
-    MCPWM_ACTION_FORCE_HIGH,     /*!<Make output high*/
-    MCPWM_ACTION_TOGGLE,         /*!<Make output toggle*/
-} mcpwm_output_action_t;
+    MCPWM_TIMER_STOP_AT_ZERO,       /*!< MCPWM timer stops when couting to zero */
+    MCPWM_TIMER_STOP_AT_PEAK,       /*!< MCPWM timer stops when counting to peak */
+    MCPWM_TIMER_START_NO_STOP,      /*!< MCPWM timer starts couting */
+    MCPWM_TIMER_START_STOP_AT_ZERO, /*!< MCPWM timer starts counting and stops when couting to zero */
+    MCPWM_TIMER_START_STOP_AT_PEAK, /*!< MCPWM timer starts counting and stops when counting to peak */
+} mcpwm_timer_operate_cmd_t;
 
-/**
- * @brief MCPWM deadtime types, used to generate deadtime, RED refers to rising edge delay and FED refers to falling edge delay
- */
 typedef enum {
-    MCPWM_DEADTIME_BYPASS = 0,          /*!<Bypass the deadtime*/
-    MCPWM_BYPASS_RED,                   /*!<MCPWMXA = no change, MCPWMXB = falling edge delay*/
-    MCPWM_BYPASS_FED,                   /*!<MCPWMXA = rising edge delay, MCPWMXB = no change*/
-    MCPWM_ACTIVE_HIGH_MODE,             /*!<MCPWMXA = rising edge delay,  MCPWMXB = falling edge delay*/
-    MCPWM_ACTIVE_LOW_MODE,              /*!<MCPWMXA = compliment of rising edge delay,  MCPWMXB = compliment of falling edge delay*/
-    MCPWM_ACTIVE_HIGH_COMPLIMENT_MODE,  /*!<MCPWMXA = rising edge delay,  MCPWMXB = compliment of falling edge delay*/
-    MCPWM_ACTIVE_LOW_COMPLIMENT_MODE,   /*!<MCPWMXA = compliment of rising edge delay,  MCPWMXB = falling edge delay*/
-    MCPWM_ACTIVE_RED_FED_FROM_PWMXA,    /*!<MCPWMXA = MCPWMXB = rising edge delay as well as falling edge delay, generated from MCPWMXA*/
-    MCPWM_ACTIVE_RED_FED_FROM_PWMXB,    /*!<MCPWMXA = MCPWMXB = rising edge delay as well as falling edge delay, generated from MCPWMXB*/
-    MCPWM_DEADTIME_TYPE_MAX,
-} mcpwm_deadtime_type_t;
+    MCPWM_GEN_ACTION_KEEP,   /*!< Generator action: Keep the same level */
+    MCPWM_GEN_ACTION_LOW,    /*!< Generator action: Force to low level */
+    MCPWM_GEN_ACTION_HIGH,   /*!< Generator action: Force to high level */
+    MCPWM_GEN_ACTION_TOGGLE, /*!< Generator action: Toggle level */
+} mcpwm_generator_action_t;
 
-/**
- * @brief MCPWM select sync signal input
- */
 typedef enum {
-    MCPWM_SELECT_SYNC0 = 4,  /*!<Select SYNC0 as input*/
-    MCPWM_SELECT_SYNC1,      /*!<Select SYNC1 as input*/
-    MCPWM_SELECT_SYNC2,      /*!<Select SYNC2 as input*/
-} mcpwm_sync_signal_t;
-
-/**
- * @brief MCPWM select capture starts from which edge
- */
-typedef enum {
-    MCPWM_NEG_EDGE = BIT(0),          /*!<Capture the negative edge*/
-    MCPWM_POS_EDGE = BIT(1),          /*!<Capture the positive edge*/
-    MCPWM_BOTH_EDGE = BIT(1)|BIT(0),  /*!<Capture both edges*/
-} mcpwm_capture_on_edge_t;
+    MCPWM_FAULT_REACTION_CBC, /*!< Reaction on fault signal: recover cycle by cycle */
+    MCPWM_FAULT_REACTION_OST, /*!< Reaction on fault signal: one shot trip */
+} mcpwm_fault_reaction_t;

+ 1 - 208
components/hal/mcpwm_hal.c

@@ -15,216 +15,9 @@
 // The HAL layer for MCPWM (common part)
 
 #include "hal/mcpwm_hal.h"
-#include "soc/soc_caps.h"
+#include "hal/mcpwm_ll.h"
 
 void mcpwm_hal_init(mcpwm_hal_context_t *hal, const mcpwm_hal_init_config_t *init_config)
 {
     hal->dev = MCPWM_LL_GET_HW(init_config->host_id);
 }
-
-void mcpwm_hal_hw_init(mcpwm_hal_context_t *hal)
-{
-    mcpwm_ll_init(hal->dev);
-    mcpwm_ll_set_clock_prescale(hal->dev, hal->prescale);
-}
-
-void mcpwm_hal_timer_start(mcpwm_hal_context_t *hal, int timer)
-{
-    mcpwm_ll_timer_start(hal->dev, timer);
-}
-
-void mcpwm_hal_timer_stop(mcpwm_hal_context_t *hal, int timer)
-{
-    mcpwm_ll_timer_stop(hal->dev, timer);
-}
-
-void mcpwm_hal_timer_update_basic(mcpwm_hal_context_t *hal, int timer)
-{
-    mcpwm_ll_timer_set_prescale(hal->dev, timer, hal->timer[timer].timer_prescale);
-
-    uint32_t period = MCPWM_BASE_CLK / (hal->timer[timer].freq *
-                                        (hal->prescale + 1) * (hal->timer[timer].timer_prescale + 1));
-    mcpwm_ll_timer_set_period(hal->dev, timer, period);
-    //write back the actual value to the context
-    hal->timer[timer].freq = MCPWM_BASE_CLK / (period *
-                             (hal->prescale + 1) * (hal->timer[timer].timer_prescale + 1));
-
-    mcpwm_ll_timer_set_count_mode(hal->dev, timer, hal->timer[timer].count_mode);
-}
-
-void mcpwm_hal_timer_enable_sync(mcpwm_hal_context_t *hal, int timer, const mcpwm_hal_sync_config_t *sync_conf)
-{
-    uint32_t set_phase = mcpwm_ll_timer_get_period(hal->dev, timer) * sync_conf->reload_permillage / 1000;
-    mcpwm_ll_sync_set_phase(hal->dev, timer, set_phase);
-
-    mcpwm_ll_sync_set_input(hal->dev, timer, sync_conf->sync_sig);
-    mcpwm_ll_sync_enable(hal->dev, timer, 1);
-}
-
-void mcpwm_hal_timer_disable_sync(mcpwm_hal_context_t *hal, int timer)
-{
-    mcpwm_ll_sync_enable(hal->dev, timer, 0);
-}
-
-void mcpwm_hal_operator_update_basic(mcpwm_hal_context_t *hal, int op)
-{
-    mcpwm_hal_operator_config_t *op_conf = &hal->op[op];
-    mcpwm_ll_operator_select_timer(hal->dev, op, op_conf->timer);
-    for (int cmp = 0; cmp < SOC_MCPWM_COMPARATORS_PER_OPERATOR; cmp++) {
-        mcpwm_hal_operator_update_comparator(hal, op, cmp);
-    }
-    for (int gen = 0; gen < SOC_MCPWM_GENERATORS_PER_OPERATOR; gen++) {
-        mcpwm_hal_operator_update_generator(hal, op, gen);
-    }
-}
-
-void mcpwm_hal_operator_update_comparator(mcpwm_hal_context_t *hal, int op, int cmp)
-{
-    int timer = hal->op[op].timer;
-    uint32_t period = mcpwm_ll_timer_get_period(hal->dev, timer);
-    mcpwm_ll_operator_set_compare(hal->dev, op, cmp, (hal->op[op].duty[cmp] * period) / 100);
-    mcpwm_ll_operator_set_compare_upmethod(hal->dev, timer);
-}
-
-void mcpwm_hal_operator_update_generator(mcpwm_hal_context_t *hal, int op, int gen_num)
-{
-    mcpwm_hal_generator_config_t *gen_config = &(hal->op[op].gen[gen_num]);
-    if (gen_config->duty_type == MCPWM_HAL_GENERATOR_MODE_FORCE_HIGH) {
-        mcpwm_ll_gen_set_zero_action(hal->dev, op, gen_num, MCPWM_ACTION_FORCE_HIGH);
-        mcpwm_ll_gen_set_period_action(hal->dev, op, gen_num, MCPWM_ACTION_FORCE_HIGH);
-        mcpwm_ll_gen_set_cmp_action(hal->dev, op, gen_num, 0, MCPWM_ACTION_FORCE_HIGH, MCPWM_ACTION_FORCE_HIGH);
-        mcpwm_ll_gen_set_cmp_action(hal->dev, op, gen_num, 1, MCPWM_ACTION_FORCE_HIGH, MCPWM_ACTION_FORCE_HIGH);
-    } else if (gen_config->duty_type == MCPWM_HAL_GENERATOR_MODE_FORCE_LOW) {
-        mcpwm_ll_gen_set_zero_action(hal->dev, op, gen_num, MCPWM_ACTION_FORCE_LOW);
-        mcpwm_ll_gen_set_period_action(hal->dev, op, gen_num, MCPWM_ACTION_FORCE_LOW);
-        mcpwm_ll_gen_set_cmp_action(hal->dev, op, gen_num, 0, MCPWM_ACTION_FORCE_LOW, MCPWM_ACTION_FORCE_LOW);
-        mcpwm_ll_gen_set_cmp_action(hal->dev, op, gen_num, 1, MCPWM_ACTION_FORCE_LOW, MCPWM_ACTION_FORCE_LOW);
-    } else if (gen_config->duty_type == MCPWM_DUTY_MODE_1 || gen_config->duty_type == MCPWM_DUTY_MODE_0) {
-        const int timer_used = hal->op[op].timer;
-        const int cmp_used = gen_config->comparator;
-        const int cmp_not_used = 1 - gen_config->comparator;
-
-        mcpwm_output_action_t inactive_action;
-        mcpwm_output_action_t active_action;
-        const mcpwm_output_action_t no_action = MCPWM_ACTION_NO_CHANGE;
-        if (gen_config->duty_type == MCPWM_DUTY_MODE_1) {
-            active_action = MCPWM_ACTION_FORCE_LOW;      //active low
-            inactive_action = MCPWM_ACTION_FORCE_HIGH;    //inactive high
-        } else {   //MCPWM_DUTY_MODE_0
-            inactive_action = MCPWM_ACTION_FORCE_LOW;    //inactive low
-            active_action = MCPWM_ACTION_FORCE_HIGH;      //active high
-        }
-
-        if (hal->timer[timer_used].count_mode == MCPWM_UP_COUNTER) {
-            mcpwm_ll_gen_set_zero_action(hal->dev, op, gen_num, active_action);
-            mcpwm_ll_gen_set_period_action(hal->dev, op, gen_num, no_action);
-            mcpwm_ll_gen_set_cmp_action(hal->dev, op, gen_num, cmp_used, inactive_action, no_action);
-        } else if (hal->timer[timer_used].count_mode == MCPWM_DOWN_COUNTER) {
-            mcpwm_ll_gen_set_zero_action(hal->dev, op, gen_num, no_action);
-            mcpwm_ll_gen_set_period_action(hal->dev, op, gen_num, inactive_action);
-            mcpwm_ll_gen_set_cmp_action(hal->dev, op, gen_num, cmp_used, no_action, active_action);
-        } else {   //Timer count up-down
-            mcpwm_ll_gen_set_zero_action(hal->dev, op, gen_num, active_action);
-            mcpwm_ll_gen_set_period_action(hal->dev, op, gen_num, no_action);
-            mcpwm_ll_gen_set_cmp_action(hal->dev, op, gen_num, cmp_used, inactive_action, active_action);
-        }
-        mcpwm_ll_gen_set_cmp_action(hal->dev, op, gen_num, cmp_not_used, no_action, no_action);
-    }
-}
-
-void mcpwm_hal_operator_enable_carrier(mcpwm_hal_context_t *hal, int op, const mcpwm_hal_carrier_conf_t *carrier_conf)
-{
-    mcpwm_ll_carrier_init(hal->dev, op);
-    mcpwm_ll_carrier_set_prescale(hal->dev, op, carrier_conf->period);
-    mcpwm_ll_carrier_set_duty(hal->dev, op, carrier_conf->duty);
-    mcpwm_ll_carrier_enable(hal->dev, op, true);
-    mcpwm_ll_carrier_set_oneshot_width(hal->dev, op, carrier_conf->oneshot_pulse_width);
-    mcpwm_ll_carrier_out_invert(hal->dev, op, carrier_conf->inverted);
-}
-
-void mcpwm_hal_operator_disable_carrier(mcpwm_hal_context_t *hal, int op)
-{
-    mcpwm_ll_carrier_enable(hal->dev, op, false);
-}
-
-void mcpwm_hal_operator_update_deadzone(mcpwm_hal_context_t *hal, int op, const mcpwm_hal_deadzone_conf_t *deadzone)
-{
-    if (deadzone->mode != MCPWM_DEADTIME_BYPASS) {
-        mcpwm_ll_deadtime_init(hal->dev, op);
-        mcpwm_ll_deadtime_set_rising_delay(hal->dev, op, deadzone->red);
-        mcpwm_ll_deadtime_set_falling_delay(hal->dev, op, deadzone->fed);
-        mcpwm_ll_set_deadtime_mode(hal->dev, op, deadzone->mode);
-    } else {
-        mcpwm_ll_deadtime_bypass(hal->dev, op);
-    }
-}
-
-void mcpwm_hal_fault_init(mcpwm_hal_context_t *hal, int fault_sig, bool level)
-{
-    mcpwm_ll_fault_enable(hal->dev, fault_sig, level);
-}
-
-void mcpwm_hal_operator_update_fault(mcpwm_hal_context_t *hal, int op, const mcpwm_hal_fault_conf_t *fault_conf)
-{
-    for (int fault_sig = 0; fault_sig < SOC_MCPWM_FAULT_DETECTORS_PER_GROUP; fault_sig++) {
-        bool enabled = (fault_conf->cbc_enabled_mask & BIT(fault_sig)) ? true : false;
-        mcpwm_ll_fault_cbc_enable_signal(hal->dev, op, fault_sig, enabled);
-    }
-    for (int fault_sig = 0; fault_sig < SOC_MCPWM_FAULT_DETECTORS_PER_GROUP; fault_sig++) {
-        bool enabled = (fault_conf->ost_enabled_mask & BIT(fault_sig)) ? true : false;
-        mcpwm_ll_fault_oneshot_enable_signal(hal->dev, op, fault_sig, enabled);
-    }
-    if (fault_conf->cbc_enabled_mask) {
-        for (int gen = 0; gen < SOC_MCPWM_GENERATORS_PER_OPERATOR; gen++) {
-            mcpwm_ll_fault_set_cyc_action(hal->dev, op, gen, fault_conf->action_on_fault[gen], fault_conf->action_on_fault[gen]);
-        }
-    }
-    if (fault_conf->ost_enabled_mask) {
-        for (int gen = 0; gen < SOC_MCPWM_GENERATORS_PER_OPERATOR; gen++) {
-            mcpwm_ll_fault_set_oneshot_action(hal->dev, op, gen, fault_conf->action_on_fault[gen], fault_conf->action_on_fault[gen]);
-        }
-    }
-}
-
-void mcpwm_hal_fault_oneshot_clear(mcpwm_hal_context_t *hal, int op)
-{
-    mcpwm_ll_fault_clear_ost(hal->dev, op);
-}
-
-void mcpwm_hal_fault_disable(mcpwm_hal_context_t *hal, int fault_sig)
-{
-    for (int op = 0; op < SOC_MCPWM_OPERATORS_PER_GROUP; op++) {
-        if (mcpwm_ll_fault_oneshot_signal_enabled(hal->dev, op, fault_sig)) {
-            mcpwm_ll_fault_clear_ost(hal->dev, op);
-        }
-    }
-    mcpwm_ll_fault_disable(hal->dev, fault_sig);
-}
-
-void mcpwm_hal_capture_enable(mcpwm_hal_context_t *hal, int cap_sig, const mcpwm_hal_capture_config_t *conf)
-{
-    mcpwm_ll_capture_enable(hal->dev, cap_sig, 1);
-    mcpwm_ll_capture_select_edge(hal->dev, cap_sig, conf->cap_edge);
-    mcpwm_ll_capture_set_prescale(hal->dev, cap_sig, conf->prescale);
-}
-
-esp_err_t mcpwm_hal_capture_get_result(mcpwm_hal_context_t *hal, int cap_sig, uint32_t *out_count, mcpwm_capture_on_edge_t *out_edge)
-{
-    const mcpwm_intr_t sig_intr = mcpwm_ll_get_cap_intr_def(cap_sig);
-    //unconditionally update the output value
-    if (out_edge) {
-        *out_edge = mcpwm_ll_get_captured_edge(hal->dev, cap_sig);
-    }
-    if (out_count) {
-        *out_count = mcpwm_ll_get_capture_val(hal->dev, cap_sig);
-    }
-    if (mcpwm_ll_get_intr(hal->dev) & sig_intr) {
-        mcpwm_ll_clear_intr(hal->dev, sig_intr);
-    }
-    return (mcpwm_ll_get_intr(hal->dev) & sig_intr ? ESP_OK : ESP_ERR_NOT_FOUND);
-}
-
-void mcpwm_hal_capture_disable(mcpwm_hal_context_t *hal, int cap_sig)
-{
-    mcpwm_ll_capture_enable(hal->dev, cap_sig, 0);
-}

+ 3 - 2
components/soc/esp32s3/include/soc/mcpwm_struct.h

@@ -40,7 +40,7 @@ typedef volatile struct {
         union {
             struct {
                 uint32_t start                         :    3;  /*0: stop @ eqz, 1: stop @ eqp, 2: free run, 3: start and stop @ next eqz, 4: start and stop @ next eqp,*/
-                uint32_t mod                           :    2;  /* 0: freeze, 1: inc, 2: dec, 3: up-down*/
+                uint32_t mode                          :    2;  /* 0: freeze, 1: inc, 2: dec, 3: up-down*/
                 uint32_t reserved5                     :    27;
             };
             uint32_t val;
@@ -50,7 +50,8 @@ typedef volatile struct {
                 uint32_t in_en                         :    1;
                 uint32_t sync_sw                       :    1;  /*write the negate value will trigger a sw sync*/
                 uint32_t out_sel                       :    2;
-                uint32_t phase                         :    17;
+                uint32_t timer_phase                   :    16; /*phase for timer reload on sync event*/
+                uint32_t phase_direct                  :    1;  /*counter direction to apply on sync event*/
                 uint32_t reserved21                    :    11;
             };
             uint32_t val;

+ 1 - 3
docs/en/api-reference/peripherals/mcpwm.rst

@@ -1,9 +1,7 @@
 MCPWM
 =====
 
-.. This peripheral is ESP32 only
-
-ESP32 has two MCPWM units which can be used to control different types of motors. Each unit has three pairs of PWM outputs.
+{IDF_TARGET_NAME} has two MCPWM units which can be used to control different types of motors. Each unit has three pairs of PWM outputs.
 
 .. figure:: ../../../_static/mcpwm-overview.png
     :align: center