static void FTM_SetReloadPoints(FTM_Type *base, uint32_t reloadPoints) { uint32_t chnlNumber = 0; uint32_t reg = 0; /* Need CNTINC bit to be 1 for CNTIN register to update with its buffer value on reload */ base->SYNCONF |= FTM_SYNCONF_CNTINC_MASK; reg = base->COMBINE; for (chnlNumber = 0; chnlNumber < (FSL_FEATURE_FTM_CHANNEL_COUNTn(base) / 2); chnlNumber++) { /* Need SYNCEN bit to be 1 for CnV reg to update with its buffer value on reload */ reg |= (1U << (FTM_COMBINE_SYNCEN0_SHIFT + (FTM_COMBINE_COMBINE1_SHIFT * chnlNumber))); } base->COMBINE = reg; /* Set the reload points */ reg = base->PWMLOAD; /* Enable the selected channel match reload points */ reg &= ~((1U << FSL_FEATURE_FTM_CHANNEL_COUNTn(base)) - 1); reg |= (reloadPoints & ((1U << FSL_FEATURE_FTM_CHANNEL_COUNTn(base)) - 1)); #if defined(FSL_FEATURE_FTM_HAS_HALFCYCLE_RELOAD) && (FSL_FEATURE_FTM_HAS_HALFCYCLE_RELOAD) /* Enable half cycle match as a reload point */ if (reloadPoints & kFTM_HalfCycMatch) { reg |= FTM_PWMLOAD_HCSEL_MASK; } else { reg &= ~FTM_PWMLOAD_HCSEL_MASK; } #endif /* FSL_FEATURE_FTM_HAS_HALFCYCLE_RELOAD */ base->PWMLOAD = reg; /* These reload points are used when counter is in up-down counting mode */ reg = base->SYNC; if (reloadPoints & kFTM_CntMax) { /* Reload when counter turns from up to down */ reg |= FTM_SYNC_CNTMAX_MASK; } else { reg &= ~FTM_SYNC_CNTMAX_MASK; } if (reloadPoints & kFTM_CntMin) { /* Reload when counter turns from down to up */ reg |= FTM_SYNC_CNTMIN_MASK; } else { reg &= ~FTM_SYNC_CNTMIN_MASK; } base->SYNC = reg; }
/*See fsl_ftm_driver.h for documentation of this function.*/ void FTM_DRV_Init(uint8_t instance, ftm_user_config_t * info) { assert(instance < HW_FTM_INSTANCE_COUNT); uint32_t ftmBaseAddr = g_ftmBaseAddr[instance]; uint8_t chan = FSL_FEATURE_FTM_CHANNEL_COUNTn(instance); /* clock setting initialization*/ CLOCK_SYS_EnableFtmClock(instance); FTM_HAL_Reset(ftmBaseAddr); /* Reset the channel registers */ for(int i = 0; i < chan; i++) { HW_FTM_CnSC_WR(ftmBaseAddr, i, 0); HW_FTM_CnV_WR(ftmBaseAddr, i, 0); } FTM_HAL_Init(ftmBaseAddr); FTM_HAL_SetSyncMode(ftmBaseAddr, info->syncMethod); FTM_HAL_SetTofFreq(ftmBaseAddr, info->tofFrequency); FTM_HAL_SetWriteProtectionCmd(ftmBaseAddr, info->isWriteProtection); FTM_HAL_SetBdmMode(ftmBaseAddr,info->BDMMode); NVIC_ClearPendingIRQ(g_ftmIrqId[instance]); INT_SYS_EnableIRQ(g_ftmIrqId[instance]); }
static void FTM_SetPwmSync(FTM_Type *base, uint32_t syncMethod) { uint8_t chnlNumber = 0; uint32_t reg = 0, syncReg = 0; syncReg = base->SYNC; /* Enable PWM synchronization of output mask register */ syncReg |= FTM_SYNC_SYNCHOM_MASK; reg = base->COMBINE; for (chnlNumber = 0; chnlNumber < (FSL_FEATURE_FTM_CHANNEL_COUNTn(base) / 2); chnlNumber++) { /* Enable PWM synchronization of registers C(n)V and C(n+1)V */ reg |= (1U << (FTM_COMBINE_SYNCEN0_SHIFT + (FTM_COMBINE_COMBINE1_SHIFT * chnlNumber))); } base->COMBINE = reg; reg = base->SYNCONF; /* Use enhanced PWM synchronization method. Use PWM sync to update register values */ reg |= (FTM_SYNCONF_SYNCMODE_MASK | FTM_SYNCONF_CNTINC_MASK | FTM_SYNCONF_INVC_MASK | FTM_SYNCONF_SWOC_MASK); if (syncMethod & FTM_SYNC_SWSYNC_MASK) { /* Enable needed bits for software trigger to update registers with its buffer value */ reg |= (FTM_SYNCONF_SWRSTCNT_MASK | FTM_SYNCONF_SWWRBUF_MASK | FTM_SYNCONF_SWINVC_MASK | FTM_SYNCONF_SWSOC_MASK | FTM_SYNCONF_SWOM_MASK); } if (syncMethod & (FTM_SYNC_TRIG0_MASK | FTM_SYNC_TRIG1_MASK | FTM_SYNC_TRIG2_MASK)) { /* Enable needed bits for hardware trigger to update registers with its buffer value */ reg |= (FTM_SYNCONF_HWRSTCNT_MASK | FTM_SYNCONF_HWWRBUF_MASK | FTM_SYNCONF_HWINVC_MASK | FTM_SYNCONF_HWSOC_MASK | FTM_SYNCONF_HWOM_MASK); /* Enable the appropriate hardware trigger that is used for PWM sync */ if (syncMethod & FTM_SYNC_TRIG0_MASK) { syncReg |= FTM_SYNC_TRIG0_MASK; } if (syncMethod & FTM_SYNC_TRIG1_MASK) { syncReg |= FTM_SYNC_TRIG1_MASK; } if (syncMethod & FTM_SYNC_TRIG2_MASK) { syncReg |= FTM_SYNC_TRIG2_MASK; } } /* Write back values to the SYNC register */ base->SYNC = syncReg; /* Write the PWM synch values to the SYNCONF register */ base->SYNCONF = reg; }
/*See fsl_ftm_driver.h for documentation of this function.*/ void FTM_DRV_PwmStop(uint8_t instance, ftm_pwm_param_t *param, uint8_t channel) { assert((param->mode == kFtmEdgeAlignedPWM) || (param->mode == kFtmCenterAlignedPWM) || (param->mode == kFtmCombinedPWM)); assert(instance < HW_FTM_INSTANCE_COUNT); assert(channel < FSL_FEATURE_FTM_CHANNEL_COUNTn(instance)); uint32_t ftmBaseAddr = g_ftmBaseAddr[instance]; /* Stop the FTM counter */ FTM_HAL_SetClockSource(ftmBaseAddr, kClock_source_FTM_None); FTM_HAL_DisablePwmMode(ftmBaseAddr, param, channel); /* Clear out the registers */ FTM_HAL_SetMod(ftmBaseAddr, 0); FTM_HAL_SetCounter(ftmBaseAddr, 0); }
uint32_t FTM_GetEnabledInterrupts(FTM_Type *base) { uint32_t enabledInterrupts = 0; int8_t chnlCount = FSL_FEATURE_FTM_CHANNEL_COUNTn(base); /* The CHANNEL_COUNT macro returns -1 if it cannot match the FTM instance */ assert(chnlCount != -1); /* Check if timer overflow interrupt is enabled */ if (base->SC & FTM_SC_TOIE_MASK) { enabledInterrupts |= kFTM_TimeOverflowInterruptEnable; } /* Check if fault interrupt is enabled */ if (base->MODE & FTM_MODE_FAULTIE_MASK) { enabledInterrupts |= kFTM_FaultInterruptEnable; } #if defined(FSL_FEATURE_FTM_HAS_RELOAD_INTERRUPT) && (FSL_FEATURE_FTM_HAS_RELOAD_INTERRUPT) /* Check if the reload interrupt is enabled */ if (base->SC & FTM_SC_RIE_MASK) { enabledInterrupts |= kFTM_ReloadInterruptEnable; } #endif /* Check if the channel interrupts are enabled */ while (chnlCount > 0) { chnlCount--; if (base->CONTROLS[chnlCount].CnSC & FTM_CnSC_CHIE_MASK) { enabledInterrupts |= (1U << chnlCount); } } return enabledInterrupts; }
void FTM_DRV_PwmChangeDutyCycle(uint8_t instance, ftm_pwm_param_t *param, uint8_t channel) { uint32_t uFTMhz; uint16_t uMod, uCnv, uCnvFirstEdge = 0; assert(instance < HW_FTM_INSTANCE_COUNT); assert(param->uDutyCyclePercent <= 100); assert(channel < FSL_FEATURE_FTM_CHANNEL_COUNTn(instance)); uint32_t ftmBaseAddr = g_ftmBaseAddr[instance]; uMod = FTM_HAL_GetMod(ftmBaseAddr); uCnv = uMod * param->uDutyCyclePercent / 100; /* For 100% duty cycle */ if(uCnv >= uMod) { uCnv = uMod + 1; } FTM_HAL_SetChnCountVal(ftmBaseAddr, channel, uCnv); }
void FTM_DRV_CounterStart(uint8_t instance, ftm_counting_mode_t countMode, uint32_t countStartVal, uint32_t countFinalVal, bool enableOverflowInt) { assert(instance < HW_FTM_INSTANCE_COUNT); uint32_t ftmBaseAddr = g_ftmBaseAddr[instance]; uint32_t channel = 0; /* Clear the overflow flag */ FTM_HAL_ClearTimerOverflow(ftmBaseAddr); FTM_HAL_SetCounterInitVal(ftmBaseAddr, countStartVal); FTM_HAL_SetMod(ftmBaseAddr, countFinalVal); FTM_HAL_SetCounter(ftmBaseAddr, 0); /* Use FTM as counter, disable all the channels */ for (channel = 0; channel < FSL_FEATURE_FTM_CHANNEL_COUNTn(instance); channel++) { FTM_HAL_SetChnEdgeLevel(ftmBaseAddr, channel, 0); } if (countMode == kCounting_FTM_UP) { FTM_HAL_SetQuadDecoderCmd(ftmBaseAddr, false); FTM_HAL_SetCpwms(ftmBaseAddr, 0); } else if (countMode == kCounting_FTM_UpDown) { FTM_HAL_SetQuadDecoderCmd(ftmBaseAddr, false); FTM_HAL_SetCpwms(ftmBaseAddr, 1); } /* Activate interrupts if required */ FTM_DRV_SetTimeOverflowIntCmd(instance, enableOverflowInt); /* Set clock source to start the counter */ FTM_HAL_SetClockSource(ftmBaseAddr, kClock_source_FTM_SystemClk); }
void FTM_UpdatePwmDutycycle(FTM_Type *base, ftm_chnl_t chnlNumber, ftm_pwm_mode_t currentPwmMode, uint8_t dutyCyclePercent) { uint16_t cnv, cnvFirstEdge = 0, mod; mod = base->MOD; if ((currentPwmMode == kFTM_EdgeAlignedPwm) || (currentPwmMode == kFTM_CenterAlignedPwm)) { cnv = (mod * dutyCyclePercent) / 100; /* For 100% duty cycle */ if (cnv >= mod) { cnv = mod + 1; } base->CONTROLS[chnlNumber].CnV = cnv; } else { /* This check is added for combined mode as the channel number should be the pair number */ if (chnlNumber >= (FSL_FEATURE_FTM_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; } }
status_t FTM_SetupPwm(FTM_Type *base, const ftm_chnl_pwm_signal_param_t *chnlParams, uint8_t numOfChnls, ftm_pwm_mode_t mode, uint32_t pwmFreq_Hz, uint32_t srcClock_Hz) { assert(chnlParams); uint32_t mod, reg; uint32_t ftmClock = (srcClock_Hz / (1U << (base->SC & FTM_SC_PS_MASK))); uint16_t cnv, cnvFirstEdge; uint8_t i; switch (mode) { case kFTM_EdgeAlignedPwm: case kFTM_CombinedPwm: base->SC &= ~FTM_SC_CPWMS_MASK; mod = (ftmClock / pwmFreq_Hz) - 1; break; case kFTM_CenterAlignedPwm: base->SC |= FTM_SC_CPWMS_MASK; mod = ftmClock / (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 FTM 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 ((mode == kFTM_EdgeAlignedPwm) || (mode == kFTM_CenterAlignedPwm)) { /* Clear the current mode and edge level bits */ reg = base->CONTROLS[chnlParams->chnlNumber].CnSC; reg &= ~(FTM_CnSC_MSA_MASK | FTM_CnSC_MSB_MASK | FTM_CnSC_ELSA_MASK | FTM_CnSC_ELSB_MASK); /* Setup the active level */ reg |= (uint32_t)(chnlParams->level << FTM_CnSC_ELSA_SHIFT); /* Edge-aligned mode needs MSB to be 1, don't care for Center-aligned mode */ reg |= FTM_CnSC_MSB(1U); /* Update the mode and edge level */ base->CONTROLS[chnlParams->chnlNumber].CnSC = reg; 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; } } base->CONTROLS[chnlParams->chnlNumber].CnV = cnv; #if defined(FSL_FEATURE_FTM_HAS_ENABLE_PWM_OUTPUT) && (FSL_FEATURE_FTM_HAS_ENABLE_PWM_OUTPUT) /* Set to output mode */ FTM_SetPwmOutputEnable(base, chnlParams->chnlNumber, true); #endif } else { /* This check is added for combined mode as the channel number should be the pair number */ if (chnlParams->chnlNumber >= (FSL_FEATURE_FTM_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; } } /* Clear the current mode and edge level bits for channel n */ reg = base->CONTROLS[chnlParams->chnlNumber * 2].CnSC; reg &= ~(FTM_CnSC_MSA_MASK | FTM_CnSC_MSB_MASK | FTM_CnSC_ELSA_MASK | FTM_CnSC_ELSB_MASK); /* Setup the active level for channel n */ reg |= (uint32_t)(chnlParams->level << FTM_CnSC_ELSA_SHIFT); /* Update the mode and edge level for channel n */ base->CONTROLS[chnlParams->chnlNumber * 2].CnSC = reg; /* Clear the current mode and edge level bits for channel n + 1 */ reg = base->CONTROLS[(chnlParams->chnlNumber * 2) + 1].CnSC; reg &= ~(FTM_CnSC_MSA_MASK | FTM_CnSC_MSB_MASK | FTM_CnSC_ELSA_MASK | FTM_CnSC_ELSB_MASK); /* Setup the active level for channel n + 1 */ reg |= (FTM_CnSC_ELSA(chnlParams->level) | FTM_CnSC_ELSB(chnlParams->level)); /* Update the mode and edge level for channel n + 1*/ base->CONTROLS[(chnlParams->chnlNumber * 2) + 1].CnSC = reg; /* Set the channel pair values */ base->CONTROLS[chnlParams->chnlNumber * 2].CnV = cnvFirstEdge; base->CONTROLS[(chnlParams->chnlNumber * 2) + 1].CnV = cnvFirstEdge + cnv; /* Set the combine bit for the channel pair */ base->COMBINE |= (1U << (FTM_COMBINE_COMBINE0_SHIFT + (FTM_COMBINE_COMBINE1_SHIFT * chnlParams->chnlNumber))); #if defined(FSL_FEATURE_FTM_HAS_ENABLE_PWM_OUTPUT) && (FSL_FEATURE_FTM_HAS_ENABLE_PWM_OUTPUT) /* Set to output mode */ FTM_SetPwmOutputEnable(base, (ftm_chnl_t)((uint8_t)chnlParams->chnlNumber * 2), true); FTM_SetPwmOutputEnable(base, (ftm_chnl_t)((uint8_t)chnlParams->chnlNumber * 2 + 1), true); #endif } chnlParams++; } return kStatus_Success; }
/*See fsl_ftm_driver.h for documentation of this function.*/ void FTM_DRV_PwmStart(uint8_t instance, ftm_pwm_param_t *param, uint8_t channel) { uint32_t uFTMhz; uint16_t uMod, uCnv, uCnvFirstEdge = 0; assert(instance < HW_FTM_INSTANCE_COUNT); assert(param->uDutyCyclePercent <= 100); assert(channel < FSL_FEATURE_FTM_CHANNEL_COUNTn(instance)); uint32_t ftmBaseAddr = g_ftmBaseAddr[instance]; /* Clear the overflow flag */ FTM_HAL_ClearTimerOverflow(g_ftmBaseAddr[instance]); FTM_HAL_EnablePwmMode(ftmBaseAddr, param, channel); #if FSL_FEATURE_FTM_BUS_CLOCK CLOCK_SYS_GetFreq(kBusClock, &uFTMhz); #else CLOCK_SYS_GetFreq(kSystemClock, &uFTMhz); #endif /* Based on Ref manual, in PWM mode CNTIN is to be set 0*/ FTM_HAL_SetCounterInitVal(ftmBaseAddr, 0); uFTMhz = uFTMhz / (1 << FTM_HAL_GetClockPs(ftmBaseAddr)); switch(param->mode) { case kFtmEdgeAlignedPWM: uMod = uFTMhz / (param->uFrequencyHZ) - 1; uCnv = uMod * param->uDutyCyclePercent / 100; /* For 100% duty cycle */ if(uCnv >= uMod) { uCnv = uMod + 1; } FTM_HAL_SetMod(ftmBaseAddr, uMod); FTM_HAL_SetChnCountVal(ftmBaseAddr, channel, uCnv); break; case kFtmCenterAlignedPWM: uMod = uFTMhz / (param->uFrequencyHZ * 2); uCnv = uMod * param->uDutyCyclePercent / 100; /* For 100% duty cycle */ if(uCnv >= uMod) { uCnv = uMod + 1; } FTM_HAL_SetMod(ftmBaseAddr, uMod); FTM_HAL_SetChnCountVal(ftmBaseAddr, channel, uCnv); break; case kFtmCombinedPWM: uMod = uFTMhz / (param->uFrequencyHZ) - 1; uCnv = uMod * param->uDutyCyclePercent / 100; uCnvFirstEdge = uMod * param->uFirstEdgeDelayPercent / 100; /* For 100% duty cycle */ if(uCnv >= uMod) { uCnv = uMod + 1; } FTM_HAL_SetMod(ftmBaseAddr, uMod); FTM_HAL_SetChnCountVal(ftmBaseAddr, FTM_HAL_GetChnPairIndex(channel) * 2, uCnvFirstEdge); FTM_HAL_SetChnCountVal(ftmBaseAddr, FTM_HAL_GetChnPairIndex(channel) * 2 + 1, uCnv + uCnvFirstEdge); break; default: assert(0); break; } /* Set clock source to start counter */ FTM_HAL_SetClockSource(ftmBaseAddr, kClock_source_FTM_SystemClk); }