Quellcode durchsuchen

Merge branch 'feature/esp32p4_mcpwm_support' into 'master'

feat(MCPWM): Add support for ESP32P4

Closes IDF-7493

See merge request espressif/esp-idf!25029
morris vor 2 Jahren
Ursprung
Commit
bd8eaf7a38

+ 1 - 33
components/driver/Kconfig

@@ -368,39 +368,7 @@ menu "Driver Configurations"
                 Note that, this option only controls the RMT driver log, won't affect other drivers.
     endmenu # RMT Configuration
 
-    menu "MCPWM Configuration"
-        depends on SOC_MCPWM_SUPPORTED
-        config MCPWM_ISR_IRAM_SAFE
-            bool "Place MCPWM ISR function into IRAM"
-            default n
-            help
-                This will ensure the MCPWM interrupt handle is IRAM-Safe, allow to avoid flash
-                cache misses, and also be able to run whilst the cache is disabled.
-                (e.g. SPI Flash write)
-
-        config MCPWM_CTRL_FUNC_IN_IRAM
-            bool "Place MCPWM control functions into IRAM"
-            default n
-            help
-                Place MCPWM control functions (like set_compare_value) into IRAM,
-                so that these functions can be IRAM-safe and able to be called in the other IRAM interrupt context.
-                Enabling this option can improve driver performance as well.
-
-        config MCPWM_SUPPRESS_DEPRECATE_WARN
-            bool "Suppress leagcy driver deprecated warning"
-            default n
-            help
-                Wether to suppress the deprecation warnings when using legacy MCPWM driver (driver/mcpwm.h).
-                If you want to continue using the legacy driver, and don't want to see related deprecation warnings,
-                you can enable this option.
-
-        config MCPWM_ENABLE_DEBUG_LOG
-            bool "Enable debug log"
-            default n
-            help
-                Wether to enable the debug log message for MCPWM driver.
-                Note that, this option only controls the MCPWM driver log, won't affect other drivers.
-    endmenu # MCPWM Configuration
+    orsource "./mcpwm/Kconfig.mcpwm"
 
     menu "I2S Configuration"
         depends on SOC_I2S_SUPPORTED

+ 33 - 0
components/driver/mcpwm/Kconfig.mcpwm

@@ -0,0 +1,33 @@
+menu "MCPWM Configuration"
+    depends on SOC_MCPWM_SUPPORTED
+    config MCPWM_ISR_IRAM_SAFE
+        bool "Place MCPWM ISR function into IRAM"
+        default n
+        help
+            This will ensure the MCPWM interrupt handle is IRAM-Safe, allow to avoid flash
+            cache misses, and also be able to run whilst the cache is disabled.
+            (e.g. SPI Flash write)
+
+    config MCPWM_CTRL_FUNC_IN_IRAM
+        bool "Place MCPWM control functions into IRAM"
+        default n
+        help
+            Place MCPWM control functions (like set_compare_value) into IRAM,
+            so that these functions can be IRAM-safe and able to be called in the other IRAM interrupt context.
+            Enabling this option can improve driver performance as well.
+
+    config MCPWM_SUPPRESS_DEPRECATE_WARN
+        bool "Suppress leagcy driver deprecated warning"
+        default n
+        help
+            Wether to suppress the deprecation warnings when using legacy MCPWM driver (driver/mcpwm.h).
+            If you want to continue using the legacy driver, and don't want to see related deprecation warnings,
+            you can enable this option.
+
+    config MCPWM_ENABLE_DEBUG_LOG
+        bool "Enable debug log"
+        default n
+        help
+            Wether to enable the debug log message for MCPWM driver.
+            Note that, this option only controls the MCPWM driver log, won't affect other drivers.
+endmenu # MCPWM Configuration

+ 5 - 6
components/driver/test_apps/mcpwm/main/test_mcpwm_cap.c

@@ -1,5 +1,5 @@
 /*
- * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
+ * SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
  *
  * SPDX-License-Identifier: Apache-2.0
  */
