123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352 |
- /*
- * Copyright (c) 2006-2018, RT-Thread Development Team
- *
- * SPDX-License-Identifier: Apache-2.0
- *
- * Change Logs:
- * Date Author Notes
- * 2020-06-02 hqfang first version
- */
- #include "drv_pwm.h"
- // #define DBG_LVL DBG_INFO
- #include <rtdbg.h>
- #ifdef BSP_USING_PWM
- #if !defined(BSP_USING_PWM0) && !defined(BSP_USING_PWM1) && !defined(BSP_USING_PWM2) \
- && !defined(BSP_USING_PWM3) && !defined(BSP_USING_PWM4)
- #error "Please define at least one BSP_USING_PWMx"
- /* this driver can be disabled at menuconfig -> Hardware Drivers Config -> On-chip Peripheral Drivers -> Enable PWM */
- #endif
- static struct gd32_pwm_config pwm_config[] =
- {
- #ifdef BSP_USING_PWM0
- {
- "pwm0",
- TIMER0,
- 1000000,
- },
- #endif
- #ifdef BSP_USING_PWM1
- {
- "pwm1",
- TIMER1,
- 1000000,
- },
- #endif
- #ifdef BSP_USING_PWM2
- {
- "pwm2",
- TIMER2,
- 1000000,
- },
- #endif
- #ifdef BSP_USING_PWM3
- {
- "pwm3",
- TIMER3,
- 1000000,
- },
- #endif
- #ifdef BSP_USING_PWM4
- {
- "pwm4",
- TIMER4,
- 1000000,
- },
- #endif
- };
- #define GD32_MAX_PWM_CHANNELS TIMER_CH_3
- static struct gd32_pwm pwm_obj[sizeof(pwm_config) / sizeof(pwm_config[0])] = {0};
- static rt_err_t gd32_pwm_enable(struct rt_device_pwm *device, struct rt_pwm_configuration *configuration, rt_bool_t enable)
- {
- struct gd32_pwm_config *config;
- config = (struct gd32_pwm_config *)device->parent.user_data;
- RT_ASSERT(config);
- if (configuration->channel > GD32_MAX_PWM_CHANNELS)
- {
- return RT_EINVAL;
- }
- if (!enable)
- {
- timer_channel_output_state_config(config->periph, configuration->channel, TIMER_CCX_DISABLE);
- }
- else
- {
- timer_channel_output_state_config(config->periph, configuration->channel, TIMER_CCX_ENABLE);
- }
- return RT_EOK;
- }
- static uint32_t gd32_get_pwm_clk(rt_uint32_t periph)
- {
- uint32_t clk;
- uint8_t clkpre;
- if (periph != TIMER0)
- {
- clk = rcu_clock_freq_get(CK_APB1);
- clkpre = GET_BITS(RCU_CFG0, 8, 10);
- }
- else
- {
- clk = rcu_clock_freq_get(CK_APB2);
- clkpre = GET_BITS(RCU_CFG0, 11, 13);
- }
- if (clkpre >= 4)
- {
- clk = clk * 2;
- }
- return clk;
- }
- static rt_err_t gd32_pwm_get(struct rt_device_pwm *device, struct rt_pwm_configuration *configuration)
- {
- uint32_t pwmclk;
- uint16_t prescale, period, clkdiv, pulse;
- struct gd32_pwm_config *config;
- config = (struct gd32_pwm_config *)device->parent.user_data;
- RT_ASSERT(config);
- pwmclk = gd32_get_pwm_clk(config->periph);
- prescale = (uint16_t)TIMER_PSC(config->periph) + 1;
- clkdiv = ((uint16_t)(TIMER_CTL0(config->periph) & TIMER_CTL0_CKDIV) >> 8);
- clkdiv = 1 << clkdiv;
- period = (uint16_t)TIMER_CAR(config->periph) + 1;
- pulse = (uint16_t)REG32((config->periph) + 0x34U + configuration->channel << 2) + 1;
- pwmclk = pwmclk / prescale / clkdiv;
- LOG_I("current pwmclk is %d\n", pwmclk);
- configuration->period = (uint64_t)period * 1000000000 / pwmclk;
- configuration->pulse = (uint64_t)pulse * 1000000000 / pwmclk;
- return RT_EOK;
- }
- static rt_err_t gd32_pwm_set(struct rt_device_pwm *device, struct rt_pwm_configuration *configuration)
- {
- timer_oc_parameter_struct timer_ocinitpara;
- timer_parameter_struct timer_initpara;
- uint32_t pwmclk, pwmclkv2;
- uint64_t period_cmp;
- uint16_t prescale, period, clkdiv, pulse;
- struct gd32_pwm_config *config;
- config = (struct gd32_pwm_config *)device->parent.user_data;
- RT_ASSERT(config);
- if (configuration->channel > GD32_MAX_PWM_CHANNELS)
- {
- LOG_I("max channel supported is %d\n", GD32_MAX_PWM_CHANNELS);
- return RT_EINVAL;
- }
- if (configuration->period < configuration->pulse)
- {
- LOG_I("period should > pulse \n");
- return RT_EINVAL;
- }
- pwmclk = gd32_get_pwm_clk(config->periph);
- // min period value >= 100
- period_cmp = (uint64_t)(1000000000 / pwmclk) * 10;
- if (configuration->period < period_cmp)
- {
- return RT_EINVAL;
- }
- period_cmp = (uint64_t)(1000000000 / (pwmclk / 65536 / 4)) * 65536;
- if (configuration->period > period_cmp)
- {
- return RT_EINVAL;
- }
- period_cmp = (uint64_t) pwmclk * configuration->period / 1000000000;
- if (period_cmp < 65536)
- {
- prescale = 0;
- clkdiv = TIMER_CKDIV_DIV1;
- period = period_cmp;
- }
- else if (period_cmp < 4294967296)
- {
- prescale = period_cmp / 65536;
- period = period_cmp / (prescale + 1);
- clkdiv = TIMER_CKDIV_DIV1;
- }
- else if (period_cmp < 8589934592)
- {
- prescale = period_cmp / 65536;
- period = period_cmp / (prescale + 1) / 2;
- clkdiv = TIMER_CKDIV_DIV2;
- }
- else
- {
- prescale = period_cmp / 65536;
- period = period_cmp / (prescale + 1) / 4;
- clkdiv = TIMER_CKDIV_DIV4;
- }
- pwmclkv2 = pwmclk / (prescale + 1) / (1 << clkdiv);
- LOG_I("current pwmclk is %d\n", pwmclkv2);
- LOG_I("Set channel %d, period %dns, pulse %dns\n", configuration->channel, \
- configuration->period, configuration->pulse);
- pulse = (uint64_t)period * configuration->pulse / configuration->period;
- LOG_I("pwmclk %d, pwmcmp %d, prescale %d, period %d, pulse %d, clkdiv %d\n", \
- pwmclk, (uint32_t)period_cmp, prescale, period, pulse, clkdiv);
- /* initialize TIMER init parameter struct */
- timer_struct_para_init(&timer_initpara);
- /* TIMER configuration */
- timer_initpara.prescaler = prescale;
- timer_initpara.alignedmode = TIMER_COUNTER_EDGE;
- timer_initpara.counterdirection = TIMER_COUNTER_UP;
- timer_initpara.period = period;
- timer_initpara.clockdivision = clkdiv;
- timer_initpara.repetitioncounter = 0;
- timer_init(config->periph, &timer_initpara);
- /* initialize TIMER channel output parameter struct */
- timer_channel_output_struct_para_init(&timer_ocinitpara);
- /* CH0, CH1 and CH2 configuration in PWM mode */
- timer_ocinitpara.outputstate = TIMER_CCX_DISABLE;
- timer_ocinitpara.outputnstate = TIMER_CCXN_DISABLE;
- timer_ocinitpara.ocpolarity = TIMER_OC_POLARITY_HIGH;
- timer_ocinitpara.ocnpolarity = TIMER_OCN_POLARITY_HIGH;
- timer_ocinitpara.ocidlestate = TIMER_OC_IDLE_STATE_LOW;
- timer_ocinitpara.ocnidlestate = TIMER_OCN_IDLE_STATE_LOW;
- timer_channel_output_config(config->periph, configuration->channel, &timer_ocinitpara);
- /* Channel configuration in PWM mode */
- timer_channel_output_pulse_value_config(config->periph, configuration->channel, pulse);
- timer_channel_output_mode_config(config->periph, configuration->channel, TIMER_OC_MODE_PWM0);
- timer_channel_output_shadow_config(config->periph, configuration->channel, TIMER_OC_SHADOW_DISABLE);
- timer_primary_output_config(config->periph, ENABLE);
- /* auto-reload preload enable */
- timer_auto_reload_shadow_enable(config->periph);
- timer_enable(config->periph);
- return RT_EOK;
- }
- static rt_err_t gd32_pwm_control(struct rt_device_pwm *device, int cmd, void *arg)
- {
- struct rt_pwm_configuration *configuration = (struct rt_pwm_configuration *)arg;
- switch (cmd)
- {
- case PWM_CMD_ENABLE:
- return gd32_pwm_enable(device, configuration, RT_TRUE);
- case PWM_CMD_DISABLE:
- return gd32_pwm_enable(device, configuration, RT_FALSE);
- case PWM_CMD_SET:
- return gd32_pwm_set(device, configuration);
- case PWM_CMD_GET:
- return gd32_pwm_get(device, configuration);
- default:
- return RT_EINVAL;
- }
- }
- static rt_err_t gd32_pwm_init(struct gd32_pwm_config *config)
- {
- timer_oc_parameter_struct timer_ocinitpara;
- timer_parameter_struct timer_initpara;
- uint32_t pwmclk;
- uint16_t prescale;
- pwmclk = gd32_get_pwm_clk(config->periph);
- /* period 1ms, duty 50% */
- prescale = pwmclk / 1000 / 1000 - 1;
- config->period = 1000000;
- LOG_I("pwmclk %d, prescale %d, period %d, clkdiv %d\n", pwmclk, prescale, 999, 0);
- /* initialize TIMER init parameter struct */
- timer_struct_para_init(&timer_initpara);
- /* TIMER configuration */
- timer_initpara.prescaler = prescale;
- timer_initpara.alignedmode = TIMER_COUNTER_EDGE;
- timer_initpara.counterdirection = TIMER_COUNTER_UP;
- timer_initpara.period = 999;
- timer_initpara.clockdivision = TIMER_CKDIV_DIV1;
- timer_initpara.repetitioncounter = 0;
- timer_init(config->periph, &timer_initpara);
- /* initialize TIMER channel output parameter struct */
- timer_channel_output_struct_para_init(&timer_ocinitpara);
- /* CH0, CH1 and CH2 configuration in PWM mode */
- timer_ocinitpara.outputstate = TIMER_CCX_DISABLE;
- timer_ocinitpara.outputnstate = TIMER_CCXN_DISABLE;
- timer_ocinitpara.ocpolarity = TIMER_OC_POLARITY_HIGH;
- timer_ocinitpara.ocnpolarity = TIMER_OCN_POLARITY_HIGH;
- timer_ocinitpara.ocidlestate = TIMER_OC_IDLE_STATE_LOW;
- timer_ocinitpara.ocnidlestate = TIMER_OCN_IDLE_STATE_LOW;
- /* Channel configuration in PWM mode */
- for (int i = 0; i <= GD32_MAX_PWM_CHANNELS; i ++)
- {
- timer_channel_output_config(config->periph, i, &timer_ocinitpara);
- timer_channel_output_pulse_value_config(config->periph, i, 499);
- timer_channel_output_mode_config(config->periph, i, TIMER_OC_MODE_PWM0);
- timer_channel_output_shadow_config(config->periph, i, TIMER_OC_SHADOW_DISABLE);
- }
- timer_primary_output_config(config->periph, ENABLE);
- /* auto-reload preload enable */
- timer_auto_reload_shadow_enable(config->periph);
- timer_enable(config->periph);
- return RT_EOK;
- }
- static struct rt_pwm_ops gd32_drv_ops =
- {
- .control = gd32_pwm_control
- };
- static int rt_pwm_init(void)
- {
- int i = 0;
- int result = RT_EOK;
- #ifdef BSP_USING_PWM0
- rcu_periph_clock_enable(RCU_TIMER0);
- #endif
- #ifdef BSP_USING_PWM1
- rcu_periph_clock_enable(RCU_TIMER1);
- #endif
- #ifdef BSP_USING_PWM2
- rcu_periph_clock_enable(RCU_TIMER2);
- #endif
- #ifdef BSP_USING_PWM3
- rcu_periph_clock_enable(RCU_TIMER3);
- #endif
- #ifdef BSP_USING_PWM4
- rcu_periph_clock_enable(RCU_TIMER4);
- #endif
- rcu_periph_clock_enable(RCU_AF);
- for (i = 0; i < sizeof(pwm_obj) / sizeof(pwm_obj[0]); i++)
- {
- pwm_obj[i].config = &pwm_config[i];
- rt_device_pwm_register(&pwm_obj[i].pwm_device, pwm_config[i].name, &gd32_drv_ops, pwm_obj[i].config);
- gd32_pwm_init(&pwm_config[i]);
- }
- return result;
- }
- INIT_DEVICE_EXPORT(rt_pwm_init);
- #endif /* RT_USING_PWM */
|