Hi Tom,
From the Kinetis reference manual, the Center Aligned PWM mode is not supported in FTM mode when FTMEN=1. Also, to use the hardware faults with FTM, the PWM chnnel should be setup in COMBINED mode. You can still setup PWM waveforms as center aligned in COMBINED mode. In this mode, you control the timing of both the rising and falling edge using the CnV channel value registers of the channel pairs n and n+1.
I’ve attached an example project created for you using Kinetis Design Studio and Processor Expert (PEx). This configures the two FTM channels in COMBINED mode, uses hardware faults with the analog comparator, and clears the fault manually in firmware. More details on the attached project are below.
Main() clears the hardware fault using this code:
if(FTM0_FMS & FTM_FMS_FAULTF0_MASK)
FTM0_FMS &= !FTM_FMS_FAULTF0_MASK; // Clear fault flag
The generated code from PEx for the FTM is in FTM0_Init() in FTM0.c. Here is the code used:
/* SIM_SCGC6: FTM0=1 */
SIM_SCGC6 |= SIM_SCGC6_FTM0_MASK;
(void)(FTM0_SC == 0U); /* Dummy read of the FTM0_SC register to clear the interrupt flag */
/* FTM0_SC: ??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,TOF=0,TOIE=0,CPWMS=0,CLKS=0,PS=0 */
FTM0_SC = (FTM_SC_CLKS(0x00) | FTM_SC_PS(0x00)); /* Stop the counter */
(void)(FTM0_C2SC == 0U); /* Dummy read of the FTM0_C2SC register to clear the interrupt flag */
(void)(FTM0_C3SC == 0U); /* Dummy read of the FTM0_C3SC register to clear the interrupt flag */
/* FTM0_MODE: WPDIS=1 */
FTM0_MODE |= FTM_MODE_WPDIS_MASK; /* Disable write protection */
/* FTM0_C0SC: ??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,CHF=0,CHIE=0,MSB=0,MSA=0,ELSB=0,ELSA=0,??=0,DMA=0 */
FTM0_C0SC = 0x00U;
/* FTM0_C1SC: ??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,CHF=0,CHIE=0,MSB=0,MSA=0,ELSB=0,ELSA=0,??=0,DMA=0 */
FTM0_C1SC = 0x00U;
/* FTM0_C2SC: ??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,CHF=0,CHIE=0,MSB=0,MSA=0,ELSB=0,ELSA=0,??=0,DMA=0 */
FTM0_C2SC = 0x00U;
/* FTM0_C3SC: ??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,CHF=0,CHIE=0,MSB=0,MSA=0,ELSB=0,ELSA=0,??=0,DMA=0 */
FTM0_C3SC = 0x00U;
/* FTM0_C4SC: ??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,CHF=0,CHIE=0,MSB=0,MSA=0,ELSB=0,ELSA=0,??=0,DMA=0 */
FTM0_C4SC = 0x00U;
/* FTM0_C5SC: ??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,CHF=0,CHIE=0,MSB=0,MSA=0,ELSB=0,ELSA=0,??=0,DMA=0 */
FTM0_C5SC = 0x00U;
/* FTM0_C6SC: ??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,CHF=0,CHIE=0,MSB=0,MSA=0,ELSB=0,ELSA=0,??=0,DMA=0 */
FTM0_C6SC = 0x00U;
/* FTM0_C7SC: ??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,CHF=0,CHIE=0,MSB=0,MSA=0,ELSB=0,ELSA=0,??=0,DMA=0 */
FTM0_C7SC = 0x00U;
/* FTM0_SC: TOF=0,CPWMS=0 */
FTM0_SC &= (uint32_t)~(uint32_t)((FTM_SC_TOF_MASK | FTM_SC_CPWMS_MASK));
/* FTM0_MODE: FTMEN=1 */
FTM0_MODE |= FTM_MODE_FTMEN_MASK;
/* FTM0_COMBINE: FAULTEN3=0,SYNCEN3=0,DTEN3=0,COMP3=0,FAULTEN2=0,SYNCEN2=0,DTEN2=0,COMP2=0,FAULTEN1=1,SYNCEN1=0,DTEN1=0,DECAPEN1=0,COMP1=0,COMBINE1=1,FAULTEN0=0,SYNCEN0=0,DTEN0=0,COMP0=0 */
FTM0_COMBINE = (uint32_t)((FTM0_COMBINE & (uint32_t)~(uint32_t)(
FTM_COMBINE_FAULTEN3_MASK |
FTM_COMBINE_SYNCEN3_MASK |
FTM_COMBINE_DTEN3_MASK |
FTM_COMBINE_COMP3_MASK |
FTM_COMBINE_FAULTEN2_MASK |
FTM_COMBINE_SYNCEN2_MASK |
FTM_COMBINE_DTEN2_MASK |
FTM_COMBINE_COMP2_MASK |
FTM_COMBINE_SYNCEN1_MASK |
FTM_COMBINE_DTEN1_MASK |
FTM_COMBINE_DECAPEN1_MASK |
FTM_COMBINE_COMP1_MASK |
FTM_COMBINE_FAULTEN0_MASK |
FTM_COMBINE_SYNCEN0_MASK |
FTM_COMBINE_DTEN0_MASK |
FTM_COMBINE_COMP0_MASK
)) | (uint32_t)(
FTM_COMBINE_FAULTEN1_MASK |
FTM_COMBINE_COMBINE1_MASK
));
/* FTM0_INVCTRL: INV3EN=0,INV2EN=0,INV1EN=0,INV0EN=0 */
FTM0_INVCTRL &= (uint32_t)~(uint32_t)(
FTM_INVCTRL_INV3EN_MASK |
FTM_INVCTRL_INV2EN_MASK |
FTM_INVCTRL_INV1EN_MASK |
FTM_INVCTRL_INV0EN_MASK
);
/* FTM0_C0SC: ??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,CHF=0,MSB=0,MSA=0,ELSB=0,ELSA=0,??=0 */
FTM0_C0SC &= (uint32_t)~(uint32_t)(
FTM_CnSC_CHF_MASK |
FTM_CnSC_MSB_MASK |
FTM_CnSC_MSA_MASK |
FTM_CnSC_ELSB_MASK |
FTM_CnSC_ELSA_MASK |
0xFFFFFF02U
);
/* FTM0_C1SC: ??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,CHF=0,MSB=0,MSA=0,ELSB=0,ELSA=0,??=0 */
FTM0_C1SC &= (uint32_t)~(uint32_t)(
FTM_CnSC_CHF_MASK |
FTM_CnSC_MSB_MASK |
FTM_CnSC_MSA_MASK |
FTM_CnSC_ELSB_MASK |
FTM_CnSC_ELSA_MASK |
0xFFFFFF02U
);
/* FTM0_C2SC: ??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,CHF=0,CHIE=0,ELSB=1,ELSA=0,??=0,DMA=0 */
FTM0_C2SC = (uint32_t)((FTM0_C2SC & (uint32_t)~(uint32_t)(
FTM_CnSC_CHF_MASK |
FTM_CnSC_CHIE_MASK |
FTM_CnSC_ELSA_MASK |
FTM_CnSC_DMA_MASK |
0xFFFFFF02U
)) | (uint32_t)(
FTM_CnSC_ELSB_MASK
));
/* FTM0_C3SC: ??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,CHF=0,CHIE=0,ELSB=1,ELSA=0,??=0,DMA=0 */
FTM0_C3SC = (uint32_t)((FTM0_C3SC & (uint32_t)~(uint32_t)(
FTM_CnSC_CHF_MASK |
FTM_CnSC_CHIE_MASK |
FTM_CnSC_ELSA_MASK |
FTM_CnSC_DMA_MASK |
0xFFFFFF02U
)) | (uint32_t)(
FTM_CnSC_ELSB_MASK
));
/* FTM0_C4SC: ??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,CHF=0,MSB=0,MSA=0,ELSB=0,ELSA=0,??=0 */
FTM0_C4SC &= (uint32_t)~(uint32_t)(
FTM_CnSC_CHF_MASK |
FTM_CnSC_MSB_MASK |
FTM_CnSC_MSA_MASK |
FTM_CnSC_ELSB_MASK |
FTM_CnSC_ELSA_MASK |
0xFFFFFF02U
);
/* FTM0_C5SC: ??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,CHF=0,MSB=0,MSA=0,ELSB=0,ELSA=0,??=0 */
FTM0_C5SC &= (uint32_t)~(uint32_t)(
FTM_CnSC_CHF_MASK |
FTM_CnSC_MSB_MASK |
FTM_CnSC_MSA_MASK |
FTM_CnSC_ELSB_MASK |
FTM_CnSC_ELSA_MASK |
0xFFFFFF02U
);
/* FTM0_C6SC: ??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,CHF=0,MSB=0,MSA=0,ELSB=0,ELSA=0,??=0 */
FTM0_C6SC &= (uint32_t)~(uint32_t)(
FTM_CnSC_CHF_MASK |
FTM_CnSC_MSB_MASK |
FTM_CnSC_MSA_MASK |
FTM_CnSC_ELSB_MASK |
FTM_CnSC_ELSA_MASK |
0xFFFFFF02U
);
/* FTM0_C7SC: ??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,CHF=0,MSB=0,MSA=0,ELSB=0,ELSA=0,??=0 */
FTM0_C7SC &= (uint32_t)~(uint32_t)(
FTM_CnSC_CHF_MASK |
FTM_CnSC_MSB_MASK |
FTM_CnSC_MSA_MASK |
FTM_CnSC_ELSB_MASK |
FTM_CnSC_ELSA_MASK |
0xFFFFFF02U
);
/* FTM0_C2V: ??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,VAL=0x3A98 */
FTM0_C2V = FTM_CnV_VAL(0x3A98);
/* FTM0_C3V: ??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,VAL=0xAFC8 */
FTM0_C3V = FTM_CnV_VAL(0xAFC8);
/* FTM0_POL: POL3=0,POL2=0 */
FTM0_POL &= (uint32_t)~(uint32_t)((FTM_POL_POL3_MASK | FTM_POL_POL2_MASK));
/* FTM0_FILTER: CH3FVAL=0,CH2FVAL=0 */
FTM0_FILTER &= (uint32_t)~(uint32_t)(
FTM_FILTER_CH3FVAL(0x0F) |
FTM_FILTER_CH2FVAL(0x0F)
);
/* FTM0_OUTINIT: ??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,CH3OI=0,CH2OI=0 */
FTM0_OUTINIT &= (uint32_t)~(uint32_t)(
FTM_OUTINIT_CH3OI_MASK |
FTM_OUTINIT_CH2OI_MASK |
0xFFFFFF00U
);
/* FTM0_OUTMASK: CH3OM=0,CH2OM=0 */
FTM0_OUTMASK &= (uint32_t)~(uint32_t)(
FTM_OUTMASK_CH3OM_MASK |
FTM_OUTMASK_CH2OM_MASK
);
/* FTM0_FLTCTRL: FFVAL=0 */
FTM0_FLTCTRL &= (uint32_t)~(uint32_t)(FTM_FLTCTRL_FFVAL(0x0F));
/* FTM0_FLTPOL: FLT3POL=0,FLT2POL=0,FLT1POL=0,FLT0POL=0 */
FTM0_FLTPOL &= (uint32_t)~(uint32_t)(
FTM_FLTPOL_FLT3POL_MASK |
FTM_FLTPOL_FLT2POL_MASK |
FTM_FLTPOL_FLT1POL_MASK |
FTM_FLTPOL_FLT0POL_MASK
);
/* FTM0_FLTCTRL: FFLTR3EN=0,FFLTR2EN=0,FFLTR1EN=0,FFLTR0EN=0,FAULT3EN=0,FAULT2EN=0,FAULT1EN=0,FAULT0EN=1 */
FTM0_FLTCTRL = (uint32_t)((FTM0_FLTCTRL & (uint32_t)~(uint32_t)(
FTM_FLTCTRL_FFLTR3EN_MASK |
FTM_FLTCTRL_FFLTR2EN_MASK |
FTM_FLTCTRL_FFLTR1EN_MASK |
FTM_FLTCTRL_FFLTR0EN_MASK |
FTM_FLTCTRL_FAULT3EN_MASK |
FTM_FLTCTRL_FAULT2EN_MASK |
FTM_FLTCTRL_FAULT1EN_MASK
)) | (uint32_t)(
FTM_FLTCTRL_FAULT0EN_MASK
));
/* FTM0_SYNC: SWSYNC=0,TRIG2=0,TRIG1=0,TRIG0=0,SYNCHOM=0,REINIT=0,CNTMAX=0,CNTMIN=0 */
FTM0_SYNC &= (uint32_t)~(uint32_t)(
FTM_SYNC_SWSYNC_MASK |
FTM_SYNC_TRIG2_MASK |
FTM_SYNC_TRIG1_MASK |
FTM_SYNC_TRIG0_MASK |
FTM_SYNC_SYNCHOM_MASK |
FTM_SYNC_REINIT_MASK |
FTM_SYNC_CNTMAX_MASK |
FTM_SYNC_CNTMIN_MASK
);
/* FTM0_EXTTRIG: INITTRIGEN=0,CH1TRIG=0,CH0TRIG=0,CH5TRIG=0,CH4TRIG=0,CH3TRIG=0,CH2TRIG=0 */
FTM0_EXTTRIG &= (uint32_t)~(uint32_t)(
FTM_EXTTRIG_INITTRIGEN_MASK |
FTM_EXTTRIG_CH1TRIG_MASK |
FTM_EXTTRIG_CH0TRIG_MASK |
FTM_EXTTRIG_CH5TRIG_MASK |
FTM_EXTTRIG_CH4TRIG_MASK |
FTM_EXTTRIG_CH3TRIG_MASK |
FTM_EXTTRIG_CH2TRIG_MASK
);
/* FTM0_MOD: MOD=0xEA60 */
FTM0_MOD = (uint32_t)((FTM0_MOD & (uint32_t)~(uint32_t)(
FTM_MOD_MOD(0x159F)
)) | (uint32_t)(
FTM_MOD_MOD(0xEA60)
));
/* FTM0_CNTIN: INIT=0 */
FTM0_CNTIN &= (uint32_t)~(uint32_t)(FTM_CNTIN_INIT(0xFFFF));
/* FTM0_DEADTIME: DTPS=0,DTVAL=0 */
FTM0_DEADTIME &= (uint32_t)~(uint32_t)(
FTM_DEADTIME_DTPS(0x03) |
FTM_DEADTIME_DTVAL(0x3F)
);
/* FTM0_CNT: COUNT=0 */
FTM0_CNT &= (uint32_t)~(uint32_t)(FTM_CNT_COUNT(0xFFFF));
/* FTM0_MODE: ??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,FAULTIE=1,FAULTM=2,PWMSYNC=0,INIT=0,FTMEN=1 */
FTM0_MODE = (uint32_t)((FTM0_MODE & (uint32_t)~(uint32_t)(
FTM_MODE_FAULTM(0x01) |
FTM_MODE_PWMSYNC_MASK |
FTM_MODE_INIT_MASK |
0xFFFFFF00U
)) | (uint32_t)(
FTM_MODE_FAULTIE_MASK |
FTM_MODE_FAULTM(0x02) |
FTM_MODE_FTMEN_MASK
));
/* FTM0_SYNCONF: ??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,SYNCMODE=0,??=0,SWOC=0,INVC=0,??=0,CNTINC=0,??=0,HWTRIGMODE=0 */
FTM0_SYNCONF &= (uint32_t)~(uint32_t)(
FTM_SYNCONF_SYNCMODE_MASK |
FTM_SYNCONF_SWOC_MASK |
FTM_SYNCONF_INVC_MASK |
FTM_SYNCONF_CNTINC_MASK |
FTM_SYNCONF_HWTRIGMODE_MASK |
0xFFE0E04AU
);
/* FTM0_SC: TOF=0,TOIE=0,CPWMS=0,CLKS=1,PS=0 */
FTM0_SC = (uint32_t)((FTM0_SC & (uint32_t)~(uint32_t)(
FTM_SC_TOF_MASK |
FTM_SC_TOIE_MASK |
FTM_SC_CPWMS_MASK |
FTM_SC_CLKS(0x02) |
FTM_SC_PS(0x07)
)) | (uint32_t)(
FTM_SC_CLKS(0x01)
));
/* FTM0_FMS: FAULTF=0,WPEN=0,FAULTF3=0,FAULTF2=0,FAULTF1=0,FAULTF0=0 */
FTM0_FMS &= (uint32_t)~(uint32_t)(
FTM_FMS_FAULTF_MASK |
FTM_FMS_WPEN_MASK |
FTM_FMS_FAULTF3_MASK |
FTM_FMS_FAULTF2_MASK |
FTM_FMS_FAULTF1_MASK |
FTM_FMS_FAULTF0_MASK
);
/* FTM0_SWOCTRL: CH3OC=0,CH2OC=0 */
FTM0_SWOCTRL &= (uint32_t)~(uint32_t)(
FTM_SWOCTRL_CH3OC_MASK |
FTM_SWOCTRL_CH2OC_MASK
);
/* FTM0_CONF: GTBEOUT=0,GTBEEN=0,BDMMODE=0,NUMTOF=0 */
FTM0_CONF &= (uint32_t)~(uint32_t)(
FTM_CONF_GTBEOUT_MASK |
FTM_CONF_GTBEEN_MASK |
FTM_CONF_BDMMODE(0x03) |
FTM_CONF_NUMTOF(0x1F)
);
/* FTM0_PWMLOAD: LDOK=0,CH7SEL=0,CH6SEL=0,CH5SEL=0,CH4SEL=0,CH3SEL=0,CH2SEL=0,CH1SEL=0,CH0SEL=0 */
FTM0_PWMLOAD &= (uint32_t)~(uint32_t)(
FTM_PWMLOAD_LDOK_MASK |
FTM_PWMLOAD_CH7SEL_MASK |
FTM_PWMLOAD_CH6SEL_MASK |
FTM_PWMLOAD_CH5SEL_MASK |
FTM_PWMLOAD_CH4SEL_MASK |
FTM_PWMLOAD_CH3SEL_MASK |
FTM_PWMLOAD_CH2SEL_MASK |
FTM_PWMLOAD_CH1SEL_MASK |
FTM_PWMLOAD_CH0SEL_MASK
);
Here’s more details on the example project. This comes from the included readme.txt:
This is an example project using FlexTimer (FTM) hardware
faults to disable the PWM output. The project is created
for the TWR-K20D72M Tower board, using KDS v2.0.0 and
Processor Expert (PEx).
PEx is used to configure FTM0 for PWM in combined mode,
with hardware fault enabled using the Init_FTM component.
The PWM signal is output on FTM0_CH2, which is pin PTC3.
This PWM can be captured with a scope on the Tower Elevator
header A38. Init_FTM component also configures hardware fault
input on FTM0 FAULT0 input, configured for the analog comparator
CMP0.
The comparator is configured using PEx component AnalogComp_LDD.
The positive input of the comparator is set to the external pin
CMP0_IN1, which is pin PTC7. On the Tower board, PTC7 is connected
to pin1 on the two-pin header J3. The comparator negative input
is connected to the internal 6-bit DAC reference. The DAC is
configured using the PEx component DAC_LDD to set the output at
half the supply voltage. The comparator output is also driven
out the output pin CMP0_OUT, which is pin PTC5. This signal
can be monitored with a scope on the Elevator header pin B62.
The FTM0 is configured to disable the PWM when the fault signal
is asserted, and the fault needs to be cleared manually by software.
The example code clears the fault in main().
To run the example, download the code to the Tower board and run.
Connect a scope to watch the PWM signal on PTC3. When the
comparator input signal pin1 on J3 is shorted to VSS, there is
no fault asserted, and the PWM waveform is present. When the short
to VSS on J3 is removed, the comparator output signal goes high
and the PWM signal stops. When the comparator input on J3 is shorted
to VSS again, the application clears the fault, and the PWM signal
- resumes.
NOTE: using the debugger to halt the MCU will stop the PWM. To see
the PWM operation, reset the MCU in the debugger and run without
halting, or run the MCU without the debugger from the application
in flash.