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