TRK-KEA64 PWM Generation with FTM

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

TRK-KEA64 PWM Generation with FTM

3,142 Views
curiosul
Contributor III

Hi there!

I am trying to generate a PWM signal using a TRK-KEA64 board. My project is using SKEAZN642 headers.

The approach was to use the FTM driver provided in Quick Start Package. But I can't really make it works!

My main.c code:

#include "FTM/FTM.h"
#include "GPIO/GPIO.h"
#include "CLK/CLK.h"

void ftm_callback()
{
     FTM_ClrOverFlowFlag(FTM2);
     OUTPUT_TOGGLE(PTC, PTC0);
}

void pwmSetDutyCycle(FTM_Type* pFTM, uint8_t ftmChannel, uint16_t dutyCycle /*0-1000*/)
{
     uint32_t dc = pFTM->MOD - (pFTM->MOD) / (1000 / dutyCycle);
     FTM_SetChannelValue(pFTM, ftmChannel, dc);
}

void PWM_Init(FTM_Type* pFTM, uint8_t ftmChannel, uint32_t clkFreqHz, uint32_t freqHz)
{
     /* FTM Config */
     FTM_ConfigType FTM_Config = { 0 };
     FTM_Config.modulo = 10000;                                   /* Timer count */
     FTM_Config.clk_source = FTM_CLOCK_SYSTEMCLOCK;          /* Set clocking source */
     FTM_Config.prescaler = FTM_CLOCK_PS_DIV1;               /* Clock divider */
     FTM_Config.mode = 1;                                        /* Edge aligned PWM */
     FTM_Config.toie = 0;                                        /* enable channel interrupt */

     /* FTM Channel Config */
     FTM_ChParamsType FTM_CH_Config = { 0 };
     FTM_CH_Config.ctrl.bits.bMode = FTM_PWMMODE_EDGEALLIGNED;     /* PWM Setting */
     FTM_CH_Config.ctrl.bits.bPWMPol = FTM_PWM_HIGHTRUEPULSE;     /* Another PWM Setting */
     FTM_CH_Config.u16CnV = (FTM_Config.modulo);                         /* 0% dutycycle by default */

     /* Init FTM module */
     FTM_Init(pFTM, &FTM_Config);

     /* Set interrupt */
     FTM_SetCallback(pFTM, ftm_callback);
     /* Init channel */
     FTM_ChannelInit(pFTM, ftmChannel, FTM_CH_Config);
}

int main(void)
{
     FTM_Type *pFTM = FTM2;
     uint8_t ftmChannel = 0;

     /* Config pin as output */
     CONFIG_PIN_AS_GPIO(PTC, PTC0, OUTPUT);

     /* Select Pins corresponds to the PTC0 for output */
     SIM_PINSEL |= SIM_PINSEL_FTM2PS0_MASK;

     /* Init PWM driver */
     PWM_Init(pFTM, ftmChannel, DEFAULT_SYSTEM_CLOCK, 10000 /*Hz*/);

     /* Set duty cycle */
     pwmSetDutyCycle(pFTM, ftmChannel, 500);     /* DutyCycle - 0 is 0% and 1000 is 100% */

     while (1)
     {
     }
}

The interrupt was disabled intentionally!

The thing is that the code works perfectly on FRDM-KEAZ64 and the duty-cycle function is also working like a charm! But on TRK-KEA64 it does not! I observed actually that anything it's harder to do on TRK-KEA64 board than the others.

When interrupts are enabled, I can toogle the pin to generate an PWM with 50% duty-cycle but this is not what I want and I think doing this I will just misunderstand the purpose of FTM driver.

Any hint or idea? A working example of this would be highly appreciated!

PS: The example from QSP works on FRDM-KEAZ64 but not on TRK-KEA64!

PS2: I have also attached and example project!

PS3: It only seems to work with processor expert but I can't really figure out how and I can't really use PE in my project!

10 Replies

2,434 Views
curiosul
Contributor III

In the meanwhile I managed to make a very basic PWM driver with no dependencies, in case someone else is reading this thread!

The code:

#include "derivative.h"