@@ -111,9 +111,9 @@ TEST_CASE("mcpwm_capture_ext_gpio", "[mcpwm]")
 
     printf("simulate GPIO capture signal\r\n");
     gpio_set_level(cap_gpio, 1);
-    vTaskDelay(pdMS_TO_TICKS(10));
+    esp_rom_delay_us(10 * 1000);
     gpio_set_level(cap_gpio, 0);
-    vTaskDelay(pdMS_TO_TICKS(10));
+    esp_rom_delay_us(10 * 1000);
     printf("capture value: Pos=%"PRIu32", Neg=%"PRIu32"\r\n", cap_value[0], cap_value[1]);
     uint32_t clk_src_res;
     TEST_ESP_OK(mcpwm_capture_timer_get_resolution(cap_timer, &clk_src_res));
@@ -176,10 +176,9 @@ TEST_CASE("mcpwm_capture_software_catch", "[mcpwm]")
 
     printf("trigger software catch\r\n");
     TEST_ESP_OK(mcpwm_capture_channel_trigger_soft_catch(cap_channel));
-    vTaskDelay(pdMS_TO_TICKS(10));
+    esp_rom_delay_us(10 * 1000);
     TEST_ESP_OK(mcpwm_capture_channel_trigger_soft_catch(cap_channel));
-    vTaskDelay(pdMS_TO_TICKS(10));
-
+    esp_rom_delay_us(10 * 1000);
     // check user data
     TEST_ASSERT_EQUAL(2, test_callback_data.cap_data_index);
     uint32_t delta = test_callback_data.cap_data[1] - test_callback_data.cap_data[0];

+ 1 - 1
components/driver/test_apps/mcpwm/main/test_mcpwm_cmpr.c

@@ -100,7 +100,7 @@ TEST_CASE("mcpwm_comparator_event_callback", "[mcpwm]")
     TEST_ESP_OK(mcpwm_timer_enable(timer));
     TEST_ESP_OK(mcpwm_timer_start_stop(timer, MCPWM_TIMER_START_NO_STOP));
 
-    vTaskDelay(pdMS_TO_TICKS(1000));
+    esp_rom_delay_us(1000 * 1000);
     TEST_ESP_OK(mcpwm_timer_start_stop(timer, MCPWM_TIMER_STOP_EMPTY));
     printf("compare_counts=%"PRIu32"\r\n", compare_counts);
     // the timer period is 10ms, the expected compare_counts = 1s/10ms = 100

+ 1 - 1
components/driver/test_apps/mcpwm/main/test_mcpwm_timer.c

@@ -169,7 +169,7 @@ TEST_CASE("mcpwm_timer_event_callbacks", "[mcpwm]")
     TEST_ESP_OK(mcpwm_timer_start_stop(timer, MCPWM_TIMER_START_NO_STOP));
 
     printf("wait for full and empty events\r\n");
-    bits = xEventGroupWaitBits(event_group, TEST_MCPWM_TIMER_EVENT_BIT_FULL | TEST_MCPWM_TIMER_EVENT_BIT_EMPTY, pdTRUE, pdTRUE, pdMS_TO_TICKS(1050));
+    bits = xEventGroupWaitBits(event_group, TEST_MCPWM_TIMER_EVENT_BIT_FULL | TEST_MCPWM_TIMER_EVENT_BIT_EMPTY, pdTRUE, pdTRUE, pdMS_TO_TICKS(1300));
     TEST_ASSERT_EQUAL(TEST_MCPWM_TIMER_EVENT_BIT_FULL | TEST_MCPWM_TIMER_EVENT_BIT_EMPTY, bits);
 
     printf("stop timer and wait for event\r\n");

+ 3 - 2
components/hal/esp32p4/include/hal/clk_gate_ll.h

@@ -77,9 +77,9 @@ static inline uint32_t periph_ll_get_clk_en_mask(periph_module_t periph)
     case PERIPH_CAM_MODULE:
         return HP_SYS_CLKRST_REG_CAM_CLK_EN;
     case PERIPH_MCPWM0_MODULE:
-        return HP_SYS_CLKRST_REG_MCPWM0_CLK_EN;
+        return HP_SYS_CLKRST_REG_MCPWM0_APB_CLK_EN;
     case PERIPH_MCPWM1_MODULE:
