libxr  1.0
Want to be the best embedded framework
Loading...
Searching...
No Matches
ch32_pwm.cpp
1// NOLINTBEGIN(cppcoreguidelines-pro-type-cstyle-cast,performance-no-int-to-ptr)
2#include "ch32_pwm.hpp"
3
4namespace LibXR
5{
6
7CH32PWM::CH32PWM(TIM_TypeDef* tim, uint16_t channel, bool active_high, GPIO_TypeDef* gpio,
8 uint16_t pin, uint32_t pin_remap, bool complementary)
9 : tim_(tim),
10 channel_(channel),
11 active_high_(active_high),
12 complementary_(complementary),
13 gpio_(gpio),
14 pin_(pin),
15 pin_remap_(pin_remap)
16{
17}
18
19bool CH32PWM::OnAPB2(TIM_TypeDef* t)
20{
21 // TIMx macros in vendor headers are casted pointer constants.
22 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-cstyle-cast)
23 return (t == TIM1)
24#if defined(TIM8)
25 || (t == TIM8)
26#endif
27#if defined(TIM9)
28 || (t == TIM9)
29#endif
30#if defined(TIM10)
31 || (t == TIM10)
32#endif
33 ;
34}
35
36bool CH32PWM::IsAdvancedTimer(TIM_TypeDef* t)
37{
38 // TIMx macros in vendor headers are casted pointer constants.
39 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-cstyle-cast)
40 return (t == TIM1)
41#if defined(TIM8)
42 || (t == TIM8)
43#endif
44#if defined(TIM9)
45 || (t == TIM9)
46#endif
47#if defined(TIM10)
48 || (t == TIM10)
49#endif
50 ;
51}
52
53uint32_t CH32PWM::GetTimerClockHz(TIM_TypeDef* t)
54{
55 RCC_ClocksTypeDef c{};
56 RCC_GetClocksFreq(&c);
57
58 const bool ON_APB2 = OnAPB2(t);
59 const uint32_t PCLK = ON_APB2 ? c.PCLK2_Frequency : c.PCLK1_Frequency;
60 const uint32_t HCLK = c.HCLK_Frequency;
61
62 if (PCLK == 0u || HCLK == 0u)
63 {
64 return 0u;
65 }
66
67 // APB 预分频 = HCLK / PCLK。=1 表示不分频;>1 表示分频,此时 TIMxCLK = 2 * PCLKx。
68 const uint32_t APB_DIV = HCLK / PCLK; // 1/2/4/8/16(取整足够)
69 const uint32_t TIMCLK = (APB_DIV > 1u) ? (PCLK * 2u) : PCLK;
70
71 return TIMCLK;
72}
73
74void CH32PWM::EnableGPIOClock(GPIO_TypeDef* gpio)
75{
76 if (gpio == GPIOA)
77 {
78 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
79 }
80 else if (gpio == GPIOB)
81 {
82 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
83 }
84 else if (gpio == GPIOC)
85 {
86 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);
87 }
88 else if (gpio == GPIOD)
89 {
90 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD, ENABLE);
91#if defined(GPIOE)
92 }
93 else if (gpio == GPIOE)
94 {
95 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE, ENABLE);
96 }
97#endif
98}
99
100void CH32PWM::EnableTIMClock(TIM_TypeDef* tim)
101{
102 // Keep each branch independent so conditional compilation cannot break brace structure.
103#if defined(TIM1) && defined(RCC_APB2Periph_TIM1)
104 if (tim == TIM1)
105 {
106 RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE);
107 return;
108 }
109#endif
110#if defined(TIM8) && defined(RCC_APB2Periph_TIM8)
111 if (tim == TIM8)
112 {
113 RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM8, ENABLE);
114 return;
115 }
116#endif
117#if defined(TIM9) && defined(RCC_APB2Periph_TIM9)
118 if (tim == TIM9)
119 {
120 RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM9, ENABLE);
121 return;
122 }
123#endif
124#if defined(TIM10) && defined(RCC_APB2Periph_TIM10)
125 if (tim == TIM10)
126 {
127 RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM10, ENABLE);
128 return;
129 }
130#endif
131#if defined(TIM2) && defined(RCC_APB1Periph_TIM2)
132 if (tim == TIM2)
133 {
134 RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
135 return;
136 }
137#endif
138#if defined(TIM3) && defined(RCC_APB1Periph_TIM3)
139 if (tim == TIM3)
140 {
141 RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
142 return;
143 }
144#endif
145#if defined(TIM4) && defined(RCC_APB1Periph_TIM4)
146 if (tim == TIM4)
147 {
148 RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE);
149 return;
150 }
151#endif
152#if defined(TIM5) && defined(RCC_APB1Periph_TIM5)
153 if (tim == TIM5)
154 {
155 RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM5, ENABLE);
156 return;
157 }
158#endif
159#if defined(TIM6) && defined(RCC_APB1Periph_TIM6)
160 if (tim == TIM6)
161 {
162 RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM6, ENABLE);
163 return;
164 }
165#endif
166#if defined(TIM7) && defined(RCC_APB1Periph_TIM7)
167 if (tim == TIM7)
168 {
169 RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM7, ENABLE);
170 return;
171 }
172#endif
173#if defined(TIM12) && defined(RCC_APB1Periph_TIM12)
174 if (tim == TIM12)
175 {
176 RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM12, ENABLE);
177 return;
178 }
179#endif
180#if defined(TIM13) && defined(RCC_APB1Periph_TIM13)
181 if (tim == TIM13)
182 {
183 RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM13, ENABLE);
184 return;
185 }
186#endif
187#if defined(TIM14) && defined(RCC_APB1Periph_TIM14)
188 if (tim == TIM14)
189 {
190 RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM14, ENABLE);
191 }
192#endif
193}
194
195void CH32PWM::ConfigureGPIO()
196{
197 // AFIO 时钟(用于重映射)
198 RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
199
200 // 需要重映射则打开
201 if (pin_remap_ != 0u)
202 {
203 GPIO_PinRemapConfig(pin_remap_, ENABLE);
204 }
205
206 // 端口时钟 + 复用推挽输出
207 EnableGPIOClock(gpio_);
208 GPIO_InitTypeDef io{};
209 io.GPIO_Pin = pin_;
210 io.GPIO_Speed = GPIO_Speed_50MHz;
211 io.GPIO_Mode = GPIO_Mode_AF_PP;
212 GPIO_Init(gpio_, &io);
213}
214
215// --- PWM 接口 ---
216
217ErrorCode CH32PWM::SetDutyCycle(float value)
218{
219 if (!tim_)
220 {
221 return ErrorCode::ARG_ERR;
222 }
223
224 if (value < 0.0f)
225 {
226 value = 0.0f;
227 }
228 if (value > 1.0f)
229 {
230 value = 1.0f;
231 }
232
233 const uint32_t ARR = ReadARR32(tim_);
234 const uint32_t PULSE = static_cast<uint32_t>((ARR + 1u) * value + 0.5f);
235
236 ApplyCompare(PULSE);
237 return ErrorCode::OK;
238}
239
240ErrorCode CH32PWM::SetConfig(Configuration cfg)
241{
242 if (!tim_)
243 {
244 return ErrorCode::ARG_ERR;
245 }
246 if (cfg.frequency == 0u)
247 {
248 return ErrorCode::ARG_ERR;
249 }
250
251 // 先完成引脚与(可选)重映射
252 ConfigureGPIO();
253
254 // 确保定时器时钟已开
255 EnableTIMClock(tim_);
256
257 const uint32_t TIMCLK = GetTimerClockHz(tim_);
258 if (TIMCLK == 0u)
259 {
260 return ErrorCode::INIT_ERR;
261 }
262
263 // 寻找 PSC/ARR(16 位),偏向更小的 PSC 以提升占空比分辨率
264 bool found = false;
265 uint32_t best_psc = 1, best_arr = 0;
266
267 for (uint32_t psc = 1; psc <= 0xFFFFu; ++psc)
268 {
269 const uint32_t ARR = TIMCLK / (psc * cfg.frequency);
270 if (ARR == 0u)
271 {
272 break;
273 }
274 if (ARR <= 0x10000u)
275 {
276 best_psc = psc;
277 best_arr = ARR; // 写寄存器时用 (arr-1)
278 found = true;
279 break;
280 }
281 }
282 if (!found || best_arr == 0u)
283 {
284 return ErrorCode::INIT_ERR;
285 }
286
287 TIM_TimeBaseInitTypeDef tb{};
288 tb.TIM_Prescaler = static_cast<uint16_t>(best_psc - 1u);
289 tb.TIM_CounterMode = TIM_CounterMode_Up;
290 tb.TIM_Period = static_cast<uint16_t>(best_arr - 1u);
291 tb.TIM_ClockDivision = TIM_CKD_DIV1;
292 tb.TIM_RepetitionCounter = 0;
293 TIM_TimeBaseInit(tim_, &tb);
294
295 // 开启 ARR 预装载
296 TIM_ARRPreloadConfig(tim_, ENABLE);
297
298 // 配置通道为 PWM1,高电平有效;初始脉宽 0
299 OcInitForChannel(0);
300
301 // 通过 UG 把预装载推入影子寄存器
302 TIM_GenerateEvent(tim_, TIM_EventSource_Update);
303
304 // 高级定时器需开启主输出(MOE)
305 if (IsAdvancedTimer(tim_))
306 {
307 TIM_CtrlPWMOutputs(tim_, ENABLE);
308 }
309 return ErrorCode::OK;
310}
311
312ErrorCode CH32PWM::Enable()
313{
314 if (!tim_)
315 {
316 return ErrorCode::ARG_ERR;
317 }
318
319 // 先开通道,后启计数器,避免毛刺
320 EnableChannel(true);
321 if (complementary_ && IsAdvancedTimer(tim_))
322 {
323 EnableChannelN(true);
324 }
325 if (IsAdvancedTimer(tim_))
326 {
327 TIM_CtrlPWMOutputs(tim_, ENABLE);
328 }
329 TIM_Cmd(tim_, ENABLE);
330 return ErrorCode::OK;
331}
332
333ErrorCode CH32PWM::Disable()
334{
335 if (!tim_)
336 {
337 return ErrorCode::ARG_ERR;
338 }
339
340 if (complementary_ && IsAdvancedTimer(tim_))
341 {
342 EnableChannelN(false);
343 }
344 EnableChannel(false);
345
346 return ErrorCode::OK;
347}
348
349// --- helpers: compare / channel gates ---
350
351void CH32PWM::ApplyCompare(uint32_t pulse)
352{
353 const uint16_t CCR = static_cast<uint16_t>(std::min<uint32_t>(pulse, 0xFFFFu));
354 switch (channel_)
355 {
356 case TIM_Channel_1:
357 TIM_SetCompare1(tim_, CCR);
358 break;
359 case TIM_Channel_2:
360 TIM_SetCompare2(tim_, CCR);
361 break;
362 case TIM_Channel_3:
363 TIM_SetCompare3(tim_, CCR);
364 break;
365 case TIM_Channel_4:
366 TIM_SetCompare4(tim_, CCR);
367 break;
368 default:
369 break;
370 }
371}
372
373void CH32PWM::OcInitForChannel(uint32_t pulse)
374{
375 TIM_OCInitTypeDef oc{};
376 oc.TIM_OCMode = TIM_OCMode_PWM1;
377 oc.TIM_OutputState = TIM_OutputState_Enable;
378 oc.TIM_Pulse = static_cast<uint16_t>(pulse);
379 oc.TIM_OCPolarity = active_high_ ? TIM_OCPolarity_High : TIM_OCPolarity_Low;
380
381#if defined(TIM_OCNPolarity_High)
382 if (complementary_ && IsAdvancedTimer(tim_))
383 {
384 oc.TIM_OutputNState = TIM_OutputNState_Enable;
385 oc.TIM_OCNPolarity = active_high_ ? TIM_OCNPolarity_High : TIM_OCNPolarity_Low;
386 }
387#endif
388
389 switch (channel_)
390 {
391 case TIM_Channel_1:
392 TIM_OC1Init(tim_, &oc);
393 TIM_OC1PreloadConfig(tim_, TIM_OCPreload_Enable);
394 break;
395 case TIM_Channel_2:
396 TIM_OC2Init(tim_, &oc);
397 TIM_OC2PreloadConfig(tim_, TIM_OCPreload_Enable);
398 break;
399 case TIM_Channel_3:
400 TIM_OC3Init(tim_, &oc);
401 TIM_OC3PreloadConfig(tim_, TIM_OCPreload_Enable);
402 break;
403 case TIM_Channel_4:
404 TIM_OC4Init(tim_, &oc);
405 TIM_OC4PreloadConfig(tim_, TIM_OCPreload_Enable);
406 break;
407 default:
408 break;
409 }
410}
411
412void CH32PWM::EnableChannel(bool en)
413{
414#if defined(TIM_CCx_Enable)
415 switch (channel_)
416 {
417 case TIM_Channel_1:
418 TIM_CCxCmd(tim_, TIM_Channel_1, en ? TIM_CCx_Enable : TIM_CCx_Disable);
419 break;
420 case TIM_Channel_2:
421 TIM_CCxCmd(tim_, TIM_Channel_2, en ? TIM_CCx_Enable : TIM_CCx_Disable);
422 break;
423 case TIM_Channel_3:
424 TIM_CCxCmd(tim_, TIM_Channel_3, en ? TIM_CCx_Enable : TIM_CCx_Disable);
425 break;
426 case TIM_Channel_4:
427 TIM_CCxCmd(tim_, TIM_Channel_4, en ? TIM_CCx_Enable : TIM_CCx_Disable);
428 break;
429 default:
430 break;
431 }
432#else
433 (void)en;
434#endif
435}
436
437void CH32PWM::EnableChannelN(bool en)
438{
439 if (!IsAdvancedTimer(tim_))
440 {
441 return;
442 }
443 switch (channel_)
444 {
445 case TIM_Channel_1:
446 TIM_CCxNCmd(tim_, TIM_Channel_1, en ? TIM_CCxN_Enable : TIM_CCxN_Disable);
447 break;
448 case TIM_Channel_2:
449 TIM_CCxNCmd(tim_, TIM_Channel_2, en ? TIM_CCxN_Enable : TIM_CCxN_Disable);
450 break;
451 case TIM_Channel_3:
452 TIM_CCxNCmd(tim_, TIM_Channel_3, en ? TIM_CCxN_Enable : TIM_CCxN_Disable);
453 break;
454 default:
455 break; // CH4 无 N
456 }
457}
458
459} // namespace LibXR
460
461// NOLINTEND(cppcoreguidelines-pro-type-cstyle-cast,performance-no-int-to-ptr)
CH32PWM(TIM_TypeDef *tim, uint16_t channel, bool active_high, GPIO_TypeDef *gpio, uint16_t pin, uint32_t pin_remap=0, bool complementary=false)
构造 PWM 对象 / Construct PWM object
LibXR 命名空间
Definition ch32_can.hpp:14