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