-        return HP_SYS_CLKRST_REG_MCPWM1_CLK_EN;
+        return HP_SYS_CLKRST_REG_MCPWM1_APB_CLK_EN;
     case PERIPH_TIMG0_MODULE:
         return HP_SYS_CLKRST_REG_TIMERGRP0_T0_CLK_EN | HP_SYS_CLKRST_REG_TIMERGRP0_T1_CLK_EN | HP_SYS_CLKRST_REG_TIMERGRP0_WDT_CLK_EN;
     case PERIPH_TIMG1_MODULE:
@@ -264,6 +264,7 @@ static inline uint32_t periph_ll_get_clk_en_reg(periph_module_t periph)
         return HP_SYS_CLKRST_PERI_CLK_CTRL119_REG;
     case PERIPH_MCPWM0_MODULE:
     case PERIPH_MCPWM1_MODULE:
+        return HP_SYS_CLKRST_SOC_CLK_CTRL2_REG;
     case PERIPH_TIMG0_MODULE:
         return HP_SYS_CLKRST_PERI_CLK_CTRL20_REG;
     case PERIPH_TIMG1_MODULE:

+ 1680 - 0
components/hal/esp32p4/include/hal/mcpwm_ll.h

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

+ 5 - 1
components/soc/esp32p4/include/soc/Kconfig.soc_caps.in

@@ -23,6 +23,10 @@ config SOC_GPTIMER_SUPPORTED
     bool
     default y
 
+config SOC_MCPWM_SUPPORTED
+    bool
+    default y
+
 config SOC_ASYNC_MEMCPY_SUPPORTED
     bool
     default y
@@ -489,7 +493,7 @@ config SOC_RMT_SUPPORT_RC_FAST
 
 config SOC_MCPWM_GROUPS
     int
-    default 1
+    default 2
 
 config SOC_MCPWM_TIMERS_PER_GROUP
     int

+ 46 - 0
components/soc/esp32p4/include/soc/clk_tree_defs.h

