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_tpm.h"
10
11 /*******************************************************************************
12 * Definitions
13 ******************************************************************************/
14
15 /* Component ID definition, used by tools. */
16 #ifndef FSL_COMPONENT_ID
17 #define FSL_COMPONENT_ID "platform.drivers.tpm"
18 #endif
19
20 #define TPM_COMBINE_SHIFT (8U)
21
22 /*******************************************************************************
23 * Prototypes
24 ******************************************************************************/
25 /*!
26 * @brief Gets the instance from the base address
27 *
28 * @param base TPM peripheral base address
29 *
30 * @return The TPM instance
31 */
32 static uint32_t TPM_GetInstance(TPM_Type *base);
33
34 /*******************************************************************************
35 * Variables
36 ******************************************************************************/
37 /*! @brief Pointers to TPM bases for each instance. */
38 static TPM_Type *const s_tpmBases[] = TPM_BASE_PTRS;
39
40 #if !(defined(FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL)
41 /*! @brief Pointers to TPM clocks for each instance. */
42 static const clock_ip_name_t s_tpmClocks[] = TPM_CLOCKS;
43 #endif /* FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL */
44
45 /*******************************************************************************
46 * Code
47 ******************************************************************************/
TPM_GetInstance(TPM_Type * base)48 static uint32_t TPM_GetInstance(TPM_Type *base)
49 {
50 uint32_t instance;
51 uint32_t tpmArrayCount = (sizeof(s_tpmBases) / sizeof(s_tpmBases[0]));
52
53 /* Find the instance index from base address mappings. */
54 for (instance = 0; instance < tpmArrayCount; instance++)
55 {
56 if (s_tpmBases[instance] == base)
57 {
58 break;
59 }
60 }
61
62 assert(instance < tpmArrayCount);
63
64 return instance;
65 }
66
67 /*!
68 * brief Ungates the TPM clock and configures the peripheral for basic operation.
69 *
70 * note This API should be called at the beginning of the application using the TPM driver.
71 *
72 * param base TPM peripheral base address
73 * param config Pointer to user's TPM config structure.
74 */
TPM_Init(TPM_Type * base,const tpm_config_t * config)75 void TPM_Init(TPM_Type *base, const tpm_config_t *config)
76 {
77 assert(NULL != config);
78
79 #if !(defined(FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL)
80 /* Enable the module clock */
81 (void)CLOCK_EnableClock(s_tpmClocks[TPM_GetInstance(base)]);
82 #endif /* FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL */
83
84 #if defined(FSL_FEATURE_TPM_HAS_GLOBAL) && FSL_FEATURE_TPM_HAS_GLOBAL
85 /* TPM reset is available on certain SoC's */
86 TPM_Reset(base);
87 #endif
88
89 /* Set the clock prescale factor */
90 base->SC = TPM_SC_PS(config->prescale);
91 #if !(defined(FSL_FEATURE_TPM_HAS_NO_CONF) && FSL_FEATURE_TPM_HAS_NO_CONF)
92 /* Setup the counter operation */
93 base->CONF = TPM_CONF_DOZEEN(config->enableDoze) | TPM_CONF_GTBEEN(config->useGlobalTimeBase) |
94 TPM_CONF_CROT(config->enableReloadOnTrigger) | TPM_CONF_CSOT(config->enableStartOnTrigger) |
95 TPM_CONF_CSOO(config->enableStopOnOverflow) |
96 #if defined(FSL_FEATURE_TPM_HAS_PAUSE_COUNTER_ON_TRIGGER) && FSL_FEATURE_TPM_HAS_PAUSE_COUNTER_ON_TRIGGER
97 TPM_CONF_CPOT(config->enablePauseOnTrigger) |
98 #endif
99 #if defined(FSL_FEATURE_TPM_HAS_EXTERNAL_TRIGGER_SELECTION) && FSL_FEATURE_TPM_HAS_EXTERNAL_TRIGGER_SELECTION
100 TPM_CONF_TRGSRC(config->triggerSource) |
101 #endif
102 TPM_CONF_TRGSEL(config->triggerSelect);
103 if (true == config->enableDebugMode)
104 {
105 base->CONF |= TPM_CONF_DBGMODE_MASK;
106 }
107 else
108 {
109 base->CONF &= ~TPM_CONF_DBGMODE_MASK;
110 }
111 #endif
112 }
113
114 /*!
115 * brief Stops the counter and gates the TPM clock
116 *
117 * param base TPM peripheral base address
118 */
TPM_Deinit(TPM_Type * base)119 void TPM_Deinit(TPM_Type *base)
120 {
121 #if defined(FSL_FEATURE_TPM_HAS_SC_CLKS) && FSL_FEATURE_TPM_HAS_SC_CLKS
122 /* Stop the counter */
123 base->SC &= ~TPM_SC_CLKS_MASK;
124 #else
125 /* Stop the counter */
126 base->SC &= ~TPM_SC_CMOD_MASK;
127 #endif
128 #if !(defined(FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL)
129 /* Gate the TPM clock */
130 (void)CLOCK_DisableClock(s_tpmClocks[TPM_GetInstance(base)]);
131 #endif /* FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL */
132 }
133
134 /*!
135 * brief Fill in the TPM config struct with the default settings
136 *
137 * The default values are:
138 * code
139 * config->prescale = kTPM_Prescale_Divide_1;
140 * config->useGlobalTimeBase = false;
141 * config->dozeEnable = false;
142 * config->dbgMode = false;
143 * config->enableReloadOnTrigger = false;
144 * config->enableStopOnOverflow = false;
145 * config->enableStartOnTrigger = false;
146 *#if FSL_FEATURE_TPM_HAS_PAUSE_COUNTER_ON_TRIGGER
147 * config->enablePauseOnTrigger = false;
148 *#endif
149 * config->triggerSelect = kTPM_Trigger_Select_0;
150 *#if FSL_FEATURE_TPM_HAS_EXTERNAL_TRIGGER_SELECTION
151 * config->triggerSource = kTPM_TriggerSource_External;
152 *#endif
153 * endcode
154 * param config Pointer to user's TPM config structure.
155 */
TPM_GetDefaultConfig(tpm_config_t * config)156 void TPM_GetDefaultConfig(tpm_config_t *config)
157 {
158 assert(NULL != config);
159
160 /* Initializes the configure structure to zero. */
161 (void)memset(config, 0, sizeof(*config));
162
163 /* TPM clock divide by 1 */
164 config->prescale = kTPM_Prescale_Divide_1;
165 #if !(defined(FSL_FEATURE_TPM_HAS_NO_CONF) && FSL_FEATURE_TPM_HAS_NO_CONF)
166 /* Use internal TPM counter as timebase */
167 config->useGlobalTimeBase = false;
168 /* TPM counter continues in doze mode */
169 config->enableDoze = false;
170 /* TPM counter pauses when in debug mode */
171 config->enableDebugMode = false;
172 /* TPM counter will not be reloaded on input trigger */
173 config->enableReloadOnTrigger = false;
174 /* TPM counter continues running after overflow */
175 config->enableStopOnOverflow = false;
176 /* TPM counter starts immediately once it is enabled */
177 config->enableStartOnTrigger = false;
178 #if defined(FSL_FEATURE_TPM_HAS_PAUSE_COUNTER_ON_TRIGGER) && FSL_FEATURE_TPM_HAS_PAUSE_COUNTER_ON_TRIGGER
179 config->enablePauseOnTrigger = false;
180 #endif
181 /* Choose trigger select 0 as input trigger for controlling counter operation */
182 config->triggerSelect = kTPM_Trigger_Select_0;
183 #if defined(FSL_FEATURE_TPM_HAS_EXTERNAL_TRIGGER_SELECTION) && FSL_FEATURE_TPM_HAS_EXTERNAL_TRIGGER_SELECTION
184 /* Choose external trigger source to control counter operation */
185 config->triggerSource = kTPM_TriggerSource_External;
186 #endif
187 #endif
188 }
189
190 /*!
191 * brief Configures the PWM signal parameters
192 *
193 * User calls this function to configure the PWM signals period, mode, dutycycle and edge. Use this
194 * function to configure all the TPM channels that will be used to output a PWM signal
195 *
196 * param base TPM peripheral base address
197 * param chnlParams Array of PWM channel parameters to configure the channel(s)
198 * param numOfChnls Number of channels to configure, this should be the size of the array passed in
199 * param mode PWM operation mode, options available in enumeration ::tpm_pwm_mode_t
200 * param pwmFreq_Hz PWM signal frequency in Hz
201 * param srcClock_Hz TPM counter clock in Hz
202 *
203 * return kStatus_Success if the PWM setup was successful,
204 * kStatus_Error on failure
205 */
TPM_SetupPwm(TPM_Type * base,const tpm_chnl_pwm_signal_param_t * chnlParams,uint8_t numOfChnls,tpm_pwm_mode_t mode,uint32_t pwmFreq_Hz,uint32_t srcClock_Hz)206 status_t TPM_SetupPwm(TPM_Type *base,
207 const tpm_chnl_pwm_signal_param_t *chnlParams,
208 uint8_t numOfChnls,
209 tpm_pwm_mode_t mode,
210 uint32_t pwmFreq_Hz,
211 uint32_t srcClock_Hz)
212 {
213 assert(NULL != chnlParams);
214 assert(0U != pwmFreq_Hz);
215 assert(0U != numOfChnls);
216 assert(0U != srcClock_Hz);
217 #if defined(FSL_FEATURE_TPM_HAS_COMBINE) && FSL_FEATURE_TPM_HAS_COMBINE
218 if (mode == kTPM_CombinedPwm)
219 {
220 assert(0 != FSL_FEATURE_TPM_COMBINE_HAS_EFFECTn(base));
221 }
222 #endif
223
224 uint32_t mod = 0;
225 uint32_t u32flag = 1;
226 uint32_t tpmClock = (srcClock_Hz / (u32flag << (base->SC & TPM_SC_PS_MASK)));
227 uint16_t cnv;
228 uint8_t i;
229
230 #if defined(FSL_FEATURE_TPM_HAS_QDCTRL) && FSL_FEATURE_TPM_HAS_QDCTRL
231 /* The TPM's QDCTRL register required to be effective */
232 if (0 != FSL_FEATURE_TPM_QDCTRL_HAS_EFFECTn(base))
233 {
234 /* Clear quadrature Decoder mode because in quadrature Decoder mode PWM doesn't operate*/
235 base->QDCTRL &= ~TPM_QDCTRL_QUADEN_MASK;
236 }
237 #endif
238
239 switch (mode)
240 {
241 case kTPM_EdgeAlignedPwm:
242 #if defined(FSL_FEATURE_TPM_HAS_COMBINE) && FSL_FEATURE_TPM_HAS_COMBINE
243 case kTPM_CombinedPwm:
244 #endif
245 base->SC &= ~TPM_SC_CPWMS_MASK;
246 mod = (tpmClock / pwmFreq_Hz) - 1u;
247 break;
248 case kTPM_CenterAlignedPwm:
249 base->SC |= TPM_SC_CPWMS_MASK;
250 mod = tpmClock / (pwmFreq_Hz * 2u);
251 break;
252 default:
253 /* All the cease have been listed above, the default case should not be reached. */
254 assert(false);
255 break;
256 }
257
258 /* Return an error in case we overflow the registers, probably would require changing
259 * clock source to get the desired frequency */
260 if (mod > 65535U)
261 {
262 return kStatus_Fail;
263 }
264 /* Set the PWM period */
265 base->MOD = mod;
266
267 /* Setup each TPM channel */
268 for (i = 0; i < numOfChnls; i++)
269 {
270 /* Return error if requested dutycycle is greater than the max allowed */
271 if (chnlParams->dutyCyclePercent > 100U)
272 {
273 return kStatus_Fail;
274 }
275 #if defined(FSL_FEATURE_TPM_HAS_COMBINE) && FSL_FEATURE_TPM_HAS_COMBINE
276 if (mode == kTPM_CombinedPwm)
277 {
278 uint16_t cnvFirstEdge;
279
280 /* This check is added for combined mode as the channel number should be the pair number */
281 if ((int8_t)(chnlParams->chnlNumber) >= (FSL_FEATURE_TPM_CHANNEL_COUNTn(base) / 2))
282 {
283 return kStatus_Fail;
284 }
285
286 /* Return error if requested value is greater than the max allowed */
287 if (chnlParams->firstEdgeDelayPercent > 100U)
288 {
289 return kStatus_Fail;
290 }
291 /* Configure delay of the first edge */
292 if (chnlParams->firstEdgeDelayPercent == 0U)
293 {
294 /* No delay for the first edge */
295 cnvFirstEdge = 0;
296 }
297 else
298 {
299 cnvFirstEdge = (uint16_t)((mod * chnlParams->firstEdgeDelayPercent) / 100U);
300 }
301 /* Configure dutycycle */
302 if (chnlParams->dutyCyclePercent == 0U)
303 {
304 /* Signal stays low */
305 cnv = 0;
306 cnvFirstEdge = 0;
307 }
308 else
309 {
310 cnv = (uint16_t)((mod * chnlParams->dutyCyclePercent) / 100U);
311 /* For 100% duty cycle */
312 if (cnv >= mod)
313 {
314 cnv = (uint16_t)(mod + 1u);
315 }
316 }
317
318 /* Set the combine bit for the channel pair */
319 base->COMBINE |=
320 (u32flag << (TPM_COMBINE_COMBINE0_SHIFT + (TPM_COMBINE_SHIFT * (uint32_t)chnlParams->chnlNumber)));
321
322 /* When switching mode, disable channel n first */
323 base->CONTROLS[(uint32_t)chnlParams->chnlNumber * 2U].CnSC &=
324 ~(TPM_CnSC_MSA_MASK | TPM_CnSC_MSB_MASK | TPM_CnSC_ELSA_MASK | TPM_CnSC_ELSB_MASK);
325
326 /* Wait till mode change to disable channel is acknowledged */
327 while (0U != (base->CONTROLS[(uint32_t)chnlParams->chnlNumber * 2U].CnSC &
328 (TPM_CnSC_MSA_MASK | TPM_CnSC_MSB_MASK | TPM_CnSC_ELSA_MASK | TPM_CnSC_ELSB_MASK)))
329 {
330 }
331
332 /* Set the requested PWM mode for channel n, PWM output requires mode select to be set to 2 */
333 base->CONTROLS[(uint32_t)chnlParams->chnlNumber * 2U].CnSC |=
334 (((uint32_t)chnlParams->level << TPM_CnSC_ELSA_SHIFT) | (2U << TPM_CnSC_MSA_SHIFT));
335
336 /* Wait till mode change is acknowledged */
337 while (0U == (base->CONTROLS[(uint32_t)chnlParams->chnlNumber * 2U].CnSC &
338 (TPM_CnSC_MSA_MASK | TPM_CnSC_MSB_MASK | TPM_CnSC_ELSA_MASK | TPM_CnSC_ELSB_MASK)))
339 {
340 }
341 /* Set the channel pair values */
342 base->CONTROLS[(uint16_t)chnlParams->chnlNumber * 2U].CnV = cnvFirstEdge;
343
344 /* When switching mode, disable channel n + 1 first */
345 base->CONTROLS[((uint32_t)chnlParams->chnlNumber * 2U) + 1U].CnSC &=
346 ~(TPM_CnSC_MSA_MASK | TPM_CnSC_MSB_MASK | TPM_CnSC_ELSA_MASK | TPM_CnSC_ELSB_MASK);
347
348 /* Wait till mode change to disable channel is acknowledged */
349 while (0U != (base->CONTROLS[((uint32_t)chnlParams->chnlNumber * 2U) + 1U].CnSC &
350 (TPM_CnSC_MSA_MASK | TPM_CnSC_MSB_MASK | TPM_CnSC_ELSA_MASK | TPM_CnSC_ELSB_MASK)))
351 {
352 }
353
354 /* Set the requested PWM mode for channel n + 1, PWM output requires mode select to be set to 2 */
355 base->CONTROLS[((uint32_t)chnlParams->chnlNumber * 2U) + 1U].CnSC |=
356 (((uint32_t)chnlParams->level << TPM_CnSC_ELSA_SHIFT) | (2U << TPM_CnSC_MSA_SHIFT));
357
358 /* Wait till mode change is acknowledged */
359 while (0U == (base->CONTROLS[((uint32_t)chnlParams->chnlNumber * 2U) + 1U].CnSC &
360 (TPM_CnSC_MSA_MASK | TPM_CnSC_MSB_MASK | TPM_CnSC_ELSA_MASK | TPM_CnSC_ELSB_MASK)))
361 {
362 }
363 /* Set the channel pair values */
364 base->CONTROLS[((uint16_t)chnlParams->chnlNumber * 2u) + 1u].CnV = (uint32_t)cnvFirstEdge + (uint32_t)cnv;
365 }
366 else
367 {
368 #endif
369 if (chnlParams->dutyCyclePercent == 0U)
370 {
371 /* Signal stays low */
372 cnv = 0;
373 }
374 else
375 {
376 cnv = (uint16_t)((mod * chnlParams->dutyCyclePercent) / 100U);
377 /* For 100% duty cycle */
378 if (cnv >= mod)
379 {
380 cnv = (uint16_t)(mod + 1U);
381 }
382 }
383 /* Fix ERROR050050 When TPM is configured in EPWM mode as PS = 0, the compare event is missed on
384 the first reload/overflow after writing 1 to the CnV register and causes an incorrect duty output.*/
385 #if (defined(FSL_FEATURE_TPM_HAS_ERRATA_050050) && FSL_FEATURE_TPM_HAS_ERRATA_050050)
386 assert(
387 !(mode == kTPM_EdgeAlignedPwm && cnv == 1U && (base->SC & TPM_SC_PS_MASK) == kTPM_Prescale_Divide_1));
388 #endif
389 /* When switching mode, disable channel first */
390 base->CONTROLS[chnlParams->chnlNumber].CnSC &=
391 ~(TPM_CnSC_MSA_MASK | TPM_CnSC_MSB_MASK | TPM_CnSC_ELSA_MASK | TPM_CnSC_ELSB_MASK);
392
393 /* Wait till mode change to disable channel is acknowledged */
394 while (0U != (base->CONTROLS[chnlParams->chnlNumber].CnSC &
395 (TPM_CnSC_MSA_MASK | TPM_CnSC_MSB_MASK | TPM_CnSC_ELSA_MASK | TPM_CnSC_ELSB_MASK)))
396 {
397 }
398
399 /* Set the requested PWM mode, PWM output requires mode select to be set to 2 */
400 base->CONTROLS[chnlParams->chnlNumber].CnSC |=
401 (((uint32_t)chnlParams->level << TPM_CnSC_ELSA_SHIFT) | (2U << TPM_CnSC_MSA_SHIFT));
402
403 /* Wait till mode change is acknowledged */
404 while (0U == (base->CONTROLS[chnlParams->chnlNumber].CnSC &
405 (TPM_CnSC_MSA_MASK | TPM_CnSC_MSB_MASK | TPM_CnSC_ELSA_MASK | TPM_CnSC_ELSB_MASK)))
406 {
407 }
408 base->CONTROLS[chnlParams->chnlNumber].CnV = cnv;
409 #if defined(FSL_FEATURE_TPM_HAS_COMBINE) && FSL_FEATURE_TPM_HAS_COMBINE
410 }
411 #endif
412
413 chnlParams++;
414 }
415
416 return kStatus_Success;
417 }
418
419 /*!
420 * brief Update the duty cycle of an active PWM signal
421 *
422 * param base TPM peripheral base address
423 * param chnlNumber The channel number. In combined mode, this represents
424 * the channel pair number
425 * param currentPwmMode The current PWM mode set during PWM setup
426 * param dutyCyclePercent New PWM pulse width, value should be between 0 to 100
427 * 0=inactive signal(0% duty cycle)...
428 * 100=active signal (100% duty cycle)
429 */
TPM_UpdatePwmDutycycle(TPM_Type * base,tpm_chnl_t chnlNumber,tpm_pwm_mode_t currentPwmMode,uint8_t dutyCyclePercent)430 void TPM_UpdatePwmDutycycle(TPM_Type *base,
431 tpm_chnl_t chnlNumber,
432 tpm_pwm_mode_t currentPwmMode,
433 uint8_t dutyCyclePercent)
434 {
435 assert((int8_t)chnlNumber < FSL_FEATURE_TPM_CHANNEL_COUNTn(base));
436 assert((uint8_t)chnlNumber < sizeof(base->CONTROLS) / sizeof(base->CONTROLS[0]));
437
438 #if defined(FSL_FEATURE_TPM_HAS_COMBINE) && FSL_FEATURE_TPM_HAS_COMBINE
439 if (currentPwmMode == kTPM_CombinedPwm)
440 {
441 assert(0 != FSL_FEATURE_TPM_COMBINE_HAS_EFFECTn(base));
442 }
443 #endif
444
445 uint16_t cnv, mod;
446
447 mod = (uint16_t)(base->MOD);
448 #if defined(FSL_FEATURE_TPM_HAS_COMBINE) && FSL_FEATURE_TPM_HAS_COMBINE
449 if (currentPwmMode == kTPM_CombinedPwm)
450 {
451 uint16_t cnvFirstEdge;
452
453 /* This check is added for combined mode as the channel number should be the pair number */
454 if ((int8_t)chnlNumber >= ((int8_t)FSL_FEATURE_TPM_CHANNEL_COUNTn(base) / 2))
455 {
456 return;
457 }
458 cnv = (uint16_t)((mod * dutyCyclePercent) / 100U);
459 cnvFirstEdge = (uint16_t)(base->CONTROLS[((uint8_t)chnlNumber * 2u) & 0x3U].CnV);
460 /* For 100% duty cycle */
461 if (cnv >= mod)
462 {
463 cnv = mod + 1u;
464 }
465 base->CONTROLS[(((uint8_t)chnlNumber * 2u) + 1u) & 0x3U].CnV = (uint32_t)cnvFirstEdge + (uint32_t)cnv;
466 }
467 else
468 {
469 #endif
470 cnv = (uint16_t)((mod * dutyCyclePercent) / 100U);
471 /* For 100% duty cycle */
472 if (cnv >= mod)
473 {
474 cnv = mod + 1u;
475 }
476 /* Fix ERROR050050 */
477 #if (defined(FSL_FEATURE_TPM_HAS_ERRATA_050050) && FSL_FEATURE_TPM_HAS_ERRATA_050050)
478 assert(!(currentPwmMode == kTPM_EdgeAlignedPwm && cnv == 1U &&
479 (base->SC & TPM_SC_PS_MASK) == kTPM_Prescale_Divide_1));
480 #endif
481 base->CONTROLS[chnlNumber].CnV = cnv;
482 #if defined(FSL_FEATURE_TPM_WAIT_CnV_REGISTER_UPDATE) && FSL_FEATURE_TPM_WAIT_CnV_REGISTER_UPDATE
483 while (!(cnv == base->CONTROLS[chnlNumber].CnV))
484 {
485 }
486 #endif
487
488 #if defined(FSL_FEATURE_TPM_HAS_COMBINE) && FSL_FEATURE_TPM_HAS_COMBINE
489 }
490 #endif
491 }
492
493 /*!
494 * brief Update the edge level selection for a channel
495 *
496 * param base TPM peripheral base address
497 * param chnlNumber The channel number
498 * param level The level to be set to the ELSnB:ELSnA field; valid values are 00, 01, 10, 11.
499 * See the appropriate SoC reference manual for details about this field.
500 */
TPM_UpdateChnlEdgeLevelSelect(TPM_Type * base,tpm_chnl_t chnlNumber,uint8_t level)501 void TPM_UpdateChnlEdgeLevelSelect(TPM_Type *base, tpm_chnl_t chnlNumber, uint8_t level)
502 {
503 assert((int8_t)chnlNumber < FSL_FEATURE_TPM_CHANNEL_COUNTn(base));
504 assert((uint8_t)chnlNumber < sizeof(base->CONTROLS) / sizeof(base->CONTROLS[0]));
505
506 uint32_t reg = base->CONTROLS[chnlNumber].CnSC
507 #if !(defined(FSL_FEATURE_TPM_CnSC_CHF_WRITE_0_CLEAR) && FSL_FEATURE_TPM_CnSC_CHF_WRITE_0_CLEAR)
508 & ~(TPM_CnSC_CHF_MASK)
509 #endif
510 ;
511
512 /* When switching mode, disable channel first */
513 base->CONTROLS[chnlNumber].CnSC &=
514 ~(TPM_CnSC_MSA_MASK | TPM_CnSC_MSB_MASK | TPM_CnSC_ELSA_MASK | TPM_CnSC_ELSB_MASK);
515
516 /* Wait till mode change to disable channel is acknowledged */
517 while (0U != (base->CONTROLS[chnlNumber].CnSC &
518 (TPM_CnSC_MSA_MASK | TPM_CnSC_MSB_MASK | TPM_CnSC_ELSA_MASK | TPM_CnSC_ELSB_MASK)))
519 {
520 }
521
522 /* Clear the field and write the new level value */
523 reg &= ~(TPM_CnSC_ELSA_MASK | TPM_CnSC_ELSB_MASK);
524 reg |= ((uint32_t)level << TPM_CnSC_ELSA_SHIFT) & (TPM_CnSC_ELSA_MASK | TPM_CnSC_ELSB_MASK);
525
526 base->CONTROLS[chnlNumber].CnSC = reg;
527
528 /* Wait till mode change is acknowledged */
529 reg &= (TPM_CnSC_MSA_MASK | TPM_CnSC_MSB_MASK | TPM_CnSC_ELSA_MASK | TPM_CnSC_ELSB_MASK);
530 while (reg != (base->CONTROLS[chnlNumber].CnSC &
531 (TPM_CnSC_MSA_MASK | TPM_CnSC_MSB_MASK | TPM_CnSC_ELSA_MASK | TPM_CnSC_ELSB_MASK)))
532 {
533 }
534 }
535
536 /*!
537 * brief Enables capturing an input signal on the channel using the function parameters.
538 *
539 * When the edge specified in the captureMode argument occurs on the channel, the TPM counter is captured into
540 * the CnV register. The user has to read the CnV register separately to get this value.
541 *
542 * param base TPM peripheral base address
543 * param chnlNumber The channel number
544 * param captureMode Specifies which edge to capture
545 */
TPM_SetupInputCapture(TPM_Type * base,tpm_chnl_t chnlNumber,tpm_input_capture_edge_t captureMode)546 void TPM_SetupInputCapture(TPM_Type *base, tpm_chnl_t chnlNumber, tpm_input_capture_edge_t captureMode)
547 {
548 assert((int8_t)chnlNumber < FSL_FEATURE_TPM_CHANNEL_COUNTn(base));
549 assert((uint8_t)chnlNumber < sizeof(base->CONTROLS) / sizeof(base->CONTROLS[0]));
550
551 #if defined(FSL_FEATURE_TPM_HAS_QDCTRL) && FSL_FEATURE_TPM_HAS_QDCTRL
552 /* The TPM's QDCTRL register required to be effective */
553 if (0 != FSL_FEATURE_TPM_QDCTRL_HAS_EFFECTn(base))
554 {
555 /* Clear quadrature Decoder mode for channel 0 or 1*/
556 if (((uint32_t)chnlNumber == 0u) || ((uint32_t)chnlNumber == 1u))
557 {
558 base->QDCTRL &= ~TPM_QDCTRL_QUADEN_MASK;
559 }
560 }
561 #endif
562
563 #if defined(FSL_FEATURE_TPM_HAS_COMBINE) && FSL_FEATURE_TPM_HAS_COMBINE
564 /* The TPM's COMBINE register required to be effective */
565 if (0 != FSL_FEATURE_TPM_COMBINE_HAS_EFFECTn(base))
566 {
567 /* Clear the combine bit for chnlNumber */
568 base->COMBINE &= ~((uint32_t)1U << (((uint32_t)chnlNumber / 2U) * TPM_COMBINE_SHIFT));
569 }
570 #endif
571
572 /* When switching mode, disable channel first */
573 base->CONTROLS[chnlNumber].CnSC &=
574 ~(TPM_CnSC_MSA_MASK | TPM_CnSC_MSB_MASK | TPM_CnSC_ELSA_MASK | TPM_CnSC_ELSB_MASK);
575
576 /* Wait till mode change to disable channel is acknowledged */
577 while (0U != (base->CONTROLS[chnlNumber].CnSC &
578 (TPM_CnSC_MSA_MASK | TPM_CnSC_MSB_MASK | TPM_CnSC_ELSA_MASK | TPM_CnSC_ELSB_MASK)))
579 {
580 }
581
582 /* Set the requested input capture mode */
583 base->CONTROLS[chnlNumber].CnSC |= (uint32_t)captureMode;
584
585 /* Wait till mode change is acknowledged */
586 while (0U == (base->CONTROLS[chnlNumber].CnSC &
587 (TPM_CnSC_MSA_MASK | TPM_CnSC_MSB_MASK | TPM_CnSC_ELSA_MASK | TPM_CnSC_ELSB_MASK)))
588 {
589 }
590 }
591
592 /*!
593 * brief Configures the TPM to generate timed pulses.
594 *
595 * When the TPM counter matches the value of compareVal argument (this is written into CnV reg), the channel
596 * output is changed based on what is specified in the compareMode argument.
597 *
598 * param base TPM peripheral base address
599 * param chnlNumber The channel number
600 * param compareMode Action to take on the channel output when the compare condition is met
601 * param compareValue Value to be programmed in the CnV register.
602 */
TPM_SetupOutputCompare(TPM_Type * base,tpm_chnl_t chnlNumber,tpm_output_compare_mode_t compareMode,uint32_t compareValue)603 void TPM_SetupOutputCompare(TPM_Type *base,
604 tpm_chnl_t chnlNumber,
605 tpm_output_compare_mode_t compareMode,
606 uint32_t compareValue)
607 {
608 assert((int8_t)chnlNumber < FSL_FEATURE_TPM_CHANNEL_COUNTn(base));
609 assert((uint8_t)chnlNumber < sizeof(base->CONTROLS) / sizeof(base->CONTROLS[0]));
610
611 #if defined(FSL_FEATURE_TPM_HAS_QDCTRL) && FSL_FEATURE_TPM_HAS_QDCTRL
612 /* The TPM's QDCTRL register required to be effective */
613 if (0 != FSL_FEATURE_TPM_QDCTRL_HAS_EFFECTn(base))
614 {
615 /* Clear quadrature Decoder mode for channel 0 or 1 */
616 if (((uint32_t)chnlNumber == 0U) || ((uint32_t)chnlNumber == 1U))
617 {
618 base->QDCTRL &= ~TPM_QDCTRL_QUADEN_MASK;
619 }
620 }
621 #endif
622
623 /* When switching mode, disable channel first */
624 base->CONTROLS[chnlNumber].CnSC &=
625 ~(TPM_CnSC_MSA_MASK | TPM_CnSC_MSB_MASK | TPM_CnSC_ELSA_MASK | TPM_CnSC_ELSB_MASK);
626
627 /* Wait till mode change to disable channel is acknowledged */
628 while (0U != (base->CONTROLS[chnlNumber].CnSC &
629 (TPM_CnSC_MSA_MASK | TPM_CnSC_MSB_MASK | TPM_CnSC_ELSA_MASK | TPM_CnSC_ELSB_MASK)))
630 {
631 }
632
633 /* Setup the channel output behaviour when a match occurs with the compare value */
634 base->CONTROLS[chnlNumber].CnSC |= (uint32_t)compareMode;
635
636 /* Setup the compare value */
637 base->CONTROLS[chnlNumber].CnV = compareValue;
638
639 /* Wait till mode change is acknowledged */
640 while (0U == (base->CONTROLS[chnlNumber].CnSC &
641 (TPM_CnSC_MSA_MASK | TPM_CnSC_MSB_MASK | TPM_CnSC_ELSA_MASK | TPM_CnSC_ELSB_MASK)))
642 {
643 }
644 }
645
646 #if defined(FSL_FEATURE_TPM_HAS_COMBINE) && FSL_FEATURE_TPM_HAS_COMBINE
647 /*!
648 * brief Configures the dual edge capture mode of the TPM.
649 *
650 * This function allows to measure a pulse width of the signal on the input of channel of a
651 * channel pair. The filter function is disabled if the filterVal argument passed is zero.
652 *
653 * param base TPM peripheral base address
654 * param chnlPairNumber The TPM channel pair number; options are 0, 1, 2, 3
655 * param edgeParam Sets up the dual edge capture function
656 * param filterValue Filter value, specify 0 to disable filter.
657 */
TPM_SetupDualEdgeCapture(TPM_Type * base,tpm_chnl_t chnlPairNumber,const tpm_dual_edge_capture_param_t * edgeParam,uint32_t filterValue)658 void TPM_SetupDualEdgeCapture(TPM_Type *base,
659 tpm_chnl_t chnlPairNumber,
660 const tpm_dual_edge_capture_param_t *edgeParam,
661 uint32_t filterValue)
662 {
663 assert(NULL != edgeParam);
664 assert((int8_t)chnlPairNumber < (int8_t)FSL_FEATURE_TPM_CHANNEL_COUNTn(base) / 2);
665 assert(0 != FSL_FEATURE_TPM_COMBINE_HAS_EFFECTn(base));
666
667 uint32_t reg;
668 uint32_t u32flag;
669
670 #if defined(FSL_FEATURE_TPM_HAS_QDCTRL) && FSL_FEATURE_TPM_HAS_QDCTRL
671 /* The TPM's QDCTRL register required to be effective */
672 if (0 != FSL_FEATURE_TPM_QDCTRL_HAS_EFFECTn(base))
673 {
674 /* Clear quadrature Decoder mode for channel 0 or 1*/
675 if ((uint32_t)chnlPairNumber == 0u)
676 {
677 base->QDCTRL &= ~TPM_QDCTRL_QUADEN_MASK;
678 }
679 }
680 #endif
681
682 /* Unlock: When switching mode, disable channel first */
683 base->CONTROLS[(uint32_t)chnlPairNumber * 2U].CnSC &=
684 ~(TPM_CnSC_MSA_MASK | TPM_CnSC_MSB_MASK | TPM_CnSC_ELSA_MASK | TPM_CnSC_ELSB_MASK);
685
686 /* Wait till mode change to disable channel is acknowledged */
687 while (0U != (base->CONTROLS[(uint32_t)chnlPairNumber * 2U].CnSC &
688 (TPM_CnSC_MSA_MASK | TPM_CnSC_MSB_MASK | TPM_CnSC_ELSA_MASK | TPM_CnSC_ELSB_MASK)))
689 {
690 }
691
692 base->CONTROLS[((uint32_t)chnlPairNumber * 2U) + 1U].CnSC &=
693 ~(TPM_CnSC_MSA_MASK | TPM_CnSC_MSB_MASK | TPM_CnSC_ELSA_MASK | TPM_CnSC_ELSB_MASK);
694
695 /* Wait till mode change to disable channel is acknowledged */
696 while (0U != (base->CONTROLS[((uint32_t)chnlPairNumber * 2U) + 1U].CnSC &
697 (TPM_CnSC_MSA_MASK | TPM_CnSC_MSB_MASK | TPM_CnSC_ELSA_MASK | TPM_CnSC_ELSB_MASK)))
698 {
699 }
700
701 /* Now, the registers for input mode can be operated. */
702 if (true == edgeParam->enableSwap)
703 {
704 u32flag = TPM_COMBINE_COMBINE0_MASK | TPM_COMBINE_COMSWAP0_MASK;
705 /* Set the combine and swap bits for the channel pair */
706 base->COMBINE |= u32flag << (TPM_COMBINE_SHIFT * (uint32_t)chnlPairNumber);
707
708 /* Input filter setup for channel n+1 input */
709 reg = base->FILTER;
710 reg &= ~((uint32_t)TPM_FILTER_CH0FVAL_MASK << (TPM_FILTER_CH1FVAL_SHIFT * ((uint32_t)chnlPairNumber + 1U)));
711 reg |= (filterValue << (TPM_FILTER_CH1FVAL_SHIFT * ((uint32_t)chnlPairNumber + 1U)));
712 base->FILTER = reg;
713 }
714 else
715 {
716 reg = base->COMBINE;
717 /* Clear the swap bit for the channel pair */
718 reg &= ~((uint32_t)TPM_COMBINE_COMSWAP0_MASK << ((uint32_t)chnlPairNumber * TPM_COMBINE_COMSWAP0_SHIFT));
719 u32flag = TPM_COMBINE_COMBINE0_MASK;
720
721 /* Set the combine bit for the channel pair */
722 reg |= u32flag << (TPM_COMBINE_SHIFT * (uint32_t)chnlPairNumber);
723 base->COMBINE = reg;
724
725 /* Input filter setup for channel n input */
726 reg = base->FILTER;
727 reg &= ~((uint32_t)TPM_FILTER_CH0FVAL_MASK << (TPM_FILTER_CH1FVAL_SHIFT * (uint32_t)chnlPairNumber));
728 reg |= (filterValue << (TPM_FILTER_CH1FVAL_SHIFT * (uint32_t)chnlPairNumber));
729 base->FILTER = reg;
730 }
731
732 /* Setup the edge detection from channel n */
733 base->CONTROLS[(uint32_t)chnlPairNumber * 2u].CnSC |= (uint32_t)edgeParam->currChanEdgeMode;
734
735 /* Wait till mode change is acknowledged */
736 while (0U == (base->CONTROLS[(uint32_t)chnlPairNumber * 2U].CnSC &
737 (TPM_CnSC_MSA_MASK | TPM_CnSC_MSB_MASK | TPM_CnSC_ELSA_MASK | TPM_CnSC_ELSB_MASK)))
738 {
739 }
740
741 /* Setup the edge detection from channel n+1 */
742 base->CONTROLS[((uint32_t)chnlPairNumber * 2U) + 1U].CnSC |= (uint32_t)edgeParam->nextChanEdgeMode;
743
744 /* Wait till mode change is acknowledged */
745 while (0U == (base->CONTROLS[((uint32_t)chnlPairNumber * 2U) + 1U].CnSC &
746 (TPM_CnSC_MSA_MASK | TPM_CnSC_MSB_MASK | TPM_CnSC_ELSA_MASK | TPM_CnSC_ELSB_MASK)))
747 {
748 }
749 }
750 #endif
751
752 #if defined(FSL_FEATURE_TPM_HAS_QDCTRL) && FSL_FEATURE_TPM_HAS_QDCTRL
753 /*!
754 * brief Configures the parameters and activates the quadrature decode mode.
755 *
756 * param base TPM peripheral base address
757 * param phaseAParams Phase A configuration parameters
758 * param phaseBParams Phase B configuration parameters
759 * param quadMode Selects encoding mode used in quadrature decoder mode
760 */
TPM_SetupQuadDecode(TPM_Type * base,const tpm_phase_params_t * phaseAParams,const tpm_phase_params_t * phaseBParams,tpm_quad_decode_mode_t quadMode)761 void TPM_SetupQuadDecode(TPM_Type *base,
762 const tpm_phase_params_t *phaseAParams,
763 const tpm_phase_params_t *phaseBParams,
764 tpm_quad_decode_mode_t quadMode)
765 {
766 assert(NULL != phaseAParams);
767 assert(NULL != phaseBParams);
768 assert(0 != FSL_FEATURE_TPM_QDCTRL_HAS_EFFECTn(base));
769
770 base->CONTROLS[0].CnSC &= ~(TPM_CnSC_MSA_MASK | TPM_CnSC_MSB_MASK | TPM_CnSC_ELSA_MASK | TPM_CnSC_ELSB_MASK);
771
772 /* Wait till mode change to disable channel is acknowledged */
773 while (0U !=
774 (base->CONTROLS[0].CnSC & (TPM_CnSC_MSA_MASK | TPM_CnSC_MSB_MASK | TPM_CnSC_ELSA_MASK | TPM_CnSC_ELSB_MASK)))
775 {
776 }
777 uint32_t reg;
778
779 /* Set Phase A filter value */
780 reg = base->FILTER;
781 reg &= ~(TPM_FILTER_CH0FVAL_MASK);
782 reg |= TPM_FILTER_CH0FVAL(phaseAParams->phaseFilterVal);
783 base->FILTER = reg;
784
785 #if defined(FSL_FEATURE_TPM_HAS_POL) && FSL_FEATURE_TPM_HAS_POL
786 /* Set Phase A polarity */
787 if (kTPM_QuadPhaseInvert == phaseAParams->phasePolarity)
788 {
789 base->POL |= TPM_POL_POL0_MASK;
790 }
791 else
792 {
793 base->POL &= ~TPM_POL_POL0_MASK;
794 }
795 #endif
796
797 base->CONTROLS[1].CnSC &= ~(TPM_CnSC_MSA_MASK | TPM_CnSC_MSB_MASK | TPM_CnSC_ELSA_MASK | TPM_CnSC_ELSB_MASK);
798
799 /* Wait till mode change to disable channel is acknowledged */
800 while (0U !=
801 (base->CONTROLS[1].CnSC & (TPM_CnSC_MSA_MASK | TPM_CnSC_MSB_MASK | TPM_CnSC_ELSA_MASK | TPM_CnSC_ELSB_MASK)))
802 {
803 }
804 /* Set Phase B filter value */
805 reg = base->FILTER;
806 reg &= ~(TPM_FILTER_CH1FVAL_MASK);
807 reg |= TPM_FILTER_CH1FVAL(phaseBParams->phaseFilterVal);
808 base->FILTER = reg;
809 #if defined(FSL_FEATURE_TPM_HAS_POL) && FSL_FEATURE_TPM_HAS_POL
810 /* Set Phase B polarity */
811 if (kTPM_QuadPhaseInvert == phaseBParams->phasePolarity)
812 {
813 base->POL |= TPM_POL_POL1_MASK;
814 }
815 else
816 {
817 base->POL &= ~TPM_POL_POL1_MASK;
818 }
819 #endif
820
821 /* Set Quadrature mode */
822 reg = base->QDCTRL;
823 reg &= ~(TPM_QDCTRL_QUADMODE_MASK);
824 reg |= TPM_QDCTRL_QUADMODE(quadMode);
825 base->QDCTRL = reg;
826
827 /* Enable Quad decode */
828 base->QDCTRL |= TPM_QDCTRL_QUADEN_MASK;
829 }
830
831 #endif
832
833 /*!
834 * brief Enables the selected TPM interrupts.
835 *
836 * param base TPM peripheral base address
837 * param mask The interrupts to enable. This is a logical OR of members of the
838 * enumeration ::tpm_interrupt_enable_t
839 */
TPM_EnableInterrupts(TPM_Type * base,uint32_t mask)840 void TPM_EnableInterrupts(TPM_Type *base, uint32_t mask)
841 {
842 uint32_t chnlInterrupts = (mask & 0xFFU);
843 uint8_t chnlNumber = 0;
844
845 /* Enable the timer overflow interrupt */
846 if ((uint32_t)kTPM_TimeOverflowInterruptEnable == (mask & (uint32_t)kTPM_TimeOverflowInterruptEnable))
847 {
848 base->SC |= TPM_SC_TOIE_MASK;
849 }
850
851 /* Enable the channel interrupts */
852 while (0U != chnlInterrupts)
853 {
854 if (0U != (chnlInterrupts & 0x1u))
855 {
856 base->CONTROLS[chnlNumber].CnSC |= TPM_CnSC_CHIE_MASK;
857 }
858 chnlNumber++;
859 chnlInterrupts = chnlInterrupts >> 1U;
860 }
861 }
862
863 /*!
864 * brief Disables the selected TPM interrupts.
865 *
866 * param base TPM peripheral base address
867 * param mask The interrupts to disable. This is a logical OR of members of the
868 * enumeration ::tpm_interrupt_enable_t
869 */
TPM_DisableInterrupts(TPM_Type * base,uint32_t mask)870 void TPM_DisableInterrupts(TPM_Type *base, uint32_t mask)
871 {
872 uint32_t chnlInterrupts = (mask & 0xFFU);
873 uint8_t chnlNumber = 0;
874
875 /* Disable the timer overflow interrupt */
876 if ((uint32_t)kTPM_TimeOverflowInterruptEnable == (mask & (uint32_t)kTPM_TimeOverflowInterruptEnable))
877 {
878 base->SC &= ~TPM_SC_TOIE_MASK;
879 }
880
881 /* Disable the channel interrupts */
882 while (0U != chnlInterrupts)
883 {
884 if (0U != (chnlInterrupts & 0x1u))
885 {
886 base->CONTROLS[chnlNumber].CnSC &= ~TPM_CnSC_CHIE_MASK;
887 }
888 chnlNumber++;
889 chnlInterrupts = chnlInterrupts >> 1U;
890 }
891 }
892
893 /*!
894 * brief Gets the enabled TPM interrupts.
895 *
896 * param base TPM peripheral base address
897 *
898 * return The enabled interrupts. This is the logical OR of members of the
899 * enumeration ::tpm_interrupt_enable_t
900 */
TPM_GetEnabledInterrupts(TPM_Type * base)901 uint32_t TPM_GetEnabledInterrupts(TPM_Type *base)
902 {
903 uint32_t enabledInterrupts = 0;
904 uint32_t u32flag = 1;
905 int8_t chnlCount = FSL_FEATURE_TPM_CHANNEL_COUNTn(base);
906
907 /* The CHANNEL_COUNT macro returns -1 if it cannot match the TPM instance */
908 assert(chnlCount != -1);
909
910 /* Check if timer overflow interrupt is enabled */
911 if (0U != (base->SC & TPM_SC_TOIE_MASK))
912 {
913 enabledInterrupts |= (uint32_t)kTPM_TimeOverflowInterruptEnable;
914 }
915
916 /* Check if the channel interrupts are enabled */
917 while (chnlCount > 0)
918 {
919 chnlCount--;
920 if (0U != (base->CONTROLS[chnlCount].CnSC & TPM_CnSC_CHIE_MASK))
921 {
922 enabledInterrupts |= (u32flag << (uint8_t)chnlCount);
923 }
924 }
925
926 return enabledInterrupts;
927 }
928