| /* |
| * Copyright (c) 2016, Freescale Semiconductor, Inc. |
| * Copyright 2016-2017 NXP |
| * |
| * Redistribution and use in source and binary forms, with or without modification, |
| * are permitted provided that the following conditions are met: |
| * |
| * o Redistributions of source code must retain the above copyright notice, this list |
| * of conditions and the following disclaimer. |
| * |
| * o Redistributions in binary form must reproduce the above copyright notice, this |
| * list of conditions and the following disclaimer in the documentation and/or |
| * other materials provided with the distribution. |
| * |
| * o Neither the name of the copyright holder nor the names of its |
| * contributors may be used to endorse or promote products derived from this |
| * software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND |
| * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
| * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
| * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR |
| * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
| * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
| * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON |
| * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
| * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| #include "fsl_sctimer.h" |
| |
| /******************************************************************************* |
| * Definitions |
| ******************************************************************************/ |
| /*! @brief Typedef for interrupt handler. */ |
| typedef void (*sctimer_isr_t)(SCT_Type *base); |
| |
| /******************************************************************************* |
| * Prototypes |
| ******************************************************************************/ |
| /*! |
| * @brief Gets the instance from the base address |
| * |
| * @param base SCTimer peripheral base address |
| * |
| * @return The SCTimer instance |
| */ |
| static uint32_t SCTIMER_GetInstance(SCT_Type *base); |
| |
| /******************************************************************************* |
| * Variables |
| ******************************************************************************/ |
| /*! @brief Pointers to SCT bases for each instance. */ |
| static SCT_Type *const s_sctBases[] = SCT_BASE_PTRS; |
| |
| #if !(defined(FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) |
| /*! @brief Pointers to SCT clocks for each instance. */ |
| static const clock_ip_name_t s_sctClocks[] = SCT_CLOCKS; |
| #endif /* FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL */ |
| |
| /*! @brief Pointers to SCT resets for each instance. */ |
| static const reset_ip_name_t s_sctResets[] = SCT_RSTS; |
| |
| /*!< @brief SCTimer event Callback function. */ |
| static sctimer_event_callback_t s_eventCallback[FSL_FEATURE_SCT_NUMBER_OF_EVENTS]; |
| |
| /*!< @brief Keep track of SCTimer event number */ |
| static uint32_t s_currentEvent; |
| |
| /*!< @brief Keep track of SCTimer state number */ |
| static uint32_t s_currentState; |
| |
| /*!< @brief Keep track of SCTimer match/capture register number */ |
| static uint32_t s_currentMatch; |
| |
| /*! @brief Pointer to SCTimer IRQ handler */ |
| static sctimer_isr_t s_sctimerIsr; |
| |
| /******************************************************************************* |
| * Code |
| ******************************************************************************/ |
| static uint32_t SCTIMER_GetInstance(SCT_Type *base) |
| { |
| uint32_t instance; |
| uint32_t sctArrayCount = (sizeof(s_sctBases) / sizeof(s_sctBases[0])); |
| |
| /* Find the instance index from base address mappings. */ |
| for (instance = 0; instance < sctArrayCount; instance++) |
| { |
| if (s_sctBases[instance] == base) |
| { |
| break; |
| } |
| } |
| |
| assert(instance < sctArrayCount); |
| |
| return instance; |
| } |
| |
| status_t SCTIMER_Init(SCT_Type *base, const sctimer_config_t *config) |
| { |
| assert(config); |
| uint32_t i; |
| |
| #if !(defined(FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) |
| /* Enable the SCTimer clock*/ |
| CLOCK_EnableClock(s_sctClocks[SCTIMER_GetInstance(base)]); |
| #endif /* FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL */ |
| |
| /* Reset the module */ |
| RESET_PeripheralReset(s_sctResets[SCTIMER_GetInstance(base)]); |
| |
| /* Setup the counter operation */ |
| base->CONFIG = SCT_CONFIG_CKSEL(config->clockSelect) | SCT_CONFIG_CLKMODE(config->clockMode) | |
| SCT_CONFIG_UNIFY(config->enableCounterUnify); |
| |
| /* Write to the control register, clear the counter and keep the counters halted */ |
| base->CTRL = SCT_CTRL_BIDIR_L(config->enableBidirection_l) | SCT_CTRL_PRE_L(config->prescale_l) | |
| SCT_CTRL_CLRCTR_L_MASK | SCT_CTRL_HALT_L_MASK; |
| |
| if (!(config->enableCounterUnify)) |
| { |
| base->CTRL |= SCT_CTRL_BIDIR_H(config->enableBidirection_h) | SCT_CTRL_PRE_H(config->prescale_h) | |
| SCT_CTRL_CLRCTR_H_MASK | SCT_CTRL_HALT_H_MASK; |
| } |
| |
| /* Initial state of channel output */ |
| base->OUTPUT = config->outInitState; |
| |
| /* Clear the global variables */ |
| s_currentEvent = 0; |
| s_currentState = 0; |
| s_currentMatch = 0; |
| |
| /* Clear the callback array */ |
| for (i = 0; i < FSL_FEATURE_SCT_NUMBER_OF_EVENTS; i++) |
| { |
| s_eventCallback[i] = NULL; |
| } |
| |
| /* Save interrupt handler */ |
| s_sctimerIsr = SCTIMER_EventHandleIRQ; |
| |
| return kStatus_Success; |
| } |
| |
| void SCTIMER_Deinit(SCT_Type *base) |
| { |
| /* Halt the counters */ |
| base->CTRL |= (SCT_CTRL_HALT_L_MASK | SCT_CTRL_HALT_H_MASK); |
| |
| #if !(defined(FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) |
| /* Disable the SCTimer clock*/ |
| CLOCK_DisableClock(s_sctClocks[SCTIMER_GetInstance(base)]); |
| #endif /* FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL */ |
| } |
| |
| void SCTIMER_GetDefaultConfig(sctimer_config_t *config) |
| { |
| assert(config); |
| |
| /* SCT operates as a unified 32-bit counter */ |
| config->enableCounterUnify = true; |
| /* System clock clocks the entire SCT module */ |
| config->clockMode = kSCTIMER_System_ClockMode; |
| /* This is used only by certain clock modes */ |
| config->clockSelect = kSCTIMER_Clock_On_Rise_Input_0; |
| /* Up count mode only for the unified counter */ |
| config->enableBidirection_l = false; |
| /* Up count mode only for Counte_H */ |
| config->enableBidirection_h = false; |
| /* Prescale factor of 1 */ |
| config->prescale_l = 0; |
| /* Prescale factor of 1 for Counter_H*/ |
| config->prescale_h = 0; |
| /* Clear outputs */ |
| config->outInitState = 0; |
| } |
| |
| status_t SCTIMER_SetupPwm(SCT_Type *base, |
| const sctimer_pwm_signal_param_t *pwmParams, |
| sctimer_pwm_mode_t mode, |
| uint32_t pwmFreq_Hz, |
| uint32_t srcClock_Hz, |
| uint32_t *event) |
| { |
| assert(pwmParams); |
| assert(srcClock_Hz); |
| assert(pwmFreq_Hz); |
| |
| uint32_t period, pulsePeriod = 0; |
| uint32_t sctClock = srcClock_Hz / (((base->CTRL & SCT_CTRL_PRE_L_MASK) >> SCT_CTRL_PRE_L_SHIFT) + 1); |
| uint32_t periodEvent, pulseEvent; |
| uint32_t reg; |
| |
| /* This function will create 2 events, return an error if we do not have enough events available */ |
| if ((s_currentEvent + 2) > FSL_FEATURE_SCT_NUMBER_OF_EVENTS) |
| { |
| return kStatus_Fail; |
| } |
| |
| if (pwmParams->dutyCyclePercent == 0) |
| { |
| return kStatus_Fail; |
| } |
| |
| /* Set unify bit to operate in 32-bit counter mode */ |
| base->CONFIG |= SCT_CONFIG_UNIFY_MASK; |
| |
| /* Use bi-directional mode for center-aligned PWM */ |
| if (mode == kSCTIMER_CenterAlignedPwm) |
| { |
| base->CTRL |= SCT_CTRL_BIDIR_L_MASK; |
| } |
| |
| /* Calculate PWM period match value */ |
| if (mode == kSCTIMER_EdgeAlignedPwm) |
| { |
| period = (sctClock / pwmFreq_Hz) - 1; |
| } |
| else |
| { |
| period = sctClock / (pwmFreq_Hz * 2); |
| } |
| |
| /* Calculate pulse width match value */ |
| pulsePeriod = (period * pwmParams->dutyCyclePercent) / 100; |
| |
| /* For 100% dutycyle, make pulse period greater than period so the event will never occur */ |
| if (pwmParams->dutyCyclePercent >= 100) |
| { |
| pulsePeriod = period + 2; |
| } |
| |
| /* Schedule an event when we reach the PWM period */ |
| SCTIMER_CreateAndScheduleEvent(base, kSCTIMER_MatchEventOnly, period, 0, kSCTIMER_Counter_L, &periodEvent); |
| |
| /* Schedule an event when we reach the pulse width */ |
| SCTIMER_CreateAndScheduleEvent(base, kSCTIMER_MatchEventOnly, pulsePeriod, 0, kSCTIMER_Counter_L, &pulseEvent); |
| |
| /* Reset the counter when we reach the PWM period */ |
| SCTIMER_SetupCounterLimitAction(base, kSCTIMER_Counter_L, periodEvent); |
| |
| /* Return the period event to the user */ |
| *event = periodEvent; |
| |
| /* For high-true level */ |
| if (pwmParams->level == kSCTIMER_HighTrue) |
| { |
| /* Set the initial output level to low which is the inactive state */ |
| base->OUTPUT &= ~(1U << pwmParams->output); |
| |
| if (mode == kSCTIMER_EdgeAlignedPwm) |
| { |
| /* Set the output when we reach the PWM period */ |
| SCTIMER_SetupOutputSetAction(base, pwmParams->output, periodEvent); |
| /* Clear the output when we reach the PWM pulse value */ |
| SCTIMER_SetupOutputClearAction(base, pwmParams->output, pulseEvent); |
| } |
| else |
| { |
| /* Clear the output when we reach the PWM pulse event */ |
| SCTIMER_SetupOutputClearAction(base, pwmParams->output, pulseEvent); |
| /* Reverse output when down counting */ |
| reg = base->OUTPUTDIRCTRL; |
| reg &= ~(SCT_OUTPUTDIRCTRL_SETCLR0_MASK << (2 * pwmParams->output)); |
| reg |= (1U << (2 * pwmParams->output)); |
| base->OUTPUTDIRCTRL = reg; |
| } |
| } |
| /* For low-true level */ |
| else |
| { |
| /* Set the initial output level to high which is the inactive state */ |
| base->OUTPUT |= (1U << pwmParams->output); |
| |
| if (mode == kSCTIMER_EdgeAlignedPwm) |
| { |
| /* Clear the output when we reach the PWM period */ |
| SCTIMER_SetupOutputClearAction(base, pwmParams->output, periodEvent); |
| /* Set the output when we reach the PWM pulse value */ |
| SCTIMER_SetupOutputSetAction(base, pwmParams->output, pulseEvent); |
| } |
| else |
| { |
| /* Set the output when we reach the PWM pulse event */ |
| SCTIMER_SetupOutputSetAction(base, pwmParams->output, pulseEvent); |
| /* Reverse output when down counting */ |
| reg = base->OUTPUTDIRCTRL; |
| reg &= ~(SCT_OUTPUTDIRCTRL_SETCLR0_MASK << (2 * pwmParams->output)); |
| reg |= (1U << (2 * pwmParams->output)); |
| base->OUTPUTDIRCTRL = reg; |
| } |
| } |
| |
| return kStatus_Success; |
| } |
| |
| void SCTIMER_UpdatePwmDutycycle(SCT_Type *base, sctimer_out_t output, uint8_t dutyCyclePercent, uint32_t event) |
| |
| { |
| assert(dutyCyclePercent > 0); |
| |
| uint32_t periodMatchReg, pulseMatchReg; |
| uint32_t pulsePeriod = 0, period; |
| |
| /* Retrieve the match register number for the PWM period */ |
| periodMatchReg = base->EVENT[event].CTRL & SCT_EVENT_CTRL_MATCHSEL_MASK; |
| |
| /* Retrieve the match register number for the PWM pulse period */ |
| pulseMatchReg = base->EVENT[event + 1].CTRL & SCT_EVENT_CTRL_MATCHSEL_MASK; |
| |
| period = base->SCTMATCH[periodMatchReg]; |
| |
| /* Calculate pulse width match value */ |
| pulsePeriod = (period * dutyCyclePercent) / 100; |
| |
| /* For 100% dutycyle, make pulse period greater than period so the event will never occur */ |
| if (dutyCyclePercent >= 100) |
| { |
| pulsePeriod = period + 2; |
| } |
| |
| /* Stop the counter before updating match register */ |
| SCTIMER_StopTimer(base, kSCTIMER_Counter_L); |
| |
| /* Update dutycycle */ |
| base->SCTMATCH[pulseMatchReg] = SCT_SCTMATCH_MATCHn_L(pulsePeriod); |
| base->SCTMATCHREL[pulseMatchReg] = SCT_SCTMATCHREL_RELOADn_L(pulsePeriod); |
| |
| /* Restart the counter */ |
| SCTIMER_StartTimer(base, kSCTIMER_Counter_L); |
| } |
| |
| status_t SCTIMER_CreateAndScheduleEvent(SCT_Type *base, |
| sctimer_event_t howToMonitor, |
| uint32_t matchValue, |
| uint32_t whichIO, |
| sctimer_counter_t whichCounter, |
| uint32_t *event) |
| { |
| uint32_t combMode = (((uint32_t)howToMonitor & SCT_EVENT_CTRL_COMBMODE_MASK) >> SCT_EVENT_CTRL_COMBMODE_SHIFT); |
| uint32_t currentCtrlVal = howToMonitor; |
| |
| /* Return an error if we have hit the limit in terms of number of events created */ |
| if (s_currentEvent >= FSL_FEATURE_SCT_NUMBER_OF_EVENTS) |
| { |
| return kStatus_Fail; |
| } |
| |
| /* IO only mode */ |
| if (combMode == 0x2U) |
| { |
| base->EVENT[s_currentEvent].CTRL = currentCtrlVal | SCT_EVENT_CTRL_IOSEL(whichIO); |
| } |
| /* Match mode only */ |
| else if (combMode == 0x1U) |
| { |
| /* Return an error if we have hit the limit in terms of number of number of match registers */ |
| if (s_currentMatch >= FSL_FEATURE_SCT_NUMBER_OF_MATCH_CAPTURE) |
| { |
| return kStatus_Fail; |
| } |
| |
| currentCtrlVal |= SCT_EVENT_CTRL_MATCHSEL(s_currentMatch); |
| /* Use Counter_L bits if counter is operating in 32-bit mode or user wants to setup the L counter */ |
| if ((base->CONFIG & SCT_CONFIG_UNIFY_MASK) || (whichCounter == kSCTIMER_Counter_L)) |
| { |
| base->SCTMATCH[s_currentMatch] = SCT_SCTMATCH_MATCHn_L(matchValue); |
| base->SCTMATCHREL[s_currentMatch] = SCT_SCTMATCHREL_RELOADn_L(matchValue); |
| } |
| else |
| { |
| /* Select the counter, no need for this if operating in 32-bit mode */ |
| currentCtrlVal |= SCT_EVENT_CTRL_HEVENT(whichCounter); |
| base->SCTMATCH[s_currentMatch] = SCT_SCTMATCH_MATCHn_H(matchValue); |
| base->SCTMATCHREL[s_currentMatch] = SCT_SCTMATCHREL_RELOADn_H(matchValue); |
| } |
| base->EVENT[s_currentEvent].CTRL = currentCtrlVal; |
| /* Increment the match register number */ |
| s_currentMatch++; |
| } |
| /* Use both Match & IO */ |
| else |
| { |
| /* Return an error if we have hit the limit in terms of number of number of match registers */ |
| if (s_currentMatch >= FSL_FEATURE_SCT_NUMBER_OF_MATCH_CAPTURE) |
| { |
| return kStatus_Fail; |
| } |
| |
| currentCtrlVal |= SCT_EVENT_CTRL_MATCHSEL(s_currentMatch) | SCT_EVENT_CTRL_IOSEL(whichIO); |
| /* Use Counter_L bits if counter is operating in 32-bit mode or user wants to setup the L counter */ |
| if ((base->CONFIG & SCT_CONFIG_UNIFY_MASK) || (whichCounter == kSCTIMER_Counter_L)) |
| { |
| base->SCTMATCH[s_currentMatch] = SCT_SCTMATCH_MATCHn_L(matchValue); |
| base->SCTMATCHREL[s_currentMatch] = SCT_SCTMATCHREL_RELOADn_L(matchValue); |
| } |
| else |
| { |
| /* Select the counter, no need for this if operating in 32-bit mode */ |
| currentCtrlVal |= SCT_EVENT_CTRL_HEVENT(whichCounter); |
| base->SCTMATCH[s_currentMatch] = SCT_SCTMATCH_MATCHn_H(matchValue); |
| base->SCTMATCHREL[s_currentMatch] = SCT_SCTMATCHREL_RELOADn_H(matchValue); |
| } |
| base->EVENT[s_currentEvent].CTRL = currentCtrlVal; |
| /* Increment the match register number */ |
| s_currentMatch++; |
| } |
| |
| /* Enable the event in the current state */ |
| base->EVENT[s_currentEvent].STATE = (1U << s_currentState); |
| |
| /* Return the event number */ |
| *event = s_currentEvent; |
| |
| /* Increment the event number */ |
| s_currentEvent++; |
| |
| return kStatus_Success; |
| } |
| |
| void SCTIMER_ScheduleEvent(SCT_Type *base, uint32_t event) |
| { |
| /* Enable event in the current state */ |
| base->EVENT[event].STATE |= (1U << s_currentState); |
| } |
| |
| status_t SCTIMER_IncreaseState(SCT_Type *base) |
| { |
| /* Return an error if we have hit the limit in terms of states used */ |
| if (s_currentState >= FSL_FEATURE_SCT_NUMBER_OF_STATES) |
| { |
| return kStatus_Fail; |
| } |
| |
| s_currentState++; |
| |
| return kStatus_Success; |
| } |
| |
| uint32_t SCTIMER_GetCurrentState(SCT_Type *base) |
| { |
| return s_currentState; |
| } |
| |
| void SCTIMER_SetupOutputToggleAction(SCT_Type *base, uint32_t whichIO, uint32_t event) |
| { |
| uint32_t reg; |
| |
| /* Set the same event to set and clear the output */ |
| base->OUT[whichIO].CLR |= (1U << event); |
| base->OUT[whichIO].SET |= (1U << event); |
| |
| /* Set the conflict resolution to toggle output */ |
| reg = base->RES; |
| reg &= ~(SCT_RES_O0RES_MASK << (2 * whichIO)); |
| reg |= (uint32_t)(kSCTIMER_ResolveToggle << (2 * whichIO)); |
| base->RES = reg; |
| } |
| |
| status_t SCTIMER_SetupCaptureAction(SCT_Type *base, |
| sctimer_counter_t whichCounter, |
| uint32_t *captureRegister, |
| uint32_t event) |
| { |
| /* Return an error if we have hit the limit in terms of number of capture/match registers used */ |
| if (s_currentMatch >= FSL_FEATURE_SCT_NUMBER_OF_MATCH_CAPTURE) |
| { |
| return kStatus_Fail; |
| } |
| |
| /* Use Counter_L bits if counter is operating in 32-bit mode or user wants to setup the L counter */ |
| if ((base->CONFIG & SCT_CONFIG_UNIFY_MASK) || (whichCounter == kSCTIMER_Counter_L)) |
| { |
| /* Set the bit to enable event */ |
| base->SCTCAPCTRL[s_currentMatch] |= SCT_SCTCAPCTRL_CAPCONn_L(1 << event); |
| |
| /* Set this resource to be a capture rather than match */ |
| base->REGMODE |= SCT_REGMODE_REGMOD_L(1 << s_currentMatch); |
| } |
| else |
| { |
| /* Set bit to enable event */ |
| base->SCTCAPCTRL[s_currentMatch] |= SCT_SCTCAPCTRL_CAPCONn_H(1 << event); |
| |
| /* Set this resource to be a capture rather than match */ |
| base->REGMODE |= SCT_REGMODE_REGMOD_H(1 << s_currentMatch); |
| } |
| |
| /* Return the match register number */ |
| *captureRegister = s_currentMatch; |
| |
| /* Increase the match register number */ |
| s_currentMatch++; |
| |
| return kStatus_Success; |
| } |
| |
| void SCTIMER_SetCallback(SCT_Type *base, sctimer_event_callback_t callback, uint32_t event) |
| { |
| s_eventCallback[event] = callback; |
| } |
| |
| void SCTIMER_EventHandleIRQ(SCT_Type *base) |
| { |
| uint32_t eventFlag = SCT0->EVFLAG; |
| /* Only clear the flags whose interrupt field is enabled */ |
| uint32_t clearFlag = (eventFlag & SCT0->EVEN); |
| uint32_t mask = eventFlag; |
| int i = 0; |
| |
| /* Invoke the callback for certain events */ |
| for (i = 0; (i < FSL_FEATURE_SCT_NUMBER_OF_EVENTS) && (mask != 0); i++) |
| { |
| if (mask & 0x1) |
| { |
| if (s_eventCallback[i] != NULL) |
| { |
| s_eventCallback[i](); |
| } |
| } |
| mask >>= 1; |
| } |
| |
| /* Clear event interrupt flag */ |
| SCT0->EVFLAG = clearFlag; |
| } |
| |
| void SCT0_IRQHandler(void) |
| { |
| s_sctimerIsr(SCT0); |
| } |