804 lines
30 KiB
C
804 lines
30 KiB
C
/*
|
|
* Copyright (c) 2016, Freescale Semiconductor, Inc.
|
|
* Copyright 2016-2020 NXP
|
|
* All rights reserved.
|
|
*
|
|
* SPDX-License-Identifier: BSD-3-Clause
|
|
*/
|
|
|
|
#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_SDK_DISABLE_DRIVER_RESET_CONTROL) && FSL_SDK_DISABLE_DRIVER_RESET_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
|
|
#endif /* FSL_SDK_DISABLE_DRIVER_RESET_CONTROL */
|
|
|
|
/*!< @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 unify 32-bit or low 16-bit match/capture register number. */
|
|
static uint32_t s_currentMatch;
|
|
/*!< @brief Keep track of SCTimer high 16-bit match/capture register number. */
|
|
static uint32_t s_currentMatchhigh;
|
|
|
|
/*! @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;
|
|
}
|
|
|
|
/*!
|
|
* brief Ungates the SCTimer clock and configures the peripheral for basic operation.
|
|
*
|
|
* note This API should be called at the beginning of the application using the SCTimer driver.
|
|
*
|
|
* param base SCTimer peripheral base address
|
|
* param config Pointer to the user configuration structure.
|
|
*
|
|
* return kStatus_Success indicates success; Else indicates failure.
|
|
*/
|
|
status_t SCTIMER_Init(SCT_Type *base, const sctimer_config_t *config)
|
|
{
|
|
assert(NULL != 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 */
|
|
|
|
#if !(defined(FSL_SDK_DISABLE_DRIVER_RESET_CONTROL) && FSL_SDK_DISABLE_DRIVER_RESET_CONTROL)
|
|
/* Reset the module. */
|
|
RESET_PeripheralReset(s_sctResets[SCTIMER_GetInstance(base)]);
|
|
#endif /* FSL_SDK_DISABLE_DRIVER_RESET_CONTROL */
|
|
|
|
/* Setup the counter operation. For Current Driver interface SCTIMER_Init don't know detail
|
|
* frequency of input clock, but User know it. So the INSYNC have to set by user level. */
|
|
base->CONFIG = SCT_CONFIG_CKSEL(config->clockSelect) | SCT_CONFIG_CLKMODE(config->clockMode) |
|
|
SCT_CONFIG_UNIFY(config->enableCounterUnify) | SCT_CONFIG_INSYNC(config->inputsync);
|
|
|
|
/* Write to the control register, keep the counters halted. */
|
|
base->CTRL =
|
|
SCT_CTRL_BIDIR_L(config->enableBidirection_l) | SCT_CTRL_PRE_L(config->prescale_l) | SCT_CTRL_HALT_L_MASK;
|
|
/* Clear the counter after changing the PRE value. */
|
|
base->CTRL |= SCT_CTRL_CLRCTR_L_MASK;
|
|
|
|
if (!(config->enableCounterUnify))
|
|
{
|
|
base->CTRL |=
|
|
SCT_CTRL_BIDIR_H(config->enableBidirection_h) | SCT_CTRL_PRE_H(config->prescale_h) | SCT_CTRL_HALT_H_MASK;
|
|
base->CTRL |= SCT_CTRL_CLRCTR_H_MASK;
|
|
}
|
|
|
|
/* Initial state of channel output */
|
|
base->OUTPUT = config->outInitState;
|
|
|
|
/* Clear the global variables */
|
|
s_currentEvent = 0U;
|
|
s_currentState = 0U;
|
|
s_currentMatch = 0U;
|
|
s_currentMatchhigh = 0U;
|
|
|
|
/* Clear the callback array */
|
|
for (i = 0; i < (uint32_t)FSL_FEATURE_SCT_NUMBER_OF_EVENTS; i++)
|
|
{
|
|
s_eventCallback[i] = NULL;
|
|
}
|
|
|
|
/* Save interrupt handler */
|
|
s_sctimerIsr = SCTIMER_EventHandleIRQ;
|
|
|
|
return kStatus_Success;
|
|
}
|
|
|
|
/*!
|
|
* brief Gates the SCTimer clock.
|
|
*
|
|
* param base SCTimer peripheral base address
|
|
*/
|
|
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 */
|
|
}
|
|
|
|
/*!
|
|
* brief Fills in the SCTimer configuration structure with the default settings.
|
|
*
|
|
* The default values are:
|
|
* code
|
|
* config->enableCounterUnify = true;
|
|
* config->clockMode = kSCTIMER_System_ClockMode;
|
|
* config->clockSelect = kSCTIMER_Clock_On_Rise_Input_0;
|
|
* config->enableBidirection_l = false;
|
|
* config->enableBidirection_h = false;
|
|
* config->prescale_l = 0U;
|
|
* config->prescale_h = 0U;
|
|
* config->outInitState = 0U;
|
|
* config->inputsync = 0xFU;
|
|
* endcode
|
|
* param config Pointer to the user configuration structure.
|
|
*/
|
|
void SCTIMER_GetDefaultConfig(sctimer_config_t *config)
|
|
{
|
|
assert(NULL != config);
|
|
|
|
/* Initializes the configure structure to zero. */
|
|
(void)memset(config, 0, sizeof(*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 = 0U;
|
|
/* Prescale factor of 1 for Counter_H*/
|
|
config->prescale_h = 0U;
|
|
/* Clear outputs */
|
|
config->outInitState = 0U;
|
|
/* Default value is 0xFU, it can be clear as 0 when speical conditions met.
|
|
* Condition can be clear as 0: (for all Clock Modes):
|
|
* (1) The corresponding input is already synchronous to the SCTimer/PWM clock.
|
|
* (2) The SCTimer/PWM clock frequency does not exceed 100 MHz.
|
|
* Note: The SCTimer/PWM clock is the bus/system clock for CKMODE 0-2 or asynchronous input
|
|
* clock for CKMODE3.
|
|
* Another condition can be clear as 0: (for CKMODE2 only)
|
|
* (1) The corresponding input is synchronous to the designated CKMODE2 input clock.
|
|
* (2) The CKMODE2 input clock frequency is less than one-third the frequency of the bus/system clock.
|
|
* Default value set as 0U, input0~input3 are set as bypasses. */
|
|
config->inputsync = 0xFU;
|
|
}
|
|
|
|
/*!
|
|
* brief Configures the PWM signal parameters.
|
|
*
|
|
* Call this function to configure the PWM signal period, mode, duty cycle, and edge. This
|
|
* function will create 2 events; one of the events will trigger on match with the pulse value
|
|
* and the other will trigger when the counter matches the PWM period. The PWM period event is
|
|
* also used as a limit event to reset the counter or change direction. Both events are enabled
|
|
* for the same state. The state number can be retrieved by calling the function
|
|
* SCTIMER_GetCurrentStateNumber().
|
|
* The counter is set to operate as one 32-bit counter (unify bit is set to 1).
|
|
* The counter operates in bi-directional mode when generating a center-aligned PWM.
|
|
*
|
|
* note When setting PWM output from multiple output pins, they all should use the same PWM mode
|
|
* i.e all PWM's should be either edge-aligned or center-aligned.
|
|
* When using this API, the PWM signal frequency of all the initialized channels must be the same.
|
|
* Otherwise all the initialized channels' PWM signal frequency is equal to the last call to the
|
|
* API's pwmFreq_Hz.
|
|
*
|
|
* param base SCTimer peripheral base address
|
|
* param pwmParams PWM parameters to configure the output
|
|
* param mode PWM operation mode, options available in enumeration ::sctimer_pwm_mode_t
|
|
* param pwmFreq_Hz PWM signal frequency in Hz
|
|
* param srcClock_Hz SCTimer counter clock in Hz
|
|
* param event Pointer to a variable where the PWM period event number is stored
|
|
*
|
|
* return kStatus_Success on success
|
|
* kStatus_Fail If we have hit the limit in terms of number of events created or if
|
|
* an incorrect PWM dutycylce is passed in.
|
|
*/
|
|
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(NULL != pwmParams);
|
|
assert(0U != srcClock_Hz);
|
|
assert(0U != pwmFreq_Hz);
|
|
assert((uint32_t)pwmParams->output < (uint32_t)FSL_FEATURE_SCT_NUMBER_OF_OUTPUTS);
|
|
assert(1U == (base->CONFIG & SCT_CONFIG_UNIFY_MASK));
|
|
|
|
/* If we do not have enough events available (this function will create two events),
|
|
* the function will return fail.
|
|
*/
|
|
status_t status = kStatus_Fail;
|
|
status_t status2;
|
|
uint32_t period, pulsePeriod = 0;
|
|
uint32_t sctClock = srcClock_Hz / (((base->CTRL & SCT_CTRL_PRE_L_MASK) >> SCT_CTRL_PRE_L_SHIFT) + 1U);
|
|
uint32_t periodEvent = 0, pulseEvent = 0;
|
|
uint32_t reg;
|
|
|
|
if ((s_currentEvent + 2U) <= (uint32_t)FSL_FEATURE_SCT_NUMBER_OF_EVENTS)
|
|
{
|
|
/* 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) - 1U;
|
|
}
|
|
else
|
|
{
|
|
period = sctClock / (pwmFreq_Hz * 2U);
|
|
}
|
|
|
|
/* Calculate pulse width and period match value:
|
|
* For EdgeAlignedPwm, "pulsePeriod = 0" results in 0% dutycyle, "pulsePeriod = period - 1U" results in 100%
|
|
* dutycyle. For CenterAlignedPwm, , "pulsePeriod = 0" results in 0% dutycyle, "pulsePeriod = period + 2U"
|
|
* results in 100% dutycyle.
|
|
*/
|
|
|
|
pulsePeriod = (uint32_t)(((uint64_t)period * pwmParams->dutyCyclePercent) / 100U);
|
|
|
|
if (pwmParams->dutyCyclePercent >= 100U)
|
|
{
|
|
if (mode == kSCTIMER_EdgeAlignedPwm)
|
|
{
|
|
pulsePeriod = period + 2U;
|
|
}
|
|
else
|
|
{
|
|
pulsePeriod = period - 1U;
|
|
}
|
|
}
|
|
|
|
/* Schedule an event when we reach the PWM period */
|
|
status =
|
|
SCTIMER_CreateAndScheduleEvent(base, kSCTIMER_MatchEventOnly, period, 0, kSCTIMER_Counter_U, &periodEvent);
|
|
|
|
/* Schedule an event when we reach the pulse width */
|
|
status2 = SCTIMER_CreateAndScheduleEvent(base, kSCTIMER_MatchEventOnly, pulsePeriod, 0, kSCTIMER_Counter_U,
|
|
&pulseEvent);
|
|
|
|
if ((kStatus_Success == status) && (kStatus_Success == status2))
|
|
{
|
|
/* Reset the counter when we reach the PWM period */
|
|
SCTIMER_SetupCounterLimitAction(base, kSCTIMER_Counter_U, periodEvent);
|
|
|
|
/* Return the period event to the user */
|
|
*event = periodEvent;
|
|
|
|
/* For high-true level */
|
|
if ((uint32_t)pwmParams->level == (uint32_t)kSCTIMER_HighTrue)
|
|
{
|
|
/* Set the initial output level to low which is the inactive state */
|
|
base->OUTPUT &= ~(1UL << (uint32_t)pwmParams->output);
|
|
|
|
if (mode == kSCTIMER_EdgeAlignedPwm)
|
|
{
|
|
/* Set the output when we reach the PWM period */
|
|
SCTIMER_SetupOutputSetAction(base, (uint32_t)pwmParams->output, periodEvent);
|
|
/* Clear the output when we reach the PWM pulse value */
|
|
SCTIMER_SetupOutputClearAction(base, (uint32_t)pwmParams->output, pulseEvent);
|
|
}
|
|
else
|
|
{
|
|
/* Clear the output when we reach the PWM pulse event */
|
|
SCTIMER_SetupOutputClearAction(base, (uint32_t)pwmParams->output, pulseEvent);
|
|
/* Reverse output when down counting */
|
|
reg = base->OUTPUTDIRCTRL;
|
|
reg &= ~((uint32_t)SCT_OUTPUTDIRCTRL_SETCLR0_MASK << (2U * (uint32_t)pwmParams->output));
|
|
reg |= (1UL << (2U * (uint32_t)pwmParams->output));
|
|
base->OUTPUTDIRCTRL = reg;
|
|
}
|
|
}
|
|
/* For low-true level */
|
|
else
|
|
{
|
|
/* Set the initial output level to high which is the inactive state */
|
|
base->OUTPUT |= (1UL << (uint32_t)pwmParams->output);
|
|
|
|
if (mode == kSCTIMER_EdgeAlignedPwm)
|
|
{
|
|
/* Clear the output when we reach the PWM period */
|
|
SCTIMER_SetupOutputClearAction(base, (uint32_t)pwmParams->output, periodEvent);
|
|
/* Set the output when we reach the PWM pulse value */
|
|
SCTIMER_SetupOutputSetAction(base, (uint32_t)pwmParams->output, pulseEvent);
|
|
}
|
|
else
|
|
{
|
|
/* Set the output when we reach the PWM pulse event */
|
|
SCTIMER_SetupOutputSetAction(base, (uint32_t)pwmParams->output, pulseEvent);
|
|
/* Reverse output when down counting */
|
|
reg = base->OUTPUTDIRCTRL;
|
|
reg &= ~((uint32_t)SCT_OUTPUTDIRCTRL_SETCLR0_MASK << (2U * (uint32_t)pwmParams->output));
|
|
reg |= (1UL << (2U * (uint32_t)pwmParams->output));
|
|
base->OUTPUTDIRCTRL = reg;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
status = kStatus_Fail;
|
|
}
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
/*!
|
|
* brief Updates the duty cycle of an active PWM signal.
|
|
*
|
|
* Before calling this function, the counter is set to operate as one 32-bit counter (unify bit is set to 1).
|
|
*
|
|
* param base SCTimer peripheral base address
|
|
* param output The output to configure
|
|
* param dutyCyclePercent New PWM pulse width; the value should be between 0 to 100
|
|
* param event Event number associated with this PWM signal. This was returned to the user by the
|
|
* function SCTIMER_SetupPwm().
|
|
*/
|
|
void SCTIMER_UpdatePwmDutycycle(SCT_Type *base, sctimer_out_t output, uint8_t dutyCyclePercent, uint32_t event)
|
|
|
|
{
|
|
assert(dutyCyclePercent <= 100U);
|
|
assert((uint32_t)output < (uint32_t)FSL_FEATURE_SCT_NUMBER_OF_OUTPUTS);
|
|
assert(1U == (base->CONFIG & SCT_CONFIG_UNIFY_MASK));
|
|
|
|
uint32_t periodMatchReg, pulseMatchReg;
|
|
uint32_t pulsePeriod = 0, period;
|
|
|
|
/* Retrieve the match register number for the PWM period */
|
|
periodMatchReg = base->EV[event].CTRL & SCT_EV_CTRL_MATCHSEL_MASK;
|
|
|
|
/* Retrieve the match register number for the PWM pulse period */
|
|
pulseMatchReg = base->EV[event + 1U].CTRL & SCT_EV_CTRL_MATCHSEL_MASK;
|
|
|
|
period = base->MATCH[periodMatchReg];
|
|
|
|
/* Calculate pulse width and period match value:
|
|
* For EdgeAlignedPwm, "pulsePeriod = 0" results in 0% dutycyle, "pulsePeriod = period - 1U" results in 100%
|
|
* dutycyle. For CenterAlignedPwm, , "pulsePeriod = 0" results in 0% dutycyle, "pulsePeriod = period + 2U"
|
|
* results in 100% dutycyle.
|
|
*/
|
|
pulsePeriod = (uint32_t)(((uint64_t)period * dutyCyclePercent) / 100U);
|
|
|
|
if (dutyCyclePercent == 100U)
|
|
{
|
|
if (0U == (base->CTRL & SCT_CTRL_BIDIR_L_MASK))
|
|
{
|
|
pulsePeriod = period + 2U;
|
|
}
|
|
else
|
|
{
|
|
pulsePeriod = period - 1U;
|
|
}
|
|
}
|
|
|
|
/* Stop the counter before updating match register */
|
|
SCTIMER_StopTimer(base, (uint32_t)kSCTIMER_Counter_U);
|
|
|
|
/* Update dutycycle */
|
|
base->MATCH[pulseMatchReg] = SCT_MATCH_MATCHn_L(pulsePeriod);
|
|
base->MATCHREL[pulseMatchReg] = SCT_MATCHREL_RELOADn_L(pulsePeriod);
|
|
|
|
/* Restart the counter */
|
|
SCTIMER_StartTimer(base, (uint32_t)kSCTIMER_Counter_U);
|
|
}
|
|
|
|
/*!
|
|
* brief Create an event that is triggered on a match or IO and schedule in current state.
|
|
*
|
|
* This function will configure an event using the options provided by the user. If the event type uses
|
|
* the counter match, then the function will set the user provided match value into a match register
|
|
* and put this match register number into the event control register.
|
|
* The event is enabled for the current state and the event number is increased by one at the end.
|
|
* The function returns the event number; this event number can be used to configure actions to be
|
|
* done when this event is triggered.
|
|
*
|
|
* param base SCTimer peripheral base address
|
|
* param howToMonitor Event type; options are available in the enumeration ::sctimer_interrupt_enable_t
|
|
* param matchValue The match value that will be programmed to a match register
|
|
* param whichIO The input or output that will be involved in event triggering. This field
|
|
* is ignored if the event type is "match only"
|
|
* param whichCounter SCTimer counter to use. In 16-bit mode, we can select Counter_L and Counter_H,
|
|
* In 32-bit mode, we can select Counter_U.
|
|
* param event Pointer to a variable where the new event number is stored
|
|
*
|
|
* return kStatus_Success on success
|
|
* kStatus_Error if we have hit the limit in terms of number of events created or
|
|
if we have reached the limit in terms of number of match registers
|
|
*/
|
|
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_EV_CTRL_COMBMODE_MASK) >> SCT_EV_CTRL_COMBMODE_SHIFT);
|
|
uint32_t currentCtrlVal = (uint32_t)howToMonitor;
|
|
status_t status = kStatus_Success;
|
|
|
|
if (s_currentEvent < (uint32_t)FSL_FEATURE_SCT_NUMBER_OF_EVENTS)
|
|
{
|
|
if (2U == combMode)
|
|
{
|
|
base->EV[s_currentEvent].CTRL = currentCtrlVal | SCT_EV_CTRL_IOSEL(whichIO);
|
|
}
|
|
else
|
|
{
|
|
if ((0U == combMode) || (3U == combMode))
|
|
{
|
|
currentCtrlVal |= SCT_EV_CTRL_IOSEL(whichIO);
|
|
}
|
|
|
|
if ((kSCTIMER_Counter_L == whichCounter) && (0U == (base->CONFIG & SCT_CONFIG_UNIFY_MASK)))
|
|
{
|
|
if (s_currentMatch < (uint32_t)FSL_FEATURE_SCT_NUMBER_OF_MATCH_CAPTURE)
|
|
{
|
|
currentCtrlVal |= SCT_EV_CTRL_MATCHSEL(s_currentMatch);
|
|
|
|
/* Use Counter_L bits if user wants to setup the Low counter */
|
|
base->MATCH_ACCESS16BIT[s_currentMatch].MATCHL = (uint16_t)matchValue;
|
|
base->MATCHREL_ACCESS16BIT[s_currentMatch].MATCHRELL = (uint16_t)matchValue;
|
|
base->EV[s_currentEvent].CTRL = currentCtrlVal;
|
|
|
|
/* Increment the match register number */
|
|
s_currentMatch++;
|
|
}
|
|
else
|
|
{
|
|
/* An error would occur if we have hit the limit in terms of number of match registers */
|
|
status = kStatus_Fail;
|
|
}
|
|
}
|
|
else if ((kSCTIMER_Counter_H == whichCounter) && (0U == (base->CONFIG & SCT_CONFIG_UNIFY_MASK)))
|
|
{
|
|
if (s_currentMatchhigh < (uint32_t)FSL_FEATURE_SCT_NUMBER_OF_MATCH_CAPTURE)
|
|
{
|
|
currentCtrlVal |= SCT_EV_CTRL_MATCHSEL(s_currentMatchhigh);
|
|
|
|
/* Use Counter_H bits if user wants to setup the High counter */
|
|
currentCtrlVal |= SCT_EV_CTRL_HEVENT(1U);
|
|
base->MATCH_ACCESS16BIT[s_currentMatchhigh].MATCHH = (uint16_t)matchValue;
|
|
base->MATCHREL_ACCESS16BIT[s_currentMatchhigh].MATCHRELH = (uint16_t)matchValue;
|
|
|
|
base->EV[s_currentEvent].CTRL = currentCtrlVal;
|
|
/* Increment the match register number */
|
|
s_currentMatchhigh++;
|
|
}
|
|
else
|
|
{
|
|
/* An error would occur if we have hit the limit in terms of number of match registers */
|
|
status = kStatus_Fail;
|
|
}
|
|
}
|
|
else if ((kSCTIMER_Counter_U == whichCounter) && (0U != (base->CONFIG & SCT_CONFIG_UNIFY_MASK)))
|
|
{
|
|
if (s_currentMatch < (uint32_t)FSL_FEATURE_SCT_NUMBER_OF_MATCH_CAPTURE)
|
|
{
|
|
/* Use Counter_L bits if counter is operating in 32-bit mode */
|
|
currentCtrlVal |= SCT_EV_CTRL_MATCHSEL(s_currentMatch);
|
|
|
|
base->MATCH[s_currentMatch] = matchValue;
|
|
base->MATCHREL[s_currentMatch] = matchValue;
|
|
base->EV[s_currentEvent].CTRL = currentCtrlVal;
|
|
|
|
/* Increment the match register number */
|
|
s_currentMatch++;
|
|
}
|
|
else
|
|
{
|
|
/* An error would occur if we have hit the limit in terms of number of match registers */
|
|
status = kStatus_Fail;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* The used counter must match the CONFIG[UNIFY] bit selection */
|
|
status = kStatus_Fail;
|
|
}
|
|
}
|
|
|
|
if (kStatus_Success == status)
|
|
{
|
|
/* Enable the event in the current state */
|
|
base->EV[s_currentEvent].STATE = (1UL << s_currentState);
|
|
|
|
/* Return the event number */
|
|
*event = s_currentEvent;
|
|
|
|
/* Increment the event number */
|
|
s_currentEvent++;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* An error would occur if we have hit the limit in terms of number of events created */
|
|
status = kStatus_Fail;
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
/*!
|
|
* brief Enable an event in the current state.
|
|
*
|
|
* This function will allow the event passed in to trigger in the current state. The event must
|
|
* be created earlier by either calling the function SCTIMER_SetupPwm() or function
|
|
* SCTIMER_CreateAndScheduleEvent() .
|
|
*
|
|
* param base SCTimer peripheral base address
|
|
* param event Event number to enable in the current state
|
|
*
|
|
*/
|
|
void SCTIMER_ScheduleEvent(SCT_Type *base, uint32_t event)
|
|
{
|
|
/* Enable event in the current state */
|
|
base->EV[event].STATE |= (1UL << s_currentState);
|
|
}
|
|
|
|
/*!
|
|
* brief Increase the state by 1
|
|
*
|
|
* All future events created by calling the function SCTIMER_ScheduleEvent() will be enabled in this new
|
|
* state.
|
|
*
|
|
* param base SCTimer peripheral base address
|
|
*
|
|
* return kStatus_Success on success
|
|
* kStatus_Error if we have hit the limit in terms of states used
|
|
|
|
*/
|
|
status_t SCTIMER_IncreaseState(SCT_Type *base)
|
|
{
|
|
status_t status = kStatus_Success;
|
|
|
|
/* Return an error if we have hit the limit in terms of states used */
|
|
if (s_currentState >= (uint32_t)FSL_FEATURE_SCT_NUMBER_OF_STATES)
|
|
{
|
|
status = kStatus_Fail;
|
|
}
|
|
else
|
|
{
|
|
s_currentState++;
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
/*!
|
|
* brief Provides the current state
|
|
*
|
|
* User can use this to set the next state by calling the function SCTIMER_SetupNextStateAction().
|
|
*
|
|
* param base SCTimer peripheral base address
|
|
*
|
|
* return The current state
|
|
*/
|
|
uint32_t SCTIMER_GetCurrentState(SCT_Type *base)
|
|
{
|
|
return s_currentState;
|
|
}
|
|
|
|
/*!
|
|
* brief Toggle the output level.
|
|
*
|
|
* This change in the output level is triggered by the event number that is passed in by the user.
|
|
*
|
|
* param base SCTimer peripheral base address
|
|
* param whichIO The output to toggle
|
|
* param event Event number that will trigger the output change
|
|
*/
|
|
void SCTIMER_SetupOutputToggleAction(SCT_Type *base, uint32_t whichIO, uint32_t event)
|
|
{
|
|
assert(whichIO < (uint32_t)FSL_FEATURE_SCT_NUMBER_OF_OUTPUTS);
|
|
|
|
uint32_t reg;
|
|
|
|
/* Set the same event to set and clear the output */
|
|
base->OUT[whichIO].CLR |= (1UL << event);
|
|
base->OUT[whichIO].SET |= (1UL << event);
|
|
|
|
/* Set the conflict resolution to toggle output */
|
|
reg = base->RES;
|
|
reg &= ~(((uint32_t)SCT_RES_O0RES_MASK) << (2U * whichIO));
|
|
reg |= ((uint32_t)(kSCTIMER_ResolveToggle)) << (2U * whichIO);
|
|
base->RES = reg;
|
|
}
|
|
|
|
/*!
|
|
* brief Setup capture of the counter value on trigger of a selected event
|
|
*
|
|
* param base SCTimer peripheral base address
|
|
* param whichCounter SCTimer counter to use. In 16-bit mode, we can select Counter_L and Counter_H,
|
|
* In 32-bit mode, we can select Counter_U.
|
|
* param captureRegister Pointer to a variable where the capture register number will be returned. User
|
|
* can read the captured value from this register when the specified event is triggered.
|
|
* param event Event number that will trigger the capture
|
|
*
|
|
* return kStatus_Success on success
|
|
* kStatus_Error if we have hit the limit in terms of number of match/capture registers available
|
|
*/
|
|
status_t SCTIMER_SetupCaptureAction(SCT_Type *base,
|
|
sctimer_counter_t whichCounter,
|
|
uint32_t *captureRegister,
|
|
uint32_t event)
|
|
{
|
|
status_t status;
|
|
|
|
if ((kSCTIMER_Counter_L == whichCounter) || (kSCTIMER_Counter_U == whichCounter))
|
|
{
|
|
if (s_currentMatch < (uint32_t)FSL_FEATURE_SCT_NUMBER_OF_MATCH_CAPTURE)
|
|
{
|
|
/* Set the bit to enable event */
|
|
base->CAPCTRL_ACCESS16BIT[s_currentMatch].CAPCTRLL |= SCT_CAPCTRLL_CAPCTRLL(1UL << event);
|
|
|
|
/* Set this resource to be a capture rather than match */
|
|
base->REGMODE_ACCESS16BIT.REGMODEL |= SCT_REGMODEL_REGMODEL(1UL << s_currentMatch);
|
|
|
|
/* Return the match register number */
|
|
*captureRegister = s_currentMatch;
|
|
|
|
/* Increase the match register number */
|
|
s_currentMatch++;
|
|
|
|
status = kStatus_Success;
|
|
}
|
|
else
|
|
{
|
|
/* Return an error if we have hit the limit in terms of number of capture/match registers used */
|
|
status = kStatus_Fail;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (s_currentMatchhigh < (uint32_t)FSL_FEATURE_SCT_NUMBER_OF_MATCH_CAPTURE)
|
|
{
|
|
/* Set bit to enable event */
|
|
base->CAPCTRL_ACCESS16BIT[s_currentMatchhigh].CAPCTRLH |= SCT_CAPCTRLL_CAPCTRLL(1UL << event);
|
|
|
|
/* Set this resource to be a capture rather than match */
|
|
base->REGMODE_ACCESS16BIT.REGMODEH |= SCT_REGMODEL_REGMODEL(1UL << s_currentMatchhigh);
|
|
|
|
/* Return the match register number */
|
|
*captureRegister = s_currentMatchhigh;
|
|
|
|
/* Increase the match register number */
|
|
s_currentMatchhigh++;
|
|
|
|
status = kStatus_Success;
|
|
}
|
|
else
|
|
{
|
|
/* Return an error if we have hit the limit in terms of number of capture/match registers used */
|
|
status = kStatus_Fail;
|
|
}
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
/*!
|
|
* brief Receive noticification when the event trigger an interrupt.
|
|
*
|
|
* If the interrupt for the event is enabled by the user, then a callback can be registered
|
|
* which will be invoked when the event is triggered
|
|
*
|
|
* param base SCTimer peripheral base address
|
|
* param event Event number that will trigger the interrupt
|
|
* param callback Function to invoke when the event is triggered
|
|
*/
|
|
|
|
void SCTIMER_SetCallback(SCT_Type *base, sctimer_event_callback_t callback, uint32_t event)
|
|
{
|
|
s_eventCallback[event] = callback;
|
|
}
|
|
|
|
/*!
|
|
* brief SCTimer interrupt handler.
|
|
*
|
|
* param base SCTimer peripheral base address.
|
|
*/
|
|
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;
|
|
uint32_t i;
|
|
|
|
/* Invoke the callback for certain events */
|
|
for (i = 0; i < (uint32_t)FSL_FEATURE_SCT_NUMBER_OF_EVENTS; i++)
|
|
{
|
|
if ((mask & 0x1U) != 0U)
|
|
{
|
|
if (s_eventCallback[i] != NULL)
|
|
{
|
|
s_eventCallback[i]();
|
|
}
|
|
}
|
|
mask >>= 1UL;
|
|
|
|
if (0U == mask)
|
|
{
|
|
/* All events have been handled. */
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Clear event interrupt flag */
|
|
SCT0->EVFLAG = clearFlag;
|
|
}
|
|
|
|
void SCT0_DriverIRQHandler(void);
|
|
void SCT0_DriverIRQHandler(void)
|
|
{
|
|
s_sctimerIsr(SCT0);
|
|
SDK_ISR_EXIT_BARRIER;
|
|
}
|