Просмотр исходного кода

[BSP][GD32]: 修复硬件iic和pwm驱动的一些问题#11222

ShiHongChao 19 часов назад
Родитель
Сommit
a10ac694e1

+ 13 - 0
bsp/gd32/arm/libraries/gd32_drivers/Kconfig

@@ -24,6 +24,19 @@ if BSP_USING_USBD
         # "ULPI: UTMI+ Low Pin Interface"
 endif
 
+if BSP_USING_HARD_I2C
+    choice
+        prompt "Select I2C Receiving Scheme"
+        default BSP_USING_RECEIVING_A
+
+        config BSP_USING_RECEIVING_A
+            bool "master receiving secheme A --- requires that the software be capable of responding quickly to the 12C event."
+
+        config BSP_USING_RECEIVING_B
+            bool "master receiving secheme B --- don't requires that the software be capable of responding quickly to the 12C event."
+    endchoice
+endif
+
 config BSP_USING_GD_DBG
     bool 
     default y

+ 75 - 14
bsp/gd32/arm/libraries/gd32_drivers/drv_hard_i2c.c

@@ -6,6 +6,8 @@
  * Change Logs:
  * Date           Author            Notes
  * 2021-12-20     BruceOu           the first version
+ * 2026-01-11     ShiHongchao       Fix the I2C master receive mode B software
+ *                                  flow and add support for mode A
  */
 
 #include "drv_hard_i2c.h"
@@ -15,7 +17,7 @@
 #define LOG_TAG              "drv.i2c"
 #include <rtdbg.h>
 
-#if !defined(BSP_USING_HARD_I2C0) && !defined(BSP_USING_HARD_I2C1) && !defined(BSP_USING_HARD_I2C2) && !defined(BSP_USING_HARD_I2C3)  && !defined(BSP_USING_HARD_I2C4)  && !defined(BSP_USING_HARD_I2C5)
+#if !defined(BSP_USING_HARD_I2C0) && !defined(BSP_USING_HARD_I2C1) && !defined(BSP_USING_HARD_I2C2) && !defined(BSP_USING_HARD_I2C3) && !defined(BSP_USING_HARD_I2C4) && !defined(BSP_USING_HARD_I2C5)
 #error "Please define at least one BSP_USING_I2Cx"
 /* this driver can be disabled at menuconfig → RT-Thread Components → Device Drivers */
 #endif