@@ -228,6 +228,52 @@ typedef enum {
 
 //////////////////////////////////////////////////MCPWM/////////////////////////////////////////////////////////////////
 
+/**
+ * @brief Array initializer for all supported clock sources of MCPWM Timer
+ */
+#if SOC_CLK_TREE_SUPPORTED
+#define SOC_MCPWM_TIMER_CLKS {SOC_MOD_CLK_PLL_F160M, SOC_MOD_CLK_RC_FAST, SOC_MOD_CLK_XTAL}
+#else
+#define SOC_MCPWM_TIMER_CLKS {SOC_MOD_CLK_XTAL}
+#endif
+
+/**
+ * @brief Type of MCPWM timer clock source
+ */
+typedef enum {
+    MCPWM_TIMER_CLK_SRC_PLL160M = SOC_MOD_CLK_PLL_F160M, /*!< Select PLL_F160M as the source clock */
+    MCPWM_TIMER_CLK_SRC_RC_FAST = SOC_MOD_CLK_RC_FAST,   /*!< Select RC_FAST as the source clock */
+    MCPWM_TIMER_CLK_SRC_XTAL = SOC_MOD_CLK_XTAL,         /*!< Select XTAL as the source clock */
+#if SOC_CLK_TREE_SUPPORTED
+    MCPWM_TIMER_CLK_SRC_DEFAULT = SOC_MOD_CLK_PLL_F160M, /*!< Select PLL_F160M as the default choice */
+#else
+    MCPWM_TIMER_CLK_SRC_DEFAULT = SOC_MOD_CLK_XTAL,      /*!< Select XTAL as the default choice */
+#endif // SOC_CLK_TREE_SUPPORTED
+} soc_periph_mcpwm_timer_clk_src_t;
+
+/**
+ * @brief Array initializer for all supported clock sources of MCPWM Capture Timer
+ */
+#if SOC_CLK_TREE_SUPPORTED
+#define SOC_MCPWM_CAPTURE_CLKS {SOC_MOD_CLK_PLL_F160M, SOC_MOD_CLK_RC_FAST, SOC_MOD_CLK_XTAL}
+#else
+#define SOC_MCPWM_CAPTURE_CLKS {SOC_MOD_CLK_XTAL}
+#endif
+
+/**
+ * @brief Type of MCPWM capture clock source
+ */
+typedef enum {
+    MCPWM_CAPTURE_CLK_SRC_PLL160M = SOC_MOD_CLK_PLL_F160M, /*!< Select PLL_F160M as the source clock */
+    MCPWM_CAPTURE_CLK_SRC_RC_FAST = SOC_MOD_CLK_RC_FAST,   /*!< Select RC_FAST as the source clock */
+    MCPWM_CAPTURE_CLK_SRC_XTAL = SOC_MOD_CLK_XTAL,         /*!< Select XTAL as the source clock */
+#if SOC_CLK_TREE_SUPPORTED
+    MCPWM_CAPTURE_CLK_SRC_DEFAULT = SOC_MOD_CLK_PLL_F160M,  /*!< Select PLL_F160M as the default choice */
+#else
+    MCPWM_CAPTURE_CLK_SRC_DEFAULT = SOC_MOD_CLK_XTAL,      /*!< Select XTAL as the default choice */
+#endif // SOC_CLK_TREE_SUPPORTED
+} soc_periph_mcpwm_capture_clk_src_t;
+
 ///////////////////////////////////////////////// I2S //////////////////////////////////////////////////////////////
 
 /////////////////////////////////////////////////I2C////////////////////////////////////////////////////////////////////

Datei-Diff unterdrückt, da er zu groß ist
+ 185 - 432
components/soc/esp32p4/include/soc/mcpwm_struct.h


+ 2 - 2
components/soc/esp32p4/include/soc/soc_caps.h

@@ -34,7 +34,7 @@
 #define SOC_AXI_GDMA_SUPPORTED          1
 #define SOC_GPTIMER_SUPPORTED           1
 // #define SOC_PCNT_SUPPORTED              1  //TODO: IDF-7475
-// #define SOC_MCPWM_SUPPORTED             1  //TODO: IDF-7493
+#define SOC_MCPWM_SUPPORTED                1
 // #define SOC_TWAI_SUPPORTED              1  //TODO: IDF-7470
 // #define SOC_ETM_SUPPORTED               1  //TODO: IDF-7478
 // #define SOC_PARLIO_SUPPORTED            1  //TODO: IDF-7471, TODO: IDF-7472
@@ -273,7 +273,7 @@
 #define SOC_RMT_SUPPORT_RC_FAST               1  /*!< Support set RC_FAST as the RMT clock source */
 
 /*-------------------------- MCPWM CAPS --------------------------------------*/
-#define SOC_MCPWM_GROUPS                     (1U)   ///< 1 MCPWM groups on the chip (i.e., the number of independent MCPWM peripherals)
+#define SOC_MCPWM_GROUPS                     (2U)   ///< 2 MCPWM groups on the chip (i.e., the number of independent MCPWM peripherals)
 #define SOC_MCPWM_TIMERS_PER_GROUP           (3)    ///< The number of timers that each group has
 #define SOC_MCPWM_OPERATORS_PER_GROUP        (3)    ///< The number of operators that each group has
 #define SOC_MCPWM_COMPARATORS_PER_OPERATOR   (2)    ///< The number of comparators that each operator has

+ 140 - 1
components/soc/esp32p4/mcpwm_periph.c

@@ -9,5 +9,144 @@
 #include "soc/gpio_sig_map.h"
 
 const mcpwm_signal_conn_t mcpwm_periph_signals = {
-
+    .groups = {
+        [0] = {
+            .module = PERIPH_MCPWM0_MODULE,
+            .irq_id = ETS_PWM0_INTR_SOURCE,
+            .operators = {
+                [0] = {
+                    .generators = {
+                        [0] = {
+                            .pwm_sig = PWM0_CH0_A_PAD_OUT_IDX
+                        },
+                        [1] = {
+                            .pwm_sig = PWM0_CH0_B_PAD_OUT_IDX
+                        }
+                    }
+                },
+                [1] = {
+                    .generators = {
+                        [0] = {
+                            .pwm_sig = PWM0_CH1_A_PAD_OUT_IDX
+                        },
+                        [1] = {
+                            .pwm_sig = PWM0_CH1_B_PAD_OUT_IDX
+                        }
+                    }
+                },
+                [2] = {
+                    .generators = {
+                        [0] = {
+                            .pwm_sig = PWM0_CH2_A_PAD_OUT_IDX
+                        },
+                        [1] = {
+                            .pwm_sig = PWM0_CH2_B_PAD_OUT_IDX
+                        }
+                    }
+                }
+            },
+            .gpio_faults = {
+                [0] = {
+                    .fault_sig = PWM0_F0_PAD_IN_IDX
+                },
+                [1] = {
+                    .fault_sig = PWM0_F1_PAD_IN_IDX
+                },
+                [2] = {
+                    .fault_sig = PWM0_F2_PAD_IN_IDX
+                }
+            },
+            .captures = {
+                [0] = {
+                    .cap_sig = PWM0_CAP0_PAD_IN_IDX
+                },
+                [1] = {
+                    .cap_sig = PWM0_CAP1_PAD_IN_IDX
+                },
+                [2] = {
+                    .cap_sig = PWM0_CAP2_PAD_IN_IDX
+                }
+            },
+            .gpio_synchros = {
+                [0] = {
+                    .sync_sig = PWM0_SYNC0_PAD_IN_IDX
+                },
+                [1] = {
+                    .sync_sig = PWM0_SYNC1_PAD_IN_IDX
+                },
+                [2] = {
+                    .sync_sig = PWM0_SYNC2_PAD_IN_IDX
+                }
+            }
+        },
+        [1] = {
+            .module = PERIPH_MCPWM1_MODULE,
+            .irq_id = ETS_PWM1_INTR_SOURCE,
+            .operators = {
+                [0] = {
+                    .generators = {
+                        [0] = {
+                            .pwm_sig = PWM1_CH0_A_PAD_OUT_IDX
+                        },
+                        [1] = {
+                            .pwm_sig = PWM1_CH0_B_PAD_OUT_IDX
+                        }
+                    }
+                },
+                [1] = {
+                    .generators = {
+                        [0] = {
+                            .pwm_sig = PWM1_CH1_A_PAD_OUT_IDX
+                        },
+                        [1] = {
+                            .pwm_sig = PWM1_CH1_B_PAD_OUT_IDX
+                        }
+                    }
+                },
+                [2] = {
+                    .generators = {
+                        [0] = {
+                            .pwm_sig = PWM1_CH2_A_PAD_OUT_IDX
+                        },
+                        [1] = {
+                            .pwm_sig = PWM1_CH2_B_PAD_OUT_IDX
+                        }
+                    }
+                }
+            },
+            .gpio_faults = {
+                [0] = {
+                    .fault_sig = PWM1_F0_PAD_IN_IDX
+                },
+                [1] = {
+                    .fault_sig = PWM1_F1_PAD_IN_IDX
+                },
+                [2] = {
+                    .fault_sig = PWM1_F2_PAD_IN_IDX
+                }
+            },
+            .captures = {
+                [0] = {
+                    .cap_sig = PWM1_CAP0_PAD_IN_IDX
+                },
+                [1] = {
+                    .cap_sig = PWM1_CAP1_PAD_IN_IDX
+                },
+                [2] = {
+                    .cap_sig = PWM1_CAP2_PAD_IN_IDX
+                }
+            },
+            .gpio_synchros = {
+                [0] = {
+                    .sync_sig = PWM1_SYNC0_PAD_IN_IDX
+                },
+                [1] = {
+                    .sync_sig = PWM1_SYNC1_PAD_IN_IDX
+                },
+                [2] = {
+                    .sync_sig = PWM1_SYNC2_PAD_IN_IDX
+                }
+            }
+        }
+    }
 };

Einige Dateien werden nicht angezeigt, da zu viele Dateien in diesem Diff geändert wurden.