void PWM_Init(FTM_Type *pFTM, uint32_t u32clock, uint16_t u16periodUS)
{
     /* SIM_SCGC: pFTM=1 */
     SIM_SCGC |= ((pFTM == FTM0) ? SIM_SCGC_FTM0_MASK : (pFTM == FTM1 ? SIM_SCGC_FTM1_MASK : SIM_SCGC_FTM2_MASK));

     if ( pFTM == FTM2 )
     {
          /* Set up mode register */
          /* FTM_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=0,FAULTM=0,CAPTEST=0,PWMSYNC=0,WPDIS=1,INIT=0,FTMEN=0 */
          pFTM->MODE = (FTM_MODE_FAULTM(0x00) | FTM_MODE_WPDIS_MASK);

          /* Clear counter initial register */
          /* FTM_CNTIN: ??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,INIT=0 */
          pFTM->CNTIN = FTM_CNTIN_INIT( 0x00 );
     }

     /* Clear status and control register */
     /* FTM_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 */
     pFTM->SC = (FTM_SC_CLKS(0x00) | FTM_SC_PS( 0x00 ));

     /* Reset counter register */
     /* FTM_CNT: ??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,COUNT=0 */
     pFTM->CNT = FTM_CNT_COUNT( 0x00 );

     /* Clear channel status and control register */
     /* FTM_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,??=0 */
     FTM_CnSC_REG(pFTM, 0) = 0x00U;

     /* Clear channel status and control register */
     /* FTM_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,??=0 */
     FTM_CnSC_REG(pFTM, 1) = 0x00U;

     if ( pFTM == FTM2 )          // only FTM2 have more than 2 Channels
     {
          /* Clear channel status and control register */
          /* FTM_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,??=0 */
          FTM_CnSC_REG(pFTM, 2) = 0x00U;

          /* Clear channel status and control register */
          /* FTM_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,??=0 */
          FTM_CnSC_REG(pFTM, 3) = 0x00U;

          /* Clear channel status and control register */
          /* FTM_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,??=0 */
          FTM_CnSC_REG(pFTM, 4) = 0x00U;

          /* Clear channel status and control register */
          /* FTM_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,??=0 */
          FTM_CnSC_REG(pFTM, 5) = 0x00U;
     }

     /* Set up modulo register */
     /* FTM_MOD: ??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,MOD=0x3E7F */
     //pFTM->MOD = FTM_MOD_MOD(0x3E7F);
     pFTM->MOD = FTM_MOD_MOD( u32clock/2/(1000000/u16periodUS) );

     if ( pFTM == FTM2 )
     {
          /* Set up Initial State for Channel Output register */
          /* FTM_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,CH7OI=0,CH6OI=0,CH5OI=0,CH4OI=0,CH3OI=0,CH2OI=0,CH1OI=0,CH0OI=1 */
          pFTM->OUTINIT = FTM_OUTINIT_CH0OI_MASK;

          /* Initialize the Output Channels */
          /* FTM_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=0,FAULTM=0,CAPTEST=0,PWMSYNC=0,WPDIS=1,INIT=1,FTMEN=0 */
          pFTM->MODE = (FTM_MODE_FAULTM(0x00) | FTM_MODE_WPDIS_MASK | FTM_MODE_INIT_MASK);
     }
}

void PWM_Enable(FTM_Type *pFTM, uint8_t channel)
{
     /* Set up channel status and control register */
     /* FTM_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=1,MSA=0,ELSB=1,ELSA=0,??=0,??=0 */
     FTM_CnSC_REG(pFTM, channel) = (FTM_CnSC_MSB_MASK | FTM_CnSC_ELSB_MASK);

     /* Set up channel value register - 0 default duty--cycle*/
     /* FTM_C0V: ??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,??=0,VAL=0 */
     FTM_CnV_REG(pFTM, channel) = FTM_CnV_VAL( 0x00 );

     /* Initialize clocks, prescalers and other timer stuff */
     pFTM->SC |= 1 << 5;     // CPWMS (Center-Aligned PWM Select): 0 = counting up, 1 = counting down
     pFTM->SC |= 0 << 6;     // TOIE (Timer Overflow Interrupt Enable): 0 = disabled, 1 = enabled
     pFTM->SC |= 0 << 4;     // CLKS (Clock Source Selection) 4-3: 00 - No clock selected, 01 - System clock
     pFTM->SC |= 1 << 3;     // CLKS (Clock Source Selection) 4-3: 10 - Fixed frequency clock, 11 - External clock
     pFTM->SC |= 0 << 2;     // PS (Prescale Factor Selection):
     pFTM->SC |= 1 << 1;     // PS (Prescale Factor Selection):
     pFTM->SC |= 1 << 0;     // PS (Prescale Factor Selection):
//                    321
//                    000 Divide by 1
//                    001 Divide by 2
//                    010 Divide by 4
//                    011 Divide by 8               V
//                    100 Divide by 16
//                    101 Divide by 32
//                    110 Divide by 64
//                    111 Divide by 128
}

void PWM_SetRatio16(FTM_Type *pFTM, uint8_t ftmChannel, uint16_t ratio)
{
     uint16_t period;
     uint16_t duty;

     // Retrieve period from FTM module
     period = (uint16_t)(pFTM->MOD) + 1;

     if ( period == 0U )
     {
          duty = ratio;
     }
     else
     {
          duty = (uint16_t)((((uint32_t)(period) * ratio) + 0x8000) >> 0x10);
     }

     // Write new duty
     FTM_CnV_REG(pFTM, ftmChannel) = (uint32_t)duty;
}

