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!
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
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
I ended up by removing that part! You were right, the PWM is provided to default routes without PINSEL!
Thank you!
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!
-----------------------------------------------------------------------------------------------------------------------
I am using KDSv3!
Board info: SCH-28199-REV D
And about the pin toggle, it does not matter anyway as interrupts are disabled!
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:
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
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:
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
Does it mean that without using SIM_PINSEL register, the FTM2_Ch0 shall provide PWM by default on port PTC0?
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.