1 /*
2 * Copyright (c) 2006-2023
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 *
6 * Change Logs:
7 * Date Author Notes
8 * 2023-06-05 zengjianwei first version
9 * 2025-06-23 Yucai Liu Support for non-complementary PWM output with advanced timers
10 */
11
12 #include <board.h>
13 #include <gd32f30x.h>
14 #include <rtdevice.h>
15 #include <rtthread.h>
16
17 #ifdef RT_USING_PWM
18
19 /* #define DRV_DEBUG */
20 #define LOG_TAG "drv.pwm"
21 #include <rtdbg.h>
22
23 #define MAX_PERIOD 65535
24 #define MIN_PERIOD 3
25 #define MIN_PULSE 2
26
27 typedef struct
28 {
29 rt_int8_t TimerIndex; /* timer index:0~13 */
30 rt_uint32_t Port; /* gpio port:GPIOA/GPIOB/GPIOC/... */
31 rt_uint32_t pin; /* gpio pin:GPIO_PIN_0~GPIO_PIN_15 */
32 /* timer channel: -2 is ch_1n, -1 is ch_0n, 0 is ch0, 1 is ch1 */
33 rt_int16_t channel;
34 char *name;
35 } TIMER_PORT_CHANNEL_MAP_S;
36
37 struct gd32_pwm
38 {
39 struct rt_device_pwm pwm_device;
40 TIMER_PORT_CHANNEL_MAP_S tim_handle;
41 };
42
43 static struct gd32_pwm gd32_pwm_obj[] = {
44 #ifdef RT_USING_PWM1
45 {.tim_handle = {3, GPIOB, GPIO_PIN_8, 2, "pwm1"}},
46 #endif
47
48 #ifdef RT_USING_PWM2
49 {.tim_handle = {3, GPIOB, GPIO_PIN_8, 2, "pwm2"}},
50 #endif
51
52 #ifdef RT_USING_PWM3
53 {.tim_handle = {3, GPIOB, GPIO_PIN_8, 2, "pwm3"}},
54 #endif
55
56 #ifdef RT_USING_PWM4
57 {.tim_handle = {3, GPIOB, GPIO_PIN_8, 2, "pwm4"}},
58 #endif
59
60 #ifdef RT_USING_PWM5
61 {.tim_handle = {3, GPIOB, GPIO_PIN_8, 2, "pwm5"}},
62 #endif
63
64 #ifdef RT_USING_PWM6
65 {.tim_handle = {3, GPIOB, GPIO_PIN_8, 2, "pwm6"}},
66 #endif
67
68 #ifdef RT_USING_PWM7
69 {.tim_handle = {3, GPIOB, GPIO_PIN_8, 2, "pwm7"}},
70 #endif
71
72 #ifdef RT_USING_PWM8
73 {.tim_handle = {3, GPIOB, GPIO_PIN_8, 2, "pwm8"}},
74 #endif
75
76 #ifdef RT_USING_PWM9
77 {.tim_handle = {3, GPIOB, GPIO_PIN_8, 2, "pwm9"}},
78 #endif
79
80 #ifdef RT_USING_PWM10
81 {.tim_handle = {3, GPIOB, GPIO_PIN_8, 2, "pwm10"}},
82 #endif
83
84 #ifdef RT_USING_PWM11
85 {.tim_handle = {3, GPIOB, GPIO_PIN_8, 2, "pwm11"}},
86 #endif
87
88 #ifdef RT_USING_PWM12
89 {.tim_handle = {3, GPIOB, GPIO_PIN_8, 2, "pwm12"}},
90 #endif
91
92 #ifdef RT_USING_PWM13
93 {.tim_handle = {3, GPIOB, GPIO_PIN_8, 2, "pwm13"}},
94 #endif
95
96 #ifdef RT_USING_PWM14
97 {.tim_handle = {3, GPIOB, GPIO_PIN_8, 2, "pwm14"}},
98 #endif
99 };
100
101 typedef struct
102 {
103 rt_uint32_t Port[7];
104 rt_int8_t TimerIndex[14];
105 } TIMER_PERIPH_LIST_S;
106
107 static TIMER_PERIPH_LIST_S gd32_timer_periph_list = {
108 .Port = {0, 0, 0, 0, 0, 0, 0},
109 .TimerIndex = {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
110 };
111
112 /*
113 * 将所有用到的 gpio port 和 timer 不重复地列举出来,以方便后面不重复地初始化
114 */
pwm_find_timer_periph(void)115 static rt_err_t pwm_find_timer_periph(void)
116 {
117 rt_int16_t i, j, k;
118
119 /* find gpio port of defined table */
120 for (i = 0; i < sizeof(gd32_pwm_obj) / sizeof(gd32_pwm_obj[0]); ++i)
121 {
122 /* find -1 of gd32_periph_list's member of Port */
123 for (j = 0; j < sizeof(gd32_timer_periph_list.Port) / sizeof(gd32_timer_periph_list.Port[0]); ++j)
124 {
125 if (0 == gd32_timer_periph_list.Port[j])
126 {
127 break;
128 }
129 }
130
131 if (j >= sizeof(gd32_timer_periph_list.Port) / sizeof(gd32_timer_periph_list.Port[0]))
132 {
133 LOG_E("Can not find -1 of gd32_periph_list's member of Port!\n");
134 break;
135 }
136
137 /* find the different of Port */
138 for (k = 0; k < j; ++k)
139 {
140 if (gd32_pwm_obj[i].tim_handle.Port == gd32_timer_periph_list.Port[k])
141 {
142 break;
143 }
144 }
145
146 /* if can not find the same Port */
147 if (k == j)
148 {
149 gd32_timer_periph_list.Port[j] = gd32_pwm_obj[i].tim_handle.Port;
150 }
151 }
152
153 /* find timer periph of defined table */
154 for (i = 0; i < sizeof(gd32_pwm_obj) / sizeof(gd32_pwm_obj[0]); ++i)
155 {
156 /* find -1 of gd32_periph_list's member of TimerIndex */
157 for (j = 0; j < sizeof(gd32_timer_periph_list.TimerIndex) / sizeof(gd32_timer_periph_list.TimerIndex[0]); ++j)
158 {
159 if (-1 == gd32_timer_periph_list.TimerIndex[j])
160 {
161 break;
162 }
163 }
164
165 if (j >= sizeof(gd32_timer_periph_list.TimerIndex) / sizeof(gd32_timer_periph_list.TimerIndex[0]))
166 {
167 LOG_E("Can not find -1 of gd32_periph_list's member of TimerIndex!\n");
168 break;
169 }
170
171 /* find the different of TimerIndex */
172 for (k = 0; k < j; ++k)
173 {
174 if (gd32_pwm_obj[i].tim_handle.TimerIndex == gd32_timer_periph_list.TimerIndex[k])
175 {
176 break;
177 }
178 }
179
180 /* if can not find the same TimerIndex */
181 if (k == j)
182 {
183 gd32_timer_periph_list.TimerIndex[j] = gd32_pwm_obj[i].tim_handle.TimerIndex;
184 }
185 }
186
187 return RT_EOK;
188 }
189
index_to_timer(rt_int8_t TimerIndex)190 static rt_uint32_t index_to_timer(rt_int8_t TimerIndex)
191 {
192 switch (TimerIndex)
193 {
194 case 0:
195 return TIMER0;
196 case 1:
197 return TIMER1;
198 case 2:
199 return TIMER2;
200 case 3:
201 return TIMER3;
202 case 4:
203 return TIMER4;
204 case 5:
205 return TIMER5;
206 case 6:
207 return TIMER6;
208 case 7:
209 return TIMER7;
210 case 8:
211 return TIMER8;
212 case 9:
213 return TIMER9;
214 case 10:
215 return TIMER10;
216 case 11:
217 return TIMER11;
218 case 12:
219 return TIMER12;
220 case 13:
221 return TIMER13;
222
223 default:
224 LOG_E("Unsport timer periph!\n");
225 }
226 return TIMER0;
227 }
228
gpio_clock_enable(rt_uint32_t Port)229 static void gpio_clock_enable(rt_uint32_t Port)
230 {
231 switch (Port)
232 {
233 case GPIOA:
234 rcu_periph_clock_enable(RCU_GPIOA);
235 break;
236 case GPIOB:
237 rcu_periph_clock_enable(RCU_GPIOB);
238 break;
239 case GPIOC:
240 rcu_periph_clock_enable(RCU_GPIOC);
241 break;
242 case GPIOD:
243 rcu_periph_clock_enable(RCU_GPIOD);
244 break;
245 case GPIOE:
246 rcu_periph_clock_enable(RCU_GPIOE);
247 break;
248 case GPIOF:
249 rcu_periph_clock_enable(RCU_GPIOF);
250 break;
251 case GPIOG:
252 rcu_periph_clock_enable(RCU_GPIOG);
253 break;
254
255 default:
256 LOG_E("Unsport gpio port!\n");
257 }
258 }
259
timer_clock_enable(rt_int8_t TimerIndex)260 static void timer_clock_enable(rt_int8_t TimerIndex)
261 {
262 switch (TimerIndex)
263 {
264 case 0:
265 rcu_periph_clock_enable(RCU_TIMER0);
266 break;
267 case 1:
268 rcu_periph_clock_enable(RCU_TIMER1);
269 break;
270 case 2:
271 rcu_periph_clock_enable(RCU_TIMER2);
272 break;
273 case 3:
274 rcu_periph_clock_enable(RCU_TIMER3);
275 break;
276 case 4:
277 rcu_periph_clock_enable(RCU_TIMER4);
278 break;
279 case 5:
280 rcu_periph_clock_enable(RCU_TIMER5);
281 break;
282 case 6:
283 rcu_periph_clock_enable(RCU_TIMER6);
284 break;
285 case 7:
286 rcu_periph_clock_enable(RCU_TIMER7);
287 break;
288 #ifndef GD32F30X_HD
289 case 8:
290 rcu_periph_clock_enable(RCU_TIMER8);
291 break;
292 case 9:
293 rcu_periph_clock_enable(RCU_TIMER9);
294 break;
295 case 10:
296 rcu_periph_clock_enable(RCU_TIMER10);
297 break;
298 case 11:
299 rcu_periph_clock_enable(RCU_TIMER11);
300 break;
301 case 12:
302 rcu_periph_clock_enable(RCU_TIMER12);
303 break;
304 case 13:
305 rcu_periph_clock_enable(RCU_TIMER13);
306 break;
307 #endif
308 default:
309 LOG_E("Unsport timer periph!\n");
310 }
311 }
312
rcu_config(void)313 static void rcu_config(void)
314 {
315 rt_int16_t i;
316
317 for (i = 0; i < sizeof(gd32_timer_periph_list.Port) / sizeof(gd32_timer_periph_list.Port[0]); ++i)
318 {
319 if (0 == gd32_timer_periph_list.Port[i])
320 {
321 break;
322 }
323
324 /* enable GPIO clock */
325 gpio_clock_enable(gd32_timer_periph_list.Port[i]);
326 }
327
328 rcu_periph_clock_enable(RCU_AF);
329
330 for (i = 0; i < sizeof(gd32_timer_periph_list.TimerIndex) / sizeof(gd32_timer_periph_list.TimerIndex[0]); ++i)
331 {
332 if (-1 == gd32_timer_periph_list.TimerIndex[i])
333 {
334 break;
335 }
336
337 /* enable timer clock */
338 timer_clock_enable(gd32_timer_periph_list.TimerIndex[i]);
339 timer_deinit(index_to_timer(gd32_timer_periph_list.TimerIndex[i]));
340 }
341 }
342
gpio_config(void)343 static void gpio_config(void)
344 {
345 rt_int16_t i;
346
347 /* config the GPIO as analog mode */
348 for (i = 0; i < sizeof(gd32_pwm_obj) / sizeof(gd32_pwm_obj[0]); ++i)
349 {
350 gpio_init(gd32_pwm_obj[i].tim_handle.Port, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, gd32_pwm_obj[i].tim_handle.pin);
351 }
352 }
353
timer_init_para(timer_parameter_struct * initpara)354 static void timer_init_para(timer_parameter_struct *initpara)
355 {
356 rt_int16_t i;
357
358 for (i = 0; i < sizeof(gd32_timer_periph_list.TimerIndex) / sizeof(gd32_timer_periph_list.TimerIndex[0]); ++i)
359 {
360 /* config timer */
361 if (-1 != gd32_timer_periph_list.TimerIndex[i])
362 {
363 timer_init(index_to_timer(gd32_timer_periph_list.TimerIndex[i]), initpara);
364 }
365 }
366 }
367
channel_output_config(timer_oc_parameter_struct * ocpara)368 static void channel_output_config(timer_oc_parameter_struct *ocpara)
369 {
370 rt_int16_t i;
371 rt_uint32_t timer_periph;
372
373 /* config the channel config */
374 for (i = 0; i < sizeof(gd32_pwm_obj) / sizeof(gd32_pwm_obj[0]); ++i)
375 {
376 if (gd32_pwm_obj[i].tim_handle.channel < 0)
377 {
378 ocpara->outputstate = TIMER_CCX_DISABLE;
379 ocpara->outputnstate = TIMER_CCXN_ENABLE;
380 gd32_pwm_obj[i].tim_handle.channel = -(gd32_pwm_obj[i].tim_handle.channel + 1);
381 }
382 timer_periph = index_to_timer(gd32_pwm_obj[i].tim_handle.TimerIndex);
383 timer_channel_output_config(timer_periph, gd32_pwm_obj[i].tim_handle.channel, ocpara);
384
385 timer_channel_output_pulse_value_config(timer_periph, gd32_pwm_obj[i].tim_handle.channel, 7999);
386 timer_channel_output_mode_config(timer_periph, gd32_pwm_obj[i].tim_handle.channel, TIMER_OC_MODE_PWM0);
387 timer_channel_output_shadow_config(timer_periph, gd32_pwm_obj[i].tim_handle.channel, TIMER_OC_SHADOW_DISABLE);
388 /* auto-reload preload shadow reg enable */
389 /* timer_auto_reload_shadow_enable(timer_periph); */
390 timer_channel_output_state_config(timer_periph, gd32_pwm_obj[i].tim_handle.channel, TIMER_CCX_DISABLE);
391 timer_channel_complementary_output_state_config(timer_periph, gd32_pwm_obj[i].tim_handle.channel, TIMER_CCXN_DISABLE);
392 }
393
394 /* enable timer */
395 for (i = 0; i < sizeof(gd32_timer_periph_list.TimerIndex) / sizeof(gd32_timer_periph_list.TimerIndex[0]); ++i)
396 {
397 if (-1 != gd32_timer_periph_list.TimerIndex[i])
398 {
399 timer_periph = index_to_timer(gd32_timer_periph_list.TimerIndex[i]);
400 if (timer_periph == TIMER0 || timer_periph == TIMER7)
401 {
402 timer_primary_output_config(timer_periph, ENABLE);
403 }
404 timer_enable(timer_periph);
405 }
406 }
407 }
408
timer_config(void)409 static void timer_config(void)
410 {
411 timer_oc_parameter_struct timer_ocintpara;
412 timer_parameter_struct timer_initpara;
413
414 /* TIMER configuration */
415 timer_initpara.prescaler = 119;
416 timer_initpara.alignedmode = TIMER_COUNTER_EDGE;
417 timer_initpara.counterdirection = TIMER_COUNTER_UP;
418 timer_initpara.period = 15999;
419 timer_initpara.clockdivision = TIMER_CKDIV_DIV1;
420 timer_initpara.repetitioncounter = 0;
421 timer_init_para(&timer_initpara);
422
423 /* CHX configuration in PWM mode */
424 timer_ocintpara.outputstate = TIMER_CCX_ENABLE;
425 timer_ocintpara.outputnstate = TIMER_CCXN_DISABLE;
426 timer_ocintpara.ocpolarity = TIMER_OC_POLARITY_HIGH;
427 timer_ocintpara.ocnpolarity = TIMER_OCN_POLARITY_HIGH;
428 timer_ocintpara.ocidlestate = TIMER_OC_IDLE_STATE_LOW;
429 timer_ocintpara.ocnidlestate = TIMER_OCN_IDLE_STATE_LOW;
430 channel_output_config(&timer_ocintpara);
431 }
432
drv_pwm_enable(TIMER_PORT_CHANNEL_MAP_S * pstTimerMap,struct rt_pwm_configuration * configuration,rt_bool_t enable)433 static rt_err_t drv_pwm_enable(TIMER_PORT_CHANNEL_MAP_S *pstTimerMap, struct rt_pwm_configuration *configuration,
434 rt_bool_t enable)
435 {
436 if (!enable)
437 {
438 timer_channel_output_state_config(index_to_timer(pstTimerMap->TimerIndex), configuration->channel,
439 TIMER_CCX_DISABLE);
440 }
441 else
442 {
443 if (configuration->complementary == RT_TRUE)
444 {
445 timer_channel_output_state_config(
446 index_to_timer(pstTimerMap->TimerIndex), configuration->channel - 1, TIMER_CCXN_ENABLE);
447 }
448 else
449 {
450 timer_channel_output_state_config(
451 index_to_timer(pstTimerMap->TimerIndex), configuration->channel, TIMER_CCX_ENABLE);
452 }
453 }
454
455 return RT_EOK;
456 }
457
drv_pwm_get(TIMER_PORT_CHANNEL_MAP_S * pstTimerMap,struct rt_pwm_configuration * configuration)458 static rt_err_t drv_pwm_get(TIMER_PORT_CHANNEL_MAP_S *pstTimerMap, struct rt_pwm_configuration *configuration)
459 {
460 rt_uint64_t tim_clock;
461 rt_uint16_t psc;
462 rt_uint32_t chxcv;
463
464 tim_clock = rcu_clock_freq_get(CK_SYS);
465
466 psc = timer_prescaler_read(index_to_timer(pstTimerMap->TimerIndex));
467 if (psc == TIMER_CKDIV_DIV2)
468 {
469 tim_clock = tim_clock / 2;
470 }
471 else if (psc == TIMER_CKDIV_DIV4)
472 {
473 tim_clock = tim_clock / 4;
474 }
475
476 chxcv = timer_channel_capture_value_register_read(index_to_timer(pstTimerMap->TimerIndex), configuration->channel);
477 /* Convert nanosecond to frequency and duty cycle. 1s = 1 * 1000 * 1000 * 1000 ns */
478 tim_clock /= 1000000UL;
479 configuration->period = (TIMER_CAR(index_to_timer(pstTimerMap->TimerIndex)) + 1) * (psc + 1) * 1000UL / tim_clock;
480 configuration->pulse = (chxcv + 1) * (psc + 1) * 1000UL / tim_clock;
481
482 return RT_EOK;
483 }
484
drv_pwm_set(TIMER_PORT_CHANNEL_MAP_S * pstTimerMap,struct rt_pwm_configuration * configuration)485 static rt_err_t drv_pwm_set(TIMER_PORT_CHANNEL_MAP_S *pstTimerMap, struct rt_pwm_configuration *configuration)
486 {
487 rt_uint32_t period, pulse;
488 rt_uint64_t tim_clock, psc;
489
490 tim_clock = rcu_clock_freq_get(CK_SYS);
491
492 /* Convert nanosecond to frequency and duty cycle. 1s = 1 * 1000 * 1000 * 1000 ns */
493 tim_clock /= 1000000UL;
494 period = (unsigned long long)configuration->period * tim_clock / 1000ULL;
495 psc = period / MAX_PERIOD + 1;
496 period = period / psc;
497
498 timer_prescaler_config(index_to_timer(pstTimerMap->TimerIndex), psc - 1, TIMER_PSC_RELOAD_NOW);
499
500 if (period < MIN_PERIOD)
501 {
502 period = MIN_PERIOD;
503 }
504
505 timer_autoreload_value_config(index_to_timer(pstTimerMap->TimerIndex), period - 1);
506
507 pulse = (unsigned long long)configuration->pulse * tim_clock / psc / 1000ULL;
508 if (pulse < MIN_PULSE)
509 {
510 pulse = MIN_PULSE;
511 }
512 else if (pulse > period)
513 {
514 pulse = period;
515 }
516
517 timer_channel_output_pulse_value_config(index_to_timer(pstTimerMap->TimerIndex), configuration->channel, pulse);
518 timer_counter_value_config(index_to_timer(pstTimerMap->TimerIndex), 0);
519
520 /* Update frequency value */
521 timer_event_software_generate(index_to_timer(pstTimerMap->TimerIndex), TIMER_EVENT_SRC_UPG);
522
523 return RT_EOK;
524 }
525
drv_pwm_control(struct rt_device_pwm * device,int cmd,void * arg)526 static rt_err_t drv_pwm_control(struct rt_device_pwm *device, int cmd, void *arg)
527 {
528 struct rt_pwm_configuration *configuration = (struct rt_pwm_configuration *)arg;
529 TIMER_PORT_CHANNEL_MAP_S *pstTimerMap = (TIMER_PORT_CHANNEL_MAP_S *)device->parent.user_data;
530
531 switch (cmd)
532 {
533 case PWM_CMD_ENABLE:
534 return drv_pwm_enable(pstTimerMap, configuration, RT_TRUE);
535 case PWM_CMD_DISABLE:
536 return drv_pwm_enable(pstTimerMap, configuration, RT_FALSE);
537 case PWM_CMD_SET:
538 return drv_pwm_set(pstTimerMap, configuration);
539 case PWM_CMD_GET:
540 return drv_pwm_get(pstTimerMap, configuration);
541 default:
542 return -RT_EINVAL;
543 }
544 }
545
546 static struct rt_pwm_ops drv_ops = {drv_pwm_control};
547
gd32_hw_pwm_init(void)548 static rt_err_t gd32_hw_pwm_init(void)
549 {
550 pwm_find_timer_periph();
551 rcu_config();
552 gpio_config();
553 timer_config();
554
555 return RT_EOK;
556 }
557
gd32_pwm_init(void)558 static int gd32_pwm_init(void)
559 {
560 int i = 0;
561 int result = RT_EOK;
562
563 /* pwm init */
564 if (gd32_hw_pwm_init() != RT_EOK)
565 {
566 LOG_E("PWM init failed");
567 result = -RT_ERROR;
568 goto __exit;
569 }
570
571 LOG_D("PWM init success");
572
573 for (i = 0; i < sizeof(gd32_pwm_obj) / sizeof(gd32_pwm_obj[0]); i++)
574 {
575 /* register pwm device */
576 if (rt_device_pwm_register(&gd32_pwm_obj[i].pwm_device, gd32_pwm_obj[i].tim_handle.name, &drv_ops,
577 &gd32_pwm_obj[i].tim_handle)== RT_EOK )
578 {
579 LOG_D("%s register success", gd32_pwm_obj[i].tim_handle.name);
580 }
581 else
582 {
583 LOG_E("%s register failed", gd32_pwm_obj[i].tim_handle.name);
584 result = -RT_ERROR;
585 }
586 }
587
588 __exit:
589 return result;
590 }
591 INIT_DEVICE_EXPORT(gd32_pwm_init);
592 #endif /* RT_USING_PWM */
593
594