int main()
{
     /* FTM module and it's channel */
     FTM_Type *pFTM = FTM1;
     uint8_t channel = 1;

     /* Init pwm driver */
     PWM_Init( pFTM, 16000000/8 /*Clock in Hz with selected prescaler*/, 1000 /*Period in US*/ );

     /* Emable PWM on a given channel*/
     PWM_Enable( pFTM, channel);

     PWM_SetRatio16( pFTM, channel, 10000 );

     int i, j;
     while ( 1 )
     {
          for ( i = 0; i <= 65535; i++ )
          {
               PWM_SetRatio16( pFTM, channel, i );
               for ( int j = 0; j <= 10; j++ )
                    ;
          }

          for ( i = 65534; i >= 1; i-- )
          {
               PWM_SetRatio16( pFTM, channel, i );
               for ( int j = 0; j <= 10; j++ )
                    ;
          }
     }
}

It works like a charm on FTM0 and FTM2! With the FTM1 I think something is wrong!

Please have a look at pinselect section:

FTM2:

SIM_PINSEL &= (uint32_t)~(uint32_t)(SIM_PINSEL_FTM2PS0_MASK);

FTM0:

SIM_PINSEL |= SIM_PINSEL_FTM0PS0_MASK;

Later edit:

Original code: [C] #include "derivative.h" void PWM_Init(FTM_Type *pFTM, uint32_t u32clock, uint - Pastebin.com 

0 Kudos

2,434 Views
mjbcswitzerland
Specialist V

Hi

I would review the section

/* Select PIN automatically */

because it looks to be incorrect in two ways:
- You are switching pins based on channels, which is not the idea of the pin options.
- In some cases you are only ORing and in some case only ANDing so changes during operation would fail.

Regards

Mark

2,434 Views
curiosul
Contributor III

I ended up by removing that part! You were right, the PWM is provided to default routes without PINSEL!

Thank you!

0 Kudos

2,434 Views
Robin_Shen
NXP TechSupport
NXP TechSupport

Would you please tell us which IDE(version) are you using for this project?
So that I can direct test it on TRK-KEA64(RevB) or (RevD).

Please shows us the Part Number and Mask Set of the onboard KEA64. If the two pieces are the same, I don't think these two boards will show different phenomenon.

Mark has point out that you are configure PTH0 as FTM2_CH0 function.(For the PWM function usage, you don't need to configure it as GPIO function.)  I don't know why you toggle PTC0 in FTM interrupt.

Best Regards,

Robin

 

-----------------------------------------------------------------------------------------------------------------------
Note: If this post answers your question, please click the Correct Answer button. Thank you!
-----------------------------------------------------------------------------------------------------------------------

2,434 Views
curiosul
Contributor III

I am using KDSv3!

Board info: SCH-28199-REV D

And about the pin toggle, it does not matter anyway as interrupts are disabled!

0 Kudos

2,434 Views
mjbcswitzerland
Specialist V

Hi Alex

Your problem may be with
 /* Select Pins corresponds to the PTC0 for output */
     SIM_PINSEL |= SIM_PINSEL_FTM2PS0_MASK;

This selects the FTM2_Ch0 output to be on pin PTH0.

See the simulation of your setup in the uTasker TRK-KEA64/KEA64 simulator below:

pastedImage_1.png

Regards

Mark

uTasker developer and supporter (+5'000 hours experience on +60 Kinetis derivatives in +80 product developments)
Kinetis: http://www.utasker.com/kinetis.html
http://www.utasker.com/kinetis/FRDM-KEAZ64Q64.html
http://www.utasker.com/kinetis/TRK-KEA64.html

2,434 Views
curiosul
Contributor III

Thank you for your fast replay and for taking time to test my project!

I am really confused. According to KEA64 Sub-Family Reference Manual PTC0 is on FTM2_Ch0.

It looks like this:

ptc0_skeazn64.png

0 Kudos

2,434 Views
mjbcswitzerland
Specialist V

Hi

The default location of FTM2_CH0 is PTC0 but if you set
SIM_PINSEL |= SIM_PINSEL_FTM2PS0_MASK;

you are switching it to its alternative location of PTH0.

Regards

Mark

2,434 Views
curiosul
Contributor III

Does it mean that without using SIM_PINSEL register, the FTM2_Ch0 shall provide PWM by default on port PTC0?

0 Kudos

2,434 Views
mjbcswitzerland
Specialist V

If you leave the SIM_PINSEL register in the state that it has out of reset it selects PTC0.
It is good practice to still set it to its required state.