ソースを参照

[bsp][gd32]:add gd32vw533xx pwm support

CYFS 1 週間 前
コミット
1c0926f223

+ 9 - 0
bsp/gd32/risc-v/gd32vw553h-eval/.ci/attachconfig/ci.attachconfig.yml

@@ -0,0 +1,9 @@
+scons.args: &scons
+    scons_arg:
+      - '--strict'
+
+# ------ component CI ------
+component.pwm:
+    kconfig:
+      - CONFIG_BSP_USING_PWM=y
+      - CONFIG_BSP_USING_PWM0=y

+ 24 - 0
bsp/gd32/risc-v/gd32vw553h-eval/board/Kconfig

@@ -78,6 +78,30 @@ menu "On-chip Peripheral Drivers"
                 default n
         endif
 
+    menuconfig BSP_USING_PWM
+        bool "Enable PWM"
+        default n
+        select RT_USING_PWM
+        if BSP_USING_PWM
+            config BSP_USING_PWM0
+                bool "Enable PWM0"
+                default y
+            config BSP_USING_PWM1
+                bool "Enable PWM1"
+                default n
+            config BSP_USING_PWM2
+                bool "Enable PWM2"
+                default n
+            config BSP_USING_PWM15
+                bool "Enable PWM15"
+                default n
+            config BSP_USING_PWM16
+                bool "Enable PWM16"
+                default n
+        endif
+
+
+
     source "$(BSP_DIR)/../libraries/gd32_drivers/Kconfig"
 
 endmenu

+ 4 - 0
bsp/gd32/risc-v/libraries/gd32_drivers/SConscript

@@ -45,6 +45,10 @@ if GetDepend('RT_USING_CLOCK_TIME'):
 if GetDepend('RT_USING_ADC'):
     src += ['drv_adc.c']
 
+# add pwm drivers.
+if GetDepend(['RT_USING_PWM', 'SOC_GD32VW553H']):
+    src += ['drv_pwm.c']
+
 path = [cwd]
 
 group = DefineGroup('Drivers', src, depend = [''], CPPPATH = path)

+ 591 - 0
bsp/gd32/risc-v/libraries/gd32_drivers/drv_pwm.c

