Combined_PWM problem using FTM + KSDK 2.0

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

Combined_PWM problem using FTM + KSDK 2.0

Jump to solution
2,157 Views
shauldorf
Contributor V

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

Labels (1)
0 Kudos
Reply
1 Solution
1,634 Views
xiangjun_rong
NXP TechSupport
NXP TechSupport

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;
}

View solution in original post

0 Kudos
Reply
6 Replies
1,634 Views
shauldorf
Contributor V

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);

FTM_C0V-Direct.jpg

FTM_C0V-Macro.jpg

Regards

Shaul

0 Kudos
Reply
1,634 Views
shauldorf
Contributor V

 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

0 Kudos
Reply
1,634 Views
xiangjun_rong
NXP TechSupport
NXP TechSupport

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

1,634 Views
xiangjun_rong
NXP TechSupport
NXP TechSupport

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

0 Kudos
Reply
1,634 Views
shauldorf
Contributor V

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

0 Kudos
Reply
1,635 Views
xiangjun_rong
NXP TechSupport
NXP TechSupport

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;
}

0 Kudos
Reply