1 /*
2  * Copyright (c) 2015, Freescale Semiconductor, Inc.
3  * Copyright 2016-2022 NXP
4  * All rights reserved.
5  *
6  * SPDX-License-Identifier: BSD-3-Clause
7  */
8 
9 #include "fsl_ftm.h"
10 
11 /*******************************************************************************
12  * Definitions
13  ******************************************************************************/
14 /* Component ID definition, used by tools. */
15 #ifndef FSL_COMPONENT_ID
16 #define FSL_COMPONENT_ID "platform.drivers.ftm"
17 #endif
18 
19 #if defined(FTM_RSTS)
20 #define FTM_RESETS_ARRAY FTM_RSTS
21 #endif
22 
23 /*******************************************************************************
24  * Prototypes
25  ******************************************************************************/
26 /*!
27  * @brief Sets the FTM register PWM synchronization method
28  *
29  * This function will set the necessary bits for the PWM synchronization mode that
30  * user wishes to use.
31  *
32  * @param base       FTM peripheral base address
33  * @param syncMethod Synchronization methods to use to update buffered registers. This is a logical
34  *                   OR of members of the enumeration ::ftm_pwm_sync_method_t
35  * @param swRstCnt   true:Enable FTM counter synchronization activated by software trigger, avtive when (syncMethod &
36  *                   FTM_SYNC_SWSYNC_MASK) != 0U;
37  *                   false:The software trigger does not activate the FTM counter synchronization.
38  * @param hwRstCnt   true:Enable FTM counter synchronization activated by hardware trigger, avtive when (syncMethod &
39  *                   (FTM_SYNC_TRIG0_MASK | FTM_SYNC_TRIG1_MASK | FTM_SYNC_TRIG2_MASK)) != 0U;
40  *                   false:Hardware trigger does not activate FTM counter synchronization
41  */
42 static void FTM_SetPwmSync(FTM_Type *base, uint32_t syncMethod, bool swRstCnt, bool hwRstCnt);
43 
44 /*!
45  * @brief Sets the reload points used as loading points for register update
46  *
47  * This function will set the necessary bits based on what the user wishes to use as loading
48  * points for FTM register update. When using this it is not required to use PWM synchnronization.
49  *
50  * @param base         FTM peripheral base address
51  * @param reloadPoints FTM reload points. This is a logical OR of members of the
52  *                     enumeration ::ftm_reload_point_t
53  */
54 static void FTM_SetReloadPoints(FTM_Type *base, uint32_t reloadPoints);
55 
56 /*******************************************************************************
57  * Variables
58  ******************************************************************************/
59 /*! @brief Pointers to FTM bases for each instance. */
60 static FTM_Type *const s_ftmBases[] = FTM_BASE_PTRS;
61 
62 #if !(defined(FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL)
63 /*! @brief Pointers to FTM clocks for each instance. */
64 static const clock_ip_name_t s_ftmClocks[] = FTM_CLOCKS;
65 #endif /* FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL */
66 #if defined(FTM_RESETS_ARRAY)
67 /* Reset array */
68 static const reset_ip_name_t s_ftmResets[] = FTM_RESETS_ARRAY;
69 #endif
70 
71 /*******************************************************************************
72  * Code
73  ******************************************************************************/
FTM_GetInstance(FTM_Type * base)74 uint32_t FTM_GetInstance(FTM_Type *base)
75 {
76     uint32_t instance;
77     uint32_t ftmArrayCount = (sizeof(s_ftmBases) / sizeof(s_ftmBases[0]));
78 
79     /* Find the instance index from base address mappings. */
80     for (instance = 0; instance < ftmArrayCount; instance++)
81     {
82         if (s_ftmBases[instance] == base)
83         {
84             break;
85         }
86     }
87 
88     assert(instance < ftmArrayCount);
89 
90     return instance;
91 }
92 
FTM_SetPwmSync(FTM_Type * base,uint32_t syncMethod,bool swRstCnt,bool hwRstCnt)93 static void FTM_SetPwmSync(FTM_Type *base, uint32_t syncMethod, bool swRstCnt, bool hwRstCnt)
94 {
95     uint8_t chnlNumber = 0;
96     uint32_t reg = 0, syncReg = 0;
97 
98     /* The CHANNEL_COUNT macro returns -1 if it cannot match the FTM instance */
99     assert(-1 != FSL_FEATURE_FTM_CHANNEL_COUNTn(base));
100 
101     syncReg = base->SYNC;
102     /* Enable PWM synchronization of output mask register */
103     syncReg |= FTM_SYNC_SYNCHOM_MASK;
104 
105     reg = base->COMBINE;
106     for (chnlNumber = 0; chnlNumber < ((uint8_t)FSL_FEATURE_FTM_CHANNEL_COUNTn(base) / 2U); chnlNumber++)
107     {
108         /* Enable PWM synchronization of registers C(n)V and C(n+1)V */
109         reg |= (1UL << (FTM_COMBINE_SYNCEN0_SHIFT + (FTM_COMBINE_COMBINE1_SHIFT * chnlNumber)));
110     }
111     base->COMBINE = reg;
112 
113     reg = base->SYNCONF;
114 
115     /* Use enhanced PWM synchronization method. Use PWM sync to update register values */
116     reg |= (FTM_SYNCONF_SYNCMODE_MASK | FTM_SYNCONF_CNTINC_MASK | FTM_SYNCONF_INVC_MASK | FTM_SYNCONF_SWOC_MASK);
117 
118     if ((syncMethod & FTM_SYNC_SWSYNC_MASK) != 0U)
119     {
120         /* Enable needed bits for software trigger to update registers with its buffer value */
121         reg |= (FTM_SYNCONF_SWWRBUF_MASK | FTM_SYNCONF_SWINVC_MASK | FTM_SYNCONF_SWSOC_MASK | FTM_SYNCONF_SWOM_MASK);
122         /* Enable software trigger synchronization count. */
123         if (swRstCnt)
124         {
125             reg |= FTM_SYNCONF_SWRSTCNT_MASK;
126         }
127     }
128 
129     if ((syncMethod & (FTM_SYNC_TRIG0_MASK | FTM_SYNC_TRIG1_MASK | FTM_SYNC_TRIG2_MASK)) != 0U)
130     {
131         /* Enable needed bits for hardware trigger to update registers with its buffer value */
132         reg |= (FTM_SYNCONF_HWWRBUF_MASK | FTM_SYNCONF_HWINVC_MASK | FTM_SYNCONF_HWSOC_MASK | FTM_SYNCONF_HWOM_MASK);
133         /* Enable hardware trigger synchronization count */
134         if (hwRstCnt)
135         {
136             reg |= FTM_SYNCONF_HWRSTCNT_MASK;
137         }
138 
139         /* Enable the appropriate hardware trigger that is used for PWM sync */
140         if ((syncMethod & FTM_SYNC_TRIG0_MASK) != 0U)
141         {
142             syncReg |= FTM_SYNC_TRIG0_MASK;
143         }
144         if ((syncMethod & FTM_SYNC_TRIG1_MASK) != 0U)
145         {
146             syncReg |= FTM_SYNC_TRIG1_MASK;
147         }
148         if ((syncMethod & FTM_SYNC_TRIG2_MASK) != 0U)
149         {
150             syncReg |= FTM_SYNC_TRIG2_MASK;
151         }
152     }
153 
154     /* Write back values to the SYNC register */
155     base->SYNC = syncReg;
156 
157     /* Write the PWM synch values to the SYNCONF register */
158     base->SYNCONF = reg;
159 }
160 
FTM_SetReloadPoints(FTM_Type * base,uint32_t reloadPoints)161 static void FTM_SetReloadPoints(FTM_Type *base, uint32_t reloadPoints)
162 {
163     uint32_t chnlNumber = 0;
164     uint32_t reg        = 0;
165     int8_t chnlCount    = FSL_FEATURE_FTM_CHANNEL_COUNTn(base);
166 
167     /* The CHANNEL_COUNT macro returns -1 if it cannot match the FTM instance */
168     assert(-1 != chnlCount);
169 
170     /* Need CNTINC bit to be 1 for CNTIN register to update with its buffer value on reload  */
171     base->SYNCONF |= FTM_SYNCONF_CNTINC_MASK;
172 
173     reg = base->COMBINE;
174     for (chnlNumber = 0; chnlNumber < ((uint32_t)chnlCount / 2U); chnlNumber++)
175     {
176         /* Need SYNCEN bit to be 1 for CnV reg to update with its buffer value on reload  */
177         reg |= (1UL << (FTM_COMBINE_SYNCEN0_SHIFT + (FTM_COMBINE_COMBINE1_SHIFT * chnlNumber)));
178     }
179     base->COMBINE = reg;
180 
181     /* Set the reload points */
182     reg = base->PWMLOAD;
183 
184     /* Enable the selected channel match reload points */
185     reg &= ~((1UL << (uint32_t)chnlCount) - 1U);
186     reg |= (reloadPoints & ((1UL << (uint32_t)chnlCount) - 1U));
187 
188 #if defined(FSL_FEATURE_FTM_HAS_HALFCYCLE_RELOAD) && (FSL_FEATURE_FTM_HAS_HALFCYCLE_RELOAD)
189     /* Enable half cycle match as a reload point */
190     if ((reloadPoints & (uint32_t)kFTM_HalfCycMatch) != 0U)
191     {
192         reg |= FTM_PWMLOAD_HCSEL_MASK;
193     }
194     else
195     {
196         reg &= ~FTM_PWMLOAD_HCSEL_MASK;
197     }
198 #endif /* FSL_FEATURE_FTM_HAS_HALFCYCLE_RELOAD */
199 
200     base->PWMLOAD = reg;
201 
202     /* These reload points are used when counter is in up-down counting mode */
203     reg = base->SYNC;
204     if ((reloadPoints & (uint32_t)kFTM_CntMax) != 0U)
205     {
206         /* Reload when counter turns from up to down */
207         reg |= FTM_SYNC_CNTMAX_MASK;
208     }
209     else
210     {
211         reg &= ~FTM_SYNC_CNTMAX_MASK;
212     }
213 
214     if ((reloadPoints & (uint32_t)kFTM_CntMin) != 0U)
215     {
216         /* Reload when counter turns from down to up */
217         reg |= FTM_SYNC_CNTMIN_MASK;
218     }
219     else
220     {
221         reg &= ~FTM_SYNC_CNTMIN_MASK;
222     }
223     base->SYNC = reg;
224 }
225 
226 /*!
227  * brief Ungates the FTM clock and configures the peripheral for basic operation.
228  *
229  * note This API should be called at the beginning of the application which is using the FTM driver.
230  *      If the FTM instance has only TPM features, please use the TPM driver.
231  *
232  * param base   FTM peripheral base address
233  * param config Pointer to the user configuration structure.
234  *
235  * return kStatus_Success indicates success; Else indicates failure.
236  */
FTM_Init(FTM_Type * base,const ftm_config_t * config)237 status_t FTM_Init(FTM_Type *base, const ftm_config_t *config)
238 {
239     assert(config);
240 #if defined(FSL_FEATURE_FTM_IS_TPM_ONLY_INSTANCE)
241     /* This function does not support the current instance, please use the TPM driver. */
242     assert((FSL_FEATURE_FTM_IS_TPM_ONLY_INSTANCE(base) == 0U));
243 #endif /* FSL_FEATURE_FTM_IS_TPM_ONLY_INSTANCE */
244 
245     uint32_t reg;
246 
247     if ((config->pwmSyncMode & (uint32_t)((uint32_t)FTM_SYNC_TRIG0_MASK | (uint32_t)FTM_SYNC_TRIG1_MASK |
248                                           (uint32_t)FTM_SYNC_TRIG2_MASK | (uint32_t)FTM_SYNC_SWSYNC_MASK)) == 0U)
249     {
250         /* Invalid PWM sync mode */
251         return kStatus_Fail;
252     }
253 
254 #if !(defined(FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL)
255     /* Ungate the FTM clock*/
256     (void)CLOCK_EnableClock(s_ftmClocks[FTM_GetInstance(base)]);
257 #endif /* FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL */
258 
259 #if defined(FTM_RESETS_ARRAY)
260     RESET_ReleasePeripheralReset(s_ftmResets[FTM_GetInstance(base)]);
261 #endif
262 
263 #if (defined(FSL_FEATURE_FTM_HAS_BASIC_FEATURE_ONLY_INSTANCE) && FSL_FEATURE_FTM_HAS_BASIC_FEATURE_ONLY_INSTANCE)
264     if (0 != FSL_FEATURE_FTM_IS_BASIC_FEATURE_ONLY_INSTANCEn(base))
265     {
266         /* Enable FTM mode and disable write protection */
267         base->MODE = FTM_MODE_FTMEN_MASK | FTM_MODE_WPDIS_MASK;
268     }
269     else
270     {
271         /* Configure the fault mode, enable FTM mode and disable write protection */
272         base->MODE = FTM_MODE_FAULTM(config->faultMode) | FTM_MODE_FTMEN_MASK | FTM_MODE_WPDIS_MASK;
273     }
274 #else
275     /* Configure the fault mode, enable FTM mode and disable write protection */
276     base->MODE = FTM_MODE_FAULTM(config->faultMode) | FTM_MODE_FTMEN_MASK | FTM_MODE_WPDIS_MASK;
277 #endif
278 
279     /* Configure the update mechanism for buffered registers */
280     FTM_SetPwmSync(base, config->pwmSyncMode, config->swTriggerResetCount, config->hwTriggerResetCount);
281 
282     /* Setup intermediate register reload points */
283     FTM_SetReloadPoints(base, config->reloadPoints);
284 
285     /* Set the clock prescale factor */
286     base->SC = FTM_SC_PS(config->prescale);
287 
288     /* Setup the counter operation */
289     base->CONF = (FTM_CONF_BDMMODE(config->bdmMode) | FTM_CONF_GTBEEN(config->useGlobalTimeBase));
290 
291     /* Initial state of channel output */
292     base->OUTINIT = config->chnlInitState;
293 
294     /* Channel polarity */
295     base->POL = config->chnlPolarity;
296 
297     /* Set the external trigger sources */
298     base->EXTTRIG = config->extTriggers;
299 #if defined(FSL_FEATURE_FTM_HAS_RELOAD_INITIALIZATION_TRIGGER) && (FSL_FEATURE_FTM_HAS_RELOAD_INITIALIZATION_TRIGGER)
300     if ((config->extTriggers & (uint32_t)kFTM_ReloadInitTrigger) != 0U)
301     {
302         base->CONF |= FTM_CONF_ITRIGR_MASK;
303     }
304     else
305     {
306         base->CONF &= ~FTM_CONF_ITRIGR_MASK;
307     }
308 #endif /* FSL_FEATURE_FTM_HAS_RELOAD_INITIALIZATION_TRIGGER */
309 
310     /* FTM deadtime insertion control */
311     base->DEADTIME = (0u |
312 #if defined(FSL_FEATURE_FTM_HAS_EXTENDED_DEADTIME_VALUE) && (FSL_FEATURE_FTM_HAS_EXTENDED_DEADTIME_VALUE)
313                       /* Has extended deadtime value register) */
314                       FTM_DEADTIME_DTVALEX(config->deadTimeValue >> 6) |
315 #endif /* FSL_FEATURE_FTM_HAS_EXTENDED_DEADTIME_VALUE */
316                       FTM_DEADTIME_DTPS(config->deadTimePrescale) | FTM_DEADTIME_DTVAL(config->deadTimeValue));
317 
318 #if (defined(FSL_FEATURE_FTM_HAS_BASIC_FEATURE_ONLY_INSTANCE) && FSL_FEATURE_FTM_HAS_BASIC_FEATURE_ONLY_INSTANCE)
319     if (0 == FSL_FEATURE_FTM_IS_BASIC_FEATURE_ONLY_INSTANCEn(base))
320     {
321         /* FTM fault filter value */
322         reg = base->FLTCTRL;
323         reg &= ~FTM_FLTCTRL_FFVAL_MASK;
324         reg |= FTM_FLTCTRL_FFVAL(config->faultFilterValue);
325         base->FLTCTRL = reg;
326     }
327 #else
328     /* FTM fault filter value */
329     reg = base->FLTCTRL;
330     reg &= ~FTM_FLTCTRL_FFVAL_MASK;
331     reg |= FTM_FLTCTRL_FFVAL(config->faultFilterValue);
332     base->FLTCTRL = reg;
333 #endif
334 
335     return kStatus_Success;
336 }
337 
338 /*!
339  * brief Gates the FTM clock.
340  *
341  * param base FTM peripheral base address
342  */
FTM_Deinit(FTM_Type * base)343 void FTM_Deinit(FTM_Type *base)
344 {
345     /* Set clock source to none to disable counter */
346     base->SC &= ~(FTM_SC_CLKS_MASK);
347 
348 #if !(defined(FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL)
349     /* Gate the FTM clock */
350     (void)CLOCK_DisableClock(s_ftmClocks[FTM_GetInstance(base)]);
351 #endif /* FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL */
352 }
353 
354 /*!
355  * brief  Fills in the FTM configuration structure with the default settings.
356  *
357  * The default values are:
358  * code
359  *   config->prescale = kFTM_Prescale_Divide_1;
360  *   config->bdmMode = kFTM_BdmMode_0;
361  *   config->pwmSyncMode = kFTM_SoftwareTrigger;
362  *   config->reloadPoints = 0;
363  *   config->faultMode = kFTM_Fault_Disable;
364  *   config->faultFilterValue = 0;
365  *   config->deadTimePrescale = kFTM_Deadtime_Prescale_1;
366  *   config->deadTimeValue =  0;
367  *   config->extTriggers = 0;
368  *   config->chnlInitState = 0;
369  *   config->chnlPolarity = 0;
370  *   config->useGlobalTimeBase = false;
371  *   config->hwTriggerResetCount = false;
372  *   config->swTriggerResetCount = true;
373  * endcode
374  * param config Pointer to the user configuration structure.
375  */
FTM_GetDefaultConfig(ftm_config_t * config)376 void FTM_GetDefaultConfig(ftm_config_t *config)
377 {
378     assert(config != NULL);
379 
380     /* Initializes the configure structure to zero. */
381     (void)memset(config, 0, sizeof(*config));
382 
383     /* Divide FTM clock by 1 */
384     config->prescale = kFTM_Prescale_Divide_1;
385     /* FTM behavior in BDM mode */
386     config->bdmMode = kFTM_BdmMode_0;
387     /* Software trigger will be used to update registers */
388     config->pwmSyncMode = (uint32_t)kFTM_SoftwareTrigger;
389     /* No intermediate register load */
390     config->reloadPoints = 0;
391     /* Fault control disabled for all channels */
392     config->faultMode = kFTM_Fault_Disable;
393     /* Disable the fault filter */
394     config->faultFilterValue = 0;
395     /* Divide the system clock by 1 */
396     config->deadTimePrescale = kFTM_Deadtime_Prescale_1;
397     /* No counts are inserted */
398     config->deadTimeValue = 0;
399     /* No external trigger */
400     config->extTriggers = 0;
401     /* Initialization value is 0 for all channels */
402     config->chnlInitState = 0;
403     /* Active high polarity for all channels */
404     config->chnlPolarity = 0;
405     /* Use internal FTM counter as timebase */
406     config->useGlobalTimeBase = false;
407     /* Set hardware trigger activation counter sync to false */
408     config->hwTriggerResetCount = false;
409     /* Set software trigger activation counter sync to true */
410     config->swTriggerResetCount = true;
411 }
412 
413 /*!
414  * brief Configures the PWM signal parameters.
415  *
416  * Call this function to configure the PWM signal period, mode, duty cycle, and edge. Use this
417  * function to configure all FTM channels that are used to output a PWM signal.
418  *
419  * param base        FTM peripheral base address
420  * param chnlParams  Array of PWM channel parameters to configure the channel(s)
421  * param numOfChnls  Number of channels to configure; This should be the size of the array passed in
422  * param mode        PWM operation mode, options available in enumeration ::ftm_pwm_mode_t
423  * param pwmFreq_Hz  PWM signal frequency in Hz
424  * param srcClock_Hz FTM counter clock in Hz
425  *
426  * return kStatus_Success if the PWM setup was successful
427  *         kStatus_Error on failure
428  */
FTM_SetupPwm(FTM_Type * base,const ftm_chnl_pwm_signal_param_t * chnlParams,uint8_t numOfChnls,ftm_pwm_mode_t mode,uint32_t pwmFreq_Hz,uint32_t srcClock_Hz)429 status_t FTM_SetupPwm(FTM_Type *base,
430                       const ftm_chnl_pwm_signal_param_t *chnlParams,
431                       uint8_t numOfChnls,
432                       ftm_pwm_mode_t mode,
433                       uint32_t pwmFreq_Hz,
434                       uint32_t srcClock_Hz)
435 {
436     assert(NULL != chnlParams);
437 
438     uint32_t mod, reg;
439     uint32_t ftmClock = (srcClock_Hz / (1UL << (base->SC & FTM_SC_PS_MASK)));
440     uint32_t cnv, cnvFirstEdge;
441     uint8_t i;
442 
443     if ((0U == pwmFreq_Hz) || (0U == srcClock_Hz) || (0U == numOfChnls))
444     {
445         return kStatus_InvalidArgument;
446     }
447 
448     if (mode == kFTM_CenterAlignedPwm)
449     {
450         base->SC |= FTM_SC_CPWMS_MASK;
451         mod = ftmClock / (pwmFreq_Hz * 2U);
452     }
453     else
454     {
455         base->SC &= ~FTM_SC_CPWMS_MASK;
456         mod = (ftmClock / pwmFreq_Hz) - 1U;
457     }
458 
459     /* Return an error in case we overflow the registers, probably would require changing
460      * clock source to get the desired frequency */
461     if (mod > 65535U)
462     {
463         return kStatus_OutOfRange;
464     }
465     /* Set the PWM period */
466     base->CNTIN = 0U;
467     base->MOD   = mod;
468 
469     /* Setup each FTM channel */
470     for (i = 0; i < numOfChnls; i++)
471     {
472         /* Return error if requested chnlNumber is greater than the max allowed */
473         if (((uint8_t)chnlParams->chnlNumber >= (uint8_t)FSL_FEATURE_FTM_CHANNEL_COUNTn(base)) ||
474             (-1 == (int8_t)FSL_FEATURE_FTM_CHANNEL_COUNTn(base)))
475         {
476             return kStatus_InvalidArgument;
477         }
478         /* Return error if requested dutycycle is greater than the max allowed */
479         if (chnlParams->dutyCyclePercent > 100U)
480         {
481             return kStatus_OutOfRange;
482         }
483 
484         if (chnlParams->dutyCyclePercent == 100U)
485         {
486             /* For 100% duty cycle */
487             cnv = mod + 1U;
488         }
489         else
490         {
491             cnv = (mod * chnlParams->dutyCyclePercent) / 100U;
492         }
493 
494         if ((mode == kFTM_EdgeAlignedPwm) || (mode == kFTM_CenterAlignedPwm))
495         {
496             /* Clear the current mode and edge level bits */
497             reg = base->CONTROLS[chnlParams->chnlNumber].CnSC;
498             reg &= ~(FTM_CnSC_MSA_MASK | FTM_CnSC_MSB_MASK | FTM_CnSC_ELSA_MASK | FTM_CnSC_ELSB_MASK);
499 
500             /* Setup the active level */
501             reg |= (uint32_t)chnlParams->level << FTM_CnSC_ELSA_SHIFT;
502 
503             /* Edge-aligned mode needs MSB to be 1, don't care for Center-aligned mode */
504             reg |= FTM_CnSC_MSB(1U);
505 
506             /* Update the mode and edge level */
507             base->CONTROLS[chnlParams->chnlNumber].CnSC = reg;
508 
509             base->CONTROLS[chnlParams->chnlNumber].CnV = cnv;
510 #if defined(FSL_FEATURE_FTM_HAS_ENABLE_PWM_OUTPUT) && (FSL_FEATURE_FTM_HAS_ENABLE_PWM_OUTPUT)
511             /* Set to output mode */
512             FTM_SetPwmOutputEnable(base, chnlParams->chnlNumber, true);
513 #endif
514         }
515         else
516         {
517             /* This check is added for combined mode as the channel number should be the pair number */
518             if (((uint32_t)chnlParams->chnlNumber) >= ((uint32_t)FSL_FEATURE_FTM_CHANNEL_COUNTn(base) / 2U))
519             {
520                 return kStatus_InvalidArgument;
521             }
522 
523             if (mode == kFTM_EdgeAlignedCombinedPwm)
524             {
525                 cnvFirstEdge = 0;
526             }
527             else if (mode == kFTM_CenterAlignedCombinedPwm)
528             {
529                 cnvFirstEdge = (mod > cnv) ? ((mod - cnv) / 2U) : 0U;
530             }
531             else
532             {
533                 /* Return error if requested value is greater than the max allowed */
534                 if ((chnlParams->firstEdgeDelayPercent + chnlParams->dutyCyclePercent) > 100U)
535                 {
536                     return kStatus_OutOfRange;
537                 }
538                 /* Configure delay of the first edge */
539                 cnvFirstEdge = (mod * chnlParams->firstEdgeDelayPercent) / 100U;
540             }
541 
542             /* Clear the current mode and edge level bits for channel n */
543             reg = base->CONTROLS[((uint32_t)chnlParams->chnlNumber) * 2U].CnSC;
544             reg &= ~(FTM_CnSC_MSA_MASK | FTM_CnSC_MSB_MASK | FTM_CnSC_ELSA_MASK | FTM_CnSC_ELSB_MASK);
545 
546             /* Setup the active level for channel n */
547             reg |= (uint32_t)chnlParams->level << FTM_CnSC_ELSA_SHIFT;
548 
549             /* Update the mode and edge level for channel n */
550             base->CONTROLS[((uint32_t)chnlParams->chnlNumber) * 2U].CnSC = reg;
551 
552             /* Clear the current mode and edge level bits for channel n + 1 */
553             reg = base->CONTROLS[(((uint32_t)chnlParams->chnlNumber) * 2U) + 1U].CnSC;
554             reg &= ~(FTM_CnSC_MSA_MASK | FTM_CnSC_MSB_MASK | FTM_CnSC_ELSA_MASK | FTM_CnSC_ELSB_MASK);
555 
556             /* Setup the active level for channel n + 1 */
557             reg |= (uint32_t)chnlParams->level << FTM_CnSC_ELSA_SHIFT;
558 
559             /* Update the mode and edge level for channel n + 1*/
560             base->CONTROLS[(((uint32_t)chnlParams->chnlNumber) * 2U) + 1U].CnSC = reg;
561 
562             /* Set the combine bit for the channel pair */
563             base->COMBINE |=
564                 (1UL << (FTM_COMBINE_COMBINE0_SHIFT + (FTM_COMBINE_COMBINE1_SHIFT * (uint32_t)chnlParams->chnlNumber)));
565 
566             /* Set the channel pair values */
567             base->CONTROLS[((uint32_t)chnlParams->chnlNumber) * 2U].CnV        = cnvFirstEdge;
568             base->CONTROLS[(((uint32_t)chnlParams->chnlNumber) * 2U) + 1U].CnV = cnvFirstEdge + cnv;
569 
570 #if defined(FSL_FEATURE_FTM_HAS_ENABLE_PWM_OUTPUT) && (FSL_FEATURE_FTM_HAS_ENABLE_PWM_OUTPUT)
571             /* Set to output mode */
572             FTM_SetPwmOutputEnable(base, (ftm_chnl_t)(uint8_t)((uint8_t)chnlParams->chnlNumber * 2U), true);
573             FTM_SetPwmOutputEnable(base, (ftm_chnl_t)(uint8_t)((uint8_t)chnlParams->chnlNumber * 2U + 1U), true);
574 #endif /* FSL_FEATURE_FTM_HAS_ENABLE_PWM_OUTPUT */
575 
576             /* Enable/Disable complementary output on the channel pair */
577             FTM_SetComplementaryEnable(base, chnlParams->chnlNumber, chnlParams->enableComplementary);
578             /* Enable/Disable Deadtime insertion on the channel pair */
579             FTM_SetDeadTimeEnable(base, chnlParams->chnlNumber, chnlParams->enableDeadtime);
580         }
581         chnlParams++;
582     }
583 
584     return kStatus_Success;
585 }
586 
587 /*!
588  * brief Updates the duty cycle of an active PWM signal.
589  *
590  * param base              FTM peripheral base address
591  * param chnlNumber        The channel/channel pair number. In combined mode, this represents
592  *                          the channel pair number
593  * param currentPwmMode    The current PWM mode set during PWM setup
594  * param dutyCyclePercent  New PWM pulse width; The value should be between 0 to 100
595  *                          0=inactive signal(0% duty cycle)...
596  *                          100=active signal (100% duty cycle)
597  * return kStatus_Success if the PWM update was successful
598  *         kStatus_Error on failure
599  */
FTM_UpdatePwmDutycycle(FTM_Type * base,ftm_chnl_t chnlNumber,ftm_pwm_mode_t currentPwmMode,uint8_t dutyCyclePercent)600 status_t FTM_UpdatePwmDutycycle(FTM_Type *base,
601                                 ftm_chnl_t chnlNumber,
602                                 ftm_pwm_mode_t currentPwmMode,
603                                 uint8_t dutyCyclePercent)
604 {
605     uint32_t cnv, cnvFirstEdge = 0, mod;
606 
607     /* Return error if requested chnlNumber is greater than the max allowed */
608     if (((uint8_t)chnlNumber >= (uint8_t)FSL_FEATURE_FTM_CHANNEL_COUNTn(base)) ||
609         (-1 == (int8_t)FSL_FEATURE_FTM_CHANNEL_COUNTn(base)))
610     {
611         return kStatus_InvalidArgument;
612     }
613 
614     mod = base->MOD;
615 
616     if (dutyCyclePercent > 100U)
617     {
618         return kStatus_OutOfRange;
619     }
620     if (dutyCyclePercent == 100U)
621     {
622         /* For 100% duty cycle */
623         cnv = mod + 1U;
624     }
625     else
626     {
627         cnv = (mod * dutyCyclePercent) / 100U;
628     }
629 
630     if ((currentPwmMode == kFTM_EdgeAlignedPwm) || (currentPwmMode == kFTM_CenterAlignedPwm))
631     {
632         base->CONTROLS[chnlNumber].CnV = cnv;
633     }
634     else
635     {
636         /* This check is added for combined mode as the channel number should be the pair number */
637         if ((uint32_t)chnlNumber >= ((uint32_t)FSL_FEATURE_FTM_CHANNEL_COUNTn(base) / 2U))
638         {
639             return kStatus_InvalidArgument;
640         }
641 
642         if (currentPwmMode == kFTM_CenterAlignedCombinedPwm)
643         {
644             cnvFirstEdge = (mod > cnv) ? ((mod - cnv) / 2U) : 0U;
645         }
646         else
647         {
648             cnvFirstEdge = base->CONTROLS[((uint32_t)chnlNumber) * 2U].CnV;
649             if (((cnvFirstEdge != 0U) && (cnv == (mod + 1U))) || (((cnvFirstEdge + cnv) > mod) && (cnv < mod)))
650             {
651                 /* Return error if firstEdgeDelayPercent + dutyCyclePercent > 100 */
652                 return kStatus_OutOfRange;
653             }
654         }
655         base->CONTROLS[((uint32_t)chnlNumber * 2U)].CnV      = cnvFirstEdge;
656         base->CONTROLS[((uint32_t)chnlNumber * 2U) + 1U].CnV = cnvFirstEdge + cnv;
657     }
658     return kStatus_Success;
659 }
660 
661 /*!
662  * brief Updates the edge level selection for a channel.
663  *
664  * param base       FTM peripheral base address
665  * param chnlNumber The channel number
666  * param level      The level to be set to the ELSnB:ELSnA field; Valid values are 00, 01, 10, 11.
667  *                   See the Kinetis SoC reference manual for details about this field.
668  */
FTM_UpdateChnlEdgeLevelSelect(FTM_Type * base,ftm_chnl_t chnlNumber,uint8_t level)669 void FTM_UpdateChnlEdgeLevelSelect(FTM_Type *base, ftm_chnl_t chnlNumber, uint8_t level)
670 {
671     uint32_t reg = base->CONTROLS[chnlNumber].CnSC;
672 
673     /* Clear the field and write the new level value */
674     reg &= ~(FTM_CnSC_ELSA_MASK | FTM_CnSC_ELSB_MASK);
675     reg |= ((uint32_t)level << FTM_CnSC_ELSA_SHIFT) & (FTM_CnSC_ELSA_MASK | FTM_CnSC_ELSB_MASK);
676 
677     base->CONTROLS[chnlNumber].CnSC = reg;
678 }
679 
680 /*!
681  * brief Configures the PWM mode parameters.
682  *
683  * Call this function to configure the PWM signal mode, duty cycle in ticks, and edge. Use this
684  * function to configure all FTM channels that are used to output a PWM signal.
685  * Please note that: This API is similar with FTM_SetupPwm() API, but will not set the timer period,
686  *                   and this API will set channel match value in timer ticks, not period percent.
687  *
688  * param base        FTM peripheral base address
689  * param chnlParams  Array of PWM channel parameters to configure the channel(s)
690  * param numOfChnls  Number of channels to configure; This should be the size of the array passed in
691  * param mode        PWM operation mode, options available in enumeration ::ftm_pwm_mode_t
692  *
693  * return kStatus_Success if the PWM setup was successful
694  *         kStatus_Error on failure
695  */
FTM_SetupPwmMode(FTM_Type * base,const ftm_chnl_pwm_config_param_t * chnlParams,uint8_t numOfChnls,ftm_pwm_mode_t mode)696 status_t FTM_SetupPwmMode(FTM_Type *base,
697                           const ftm_chnl_pwm_config_param_t *chnlParams,
698                           uint8_t numOfChnls,
699                           ftm_pwm_mode_t mode)
700 {
701     assert(chnlParams != NULL);
702     assert(numOfChnls != 0U);
703     /* The CHANNEL_COUNT macro returns -1 if it cannot match the FTM instance */
704     assert(-1 != FSL_FEATURE_FTM_CHANNEL_COUNTn(base));
705 
706     uint32_t reg;
707     uint32_t mod, cnvFirstEdge;
708     uint8_t i;
709 
710     switch (mode)
711     {
712         case kFTM_EdgeAlignedPwm:
713         case kFTM_EdgeAlignedCombinedPwm:
714         case kFTM_CenterAlignedCombinedPwm:
715         case kFTM_AsymmetricalCombinedPwm:
716             base->SC &= ~FTM_SC_CPWMS_MASK;
717             break;
718         case kFTM_CenterAlignedPwm:
719             base->SC |= FTM_SC_CPWMS_MASK;
720             break;
721         default:
722             assert(false);
723             break;
724     }
725 
726     /* Get percent PWM period */
727     mod = base->MOD;
728     /* Setup each FTM channel */
729     for (i = 0; i < numOfChnls; i++)
730     {
731         if ((mode == kFTM_EdgeAlignedPwm) || (mode == kFTM_CenterAlignedPwm))
732         {
733             /* Clear the current mode and edge level bits */
734             reg = base->CONTROLS[chnlParams->chnlNumber].CnSC;
735             reg &= ~(FTM_CnSC_MSA_MASK | FTM_CnSC_MSB_MASK | FTM_CnSC_ELSA_MASK | FTM_CnSC_ELSB_MASK);
736 
737             /* Setup the active level */
738             reg |= (uint32_t)chnlParams->level << FTM_CnSC_ELSA_SHIFT;
739 
740             /* Edge-aligned mode needs MSB to be 1, don't care for Center-aligned mode */
741             reg |= FTM_CnSC_MSB(1U);
742 
743             /* Update the mode and edge level */
744             base->CONTROLS[chnlParams->chnlNumber].CnSC = reg;
745 
746             base->CONTROLS[chnlParams->chnlNumber].CnV = chnlParams->dutyValue;
747 #if defined(FSL_FEATURE_FTM_HAS_ENABLE_PWM_OUTPUT) && (FSL_FEATURE_FTM_HAS_ENABLE_PWM_OUTPUT)
748             /* Set to output mode */
749             FTM_SetPwmOutputEnable(base, chnlParams->chnlNumber, true);
750 #endif
751         }
752         else
753         {
754             /* This check is added for combined mode as the channel number should be the pair number */
755             if (((uint32_t)chnlParams->chnlNumber) >= (((uint32_t)FSL_FEATURE_FTM_CHANNEL_COUNTn(base)) / 2U))
756             {
757                 return kStatus_Fail;
758             }
759 
760             if (mode == kFTM_EdgeAlignedCombinedPwm)
761             {
762                 cnvFirstEdge = 0;
763             }
764             else if (mode == kFTM_CenterAlignedCombinedPwm)
765             {
766                 cnvFirstEdge = (mod - chnlParams->dutyValue) / 2U;
767             }
768             else
769             {
770                 /* Return error if requested value is greater than the mod */
771                 if (chnlParams->firstEdgeValue > mod)
772                 {
773                     return kStatus_Fail;
774                 }
775                 cnvFirstEdge = chnlParams->firstEdgeValue;
776             }
777 
778             /* Re-configure first edge when 0% duty cycle */
779             if (chnlParams->dutyValue == 0U)
780             {
781                 /* Signal stays low */
782                 cnvFirstEdge = 0;
783             }
784 
785             /* Clear the current mode and edge level bits for channel n */
786             reg = base->CONTROLS[((uint32_t)chnlParams->chnlNumber) * 2U].CnSC;
787             reg &= ~(FTM_CnSC_MSA_MASK | FTM_CnSC_MSB_MASK | FTM_CnSC_ELSA_MASK | FTM_CnSC_ELSB_MASK);
788 
789             /* Setup the active level for channel n */
790             reg |= (uint32_t)chnlParams->level << FTM_CnSC_ELSA_SHIFT;
791 
792             /* Update the mode and edge level for channel n */
793             base->CONTROLS[((uint32_t)chnlParams->chnlNumber) * 2U].CnSC = reg;
794 
795             /* Clear the current mode and edge level bits for channel n + 1 */
796             reg = base->CONTROLS[(((uint32_t)chnlParams->chnlNumber) * 2U) + 1U].CnSC;
797             reg &= ~(FTM_CnSC_MSA_MASK | FTM_CnSC_MSB_MASK | FTM_CnSC_ELSA_MASK | FTM_CnSC_ELSB_MASK);
798 
799             /* Setup the active level for channel n + 1 */
800             reg |= (uint32_t)chnlParams->level << FTM_CnSC_ELSA_SHIFT;
801 
802             /* Update the mode and edge level for channel n + 1*/
803             base->CONTROLS[(((uint32_t)chnlParams->chnlNumber) * 2U) + 1U].CnSC = reg;
804 
805             /* Set the combine bit for the channel pair */
806             base->COMBINE |=
807                 (1UL << (FTM_COMBINE_COMBINE0_SHIFT + (FTM_COMBINE_COMBINE1_SHIFT * (uint32_t)chnlParams->chnlNumber)));
808 
809             /* Set the channel pair values */
810             base->CONTROLS[((uint32_t)chnlParams->chnlNumber) * 2U].CnV        = cnvFirstEdge;
811             base->CONTROLS[(((uint32_t)chnlParams->chnlNumber) * 2U) + 1U].CnV = cnvFirstEdge + chnlParams->dutyValue;
812 
813 #if defined(FSL_FEATURE_FTM_HAS_ENABLE_PWM_OUTPUT) && (FSL_FEATURE_FTM_HAS_ENABLE_PWM_OUTPUT)
814             /* Set to output mode */
815             FTM_SetPwmOutputEnable(base, (ftm_chnl_t)(uint8_t)((uint8_t)chnlParams->chnlNumber * 2U), true);
816             FTM_SetPwmOutputEnable(base, (ftm_chnl_t)(uint8_t)((uint8_t)chnlParams->chnlNumber * 2U + 1U), true);
817 #endif /* FSL_FEATURE_FTM_HAS_ENABLE_PWM_OUTPUT */
818 
819             /* Enable/Disable complementary output on the channel pair */
820             FTM_SetComplementaryEnable(base, chnlParams->chnlNumber, chnlParams->enableComplementary);
821             /* Enable/Disable Deadtime insertion on the channel pair */
822             FTM_SetDeadTimeEnable(base, chnlParams->chnlNumber, chnlParams->enableDeadtime);
823         }
824         chnlParams++;
825     }
826 
827     return kStatus_Success;
828 }
829 
830 /*!
831  * brief Enables capturing an input signal on the channel using the function parameters.
832  *
833  * When the edge specified in the captureMode argument occurs on the channel, the FTM counter is
834  * captured into the CnV register. The user has to read the CnV register separately to get this
835  * value. The filter function is disabled if the filterVal argument passed in is 0. The filter
836  * function is available only for channels 0, 1, 2, 3.
837  *
838  * param base        FTM peripheral base address
839  * param chnlNumber  The channel number
840  * param captureMode Specifies which edge to capture
841  * param filterValue Filter value, specify 0 to disable filter. Available only for channels 0-3.
842  */
FTM_SetupInputCapture(FTM_Type * base,ftm_chnl_t chnlNumber,ftm_input_capture_edge_t captureMode,uint32_t filterValue)843 void FTM_SetupInputCapture(FTM_Type *base,
844                            ftm_chnl_t chnlNumber,
845                            ftm_input_capture_edge_t captureMode,
846                            uint32_t filterValue)
847 {
848     uint32_t reg;
849 
850     /* Clear the combine bit for the channel pair */
851     base->COMBINE &=
852         ~(1UL << (FTM_COMBINE_COMBINE0_SHIFT + (FTM_COMBINE_COMBINE1_SHIFT * ((uint32_t)chnlNumber >> 1))));
853     /* Clear the dual edge capture mode because it's it's higher priority */
854     base->COMBINE &=
855         ~(1UL << (FTM_COMBINE_DECAPEN0_SHIFT + (FTM_COMBINE_COMBINE1_SHIFT * ((uint32_t)chnlNumber >> 1))));
856 #if !(defined(FSL_FEATURE_FTM_HAS_NO_QDCTRL) && FSL_FEATURE_FTM_HAS_NO_QDCTRL)
857     /* Clear the quadrature decoder mode beacause it's higher priority */
858     base->QDCTRL &= ~FTM_QDCTRL_QUADEN_MASK;
859 #endif
860 
861     reg = base->CONTROLS[chnlNumber].CnSC;
862     reg &= ~(FTM_CnSC_MSA_MASK | FTM_CnSC_MSB_MASK | FTM_CnSC_ELSA_MASK | FTM_CnSC_ELSB_MASK);
863     reg |= (uint32_t)captureMode;
864 
865     /* Set the requested input capture mode */
866     base->CONTROLS[chnlNumber].CnSC = reg;
867     /* Input filter available only for channels 0, 1, 2, 3 */
868     if (chnlNumber < kFTM_Chnl_4)
869     {
870         reg = base->FILTER;
871         reg &= ~((uint32_t)FTM_FILTER_CH0FVAL_MASK << (FTM_FILTER_CH1FVAL_SHIFT * (uint32_t)chnlNumber));
872         reg |= (filterValue << (FTM_FILTER_CH1FVAL_SHIFT * (uint32_t)chnlNumber));
873         base->FILTER = reg;
874     }
875 #if defined(FSL_FEATURE_FTM_HAS_ENABLE_PWM_OUTPUT) && (FSL_FEATURE_FTM_HAS_ENABLE_PWM_OUTPUT)
876     /* Set to input mode */
877     FTM_SetPwmOutputEnable(base, chnlNumber, false);
878 #endif
879 }
880 
881 /*!
882  * brief Configures the FTM to generate timed pulses.
883  *
884  * When the FTM counter matches the value of compareVal argument (this is written into CnV reg),
885  * the channel output is changed based on what is specified in the compareMode argument.
886  *
887  * param base         FTM peripheral base address
888  * param chnlNumber   The channel number
889  * param compareMode  Action to take on the channel output when the compare condition is met
890  * param compareValue Value to be programmed in the CnV register.
891  */
FTM_SetupOutputCompare(FTM_Type * base,ftm_chnl_t chnlNumber,ftm_output_compare_mode_t compareMode,uint32_t compareValue)892 void FTM_SetupOutputCompare(FTM_Type *base,
893                             ftm_chnl_t chnlNumber,
894                             ftm_output_compare_mode_t compareMode,
895                             uint32_t compareValue)
896 {
897     uint32_t reg;
898 
899     /* Clear the combine bit for the channel pair */
900     base->COMBINE &=
901         ~(1UL << (FTM_COMBINE_COMBINE0_SHIFT + (FTM_COMBINE_COMBINE1_SHIFT * ((uint32_t)chnlNumber >> 1))));
902     /* Clear the dual edge capture mode because it's it's higher priority */
903     base->COMBINE &=
904         ~(1UL << (FTM_COMBINE_DECAPEN0_SHIFT + (FTM_COMBINE_COMBINE1_SHIFT * ((uint32_t)chnlNumber >> 1))));
905 #if !(defined(FSL_FEATURE_FTM_HAS_NO_QDCTRL) && FSL_FEATURE_FTM_HAS_NO_QDCTRL)
906     /* Clear the quadrature decoder mode beacause it's higher priority */
907     base->QDCTRL &= ~FTM_QDCTRL_QUADEN_MASK;
908 #endif
909 
910     reg = base->CONTROLS[chnlNumber].CnSC;
911     reg &= ~(FTM_CnSC_MSA_MASK | FTM_CnSC_MSB_MASK | FTM_CnSC_ELSA_MASK | FTM_CnSC_ELSB_MASK);
912     reg |= (uint32_t)compareMode;
913     /* Setup the channel output behaviour when a match occurs with the compare value */
914     base->CONTROLS[chnlNumber].CnSC = reg;
915 
916     /* Set output on match to the requested level */
917     base->CONTROLS[chnlNumber].CnV = compareValue;
918 
919 #if defined(FSL_FEATURE_FTM_HAS_ENABLE_PWM_OUTPUT) && (FSL_FEATURE_FTM_HAS_ENABLE_PWM_OUTPUT)
920     /* Set to output mode */
921     FTM_SetPwmOutputEnable(base, chnlNumber, true);
922 #endif
923 }
924 
925 /*!
926  * brief Configures the dual edge capture mode of the FTM.
927  *
928  * This function sets up the dual edge capture mode on a channel pair. The capture edge for the
929  * channel pair and the capture mode (one-shot or continuous) is specified in the parameter
930  * argument. The filter function is disabled if the filterVal argument passed is zero. The filter
931  * function is available only on channels 0 and 2. The user has to read the channel CnV registers
932  * separately to get the capture values.
933  *
934  * param base           FTM peripheral base address
935  * param chnlPairNumber The FTM channel pair number; options are 0, 1, 2, 3
936  * param edgeParam      Sets up the dual edge capture function
937  * param filterValue    Filter value, specify 0 to disable filter. Available only for channel pair 0 and 1.
938  */
FTM_SetupDualEdgeCapture(FTM_Type * base,ftm_chnl_t chnlPairNumber,const ftm_dual_edge_capture_param_t * edgeParam,uint32_t filterValue)939 void FTM_SetupDualEdgeCapture(FTM_Type *base,
940                               ftm_chnl_t chnlPairNumber,
941                               const ftm_dual_edge_capture_param_t *edgeParam,
942                               uint32_t filterValue)
943 {
944     assert(edgeParam);
945 
946     uint32_t reg;
947 
948     reg = base->COMBINE;
949     /* Clear the combine bit for the channel pair */
950     reg &= ~(1UL << (FTM_COMBINE_COMBINE0_SHIFT + (FTM_COMBINE_COMBINE1_SHIFT * (uint32_t)chnlPairNumber)));
951     /* Enable the DECAPEN bit */
952     reg |= (1UL << (FTM_COMBINE_DECAPEN0_SHIFT + (FTM_COMBINE_COMBINE1_SHIFT * (uint32_t)chnlPairNumber)));
953     reg |= (1UL << (FTM_COMBINE_DECAP0_SHIFT + (FTM_COMBINE_COMBINE1_SHIFT * (uint32_t)chnlPairNumber)));
954     base->COMBINE = reg;
955 
956     /* Setup the edge detection from channel n and n + 1 */
957     reg = base->CONTROLS[((uint32_t)chnlPairNumber) * 2U].CnSC;
958     reg &= ~(FTM_CnSC_MSA_MASK | FTM_CnSC_MSB_MASK | FTM_CnSC_ELSA_MASK | FTM_CnSC_ELSB_MASK);
959     reg |= ((uint32_t)edgeParam->mode | (uint32_t)edgeParam->currChanEdgeMode);
960     base->CONTROLS[((uint32_t)chnlPairNumber) * 2U].CnSC = reg;
961 
962     reg = base->CONTROLS[(((uint32_t)chnlPairNumber) * 2U) + 1U].CnSC;
963     reg &= ~(FTM_CnSC_MSA_MASK | FTM_CnSC_MSB_MASK | FTM_CnSC_ELSA_MASK | FTM_CnSC_ELSB_MASK);
964     reg |= ((uint32_t)edgeParam->mode | (uint32_t)edgeParam->nextChanEdgeMode);
965     base->CONTROLS[(((uint32_t)chnlPairNumber) * 2U) + 1U].CnSC = reg;
966 
967     /* Input filter available only for channels 0, 1, 2, 3 */
968     if (chnlPairNumber < kFTM_Chnl_4)
969     {
970         reg = base->FILTER;
971         reg &= ~((uint32_t)FTM_FILTER_CH0FVAL_MASK << (FTM_FILTER_CH1FVAL_SHIFT * (uint32_t)chnlPairNumber));
972         reg |= (filterValue << (FTM_FILTER_CH1FVAL_SHIFT * (uint32_t)chnlPairNumber));
973         base->FILTER = reg;
974     }
975 
976 #if defined(FSL_FEATURE_FTM_HAS_ENABLE_PWM_OUTPUT) && (FSL_FEATURE_FTM_HAS_ENABLE_PWM_OUTPUT)
977     /* Set to input mode */
978     FTM_SetPwmOutputEnable(base, chnlPairNumber, false);
979 #endif
980 }
981 
982 /*!
983  * brief Configures the parameters and activates the quadrature decoder mode.
984  *
985  * param base         FTM peripheral base address
986  * param phaseAParams Phase A configuration parameters
987  * param phaseBParams Phase B configuration parameters
988  * param quadMode     Selects encoding mode used in quadrature decoder mode
989  */
FTM_SetupQuadDecode(FTM_Type * base,const ftm_phase_params_t * phaseAParams,const ftm_phase_params_t * phaseBParams,ftm_quad_decode_mode_t quadMode)990 void FTM_SetupQuadDecode(FTM_Type *base,
991                          const ftm_phase_params_t *phaseAParams,
992                          const ftm_phase_params_t *phaseBParams,
993                          ftm_quad_decode_mode_t quadMode)
994 {
995     assert(phaseAParams != NULL);
996     assert(phaseBParams != NULL);
997 
998     uint32_t reg;
999 
1000     /* Set Phase A filter value if phase filter is enabled */
1001     if (phaseAParams->enablePhaseFilter)
1002     {
1003         reg = base->FILTER;
1004         reg &= ~(FTM_FILTER_CH0FVAL_MASK);
1005         reg |= FTM_FILTER_CH0FVAL(phaseAParams->phaseFilterVal);
1006         base->FILTER = reg;
1007     }
1008 
1009     /* Set Phase B filter value if phase filter is enabled */
1010     if (phaseBParams->enablePhaseFilter)
1011     {
1012         reg = base->FILTER;
1013         reg &= ~(FTM_FILTER_CH1FVAL_MASK);
1014         reg |= FTM_FILTER_CH1FVAL(phaseBParams->phaseFilterVal);
1015         base->FILTER = reg;
1016     }
1017 #if !(defined(FSL_FEATURE_FTM_HAS_NO_QDCTRL) && FSL_FEATURE_FTM_HAS_NO_QDCTRL)
1018     /* Set Quadrature decode properties */
1019     reg = base->QDCTRL;
1020     reg &= ~(FTM_QDCTRL_QUADMODE_MASK | FTM_QDCTRL_PHAFLTREN_MASK | FTM_QDCTRL_PHBFLTREN_MASK | FTM_QDCTRL_PHAPOL_MASK |
1021              FTM_QDCTRL_PHBPOL_MASK);
1022     reg |= (FTM_QDCTRL_QUADMODE(quadMode) | FTM_QDCTRL_PHAFLTREN(phaseAParams->enablePhaseFilter) |
1023             FTM_QDCTRL_PHBFLTREN(phaseBParams->enablePhaseFilter) | FTM_QDCTRL_PHAPOL(phaseAParams->phasePolarity) |
1024             FTM_QDCTRL_PHBPOL(phaseBParams->phasePolarity));
1025     base->QDCTRL = reg;
1026     /* Enable Quad decode */
1027     base->QDCTRL |= FTM_QDCTRL_QUADEN_MASK;
1028 #endif
1029 }
1030 
1031 /*!
1032  * brief Sets up the working of the FTM fault inputs protection.
1033  *
1034  * FTM can have up to 4 fault inputs. This function sets up fault parameters, fault level, and input filter.
1035  *
1036  * param base        FTM peripheral base address
1037  * param faultNumber FTM fault to configure.
1038  * param faultParams Parameters passed in to set up the fault input
1039  */
FTM_SetupFaultInput(FTM_Type * base,ftm_fault_input_t faultNumber,const ftm_fault_param_t * faultParams)1040 void FTM_SetupFaultInput(FTM_Type *base, ftm_fault_input_t faultNumber, const ftm_fault_param_t *faultParams)
1041 {
1042     assert(faultParams != NULL);
1043     /* Fault input is not supported if the instance has only basic feature.*/
1044 #if (defined(FSL_FEATURE_FTM_HAS_BASIC_FEATURE_ONLY_INSTANCE) && FSL_FEATURE_FTM_HAS_BASIC_FEATURE_ONLY_INSTANCE)
1045     assert(0 == FSL_FEATURE_FTM_IS_BASIC_FEATURE_ONLY_INSTANCEn(base));
1046 #endif
1047 
1048     if (faultParams->useFaultFilter)
1049     {
1050         /* Enable the fault filter */
1051         base->FLTCTRL |= ((uint32_t)FTM_FLTCTRL_FFLTR0EN_MASK << (FTM_FLTCTRL_FFLTR0EN_SHIFT + (uint32_t)faultNumber));
1052     }
1053     else
1054     {
1055         /* Disable the fault filter */
1056         base->FLTCTRL &= ~((uint32_t)FTM_FLTCTRL_FFLTR0EN_MASK << (FTM_FLTCTRL_FFLTR0EN_SHIFT + (uint32_t)faultNumber));
1057     }
1058 
1059     if (faultParams->faultLevel)
1060     {
1061         /* Active low polarity for the fault input pin */
1062         base->FLTPOL |= (1UL << (uint32_t)faultNumber);
1063     }
1064     else
1065     {
1066         /* Active high polarity for the fault input pin */
1067         base->FLTPOL &= ~(1UL << (uint32_t)faultNumber);
1068     }
1069 
1070     if (faultParams->enableFaultInput)
1071     {
1072         /* Enable the fault input */
1073         base->FLTCTRL |= ((uint32_t)FTM_FLTCTRL_FAULT0EN_MASK << (uint32_t)faultNumber);
1074     }
1075     else
1076     {
1077         /* Disable the fault input */
1078         base->FLTCTRL &= ~((uint32_t)FTM_FLTCTRL_FAULT0EN_MASK << (uint32_t)faultNumber);
1079     }
1080 }
1081 
1082 /*!
1083  * brief Enables the selected FTM interrupts.
1084  *
1085  * param base FTM peripheral base address
1086  * param mask The interrupts to enable. This is a logical OR of members of the
1087  *             enumeration ::ftm_interrupt_enable_t
1088  */
FTM_EnableInterrupts(FTM_Type * base,uint32_t mask)1089 void FTM_EnableInterrupts(FTM_Type *base, uint32_t mask)
1090 {
1091     uint32_t chnlInts  = (mask & 0xFFU);
1092     uint8_t chnlNumber = 0;
1093 
1094     /* Enable the timer overflow interrupt */
1095     if ((mask & (uint32_t)kFTM_TimeOverflowInterruptEnable) != 0U)
1096     {
1097         base->SC |= FTM_SC_TOIE_MASK;
1098     }
1099 
1100     /* Fault input is not supported if the instance has only basic feature.*/
1101 #if (defined(FSL_FEATURE_FTM_HAS_BASIC_FEATURE_ONLY_INSTANCE) && FSL_FEATURE_FTM_HAS_BASIC_FEATURE_ONLY_INSTANCE)
1102     if (0 == FSL_FEATURE_FTM_IS_BASIC_FEATURE_ONLY_INSTANCEn(base))
1103     {
1104         /* Enable the fault interrupt */
1105         if ((mask & (uint32_t)kFTM_FaultInterruptEnable) != 0U)
1106         {
1107             base->MODE |= FTM_MODE_FAULTIE_MASK;
1108         }
1109     }
1110 #else
1111     /* Enable the fault interrupt */
1112     if ((mask & (uint32_t)kFTM_FaultInterruptEnable) != 0U)
1113     {
1114         base->MODE |= FTM_MODE_FAULTIE_MASK;
1115     }
1116 #endif
1117 
1118 #if defined(FSL_FEATURE_FTM_HAS_RELOAD_INTERRUPT) && (FSL_FEATURE_FTM_HAS_RELOAD_INTERRUPT)
1119     /* Enable the reload interrupt available only on certain SoC's */
1120     if ((mask & (uint32_t)kFTM_ReloadInterruptEnable) != 0U)
1121     {
1122         base->SC |= FTM_SC_RIE_MASK;
1123     }
1124 #endif
1125 
1126     /* Enable the channel interrupts */
1127     while (chnlInts != 0U)
1128     {
1129         if ((chnlInts & 0x1U) != 0U)
1130         {
1131             base->CONTROLS[chnlNumber].CnSC |= FTM_CnSC_CHIE_MASK;
1132         }
1133         chnlNumber++;
1134         chnlInts = chnlInts >> 1U;
1135     }
1136 }
1137 
1138 /*!
1139  * brief Disables the selected FTM interrupts.
1140  *
1141  * param base FTM peripheral base address
1142  * param mask The interrupts to enable. This is a logical OR of members of the
1143  *             enumeration ::ftm_interrupt_enable_t
1144  */
FTM_DisableInterrupts(FTM_Type * base,uint32_t mask)1145 void FTM_DisableInterrupts(FTM_Type *base, uint32_t mask)
1146 {
1147     uint32_t chnlInts  = (mask & 0xFFU);
1148     uint8_t chnlNumber = 0;
1149 
1150     /* Disable the timer overflow interrupt */
1151     if ((mask & (uint32_t)kFTM_TimeOverflowInterruptEnable) != 0U)
1152     {
1153         base->SC &= ~FTM_SC_TOIE_MASK;
1154     }
1155     /* Fault input is not supported if the instance has only basic feature.*/
1156 #if (defined(FSL_FEATURE_FTM_HAS_BASIC_FEATURE_ONLY_INSTANCE) && FSL_FEATURE_FTM_HAS_BASIC_FEATURE_ONLY_INSTANCE)
1157     if (0 == FSL_FEATURE_FTM_IS_BASIC_FEATURE_ONLY_INSTANCEn(base))
1158     {
1159         /* Disable the fault interrupt */
1160         if ((mask & (uint32_t)kFTM_FaultInterruptEnable) != 0U)
1161         {
1162             base->MODE &= ~FTM_MODE_FAULTIE_MASK;
1163         }
1164     }
1165 #else
1166     /* Disable the fault interrupt */
1167     if ((mask & (uint32_t)kFTM_FaultInterruptEnable) != 0U)
1168     {
1169         base->MODE &= ~FTM_MODE_FAULTIE_MASK;
1170     }
1171 #endif
1172 
1173 #if defined(FSL_FEATURE_FTM_HAS_RELOAD_INTERRUPT) && (FSL_FEATURE_FTM_HAS_RELOAD_INTERRUPT)
1174     /* Disable the reload interrupt available only on certain SoC's */
1175     if ((mask & (uint32_t)kFTM_ReloadInterruptEnable) != 0U)
1176     {
1177         base->SC &= ~FTM_SC_RIE_MASK;
1178     }
1179 #endif
1180 
1181     /* Disable the channel interrupts */
1182     while (chnlInts != 0U)
1183     {
1184         if ((chnlInts & 0x01U) != 0U)
1185         {
1186             base->CONTROLS[chnlNumber].CnSC &= ~FTM_CnSC_CHIE_MASK;
1187         }
1188         chnlNumber++;
1189         chnlInts = chnlInts >> 1U;
1190     }
1191 }
1192 
1193 /*!
1194  * brief Gets the enabled FTM interrupts.
1195  *
1196  * param base FTM peripheral base address
1197  *
1198  * return The enabled interrupts. This is the logical OR of members of the
1199  *         enumeration ::ftm_interrupt_enable_t
1200  */
FTM_GetEnabledInterrupts(FTM_Type * base)1201 uint32_t FTM_GetEnabledInterrupts(FTM_Type *base)
1202 {
1203     uint32_t enabledInterrupts = 0;
1204     int8_t chnlCount           = FSL_FEATURE_FTM_CHANNEL_COUNTn(base);
1205 
1206     /* The CHANNEL_COUNT macro returns -1 if it cannot match the FTM instance */
1207     assert(chnlCount != -1);
1208 
1209     /* Check if timer overflow interrupt is enabled */
1210     if ((base->SC & FTM_SC_TOIE_MASK) != 0U)
1211     {
1212         enabledInterrupts |= (uint32_t)kFTM_TimeOverflowInterruptEnable;
1213     }
1214     /* Fault input is not supported if the instance has only basic feature.*/
1215 #if (defined(FSL_FEATURE_FTM_HAS_BASIC_FEATURE_ONLY_INSTANCE) && FSL_FEATURE_FTM_HAS_BASIC_FEATURE_ONLY_INSTANCE)
1216     if (0 == FSL_FEATURE_FTM_IS_BASIC_FEATURE_ONLY_INSTANCEn(base))
1217     {
1218         /* Check if fault interrupt is enabled */
1219         if ((base->MODE & FTM_MODE_FAULTIE_MASK) != 0U)
1220         {
1221             enabledInterrupts |= (uint32_t)kFTM_FaultInterruptEnable;
1222         }
1223     }
1224 #else
1225     /* Check if fault interrupt is enabled */
1226     if ((base->MODE & FTM_MODE_FAULTIE_MASK) != 0U)
1227     {
1228         enabledInterrupts |= (uint32_t)kFTM_FaultInterruptEnable;
1229     }
1230 #endif
1231 
1232 #if defined(FSL_FEATURE_FTM_HAS_RELOAD_INTERRUPT) && (FSL_FEATURE_FTM_HAS_RELOAD_INTERRUPT)
1233     /* Check if the reload interrupt is enabled */
1234     if ((base->SC & FTM_SC_RIE_MASK) != 0U)
1235     {
1236         enabledInterrupts |= (uint32_t)kFTM_ReloadInterruptEnable;
1237     }
1238 #endif
1239 
1240     /* Check if the channel interrupts are enabled */
1241     while (chnlCount > 0)
1242     {
1243         chnlCount--;
1244         if ((base->CONTROLS[chnlCount].CnSC & FTM_CnSC_CHIE_MASK) != 0x00U)
1245         {
1246             enabledInterrupts |= (1UL << (uint32_t)chnlCount);
1247         }
1248     }
1249 
1250     return enabledInterrupts;
1251 }
1252 
1253 /*!
1254  * brief Gets the FTM status flags.
1255  *
1256  * param base FTM peripheral base address
1257  *
1258  * return The status flags. This is the logical OR of members of the
1259  *         enumeration ::ftm_status_flags_t
1260  */
FTM_GetStatusFlags(FTM_Type * base)1261 uint32_t FTM_GetStatusFlags(FTM_Type *base)
1262 {
1263     uint32_t statusFlags = 0;
1264 
1265     /* Check the timer flag */
1266     if ((base->SC & FTM_SC_TOF_MASK) != 0U)
1267     {
1268         statusFlags |= (uint32_t)kFTM_TimeOverflowFlag;
1269     }
1270     /* Fault input is not supported if the instance has only basic feature.*/
1271 #if (defined(FSL_FEATURE_FTM_HAS_BASIC_FEATURE_ONLY_INSTANCE) && FSL_FEATURE_FTM_HAS_BASIC_FEATURE_ONLY_INSTANCE)
1272     if (0 == FSL_FEATURE_FTM_IS_BASIC_FEATURE_ONLY_INSTANCEn(base))
1273     {
1274         /* Check fault flag */
1275         if ((base->FMS & FTM_FMS_FAULTF_MASK) != 0U)
1276         {
1277             statusFlags |= (uint32_t)kFTM_FaultFlag;
1278         }
1279     }
1280 #else
1281     /* Check fault flag */
1282     if ((base->FMS & FTM_FMS_FAULTF_MASK) != 0U)
1283     {
1284         statusFlags |= (uint32_t)kFTM_FaultFlag;
1285     }
1286 #endif
1287     /* Check channel trigger flag */
1288     if ((base->EXTTRIG & FTM_EXTTRIG_TRIGF_MASK) != 0U)
1289     {
1290         statusFlags |= (uint32_t)kFTM_ChnlTriggerFlag;
1291     }
1292 #if defined(FSL_FEATURE_FTM_HAS_RELOAD_INTERRUPT) && (FSL_FEATURE_FTM_HAS_RELOAD_INTERRUPT)
1293     /* Check reload flag */
1294     if ((base->SC & FTM_SC_RF_MASK) != 0U)
1295     {
1296         statusFlags |= (uint32_t)kFTM_ReloadFlag;
1297     }
1298 #endif
1299 
1300     /* Lower 8 bits contain the channel status flags */
1301     statusFlags |= (base->STATUS & 0xFFU);
1302 
1303     return statusFlags;
1304 }
1305 
1306 /*!
1307  * brief Clears the FTM status flags.
1308  *
1309  * param base FTM peripheral base address
1310  * param mask The status flags to clear. This is a logical OR of members of the
1311  *             enumeration ::ftm_status_flags_t
1312  */
FTM_ClearStatusFlags(FTM_Type * base,uint32_t mask)1313 void FTM_ClearStatusFlags(FTM_Type *base, uint32_t mask)
1314 {
1315     /* Clear the timer overflow flag by writing a 0 to the bit while it is set */
1316     if ((mask & (uint32_t)kFTM_TimeOverflowFlag) != 0U)
1317     {
1318         base->SC &= ~FTM_SC_TOF_MASK;
1319     }
1320     /* Fault input is not supported if the instance has only basic feature.*/
1321 #if (defined(FSL_FEATURE_FTM_HAS_BASIC_FEATURE_ONLY_INSTANCE) && FSL_FEATURE_FTM_HAS_BASIC_FEATURE_ONLY_INSTANCE)
1322     if (0 == FSL_FEATURE_FTM_IS_BASIC_FEATURE_ONLY_INSTANCEn(base))
1323     {
1324         /* Clear fault flag by writing a 0 to the bit while it is set */
1325         if ((mask & (uint32_t)kFTM_FaultFlag) != 0U)
1326         {
1327             base->FMS &= ~FTM_FMS_FAULTF_MASK;
1328         }
1329     }
1330 #else
1331     /* Clear fault flag by writing a 0 to the bit while it is set */
1332     if ((mask & (uint32_t)kFTM_FaultFlag) != 0U)
1333     {
1334         base->FMS &= ~FTM_FMS_FAULTF_MASK;
1335     }
1336 #endif
1337     /* Clear channel trigger flag */
1338     if ((mask & (uint32_t)kFTM_ChnlTriggerFlag) != 0U)
1339     {
1340         base->EXTTRIG &= ~FTM_EXTTRIG_TRIGF_MASK;
1341     }
1342 
1343 #if defined(FSL_FEATURE_FTM_HAS_RELOAD_INTERRUPT) && (FSL_FEATURE_FTM_HAS_RELOAD_INTERRUPT)
1344     /* Check reload flag by writing a 0 to the bit while it is set */
1345     if ((mask & (uint32_t)kFTM_ReloadFlag) != 0U)
1346     {
1347         base->SC &= ~FTM_SC_RF_MASK;
1348     }
1349 #endif
1350     /* Clear the channel status flags by writing a 0 to the bit */
1351     base->STATUS &= ~(mask & 0xFFU);
1352 }
1353