@@ -0,0 +1,591 @@
+/*
+ * Copyright (c) 2006-2022, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date           Author            Notes
+ * 2026-01-27     CYFS              first version
+ */
+
+#include <board.h>
+#include <rtdevice.h>
+#include <rtthread.h>
+
+#ifdef RT_USING_PWM
+
+#define DBG_TAG             "drv.pwm"
+#define DBG_LVL             DBG_LOG
+
+#include <rtdbg.h>
+
+#define MAX_PERIOD 65535
+#define MIN_PERIOD 3
+#define MIN_PULSE  2
+
+
+typedef struct gd32_pwm_pin
+{
+    rt_uint32_t Port;
+    rt_uint32_t pin;
+    rt_uint32_t alt_func_num;
+    rt_int16_t  channel;
+    rt_int8_t   TimerIndex;
+
+} gd32_pwm_pin_t;
+
+static gd32_pwm_pin_t gd32_pwm_pin_obj[] = {
+#ifdef BSP_USING_PWM0
+    {
+        .Port         = GPIOA,
+        .pin          = GPIO_PIN_9,
+        .alt_func_num = GPIO_AF_1,
+        .channel      = TIMER_CH_1,
+        .TimerIndex   = 0,
+    },
+    {
+        .Port         = GPIOA,
+        .pin          = GPIO_PIN_10,
+        .alt_func_num = GPIO_AF_1,
+        .channel      = TIMER_CH_2,
+        .TimerIndex   = 0,
+    },
+    {
+        .Port         = GPIOA,
+        .pin          = GPIO_PIN_11,
+        .alt_func_num = GPIO_AF_1,
+        .channel      = TIMER_CH_3,
+        .TimerIndex   = 0,
+    },
+#endif
+
+#ifdef BSP_USING_PWM1
+{
+        .Port         = GPIOA,
+        .pin          = GPIO_PIN_0,
+        .alt_func_num = GPIO_AF_1,
+        .channel      = TIMER_CH_0,
+        .TimerIndex   = 1,
+    },
+    {
+        .Port         = GPIOA,
+        .pin          = GPIO_PIN_1,
+        .alt_func_num = GPIO_AF_1,
+        .channel      = TIMER_CH_1,
+        .TimerIndex   = 1,
+    },
+    {
+        .Port         = GPIOA,
+        .pin          = GPIO_PIN_2,
+        .alt_func_num = GPIO_AF_1,
+        .channel      = TIMER_CH_2,
+        .TimerIndex   = 1,
+    },
+    {
+        .Port         = GPIOA,
+        .pin          = GPIO_PIN_3,
+        .alt_func_num = GPIO_AF_1,
+        .channel      = TIMER_CH_3,
+        .TimerIndex   = 1,
+    },
+#endif
+
+#ifdef BSP_USING_PWM2
+    {
+        .Port         = GPIOA,
+        .pin          = GPIO_PIN_6,
+        .alt_func_num = GPIO_AF_2,
+        .channel      = TIMER_CH_0,
+        .TimerIndex   = 2,
+    },
+    {
+        .Port         = GPIOA,
+        .pin          = GPIO_PIN_7,
+        .alt_func_num = GPIO_AF_2,
+        .channel      = TIMER_CH_1,
+        .TimerIndex   = 2,
+    },
+
+#endif
+
+#ifdef BSP_USING_PWM15
+    {
+        .Port         = GPIOB,
+        .pin          = GPIO_PIN_13,
+        .alt_func_num = GPIO_AF_8,
+        .channel      = TIMER_CH_0,
+        .TimerIndex   = 15,
+    },
+#endif
+
+#ifdef BSP_USING_PWM16
+    {
+        .Port         = GPIOB,
+        .pin          = GPIO_PIN_0,
+        .alt_func_num = GPIO_AF_9,
+        .channel      = TIMER_CH_0,
+        .TimerIndex   = 16,
+    },
+#endif
+
+};
+
+typedef struct gd32_pwm_device
+{
+    struct rt_device_pwm pwm_device;
+    rt_int8_t   TimerIndex;
+    char *name;
+} gd32_pwm_device_t;
+
+
+static gd32_pwm_device_t gd32_pwm_obj[] = {
+#ifdef BSP_USING_PWM0
+    {
+        .name = "pwm0",
+        .TimerIndex = 0,
+    },
+#endif
+#ifdef BSP_USING_PWM1
+    {
+        .name = "pwm1",
+        .TimerIndex = 1,
+    },
+#endif
+#ifdef BSP_USING_PWM2
+    {
+        .name = "pwm2",
+        .TimerIndex = 2,
+    },
+#endif
+#ifdef BSP_USING_PWM15
+    {
+        .name = "pwm15",
+        .TimerIndex = 15,
+    },
+#endif
+#ifdef BSP_USING_PWM16
+    {
+        .name = "pwm16",
+        .TimerIndex = 16,
+    },
+#endif
+};
+
+typedef struct
+{
+    rt_uint32_t Port[3];
+    rt_int8_t   TimerIndex[5];
+} TIMER_PERIPH_LIST_S;
+
+static TIMER_PERIPH_LIST_S gd32_timer_periph_list = {
+    .Port       = {0, 0, 0},
+    .TimerIndex = {-1, -1, -1, -1, -1},
+};
+
+/*
+ * List all used GPIO ports and timers without duplication to avoid repeated initialization later.
+ */
+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_pin_obj) / sizeof(gd32_pwm_pin_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_pin_obj[i].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_pin_obj[i].Port;
+        }
+    }
+
+    /* find timer periph of defined table */
+    for (i = 0; i < sizeof(gd32_pwm_pin_obj) / sizeof(gd32_pwm_pin_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_pin_obj[i].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_pin_obj[i].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 15:
+        return TIMER15;
+    case 16:
+        return TIMER16;
+    default:
+        LOG_E("Unsupported timer periph!\n");
+    }
+    return TIMER0;
+}
+
+static void gpio_clock_enable(rt_uint32_t Port)
+{
+    switch (Port)
+    {
+    case GPIOA:
+        rcu_periph_clock_enable(RCU_GPIOA);
+        break;
+    case GPIOB:
+        rcu_periph_clock_enable(RCU_GPIOB);
+        break;
+    case GPIOC:
+        rcu_periph_clock_enable(RCU_GPIOC);
+        break;
+    default:
+        LOG_E("Unsupported gpio port!\n");
+    }
+}
+
+static void timer_clock_enable(rt_int8_t TimerIndex)
+{
+    switch (TimerIndex)
+    {
+    case 0:
+        rcu_periph_clock_enable(RCU_TIMER0);
+        break;
+    case 1:
+        rcu_periph_clock_enable(RCU_TIMER1);
+        break;
+    case 2:
+        rcu_periph_clock_enable(RCU_TIMER2);
+        break;
+    case 15:
+        rcu_periph_clock_enable(RCU_TIMER15);
+        break;
+    case 16:
+        rcu_periph_clock_enable(RCU_TIMER16);
+        break;
+    default:
+        LOG_E("Unsport timer periph!\n");
+    }
+}
+static void gpio_config(void)
+{
+    rt_int16_t i;
+    /* configure GPIO pins in alternate-function mode for TIMER PWM output */
+    for (i = 0; i < sizeof(gd32_pwm_pin_obj) / sizeof(gd32_pwm_pin_obj[0]); ++i)
+    {
+        /* configure pin as alternate function for the corresponding TIMER channel */
+        gpio_mode_set(gd32_pwm_pin_obj[i].Port, GPIO_MODE_AF, GPIO_PUPD_NONE, gd32_pwm_pin_obj[i].pin);
+        gpio_output_options_set(gd32_pwm_pin_obj[i].Port, GPIO_OTYPE_PP, GPIO_OSPEED_MAX, gd32_pwm_pin_obj[i].pin);
+        gpio_af_set(gd32_pwm_pin_obj[i].Port, gd32_pwm_pin_obj[i].alt_func_num, gd32_pwm_pin_obj[i].pin);
+    }
+}
+
+static void rcu_config(void)
+{
+    rt_int16_t i;
+    rcu_timer_clock_prescaler_config(RCU_TIMER_PSC_MUL4);
+    for (i = 0; i < sizeof(gd32_timer_periph_list.Port) / sizeof(gd32_timer_periph_list.Port[0]); ++i)
+    {
+        if (0 == gd32_timer_periph_list.Port[i])
+        {
+            break;
+        }
+
+        /* enable GPIO clock */
+        gpio_clock_enable(gd32_timer_periph_list.Port[i]);
+    }
+
+    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])
+        {
+            break;
+        }
+
+        /* enable timer clock */
+        timer_clock_enable(gd32_timer_periph_list.TimerIndex[i]);
+        timer_deinit(index_to_timer(gd32_timer_periph_list.TimerIndex[i]));
+    }
+}
+
+static void timer_init_para(timer_parameter_struct *initpara)
+{
+    rt_int16_t i;
+
+    for (i = 0; i < sizeof(gd32_timer_periph_list.TimerIndex) / sizeof(gd32_timer_periph_list.TimerIndex[0]); ++i)
+    {
+        /* config timer */
+        if (-1 != gd32_timer_periph_list.TimerIndex[i])
+        {
+            timer_init(index_to_timer(gd32_timer_periph_list.TimerIndex[i]), initpara);
+        }
+    }
+}
+
+static void channel_output_config(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_pin_obj) / sizeof(gd32_pwm_pin_obj[0]); ++i)
+    {
+
+        timer_periph = index_to_timer(gd32_pwm_pin_obj[i].TimerIndex);
+        timer_channel_output_config(timer_periph, gd32_pwm_pin_obj[i].channel, ocpara);
+
+        timer_channel_output_pulse_value_config(timer_periph, gd32_pwm_pin_obj[i].channel, 7999);
+        timer_channel_output_mode_config(timer_periph, gd32_pwm_pin_obj[i].channel, TIMER_OC_MODE_PWM0);
+        timer_channel_output_shadow_config(timer_periph, gd32_pwm_pin_obj[i].channel, 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_pin_obj[i].channel, TIMER_CCX_DISABLE);
+        timer_channel_complementary_output_state_config(timer_periph, gd32_pwm_pin_obj[i].channel, TIMER_CCXN_DISABLE);
+    }
+
+    /* 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_primary_output_config(timer_periph, ENABLE);
+            }
+            timer_enable(timer_periph);
+        }
+    }
+}
+
+static void timer_config(void)
+{
+    timer_oc_parameter_struct timer_ocintpara;
+    timer_parameter_struct    timer_initpara;
+
+    /* TIMER configuration */
+    timer_initpara.prescaler         = 119;
+    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);
+
+    /* CHX configuration in PWM mode */
+    timer_ocintpara.outputstate  = TIMER_CCX_ENABLE;
+    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);
+}
+
+static rt_err_t gd32_hw_pwm_init(void)
+{
+    pwm_find_timer_periph();
+    rcu_config();
+    gpio_config();
+    timer_config();
+    return RT_EOK;
+}
+
+static rt_err_t drv_pwm_enable(gd32_pwm_device_t *pstTimerMap,
+                      struct rt_pwm_configuration *configuration,
+                      rt_bool_t enable)
+{
+    uint32_t timer_periph = index_to_timer(pstTimerMap->TimerIndex);
+    uint32_t channel = configuration->channel;
+    
+    if ((channel != TIMER_CH_0) &&
+        (channel != TIMER_CH_1) &&
+        (channel != TIMER_CH_2) &&
+        (channel != TIMER_CH_3))
+    {
+        return -RT_EINVAL;
+    }
+
+    if (enable)
+    {
+        timer_channel_output_state_config(timer_periph, channel, TIMER_CCX_ENABLE);
+    }
+    else
+    {
+        timer_channel_output_state_config(timer_periph, channel, TIMER_CCX_DISABLE);
+    }
+
+    return RT_EOK;
+}
+
+static rt_err_t drv_pwm_set(gd32_pwm_device_t *pstTimerMap, struct rt_pwm_configuration *configuration)
+{
+    rt_uint32_t period, pulse;
+    rt_uint64_t tim_clock, psc;
+    tim_clock = rcu_clock_freq_get(CK_SYS);
+
+    /* Convert nanosecond to frequency and duty cycle. 1s = 1 * 1000 * 1000 * 1000 ns */
+    tim_clock /= 1000000UL;
+    period     = (unsigned long long)configuration->period * tim_clock / 1000ULL;
+    psc        = period / MAX_PERIOD + 1;
+    period     = period / psc;
+
+    timer_prescaler_config(index_to_timer(pstTimerMap->TimerIndex), psc - 1, TIMER_PSC_RELOAD_NOW);
+
+    if (period < MIN_PERIOD)
+    {
+        period = MIN_PERIOD;
+    }
+
+    timer_autoreload_value_config(index_to_timer(pstTimerMap->TimerIndex), period - 1);
+
+    pulse = (unsigned long long)configuration->pulse * tim_clock / psc / 1000ULL;
+    if (pulse < MIN_PULSE)
+    {
+        pulse = MIN_PULSE;
+    }
+    else if (pulse > period)
+    {
+        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);
+
+    /* Update frequency value */
+    timer_event_software_generate(index_to_timer(pstTimerMap->TimerIndex), TIMER_EVENT_SRC_UPG);
+
+    return RT_EOK;
+}
+
+
+static rt_err_t drv_pwm_get(gd32_pwm_device_t *pstTimerMap, 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);
+
+    psc = timer_prescaler_read(index_to_timer(pstTimerMap->TimerIndex));
+    if (psc == TIMER_CKDIV_DIV2)
+    {
+        tim_clock = tim_clock / 2;
+    }
+    else if (psc == TIMER_CKDIV_DIV4)
+    {
+        tim_clock = tim_clock / 4;
+    }
+
+    chxcv = timer_channel_capture_value_register_read(index_to_timer(pstTimerMap->TimerIndex), configuration->channel);
+    /* 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->pulse   = (chxcv + 1) * (psc + 1) * 1000UL / tim_clock;
+
+    return RT_EOK;
+}
+
+
+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;
+    gd32_pwm_device_t    *pstTimerMap   = (gd32_pwm_device_t *)device->parent.user_data;
+
+    switch (cmd)
+    {
+    case PWM_CMD_ENABLE:
+        return drv_pwm_enable(pstTimerMap, configuration, RT_TRUE);
+    case PWM_CMD_DISABLE:
+        return drv_pwm_enable(pstTimerMap, configuration, RT_FALSE);
+    case PWM_CMD_SET:
+        return drv_pwm_set(pstTimerMap, configuration);
+    case PWM_CMD_GET:
+        return drv_pwm_get(pstTimerMap, configuration);
+    default:
+        return -RT_EINVAL;
+}
+}
+
+static struct rt_pwm_ops drv_ops = {drv_pwm_control};
+
+static int gd32_pwm_init(void)
+{
+    int i      = 0;
+    int result = RT_EOK;
+
+    /* pwm init */
+    gd32_hw_pwm_init();
+    LOG_D("PWM init success");
+
+    for (i = 0; i < sizeof(gd32_pwm_obj) / sizeof(gd32_pwm_obj[0]); i++)
+    {
+        if (rt_device_pwm_register(&gd32_pwm_obj[i].pwm_device, gd32_pwm_obj[i].name, &drv_ops, &gd32_pwm_obj[i]) == RT_EOK)
+        {
+            LOG_D("%s register success", gd32_pwm_obj[i].name);
+        }
+        else
+        {
+            LOG_E("%s register failed", gd32_pwm_obj[i].name);
+            result = -RT_ERROR;
+        }
+    }
+    return result;
+}
+
+INIT_DEVICE_EXPORT(gd32_pwm_init);
+
+#endif /* RT_USING_PWM */