123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559 |
- /*
- * The Clear BSD License
- * Copyright (c) 2016, Freescale Semiconductor, Inc.
- * Copyright 2016-2017 NXP
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without modification,
- * are permitted (subject to the limitations in the disclaimer below) 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.
- *
- * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY THIS LICENSE.
- * 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
- ******************************************************************************/
- /* Component ID definition, used by tools. */
- #ifndef FSL_COMPONENT_ID
- #define FSL_COMPONENT_ID "platform.drivers.sctimer"
- #endif
- /*! @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 */
- #if defined(FSL_FEATURE_SCT_WRITE_ZERO_ASSERT_RESET) && FSL_FEATURE_SCT_WRITE_ZERO_ASSERT_RESET
- /*! @brief Pointers to SCT resets for each instance, writing a zero asserts the reset */
- static const reset_ip_name_t s_sctResets[] = SCT_RSTS_N;
- #else
- /*! @brief Pointers to SCT resets for each instance, writing a one asserts the reset */
- static const reset_ip_name_t s_sctResets[] = SCT_RSTS;
- #endif
- /*!< @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);
- assert(pwmParams->output < FSL_FEATURE_SCT_NUMBER_OF_OUTPUTS);
- 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 = 0, pulseEvent = 0;
- 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);
- assert(output < FSL_FEATURE_SCT_NUMBER_OF_OUTPUTS);
-
- 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)
- {
- assert(whichIO < FSL_FEATURE_SCT_NUMBER_OF_OUTPUTS);
- 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);
- /* 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
- }
|