1 | /* |
2 | * Copyright (c) 2015, Freescale Semiconductor, Inc. |
3 | * Copyright 2016-2020 NXP |
4 | * All rights reserved. |
5 | * |
6 | * SPDX-License-Identifier: BSD-3-Clause |
7 | */ |
8 | |
9 | #include "fsl_pwm.h" |
10 | |
11 | /* Component ID definition, used by tools. */ |
12 | #ifndef FSL_COMPONENT_ID |
13 | #define FSL_COMPONENT_ID "platform.drivers.pwm" |
14 | #endif |
15 | |
16 | /******************************************************************************* |
17 | * Prototypes |
18 | ******************************************************************************/ |
19 | /*! |
20 | * @brief Get the instance from the base address |
21 | * |
22 | * @param base PWM peripheral base address |
23 | * |
24 | * @return The PWM module instance |
25 | */ |
26 | static uint32_t PWM_GetInstance(PWM_Type *base); |
27 | |
28 | /******************************************************************************* |
29 | * Variables |
30 | ******************************************************************************/ |
31 | /*! @brief Pointers to PWM bases for each instance. */ |
32 | static PWM_Type *const s_pwmBases[] = PWM_BASE_PTRS; |
33 | |
34 | #if !(defined(FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) |
35 | /*! @brief Pointers to PWM clocks for each PWM submodule. */ |
36 | static const clock_ip_name_t s_pwmClocks[][FSL_FEATURE_PWM_SUBMODULE_COUNT] = PWM_CLOCKS; |
37 | #endif /* FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL */ |
38 | |
39 | /******************************************************************************* |
40 | * Code |
41 | ******************************************************************************/ |
42 | |
43 | /*! |
44 | * brief Complement the variable of type uint16_t as needed |
45 | * |
46 | * This function can complement the variable of type uint16_t as needed.For example, |
47 | * need to ask for the opposite of a positive integer. |
48 | * |
49 | * param value Parameters of type uint16_t |
50 | */ |
51 | static inline uint16_t PWM_GetComplementU16(uint16_t value) |
52 | { |
53 | return (~value + 1U); |
54 | } |
55 | |
56 | static inline uint16_t dutyCycleToReloadValue(uint8_t dutyCyclePercent) |
57 | { |
58 | /* Rounding calculations to improve the accuracy of reloadValue */ |
59 | return ((65535U * dutyCyclePercent) + 50U) / 100U; |
60 | } |
61 | |
62 | static uint32_t PWM_GetInstance(PWM_Type *base) |
63 | { |
64 | uint32_t instance; |
65 | |
66 | /* Find the instance index from base address mappings. */ |
67 | for (instance = 0; instance < ARRAY_SIZE(s_pwmBases); instance++) |
68 | { |
69 | if (s_pwmBases[instance] == base) |
70 | { |
71 | break; |
72 | } |
73 | } |
74 | |
75 | assert(instance < ARRAY_SIZE(s_pwmBases)); |
76 | |
77 | return instance; |
78 | } |
79 | |
80 | /*! |
81 | * brief Ungates the PWM submodule clock and configures the peripheral for basic operation. |
82 | * |
83 | * note This API should be called at the beginning of the application using the PWM driver. |
84 | * |
85 | * param base PWM peripheral base address |
86 | * param subModule PWM submodule to configure |
87 | * param config Pointer to user's PWM config structure. |
88 | * |
89 | * return kStatus_Success means success; else failed. |
90 | */ |
91 | status_t PWM_Init(PWM_Type *base, pwm_submodule_t subModule, const pwm_config_t *config) |
92 | { |
93 | assert(config); |
94 | |
95 | uint16_t reg; |
96 | |
97 | /* Source clock for submodule 0 cannot be itself */ |
98 | if ((config->clockSource == kPWM_Submodule0Clock) && (subModule == kPWM_Module_0)) |
99 | { |
100 | return kStatus_Fail; |
101 | } |
102 | |
103 | /* Reload source select clock for submodule 0 cannot be master reload */ |
104 | if ((config->reloadSelect == kPWM_MasterReload) && (subModule == kPWM_Module_0)) |
105 | { |
106 | return kStatus_Fail; |
107 | } |
108 | |
109 | #if !(defined(FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) |
110 | /* Ungate the PWM submodule clock*/ |
111 | CLOCK_EnableClock(s_pwmClocks[PWM_GetInstance(base)][subModule]); |
112 | #endif /* FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL */ |
113 | |
114 | /* Clear the fault status flags */ |
115 | base->FSTS |= PWM_FSTS_FFLAG_MASK; |
116 | |
117 | reg = base->SM[subModule].CTRL2; |
118 | |
119 | /* Setup the submodule clock-source, control source of the INIT signal, |
120 | * source of the force output signal, operation in debug & wait modes and reload source select |
121 | */ |
122 | reg &= ~(uint16_t)(PWM_CTRL2_CLK_SEL_MASK | PWM_CTRL2_FORCE_SEL_MASK | PWM_CTRL2_INIT_SEL_MASK | |
123 | PWM_CTRL2_INDEP_MASK | PWM_CTRL2_WAITEN_MASK | PWM_CTRL2_DBGEN_MASK | PWM_CTRL2_RELOAD_SEL_MASK); |
124 | reg |= (PWM_CTRL2_CLK_SEL(config->clockSource) | PWM_CTRL2_FORCE_SEL(config->forceTrigger) | |
125 | PWM_CTRL2_INIT_SEL(config->initializationControl) | PWM_CTRL2_DBGEN(config->enableDebugMode) | |
126 | PWM_CTRL2_WAITEN(config->enableWait) | PWM_CTRL2_RELOAD_SEL(config->reloadSelect)); |
127 | |
128 | /* Setup PWM A & B to be independent or a complementary-pair */ |
129 | switch (config->pairOperation) |
130 | { |
131 | case kPWM_Independent: |
132 | reg |= PWM_CTRL2_INDEP_MASK; |
133 | break; |
134 | case kPWM_ComplementaryPwmA: |
135 | base->MCTRL &= ~((uint16_t)1U << (PWM_MCTRL_IPOL_SHIFT + (uint16_t)subModule)); |
136 | break; |
137 | case kPWM_ComplementaryPwmB: |
138 | base->MCTRL |= ((uint16_t)1U << (PWM_MCTRL_IPOL_SHIFT + (uint16_t)subModule)); |
139 | break; |
140 | default: |
141 | assert(false); |
142 | break; |
143 | } |
144 | base->SM[subModule].CTRL2 = reg; |
145 | |
146 | reg = base->SM[subModule].CTRL; |
147 | |
148 | /* Setup the clock prescale, load mode and frequency */ |
149 | reg &= ~(uint16_t)(PWM_CTRL_PRSC_MASK | PWM_CTRL_LDFQ_MASK | PWM_CTRL_LDMOD_MASK); |
150 | reg |= (PWM_CTRL_PRSC(config->prescale) | PWM_CTRL_LDFQ(config->reloadFrequency)); |
151 | |
152 | /* Setup register reload logic */ |
153 | switch (config->reloadLogic) |
154 | { |
155 | case kPWM_ReloadImmediate: |
156 | reg |= PWM_CTRL_LDMOD_MASK; |
157 | break; |
158 | case kPWM_ReloadPwmHalfCycle: |
159 | reg |= PWM_CTRL_HALF_MASK; |
160 | reg &= (uint16_t)(~PWM_CTRL_FULL_MASK); |
161 | break; |
162 | case kPWM_ReloadPwmFullCycle: |
163 | reg &= (uint16_t)(~PWM_CTRL_HALF_MASK); |
164 | reg |= PWM_CTRL_FULL_MASK; |
165 | break; |
166 | case kPWM_ReloadPwmHalfAndFullCycle: |
167 | reg |= PWM_CTRL_HALF_MASK; |
168 | reg |= PWM_CTRL_FULL_MASK; |
169 | break; |
170 | default: |
171 | assert(false); |
172 | break; |
173 | } |
174 | base->SM[subModule].CTRL = reg; |
175 | |
176 | /* Issue a Force trigger event when configured to trigger locally */ |
177 | if (config->forceTrigger == kPWM_Force_Local) |
178 | { |
179 | base->SM[subModule].CTRL2 |= PWM_CTRL2_FORCE(1U); |
180 | } |
181 | |
182 | return kStatus_Success; |
183 | } |
184 | |
185 | /*! |
186 | * brief Gate the PWM submodule clock |
187 | * |
188 | * param base PWM peripheral base address |
189 | * param subModule PWM submodule to deinitialize |
190 | */ |
191 | void PWM_Deinit(PWM_Type *base, pwm_submodule_t subModule) |
192 | { |
193 | /* Stop the submodule */ |
194 | base->MCTRL &= ~((uint16_t)1U << (PWM_MCTRL_RUN_SHIFT + (uint16_t)subModule)); |
195 | |
196 | #if !(defined(FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) |
197 | /* Gate the PWM submodule clock*/ |
198 | CLOCK_DisableClock(s_pwmClocks[PWM_GetInstance(base)][subModule]); |
199 | #endif /* FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL */ |
200 | } |
201 | |
202 | /*! |
203 | * brief Fill in the PWM config struct with the default settings |
204 | * |
205 | * The default values are: |
206 | * code |
207 | * config->enableDebugMode = false; |
208 | * config->enableWait = false; |
209 | * config->reloadSelect = kPWM_LocalReload; |
210 | * config->clockSource = kPWM_BusClock; |
211 | * config->prescale = kPWM_Prescale_Divide_1; |
212 | * config->initializationControl = kPWM_Initialize_LocalSync; |
213 | * config->forceTrigger = kPWM_Force_Local; |
214 | * config->reloadFrequency = kPWM_LoadEveryOportunity; |
215 | * config->reloadLogic = kPWM_ReloadImmediate; |
216 | * config->pairOperation = kPWM_Independent; |
217 | * endcode |
218 | * param config Pointer to user's PWM config structure. |
219 | */ |
220 | void PWM_GetDefaultConfig(pwm_config_t *config) |
221 | { |
222 | assert(config); |
223 | |
224 | /* Initializes the configure structure to zero. */ |
225 | (void)memset(config, 0, sizeof(*config)); |
226 | |
227 | /* PWM is paused in debug mode */ |
228 | config->enableDebugMode = false; |
229 | /* PWM is paused in wait mode */ |
230 | config->enableWait = false; |
231 | /* PWM module uses the local reload signal to reload registers */ |
232 | config->reloadSelect = kPWM_LocalReload; |
233 | /* Use the IP Bus clock as source clock for the PWM submodule */ |
234 | config->clockSource = kPWM_BusClock; |
235 | /* Clock source prescale is set to divide by 1*/ |
236 | config->prescale = kPWM_Prescale_Divide_1; |
237 | /* Local sync causes initialization */ |
238 | config->initializationControl = kPWM_Initialize_LocalSync; |
239 | /* The local force signal, CTRL2[FORCE], from the submodule is used to force updates */ |
240 | config->forceTrigger = kPWM_Force_Local; |
241 | /* PWM reload frequency, reload opportunity is PWM half cycle or full cycle. |
242 | * This field is not used in Immediate reload mode |
243 | */ |
244 | config->reloadFrequency = kPWM_LoadEveryOportunity; |
245 | /* Buffered-registers get loaded with new values as soon as LDOK bit is set */ |
246 | config->reloadLogic = kPWM_ReloadImmediate; |
247 | /* PWM A & PWM B operate as 2 independent channels */ |
248 | config->pairOperation = kPWM_Independent; |
249 | } |
250 | |
251 | /*! |
252 | * brief Sets up the PWM signals for a PWM submodule. |
253 | * |
254 | * The function initializes the submodule according to the parameters passed in by the user. The function |
255 | * also sets up the value compare registers to match the PWM signal requirements. |
256 | * If the dead time insertion logic is enabled, the pulse period is reduced by the |
257 | * dead time period specified by the user. |
258 | * |
259 | * param base PWM peripheral base address |
260 | * param subModule PWM submodule to configure |
261 | * param chnlParams Array of PWM channel parameters to configure the channel(s) |
262 | * param numOfChnls Number of channels to configure, this should be the size of the array passed in. |
263 | * Array size should not be more than 2 as each submodule has 2 pins to output PWM |
264 | * param mode PWM operation mode, options available in enumeration ::pwm_mode_t |
265 | * param pwmFreq_Hz PWM signal frequency in Hz |
266 | * param srcClock_Hz PWM main counter clock in Hz. |
267 | * |
268 | * return Returns kStatusFail if there was error setting up the signal; kStatusSuccess otherwise |
269 | */ |
270 | status_t PWM_SetupPwm(PWM_Type *base, |
271 | pwm_submodule_t subModule, |
272 | const pwm_signal_param_t *chnlParams, |
273 | uint8_t numOfChnls, |
274 | pwm_mode_t mode, |
275 | uint32_t pwmFreq_Hz, |
276 | uint32_t srcClock_Hz) |
277 | { |
278 | assert(chnlParams); |
279 | assert(pwmFreq_Hz); |
280 | assert(numOfChnls); |
281 | assert(srcClock_Hz); |
282 | |
283 | uint32_t pwmClock; |
284 | uint16_t pulseCnt = 0, pwmHighPulse = 0; |
285 | uint16_t modulo = 0; |
286 | uint8_t i, polarityShift = 0, outputEnableShift = 0; |
287 | |
288 | if (numOfChnls > 2U) |
289 | { |
290 | /* Each submodule has 2 signals; PWM A & PWM B */ |
291 | return kStatus_Fail; |
292 | } |
293 | |
294 | /* Divide the clock by the prescale value */ |
295 | pwmClock = (srcClock_Hz / (1UL << ((base->SM[subModule].CTRL & PWM_CTRL_PRSC_MASK) >> PWM_CTRL_PRSC_SHIFT))); |
296 | pulseCnt = (uint16_t)(pwmClock / pwmFreq_Hz); |
297 | |
298 | /* Setup each PWM channel */ |
299 | for (i = 0; i < numOfChnls; i++) |
300 | { |
301 | /* Calculate pulse width */ |
302 | pwmHighPulse = (pulseCnt * chnlParams->dutyCyclePercent) / 100U; |
303 | |
304 | /* Setup the different match registers to generate the PWM signal */ |
305 | switch (mode) |
306 | { |
307 | case kPWM_SignedCenterAligned: |
308 | /* Setup the PWM period for a signed center aligned signal */ |
309 | if (i == 0U) |
310 | { |
311 | modulo = (pulseCnt >> 1U); |
312 | /* Indicates the start of the PWM period */ |
313 | base->SM[subModule].INIT = PWM_GetComplementU16(modulo); |
314 | /* Indicates the center value */ |
315 | base->SM[subModule].VAL0 = 0; |
316 | /* Indicates the end of the PWM period */ |
317 | /* The change during the end to start of the PWM period requires a count time */ |
318 | base->SM[subModule].VAL1 = modulo - 1U; |
319 | } |
320 | |
321 | /* Setup the PWM dutycycle */ |
322 | if (chnlParams->pwmChannel == kPWM_PwmA) |
323 | { |
324 | base->SM[subModule].VAL2 = PWM_GetComplementU16(pwmHighPulse / 2U); |
325 | base->SM[subModule].VAL3 = (pwmHighPulse / 2U); |
326 | } |
327 | else |
328 | { |
329 | base->SM[subModule].VAL4 = PWM_GetComplementU16(pwmHighPulse / 2U); |
330 | base->SM[subModule].VAL5 = (pwmHighPulse / 2U); |
331 | } |
332 | break; |
333 | case kPWM_CenterAligned: |
334 | /* Setup the PWM period for an unsigned center aligned signal */ |
335 | /* Indicates the start of the PWM period */ |
336 | if (i == 0U) |
337 | { |
338 | base->SM[subModule].INIT = 0; |
339 | /* Indicates the center value */ |
340 | base->SM[subModule].VAL0 = (pulseCnt / 2U); |
341 | /* Indicates the end of the PWM period */ |
342 | /* The change during the end to start of the PWM period requires a count time */ |
343 | base->SM[subModule].VAL1 = pulseCnt - 1U; |
344 | } |
345 | |
346 | /* Setup the PWM dutycycle */ |
347 | if (chnlParams->pwmChannel == kPWM_PwmA) |
348 | { |
349 | base->SM[subModule].VAL2 = ((pulseCnt - pwmHighPulse) / 2U); |
350 | base->SM[subModule].VAL3 = ((pulseCnt + pwmHighPulse) / 2U); |
351 | } |
352 | else |
353 | { |
354 | base->SM[subModule].VAL4 = ((pulseCnt - pwmHighPulse) / 2U); |
355 | base->SM[subModule].VAL5 = ((pulseCnt + pwmHighPulse) / 2U); |
356 | } |
357 | break; |
358 | case kPWM_SignedEdgeAligned: |
359 | /* Setup the PWM period for a signed edge aligned signal */ |
360 | if (i == 0U) |
361 | { |
362 | modulo = (pulseCnt >> 1U); |
363 | /* Indicates the start of the PWM period */ |
364 | base->SM[subModule].INIT = PWM_GetComplementU16(modulo); |
365 | /* Indicates the center value */ |
366 | base->SM[subModule].VAL0 = 0; |
367 | /* Indicates the end of the PWM period */ |
368 | /* The change during the end to start of the PWM period requires a count time */ |
369 | base->SM[subModule].VAL1 = modulo - 1U; |
370 | } |
371 | |
372 | /* Setup the PWM dutycycle */ |
373 | if (chnlParams->pwmChannel == kPWM_PwmA) |
374 | { |
375 | base->SM[subModule].VAL2 = PWM_GetComplementU16(modulo); |
376 | base->SM[subModule].VAL3 = PWM_GetComplementU16(modulo) + pwmHighPulse; |
377 | } |
378 | else |
379 | { |
380 | base->SM[subModule].VAL4 = PWM_GetComplementU16(modulo); |
381 | base->SM[subModule].VAL5 = PWM_GetComplementU16(modulo) + pwmHighPulse; |
382 | } |
383 | break; |
384 | case kPWM_EdgeAligned: |
385 | /* Setup the PWM period for a unsigned edge aligned signal */ |
386 | /* Indicates the start of the PWM period */ |
387 | if (i == 0U) |
388 | { |
389 | base->SM[subModule].INIT = 0; |
390 | /* Indicates the center value */ |
391 | base->SM[subModule].VAL0 = (pulseCnt / 2U); |
392 | /* Indicates the end of the PWM period */ |
393 | /* The change during the end to start of the PWM period requires a count time */ |
394 | base->SM[subModule].VAL1 = pulseCnt - 1U; |
395 | } |
396 | |
397 | /* Setup the PWM dutycycle */ |
398 | if (chnlParams->pwmChannel == kPWM_PwmA) |
399 | { |
400 | base->SM[subModule].VAL2 = 0; |
401 | base->SM[subModule].VAL3 = pwmHighPulse; |
402 | } |
403 | else |
404 | { |
405 | base->SM[subModule].VAL4 = 0; |
406 | base->SM[subModule].VAL5 = pwmHighPulse; |
407 | } |
408 | break; |
409 | default: |
410 | assert(false); |
411 | break; |
412 | } |
413 | /* Setup register shift values based on the channel being configured. |
414 | * Also setup the deadtime value |
415 | */ |
416 | if (chnlParams->pwmChannel == kPWM_PwmA) |
417 | { |
418 | polarityShift = PWM_OCTRL_POLA_SHIFT; |
419 | outputEnableShift = PWM_OUTEN_PWMA_EN_SHIFT; |
420 | base->SM[subModule].DTCNT0 = PWM_DTCNT0_DTCNT0(chnlParams->deadtimeValue); |
421 | } |
422 | else |
423 | { |
424 | polarityShift = PWM_OCTRL_POLB_SHIFT; |
425 | outputEnableShift = PWM_OUTEN_PWMB_EN_SHIFT; |
426 | base->SM[subModule].DTCNT1 = PWM_DTCNT1_DTCNT1(chnlParams->deadtimeValue); |
427 | } |
428 | |
429 | /* Set PWM output fault status */ |
430 | switch (chnlParams->pwmChannel) |
431 | { |
432 | case kPWM_PwmA: |
433 | base->SM[subModule].OCTRL &= ~((uint16_t)PWM_OCTRL_PWMAFS_MASK); |
434 | base->SM[subModule].OCTRL |= (((uint16_t)(chnlParams->faultState) << (uint16_t)PWM_OCTRL_PWMAFS_SHIFT) & |
435 | (uint16_t)PWM_OCTRL_PWMAFS_MASK); |
436 | break; |
437 | case kPWM_PwmB: |
438 | base->SM[subModule].OCTRL &= ~((uint16_t)PWM_OCTRL_PWMBFS_MASK); |
439 | base->SM[subModule].OCTRL |= (((uint16_t)(chnlParams->faultState) << (uint16_t)PWM_OCTRL_PWMBFS_SHIFT) & |
440 | (uint16_t)PWM_OCTRL_PWMBFS_MASK); |
441 | break; |
442 | case kPWM_PwmX: |
443 | base->SM[subModule].OCTRL &= ~((uint16_t)PWM_OCTRL_PWMXFS_MASK); |
444 | base->SM[subModule].OCTRL |= (((uint16_t)(chnlParams->faultState) << (uint16_t)PWM_OCTRL_PWMXFS_SHIFT) & |
445 | (uint16_t)PWM_OCTRL_PWMXFS_MASK); |
446 | break; |
447 | default: |
448 | assert(false); |
449 | break; |
450 | } |
451 | |
452 | /* Setup signal active level */ |
453 | if ((bool)chnlParams->level == kPWM_HighTrue) |
454 | { |
455 | base->SM[subModule].OCTRL &= ~((uint16_t)1U << (uint16_t)polarityShift); |
456 | } |
457 | else |
458 | { |
459 | base->SM[subModule].OCTRL |= ((uint16_t)1U << (uint16_t)polarityShift); |
460 | } |
461 | /* Enable PWM output */ |
462 | base->OUTEN |= ((uint16_t)1U << ((uint16_t)outputEnableShift + (uint16_t)subModule)); |
463 | |
464 | /* Get the next channel parameters */ |
465 | chnlParams++; |
466 | } |
467 | |
468 | return kStatus_Success; |
469 | } |
470 | |
471 | /*! |
472 | * brief Updates the PWM signal's dutycycle. |
473 | * |
474 | * The function updates the PWM dutycyle to the new value that is passed in. |
475 | * If the dead time insertion logic is enabled then the pulse period is reduced by the |
476 | * dead time period specified by the user. |
477 | * |
478 | * param base PWM peripheral base address |
479 | * param subModule PWM submodule to configure |
480 | * param pwmSignal Signal (PWM A or PWM B) to update |
481 | * param currPwmMode The current PWM mode set during PWM setup |
482 | * param dutyCyclePercent New PWM pulse width, value should be between 0 to 100 |
483 | * 0=inactive signal(0% duty cycle)... |
484 | * 100=active signal (100% duty cycle) |
485 | */ |
486 | void PWM_UpdatePwmDutycycle(PWM_Type *base, |
487 | pwm_submodule_t subModule, |
488 | pwm_channels_t pwmSignal, |
489 | pwm_mode_t currPwmMode, |
490 | uint8_t dutyCyclePercent) |
491 | { |
492 | assert(dutyCyclePercent <= 100U); |
493 | assert((uint16_t)pwmSignal < 2U); |
494 | uint16_t reloadValue = dutyCycleToReloadValue(dutyCyclePercent); |
495 | |
496 | PWM_UpdatePwmDutycycleHighAccuracy(base, subModule, pwmSignal, currPwmMode, reloadValue); |
497 | } |
498 | |
499 | /*! |
500 | * brief Updates the PWM signal's dutycycle with 16-bit accuracy. |
501 | * |
502 | * The function updates the PWM dutycyle to the new value that is passed in. |
503 | * If the dead time insertion logic is enabled then the pulse period is reduced by the |
504 | * dead time period specified by the user. |
505 | * |
506 | * param base PWM peripheral base address |
507 | * param subModule PWM submodule to configure |
508 | * param pwmSignal Signal (PWM A or PWM B) to update |
509 | * param currPwmMode The current PWM mode set during PWM setup |
510 | * param dutyCycle New PWM pulse width, value should be between 0 to 65535 |
511 | * 0=inactive signal(0% duty cycle)... |
512 | * 65535=active signal (100% duty cycle) |
513 | */ |
514 | void PWM_UpdatePwmDutycycleHighAccuracy( |
515 | PWM_Type *base, pwm_submodule_t subModule, pwm_channels_t pwmSignal, pwm_mode_t currPwmMode, uint16_t dutyCycle) |
516 | { |
517 | assert((uint16_t)pwmSignal < 2U); |
518 | uint16_t pulseCnt = 0, pwmHighPulse = 0; |
519 | uint16_t modulo = 0; |
520 | |
521 | switch (currPwmMode) |
522 | { |
523 | case kPWM_SignedCenterAligned: |
524 | modulo = base->SM[subModule].VAL1 + 1U; |
525 | pulseCnt = modulo * 2U; |
526 | /* Calculate pulse width */ |
527 | pwmHighPulse = (pulseCnt * dutyCycle) / 65535U; |
528 | |
529 | /* Setup the PWM dutycycle */ |
530 | if (pwmSignal == kPWM_PwmA) |
531 | { |
532 | base->SM[subModule].VAL2 = PWM_GetComplementU16(pwmHighPulse / 2U); |
533 | base->SM[subModule].VAL3 = (pwmHighPulse / 2U); |
534 | } |
535 | else |
536 | { |
537 | base->SM[subModule].VAL4 = PWM_GetComplementU16(pwmHighPulse / 2U); |
538 | base->SM[subModule].VAL5 = (pwmHighPulse / 2U); |
539 | } |
540 | break; |
541 | case kPWM_CenterAligned: |
542 | pulseCnt = base->SM[subModule].VAL1 + 1U; |
543 | /* Calculate pulse width */ |
544 | pwmHighPulse = (pulseCnt * dutyCycle) / 65535U; |
545 | |
546 | /* Setup the PWM dutycycle */ |
547 | if (pwmSignal == kPWM_PwmA) |
548 | { |
549 | base->SM[subModule].VAL2 = ((pulseCnt - pwmHighPulse) / 2U); |
550 | base->SM[subModule].VAL3 = ((pulseCnt + pwmHighPulse) / 2U); |
551 | } |
552 | else |
553 | { |
554 | base->SM[subModule].VAL4 = ((pulseCnt - pwmHighPulse) / 2U); |
555 | base->SM[subModule].VAL5 = ((pulseCnt + pwmHighPulse) / 2U); |
556 | } |
557 | break; |
558 | case kPWM_SignedEdgeAligned: |
559 | modulo = base->SM[subModule].VAL1 + 1U; |
560 | pulseCnt = modulo * 2U; |
561 | /* Calculate pulse width */ |
562 | pwmHighPulse = (pulseCnt * dutyCycle) / 65535U; |
563 | |
564 | /* Setup the PWM dutycycle */ |
565 | if (pwmSignal == kPWM_PwmA) |
566 | { |
567 | base->SM[subModule].VAL2 = PWM_GetComplementU16(modulo); |
568 | base->SM[subModule].VAL3 = PWM_GetComplementU16(modulo) + pwmHighPulse; |
569 | } |
570 | else |
571 | { |
572 | base->SM[subModule].VAL4 = PWM_GetComplementU16(modulo); |
573 | base->SM[subModule].VAL5 = PWM_GetComplementU16(modulo) + pwmHighPulse; |
574 | } |
575 | break; |
576 | case kPWM_EdgeAligned: |
577 | pulseCnt = base->SM[subModule].VAL1 + 1U; |
578 | /* Calculate pulse width */ |
579 | pwmHighPulse = (pulseCnt * dutyCycle) / 65535U; |
580 | |
581 | /* Setup the PWM dutycycle */ |
582 | if (pwmSignal == kPWM_PwmA) |
583 | { |
584 | base->SM[subModule].VAL2 = 0; |
585 | base->SM[subModule].VAL3 = pwmHighPulse; |
586 | } |
587 | else |
588 | { |
589 | base->SM[subModule].VAL4 = 0; |
590 | base->SM[subModule].VAL5 = pwmHighPulse; |
591 | } |
592 | break; |
593 | default: |
594 | assert(false); |
595 | break; |
596 | } |
597 | } |
598 | |
599 | /*! |
600 | * brief Sets up the PWM input capture |
601 | * |
602 | * Each PWM submodule has 3 pins that can be configured for use as input capture pins. This function |
603 | * sets up the capture parameters for each pin and enables the pin for input capture operation. |
604 | * |
605 | * param base PWM peripheral base address |
606 | * param subModule PWM submodule to configure |
607 | * param pwmChannel Channel in the submodule to setup |
608 | * param inputCaptureParams Parameters passed in to set up the input pin |
609 | */ |
610 | void PWM_SetupInputCapture(PWM_Type *base, |
611 | pwm_submodule_t subModule, |
612 | pwm_channels_t pwmChannel, |
613 | const pwm_input_capture_param_t *inputCaptureParams) |
614 | { |
615 | uint16_t reg = 0; |
616 | switch (pwmChannel) |
617 | { |
618 | case kPWM_PwmA: |
619 | /* Setup the capture paramters for PWM A pin */ |
620 | reg = (PWM_CAPTCTRLA_INP_SELA(inputCaptureParams->captureInputSel) | |
621 | PWM_CAPTCTRLA_EDGA0(inputCaptureParams->edge0) | PWM_CAPTCTRLA_EDGA1(inputCaptureParams->edge1) | |
622 | PWM_CAPTCTRLA_ONESHOTA(inputCaptureParams->enableOneShotCapture) | |
623 | PWM_CAPTCTRLA_CFAWM(inputCaptureParams->fifoWatermark)); |
624 | /* Enable the edge counter if using the output edge counter */ |
625 | if (inputCaptureParams->captureInputSel) |
626 | { |
627 | reg |= PWM_CAPTCTRLA_EDGCNTA_EN_MASK; |
628 | } |
629 | /* Enable input capture operation */ |
630 | reg |= PWM_CAPTCTRLA_ARMA_MASK; |
631 | |
632 | base->SM[subModule].CAPTCTRLA = reg; |
633 | |
634 | /* Setup the compare value when using the edge counter as source */ |
635 | base->SM[subModule].CAPTCOMPA = PWM_CAPTCOMPA_EDGCMPA(inputCaptureParams->edgeCompareValue); |
636 | /* Setup PWM A pin for input capture */ |
637 | base->OUTEN &= ~((uint16_t)1U << (PWM_OUTEN_PWMA_EN_SHIFT + (uint16_t)subModule)); |
638 | break; |
639 | case kPWM_PwmB: |
640 | /* Setup the capture paramters for PWM B pin */ |
641 | reg = (PWM_CAPTCTRLB_INP_SELB(inputCaptureParams->captureInputSel) | |
642 | PWM_CAPTCTRLB_EDGB0(inputCaptureParams->edge0) | PWM_CAPTCTRLB_EDGB1(inputCaptureParams->edge1) | |
643 | PWM_CAPTCTRLB_ONESHOTB(inputCaptureParams->enableOneShotCapture) | |
644 | PWM_CAPTCTRLB_CFBWM(inputCaptureParams->fifoWatermark)); |
645 | /* Enable the edge counter if using the output edge counter */ |
646 | if (inputCaptureParams->captureInputSel) |
647 | { |
648 | reg |= PWM_CAPTCTRLB_EDGCNTB_EN_MASK; |
649 | } |
650 | /* Enable input capture operation */ |
651 | reg |= PWM_CAPTCTRLB_ARMB_MASK; |
652 | |
653 | base->SM[subModule].CAPTCTRLB = reg; |
654 | |
655 | /* Setup the compare value when using the edge counter as source */ |
656 | base->SM[subModule].CAPTCOMPB = PWM_CAPTCOMPB_EDGCMPB(inputCaptureParams->edgeCompareValue); |
657 | /* Setup PWM B pin for input capture */ |
658 | base->OUTEN &= ~((uint16_t)1U << (PWM_OUTEN_PWMB_EN_SHIFT + (uint16_t)subModule)); |
659 | break; |
660 | case kPWM_PwmX: |
661 | reg = (PWM_CAPTCTRLX_INP_SELX(inputCaptureParams->captureInputSel) | |
662 | PWM_CAPTCTRLX_EDGX0(inputCaptureParams->edge0) | PWM_CAPTCTRLX_EDGX1(inputCaptureParams->edge1) | |
663 | PWM_CAPTCTRLX_ONESHOTX(inputCaptureParams->enableOneShotCapture) | |
664 | PWM_CAPTCTRLX_CFXWM(inputCaptureParams->fifoWatermark)); |
665 | /* Enable the edge counter if using the output edge counter */ |
666 | if (inputCaptureParams->captureInputSel) |
667 | { |
668 | reg |= PWM_CAPTCTRLX_EDGCNTX_EN_MASK; |
669 | } |
670 | /* Enable input capture operation */ |
671 | reg |= PWM_CAPTCTRLX_ARMX_MASK; |
672 | |
673 | base->SM[subModule].CAPTCTRLX = reg; |
674 | |
675 | /* Setup the compare value when using the edge counter as source */ |
676 | base->SM[subModule].CAPTCOMPX = PWM_CAPTCOMPX_EDGCMPX(inputCaptureParams->edgeCompareValue); |
677 | /* Setup PWM X pin for input capture */ |
678 | base->OUTEN &= ~((uint16_t)1U << (PWM_OUTEN_PWMX_EN_SHIFT + (uint16_t)subModule)); |
679 | break; |
680 | default: |
681 | assert(false); |
682 | break; |
683 | } |
684 | } |
685 | |
686 | /*! |
687 | * @brief Sets up the PWM fault input filter. |
688 | * |
689 | * @param base PWM peripheral base address |
690 | * @param faultInputFilterParams Parameters passed in to set up the fault input filter. |
691 | */ |
692 | void PWM_SetupFaultInputFilter(PWM_Type *base, const pwm_fault_input_filter_param_t *faultInputFilterParams) |
693 | { |
694 | assert(NULL != faultInputFilterParams); |
695 | |
696 | /* When changing values for fault period from a non-zero value, first write a value of 0 to clear the filter. */ |
697 | if (0U != (base->FFILT & PWM_FFILT_FILT_PER_MASK)) |
698 | { |
699 | base->FFILT &= ~(uint16_t)(PWM_FFILT_FILT_PER_MASK); |
700 | } |
701 | |
702 | base->FFILT = (uint16_t)(PWM_FFILT_FILT_PER(faultInputFilterParams->faultFilterPeriod) | |
703 | PWM_FFILT_FILT_CNT(faultInputFilterParams->faultFilterCount) | |
704 | PWM_FFILT_GSTR(faultInputFilterParams->faultGlitchStretch ? 1U : 0U)); |
705 | } |
706 | |
707 | /*! |
708 | * brief Sets up the PWM fault protection. |
709 | * |
710 | * PWM has 4 fault inputs. |
711 | * |
712 | * param base PWM peripheral base address |
713 | * param faultNum PWM fault to configure. |
714 | * param faultParams Pointer to the PWM fault config structure |
715 | */ |
716 | void PWM_SetupFaults(PWM_Type *base, pwm_fault_input_t faultNum, const pwm_fault_param_t *faultParams) |
717 | { |
718 | assert(faultParams); |
719 | uint16_t reg; |
720 | |
721 | reg = base->FCTRL; |
722 | /* Set the faults level-settting */ |
723 | if (faultParams->faultLevel) |
724 | { |
725 | reg |= ((uint16_t)1U << (PWM_FCTRL_FLVL_SHIFT + (uint16_t)faultNum)); |
726 | } |
727 | else |
728 | { |
729 | reg &= ~((uint16_t)1U << (PWM_FCTRL_FLVL_SHIFT + (uint16_t)faultNum)); |
730 | } |
731 | /* Set the fault clearing mode */ |
732 | if ((uint16_t)faultParams->faultClearingMode != 0U) |
733 | { |
734 | /* Use manual fault clearing */ |
735 | reg &= ~((uint16_t)1U << (PWM_FCTRL_FAUTO_SHIFT + (uint16_t)faultNum)); |
736 | if (faultParams->faultClearingMode == kPWM_ManualSafety) |
737 | { |
738 | /* Use manual fault clearing with safety mode enabled */ |
739 | reg |= ((uint16_t)1U << (PWM_FCTRL_FSAFE_SHIFT + (uint16_t)faultNum)); |
740 | } |
741 | else |
742 | { |
743 | /* Use manual fault clearing with safety mode disabled */ |
744 | reg &= ~((uint16_t)1U << (PWM_FCTRL_FSAFE_SHIFT + (uint16_t)faultNum)); |
745 | } |
746 | } |
747 | else |
748 | { |
749 | /* Use automatic fault clearing */ |
750 | reg |= ((uint16_t)1U << (PWM_FCTRL_FAUTO_SHIFT + (uint16_t)faultNum)); |
751 | } |
752 | base->FCTRL = reg; |
753 | |
754 | /* Set the combinational path option */ |
755 | if (faultParams->enableCombinationalPath) |
756 | { |
757 | /* Combinational path from the fault input to the PWM output is available */ |
758 | base->FCTRL2 &= ~((uint16_t)1U << (uint16_t)faultNum); |
759 | } |
760 | else |
761 | { |
762 | /* No combinational path available, only fault filter & latch signal can disable PWM output */ |
763 | base->FCTRL2 |= ((uint16_t)1U << (uint16_t)faultNum); |
764 | } |
765 | |
766 | /* Initially clear both recovery modes */ |
767 | reg = base->FSTS; |
768 | reg &= ~(((uint16_t)1U << (PWM_FSTS_FFULL_SHIFT + (uint16_t)faultNum)) | |
769 | ((uint16_t)1U << (PWM_FSTS_FHALF_SHIFT + (uint16_t)faultNum))); |
770 | /* Setup fault recovery */ |
771 | switch (faultParams->recoverMode) |
772 | { |
773 | case kPWM_NoRecovery: |
774 | break; |
775 | case kPWM_RecoverHalfCycle: |
776 | reg |= ((uint16_t)1U << (PWM_FSTS_FHALF_SHIFT + (uint16_t)faultNum)); |
777 | break; |
778 | case kPWM_RecoverFullCycle: |
779 | reg |= ((uint16_t)1U << (PWM_FSTS_FFULL_SHIFT + (uint16_t)faultNum)); |
780 | break; |
781 | case kPWM_RecoverHalfAndFullCycle: |
782 | reg |= ((uint16_t)1U << (PWM_FSTS_FHALF_SHIFT + (uint16_t)faultNum)); |
783 | reg |= ((uint16_t)1U << (PWM_FSTS_FFULL_SHIFT + (uint16_t)faultNum)); |
784 | break; |
785 | default: |
786 | assert(false); |
787 | break; |
788 | } |
789 | base->FSTS = reg; |
790 | } |
791 | |
792 | /*! |
793 | * brief Fill in the PWM fault config struct with the default settings |
794 | * |
795 | * The default values are: |
796 | * code |
797 | * config->faultClearingMode = kPWM_Automatic; |
798 | * config->faultLevel = false; |
799 | * config->enableCombinationalPath = true; |
800 | * config->recoverMode = kPWM_NoRecovery; |
801 | * endcode |
802 | * param config Pointer to user's PWM fault config structure. |
803 | */ |
804 | void PWM_FaultDefaultConfig(pwm_fault_param_t *config) |
805 | { |
806 | assert(config); |
807 | |
808 | /* Initializes the configure structure to zero. */ |
809 | (void)memset(config, 0, sizeof(*config)); |
810 | |
811 | /* PWM uses automatic fault clear mode */ |
812 | config->faultClearingMode = kPWM_Automatic; |
813 | /* PWM fault level is set to logic 0 */ |
814 | config->faultLevel = false; |
815 | /* Combinational Path from fault input is enabled */ |
816 | config->enableCombinationalPath = true; |
817 | /* PWM output will stay inactive when recovering from a fault */ |
818 | config->recoverMode = kPWM_NoRecovery; |
819 | } |
820 | |
821 | /*! |
822 | * brief Selects the signal to output on a PWM pin when a FORCE_OUT signal is asserted. |
823 | * |
824 | * The user specifies which channel to configure by supplying the submodule number and whether |
825 | * to modify PWM A or PWM B within that submodule. |
826 | * |
827 | * param base PWM peripheral base address |
828 | * param subModule PWM submodule to configure |
829 | * param pwmChannel Channel to configure |
830 | * param mode Signal to output when a FORCE_OUT is triggered |
831 | */ |
832 | void PWM_SetupForceSignal(PWM_Type *base, pwm_submodule_t subModule, pwm_channels_t pwmChannel, pwm_force_signal_t mode) |
833 | |
834 | { |
835 | uint16_t shift; |
836 | uint16_t reg; |
837 | |
838 | /* DTSRCSEL register has 4 bits per submodule; 2 bits for PWM A and 2 bits for PWM B */ |
839 | shift = ((uint16_t)subModule * 4U) + ((uint16_t)pwmChannel * 2U); |
840 | |
841 | /* Setup the signal to be passed upon occurrence of a FORCE_OUT signal */ |
842 | reg = base->DTSRCSEL; |
843 | reg &= ~((uint16_t)0x3U << shift); |
844 | reg |= (uint16_t)((uint16_t)mode << shift); |
845 | base->DTSRCSEL = reg; |
846 | } |
847 | |
848 | /*! |
849 | * brief Enables the selected PWM interrupts |
850 | * |
851 | * param base PWM peripheral base address |
852 | * param subModule PWM submodule to configure |
853 | * param mask The interrupts to enable. This is a logical OR of members of the |
854 | * enumeration ::pwm_interrupt_enable_t |
855 | */ |
856 | void PWM_EnableInterrupts(PWM_Type *base, pwm_submodule_t subModule, uint32_t mask) |
857 | { |
858 | /* Upper 16 bits are for related to the submodule */ |
859 | base->SM[subModule].INTEN |= ((uint16_t)mask & 0xFFFFU); |
860 | /* Fault related interrupts */ |
861 | base->FCTRL |= ((uint16_t)(mask >> 16U) & PWM_FCTRL_FIE_MASK); |
862 | } |
863 | |
864 | /*! |
865 | * brief Disables the selected PWM interrupts |
866 | * |
867 | * param base PWM peripheral base address |
868 | * param subModule PWM submodule to configure |
869 | * param mask The interrupts to enable. This is a logical OR of members of the |
870 | * enumeration ::pwm_interrupt_enable_t |
871 | */ |
872 | void PWM_DisableInterrupts(PWM_Type *base, pwm_submodule_t subModule, uint32_t mask) |
873 | { |
874 | base->SM[subModule].INTEN &= ~((uint16_t)mask & 0xFFFFU); |
875 | base->FCTRL &= ~((uint16_t)(mask >> 16U) & PWM_FCTRL_FIE_MASK); |
876 | } |
877 | |
878 | /*! |
879 | * brief Gets the enabled PWM interrupts |
880 | * |
881 | * param base PWM peripheral base address |
882 | * param subModule PWM submodule to configure |
883 | * |
884 | * return The enabled interrupts. This is the logical OR of members of the |
885 | * enumeration ::pwm_interrupt_enable_t |
886 | */ |
887 | uint32_t PWM_GetEnabledInterrupts(PWM_Type *base, pwm_submodule_t subModule) |
888 | { |
889 | uint32_t enabledInterrupts; |
890 | |
891 | enabledInterrupts = base->SM[subModule].INTEN; |
892 | enabledInterrupts |= (((uint32_t)base->FCTRL & PWM_FCTRL_FIE_MASK) << 16UL); |
893 | return enabledInterrupts; |
894 | } |
895 | |
896 | /*! |
897 | * brief Gets the PWM status flags |
898 | * |
899 | * param base PWM peripheral base address |
900 | * param subModule PWM submodule to configure |
901 | * |
902 | * return The status flags. This is the logical OR of members of the |
903 | * enumeration ::pwm_status_flags_t |
904 | */ |
905 | uint32_t PWM_GetStatusFlags(PWM_Type *base, pwm_submodule_t subModule) |
906 | { |
907 | uint32_t statusFlags; |
908 | |
909 | statusFlags = base->SM[subModule].STS; |
910 | statusFlags |= (((uint32_t)base->FSTS & PWM_FSTS_FFLAG_MASK) << 16UL); |
911 | |
912 | return statusFlags; |
913 | } |
914 | |
915 | /*! |
916 | * brief Clears the PWM status flags |
917 | * |
918 | * param base PWM peripheral base address |
919 | * param subModule PWM submodule to configure |
920 | * param mask The status flags to clear. This is a logical OR of members of the |
921 | * enumeration ::pwm_status_flags_t |
922 | */ |
923 | void PWM_ClearStatusFlags(PWM_Type *base, pwm_submodule_t subModule, uint32_t mask) |
924 | { |
925 | uint16_t reg; |
926 | |
927 | base->SM[subModule].STS = ((uint16_t)mask & 0xFFFFU); |
928 | reg = base->FSTS; |
929 | /* Clear the fault flags and set only the ones we wish to clear as the fault flags are cleared |
930 | * by writing a login one |
931 | */ |
932 | reg &= ~(uint16_t)(PWM_FSTS_FFLAG_MASK); |
933 | reg |= (uint16_t)((mask >> 16U) & PWM_FSTS_FFLAG_MASK); |
934 | base->FSTS = reg; |
935 | } |
936 | |