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 */
26static uint32_t PWM_GetInstance(PWM_Type *base);
27
28/*******************************************************************************
29 * Variables
30 ******************************************************************************/
31/*! @brief Pointers to PWM bases for each instance. */
32static 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. */
36static 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 */
51static inline uint16_t PWM_GetComplementU16(uint16_t value)
52{
53 return (~value + 1U);
54}
55
56static 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
62static 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 */
91status_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 */
191void 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 */
220void 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 */
270status_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 */
486void 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 */
514void 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 */
610void 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 */
692void 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 */
716void 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 */
804void 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 */
832void 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 */
856void 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 */
872void 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 */
887uint32_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 */
905uint32_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 */
923void 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