1 /*
2  * Copyright (c) 2006-2018, RT-Thread Development Team
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  *
6  * Change Logs:
7  * Date           Author       Notes
8  * 2020-06-02     hqfang       first version
9  */
10 
11 #include "drv_pwm.h"
12 // #define DBG_LVL               DBG_INFO
13 
14 #include <rtdbg.h>
15 
16 #ifdef BSP_USING_PWM
17 
18 #if !defined(BSP_USING_PWM0) && !defined(BSP_USING_PWM1) && !defined(BSP_USING_PWM2) \
19     && !defined(BSP_USING_PWM3) && !defined(BSP_USING_PWM4)
20     #error "Please define at least one BSP_USING_PWMx"
21     /* this driver can be disabled at menuconfig -> Hardware Drivers Config -> On-chip Peripheral Drivers -> Enable PWM */
22 #endif
23 
24 static struct gd32_pwm_config pwm_config[] =
25 {
26 #ifdef BSP_USING_PWM0
27     {
28         "pwm0",
29         TIMER0,
30         1000000,
31     },
32 #endif
33 #ifdef BSP_USING_PWM1
34     {
35         "pwm1",
36         TIMER1,
37         1000000,
38     },
39 #endif
40 #ifdef BSP_USING_PWM2
41     {
42         "pwm2",
43         TIMER2,
44         1000000,
45     },
46 #endif
47 #ifdef BSP_USING_PWM3
48     {
49         "pwm3",
50         TIMER3,
51         1000000,
52     },
53 #endif
54 #ifdef BSP_USING_PWM4
55     {
56         "pwm4",
57         TIMER4,
58         1000000,
59     },
60 #endif
61 };
62 
63 #define GD32_MAX_PWM_CHANNELS   TIMER_CH_3
64 static struct gd32_pwm pwm_obj[sizeof(pwm_config) / sizeof(pwm_config[0])] = {0};
65 
gd32_pwm_enable(struct rt_device_pwm * device,struct rt_pwm_configuration * configuration,rt_bool_t enable)66 static rt_err_t gd32_pwm_enable(struct rt_device_pwm *device, struct rt_pwm_configuration *configuration, rt_bool_t enable)
67 {
68     struct gd32_pwm_config *config;
69     config = (struct gd32_pwm_config *)device->parent.user_data;
70     RT_ASSERT(config);
71 
72     if (configuration->channel > GD32_MAX_PWM_CHANNELS)
73     {
74         return -RT_EINVAL;
75     }
76     if (!enable)
77     {
78         timer_channel_output_state_config(config->periph, configuration->channel, TIMER_CCX_DISABLE);
79     }
80     else
81     {
82         timer_channel_output_state_config(config->periph, configuration->channel, TIMER_CCX_ENABLE);
83     }
84 
85     return RT_EOK;
86 }
87 
gd32_get_pwm_clk(rt_uint32_t periph)88 static uint32_t gd32_get_pwm_clk(rt_uint32_t periph)
89 {
90     uint32_t clk;
91     uint8_t clkpre;
92     if (periph != TIMER0)
93     {
94         clk = rcu_clock_freq_get(CK_APB1);
95         clkpre = GET_BITS(RCU_CFG0, 8, 10);
96     }
97     else
98     {
99         clk = rcu_clock_freq_get(CK_APB2);
100         clkpre = GET_BITS(RCU_CFG0, 11, 13);
101     }
102     if (clkpre >= 4)
103     {
104         clk = clk * 2;
105     }
106     return clk;
107 }
108 
gd32_pwm_get(struct rt_device_pwm * device,struct rt_pwm_configuration * configuration)109 static rt_err_t gd32_pwm_get(struct rt_device_pwm *device, struct rt_pwm_configuration *configuration)
110 {
111     uint32_t pwmclk;
112     uint16_t prescale, period, clkdiv, pulse;
113     struct gd32_pwm_config *config;
114     config = (struct gd32_pwm_config *)device->parent.user_data;
115     RT_ASSERT(config);
116 
117     pwmclk = gd32_get_pwm_clk(config->periph);
118     prescale = (uint16_t)TIMER_PSC(config->periph) + 1;
119     clkdiv = ((uint16_t)(TIMER_CTL0(config->periph) & TIMER_CTL0_CKDIV) >> 8);
120     clkdiv = 1 << clkdiv;
121     period = (uint16_t)TIMER_CAR(config->periph) + 1;
122     pulse = (uint16_t)REG32((config->periph) + 0x34U + configuration->channel << 2) + 1;
123 
124     pwmclk = pwmclk / prescale / clkdiv;
125     LOG_I("current pwmclk is %d\n", pwmclk);
126 
127     configuration->period = (uint64_t)period * 1000000000 / pwmclk;
128     configuration->pulse = (uint64_t)pulse * 1000000000 / pwmclk;
129     return RT_EOK;
130 }
131 
gd32_pwm_set(struct rt_device_pwm * device,struct rt_pwm_configuration * configuration)132 static rt_err_t gd32_pwm_set(struct rt_device_pwm *device, struct rt_pwm_configuration *configuration)
133 {
134     timer_oc_parameter_struct timer_ocinitpara;
135     timer_parameter_struct timer_initpara;
136     uint32_t pwmclk, pwmclkv2;
137     uint64_t period_cmp;
138     uint16_t prescale, period, clkdiv, pulse;
139     struct gd32_pwm_config *config;
140     config = (struct gd32_pwm_config *)device->parent.user_data;
141     RT_ASSERT(config);
142 
143     if (configuration->channel > GD32_MAX_PWM_CHANNELS)
144     {
145         LOG_I("max channel supported is %d\n", GD32_MAX_PWM_CHANNELS);
146         return -RT_EINVAL;
147     }
148     if (configuration->period < configuration->pulse)
149     {
150         LOG_I("period should > pulse \n");
151         return -RT_EINVAL;
152     }
153 
154     pwmclk = gd32_get_pwm_clk(config->periph);
155 
156     // min period value >= 100
157     period_cmp = (uint64_t)(1000000000 / pwmclk) * 10;
158     if (configuration->period < period_cmp)
159     {
160         return -RT_EINVAL;
161     }
162     period_cmp = (uint64_t)(1000000000 / (pwmclk / 65536 / 4)) * 65536;
163     if (configuration->period > period_cmp)
164     {
165         return -RT_EINVAL;
166     }
167 
168     period_cmp = (uint64_t) pwmclk * configuration->period / 1000000000;
169 
170     if (period_cmp < 65536)
171     {
172         prescale = 0;
173         clkdiv = TIMER_CKDIV_DIV1;
174         period = period_cmp;
175     }
176     else if (period_cmp < 4294967296)
177     {
178         prescale = period_cmp / 65536;
179         period = period_cmp / (prescale + 1);
180         clkdiv = TIMER_CKDIV_DIV1;
181     }
182     else if (period_cmp < 8589934592)
183     {
184         prescale = period_cmp / 65536;
185         period = period_cmp / (prescale + 1) / 2;
186         clkdiv = TIMER_CKDIV_DIV2;
187     }
188     else
189     {
190         prescale = period_cmp / 65536;
191         period = period_cmp / (prescale + 1) / 4;
192         clkdiv = TIMER_CKDIV_DIV4;
193     }
194 
195     pwmclkv2 = pwmclk / (prescale + 1) / (1 << clkdiv);
196     LOG_I("current pwmclk is %d\n", pwmclkv2);
197 
198     LOG_I("Set channel %d, period %dns, pulse %dns\n", configuration->channel, \
199           configuration->period, configuration->pulse);
200     pulse = (uint64_t)period * configuration->pulse / configuration->period;
201     LOG_I("pwmclk %d, pwmcmp %d, prescale %d, period %d, pulse %d, clkdiv %d\n", \
202           pwmclk, (uint32_t)period_cmp, prescale, period, pulse, clkdiv);
203 
204     /* initialize TIMER init parameter struct */
205     timer_struct_para_init(&timer_initpara);
206     /* TIMER configuration */
207     timer_initpara.prescaler         = prescale;
208     timer_initpara.alignedmode       = TIMER_COUNTER_EDGE;
209     timer_initpara.counterdirection  = TIMER_COUNTER_UP;
210     timer_initpara.period            = period;
211     timer_initpara.clockdivision     = clkdiv;
212     timer_initpara.repetitioncounter = 0;
213     timer_init(config->periph, &timer_initpara);
214 
215     /* initialize TIMER channel output parameter struct */
216     timer_channel_output_struct_para_init(&timer_ocinitpara);
217     /* CH0, CH1 and CH2 configuration in PWM mode */
218     timer_ocinitpara.outputstate  = TIMER_CCX_DISABLE;
219     timer_ocinitpara.outputnstate = TIMER_CCXN_DISABLE;
220     timer_ocinitpara.ocpolarity   = TIMER_OC_POLARITY_HIGH;
221     timer_ocinitpara.ocnpolarity  = TIMER_OCN_POLARITY_HIGH;
222     timer_ocinitpara.ocidlestate  = TIMER_OC_IDLE_STATE_LOW;
223     timer_ocinitpara.ocnidlestate = TIMER_OCN_IDLE_STATE_LOW;
224 
225     timer_channel_output_config(config->periph, configuration->channel, &timer_ocinitpara);
226 
227     /* Channel configuration in PWM mode */
228     timer_channel_output_pulse_value_config(config->periph, configuration->channel, pulse);
229     timer_channel_output_mode_config(config->periph, configuration->channel, TIMER_OC_MODE_PWM0);
230     timer_channel_output_shadow_config(config->periph, configuration->channel, TIMER_OC_SHADOW_DISABLE);
231 
232     timer_primary_output_config(config->periph, ENABLE);
233     /* auto-reload preload enable */
234     timer_auto_reload_shadow_enable(config->periph);
235     timer_enable(config->periph);
236 
237     return RT_EOK;
238 }
239 
gd32_pwm_control(struct rt_device_pwm * device,int cmd,void * arg)240 static rt_err_t gd32_pwm_control(struct rt_device_pwm *device, int cmd, void *arg)
241 {
242     struct rt_pwm_configuration *configuration = (struct rt_pwm_configuration *)arg;
243 
244     switch (cmd)
245     {
246     case PWM_CMD_ENABLE:
247         return gd32_pwm_enable(device, configuration, RT_TRUE);
248     case PWM_CMD_DISABLE:
249         return gd32_pwm_enable(device, configuration, RT_FALSE);
250     case PWM_CMD_SET:
251         return gd32_pwm_set(device, configuration);
252     case PWM_CMD_GET:
253         return gd32_pwm_get(device, configuration);
254     default:
255         return -RT_EINVAL;
256     }
257 }
258 
gd32_pwm_init(struct gd32_pwm_config * config)259 static rt_err_t gd32_pwm_init(struct gd32_pwm_config *config)
260 {
261     timer_oc_parameter_struct timer_ocinitpara;
262     timer_parameter_struct timer_initpara;
263     uint32_t pwmclk;
264     uint16_t prescale;
265 
266     pwmclk = gd32_get_pwm_clk(config->periph);
267 
268     /* period 1ms, duty 50% */
269     prescale = pwmclk / 1000 / 1000 - 1;
270 
271     config->period = 1000000;
272 
273     LOG_I("pwmclk %d, prescale %d, period %d, clkdiv %d\n", pwmclk, prescale, 999, 0);
274 
275     /* initialize TIMER init parameter struct */
276     timer_struct_para_init(&timer_initpara);
277     /* TIMER configuration */
278     timer_initpara.prescaler         = prescale;
279     timer_initpara.alignedmode       = TIMER_COUNTER_EDGE;
280     timer_initpara.counterdirection  = TIMER_COUNTER_UP;
281     timer_initpara.period            = 999;
282     timer_initpara.clockdivision     = TIMER_CKDIV_DIV1;
283     timer_initpara.repetitioncounter = 0;
284     timer_init(config->periph, &timer_initpara);
285 
286     /* initialize TIMER channel output parameter struct */
287     timer_channel_output_struct_para_init(&timer_ocinitpara);
288     /* CH0, CH1 and CH2 configuration in PWM mode */
289     timer_ocinitpara.outputstate  = TIMER_CCX_DISABLE;
290     timer_ocinitpara.outputnstate = TIMER_CCXN_DISABLE;
291     timer_ocinitpara.ocpolarity   = TIMER_OC_POLARITY_HIGH;
292     timer_ocinitpara.ocnpolarity  = TIMER_OCN_POLARITY_HIGH;
293     timer_ocinitpara.ocidlestate  = TIMER_OC_IDLE_STATE_LOW;
294     timer_ocinitpara.ocnidlestate = TIMER_OCN_IDLE_STATE_LOW;
295 
296     /* Channel configuration in PWM mode */
297     for (int i = 0; i <= GD32_MAX_PWM_CHANNELS; i ++)
298     {
299         timer_channel_output_config(config->periph, i, &timer_ocinitpara);
300         timer_channel_output_pulse_value_config(config->periph, i, 499);
301         timer_channel_output_mode_config(config->periph, i, TIMER_OC_MODE_PWM0);
302         timer_channel_output_shadow_config(config->periph, i, TIMER_OC_SHADOW_DISABLE);
303     }
304 
305     timer_primary_output_config(config->periph, ENABLE);
306     /* auto-reload preload enable */
307     timer_auto_reload_shadow_enable(config->periph);
308     timer_enable(config->periph);
309 
310     return RT_EOK;
311 }
312 
313 static struct rt_pwm_ops gd32_drv_ops =
314 {
315     .control = gd32_pwm_control
316 };
317 
rt_pwm_init(void)318 static int rt_pwm_init(void)
319 {
320     int i = 0;
321     int result = RT_EOK;
322 
323 #ifdef BSP_USING_PWM0
324     rcu_periph_clock_enable(RCU_TIMER0);
325 #endif
326 #ifdef BSP_USING_PWM1
327     rcu_periph_clock_enable(RCU_TIMER1);
328 #endif
329 #ifdef BSP_USING_PWM2
330     rcu_periph_clock_enable(RCU_TIMER2);
331 #endif
332 #ifdef BSP_USING_PWM3
333     rcu_periph_clock_enable(RCU_TIMER3);
334 #endif
335 #ifdef BSP_USING_PWM4
336     rcu_periph_clock_enable(RCU_TIMER4);
337 #endif
338     rcu_periph_clock_enable(RCU_AF);
339 
340     for (i = 0; i < sizeof(pwm_obj) / sizeof(pwm_obj[0]); i++)
341     {
342         pwm_obj[i].config = &pwm_config[i];
343         rt_device_pwm_register(&pwm_obj[i].pwm_device, pwm_config[i].name, &gd32_drv_ops, pwm_obj[i].config);
344         gd32_pwm_init(&pwm_config[i]);
345     }
346 
347     return result;
348 }
349 
350 INIT_DEVICE_EXPORT(rt_pwm_init);
351 
352 #endif /* RT_USING_PWM */
353