/* * The Clear BSD License * Copyright (c) 2015, 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_tpm.h" /******************************************************************************* * Definitions ******************************************************************************/ /* Component ID definition, used by tools. */ #ifndef FSL_COMPONENT_ID #define FSL_COMPONENT_ID "platform.drivers.tpm" #endif #define TPM_COMBINE_SHIFT (8U) /******************************************************************************* * Prototypes ******************************************************************************/ /*! * @brief Gets the instance from the base address * * @param base TPM peripheral base address * * @return The TPM instance */ static uint32_t TPM_GetInstance(TPM_Type *base); /******************************************************************************* * Variables ******************************************************************************/ /*! @brief Pointers to TPM bases for each instance. */ static TPM_Type *const s_tpmBases[] = TPM_BASE_PTRS; #if !(defined(FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) /*! @brief Pointers to TPM clocks for each instance. */ static const clock_ip_name_t s_tpmClocks[] = TPM_CLOCKS; #endif /* FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL */ /******************************************************************************* * Code ******************************************************************************/ static uint32_t TPM_GetInstance(TPM_Type *base) { uint32_t instance; uint32_t tpmArrayCount = (sizeof(s_tpmBases) / sizeof(s_tpmBases[0])); /* Find the instance index from base address mappings. */ for (instance = 0; instance < tpmArrayCount; instance++) { if (s_tpmBases[instance] == base) { break; } } assert(instance < tpmArrayCount); return instance; } void TPM_Init(TPM_Type *base, const tpm_config_t *config) { assert(config); #if !(defined(FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) /* Enable the module clock */ CLOCK_EnableClock(s_tpmClocks[TPM_GetInstance(base)]); #endif /* FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL */ #if defined(FSL_FEATURE_TPM_HAS_GLOBAL) && FSL_FEATURE_TPM_HAS_GLOBAL /* TPM reset is available on certain SoC's */ TPM_Reset(base); #endif /* Set the clock prescale factor */ base->SC = TPM_SC_PS(config->prescale); #if !(defined(FSL_FEATURE_TPM_HAS_NO_CONF) && FSL_FEATURE_TPM_HAS_NO_CONF) /* Setup the counter operation */ base->CONF = TPM_CONF_DOZEEN(config->enableDoze) | TPM_CONF_GTBEEN(config->useGlobalTimeBase) | TPM_CONF_CROT(config->enableReloadOnTrigger) | TPM_CONF_CSOT(config->enableStartOnTrigger) | TPM_CONF_CSOO(config->enableStopOnOverflow) | #if defined(FSL_FEATURE_TPM_HAS_PAUSE_COUNTER_ON_TRIGGER) && FSL_FEATURE_TPM_HAS_PAUSE_COUNTER_ON_TRIGGER TPM_CONF_CPOT(config->enablePauseOnTrigger) | #endif #if defined(FSL_FEATURE_TPM_HAS_EXTERNAL_TRIGGER_SELECTION) && FSL_FEATURE_TPM_HAS_EXTERNAL_TRIGGER_SELECTION TPM_CONF_TRGSRC(config->triggerSource) | #endif TPM_CONF_TRGSEL(config->triggerSelect); if (config->enableDebugMode) { base->CONF |= TPM_CONF_DBGMODE_MASK; } else { base->CONF &= ~TPM_CONF_DBGMODE_MASK; } #endif } void TPM_Deinit(TPM_Type *base) { #if defined(FSL_FEATURE_TPM_HAS_SC_CLKS) && FSL_FEATURE_TPM_HAS_SC_CLKS /* Stop the counter */ base->SC &= ~TPM_SC_CLKS_MASK; #else /* Stop the counter */ base->SC &= ~TPM_SC_CMOD_MASK; #endif #if !(defined(FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) /* Gate the TPM clock */ CLOCK_DisableClock(s_tpmClocks[TPM_GetInstance(base)]); #endif /* FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL */ } void TPM_GetDefaultConfig(tpm_config_t *config) { assert(config); /* TPM clock divide by 1 */ config->prescale = kTPM_Prescale_Divide_1; #if !(defined(FSL_FEATURE_TPM_HAS_NO_CONF) && FSL_FEATURE_TPM_HAS_NO_CONF) /* Use internal TPM counter as timebase */ config->useGlobalTimeBase = false; /* TPM counter continues in doze mode */ config->enableDoze = false; /* TPM counter pauses when in debug mode */ config->enableDebugMode = false; /* TPM counter will not be reloaded on input trigger */ config->enableReloadOnTrigger = false; /* TPM counter continues running after overflow */ config->enableStopOnOverflow = false; /* TPM counter starts immediately once it is enabled */ config->enableStartOnTrigger = false; #if defined(FSL_FEATURE_TPM_HAS_PAUSE_COUNTER_ON_TRIGGER) && FSL_FEATURE_TPM_HAS_PAUSE_COUNTER_ON_TRIGGER config->enablePauseOnTrigger = false; #endif /* Choose trigger select 0 as input trigger for controlling counter operation */ config->triggerSelect = kTPM_Trigger_Select_0; #if defined(FSL_FEATURE_TPM_HAS_EXTERNAL_TRIGGER_SELECTION) && FSL_FEATURE_TPM_HAS_EXTERNAL_TRIGGER_SELECTION /* Choose external trigger source to control counter operation */ config->triggerSource = kTPM_TriggerSource_External; #endif #endif } status_t TPM_SetupPwm(TPM_Type *base, const tpm_chnl_pwm_signal_param_t *chnlParams, uint8_t numOfChnls, tpm_pwm_mode_t mode, uint32_t pwmFreq_Hz, uint32_t srcClock_Hz) { assert(chnlParams); assert(pwmFreq_Hz); assert(numOfChnls); assert(srcClock_Hz); #if defined(FSL_FEATURE_TPM_HAS_COMBINE) && FSL_FEATURE_TPM_HAS_COMBINE if(mode == kTPM_CombinedPwm) { assert(FSL_FEATURE_TPM_COMBINE_HAS_EFFECTn(base)); } #endif uint32_t mod; uint32_t tpmClock = (srcClock_Hz / (1U << (base->SC & TPM_SC_PS_MASK))); uint16_t cnv; uint8_t i; #if defined(FSL_FEATURE_TPM_HAS_QDCTRL) && FSL_FEATURE_TPM_HAS_QDCTRL /* The TPM's QDCTRL register required to be effective */ if( FSL_FEATURE_TPM_QDCTRL_HAS_EFFECTn(base) ) { /* Clear quadrature Decoder mode because in quadrature Decoder mode PWM doesn't operate*/ base->QDCTRL &= ~TPM_QDCTRL_QUADEN_MASK; } #endif switch (mode) { case kTPM_EdgeAlignedPwm: #if defined(FSL_FEATURE_TPM_HAS_COMBINE) && FSL_FEATURE_TPM_HAS_COMBINE case kTPM_CombinedPwm: #endif base->SC &= ~TPM_SC_CPWMS_MASK; mod = (tpmClock / pwmFreq_Hz) - 1; break; case kTPM_CenterAlignedPwm: base->SC |= TPM_SC_CPWMS_MASK; mod = tpmClock / (pwmFreq_Hz * 2); break; default: return kStatus_Fail; } /* Return an error in case we overflow the registers, probably would require changing * clock source to get the desired frequency */ if (mod > 65535U) { return kStatus_Fail; } /* Set the PWM period */ base->MOD = mod; /* Setup each TPM channel */ for (i = 0; i < numOfChnls; i++) { /* Return error if requested dutycycle is greater than the max allowed */ if (chnlParams->dutyCyclePercent > 100) { return kStatus_Fail; } #if defined(FSL_FEATURE_TPM_HAS_COMBINE) && FSL_FEATURE_TPM_HAS_COMBINE if (mode == kTPM_CombinedPwm) { uint16_t cnvFirstEdge; /* This check is added for combined mode as the channel number should be the pair number */ if (chnlParams->chnlNumber >= (FSL_FEATURE_TPM_CHANNEL_COUNTn(base) / 2)) { return kStatus_Fail; } /* Return error if requested value is greater than the max allowed */ if (chnlParams->firstEdgeDelayPercent > 100) { return kStatus_Fail; } /* Configure delay of the first edge */ if (chnlParams->firstEdgeDelayPercent == 0) { /* No delay for the first edge */ cnvFirstEdge = 0; } else { cnvFirstEdge = (mod * chnlParams->firstEdgeDelayPercent) / 100; } /* Configure dutycycle */ if (chnlParams->dutyCyclePercent == 0) { /* Signal stays low */ cnv = 0; cnvFirstEdge = 0; } else { cnv = (mod * chnlParams->dutyCyclePercent) / 100; /* For 100% duty cycle */ if (cnv >= mod) { cnv = mod + 1; } } /* Set the combine bit for the channel pair */ base->COMBINE |= (1U << (TPM_COMBINE_COMBINE0_SHIFT + (TPM_COMBINE_SHIFT * chnlParams->chnlNumber))); /* When switching mode, disable channel n first */ base->CONTROLS[chnlParams->chnlNumber * 2].CnSC &= ~(TPM_CnSC_MSA_MASK | TPM_CnSC_MSB_MASK | TPM_CnSC_ELSA_MASK | TPM_CnSC_ELSB_MASK); /* Wait till mode change to disable channel is acknowledged */ while ((base->CONTROLS[chnlParams->chnlNumber * 2].CnSC & (TPM_CnSC_MSA_MASK | TPM_CnSC_MSB_MASK | TPM_CnSC_ELSA_MASK | TPM_CnSC_ELSB_MASK))) { } /* Set the requested PWM mode for channel n, PWM output requires mode select to be set to 2 */ base->CONTROLS[chnlParams->chnlNumber * 2].CnSC |= ((chnlParams->level << TPM_CnSC_ELSA_SHIFT) | (2U << TPM_CnSC_MSA_SHIFT)); /* Wait till mode change is acknowledged */ while (!(base->CONTROLS[chnlParams->chnlNumber * 2].CnSC & (TPM_CnSC_MSA_MASK | TPM_CnSC_MSB_MASK | TPM_CnSC_ELSA_MASK | TPM_CnSC_ELSB_MASK))) { } /* Set the channel pair values */ base->CONTROLS[chnlParams->chnlNumber * 2].CnV = cnvFirstEdge; /* When switching mode, disable channel n + 1 first */ base->CONTROLS[(chnlParams->chnlNumber * 2) + 1].CnSC &= ~(TPM_CnSC_MSA_MASK | TPM_CnSC_MSB_MASK | TPM_CnSC_ELSA_MASK | TPM_CnSC_ELSB_MASK); /* Wait till mode change to disable channel is acknowledged */ while ((base->CONTROLS[(chnlParams->chnlNumber * 2) + 1].CnSC & (TPM_CnSC_MSA_MASK | TPM_CnSC_MSB_MASK | TPM_CnSC_ELSA_MASK | TPM_CnSC_ELSB_MASK))) { } /* Set the requested PWM mode for channel n + 1, PWM output requires mode select to be set to 2 */ base->CONTROLS[(chnlParams->chnlNumber * 2) + 1].CnSC |= ((chnlParams->level << TPM_CnSC_ELSA_SHIFT) | (2U << TPM_CnSC_MSA_SHIFT)); /* Wait till mode change is acknowledged */ while (!(base->CONTROLS[(chnlParams->chnlNumber * 2) + 1].CnSC & (TPM_CnSC_MSA_MASK | TPM_CnSC_MSB_MASK | TPM_CnSC_ELSA_MASK | TPM_CnSC_ELSB_MASK))) { } /* Set the channel pair values */ base->CONTROLS[(chnlParams->chnlNumber * 2) + 1].CnV = cnvFirstEdge + cnv; } else { #endif if (chnlParams->dutyCyclePercent == 0) { /* Signal stays low */ cnv = 0; } else { cnv = (mod * chnlParams->dutyCyclePercent) / 100; /* For 100% duty cycle */ if (cnv >= mod) { cnv = mod + 1; } } /* When switching mode, disable channel first */ base->CONTROLS[chnlParams->chnlNumber].CnSC &= ~(TPM_CnSC_MSA_MASK | TPM_CnSC_MSB_MASK | TPM_CnSC_ELSA_MASK | TPM_CnSC_ELSB_MASK); /* Wait till mode change to disable channel is acknowledged */ while ((base->CONTROLS[chnlParams->chnlNumber].CnSC & (TPM_CnSC_MSA_MASK | TPM_CnSC_MSB_MASK | TPM_CnSC_ELSA_MASK | TPM_CnSC_ELSB_MASK))) { } /* Set the requested PWM mode, PWM output requires mode select to be set to 2 */ base->CONTROLS[chnlParams->chnlNumber].CnSC |= ((chnlParams->level << TPM_CnSC_ELSA_SHIFT) | (2U << TPM_CnSC_MSA_SHIFT)); /* Wait till mode change is acknowledged */ while (!(base->CONTROLS[chnlParams->chnlNumber].CnSC & (TPM_CnSC_MSA_MASK | TPM_CnSC_MSB_MASK | TPM_CnSC_ELSA_MASK | TPM_CnSC_ELSB_MASK))) { } base->CONTROLS[chnlParams->chnlNumber].CnV = cnv; #if defined(FSL_FEATURE_TPM_HAS_COMBINE) && FSL_FEATURE_TPM_HAS_COMBINE } #endif chnlParams++; } return kStatus_Success; } void TPM_UpdatePwmDutycycle(TPM_Type *base, tpm_chnl_t chnlNumber, tpm_pwm_mode_t currentPwmMode, uint8_t dutyCyclePercent) { assert(chnlNumber < FSL_FEATURE_TPM_CHANNEL_COUNTn(base)); #if defined(FSL_FEATURE_TPM_HAS_COMBINE) && FSL_FEATURE_TPM_HAS_COMBINE if(currentPwmMode == kTPM_CombinedPwm) { assert(FSL_FEATURE_TPM_COMBINE_HAS_EFFECTn(base)); } #endif uint16_t cnv, mod; mod = base->MOD; #if defined(FSL_FEATURE_TPM_HAS_COMBINE) && FSL_FEATURE_TPM_HAS_COMBINE if (currentPwmMode == kTPM_CombinedPwm) { uint16_t cnvFirstEdge; /* This check is added for combined mode as the channel number should be the pair number */ if (chnlNumber >= (FSL_FEATURE_TPM_CHANNEL_COUNTn(base) / 2)) { return; } cnv = (mod * dutyCyclePercent) / 100; cnvFirstEdge = base->CONTROLS[chnlNumber * 2].CnV; /* For 100% duty cycle */ if (cnv >= mod) { cnv = mod + 1; } base->CONTROLS[(chnlNumber * 2) + 1].CnV = cnvFirstEdge + cnv; } else { #endif cnv = (mod * dutyCyclePercent) / 100; /* For 100% duty cycle */ if (cnv >= mod) { cnv = mod + 1; } base->CONTROLS[chnlNumber].CnV = cnv; #if defined(FSL_FEATURE_TPM_WAIT_CnV_REGISTER_UPDATE) && FSL_FEATURE_TPM_WAIT_CnV_REGISTER_UPDATE while(!(cnv == base->CONTROLS[chnlNumber].CnV)) { } #endif #if defined(FSL_FEATURE_TPM_HAS_COMBINE) && FSL_FEATURE_TPM_HAS_COMBINE } #endif } void TPM_UpdateChnlEdgeLevelSelect(TPM_Type *base, tpm_chnl_t chnlNumber, uint8_t level) { assert(chnlNumber < FSL_FEATURE_TPM_CHANNEL_COUNTn(base)); uint32_t reg = base->CONTROLS[chnlNumber].CnSC #if !(defined(FSL_FEATURE_TPM_CnSC_CHF_WRITE_0_CLEAR) && FSL_FEATURE_TPM_CnSC_CHF_WRITE_0_CLEAR) & ~(TPM_CnSC_CHF_MASK) #endif ; /* When switching mode, disable channel first */ base->CONTROLS[chnlNumber].CnSC &= ~(TPM_CnSC_MSA_MASK | TPM_CnSC_MSB_MASK | TPM_CnSC_ELSA_MASK | TPM_CnSC_ELSB_MASK); /* Wait till mode change to disable channel is acknowledged */ while ((base->CONTROLS[chnlNumber].CnSC & (TPM_CnSC_MSA_MASK | TPM_CnSC_MSB_MASK | TPM_CnSC_ELSA_MASK | TPM_CnSC_ELSB_MASK))) { } /* Clear the field and write the new level value */ reg &= ~(TPM_CnSC_ELSA_MASK | TPM_CnSC_ELSB_MASK); reg |= ((uint32_t)level << TPM_CnSC_ELSA_SHIFT) & (TPM_CnSC_ELSA_MASK | TPM_CnSC_ELSB_MASK); base->CONTROLS[chnlNumber].CnSC = reg; /* Wait till mode change is acknowledged */ reg &= (TPM_CnSC_MSA_MASK | TPM_CnSC_MSB_MASK | TPM_CnSC_ELSA_MASK | TPM_CnSC_ELSB_MASK); while (reg != (base->CONTROLS[chnlNumber].CnSC & (TPM_CnSC_MSA_MASK | TPM_CnSC_MSB_MASK | TPM_CnSC_ELSA_MASK | TPM_CnSC_ELSB_MASK))) { } } void TPM_SetupInputCapture(TPM_Type *base, tpm_chnl_t chnlNumber, tpm_input_capture_edge_t captureMode) { assert(chnlNumber < FSL_FEATURE_TPM_CHANNEL_COUNTn(base)); #if defined(FSL_FEATURE_TPM_HAS_QDCTRL) && FSL_FEATURE_TPM_HAS_QDCTRL /* The TPM's QDCTRL register required to be effective */ if( FSL_FEATURE_TPM_QDCTRL_HAS_EFFECTn(base) ) { /* Clear quadrature Decoder mode for channel 0 or 1*/ if ((chnlNumber == 0) || (chnlNumber == 1)) { base->QDCTRL &= ~TPM_QDCTRL_QUADEN_MASK; } } #endif #if defined(FSL_FEATURE_TPM_HAS_COMBINE) && FSL_FEATURE_TPM_HAS_COMBINE /* The TPM's COMBINE register required to be effective */ if( FSL_FEATURE_TPM_COMBINE_HAS_EFFECTn(base) ) { /* Clear the combine bit for chnlNumber */ base->COMBINE &= ~(1U << TPM_COMBINE_SHIFT * (chnlNumber / 2)); } #endif /* When switching mode, disable channel first */ base->CONTROLS[chnlNumber].CnSC &= ~(TPM_CnSC_MSA_MASK | TPM_CnSC_MSB_MASK | TPM_CnSC_ELSA_MASK | TPM_CnSC_ELSB_MASK); /* Wait till mode change to disable channel is acknowledged */ while ((base->CONTROLS[chnlNumber].CnSC & (TPM_CnSC_MSA_MASK | TPM_CnSC_MSB_MASK | TPM_CnSC_ELSA_MASK | TPM_CnSC_ELSB_MASK))) { } /* Set the requested input capture mode */ base->CONTROLS[chnlNumber].CnSC |= captureMode; /* Wait till mode change is acknowledged */ while (!(base->CONTROLS[chnlNumber].CnSC & (TPM_CnSC_MSA_MASK | TPM_CnSC_MSB_MASK | TPM_CnSC_ELSA_MASK | TPM_CnSC_ELSB_MASK))) { } } void TPM_SetupOutputCompare(TPM_Type *base, tpm_chnl_t chnlNumber, tpm_output_compare_mode_t compareMode, uint32_t compareValue) { assert(chnlNumber < FSL_FEATURE_TPM_CHANNEL_COUNTn(base)); #if defined(FSL_FEATURE_TPM_HAS_QDCTRL) && FSL_FEATURE_TPM_HAS_QDCTRL /* The TPM's QDCTRL register required to be effective */ if( FSL_FEATURE_TPM_QDCTRL_HAS_EFFECTn(base) ) { /* Clear quadrature Decoder mode for channel 0 or 1 */ if ((chnlNumber == 0) || (chnlNumber == 1)) { base->QDCTRL &= ~TPM_QDCTRL_QUADEN_MASK; } } #endif /* When switching mode, disable channel first */ base->CONTROLS[chnlNumber].CnSC &= ~(TPM_CnSC_MSA_MASK | TPM_CnSC_MSB_MASK | TPM_CnSC_ELSA_MASK | TPM_CnSC_ELSB_MASK); /* Wait till mode change to disable channel is acknowledged */ while ((base->CONTROLS[chnlNumber].CnSC & (TPM_CnSC_MSA_MASK | TPM_CnSC_MSB_MASK | TPM_CnSC_ELSA_MASK | TPM_CnSC_ELSB_MASK))) { } /* Setup the channel output behaviour when a match occurs with the compare value */ base->CONTROLS[chnlNumber].CnSC |= compareMode; /* Setup the compare value */ base->CONTROLS[chnlNumber].CnV = compareValue; /* Wait till mode change is acknowledged */ while (!(base->CONTROLS[chnlNumber].CnSC & (TPM_CnSC_MSA_MASK | TPM_CnSC_MSB_MASK | TPM_CnSC_ELSA_MASK | TPM_CnSC_ELSB_MASK))) { } } #if defined(FSL_FEATURE_TPM_HAS_COMBINE) && FSL_FEATURE_TPM_HAS_COMBINE void TPM_SetupDualEdgeCapture(TPM_Type *base, tpm_chnl_t chnlPairNumber, const tpm_dual_edge_capture_param_t *edgeParam, uint32_t filterValue) { assert(edgeParam); assert(chnlPairNumber < FSL_FEATURE_TPM_CHANNEL_COUNTn(base) / 2); assert(FSL_FEATURE_TPM_COMBINE_HAS_EFFECTn(base)); uint32_t reg; #if defined(FSL_FEATURE_TPM_HAS_QDCTRL) && FSL_FEATURE_TPM_HAS_QDCTRL /* The TPM's QDCTRL register required to be effective */ if( FSL_FEATURE_TPM_QDCTRL_HAS_EFFECTn(base) ) { /* Clear quadrature Decoder mode for channel 0 or 1*/ if (chnlPairNumber == 0) { base->QDCTRL &= ~TPM_QDCTRL_QUADEN_MASK; } } #endif /* Unlock: When switching mode, disable channel first */ base->CONTROLS[chnlPairNumber * 2].CnSC &= ~(TPM_CnSC_MSA_MASK | TPM_CnSC_MSB_MASK | TPM_CnSC_ELSA_MASK | TPM_CnSC_ELSB_MASK); /* Wait till mode change to disable channel is acknowledged */ while ((base->CONTROLS[chnlPairNumber * 2].CnSC & (TPM_CnSC_MSA_MASK | TPM_CnSC_MSB_MASK | TPM_CnSC_ELSA_MASK | TPM_CnSC_ELSB_MASK))) { } base->CONTROLS[chnlPairNumber * 2 + 1].CnSC &= ~(TPM_CnSC_MSA_MASK | TPM_CnSC_MSB_MASK | TPM_CnSC_ELSA_MASK | TPM_CnSC_ELSB_MASK); /* Wait till mode change to disable channel is acknowledged */ while ((base->CONTROLS[chnlPairNumber * 2 + 1].CnSC & (TPM_CnSC_MSA_MASK | TPM_CnSC_MSB_MASK | TPM_CnSC_ELSA_MASK | TPM_CnSC_ELSB_MASK))) { } /* Now, the registers for input mode can be operated. */ if (edgeParam->enableSwap) { /* Set the combine and swap bits for the channel pair */ base->COMBINE |= (TPM_COMBINE_COMBINE0_MASK | TPM_COMBINE_COMSWAP0_MASK) << (TPM_COMBINE_SHIFT * chnlPairNumber); /* Input filter setup for channel n+1 input */ reg = base->FILTER; reg &= ~(TPM_FILTER_CH0FVAL_MASK << (TPM_FILTER_CH1FVAL_SHIFT * (chnlPairNumber + 1))); reg |= (filterValue << (TPM_FILTER_CH1FVAL_SHIFT * (chnlPairNumber + 1))); base->FILTER = reg; } else { reg = base->COMBINE; /* Clear the swap bit for the channel pair */ reg &= ~(TPM_COMBINE_COMSWAP0_MASK << (TPM_COMBINE_COMSWAP0_SHIFT * chnlPairNumber)); /* Set the combine bit for the channel pair */ reg |= TPM_COMBINE_COMBINE0_MASK << (TPM_COMBINE_SHIFT * chnlPairNumber); base->COMBINE = reg; /* Input filter setup for channel n input */ reg = base->FILTER; reg &= ~(TPM_FILTER_CH0FVAL_MASK << (TPM_FILTER_CH1FVAL_SHIFT * chnlPairNumber)); reg |= (filterValue << (TPM_FILTER_CH1FVAL_SHIFT * chnlPairNumber)); base->FILTER = reg; } /* Setup the edge detection from channel n */ base->CONTROLS[chnlPairNumber * 2].CnSC |= edgeParam->currChanEdgeMode; /* Wait till mode change is acknowledged */ while (!(base->CONTROLS[chnlPairNumber * 2].CnSC & (TPM_CnSC_MSA_MASK | TPM_CnSC_MSB_MASK | TPM_CnSC_ELSA_MASK | TPM_CnSC_ELSB_MASK))) { } /* Setup the edge detection from channel n+1 */ base->CONTROLS[(chnlPairNumber * 2) + 1].CnSC |= edgeParam->nextChanEdgeMode; /* Wait till mode change is acknowledged */ while (!(base->CONTROLS[(chnlPairNumber * 2) + 1].CnSC & (TPM_CnSC_MSA_MASK | TPM_CnSC_MSB_MASK | TPM_CnSC_ELSA_MASK | TPM_CnSC_ELSB_MASK))) { } } #endif #if defined(FSL_FEATURE_TPM_HAS_QDCTRL) && FSL_FEATURE_TPM_HAS_QDCTRL void TPM_SetupQuadDecode(TPM_Type *base, const tpm_phase_params_t *phaseAParams, const tpm_phase_params_t *phaseBParams, tpm_quad_decode_mode_t quadMode) { assert(phaseAParams); assert(phaseBParams); assert(FSL_FEATURE_TPM_QDCTRL_HAS_EFFECTn(base)); base->CONTROLS[0].CnSC &= ~(TPM_CnSC_MSA_MASK | TPM_CnSC_MSB_MASK | TPM_CnSC_ELSA_MASK | TPM_CnSC_ELSB_MASK); /* Wait till mode change to disable channel is acknowledged */ while ((base->CONTROLS[0].CnSC & (TPM_CnSC_MSA_MASK | TPM_CnSC_MSB_MASK | TPM_CnSC_ELSA_MASK | TPM_CnSC_ELSB_MASK))) { } uint32_t reg; /* Set Phase A filter value */ reg = base->FILTER; reg &= ~(TPM_FILTER_CH0FVAL_MASK); reg |= TPM_FILTER_CH0FVAL(phaseAParams->phaseFilterVal); base->FILTER = reg; #if defined(FSL_FEATURE_TPM_HAS_POL) && FSL_FEATURE_TPM_HAS_POL /* Set Phase A polarity */ if (phaseAParams->phasePolarity) { base->POL |= TPM_POL_POL0_MASK; } else { base->POL &= ~TPM_POL_POL0_MASK; } #endif base->CONTROLS[1].CnSC &= ~(TPM_CnSC_MSA_MASK | TPM_CnSC_MSB_MASK | TPM_CnSC_ELSA_MASK | TPM_CnSC_ELSB_MASK); /* Wait till mode change to disable channel is acknowledged */ while ((base->CONTROLS[1].CnSC & (TPM_CnSC_MSA_MASK | TPM_CnSC_MSB_MASK | TPM_CnSC_ELSA_MASK | TPM_CnSC_ELSB_MASK))) { } /* Set Phase B filter value */ reg = base->FILTER; reg &= ~(TPM_FILTER_CH1FVAL_MASK); reg |= TPM_FILTER_CH1FVAL(phaseBParams->phaseFilterVal); base->FILTER = reg; #if defined(FSL_FEATURE_TPM_HAS_POL) && FSL_FEATURE_TPM_HAS_POL /* Set Phase B polarity */ if (phaseBParams->phasePolarity) { base->POL |= TPM_POL_POL1_MASK; } else { base->POL &= ~TPM_POL_POL1_MASK; } #endif /* Set Quadrature mode */ reg = base->QDCTRL; reg &= ~(TPM_QDCTRL_QUADMODE_MASK); reg |= TPM_QDCTRL_QUADMODE(quadMode); base->QDCTRL = reg; /* Enable Quad decode */ base->QDCTRL |= TPM_QDCTRL_QUADEN_MASK; } #endif void TPM_EnableInterrupts(TPM_Type *base, uint32_t mask) { uint32_t chnlInterrupts = (mask & 0xFF); uint8_t chnlNumber = 0; /* Enable the timer overflow interrupt */ if (mask & kTPM_TimeOverflowInterruptEnable) { base->SC |= TPM_SC_TOIE_MASK; } /* Enable the channel interrupts */ while (chnlInterrupts) { if (chnlInterrupts & 0x1) { base->CONTROLS[chnlNumber].CnSC |= TPM_CnSC_CHIE_MASK; } chnlNumber++; chnlInterrupts = chnlInterrupts >> 1U; } } void TPM_DisableInterrupts(TPM_Type *base, uint32_t mask) { uint32_t chnlInterrupts = (mask & 0xFF); uint8_t chnlNumber = 0; /* Disable the timer overflow interrupt */ if (mask & kTPM_TimeOverflowInterruptEnable) { base->SC &= ~TPM_SC_TOIE_MASK; } /* Disable the channel interrupts */ while (chnlInterrupts) { if (chnlInterrupts & 0x1) { base->CONTROLS[chnlNumber].CnSC &= ~TPM_CnSC_CHIE_MASK; } chnlNumber++; chnlInterrupts = chnlInterrupts >> 1U; } } uint32_t TPM_GetEnabledInterrupts(TPM_Type *base) { uint32_t enabledInterrupts = 0; int8_t chnlCount = FSL_FEATURE_TPM_CHANNEL_COUNTn(base); /* The CHANNEL_COUNT macro returns -1 if it cannot match the TPM instance */ assert(chnlCount != -1); /* Check if timer overflow interrupt is enabled */ if (base->SC & TPM_SC_TOIE_MASK) { enabledInterrupts |= kTPM_TimeOverflowInterruptEnable; } /* Check if the channel interrupts are enabled */ while (chnlCount > 0) { chnlCount--; if (base->CONTROLS[chnlCount].CnSC & TPM_CnSC_CHIE_MASK) { enabledInterrupts |= (1U << chnlCount); } } return enabledInterrupts; }