1 /*
2  * Copyright (c) 2006-2023, RT-Thread Development Team
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  *
6  * Change Logs:
7  * Date           Author            Notes
8  * 2022-01-21     charlown           first version
9  */
10 
11 #include <rtthread.h>
12 #include <rtdevice.h>
13 #include <board.h>
14 
15 #ifdef BSP_USING_PWM
16 
17 #define LOG_TAG "drv.pwm"
18 #include <drv_log.h>
19 
20 #ifndef ITEM_NUM
21 #define ITEM_NUM(items) sizeof(items) / sizeof(items[0])
22 #endif
23 
24 #define MAX_COUNTER 65535
25 #define MIN_COUNTER 2
26 #define MIN_PULSE 2
27 
28 struct rtdevice_pwm_device
29 {
30   struct rt_device_pwm parent;
31   TIM_TypeDef *periph;
32   rt_uint8_t channel[4];
33   char *name;
34 };
35 
36 /*
37 * channel = 0xFF: the channel is not use.
38 */
39 struct rtdevice_pwm_device pwm_device_list[] =
40     {
41 #ifdef BSP_USING_TIM1_PWM
42         {
43             .periph = TIM1,
44             .name = "pwm1",
45 #ifdef BSP_USING_TIM1_PWM_CH1
46             .channel[0] = TIM_Channel_1,
47 #else
48             .channel[0] = 0xFF,
49 #endif
50 
51 #ifdef BSP_USING_TIM1_PWM_CH2
52             .channel[1] = TIM_Channel_2,
53 #else
54             .channel[1] = 0xFF,
55 #endif
56 
57 #ifdef BSP_USING_TIM1_PWM_CH3
58             .channel[2] = TIM_Channel_3,
59 #else
60             .channel[2] = 0xFF,
61 #endif
62 
63 #ifdef BSP_USING_TIM1_PWM_CH4
64             .channel[3] = TIM_Channel_4,
65 #else
66             .channel[3] = 0xFF,
67 #endif
68         },
69 #endif
70 
71 #ifdef BSP_USING_TIM2_PWM
72         {
73             .periph = TIM2,
74             .name = "pwm2",
75 #ifdef BSP_USING_TIM2_PWM_CH1
76             .channel[0] = TIM_Channel_1,
77 #else
78             .channel[0] = 0xFF,
79 #endif
80 
81 #ifdef BSP_USING_TIM2_PWM_CH2
82             .channel[1] = TIM_Channel_2,
83 #else
84             .channel[1] = 0xFF,
85 #endif
86 
87 #ifdef BSP_USING_TIM2_PWM_CH3
88             .channel[2] = TIM_Channel_3,
89 #else
90             .channel[2] = 0xFF,
91 #endif
92 
93 #ifdef BSP_USING_TIM2_PWM_CH4
94             .channel[3] = TIM_Channel_4,
95 #else
96             .channel[3] = 0xFF,
97 #endif
98         },
99 #endif
100 
101 #ifdef BSP_USING_TIM3_PWM
102         {
103             .periph = TIM3,
104             .name = "pwm3",
105 #ifdef BSP_USING_TIM3_PWM_CH1
106             .channel[0] = TIM_Channel_1,
107 #else
108             .channel[0] = 0xFF,
109 #endif
110 
111 #ifdef BSP_USING_TIM3_PWM_CH2
112             .channel[1] = TIM_Channel_2,
113 #else
114             .channel[1] = 0xFF,
115 #endif
116 
117 #ifdef BSP_USING_TIM3_PWM_CH3
118             .channel[2] = TIM_Channel_3,
119 #else
120             .channel[2] = 0xFF,
121 #endif
122 
123 #ifdef BSP_USING_TIM3_PWM_CH4
124             .channel[3] = TIM_Channel_4,
125 #else
126             .channel[3] = 0xFF,
127 #endif
128         },
129 #endif
130 
131 #ifdef BSP_USING_TIM4_PWM
132         {
133             .periph = TIM4,
134             .name = "pwm4",
135 #ifdef BSP_USING_TIM4_PWM_CH1
136             .channel[0] = TIM_Channel_1,
137 #else
138             .channel[0] = 0xFF,
139 #endif
140 
141 #ifdef BSP_USING_TIM4_PWM_CH2
142             .channel[1] = TIM_Channel_2,
143 #else
144             .channel[1] = 0xFF,
145 #endif
146 
147 #ifdef BSP_USING_TIM4_PWM_CH3
148             .channel[2] = TIM_Channel_3,
149 #else
150             .channel[2] = 0xFF,
151 #endif
152 
153 #ifdef BSP_USING_TIM4_PWM_CH4
154             .channel[3] = TIM_Channel_4,
155 #else
156             .channel[3] = 0xFF,
157 #endif
158         },
159 #endif
160 
161 #ifdef BSP_USING_TIM5_PWM
162         {
163             .periph = TIM5,
164             .name = "pwm5",
165 #ifdef BSP_USING_TIM5_PWM_CH1
166             .channel[0] = TIM_Channel_1,
167 #else
168             .channel[0] = 0xFF,
169 #endif
170 
171 #ifdef BSP_USING_TIM5_PWM_CH2
172             .channel[1] = TIM_Channel_2,
173 #else
174             .channel[1] = 0xFF,
175 #endif
176 
177 #ifdef BSP_USING_TIM5_PWM_CH3
178             .channel[2] = TIM_Channel_3,
179 #else
180             .channel[2] = 0xFF,
181 #endif
182 
183 #ifdef BSP_USING_TIM5_PWM_CH4
184             .channel[3] = TIM_Channel_4,
185 #else
186             .channel[3] = 0xFF,
187 #endif
188         },
189 #endif
190 
191 #ifdef BSP_USING_TIM8_PWM
192         {
193             .periph = TIM8,
194             .name = "pwm8",
195 #ifdef BSP_USING_TIM8_PWM_CH1
196             .channel[0] = TIM_Channel_1,
197 #else
198             .channel[0] = 0xFF,
199 #endif
200 
201 #ifdef BSP_USING_TIM8_PWM_CH2
202             .channel[1] = TIM_Channel_2,
203 #else
204             .channel[1] = 0xFF,
205 #endif
206 
207 #ifdef BSP_USING_TIM8_PWM_CH3
208             .channel[2] = TIM_Channel_3,
209 #else
210             .channel[2] = 0xFF,
211 #endif
212 
213 #ifdef BSP_USING_TIM8_PWM_CH4
214             .channel[3] = TIM_Channel_4,
215 #else
216             .channel[3] = 0xFF,
217 #endif
218         },
219 #endif
220 
221 #ifdef BSP_USING_TIM9_PWM
222         {
223             .periph = TIM9,
224             .name = "pwm9",
225 #ifdef BSP_USING_TIM9_PWM_CH1
226             .channel[0] = TIM_Channel_1,
227 #else
228             .channel[0] = 0xFF,
229 #endif
230 
231 #ifdef BSP_USING_TIM9_PWM_CH2
232             .channel[1] = TIM_Channel_2,
233 #else
234             .channel[1] = 0xFF,
235 #endif
236 
237 #ifdef BSP_USING_TIM9_PWM_CH3
238             .channel[2] = TIM_Channel_3,
239 #else
240             .channel[2] = 0xFF,
241 #endif
242 
243 #ifdef BSP_USING_TIM9_PWM_CH4
244             .channel[3] = TIM_Channel_4,
245 #else
246             .channel[3] = 0xFF,
247 #endif
248         },
249 #endif
250 
251 #ifdef BSP_USING_TIM10_PWM
252         {
253             .periph = TIM10,
254             .name = "pwm10",
255 #ifdef BSP_USING_TIM10_PWM_CH1
256             .channel[0] = TIM_Channel_1,
257 #else
258             .channel[0] = 0xFF,
259 #endif
260 
261 #ifdef BSP_USING_TIM10_PWM_CH2
262             .channel[1] = TIM_Channel_2,
263 #else
264             .channel[1] = 0xFF,
265 #endif
266 
267 #ifdef BSP_USING_TIM10_PWM_CH3
268             .channel[2] = TIM_Channel_3,
269 #else
270             .channel[2] = 0xFF,
271 #endif
272 
273 #ifdef BSP_USING_TIM10_PWM_CH4
274             .channel[3] = TIM_Channel_4,
275 #else
276             .channel[3] = 0xFF,
277 #endif
278         },
279 #endif
280 
281 };
282 
ch32f2_pwm_device_enable(struct rt_device_pwm * device,struct rt_pwm_configuration * configuration,rt_bool_t enable)283 static rt_err_t ch32f2_pwm_device_enable(struct rt_device_pwm *device, struct rt_pwm_configuration *configuration, rt_bool_t enable)
284 {
285   struct rtdevice_pwm_device *pwm_device;
286   rt_uint32_t channel_index;
287   rt_uint16_t ccx_state;
288 
289   pwm_device = (struct rtdevice_pwm_device *)device;
290   channel_index = configuration->channel;
291 
292   if (enable == RT_TRUE)
293   {
294     ccx_state = TIM_CCx_Enable;
295   }
296   else
297   {
298     ccx_state = TIM_CCx_Disable;
299   }
300 
301   if (channel_index <= 4 && channel_index > 0)
302   {
303     if (pwm_device->channel[channel_index - 1] == 0xFF)
304       return -RT_EINVAL;
305 
306     TIM_CCxCmd(pwm_device->periph, pwm_device->channel[channel_index - 1], ccx_state);
307   }
308   else
309   {
310     return -RT_EINVAL;
311   }
312 
313   TIM_Cmd(pwm_device->periph, ENABLE);
314 
315   return RT_EOK;
316 }
317 
ch32f2_pwm_device_get(struct rt_device_pwm * device,struct rt_pwm_configuration * configuration)318 static rt_err_t ch32f2_pwm_device_get(struct rt_device_pwm *device, struct rt_pwm_configuration *configuration)
319 {
320   struct rtdevice_pwm_device *pwm_device;
321   rt_uint32_t arr_counter, ccr_counter, prescaler, sample_freq;
322   rt_uint32_t channel_index;
323   rt_uint32_t tim_clock;
324 
325   pwm_device = (struct rtdevice_pwm_device *)device;
326 
327   tim_clock = ch32f2_tim_clock_get(pwm_device->periph);
328   channel_index = configuration->channel;
329   arr_counter = pwm_device->periph->ATRLR + 1;
330   prescaler = pwm_device->periph->PSC + 1;
331 
332   sample_freq = (tim_clock / prescaler) / arr_counter;
333 
334   /* unit:ns */
335   configuration->period = 1000000000 / sample_freq;
336 
337   if (channel_index == 1)
338   {
339     ccr_counter = pwm_device->periph->CH1CVR + 1;
340     configuration->pulse = ((ccr_counter * 100) / arr_counter) * configuration->period / 100;
341   }
342   else if (channel_index == 2)
343   {
344     ccr_counter = pwm_device->periph->CH2CVR + 1;
345     configuration->pulse = ((ccr_counter * 100) / arr_counter) * configuration->period / 100;
346   }
347   else if (channel_index == 3)
348   {
349     ccr_counter = pwm_device->periph->CH3CVR + 1;
350     configuration->pulse = ((ccr_counter * 100) / arr_counter) * configuration->period / 100;
351   }
352   else if (channel_index == 4)
353   {
354     ccr_counter = pwm_device->periph->CH4CVR + 1;
355     configuration->pulse = ((ccr_counter * 100) / arr_counter) * configuration->period / 100;
356   }
357   else
358     return -RT_EINVAL;
359 
360   return RT_EOK;
361 }
362 
ch32f2_pwm_device_set(struct rt_device_pwm * device,struct rt_pwm_configuration * configuration)363 static rt_err_t ch32f2_pwm_device_set(struct rt_device_pwm *device, struct rt_pwm_configuration *configuration)
364 {
365   struct rtdevice_pwm_device *pwm_device;
366   rt_uint32_t arr_counter, ccr_counter, prescaler, sample_freq;
367   rt_uint32_t channel_index;
368   rt_uint32_t tim_clock;
369 
370   TIM_TimeBaseInitTypeDef TIM_TimeBaseInitType;
371   TIM_OCInitTypeDef TIM_OCInitType;
372 
373   pwm_device = (struct rtdevice_pwm_device *)device;
374 
375   tim_clock = ch32f2_tim_clock_get(pwm_device->periph);
376 
377   channel_index = configuration->channel;
378 
379   /* change to freq, unit:Hz */
380   sample_freq = 1000000000 / configuration->period;
381 
382   /*counter = (tim_clk / prescaler) / sample_freq */
383   /*normally, tim_clk is not need div, if arr_counter over 65536, need div.*/
384   prescaler = 1;
385 
386   arr_counter = (tim_clock / prescaler) / sample_freq;
387 
388   if (arr_counter > MAX_COUNTER)
389   {
390     /* need div tim_clock
391     * and round up the prescaler value.
392     * (tim_clock >> 16) = tim_clock / 65536
393     */
394     if ((tim_clock >> 16) % sample_freq == 0)
395       prescaler = (tim_clock >> 16) / sample_freq;
396     else
397       prescaler = (tim_clock >> 16) / sample_freq + 1;
398 
399     /*counter = (tim_clk / prescaler) / sample_freq */
400     arr_counter = (tim_clock / prescaler) / sample_freq;
401   }
402   /* ccr_counter = duty cycle * arr_counter */
403   ccr_counter = (configuration->pulse * 100 / configuration->period) * arr_counter / 100;
404 
405   /* check arr_counter > 1, cxx_counter > 1 */
406   if (arr_counter < MIN_COUNTER)
407   {
408     arr_counter = MIN_COUNTER;
409   }
410 
411   if (ccr_counter < MIN_PULSE)
412   {
413     ccr_counter = MIN_PULSE;
414   }
415 
416   /* TMRe base configuration */
417   TIM_TimeBaseStructInit(&TIM_TimeBaseInitType);
418   TIM_TimeBaseInitType.TIM_Period = arr_counter - 1;
419   TIM_TimeBaseInitType.TIM_Prescaler = prescaler - 1;
420   TIM_TimeBaseInitType.TIM_ClockDivision = TIM_CKD_DIV1;
421   TIM_TimeBaseInitType.TIM_CounterMode = TIM_CounterMode_Up;
422 
423   TIM_TimeBaseInit(pwm_device->periph, &TIM_TimeBaseInitType);
424 
425 
426   TIM_OCStructInit(&TIM_OCInitType);
427   TIM_OCInitType.TIM_OCMode = TIM_OCMode_PWM1;
428   TIM_OCInitType.TIM_OutputState = TIM_OutputState_Enable;
429   TIM_OCInitType.TIM_Pulse = ccr_counter - 1;
430   TIM_OCInitType.TIM_OCPolarity = TIM_OCPolarity_High;
431 
432   if (channel_index == 1)
433   {
434     TIM_OC1Init(pwm_device->periph, &TIM_OCInitType);
435     TIM_OC1PreloadConfig(pwm_device->periph, TIM_OCPreload_Disable);
436   }
437   else if (channel_index == 2)
438   {
439     TIM_OC2Init(pwm_device->periph, &TIM_OCInitType);
440     TIM_OC2PreloadConfig(pwm_device->periph, TIM_OCPreload_Disable);
441   }
442   else if (channel_index == 3)
443   {
444     TIM_OC3Init(pwm_device->periph, &TIM_OCInitType);
445     TIM_OC3PreloadConfig(pwm_device->periph, TIM_OCPreload_Disable);
446   }
447   else if (channel_index == 4)
448   {
449     TIM_OC4Init(pwm_device->periph, &TIM_OCInitType);
450     TIM_OC4PreloadConfig(pwm_device->periph, TIM_OCPreload_Disable);
451   }
452   else
453   {
454     return -RT_EINVAL;
455   }
456 
457   TIM_ARRPreloadConfig(pwm_device->periph, ENABLE);
458   TIM_CtrlPWMOutputs(pwm_device->periph, ENABLE);
459 
460   return RT_EOK;
461 }
462 
drv_pwm_control(struct rt_device_pwm * device,int cmd,void * arg)463 static rt_err_t drv_pwm_control(struct rt_device_pwm *device, int cmd, void *arg)
464 {
465   struct rt_pwm_configuration *configuration;
466 
467   configuration = (struct rt_pwm_configuration *)arg;
468 
469   switch (cmd)
470   {
471   case PWM_CMD_ENABLE:
472     return ch32f2_pwm_device_enable(device, configuration, RT_TRUE);
473   case PWM_CMD_DISABLE:
474     return ch32f2_pwm_device_enable(device, configuration, RT_FALSE);
475   case PWM_CMD_SET:
476     return ch32f2_pwm_device_set(device, configuration);
477   case PWM_CMD_GET:
478     return ch32f2_pwm_device_get(device, configuration);
479   default:
480     return -RT_EINVAL;
481   }
482 }
483 
484 static struct rt_pwm_ops pwm_ops =
485     {
486         .control = drv_pwm_control};
487 
rt_hw_pwm_init(void)488 static int rt_hw_pwm_init(void)
489 {
490   int result = RT_EOK;
491   int index = 0;
492   int channel_index;
493 
494   for (index = 0; index < ITEM_NUM(pwm_device_list); index++)
495   {
496     ch32f2_tim_clock_init(pwm_device_list[index].periph);
497     for (channel_index = 0; channel_index < sizeof(pwm_device_list[index].channel); channel_index++)
498     {
499       if (pwm_device_list[index].channel[channel_index] != 0xFF)
500       {
501         ch32f2_pwm_io_init(pwm_device_list[index].periph, pwm_device_list[index].channel[channel_index]);
502       }
503     }
504 
505     if (rt_device_pwm_register(&pwm_device_list[index].parent, pwm_device_list[index].name, &pwm_ops, RT_NULL) == RT_EOK)
506     {
507       LOG_D("%s register success", pwm_device_list[index].name);
508     }
509     else
510     {
511       LOG_D("%s register failed", pwm_device_list[index].name);
512       result = -RT_ERROR;
513     }
514   }
515 
516   return result;
517 }
518 
519 INIT_BOARD_EXPORT(rt_hw_pwm_init);
520 
521 #endif /* BSP_USING_PWM */
522