The test is based on NXP community “https://community.nxp.com/thread/428308”: <SDK_2.0_MK64FN1M0xxx12>\boards\twrk64f120m\driver_examples\ftm\pwm_twochannel.
My test board is FRDM-K64F and Software KDS 3.2 + KSDK v2.0.
I modified this example to test three PWM’s operation modes: edge, center and combined.
The first two are working O.K.
With the third (combined) is a problem, there is no PWM output.
Looking in “EmbSys Registers” debugger window the CnV registers are zero (no configuration).
When I configured those registers directly FTM output behavior was as expected.
Is there any problem with the ….. , kFTM_CombinedPwm, … API or something is missing in my attached program?
// Init board hardware.
BOARD_InitPins();
BOARD_BootClockRUN();
BOARD_InitDebugConsole();
// Add your code here
ftm_config_t ftmInfo;
ftm_chnl_pwm_signal_param_t ftmParam[4];
FTM_GetDefaultConfig(&ftmInfo);
// Configure ftm params with frequency 24kHZ
ftmParam[0].chnlNumber = (ftm_chnl_t)Channel_0;
ftmParam[0].level = kFTM_LowTrue;
ftmParam[0].dutyCyclePercent = 10U;
ftmParam[0].firstEdgeDelayPercent = 10u;
ftmParam[1].chnlNumber = (ftm_chnl_t)Channel_1;
ftmParam[1].level = kFTM_LowTrue;
ftmParam[1].dutyCyclePercent = 40U;
ftmParam[1].firstEdgeDelayPercent = 40u;
ftmParam[2].chnlNumber = (ftm_chnl_t)Channel_2;
ftmParam[2].level = kFTM_LowTrue;
ftmParam[2].dutyCyclePercent = 50U;
ftmParam[2].firstEdgeDelayPercent = 50u;
ftmParam[3].chnlNumber = (ftm_chnl_t)Channel_3;
ftmParam[3].level = kFTM_LowTrue;
ftmParam[3].dutyCyclePercent = 90U;
ftmParam[3].firstEdgeDelayPercent = 90u;
/* Initializes the FTM module. */
FTM_Init(FTM_BASEADDR, &ftmInfo);
/* Mode Select */
// Edge Aligned PWM
// FTM_SetupPwm(FTM_BASEADDR, ftmParam, 4U, kFTM_EdgeAlignedPwm, 1000U, FTM_Clock_src);
// Center Aligned PWM
// FTM_SetupPwm(FTM_BASEADDR, ftmParam, 4U, kFTM_CenterAlignedPwm, 1000U, FTM_Clock_src);
// Combined Aligned PWM
FTM_SetupPwm(FTM_BASEADDR, ftmParam, 4U, kFTM_CombinedPwm, 1000U, FTM_Clock_src);
/* Complementary Enable */
FTM_SetComplementaryEnable(FTM_BASEADDR, 0,1);
// FTM_SetComplementaryEnable(FTM_BASEADDR, 1,1);
// FTM_SetComplementaryEnable(FTM_BASEADDR, 2,1);
// FTM_SetComplementaryEnable(FTM_BASEADDR, 3,1);
/* Direct CnV configuration n=0, 1, 2, 3 */
FTM0->CONTROLS[0].CnV=0x1000;
FTM0->CONTROLS[1].CnV=0x4000;
FTM0->CONTROLS[2].CnV=0x5000;
FTM0->CONTROLS[3].CnV=0xc000;
FTM_StartTimer(FTM_BASEADDR, kFTM_SystemClock);
Thanks,
Shaul
Solved! Go to Solution.
Hi,
Although I do not know which register take effect on accessing the CnV registers, after a test foralong time, I found that it is okay if you put the writting CnV code at the end of for() loop. I modify the code here.
The code is correct for the branch if ((mode == kFTM_EdgeAlignedPwm) || (mode == kFTM_CenterAlignedPwm)). The code is wrong for only "else" in the above condition.
Hope it can help you.
BR
XiangJun Rong
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 |= (FTM_CnSC_ELSA(chnlParams->level) | FTM_CnSC_ELSB(chnlParams->level));
/* 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;
}
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 |= (FTM_CnSC_ELSA(chnlParams->level) | FTM_CnSC_ELSB(chnlParams->level));
/* 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; //Rong commented
//base->CONTROLS[(chnlParams->chnlNumber * 2) + 1].CnV = cnvFirstEdge + cnv; //Rong commented
/* Set the combine bit for the channel pair */
base->COMBINE |=
(1U << (FTM_COMBINE_COMBINE0_SHIFT + (FTM_COMBINE_COMBINE1_SHIFT * chnlParams->chnlNumber)));
///////////////////////////////////////////////////////////////////////////////////////////Rong modified,
base->CONTROLS[chnlParams->chnlNumber*2].CnV=cnvFirstEdge;
base->CONTROLS[(chnlParams->chnlNumber*2) + 1].CnV=cnvFirstEdge + cnv;
//////////////////////////////////////////////////////////////////////////////////////Rong modified,
}
#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
chnlParams++;
}
return kStatus_Success;
}
Hello Rong
I have additional question regarding to FTM PWM configuration
I’m trying to configure FTM registers using MK6412F.h “macros definitions” instead direct configuration (without macro) .
The only problem that I have is with FTM_CnSC register configuration.
When I use direct configuration there is channel #0, but when I use macro to configure FTM_CnSC channel #0 is not working.
I’m doing something wrong
For reference please see my source and EMSysRegister screen capture.
In register capture screen you can see tha
/* Add your code here */
SIM->SCGC5 |= 0x800;
PORTC->PCR[1] = 0x400;
PORTC->PCR[2] = 0x400;
SIM->SCGC6|=0x03000000; //enable FTM0 and FTM0 module clock
// FTM0->CONF=0xC0; //Direct register set up BDM in 11
FTM0->CONF = FTM_CONF_BDMMODE(3u);
// FTM0->FMS=0x00; //clear the WPEN so that WPDIS is set
FTM0->FMS=FTM_FMS_WPEN(0);
// FTM0->MODE|=0x05; //enable write the FTM CnV register
FTM0->MODE = FTM_MODE_FTMEN(1);
FTM0->MODE = FTM_MODE_WPDIS(1);
// FTM0->MOD=1000;
FTM0->MOD = FTM_MOD_MOD(1000);
// FTM0->CONTROLS[0].CnSC = 0x28; //edge-alignment
/* +++++++++ problem +++++++ */
FTM0->CONTROLS[0].CnSC = FTM_CnSC_ELSB(1); // Not working
FTM0->CONTROLS[0].CnSC = FTM_CnSC_MSB(1); // Not working
FTM0->CONTROLS[1].CnSC = 0x28;
// FTM0->COMBINE=0x00; //combine mode for CH0&CH1 of FTM0
FTM0->COMBINE = FTM_COMBINE_COMBINE0(1);
// FTM0->COMBINE=0x02; //complement mode for CH0&CH1 of FTM0
FTM0->COMBINE = FTM_COMBINE_COMP0(0);
// FTM0->CONTROLS[0].CnV = 500;
FTM0->CONTROLS[0].CnV = FTM_CnV_VAL(250);
FTM0->CONTROLS[1].CnV = FTM_CnV_VAL(500);
// FTM0->CNTIN = 0;
FTM0->CNTIN = FTM_CNTIN_INIT(0);
// FTM0->SC=0x08; //PWM edge_alignment, system clock dividing by 1
FTM0->SC=FTM_SC_CLKS(1);
Regards
Shaul
Rong Thanks :smileyhappy::smileycheck:
Your solution is working fine.
Is it correct that hardware implementation limits the delay to
Delay[%] + PW[%] < 100%
B.R.
Shaul
Hi, Shasul,
i think the answer is YES. As you know that in Combined mode, you have to meet the condition:
0x0000<=FTM_C2nV<FTM_C2n+1V<FTM_MOD<0xFFFF
In the code of FTM_SetupPwm() function.
cnvFirstEdge = (mod * chnlParams->firstEdgeDelayPercent) / 100;
cnv = (mod * chnlParams->dutyCyclePercent) / 100;
base->CONTROLS[chnlParams->chnlNumber*2].CnV=cnvFirstEdge;
base->CONTROLS[(chnlParams->chnlNumber*2) + 1].CnV=cnvFirstEdge + cnv;
From above code, you see that dutyCyclePercent + dutyCyclePercent must be less than or equal to 100.
Hope it can help you.
BR
XiangJun Rong
Hi, Shaul,
I see your issue, I faced the same issue, I tried to use pointer to access the CnV register, but failed. The phenomenon is weird.
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)
{
............................
static uint32_t* temp;
temp=&base->CONTROLS[chnlParams->chnlNumber * 2].CnV;
*(uint16_t*)temp= cnvFirstEdge;
temp=&base->CONTROLS[(chnlParams->chnlNumber * 2) + 1].CnV;
*temp= cnvFirstEdge + cnv;
}
I am investigating the cause.
Currently, pls use FTM0->CONTROLS[0].CnV=0x1000; to access the CnV register as a workaround.
BR
XiangJun Rong
Thanks;
Have you estimation if it will be solved.
In general I prefer to use pointer access.
In my project I want to change CnV (ftmParam[n].firstEdgeDelayPercent) during program runtime. Using percentage (%) is easier (of curse if the API SetupPwm in fsl_ftm.c will be corrected).
BR
Shaul
Hi,
Although I do not know which register take effect on accessing the CnV registers, after a test foralong time, I found that it is okay if you put the writting CnV code at the end of for() loop. I modify the code here.
The code is correct for the branch if ((mode == kFTM_EdgeAlignedPwm) || (mode == kFTM_CenterAlignedPwm)). The code is wrong for only "else" in the above condition.
Hope it can help you.
BR
XiangJun Rong
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 |= (FTM_CnSC_ELSA(chnlParams->level) | FTM_CnSC_ELSB(chnlParams->level));
/* 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;
}
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 |= (FTM_CnSC_ELSA(chnlParams->level) | FTM_CnSC_ELSB(chnlParams->level));
/* 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; //Rong commented
//base->CONTROLS[(chnlParams->chnlNumber * 2) + 1].CnV = cnvFirstEdge + cnv; //Rong commented
/* Set the combine bit for the channel pair */
base->COMBINE |=
(1U << (FTM_COMBINE_COMBINE0_SHIFT + (FTM_COMBINE_COMBINE1_SHIFT * chnlParams->chnlNumber)));
///////////////////////////////////////////////////////////////////////////////////////////Rong modified,
base->CONTROLS[chnlParams->chnlNumber*2].CnV=cnvFirstEdge;
base->CONTROLS[(chnlParams->chnlNumber*2) + 1].CnV=cnvFirstEdge + cnv;
//////////////////////////////////////////////////////////////////////////////////////Rong modified,
}
#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
chnlParams++;
}
return kStatus_Success;
}