@@ -112,8 +114,8 @@ static const struct gd32_i2c_bus gd_i2c_config[] = {
 
     RCU_I2C0, RCU_GPIOB, RCU_GPIOB,    /* periph clock, scl gpio clock, sda gpio clock */
 
-    GPIOB, GPIO_AF_4, GPIO_PIN_6,    /* scl port, scl alternate, scl pin */
-    GPIOB, GPIO_AF_4, GPIO_PIN_7,    /* sda port, sda alternate, sda pin */
+    GPIOB, GPIO_AF_4, GPIO_PIN_8,    /* scl port, scl alternate, scl pin */
+    GPIOB, GPIO_AF_4, GPIO_PIN_9,    /* sda port, sda alternate, sda pin */
 
         &i2c0,
         "hwi2c0",
@@ -229,13 +231,52 @@ static void gd32_i2c_gpio_init(const struct gd32_i2c_bus *i2c)
 static uint8_t gd32_i2c_read(rt_uint32_t i2c_periph, rt_uint8_t *p_buffer, rt_uint16_t data_byte)
 {
     if (data_byte == 0) return 1;
-    /* while there is data to be read */
 
+#ifdef BSP_USING_RECEIVING_A
+    /*
+        In single-byte reception, disable ACK because the master needs to send
+        NACK after receiving the first byte,indicating no more data will be
+        received, then immediately send the stop condition
+    */
+    if(data_byte == 1)
+    {
+        /* disable acknowledge */
+        i2c_ack_config(i2c_periph, I2C_ACK_DISABLE);
+        /* send a stop condition to I2C bus */
+        i2c_stop_on_bus(i2c_periph);
+    }
+#endif
+
+    /* while there is data to be read */
     while(data_byte)
     {
 #if defined (SOC_SERIES_GD32F5xx) || defined (SOC_SERIES_GD32F4xx)
         if(IS_I2C_LEGACY(i2c_periph))
         {
+#ifdef BSP_USING_RECEIVING_A
+            /*
+                After receiving the second-to-last byte, ACK should be disabled
+                and STOP should be set, to ensure that NACK is sent after receiving
+                the last byte and the stop condition is transmitted
+            */
+            if(2 == data_byte)
+            {
+                    /* wait until BTC bit is set */
+                    while(!i2c_flag_get(i2c_periph, I2C_FLAG_RBNE));
+                    /* disable acknowledge */
+                    i2c_ack_config(i2c_periph, I2C_ACK_DISABLE);
+                    /* send a stop condition to I2C bus */
+                    i2c_stop_on_bus(i2c_periph);
+            }
+#elif defined(BSP_USING_RECEIVING_B)
+            /*
+                For 3-byte reception: Wait for byte transfer completion, then
+                disable ACK so NACK is automatically sent after receiving the
+                last byte
+                For 2-byte reception: Wait for byte transfer completion, then
+                send stop condition to ensure direct stop after receiving the
+                last byte instead of sending ACK
+            */
             if(3 == data_byte)
             {
                     /* wait until BTC bit is set */
@@ -243,14 +284,16 @@ static uint8_t gd32_i2c_read(rt_uint32_t i2c_periph, rt_uint8_t *p_buffer, rt_ui
                     /* disable acknowledge */
                     i2c_ack_config(i2c_periph, I2C_ACK_DISABLE);
             }
-
-            if(2 == data_byte)
+            else if(2 == data_byte)
             {
                     /* wait until BTC bit is set */
                     while(!i2c_flag_get(i2c_periph, I2C_FLAG_BTC));
                     /* send a stop condition to I2C bus */
                     i2c_stop_on_bus(i2c_periph);
             }
+#else
+#error "Please select the receiving scheme."
+#endif
             /* wait until RBNE bit is set */
             if(i2c_flag_get(i2c_periph, I2C_FLAG_RBNE))
             {
@@ -379,11 +422,18 @@ static rt_ssize_t gd32_i2c_master_xfer(struct rt_i2c_bus_device *bus, struct rt_
                     {
                             i2c_stop_on_bus(gd32_i2c->i2c_periph);
                     }
-                        /* enable acknowledge */
+                    /* enable acknowledge */
                     i2c_ack_config(gd32_i2c->i2c_periph, I2C_ACK_ENABLE);
-                        /* i2c master sends start signal only when the bus is idle */
+                    /* i2c master sends start signal only when the bus is idle */
                     while(i2c_flag_get(gd32_i2c->i2c_periph, I2C_FLAG_I2CBSY));
-                        /* send the start signal */
+#ifdef BSP_USING_RECEIVING_B
+                    /*  */
+                    if(msg->len == 2)
+                    {
+                        i2c_ackpos_config(gd32_i2c->i2c_periph, I2C_ACKPOS_NEXT);
+                    }
+#endif
+                    /* send the start signal */
                     i2c_start_on_bus(gd32_i2c->i2c_periph);
                      /* i2c master sends START signal successfully */
                     while(!i2c_flag_get(gd32_i2c->i2c_periph, I2C_FLAG_SBSEND));
@@ -391,14 +441,26 @@ static rt_ssize_t gd32_i2c_master_xfer(struct rt_i2c_bus_device *bus, struct rt_
                     i2c_master_addressing(gd32_i2c->i2c_periph, msg->addr, I2C_RECEIVER);
 
                     while(!i2c_flag_get(gd32_i2c->i2c_periph, I2C_FLAG_ADDSEND));
-                       /* address flag set means i2c slave sends ACK */
+#ifdef BSP_USING_RECEIVING_B
+                    if(msg->len <= 2)
+                    {
+                        i2c_ack_config(gd32_i2c->i2c_periph, I2C_ACK_DISABLE);
+                    }
+#endif
+                    /* address flag set means i2c slave sends ACK */
                     i2c_flag_clear(gd32_i2c->i2c_periph, I2C_FLAG_ADDSEND);
+#ifdef BSP_USING_RECEIVING_B
+                    if(msg->len == 1)
+                    {
+                        i2c_stop_on_bus(gd32_i2c->i2c_periph);
+                    }
+#endif
 
                }else {
-                     /* configure slave address */
+                    /* configure slave address */
                     while(i2c_flag_get(gd32_i2c->i2c_periph, I2C_FLAG_I2CBSY));
-                     //i2c_transfer_byte_number_config(gd32_i2c->i2c_periph, w_total_byte);
-                     /* send a start condition to I2C bus */
+                    //i2c_transfer_byte_number_config(gd32_i2c->i2c_periph, w_total_byte);
+                    /* send a start condition to I2C bus */
                     i2c_start_on_bus(gd32_i2c->i2c_periph);
                     while(!i2c_flag_get(gd32_i2c->i2c_periph, I2C_FLAG_SBSEND));
 
@@ -559,4 +621,3 @@ int rt_hw_i2c_init(void)
 INIT_BOARD_EXPORT(rt_hw_i2c_init);
 
 #endif /* RT_USING_I2C */
-

+ 422 - 291
bsp/gd32/arm/libraries/gd32_drivers/drv_pwm.c

@@ -6,15 +6,21 @@
  * Change Logs:
  * Date           Author            Notes
  * 2023-06-05     zengjianwei       first version
- * 2025-06-23     Yucai Liu         Support for non-complementary PWM output with advanced timers
+ * 2025-06-23     Yucai Liu         Support for non-complementary PWM output with
+ *                                  advanced timers
+ * 2025-12-26     shihongchao       Optimize the timer clock frequency acquisition
+ *                                  method; optimize the gd32_pwm structure to make
+ *                                  it easier to configure; optimize the RCU enable
+ *                                  logic; optimize GPIO configuration to maintain
+ *                                  floating input mode when channels are disabled,
+ *                                  reducing power consumption.
  */
 
 #include <board.h>
-#include <gd32f30x.h>
 #include <rtdevice.h>
 #include <rtthread.h>
 
-#ifdef RT_USING_PWM
+#ifdef BSP_USING_PWM
 
 /* #define DRV_DEBUG */
 #define LOG_TAG "drv.pwm"
@@ -24,208 +30,186 @@
 #define MIN_PERIOD 3
 #define MIN_PULSE  2
 
-typedef struct
-{
-    rt_int8_t   TimerIndex; /* timer index:0~13 */
-    rt_uint32_t Port;       /* gpio port:GPIOA/GPIOB/GPIOC/... */
-    rt_uint32_t pin;        /* gpio pin:GPIO_PIN_0~GPIO_PIN_15 */
-    /* timer channel: -2 is ch_1n, -1 is ch_0n, 0 is ch0, 1 is ch1 */
-    rt_int16_t channel;
-    char      *name;
-} TIMER_PORT_CHANNEL_MAP_S;
+typedef struct{
+    uint32_t gpio_port;
+    uint32_t gpio_af;
+    uint16_t gpio_pin;
+}channel_type;
 
 struct gd32_pwm
 {
-    struct rt_device_pwm     pwm_device;
-    TIMER_PORT_CHANNEL_MAP_S tim_handle;
+    struct rt_device_pwm       pwm_device;   /* Inherit PWM device */
+           char                *name;        /* Device name */
+           uint32_t            timerx;       /* Hardware timer dependent on PWM */
+           rcu_clock_freq_enum apb_of;       /* APB bus to which TIMER belongs */
+           channel_type        channels[4];  /* PWM channels */
+           channel_type        nchannels[3]; /* PWM complementary channels, only supported by advanced timers */
 };
 
 static struct gd32_pwm gd32_pwm_obj[] = {
-#ifdef RT_USING_PWM1
-    {.tim_handle = {3, GPIOB, GPIO_PIN_8, 2, "pwm1"}},
-#endif
-
-#ifdef RT_USING_PWM2
-    {.tim_handle = {3, GPIOB, GPIO_PIN_8, 2, "pwm2"}},
-#endif
-
-#ifdef RT_USING_PWM3
-    {.tim_handle = {3, GPIOB, GPIO_PIN_8, 2, "pwm3"}},
+#ifdef BSP_USING_PWM0
+    {
+        .name = "pwm0",
+        .timerx = TIMER0,
+        .apb_of = CK_APB2,
+        .channels = {
+            {GPIOC, GPIO_AF_1, GPIO_PIN_0},
+            {GPIOA, GPIO_AF_1, GPIO_PIN_1},
+            {GPIOA, GPIO_AF_1, GPIO_PIN_2},
+            {GPIOA, GPIO_AF_1, GPIO_PIN_3},
+        },
+        .nchannels = {
+            {GPIOB, GPIO_AF_1, GPIO_PIN_13},
+            {GPIOB, GPIO_AF_1, GPIO_PIN_14},
+            {GPIOB, GPIO_AF_1, GPIO_PIN_15},
+        }
+    },
 #endif
 
-#ifdef RT_USING_PWM4
-    {.tim_handle = {3, GPIOB, GPIO_PIN_8, 2, "pwm4"}},
+#ifdef BSP_USING_PWM1
+    {
+        .name = "pwm1",
+        .timerx = TIMER1,
+        .apb_of = CK_APB1,
+        .channels = {
+            {GPIOA, GPIO_AF_1, GPIO_PIN_0},
+            {GPIOA, GPIO_AF_1, GPIO_PIN_1},
+            {GPIOA, GPIO_AF_1, GPIO_PIN_2},
+            {GPIOB, GPIO_AF_1, GPIO_PIN_2},
+        },
+    },
 #endif
 
-#ifdef RT_USING_PWM5
-    {.tim_handle = {3, GPIOB, GPIO_PIN_8, 2, "pwm5"}},
+#ifdef BSP_USING_PWM2
+    {
+        .name = "pwm2",
+        .timerx = TIMER2,
+        .apb_of = CK_APB1,
+        .channels = {
+            {GPIOA, GPIO_AF_1, GPIO_PIN_6},
+            {GPIOA, GPIO_AF_1, GPIO_PIN_7},
+            {GPIOB, GPIO_AF_2, GPIO_PIN_0},
+            {GPIOB, GPIO_AF_2, GPIO_PIN_1},
+        },
+    },
 #endif
 
-#ifdef RT_USING_PWM6
-    {.tim_handle = {3, GPIOB, GPIO_PIN_8, 2, "pwm6"}},
+#ifdef BSP_USING_PWM3
+    {
+        .name = "pwm3",
+        .timerx = TIMER3,
+        .apb_of = CK_APB1,
+        .channels = {
+            {GPIOA, GPIO_AF_1, GPIO_PIN_0},
+            {GPIOA, GPIO_AF_1, GPIO_PIN_1},
+            {GPIOA, GPIO_AF_1, GPIO_PIN_2},
+            {GPIOA, GPIO_AF_1, GPIO_PIN_3},
+        },
+    },
 #endif
 
-#ifdef RT_USING_PWM7
-    {.tim_handle = {3, GPIOB, GPIO_PIN_8, 2, "pwm7"}},
+#ifdef BSP_USING_PWM4
+    {
+        .name = "pwm4",
+        .timerx = TIMER4,
+        .apb_of = CK_APB1,
+        .channels = {
+            {GPIOA, GPIO_AF_2, GPIO_PIN_0},
+            {GPIOA, GPIO_AF_2, GPIO_PIN_1},
+            {GPIOA, GPIO_AF_1, GPIO_PIN_2},
+            {GPIOA, GPIO_AF_1, GPIO_PIN_3},
+        },
+    },
 #endif
 
-#ifdef RT_USING_PWM8
-    {.tim_handle = {3, GPIOB, GPIO_PIN_8, 2, "pwm8"}},
+#ifdef BSP_USING_PWM7
+    {
+        .name = "pwm7",
+        .timerx = TIMER7,
+        .apb_of = CK_APB2,
+        .channels = {
+            {GPIOA, GPIO_AF_1, GPIO_PIN_0},
+            {GPIOA, GPIO_AF_1, GPIO_PIN_1},
+            {GPIOC, GPIO_AF_1, GPIO_PIN_8},
+            {GPIOA, GPIO_AF_1, GPIO_PIN_3},
+        },
+        .nchannels = {
+            {GPIOA, GPIO_AF_1, GPIO_PIN_5},
+            {GPIOB, GPIO_AF_1, GPIO_PIN_0},
+            {GPIOB, GPIO_AF_1, GPIO_PIN_1},
+        }
+    },
 #endif
 
-#ifdef RT_USING_PWM9
-    {.tim_handle = {3, GPIOB, GPIO_PIN_8, 2, "pwm9"}},
+#ifdef BSP_USING_PWM8
+    {
+        .name = "pwm8",
+        .timerx = TIMER8,
+        .apb_of = CK_APB2,
+        .channels = {
+            {GPIOA, GPIO_AF_1, GPIO_PIN_2},
+            {GPIOA, GPIO_AF_3, GPIO_PIN_3},
+        }, // L1 general-purpose timer is a two-channel timer
+    },
 #endif
 
-#ifdef RT_USING_PWM10
-    {.tim_handle = {3, GPIOB, GPIO_PIN_8, 2, "pwm10"}},
+#ifdef BSP_USING_PWM9
+    {
+        .name = "pwm9",
+        .timerx = TIMER9,
+        .apb_of = CK_APB2,
+        .channels = {
+            {GPIOA, GPIO_AF_1, GPIO_PIN_0},
+        }, // L2 general-purpose timer is a single-channel timer
+    },
 #endif
 
-#ifdef RT_USING_PWM11
-    {.tim_handle = {3, GPIOB, GPIO_PIN_8, 2, "pwm11"}},
+#ifdef BSP_USING_PWM10
+    {
+        .name = "pwm10",
+        .timerx = TIMER10,
+        .apb_of = CK_APB2,
+        .channels = {
+            {GPIOA, GPIO_AF_1, GPIO_PIN_0},
+        }, // L2 general-purpose timer is a single-channel timer
+    },
 #endif
 
-#ifdef RT_USING_PWM12
-    {.tim_handle = {3, GPIOB, GPIO_PIN_8, 2, "pwm12"}},
+#ifdef BSP_USING_PWM11
+    {
+        .name = "pwm11",
+        .timerx = TIMER11,
+        .apb_of = CK_APB1,
+        .channels = {
+            {GPIOA, GPIO_AF_1, GPIO_PIN_0},
+            {GPIOA, GPIO_AF_1, GPIO_PIN_1},
+        }, // L1 general-purpose timer is a two-channel timer
+    },
 #endif
 
-#ifdef RT_USING_PWM13
-    {.tim_handle = {3, GPIOB, GPIO_PIN_8, 2, "pwm13"}},
+#ifdef BSP_USING_PWM12
+    {
+        .name = "pwm12",
+        .timerx = TIMER12,
+        .apb_of = CK_APB1,
+        .channels = {
+            {GPIOA, GPIO_AF_1, GPIO_PIN_0},
+        }, // L2 general-purpose timer is a single-channel timer
+    },
 #endif
 
-#ifdef RT_USING_PWM14
-    {.tim_handle = {3, GPIOB, GPIO_PIN_8, 2, "pwm14"}},
+#ifdef BSP_USING_PWM13
+    {
+        .name = "pwm13",
+        .timerx = TIMER13,
+        .apb_of = CK_APB1,
+        .channels = {
+            {GPIOA, GPIO_AF_1, GPIO_PIN_7},
+        }, // L2 general-purpose timer is a single-channel timer
+    },
 #endif
 };
 
-typedef struct
-{
-    rt_uint32_t Port[7];
-    rt_int8_t   TimerIndex[14];
-} TIMER_PERIPH_LIST_S;
-
-static TIMER_PERIPH_LIST_S gd32_timer_periph_list = {
-    .Port       = {0, 0, 0, 0, 0, 0, 0},
-    .TimerIndex = {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
-};
-
-/*
- * 将所有用到的 gpio port 和 timer 不重复地列举出来,以方便后面不重复地初始化
- */
-static rt_err_t pwm_find_timer_periph(void)
-{
-    rt_int16_t i, j, k;
-
-    /* find gpio port of defined table */
-    for (i = 0; i < sizeof(gd32_pwm_obj) / sizeof(gd32_pwm_obj[0]); ++i)
-    {
-        /* find -1 of gd32_periph_list's member of Port */
-        for (j = 0; j < sizeof(gd32_timer_periph_list.Port) / sizeof(gd32_timer_periph_list.Port[0]); ++j)
-        {
-            if (0 == gd32_timer_periph_list.Port[j])
-            {
-                break;
-            }
-        }
-
-        if (j >= sizeof(gd32_timer_periph_list.Port) / sizeof(gd32_timer_periph_list.Port[0]))
-        {
-            LOG_E("Can not find -1 of gd32_periph_list's member of Port!\n");
-            break;
-        }
-
-        /* find the different of Port */
-        for (k = 0; k < j; ++k)
-        {
-            if (gd32_pwm_obj[i].tim_handle.Port == gd32_timer_periph_list.Port[k])
-            {
-                break;
-            }
-        }
-
-        /* if can not find the same Port */
-        if (k == j)
-        {
-            gd32_timer_periph_list.Port[j] = gd32_pwm_obj[i].tim_handle.Port;
-        }
-    }
-
-    /* find timer periph of defined table */
-    for (i = 0; i < sizeof(gd32_pwm_obj) / sizeof(gd32_pwm_obj[0]); ++i)
-    {
-        /* find -1 of gd32_periph_list's member of TimerIndex */
-        for (j = 0; j < sizeof(gd32_timer_periph_list.TimerIndex) / sizeof(gd32_timer_periph_list.TimerIndex[0]); ++j)
-        {
-            if (-1 == gd32_timer_periph_list.TimerIndex[j])
-            {
-                break;
-            }
-        }
-
-        if (j >= sizeof(gd32_timer_periph_list.TimerIndex) / sizeof(gd32_timer_periph_list.TimerIndex[0]))
-        {
-            LOG_E("Can not find -1 of gd32_periph_list's member of TimerIndex!\n");
-            break;
-        }
-
-        /* find the different of TimerIndex */
-        for (k = 0; k < j; ++k)
-        {
-            if (gd32_pwm_obj[i].tim_handle.TimerIndex == gd32_timer_periph_list.TimerIndex[k])
-            {
-                break;
-            }
-        }
-
-        /* if can not find the same TimerIndex */
-        if (k == j)
-        {
-            gd32_timer_periph_list.TimerIndex[j] = gd32_pwm_obj[i].tim_handle.TimerIndex;
-        }
-    }
-
-    return RT_EOK;
-}
-
-static rt_uint32_t index_to_timer(rt_int8_t TimerIndex)
-{
-    switch (TimerIndex)
-    {
-    case 0:
-        return TIMER0;
-    case 1:
-        return TIMER1;
-    case 2:
-        return TIMER2;
-    case 3:
-        return TIMER3;
-    case 4:
-        return TIMER4;
-    case 5:
-        return TIMER5;
-    case 6:
-        return TIMER6;
-    case 7:
-        return TIMER7;
-    case 8:
-        return TIMER8;
-    case 9:
-        return TIMER9;
-    case 10:
-        return TIMER10;
-    case 11:
-        return TIMER11;
-    case 12:
-        return TIMER12;
-    case 13:
-        return TIMER13;
-
-    default:
-        LOG_E("Unsport timer periph!\n");
-    }
-    return TIMER0;
-}
-
 static void gpio_clock_enable(rt_uint32_t Port)
 {
     switch (Port)
@@ -257,54 +241,52 @@ static void gpio_clock_enable(rt_uint32_t Port)
     }
 }
 
-static void timer_clock_enable(rt_int8_t TimerIndex)
+static void timer_clock_enable(uint32_t timer)
 {
-    switch (TimerIndex)
+    switch (timer)
     {
-    case 0:
+    case TIMER0:
         rcu_periph_clock_enable(RCU_TIMER0);
         break;
-    case 1:
+    case TIMER1:
         rcu_periph_clock_enable(RCU_TIMER1);
         break;
-    case 2:
+    case TIMER2:
         rcu_periph_clock_enable(RCU_TIMER2);
         break;
-    case 3:
+    case TIMER3:
         rcu_periph_clock_enable(RCU_TIMER3);
         break;
-    case 4:
+    case TIMER4:
         rcu_periph_clock_enable(RCU_TIMER4);
         break;
-    case 5:
+    case TIMER5:
         rcu_periph_clock_enable(RCU_TIMER5);
         break;
-    case 6:
+    case TIMER6:
         rcu_periph_clock_enable(RCU_TIMER6);
         break;
-    case 7:
+    case TIMER7:
         rcu_periph_clock_enable(RCU_TIMER7);
         break;
-#ifndef GD32F30X_HD
-    case 8:
+    case TIMER8:
         rcu_periph_clock_enable(RCU_TIMER8);
         break;
-    case 9:
+    case TIMER9:
         rcu_periph_clock_enable(RCU_TIMER9);
         break;
-    case 10:
+    case TIMER10:
         rcu_periph_clock_enable(RCU_TIMER10);
         break;
-    case 11:
+    case TIMER11:
         rcu_periph_clock_enable(RCU_TIMER11);
         break;
-    case 12:
+    case TIMER12:
         rcu_periph_clock_enable(RCU_TIMER12);
         break;
-    case 13:
+    case TIMER13:
         rcu_periph_clock_enable(RCU_TIMER13);
         break;
-#endif
     default:
         LOG_E("Unsport timer periph!\n");
     }
@@ -314,96 +296,191 @@ static void rcu_config(void)
 {
     rt_int16_t i;
 
-    for (i = 0; i < sizeof(gd32_timer_periph_list.Port) / sizeof(gd32_timer_periph_list.Port[0]); ++i)
+    for (i = 0; i < sizeof(gd32_pwm_obj) / sizeof(gd32_pwm_obj[0]); ++i)
     {
-        if (0 == gd32_timer_periph_list.Port[i])
+        /* enable GPIO clock */
+        switch (gd32_pwm_obj[i].timerx)
         {
+        /* Advanced timer */
+        case TIMER0:
+        case TIMER7:
+            gpio_clock_enable(gd32_pwm_obj[i].nchannels[0].gpio_port);
+            gpio_clock_enable(gd32_pwm_obj[i].nchannels[1].gpio_port);
+            gpio_clock_enable(gd32_pwm_obj[i].nchannels[2].gpio_port);
+
+        /* L0 general-purpose timer */
+        case TIMER1:
+        case TIMER2:
+        case TIMER3:
+        case TIMER4:
+            gpio_clock_enable(gd32_pwm_obj[i].channels[2].gpio_port);
+            gpio_clock_enable(gd32_pwm_obj[i].channels[3].gpio_port);
+
+        /* L1 general-purpose timer */
+        case TIMER8:
+        case TIMER11:
+            gpio_clock_enable(gd32_pwm_obj[i].channels[1].gpio_port);
+
+        /* L2 general-purpose timer */
+        case TIMER9:
+        case TIMER10:
+        case TIMER12:
+        case TIMER13:
+            gpio_clock_enable(gd32_pwm_obj[i].channels[0].gpio_port);
             break;
-        }
-
-        /* enable GPIO clock */
-        gpio_clock_enable(gd32_timer_periph_list.Port[i]);
-    }
-
-    rcu_periph_clock_enable(RCU_AF);
 
-    for (i = 0; i < sizeof(gd32_timer_periph_list.TimerIndex) / sizeof(gd32_timer_periph_list.TimerIndex[0]); ++i)
-    {
-        if (-1 == gd32_timer_periph_list.TimerIndex[i])
-        {
+        default:
+            LOG_E("Unsport timer periph at rcu_config!\n");
             break;
         }
+    }
 
+    for (i = 0; i < sizeof(gd32_pwm_obj) / sizeof(gd32_pwm_obj[0]); ++i)
+    {
         /* enable timer clock */
-        timer_clock_enable(gd32_timer_periph_list.TimerIndex[i]);
-        timer_deinit(index_to_timer(gd32_timer_periph_list.TimerIndex[i]));
+        timer_clock_enable(gd32_pwm_obj[i].timerx);
+        timer_deinit(gd32_pwm_obj[i].timerx);
     }
 }
 
-static void gpio_config(void)
+/**
+ * @brief Configure PWM output pin to PWM output mode
+ * @param pwm PWM object
+ * @param configuration Configuration information passed by PWM driver framework
+ */
+static void gpio_config_pwmout(const struct gd32_pwm *pwm,
+                               const struct rt_pwm_configuration *configuration)
 {
-    rt_int16_t i;
-
-    /* config the GPIO as analog mode */
-    for (i = 0; i < sizeof(gd32_pwm_obj) / sizeof(gd32_pwm_obj[0]); ++i)
+    channel_type channel;
+    uint8_t channel_num = configuration->channel;
+    if(channel_num == 0)
+    {
+        LOG_E("PWM channel starts from 1!\n");
+        return;
+    }
+    if(configuration->complementary)
+    {
+        if(channel_num > 3)
+        {
+            LOG_E("GD32 PWM complementary channel max 3!\n");
+            return;
+        };
+        channel = pwm->nchannels[channel_num-1];
+    }
+    else
     {
-        gpio_init(gd32_pwm_obj[i].tim_handle.Port, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, gd32_pwm_obj[i].tim_handle.pin);
+        if(channel_num > 4)
+        {
+            LOG_E("GD32 PWM complementary channel max 4!\n");
+            return;
+        };
+        channel = pwm->channels[channel_num-1];
     }
+    gpio_mode_set(channel.gpio_port, GPIO_MODE_AF, GPIO_PUPD_NONE, channel.gpio_pin);
+    gpio_output_options_set(channel.gpio_port, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, channel.gpio_pin);
+    gpio_af_set(channel.gpio_port, channel.gpio_af, channel.gpio_pin);
 }
 
-static void timer_init_para(timer_parameter_struct *initpara)
+/**
+ * @brief Configure PWM output pin to PWM floating input mode
+ * @param pwm PWM object
+ * @param configuration Configuration information passed by PWM driver framework
+ */
+static void gpio_config_input(const struct gd32_pwm *pwm,
+                              const struct rt_pwm_configuration *configuration)
 {
-    rt_int16_t i;
-
-    for (i = 0; i < sizeof(gd32_timer_periph_list.TimerIndex) / sizeof(gd32_timer_periph_list.TimerIndex[0]); ++i)
+    channel_type channel = {0};
+    uint8_t channel_num = configuration->channel;
+    if(channel_num == 0)
     {
-        /* config timer */
-        if (-1 != gd32_timer_periph_list.TimerIndex[i])
+        return;
+    }
+    if(configuration->complementary)
+    {
+        if(channel_num > 3)
         {
-            timer_init(index_to_timer(gd32_timer_periph_list.TimerIndex[i]), initpara);
-        }
+            LOG_E("GD32 PWM complementary channel max 3!\n");
+            return;
+        };
+        channel = pwm->nchannels[channel_num-1];
+    }
+    else
+    {
+        if(channel_num > 4)
+        {
+            LOG_E("GD32 PWM complementary channel max 4!\n");
+            return;
+        };
+        channel = pwm->channels[channel_num-1];
     }
+    gpio_mode_set(channel.gpio_port, GPIO_MODE_INPUT, GPIO_PUPD_NONE, channel.gpio_pin);
 }
 
-static void channel_output_config(timer_oc_parameter_struct *ocpara)
+static void channel_output_config(rt_uint32_t timer_periph, timer_oc_parameter_struct *ocpara)
 {
-    rt_int16_t  i;
-    rt_uint32_t timer_periph;
-
-    /* config the channel config */
-    for (i = 0; i < sizeof(gd32_pwm_obj) / sizeof(gd32_pwm_obj[0]); ++i)
+    switch (timer_periph)
     {
-        if (gd32_pwm_obj[i].tim_handle.channel < 0)
-        {
-            ocpara->outputstate                = TIMER_CCX_DISABLE;
-            ocpara->outputnstate               = TIMER_CCXN_ENABLE;
-            gd32_pwm_obj[i].tim_handle.channel = -(gd32_pwm_obj[i].tim_handle.channel + 1);
-        }
-        timer_periph = index_to_timer(gd32_pwm_obj[i].tim_handle.TimerIndex);
-        timer_channel_output_config(timer_periph, gd32_pwm_obj[i].tim_handle.channel, ocpara);
+    /* Advanced timer */
+    case TIMER0:
+    case TIMER7:
+        timer_primary_output_config(timer_periph, ENABLE);
+
+    /* L0 general-purpose timer */
+    case TIMER1:
+    case TIMER2:
+    case TIMER3:
+    case TIMER4:
+        timer_channel_output_config(timer_periph, TIMER_CH_2, ocpara);
+        timer_channel_output_pulse_value_config(timer_periph, TIMER_CH_2, 7999);
+        timer_channel_output_mode_config(timer_periph, TIMER_CH_2, TIMER_OC_MODE_PWM0);
+        timer_channel_output_shadow_config(timer_periph, TIMER_CH_2, TIMER_OC_SHADOW_DISABLE);
+        /* auto-reload preload shadow reg enable */
+        /* timer_auto_reload_shadow_enable(timer_periph); */
+        timer_channel_output_state_config(timer_periph, TIMER_CH_2, TIMER_CCX_DISABLE);
+        timer_channel_complementary_output_state_config(timer_periph, TIMER_CH_2, TIMER_CCXN_DISABLE);
 
-        timer_channel_output_pulse_value_config(timer_periph, gd32_pwm_obj[i].tim_handle.channel, 7999);
-        timer_channel_output_mode_config(timer_periph, gd32_pwm_obj[i].tim_handle.channel, TIMER_OC_MODE_PWM0);
-        timer_channel_output_shadow_config(timer_periph, gd32_pwm_obj[i].tim_handle.channel, TIMER_OC_SHADOW_DISABLE);
+        timer_channel_output_config(timer_periph, TIMER_CH_3, ocpara);
+        timer_channel_output_pulse_value_config(timer_periph, TIMER_CH_3, 7999);
+        timer_channel_output_mode_config(timer_periph, TIMER_CH_3, TIMER_OC_MODE_PWM0);
+        timer_channel_output_shadow_config(timer_periph, TIMER_CH_3, TIMER_OC_SHADOW_DISABLE);
         /* auto-reload preload shadow reg enable */
         /* timer_auto_reload_shadow_enable(timer_periph); */
-        timer_channel_output_state_config(timer_periph, gd32_pwm_obj[i].tim_handle.channel, TIMER_CCX_DISABLE);
-        timer_channel_complementary_output_state_config(timer_periph, gd32_pwm_obj[i].tim_handle.channel, TIMER_CCXN_DISABLE);
-    }
+        timer_channel_output_state_config(timer_periph, TIMER_CH_3, TIMER_CCX_DISABLE);
+        timer_channel_complementary_output_state_config(timer_periph, TIMER_CH_3, TIMER_CCXN_DISABLE);
+
+    /* L1 general-purpose timer */
+    case TIMER8:
+    case TIMER11:
+        timer_channel_output_config(timer_periph, TIMER_CH_1, ocpara);
+        timer_channel_output_pulse_value_config(timer_periph, TIMER_CH_1, 7999);
+        timer_channel_output_mode_config(timer_periph, TIMER_CH_1, TIMER_OC_MODE_PWM0);
+        timer_channel_output_shadow_config(timer_periph, TIMER_CH_1, TIMER_OC_SHADOW_DISABLE);
+        /* auto-reload preload shadow reg enable */
+        /* timer_auto_reload_shadow_enable(timer_periph); */
+        timer_channel_output_state_config(timer_periph, TIMER_CH_1, TIMER_CCX_DISABLE);
+        timer_channel_complementary_output_state_config(timer_periph, TIMER_CH_1, TIMER_CCXN_DISABLE);
+
+    /* L2 general-purpose timer */
+    case TIMER9:
+    case TIMER10:
+    case TIMER12:
+    case TIMER13:
+        timer_channel_output_config(timer_periph, TIMER_CH_0, ocpara);
+        timer_channel_output_pulse_value_config(timer_periph, TIMER_CH_0, 7999);
+        timer_channel_output_mode_config(timer_periph, TIMER_CH_0, TIMER_OC_MODE_PWM0);
+        timer_channel_output_shadow_config(timer_periph, TIMER_CH_0, TIMER_OC_SHADOW_DISABLE);
+        /* auto-reload preload shadow reg enable */
+        /* timer_auto_reload_shadow_enable(timer_periph); */
+        timer_channel_output_state_config(timer_periph, TIMER_CH_0, TIMER_CCX_DISABLE);
+        timer_channel_complementary_output_state_config(timer_periph, TIMER_CH_0, TIMER_CCXN_DISABLE);
+        break;
 
-    /* enable timer */
-    for (i = 0; i < sizeof(gd32_timer_periph_list.TimerIndex) / sizeof(gd32_timer_periph_list.TimerIndex[0]); ++i)
-    {
-        if (-1 != gd32_timer_periph_list.TimerIndex[i])
-        {
-            timer_periph = index_to_timer(gd32_timer_periph_list.TimerIndex[i]);
-            if (timer_periph == TIMER0 || timer_periph == TIMER7)
-            {
-                timer_primary_output_config(timer_periph, ENABLE);
-            }
-            timer_enable(timer_periph);
-        }
+    default:
+        LOG_E("Unsport timer periph at channel_output_config!\n");
+        break;
     }
+
+    timer_enable(timer_periph);
 }
 
 static void timer_config(void)
@@ -412,58 +489,103 @@ static void timer_config(void)
     timer_parameter_struct    timer_initpara;
 
     /* TIMER configuration */
-    timer_initpara.prescaler         = 119;
+    timer_initpara.prescaler         = 199;
     timer_initpara.alignedmode       = TIMER_COUNTER_EDGE;
     timer_initpara.counterdirection  = TIMER_COUNTER_UP;
     timer_initpara.period            = 15999;
     timer_initpara.clockdivision     = TIMER_CKDIV_DIV1;
     timer_initpara.repetitioncounter = 0;
-    timer_init_para(&timer_initpara);
+    for (size_t i = 0; i < sizeof(gd32_pwm_obj) / sizeof(gd32_pwm_obj[0]); ++i)
+    {
+        timer_init(gd32_pwm_obj[i].timerx, &timer_initpara);
+    }
 
     /* CHX configuration in PWM mode */
-    timer_ocintpara.outputstate  = TIMER_CCX_ENABLE;
+    timer_ocintpara.outputstate  = TIMER_CCX_DISABLE;
     timer_ocintpara.outputnstate = TIMER_CCXN_DISABLE;
     timer_ocintpara.ocpolarity   = TIMER_OC_POLARITY_HIGH;
     timer_ocintpara.ocnpolarity  = TIMER_OCN_POLARITY_HIGH;
     timer_ocintpara.ocidlestate  = TIMER_OC_IDLE_STATE_LOW;
     timer_ocintpara.ocnidlestate = TIMER_OCN_IDLE_STATE_LOW;
-    channel_output_config(&timer_ocintpara);
+    /* config the channel config */
+    for (size_t i = 0; i < sizeof(gd32_pwm_obj) / sizeof(gd32_pwm_obj[0]); ++i)
+    {
+        channel_output_config(gd32_pwm_obj[i].timerx, &timer_ocintpara);
+    }
 }
 
-static rt_err_t drv_pwm_enable(TIMER_PORT_CHANNEL_MAP_S *pstTimerMap, struct rt_pwm_configuration *configuration,
+static rt_err_t drv_pwm_enable(struct gd32_pwm *pwm, const struct rt_pwm_configuration *configuration,
                                rt_bool_t enable)
 {
+    if(channel_num == 0)
+    {
+        LOG_E("GD32 PWM channel starts from 1!\n");
+        return -RT_EINVAL;
+    }
+    if(configuration->complementary)
+    {
+        if(channel_num > 3)
+        {
+            LOG_E("GD32 PWM complementary channel max 3!\n");
+            return -RT_EINVAL;
+        };
+    }
+    else
+    {
+        if(channel_num > 4)
+        {
+            LOG_E("GD32 PWM complementary channel max 4!\n");
+            return -RT_EINVAL;
+        };
+    }
+
     if (!enable)
     {
-        timer_channel_output_state_config(index_to_timer(pstTimerMap->TimerIndex), configuration->channel,
-                                          TIMER_CCX_DISABLE);
+        gpio_config_input(pwm, configuration);
+        if (configuration->complementary == RT_TRUE)
+        {
+            timer_channel_complementary_output_state_config(pwm->timerx, configuration->channel-1,
+                TIMER_CCXN_DISABLE);
+        }
+        else
+        {
+            timer_channel_output_state_config(pwm->timerx, configuration->channel-1,
+                TIMER_CCX_DISABLE);
+        }
     }
     else
     {
+        gpio_config_pwmout(pwm, configuration);
         if (configuration->complementary == RT_TRUE)
         {
-            timer_channel_output_state_config(
-                index_to_timer(pstTimerMap->TimerIndex), configuration->channel - 1, TIMER_CCXN_ENABLE);
+            timer_channel_complementary_output_state_config(pwm->timerx, configuration->channel-1,
+                TIMER_CCXN_ENABLE);
         }
         else
         {
-            timer_channel_output_state_config(
-                index_to_timer(pstTimerMap->TimerIndex), configuration->channel, TIMER_CCX_ENABLE);
+            timer_channel_output_state_config(pwm->timerx, configuration->channel-1,
+                TIMER_CCX_ENABLE);
         }
     }
 
     return RT_EOK;
 }
 
-static rt_err_t drv_pwm_get(TIMER_PORT_CHANNEL_MAP_S *pstTimerMap, struct rt_pwm_configuration *configuration)
+static rt_err_t drv_pwm_get(const struct gd32_pwm *pwm, struct rt_pwm_configuration *configuration)
 {
     rt_uint64_t tim_clock;
     rt_uint16_t psc;
     rt_uint32_t chxcv;
 
-    tim_clock = rcu_clock_freq_get(CK_SYS);
+    if(configuration->channel == 0)
+    {
+        return -RT_EINVAL;
+    }
+
+    rt_uint8_t coef = (RCU_CFG1&RCU_CFG1_TIMERSEL)?4:2;
+    tim_clock = rcu_clock_freq_get(pwm->apb_of)*coef;
 
-    psc = timer_prescaler_read(index_to_timer(pstTimerMap->TimerIndex));
+    psc = timer_prescaler_read(pwm->timerx);
     if (psc == TIMER_CKDIV_DIV2)
     {
         tim_clock = tim_clock / 2;
@@ -473,21 +595,27 @@ static rt_err_t drv_pwm_get(TIMER_PORT_CHANNEL_MAP_S *pstTimerMap, struct rt_pwm
         tim_clock = tim_clock / 4;
     }
 
-    chxcv = timer_channel_capture_value_register_read(index_to_timer(pstTimerMap->TimerIndex), configuration->channel);
+    chxcv = timer_channel_capture_value_register_read(pwm->timerx, configuration->channel-1);
     /* Convert nanosecond to frequency and duty cycle. 1s = 1 * 1000 * 1000 * 1000 ns */
     tim_clock             /= 1000000UL;
-    configuration->period  = (TIMER_CAR(index_to_timer(pstTimerMap->TimerIndex)) + 1) * (psc + 1) * 1000UL / tim_clock;
+    configuration->period  = (TIMER_CAR(pwm->timerx) + 1) * (psc + 1) * 1000UL / tim_clock;
     configuration->pulse   = (chxcv + 1) * (psc + 1) * 1000UL / tim_clock;
 
     return RT_EOK;
 }
 
-static rt_err_t drv_pwm_set(TIMER_PORT_CHANNEL_MAP_S *pstTimerMap, struct rt_pwm_configuration *configuration)
+static rt_err_t drv_pwm_set(struct gd32_pwm *pwm, struct rt_pwm_configuration *configuration)
 {
     rt_uint32_t period, pulse;
     rt_uint64_t tim_clock, psc;
 
-    tim_clock = rcu_clock_freq_get(CK_SYS);
+    rt_uint8_t coef = (RCU_CFG1&RCU_CFG1_TIMERSEL)?4:2;
+    tim_clock = rcu_clock_freq_get(pwm->apb_of)*coef;
+
+    if(configuration->channel == 0)
+    {
+        return -RT_EINVAL;
+    }
 
     /* Convert nanosecond to frequency and duty cycle. 1s = 1 * 1000 * 1000 * 1000 ns */
     tim_clock /= 1000000UL;
@@ -495,14 +623,14 @@ static rt_err_t drv_pwm_set(TIMER_PORT_CHANNEL_MAP_S *pstTimerMap, struct rt_pwm
     psc        = period / MAX_PERIOD + 1;
     period     = period / psc;
 
-    timer_prescaler_config(index_to_timer(pstTimerMap->TimerIndex), psc - 1, TIMER_PSC_RELOAD_NOW);
+    timer_prescaler_config(pwm->timerx, psc - 1, TIMER_PSC_RELOAD_NOW);
 
     if (period < MIN_PERIOD)
     {
         period = MIN_PERIOD;
     }
 
-    timer_autoreload_value_config(index_to_timer(pstTimerMap->TimerIndex), period - 1);
+    timer_autoreload_value_config(pwm->timerx, period - 1);
 
     pulse = (unsigned long long)configuration->pulse * tim_clock / psc / 1000ULL;
     if (pulse < MIN_PULSE)
@@ -514,11 +642,11 @@ static rt_err_t drv_pwm_set(TIMER_PORT_CHANNEL_MAP_S *pstTimerMap, struct rt_pwm
         pulse = period;
     }
 
-    timer_channel_output_pulse_value_config(index_to_timer(pstTimerMap->TimerIndex), configuration->channel, pulse);
-    timer_counter_value_config(index_to_timer(pstTimerMap->TimerIndex), 0);
+    timer_channel_output_pulse_value_config(pwm->timerx, configuration->channel-1, pulse);
+    timer_counter_value_config(pwm->timerx, 0);
 
     /* Update frequency value */
-    timer_event_software_generate(index_to_timer(pstTimerMap->TimerIndex), TIMER_EVENT_SRC_UPG);
+    timer_event_software_generate(pwm->timerx, TIMER_EVENT_SRC_UPG);
 
     return RT_EOK;
 }
@@ -526,18 +654,18 @@ static rt_err_t drv_pwm_set(TIMER_PORT_CHANNEL_MAP_S *pstTimerMap, struct rt_pwm
 static rt_err_t drv_pwm_control(struct rt_device_pwm *device, int cmd, void *arg)
 {
     struct rt_pwm_configuration *configuration = (struct rt_pwm_configuration *)arg;
-    TIMER_PORT_CHANNEL_MAP_S    *pstTimerMap   = (TIMER_PORT_CHANNEL_MAP_S *)device->parent.user_data;
+    struct gd32_pwm *pwm = rt_container_of(device, struct gd32_pwm, pwm_device);
 
     switch (cmd)
     {
     case PWM_CMD_ENABLE:
-        return drv_pwm_enable(pstTimerMap, configuration, RT_TRUE);
+        return drv_pwm_enable(pwm, configuration, RT_TRUE);
     case PWM_CMD_DISABLE:
-        return drv_pwm_enable(pstTimerMap, configuration, RT_FALSE);
+        return drv_pwm_enable(pwm, configuration, RT_FALSE);
     case PWM_CMD_SET:
-        return drv_pwm_set(pstTimerMap, configuration);
+        return drv_pwm_set(pwm, configuration);
     case PWM_CMD_GET:
-        return drv_pwm_get(pstTimerMap, configuration);
+        return drv_pwm_get(pwm, configuration);
     default:
         return -RT_EINVAL;
     }
@@ -547,15 +675,19 @@ static struct rt_pwm_ops drv_ops = {drv_pwm_control};
 
 static rt_err_t gd32_hw_pwm_init(void)
 {
-    pwm_find_timer_periph();
     rcu_config();
-    gpio_config();
     timer_config();
 
+    /*
+     * GPIO is not configured here. When PWM channel is enabled, it will be configured as PWM output.
+     * When disabled, it will be configured as floating input.
+     * GPIO defaults to floating input.
+    */
+
     return RT_EOK;
 }
 
-static int gd32_pwm_init(void)
+static int rt_hw_pwm_init(void)
 {
     int i      = 0;
     int result = RT_EOK;
@@ -573,14 +705,14 @@ static int gd32_pwm_init(void)
     for (i = 0; i < sizeof(gd32_pwm_obj) / sizeof(gd32_pwm_obj[0]); i++)
     {
         /* register pwm device */
-        if (rt_device_pwm_register(&gd32_pwm_obj[i].pwm_device, gd32_pwm_obj[i].tim_handle.name, &drv_ops,
-                                   &gd32_pwm_obj[i].tim_handle)== RT_EOK )
+        if (rt_device_pwm_register(&gd32_pwm_obj[i].pwm_device, gd32_pwm_obj[i].name, &drv_ops,
+                                   RT_NULL)== RT_EOK )
         {
-            LOG_D("%s register success", gd32_pwm_obj[i].tim_handle.name);
+            LOG_D("%s register success", gd32_pwm_obj[i].name);
         }
         else
         {
-            LOG_E("%s register failed", gd32_pwm_obj[i].tim_handle.name);
+            LOG_E("%s register failed", gd32_pwm_obj[i].name);
             result = -RT_ERROR;
         }
     }
@@ -588,6 +720,5 @@ static int gd32_pwm_init(void)
 __exit:
     return result;
 }
-INIT_DEVICE_EXPORT(gd32_pwm_init);
-#endif /* RT_USING_PWM */
-
+INIT_DEVICE_EXPORT(rt_hw_pwm_init);
+#endif /* BSP_USING_PWM */