2 * Copyright (c) 2016, Freescale Semiconductor, Inc.
\r
3 * Copyright 2016-2018 NXP
\r
4 * All rights reserved.
\r
6 * SPDX-License-Identifier: BSD-3-Clause
\r
9 #include "fsl_ctimer.h"
\r
11 /* Component ID definition, used by tools. */
\r
12 #ifndef FSL_COMPONENT_ID
\r
13 #define FSL_COMPONENT_ID "platform.drivers.ctimer"
\r
16 /*******************************************************************************
\r
18 ******************************************************************************/
\r
20 * @brief Gets the instance from the base address
\r
22 * @param base Ctimer peripheral base address
\r
24 * @return The Timer instance
\r
26 static uint32_t CTIMER_GetInstance(CTIMER_Type *base);
\r
28 /*******************************************************************************
\r
30 ******************************************************************************/
\r
31 /*! @brief Pointers to Timer bases for each instance. */
\r
32 static CTIMER_Type *const s_ctimerBases[] = CTIMER_BASE_PTRS;
\r
34 #if !(defined(FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL)
\r
35 /*! @brief Pointers to Timer clocks for each instance. */
\r
36 static const clock_ip_name_t s_ctimerClocks[] = CTIMER_CLOCKS;
\r
37 #endif /* FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL */
\r
39 #if !(defined(FSL_FEATURE_CTIMER_HAS_NO_RESET) && (FSL_FEATURE_CTIMER_HAS_NO_RESET))
\r
40 #if !(defined(FSL_SDK_DISABLE_DRIVER_RESET_CONTROL) && FSL_SDK_DISABLE_DRIVER_RESET_CONTROL)
\r
41 #if defined(FSL_FEATURE_CTIMER_WRITE_ZERO_ASSERT_RESET) && FSL_FEATURE_CTIMER_WRITE_ZERO_ASSERT_RESET
\r
42 /*! @brief Pointers to Timer resets for each instance, writing a zero asserts the reset */
\r
43 static const reset_ip_name_t s_ctimerResets[] = CTIMER_RSTS_N;
\r
45 /*! @brief Pointers to Timer resets for each instance, writing a one asserts the reset */
\r
46 static const reset_ip_name_t s_ctimerResets[] = CTIMER_RSTS;
\r
49 #endif /* FSL_SDK_DISABLE_DRIVER_RESET_CONTROL */
\r
51 /*! @brief Pointers real ISRs installed by drivers for each instance. */
\r
52 static ctimer_callback_t *s_ctimerCallback[FSL_FEATURE_SOC_CTIMER_COUNT] = {0};
\r
54 /*! @brief Callback type installed by drivers for each instance. */
\r
55 static ctimer_callback_type_t ctimerCallbackType[FSL_FEATURE_SOC_CTIMER_COUNT] = {kCTIMER_SingleCallback};
\r
57 /*! @brief Array to map timer instance to IRQ number. */
\r
58 static const IRQn_Type s_ctimerIRQ[] = CTIMER_IRQS;
\r
60 /*******************************************************************************
\r
62 ******************************************************************************/
\r
63 static uint32_t CTIMER_GetInstance(CTIMER_Type *base)
\r
66 uint32_t ctimerArrayCount = (sizeof(s_ctimerBases) / sizeof(s_ctimerBases[0]));
\r
68 /* Find the instance index from base address mappings. */
\r
69 for (instance = 0; instance < ctimerArrayCount; instance++)
\r
71 if (s_ctimerBases[instance] == base)
\r
77 assert(instance < ctimerArrayCount);
\r
83 * brief Ungates the clock and configures the peripheral for basic operation.
\r
85 * note This API should be called at the beginning of the application before using the driver.
\r
87 * param base Ctimer peripheral base address
\r
88 * param config Pointer to the user configuration structure.
\r
90 void CTIMER_Init(CTIMER_Type *base, const ctimer_config_t *config)
\r
94 #if !(defined(FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL)
\r
95 /* Enable the timer clock*/
\r
96 CLOCK_EnableClock(s_ctimerClocks[CTIMER_GetInstance(base)]);
\r
97 #endif /* FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL */
\r
99 #if !(defined(FSL_SDK_DISABLE_DRIVER_RESET_CONTROL) && FSL_SDK_DISABLE_DRIVER_RESET_CONTROL)
\r
100 /* Reset the module. */
\r
101 #if !(defined(FSL_FEATURE_CTIMER_HAS_NO_RESET) && (FSL_FEATURE_CTIMER_HAS_NO_RESET))
\r
102 RESET_PeripheralReset(s_ctimerResets[CTIMER_GetInstance(base)]);
\r
104 #endif /* FSL_SDK_DISABLE_DRIVER_RESET_CONTROL */
\r
106 /* Setup the cimer mode and count select */
\r
107 #if !(defined(FSL_FEATURE_CTIMER_HAS_NO_INPUT_CAPTURE) && (FSL_FEATURE_CTIMER_HAS_NO_INPUT_CAPTURE))
\r
108 base->CTCR = CTIMER_CTCR_CTMODE(config->mode) | CTIMER_CTCR_CINSEL(config->input);
\r
110 /* Setup the timer prescale value */
\r
111 base->PR = CTIMER_PR_PRVAL(config->prescale);
\r
115 * brief Gates the timer clock.
\r
117 * param base Ctimer peripheral base address
\r
119 void CTIMER_Deinit(CTIMER_Type *base)
\r
121 uint32_t index = CTIMER_GetInstance(base);
\r
122 /* Stop the timer */
\r
123 base->TCR &= ~CTIMER_TCR_CEN_MASK;
\r
125 #if !(defined(FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL)
\r
126 /* Disable the timer clock*/
\r
127 CLOCK_DisableClock(s_ctimerClocks[index]);
\r
128 #endif /* FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL */
\r
130 /* Disable IRQ at NVIC Level */
\r
131 DisableIRQ(s_ctimerIRQ[index]);
\r
135 * brief Fills in the timers configuration structure with the default settings.
\r
137 * The default values are:
\r
139 * config->mode = kCTIMER_TimerMode;
\r
140 * config->input = kCTIMER_Capture_0;
\r
141 * config->prescale = 0;
\r
143 * param config Pointer to the user configuration structure.
\r
145 void CTIMER_GetDefaultConfig(ctimer_config_t *config)
\r
149 /* Initializes the configure structure to zero. */
\r
150 memset(config, 0, sizeof(*config));
\r
152 /* Run as a timer */
\r
153 config->mode = kCTIMER_TimerMode;
\r
154 /* This field is ignored when mode is timer */
\r
155 config->input = kCTIMER_Capture_0;
\r
156 /* Timer counter is incremented on every APB bus clock */
\r
157 config->prescale = 0;
\r
161 * brief Configures the PWM signal parameters.
\r
163 * Enables PWM mode on the match channel passed in and will then setup the match value
\r
164 * and other match parameters to generate a PWM signal.
\r
165 * This function will assign match channel 3 to set the PWM cycle.
\r
167 * note When setting PWM output from multiple output pins, all should use the same PWM
\r
168 * frequency. Please use CTIMER_SetupPwmPeriod to set up the PWM with high resolution.
\r
170 * param base Ctimer peripheral base address
\r
171 * param matchChannel Match pin to be used to output the PWM signal
\r
172 * param dutyCyclePercent PWM pulse width; the value should be between 0 to 100
\r
173 * param pwmFreq_Hz PWM signal frequency in Hz
\r
174 * param srcClock_Hz Timer counter clock in Hz
\r
175 * param enableInt Enable interrupt when the timer value reaches the match value of the PWM pulse,
\r
176 * if it is 0 then no interrupt is generated
\r
178 * return kStatus_Success on success
\r
179 * kStatus_Fail If matchChannel passed in is 3; this channel is reserved to set the PWM cycle
\r
181 status_t CTIMER_SetupPwm(CTIMER_Type *base,
\r
182 ctimer_match_t matchChannel,
\r
183 uint8_t dutyCyclePercent,
\r
184 uint32_t pwmFreq_Hz,
\r
185 uint32_t srcClock_Hz,
\r
188 assert(pwmFreq_Hz > 0);
\r
191 uint32_t period, pulsePeriod = 0;
\r
192 uint32_t timerClock = srcClock_Hz / (base->PR + 1);
\r
193 uint32_t index = CTIMER_GetInstance(base);
\r
195 if (matchChannel == kCTIMER_Match_3)
\r
197 return kStatus_Fail;
\r
200 /* Enable PWM mode on the channel */
\r
201 base->PWMC |= (1U << matchChannel);
\r
203 /* Clear the stop, reset and interrupt bits for this channel */
\r
205 reg &= ~((CTIMER_MCR_MR0R_MASK | CTIMER_MCR_MR0S_MASK | CTIMER_MCR_MR0I_MASK) << (matchChannel * 3));
\r
207 /* If call back function is valid then enable match interrupt for the channel */
\r
210 reg |= (CTIMER_MCR_MR0I_MASK << (CTIMER_MCR_MR0I_SHIFT + (matchChannel * 3)));
\r
213 /* Reset the counter when match on channel 3 */
\r
214 reg |= CTIMER_MCR_MR3R_MASK;
\r
218 /* Calculate PWM period match value */
\r
219 period = (timerClock / pwmFreq_Hz) - 1;
\r
221 /* Calculate pulse width match value */
\r
222 if (dutyCyclePercent == 0)
\r
224 pulsePeriod = period + 1;
\r
228 pulsePeriod = (period * (100 - dutyCyclePercent)) / 100;
\r
231 /* Match on channel 3 will define the PWM period */
\r
232 base->MR[kCTIMER_Match_3] = period;
\r
234 /* This will define the PWM pulse period */
\r
235 base->MR[matchChannel] = pulsePeriod;
\r
236 /* Clear status flags */
\r
237 CTIMER_ClearStatusFlags(base, CTIMER_IR_MR0INT_MASK << matchChannel);
\r
238 /* If call back function is valid then enable interrupt and update the call back function */
\r
241 EnableIRQ(s_ctimerIRQ[index]);
\r
244 return kStatus_Success;
\r
248 * brief Configures the PWM signal parameters.
\r
250 * Enables PWM mode on the match channel passed in and will then setup the match value
\r
251 * and other match parameters to generate a PWM signal.
\r
252 * This function will assign match channel 3 to set the PWM cycle.
\r
254 * note When setting PWM output from multiple output pins, all should use the same PWM
\r
257 * param base Ctimer peripheral base address
\r
258 * param matchChannel Match pin to be used to output the PWM signal
\r
259 * param pwmPeriod PWM period match value
\r
260 * param pulsePeriod Pulse width match value
\r
261 * param enableInt Enable interrupt when the timer value reaches the match value of the PWM pulse,
\r
262 * if it is 0 then no interrupt is generated
\r
264 * return kStatus_Success on success
\r
265 * kStatus_Fail If matchChannel passed in is 3; this channel is reserved to set the PWM period
\r
267 status_t CTIMER_SetupPwmPeriod(
\r
268 CTIMER_Type *base, ctimer_match_t matchChannel, uint32_t pwmPeriod, uint32_t pulsePeriod, bool enableInt)
\r
270 /* Some CTimers only have 16bits , so the value is limited*/
\r
271 #if defined(FSL_FEATURE_SOC_CTIMER16B) && FSL_FEATURE_SOC_CTIMER16B
\r
272 assert(!((FSL_FEATURE_CTIMER_BIT_SIZEn(base) < 32) && (pulsePeriod > 0xFFFFU)));
\r
276 uint32_t index = CTIMER_GetInstance(base);
\r
278 if (matchChannel == kCTIMER_Match_3)
\r
280 return kStatus_Fail;
\r
283 /* Enable PWM mode on the channel */
\r
284 base->PWMC |= (1U << matchChannel);
\r
286 /* Clear the stop, reset and interrupt bits for this channel */
\r
288 reg &= ~((CTIMER_MCR_MR0R_MASK | CTIMER_MCR_MR0S_MASK | CTIMER_MCR_MR0I_MASK) << (matchChannel * 3));
\r
290 /* If call back function is valid then enable match interrupt for the channel */
\r
293 reg |= (CTIMER_MCR_MR0I_MASK << (CTIMER_MCR_MR0I_SHIFT + (matchChannel * 3)));
\r
296 /* Reset the counter when match on channel 3 */
\r
297 reg |= CTIMER_MCR_MR3R_MASK;
\r
301 /* Match on channel 3 will define the PWM period */
\r
302 base->MR[kCTIMER_Match_3] = pwmPeriod;
\r
304 /* This will define the PWM pulse period */
\r
305 base->MR[matchChannel] = pulsePeriod;
\r
306 /* Clear status flags */
\r
307 CTIMER_ClearStatusFlags(base, CTIMER_IR_MR0INT_MASK << matchChannel);
\r
308 /* If call back function is valid then enable interrupt and update the call back function */
\r
311 EnableIRQ(s_ctimerIRQ[index]);
\r
314 return kStatus_Success;
\r
318 * brief Updates the duty cycle of an active PWM signal.
\r
320 * note Please use CTIMER_UpdatePwmPulsePeriod to update the PWM with high resolution.
\r
322 * param base Ctimer peripheral base address
\r
323 * param matchChannel Match pin to be used to output the PWM signal
\r
324 * param dutyCyclePercent New PWM pulse width; the value should be between 0 to 100
\r
326 void CTIMER_UpdatePwmDutycycle(CTIMER_Type *base, ctimer_match_t matchChannel, uint8_t dutyCyclePercent)
\r
328 uint32_t pulsePeriod = 0, period;
\r
330 /* Match channel 3 defines the PWM period */
\r
331 period = base->MR[kCTIMER_Match_3];
\r
333 /* Calculate pulse width match value */
\r
334 pulsePeriod = (period * dutyCyclePercent) / 100;
\r
336 /* For 0% dutycyle, make pulse period greater than period so the event will never occur */
\r
337 if (dutyCyclePercent == 0)
\r
339 pulsePeriod = period + 1;
\r
343 pulsePeriod = (period * (100 - dutyCyclePercent)) / 100;
\r
346 /* Update dutycycle */
\r
347 base->MR[matchChannel] = pulsePeriod;
\r
351 * brief Setup the match register.
\r
353 * User configuration is used to setup the match value and action to be taken when a match occurs.
\r
355 * param base Ctimer peripheral base address
\r
356 * param matchChannel Match register to configure
\r
357 * param config Pointer to the match configuration structure
\r
359 void CTIMER_SetupMatch(CTIMER_Type *base, ctimer_match_t matchChannel, const ctimer_match_config_t *config)
\r
361 /* Some CTimers only have 16bits , so the value is limited*/
\r
362 #if defined(FSL_FEATURE_SOC_CTIMER16B) && FSL_FEATURE_SOC_CTIMER16B
\r
363 assert(!(FSL_FEATURE_CTIMER_BIT_SIZEn(base) < 32 && config->matchValue > 0xFFFFU));
\r
366 uint32_t index = CTIMER_GetInstance(base);
\r
368 /* Set the counter operation when a match on this channel occurs */
\r
370 reg &= ~((CTIMER_MCR_MR0R_MASK | CTIMER_MCR_MR0S_MASK | CTIMER_MCR_MR0I_MASK) << (matchChannel * 3));
\r
371 reg |= (uint32_t)((uint32_t)(config->enableCounterReset) << (CTIMER_MCR_MR0R_SHIFT + (matchChannel * 3)));
\r
372 reg |= (uint32_t)((uint32_t)(config->enableCounterStop) << (CTIMER_MCR_MR0S_SHIFT + (matchChannel * 3)));
\r
373 reg |= (uint32_t)((uint32_t)(config->enableInterrupt) << (CTIMER_MCR_MR0I_SHIFT + (matchChannel * 3)));
\r
377 /* Set the match output operation when a match on this channel occurs */
\r
378 reg &= ~(CTIMER_EMR_EMC0_MASK << (matchChannel * 2));
\r
379 reg |= (uint32_t)config->outControl << (CTIMER_EMR_EMC0_SHIFT + (matchChannel * 2));
\r
381 /* Set the initial state of the EM bit/output */
\r
382 reg &= ~(CTIMER_EMR_EM0_MASK << matchChannel);
\r
383 reg |= (uint32_t)config->outPinInitState << matchChannel;
\r
386 /* Set the match value */
\r
387 base->MR[matchChannel] = config->matchValue;
\r
388 /* Clear status flags */
\r
389 CTIMER_ClearStatusFlags(base, CTIMER_IR_MR0INT_MASK << matchChannel);
\r
390 /* If interrupt is enabled then enable interrupt and update the call back function */
\r
391 if (config->enableInterrupt)
\r
393 EnableIRQ(s_ctimerIRQ[index]);
\r
397 #if !(defined(FSL_FEATURE_CTIMER_HAS_NO_INPUT_CAPTURE) && (FSL_FEATURE_CTIMER_HAS_NO_INPUT_CAPTURE))
\r
399 * brief Setup the capture.
\r
401 * param base Ctimer peripheral base address
\r
402 * param capture Capture channel to configure
\r
403 * param edge Edge on the channel that will trigger a capture
\r
404 * param enableInt Flag to enable channel interrupts, if enabled then the registered call back
\r
405 * is called upon capture
\r
407 void CTIMER_SetupCapture(CTIMER_Type *base,
\r
408 ctimer_capture_channel_t capture,
\r
409 ctimer_capture_edge_t edge,
\r
412 uint32_t reg = base->CCR;
\r
413 uint32_t index = CTIMER_GetInstance(base);
\r
415 /* Set the capture edge */
\r
416 reg &= ~((CTIMER_CCR_CAP0RE_MASK | CTIMER_CCR_CAP0FE_MASK | CTIMER_CCR_CAP0I_MASK) << (capture * 3));
\r
417 reg |= (uint32_t)edge << (CTIMER_CCR_CAP0RE_SHIFT + (capture * 3));
\r
418 /* Clear status flags */
\r
419 CTIMER_ClearStatusFlags(base, (kCTIMER_Capture0Flag << capture));
\r
420 /* If call back function is valid then enable capture interrupt for the channel and update the call back function */
\r
423 reg |= CTIMER_CCR_CAP0I_MASK << (capture * 3);
\r
424 EnableIRQ(s_ctimerIRQ[index]);
\r
431 * brief Register callback.
\r
433 * param base Ctimer peripheral base address
\r
434 * param cb_func callback function
\r
435 * param cb_type callback function type, singular or multiple
\r
437 void CTIMER_RegisterCallBack(CTIMER_Type *base, ctimer_callback_t *cb_func, ctimer_callback_type_t cb_type)
\r
439 uint32_t index = CTIMER_GetInstance(base);
\r
440 s_ctimerCallback[index] = cb_func;
\r
441 ctimerCallbackType[index] = cb_type;
\r
444 void CTIMER_GenericIRQHandler(uint32_t index)
\r
446 uint32_t int_stat, i, mask;
\r
447 /* Get Interrupt status flags */
\r
448 int_stat = CTIMER_GetStatusFlags(s_ctimerBases[index]);
\r
449 /* Clear the status flags that were set */
\r
450 CTIMER_ClearStatusFlags(s_ctimerBases[index], int_stat);
\r
451 if (ctimerCallbackType[index] == kCTIMER_SingleCallback)
\r
453 if (s_ctimerCallback[index][0])
\r
455 s_ctimerCallback[index][0](int_stat);
\r
460 #if defined(FSL_FEATURE_CTIMER_HAS_NO_INPUT_CAPTURE) && FSL_FEATURE_CTIMER_HAS_NO_INPUT_CAPTURE
\r
461 for (i = 0; i <= CTIMER_IR_MR3INT_SHIFT; i++)
\r
463 #if defined(FSL_FEATURE_CTIMER_HAS_IR_CR3INT) && FSL_FEATURE_CTIMER_HAS_IR_CR3INT
\r
464 for (i = 0; i <= CTIMER_IR_CR3INT_SHIFT; i++)
\r
466 for (i = 0; i <= CTIMER_IR_CR2INT_SHIFT; i++)
\r
467 #endif /* FSL_FEATURE_CTIMER_HAS_IR_CR3INT */
\r
471 /* For each status flag bit that was set call the callback function if it is valid */
\r
472 if ((int_stat & mask) && (s_ctimerCallback[index][i]))
\r
474 s_ctimerCallback[index][i](int_stat);
\r
478 /* Add for ARM errata 838869, affects Cortex-M4, Cortex-M4F Store immediate overlapping
\r
479 exception return operation might vector to incorrect interrupt */
\r
480 #if defined __CORTEX_M && (__CORTEX_M == 4U)
\r
485 /* IRQ handler functions overloading weak symbols in the startup */
\r
486 #if defined(CTIMER0)
\r
487 void CTIMER0_DriverIRQHandler(void)
\r
489 CTIMER_GenericIRQHandler(0);
\r
490 /* Add for ARM errata 838869, affects Cortex-M4, Cortex-M4F Store immediate overlapping
\r
491 exception return operation might vector to incorrect interrupt */
\r
492 #if defined __CORTEX_M && (__CORTEX_M == 4U)
\r
498 #if defined(CTIMER1)
\r
499 void CTIMER1_DriverIRQHandler(void)
\r
501 CTIMER_GenericIRQHandler(1);
\r
502 /* Add for ARM errata 838869, affects Cortex-M4, Cortex-M4F Store immediate overlapping
\r
503 exception return operation might vector to incorrect interrupt */
\r
504 #if defined __CORTEX_M && (__CORTEX_M == 4U)
\r
510 #if defined(CTIMER2)
\r
511 void CTIMER2_DriverIRQHandler(void)
\r
513 CTIMER_GenericIRQHandler(2);
\r
514 /* Add for ARM errata 838869, affects Cortex-M4, Cortex-M4F Store immediate overlapping
\r
515 exception return operation might vector to incorrect interrupt */
\r
516 #if defined __CORTEX_M && (__CORTEX_M == 4U)
\r
522 #if defined(CTIMER3)
\r
523 void CTIMER3_DriverIRQHandler(void)
\r
525 CTIMER_GenericIRQHandler(3);
\r
526 /* Add for ARM errata 838869, affects Cortex-M4, Cortex-M4F Store immediate overlapping
\r
527 exception return operation might vector to incorrect interrupt */
\r
528 #if defined __CORTEX_M && (__CORTEX_M == 4U)
\r
534 #if defined(CTIMER4)
\r
535 void CTIMER4_DriverIRQHandler(void)
\r
537 CTIMER_GenericIRQHandler(4);
\r
538 /* Add for ARM errata 838869, affects Cortex-M4, Cortex-M4F Store immediate overlapping
\r
539 exception return operation might vector to incorrect interrupt */
\r
540 #if defined __CORTEX_M && (__CORTEX_M == 4U)
\r