/* | |
* Copyright (c) 2016, Freescale Semiconductor, Inc. | |
* Copyright 2016-2018 NXP | |
* All rights reserved. | |
* | |
* SPDX-License-Identifier: BSD-3-Clause | |
*/ | |
#include "fsl_ctimer.h" | |
/* Component ID definition, used by tools. */ | |
#ifndef FSL_COMPONENT_ID | |
#define FSL_COMPONENT_ID "platform.drivers.ctimer" | |
#endif | |
/******************************************************************************* | |
* Prototypes | |
******************************************************************************/ | |
/*! | |
* @brief Gets the instance from the base address | |
* | |
* @param base Ctimer peripheral base address | |
* | |
* @return The Timer instance | |
*/ | |
static uint32_t CTIMER_GetInstance(CTIMER_Type *base); | |
/******************************************************************************* | |
* Variables | |
******************************************************************************/ | |
/*! @brief Pointers to Timer bases for each instance. */ | |
static CTIMER_Type *const s_ctimerBases[] = CTIMER_BASE_PTRS; | |
#if !(defined(FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) | |
/*! @brief Pointers to Timer clocks for each instance. */ | |
static const clock_ip_name_t s_ctimerClocks[] = CTIMER_CLOCKS; | |
#endif /* FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL */ | |
#if !(defined(FSL_FEATURE_CTIMER_HAS_NO_RESET) && (FSL_FEATURE_CTIMER_HAS_NO_RESET)) | |
#if !(defined(FSL_SDK_DISABLE_DRIVER_RESET_CONTROL) && FSL_SDK_DISABLE_DRIVER_RESET_CONTROL) | |
#if defined(FSL_FEATURE_CTIMER_WRITE_ZERO_ASSERT_RESET) && FSL_FEATURE_CTIMER_WRITE_ZERO_ASSERT_RESET | |
/*! @brief Pointers to Timer resets for each instance, writing a zero asserts the reset */ | |
static const reset_ip_name_t s_ctimerResets[] = CTIMER_RSTS_N; | |
#else | |
/*! @brief Pointers to Timer resets for each instance, writing a one asserts the reset */ | |
static const reset_ip_name_t s_ctimerResets[] = CTIMER_RSTS; | |
#endif | |
#endif | |
#endif /* FSL_SDK_DISABLE_DRIVER_RESET_CONTROL */ | |
/*! @brief Pointers real ISRs installed by drivers for each instance. */ | |
static ctimer_callback_t *s_ctimerCallback[FSL_FEATURE_SOC_CTIMER_COUNT] = {0}; | |
/*! @brief Callback type installed by drivers for each instance. */ | |
static ctimer_callback_type_t ctimerCallbackType[FSL_FEATURE_SOC_CTIMER_COUNT] = {kCTIMER_SingleCallback}; | |
/*! @brief Array to map timer instance to IRQ number. */ | |
static const IRQn_Type s_ctimerIRQ[] = CTIMER_IRQS; | |
/******************************************************************************* | |
* Code | |
******************************************************************************/ | |
static uint32_t CTIMER_GetInstance(CTIMER_Type *base) | |
{ | |
uint32_t instance; | |
uint32_t ctimerArrayCount = (sizeof(s_ctimerBases) / sizeof(s_ctimerBases[0])); | |
/* Find the instance index from base address mappings. */ | |
for (instance = 0; instance < ctimerArrayCount; instance++) | |
{ | |
if (s_ctimerBases[instance] == base) | |
{ | |
break; | |
} | |
} | |
assert(instance < ctimerArrayCount); | |
return instance; | |
} | |
/*! | |
* brief Ungates the clock and configures the peripheral for basic operation. | |
* | |
* note This API should be called at the beginning of the application before using the driver. | |
* | |
* param base Ctimer peripheral base address | |
* param config Pointer to the user configuration structure. | |
*/ | |
void CTIMER_Init(CTIMER_Type *base, const ctimer_config_t *config) | |
{ | |
assert(config); | |
#if !(defined(FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) | |
/* Enable the timer clock*/ | |
CLOCK_EnableClock(s_ctimerClocks[CTIMER_GetInstance(base)]); | |
#endif /* FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL */ | |
#if !(defined(FSL_SDK_DISABLE_DRIVER_RESET_CONTROL) && FSL_SDK_DISABLE_DRIVER_RESET_CONTROL) | |
/* Reset the module. */ | |
#if !(defined(FSL_FEATURE_CTIMER_HAS_NO_RESET) && (FSL_FEATURE_CTIMER_HAS_NO_RESET)) | |
RESET_PeripheralReset(s_ctimerResets[CTIMER_GetInstance(base)]); | |
#endif | |
#endif /* FSL_SDK_DISABLE_DRIVER_RESET_CONTROL */ | |
/* Setup the cimer mode and count select */ | |
#if !(defined(FSL_FEATURE_CTIMER_HAS_NO_INPUT_CAPTURE) && (FSL_FEATURE_CTIMER_HAS_NO_INPUT_CAPTURE)) | |
base->CTCR = CTIMER_CTCR_CTMODE(config->mode) | CTIMER_CTCR_CINSEL(config->input); | |
#endif | |
/* Setup the timer prescale value */ | |
base->PR = CTIMER_PR_PRVAL(config->prescale); | |
} | |
/*! | |
* brief Gates the timer clock. | |
* | |
* param base Ctimer peripheral base address | |
*/ | |
void CTIMER_Deinit(CTIMER_Type *base) | |
{ | |
uint32_t index = CTIMER_GetInstance(base); | |
/* Stop the timer */ | |
base->TCR &= ~CTIMER_TCR_CEN_MASK; | |
#if !(defined(FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) | |
/* Disable the timer clock*/ | |
CLOCK_DisableClock(s_ctimerClocks[index]); | |
#endif /* FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL */ | |
/* Disable IRQ at NVIC Level */ | |
DisableIRQ(s_ctimerIRQ[index]); | |
} | |
/*! | |
* brief Fills in the timers configuration structure with the default settings. | |
* | |
* The default values are: | |
* code | |
* config->mode = kCTIMER_TimerMode; | |
* config->input = kCTIMER_Capture_0; | |
* config->prescale = 0; | |
* endcode | |
* param config Pointer to the user configuration structure. | |
*/ | |
void CTIMER_GetDefaultConfig(ctimer_config_t *config) | |
{ | |
assert(config); | |
/* Initializes the configure structure to zero. */ | |
memset(config, 0, sizeof(*config)); | |
/* Run as a timer */ | |
config->mode = kCTIMER_TimerMode; | |
/* This field is ignored when mode is timer */ | |
config->input = kCTIMER_Capture_0; | |
/* Timer counter is incremented on every APB bus clock */ | |
config->prescale = 0; | |
} | |
/*! | |
* brief Configures the PWM signal parameters. | |
* | |
* Enables PWM mode on the match channel passed in and will then setup the match value | |
* and other match parameters to generate a PWM signal. | |
* This function will assign match channel 3 to set the PWM cycle. | |
* | |
* note When setting PWM output from multiple output pins, all should use the same PWM | |
* frequency. Please use CTIMER_SetupPwmPeriod to set up the PWM with high resolution. | |
* | |
* param base Ctimer peripheral base address | |
* param matchChannel Match pin to be used to output the PWM signal | |
* param dutyCyclePercent PWM pulse width; the value should be between 0 to 100 | |
* param pwmFreq_Hz PWM signal frequency in Hz | |
* param srcClock_Hz Timer counter clock in Hz | |
* param enableInt Enable interrupt when the timer value reaches the match value of the PWM pulse, | |
* if it is 0 then no interrupt is generated | |
* | |
* return kStatus_Success on success | |
* kStatus_Fail If matchChannel passed in is 3; this channel is reserved to set the PWM cycle | |
*/ | |
status_t CTIMER_SetupPwm(CTIMER_Type *base, | |
ctimer_match_t matchChannel, | |
uint8_t dutyCyclePercent, | |
uint32_t pwmFreq_Hz, | |
uint32_t srcClock_Hz, | |
bool enableInt) | |
{ | |
assert(pwmFreq_Hz > 0); | |
uint32_t reg; | |
uint32_t period, pulsePeriod = 0; | |
uint32_t timerClock = srcClock_Hz / (base->PR + 1); | |
uint32_t index = CTIMER_GetInstance(base); | |
if (matchChannel == kCTIMER_Match_3) | |
{ | |
return kStatus_Fail; | |
} | |
/* Enable PWM mode on the channel */ | |
base->PWMC |= (1U << matchChannel); | |
/* Clear the stop, reset and interrupt bits for this channel */ | |
reg = base->MCR; | |
reg &= ~((CTIMER_MCR_MR0R_MASK | CTIMER_MCR_MR0S_MASK | CTIMER_MCR_MR0I_MASK) << (matchChannel * 3)); | |
/* If call back function is valid then enable match interrupt for the channel */ | |
if (enableInt) | |
{ | |
reg |= (CTIMER_MCR_MR0I_MASK << (CTIMER_MCR_MR0I_SHIFT + (matchChannel * 3))); | |
} | |
/* Reset the counter when match on channel 3 */ | |
reg |= CTIMER_MCR_MR3R_MASK; | |
base->MCR = reg; | |
/* Calculate PWM period match value */ | |
period = (timerClock / pwmFreq_Hz) - 1; | |
/* Calculate pulse width match value */ | |
if (dutyCyclePercent == 0) | |
{ | |
pulsePeriod = period + 1; | |
} | |
else | |
{ | |
pulsePeriod = (period * (100 - dutyCyclePercent)) / 100; | |
} | |
/* Match on channel 3 will define the PWM period */ | |
base->MR[kCTIMER_Match_3] = period; | |
/* This will define the PWM pulse period */ | |
base->MR[matchChannel] = pulsePeriod; | |
/* Clear status flags */ | |
CTIMER_ClearStatusFlags(base, CTIMER_IR_MR0INT_MASK << matchChannel); | |
/* If call back function is valid then enable interrupt and update the call back function */ | |
if (enableInt) | |
{ | |
EnableIRQ(s_ctimerIRQ[index]); | |
} | |
return kStatus_Success; | |
} | |
/*! | |
* brief Configures the PWM signal parameters. | |
* | |
* Enables PWM mode on the match channel passed in and will then setup the match value | |
* and other match parameters to generate a PWM signal. | |
* This function will assign match channel 3 to set the PWM cycle. | |
* | |
* note When setting PWM output from multiple output pins, all should use the same PWM | |
* period | |
* | |
* param base Ctimer peripheral base address | |
* param matchChannel Match pin to be used to output the PWM signal | |
* param pwmPeriod PWM period match value | |
* param pulsePeriod Pulse width match value | |
* param enableInt Enable interrupt when the timer value reaches the match value of the PWM pulse, | |
* if it is 0 then no interrupt is generated | |
* | |
* return kStatus_Success on success | |
* kStatus_Fail If matchChannel passed in is 3; this channel is reserved to set the PWM period | |
*/ | |
status_t CTIMER_SetupPwmPeriod( | |
CTIMER_Type *base, ctimer_match_t matchChannel, uint32_t pwmPeriod, uint32_t pulsePeriod, bool enableInt) | |
{ | |
/* Some CTimers only have 16bits , so the value is limited*/ | |
#if defined(FSL_FEATURE_SOC_CTIMER16B) && FSL_FEATURE_SOC_CTIMER16B | |
assert(!((FSL_FEATURE_CTIMER_BIT_SIZEn(base) < 32) && (pulsePeriod > 0xFFFFU))); | |
#endif | |
uint32_t reg; | |
uint32_t index = CTIMER_GetInstance(base); | |
if (matchChannel == kCTIMER_Match_3) | |
{ | |
return kStatus_Fail; | |
} | |
/* Enable PWM mode on the channel */ | |
base->PWMC |= (1U << matchChannel); | |
/* Clear the stop, reset and interrupt bits for this channel */ | |
reg = base->MCR; | |
reg &= ~((CTIMER_MCR_MR0R_MASK | CTIMER_MCR_MR0S_MASK | CTIMER_MCR_MR0I_MASK) << (matchChannel * 3)); | |
/* If call back function is valid then enable match interrupt for the channel */ | |
if (enableInt) | |
{ | |
reg |= (CTIMER_MCR_MR0I_MASK << (CTIMER_MCR_MR0I_SHIFT + (matchChannel * 3))); | |
} | |
/* Reset the counter when match on channel 3 */ | |
reg |= CTIMER_MCR_MR3R_MASK; | |
base->MCR = reg; | |
/* Match on channel 3 will define the PWM period */ | |
base->MR[kCTIMER_Match_3] = pwmPeriod; | |
/* This will define the PWM pulse period */ | |
base->MR[matchChannel] = pulsePeriod; | |
/* Clear status flags */ | |
CTIMER_ClearStatusFlags(base, CTIMER_IR_MR0INT_MASK << matchChannel); | |
/* If call back function is valid then enable interrupt and update the call back function */ | |
if (enableInt) | |
{ | |
EnableIRQ(s_ctimerIRQ[index]); | |
} | |
return kStatus_Success; | |
} | |
/*! | |
* brief Updates the duty cycle of an active PWM signal. | |
* | |
* note Please use CTIMER_UpdatePwmPulsePeriod to update the PWM with high resolution. | |
* | |
* param base Ctimer peripheral base address | |
* param matchChannel Match pin to be used to output the PWM signal | |
* param dutyCyclePercent New PWM pulse width; the value should be between 0 to 100 | |
*/ | |
void CTIMER_UpdatePwmDutycycle(CTIMER_Type *base, ctimer_match_t matchChannel, uint8_t dutyCyclePercent) | |
{ | |
uint32_t pulsePeriod = 0, period; | |
/* Match channel 3 defines the PWM period */ | |
period = base->MR[kCTIMER_Match_3]; | |
/* Calculate pulse width match value */ | |
pulsePeriod = (period * dutyCyclePercent) / 100; | |
/* For 0% dutycyle, make pulse period greater than period so the event will never occur */ | |
if (dutyCyclePercent == 0) | |
{ | |
pulsePeriod = period + 1; | |
} | |
else | |
{ | |
pulsePeriod = (period * (100 - dutyCyclePercent)) / 100; | |
} | |
/* Update dutycycle */ | |
base->MR[matchChannel] = pulsePeriod; | |
} | |
/*! | |
* brief Setup the match register. | |
* | |
* User configuration is used to setup the match value and action to be taken when a match occurs. | |
* | |
* param base Ctimer peripheral base address | |
* param matchChannel Match register to configure | |
* param config Pointer to the match configuration structure | |
*/ | |
void CTIMER_SetupMatch(CTIMER_Type *base, ctimer_match_t matchChannel, const ctimer_match_config_t *config) | |
{ | |
/* Some CTimers only have 16bits , so the value is limited*/ | |
#if defined(FSL_FEATURE_SOC_CTIMER16B) && FSL_FEATURE_SOC_CTIMER16B | |
assert(!(FSL_FEATURE_CTIMER_BIT_SIZEn(base) < 32 && config->matchValue > 0xFFFFU)); | |
#endif | |
uint32_t reg; | |
uint32_t index = CTIMER_GetInstance(base); | |
/* Set the counter operation when a match on this channel occurs */ | |
reg = base->MCR; | |
reg &= ~((CTIMER_MCR_MR0R_MASK | CTIMER_MCR_MR0S_MASK | CTIMER_MCR_MR0I_MASK) << (matchChannel * 3)); | |
reg |= (uint32_t)((uint32_t)(config->enableCounterReset) << (CTIMER_MCR_MR0R_SHIFT + (matchChannel * 3))); | |
reg |= (uint32_t)((uint32_t)(config->enableCounterStop) << (CTIMER_MCR_MR0S_SHIFT + (matchChannel * 3))); | |
reg |= (uint32_t)((uint32_t)(config->enableInterrupt) << (CTIMER_MCR_MR0I_SHIFT + (matchChannel * 3))); | |
base->MCR = reg; | |
reg = base->EMR; | |
/* Set the match output operation when a match on this channel occurs */ | |
reg &= ~(CTIMER_EMR_EMC0_MASK << (matchChannel * 2)); | |
reg |= (uint32_t)config->outControl << (CTIMER_EMR_EMC0_SHIFT + (matchChannel * 2)); | |
/* Set the initial state of the EM bit/output */ | |
reg &= ~(CTIMER_EMR_EM0_MASK << matchChannel); | |
reg |= (uint32_t)config->outPinInitState << matchChannel; | |
base->EMR = reg; | |
/* Set the match value */ | |
base->MR[matchChannel] = config->matchValue; | |
/* Clear status flags */ | |
CTIMER_ClearStatusFlags(base, CTIMER_IR_MR0INT_MASK << matchChannel); | |
/* If interrupt is enabled then enable interrupt and update the call back function */ | |
if (config->enableInterrupt) | |
{ | |
EnableIRQ(s_ctimerIRQ[index]); | |
} | |
} | |
#if !(defined(FSL_FEATURE_CTIMER_HAS_NO_INPUT_CAPTURE) && (FSL_FEATURE_CTIMER_HAS_NO_INPUT_CAPTURE)) | |
/*! | |
* brief Setup the capture. | |
* | |
* param base Ctimer peripheral base address | |
* param capture Capture channel to configure | |
* param edge Edge on the channel that will trigger a capture | |
* param enableInt Flag to enable channel interrupts, if enabled then the registered call back | |
* is called upon capture | |
*/ | |
void CTIMER_SetupCapture(CTIMER_Type *base, | |
ctimer_capture_channel_t capture, | |
ctimer_capture_edge_t edge, | |
bool enableInt) | |
{ | |
uint32_t reg = base->CCR; | |
uint32_t index = CTIMER_GetInstance(base); | |
/* Set the capture edge */ | |
reg &= ~((CTIMER_CCR_CAP0RE_MASK | CTIMER_CCR_CAP0FE_MASK | CTIMER_CCR_CAP0I_MASK) << (capture * 3)); | |
reg |= (uint32_t)edge << (CTIMER_CCR_CAP0RE_SHIFT + (capture * 3)); | |
/* Clear status flags */ | |
CTIMER_ClearStatusFlags(base, (kCTIMER_Capture0Flag << capture)); | |
/* If call back function is valid then enable capture interrupt for the channel and update the call back function */ | |
if (enableInt) | |
{ | |
reg |= CTIMER_CCR_CAP0I_MASK << (capture * 3); | |
EnableIRQ(s_ctimerIRQ[index]); | |
} | |
base->CCR = reg; | |
} | |
#endif | |
/*! | |
* brief Register callback. | |
* | |
* param base Ctimer peripheral base address | |
* param cb_func callback function | |
* param cb_type callback function type, singular or multiple | |
*/ | |
void CTIMER_RegisterCallBack(CTIMER_Type *base, ctimer_callback_t *cb_func, ctimer_callback_type_t cb_type) | |
{ | |
uint32_t index = CTIMER_GetInstance(base); | |
s_ctimerCallback[index] = cb_func; | |
ctimerCallbackType[index] = cb_type; | |
} | |
void CTIMER_GenericIRQHandler(uint32_t index) | |
{ | |
uint32_t int_stat, i, mask; | |
/* Get Interrupt status flags */ | |
int_stat = CTIMER_GetStatusFlags(s_ctimerBases[index]); | |
/* Clear the status flags that were set */ | |
CTIMER_ClearStatusFlags(s_ctimerBases[index], int_stat); | |
if (ctimerCallbackType[index] == kCTIMER_SingleCallback) | |
{ | |
if (s_ctimerCallback[index][0]) | |
{ | |
s_ctimerCallback[index][0](int_stat); | |
} | |
} | |
else | |
{ | |
#if defined(FSL_FEATURE_CTIMER_HAS_NO_INPUT_CAPTURE) && FSL_FEATURE_CTIMER_HAS_NO_INPUT_CAPTURE | |
for (i = 0; i <= CTIMER_IR_MR3INT_SHIFT; i++) | |
#else | |
#if defined(FSL_FEATURE_CTIMER_HAS_IR_CR3INT) && FSL_FEATURE_CTIMER_HAS_IR_CR3INT | |
for (i = 0; i <= CTIMER_IR_CR3INT_SHIFT; i++) | |
#else | |
for (i = 0; i <= CTIMER_IR_CR2INT_SHIFT; i++) | |
#endif /* FSL_FEATURE_CTIMER_HAS_IR_CR3INT */ | |
#endif | |
{ | |
mask = 0x01 << i; | |
/* For each status flag bit that was set call the callback function if it is valid */ | |
if ((int_stat & mask) && (s_ctimerCallback[index][i])) | |
{ | |
s_ctimerCallback[index][i](int_stat); | |
} | |
} | |
} | |
/* Add for ARM errata 838869, affects Cortex-M4, Cortex-M4F Store immediate overlapping | |
exception return operation might vector to incorrect interrupt */ | |
#if defined __CORTEX_M && (__CORTEX_M == 4U) | |
__DSB(); | |
#endif | |
} | |
/* IRQ handler functions overloading weak symbols in the startup */ | |
#if defined(CTIMER0) | |
void CTIMER0_DriverIRQHandler(void) | |
{ | |
CTIMER_GenericIRQHandler(0); | |
/* Add for ARM errata 838869, affects Cortex-M4, Cortex-M4F Store immediate overlapping | |
exception return operation might vector to incorrect interrupt */ | |
#if defined __CORTEX_M && (__CORTEX_M == 4U) | |
__DSB(); | |
#endif | |
} | |
#endif | |
#if defined(CTIMER1) | |
void CTIMER1_DriverIRQHandler(void) | |
{ | |
CTIMER_GenericIRQHandler(1); | |
/* Add for ARM errata 838869, affects Cortex-M4, Cortex-M4F Store immediate overlapping | |
exception return operation might vector to incorrect interrupt */ | |
#if defined __CORTEX_M && (__CORTEX_M == 4U) | |
__DSB(); | |
#endif | |
} | |
#endif | |
#if defined(CTIMER2) | |
void CTIMER2_DriverIRQHandler(void) | |
{ | |
CTIMER_GenericIRQHandler(2); | |
/* Add for ARM errata 838869, affects Cortex-M4, Cortex-M4F Store immediate overlapping | |
exception return operation might vector to incorrect interrupt */ | |
#if defined __CORTEX_M && (__CORTEX_M == 4U) | |
__DSB(); | |
#endif | |
} | |
#endif | |
#if defined(CTIMER3) | |
void CTIMER3_DriverIRQHandler(void) | |
{ | |
CTIMER_GenericIRQHandler(3); | |
/* Add for ARM errata 838869, affects Cortex-M4, Cortex-M4F Store immediate overlapping | |
exception return operation might vector to incorrect interrupt */ | |
#if defined __CORTEX_M && (__CORTEX_M == 4U) | |
__DSB(); | |
#endif | |
} | |
#endif | |
#if defined(CTIMER4) | |
void CTIMER4_DriverIRQHandler(void) | |
{ | |
CTIMER_GenericIRQHandler(4); | |
/* Add for ARM errata 838869, affects Cortex-M4, Cortex-M4F Store immediate overlapping | |
exception return operation might vector to incorrect interrupt */ | |
#if defined __CORTEX_M && (__CORTEX_M == 4U) | |
__DSB(); | |
#endif | |
} | |
#endif |