Has anyone generated a PWM signal for a Kinetis without using PE?

cancel
Showing results for 
Search instead for 
Did you mean: 

Has anyone generated a PWM signal for a Kinetis without using PE?

3,774 Views
myke_predko
Senior Contributor III

Hi Folks,

I think the command line says it all - I want to generate PWMs for the K20 in my project that is running MQX 4.0. 

There seems to be a couple of threads discussing how to do it with Processor Expert, but I would like to do it without - has anybody been able to do this?

Thanx,

myke

Tags (4)
11 Replies

470 Views
Monica
Senior Contributor III

DRM135SW  :smileywink:


How is the project going Myke?

0 Kudos

470 Views
myke_predko
Senior Contributor III

Hi Monica,

It's an uphill climb, the prototype PWMs working and now trying to integrate:

  • DRM135SW (PWM)
  • AN4652 (Interrupt based I2C)
  • lwadc (multiple channels per ADC)

into my MQX bsp build. 

Once that's done, I will do more to characterize the PWM operation

Thanx for asking,

myke

470 Views
Monica
Senior Contributor III

GREAT!

Thanks for the update and keep the hard work along! :smileygrin:

We'll be around!

Best regards,

Monica.

0 Kudos

470 Views
myke_predko
Senior Contributor III

Hi Monica and Martin,

I've spent some time with the DRM135SW with problems writing to the PWM Count registers.  I've slightly modified the DRM135SW FTM0_init method in "peripherals_init" to output on FTM0_CH4 (PTD4) and FTM0_CH5 (PTDE) of the TWRK20D72M. 

What I am trying to do is two PWM outputs running automatically based on the FTM0 clock - I want to control the PWM duty cycles seperately by writing to the FTM0_C4V/_C5V registers (which I believe are the compare values).  Going further, I will get the actual PWM frequency I'm looking for as well as write dynamically to the _C4V/_C5V registers to change the duty cycle of the outputs (they are controlling DC motors). 

With the code below, there are no problems with the execution of the code (there was until I enabled the clock to FTM0) BUT the FTM0_C4V and FTM0_C5V registers are not updated by the writes (I tried to enable them using the FTM0_COMBINE register without any luck).  Also the output is a solid high output with a periodic partial low (from 3.3V "High") to 1.8V which I guess is the FTM0 timer overflowing/resetting - sorry I haven't been able to get its period, but it seems like it is on the order of 100s of milliseconds to seconds. 

I've left the DRM135SW code in (and commented out when I've modified it) so you can see the changes I've made. 

Any suggestions would be appreciated,

myke

#define TPM_C 48e6                        // core clock (only for calculation, I does not afect real clock)

#define PWM_FREQ 16000                    // PWM frequency [Hz]

#define MODULO ( TPM_C / PWM_FREQ ) // calculation the modulo for FTM0

//  Enable clock for Flex Timers

  SIM_SCGC6 |= SIM_SCGC6_FTM0_MASK;

  FTM0_MODE &= ~FTM_MODE_WPDIS_MASK; /* Disable write protection */

  FTM0_MODE |= FTM_MODE_FTMEN_MASK;

  FTM0_CONF |= FTM_CONF_BDMMODE(3); // counter run in BDM mode

  FTM0_MODE |= FTM_MODE_FAULTM_MASK | FTM_MODE_FTMEN_MASK;

  /* Set PWM frequency; MODULO = Fclk/Fpwm */

  FTM0_MOD = MODULO;

  FTM0_CNTIN = 0x00;

  FTM0_SYNCONF |= FTM_SYNCONF_SYNCMODE_MASK; // should be set enhanced PWM sync mode

  /* CTNMAX = 1 - PWM update at counter in max. value */

  FTM0_SYNC |= FTM_SYNC_CNTMAX_MASK;

  FTM0_SYNC |= FTM_SYNC_SWSYNC_MASK;  

  /* Disable all channels outputs using the OUTPUT MASK feature.

   However, the output pins are still driven as GPIO since the

   channel mode is set to FTM channel disabled after RESET */

  FTM0_OUTMASK = FTM_OUTMASK_CH7OM_MASK | FTM_OUTMASK_CH6OM_MASK | FTM_OUTMASK_CH5OM_MASK | FTM_OUTMASK_CH4OM_MASK |

   FTM_OUTMASK_CH3OM_MASK | FTM_OUTMASK_CH2OM_MASK | FTM_OUTMASK_CH1OM_MASK | FTM_OUTMASK_CH0OM_MASK;

  

//  /* COMBINE = 1 - combine mode set

//  COMP = 1 - complementary PWM set

//  DTEN = 1 - deadtime enabled

//  SYNCEN = 1 - PWM update synchronization enabled

//  FAULTEN = 1 - fault control enabled */ 

//  FTM0_COMBINE =  FTM_COMBINE_FAULTEN0_MASK | FTM_COMBINE_SYNCEN0_MASK | FTM_COMBINE_DTEN0_MASK

//                | FTM_COMBINE_COMP0_MASK | FTM_COMBINE_COMBINE0_MASK

//                | FTM_COMBINE_FAULTEN1_MASK | FTM_COMBINE_SYNCEN1_MASK | FTM_COMBINE_DTEN1_MASK

//                | FTM_COMBINE_COMP1_MASK | FTM_COMBINE_COMBINE1_MASK

//                | FTM_COMBINE_FAULTEN2_MASK | FTM_COMBINE_SYNCEN2_MASK | FTM_COMBINE_DTEN2_MASK

//                | FTM_COMBINE_COMP2_MASK | FTM_COMBINE_COMBINE2_MASK;

  FTM0_COMBINE =  FTM_COMBINE_FAULTEN2_MASK | FTM_COMBINE_SYNCEN2_MASK;

  

  /* polarity setting, 3ppa driver high sides are active low */

//  FTM0_POL = FTM_POL_POL0_MASK | FTM_POL_POL2_MASK | FTM_POL_POL4_MASK ;

   /* Dead time = 1 us for 48 MHz core clock */

//  FTM0_DEADTIME = 138;

   /* Initial setting of value registers */

//  FTM0_C0V = 0;

//  FTM0_C1V = MODULO/2;

//  FTM0_C2V = 0;  

//  FTM0_C3V = MODULO/2;

//  FTM0_C4V = 0;

//  FTM0_C5V = MODULO/2;

   FTM0_C4V = MODULO / 4;

   FTM0_C5V = MODULO / 2;

   /* SWSYNC = 1 - set PWM value update. This bit is cleared automatically */

    FTM0_SYNC |= FTM_SYNC_SWSYNC_MASK;  

   /* Main loop */

   /* ELSnB:ELSnA = 1:0 Set channel mode to generate positive PWM

   Note:

   1. From this moment the output pins are under FTM control. Since the PWM output is disabled by

   the FTM0OUTMASK register, there is no change on PWM outputs. Before the channel mode is set,

   the correct output pin polarity has to be defined.

   2. Even if the odd channels are generated automatically by complementary logic, these channels

   have to set to be in the same channel mode. */

  

//  FTM0_C0SC |= FTM_CnSC_ELSB_MASK ;

//  FTM0_C1SC |= FTM_CnSC_ELSB_MASK ;

//  FTM0_C2SC |= FTM_CnSC_ELSB_MASK ;

//  FTM0_C3SC |= FTM_CnSC_ELSB_MASK ;

   FTM0_C4SC |= FTM_CnSC_ELSB_MASK ;

   FTM0_C5SC |= FTM_CnSC_ELSB_MASK ;

  

   FTM0_EXTTRIG |= FTM_EXTTRIG_INITTRIGEN_MASK;

  

   FTM0_MODE |= FTM_MODE_INIT_MASK;

   /* Set system clock as source for FTM0 (CLKS[1:0] = 01) */

   FTM0_SC |= FTM_SC_CLKS(1); 

   //PORT for FTM0 initialization  

//  PORTC_PCR1 = PORT_PCR_MUX(4); // FTM0 CH0

//  PORTC_PCR2 = PORT_PCR_MUX(4); // FTM0 CH1

//  PORTC_PCR3 = PORT_PCR_MUX(4); // FTM0 CH2

//  PORTA_PCR6 = PORT_PCR_MUX(3); // FTM0 CH3

//  PORTA_PCR7 = PORT_PCR_MUX(3); // FTM0 CH4

//  PORTD_PCR5 = PORT_PCR_MUX(4); // FTM0 CH5 

   PORTD_PCR4 |= PORT_PCR_MUX(4) | PORT_PCR_DSE_MASK; // FTM0 CH4 with high power operation

   PORTD_PCR5 |= PORT_PCR_MUX(4) | PORT_PCR_DSE_MASK; // FTM0 CH5 with high power operation

0 Kudos

470 Views
myke_predko
Senior Contributor III

Sorry, one correction.  In the original post, I noted the output was always high - I just noticed that I had the motor circuit wired in and when I disconnected it, the two FTM0 PWM pins (PTD4 & PTD5) are always low.  I suspect that the low pulse is there but can't be seen unless the line is pulled up. 

myke

0 Kudos

470 Views
myke_predko
Senior Contributor III

Okay, I think I have something working.

I found that the DRM135SW doesn't seem to put the FTM0 Channels into PWM mode (which is why the writes to the _C4V/_C5V registers wouldn't accept the write values) as well as disabling the outputs which prevents them from coming out.

Two questions for you:

1.  Could you please review my code below and let me know if you have any concerns or comments for me?  It is basically the DRM135SW taken from the FTM0_init method in "peripherals_init.c" with some modifications to enable the FTM0 clock, select the PWMs I am interested in and making the changes specified above.

2.  Explain how the code in DRM135SW works?  As well as masking the output, I don't see any statements putting the FTM0 channels into PWM mode or turning off the masks of the outputs?

Next steps are to:

a) Retime operation with the correct clocking available in the TWRK20D72M

b) Change the PWM value dynamically to change the motor speeds

c) Invert the PWM output to allow simple motor reversal

Thanx,

myke

#define TPM_C 48e6                        // core clock (only for calculation, I does not afect real clock)

#define PWM_FREQ 16000                    // PWM frequency [Hz]

#define MODULO ( TPM_C / PWM_FREQ ) // calculation the modulo for FTM0

//  Enable clock for Flex Timers

// SIM_SCGC6 |= SIM_SCGC6_FTM0_MASK | SIM_SCGC6_FTM1_MASK;

  SIM_SCGC6 |= SIM_SCGC6_FTM0_MASK;


   FTM0_MODE &= ~FTM_MODE_WPDIS_MASK; /* Disable write protection */

   FTM0_MODE |= FTM_MODE_FTMEN_MASK;

   FTM0_CONF |= FTM_CONF_BDMMODE(3); // counter run in BDM mode

   FTM0_MODE |= FTM_MODE_FAULTM_MASK | FTM_MODE_FTMEN_MASK;

   /* Set PWM frequency; MODULO = Fclk/Fpwm */

   FTM0_MOD = MODULO;

   FTM0_CNTIN = 0x00;

   FTM0_SYNCONF |= FTM_SYNCONF_SYNCMODE_MASK; // should be set enhanced PWM sync mode

   /* CTNMAX = 1 - PWM update at counter in max. value */

   FTM0_SYNC |= FTM_SYNC_CNTMAX_MASK;

   FTM0_SYNC |= FTM_SYNC_SWSYNC_MASK; 

    /* Disable all channels outputs using the OUTPUT MASK feature.

   However, the output pins are still driven as GPIO since the

   channel mode is set to FTM channel disabled after RESET */

//  FTM0_OUTMASK = FTM_OUTMASK_CH7OM_MASK | FTM_OUTMASK_CH6OM_MASK | FTM_OUTMASK_CH5OM_MASK | FTM_OUTMASK_CH4OM_MASK |

//  FTM_OUTMASK_CH3OM_MASK | FTM_OUTMASK_CH2OM_MASK | FTM_OUTMASK_CH1OM_MASK | FTM_OUTMASK_CH0OM_MASK;

   FTM0_OUTMASK = FTM_OUTMASK_CH7OM_MASK | FTM_OUTMASK_CH6OM_MASK |

   FTM_OUTMASK_CH3OM_MASK | FTM_OUTMASK_CH2OM_MASK | FTM_OUTMASK_CH1OM_MASK | FTM_OUTMASK_CH0OM_MASK;

 

//  /* COMBINE = 1 - combine mode set

//  COMP = 1 - complementary PWM set

//  DTEN = 1 - deadtime enabled

//  SYNCEN = 1 - PWM update synchronization enabled

//  FAULTEN = 1 - fault control enabled */

//  FTM0_COMBINE =  FTM_COMBINE_FAULTEN0_MASK | FTM_COMBINE_SYNCEN0_MASK | FTM_COMBINE_DTEN0_MASK

//                | FTM_COMBINE_COMP0_MASK | FTM_COMBINE_COMBINE0_MASK

//                | FTM_COMBINE_FAULTEN1_MASK | FTM_COMBINE_SYNCEN1_MASK | FTM_COMBINE_DTEN1_MASK

//                | FTM_COMBINE_COMP1_MASK | FTM_COMBINE_COMBINE1_MASK

//                | FTM_COMBINE_FAULTEN2_MASK | FTM_COMBINE_SYNCEN2_MASK | FTM_COMBINE_DTEN2_MASK

//                | FTM_COMBINE_COMP2_MASK | FTM_COMBINE_COMBINE2_MASK;

  //  MAP: Put the FTM0 Channels into PWM mode

  FTM0_C4SC |= FTM_CnSC_MSB_MASK;

  FTM0_C5SC |= FTM_CnSC_MSB_MASK;

   /* polarity setting, 3ppa driver high sides are active low */

//  FTM0_POL = FTM_POL_POL0_MASK | FTM_POL_POL2_MASK | FTM_POL_POL4_MASK ;

   /* Dead time = 1 us for 48 MHz core clock */

//  FTM0_DEADTIME = 138;

//  Notes (12) - Just using Channels 4 & 5

   /* Initial setting of value registers */

//  FTM0_C0V = 0;

//  FTM0_C1V = MODULO/2;

//  FTM0_C2V = 0; 

//  FTM0_C3V = MODULO/2;

//  FTM0_C4V = 0;

//  FTM0_C5V = MODULO/2;

   FTM0_C4V = MODULO / 4;

   FTM0_C5V = MODULO / 2;

   /* SWSYNC = 1 - set PWM value update. This bit is cleared automatically */

    FTM0_SYNC |= FTM_SYNC_SWSYNC_MASK; 

   /* Main loop */

   /* ELSnB:ELSnA = 1:0 Set channel mode to generate positive PWM

   Note:

   1. From this moment the output pins are under FTM control. Since the PWM output is disabled by

   the FTM0OUTMASK register, there is no change on PWM outputs. Before the channel mode is set,

   the correct output pin polarity has to be defined.

   2. Even if the odd channels are generated automatically by complementary logic, these channels

   have to set to be in the same channel mode. */

 

//  FTM0_C0SC |= FTM_CnSC_ELSB_MASK ;

//  FTM0_C1SC |= FTM_CnSC_ELSB_MASK ;

//  FTM0_C2SC |= FTM_CnSC_ELSB_MASK ;

//  FTM0_C3SC |= FTM_CnSC_ELSB_MASK ;

   FTM0_C4SC |= FTM_CnSC_ELSB_MASK ;

   FTM0_C5SC |= FTM_CnSC_ELSB_MASK ;

 

   FTM0_EXTTRIG |= FTM_EXTTRIG_INITTRIGEN_MASK;

 

   FTM0_MODE |= FTM_MODE_INIT_MASK;

   /* Set system clock as source for FTM0 (CLKS[1:0] = 01) */

   FTM0_SC |= FTM_SC_CLKS(1);

   //PORT for FTM0 initialization 

//  PORTC_PCR1 = PORT_PCR_MUX(4); // FTM0 CH0

//  PORTC_PCR2 = PORT_PCR_MUX(4); // FTM0 CH1

//  PORTC_PCR3 = PORT_PCR_MUX(4); // FTM0 CH2

//  PORTA_PCR6 = PORT_PCR_MUX(3); // FTM0 CH3

//  PORTA_PCR7 = PORT_PCR_MUX(3); // FTM0 CH4

//  PORTD_PCR5 = PORT_PCR_MUX(4); // FTM0 CH5

   PORTD_PCR4 |= PORT_PCR_MUX(4) | PORT_PCR_DSE_MASK; // FTM0 CH4 with high power operation

   PORTD_PCR5 |= PORT_PCR_MUX(4) | PORT_PCR_DSE_MASK; // FTM0 CH5 with high power operation

470 Views
myke_predko
Senior Contributor III

Something for the community; I just tried to port the code above to a MK20DN512VLL10 and discovered that it wouldn't work.

The problem is in the statements:

PORTD_PCR4 |= PORT_PCR_MUX(4) | PORT_PCR_DSE_MASK; // FTM0 CH4 with high power operation

PORTD_PCR5 |= PORT_PCR_MUX(4) | PORT_PCR_DSE_MASK; // FTM0 CH5 with high power operation

Note that the statements above OR the new mux value and DSE mask to the contents of PCR registers - it does not replace them.  For proper operation, the code should be:

PORTD_PCR4 = PORT_PCR_MUX(4) | PORT_PCR_DSE_MASK; // FTM0 CH4 with high power operation

PORTD_PCR5 = PORT_PCR_MUX(4) | PORT_PCR_DSE_MASK; // FTM0 CH5 with high power operation

Something to watch for when you get other people's code.

myke

470 Views
ivanlovas
NXP Employee
NXP Employee

Hi Myke,

The DRM135SW utilize FlexTimer in Complementary mode. You should write only into FTM0_C1V , FTM0_C3V, FTM0_C5V. The channel 2,4 and 6 are generated automatically. Value registers are buffered you should write LDOK bit after you change a values registers. ( for example : FTM0_PWMLOAD = 0x200 ; ) . After inicialization of FlexTimer PWM outputs are disabled. You should write FTM0_OUTMASK = 0; to enable all PWM outputs. I'm not sure but maybe you also need to set sync trigger. FTM0_SYNC |= FTM_SYNC_SWSYNC_MASK;

0 Kudos

470 Views
myke_predko
Senior Contributor III

Hi Junior,

One last comment.  To change the _C#V registers of the Kinetis, you must turn off the PWM to the channel to be able to write to the register.  For example:

    FTM0_SC &= ~FTM_SC_CLKS(1); 

    FTM0_C4V = MODULO / 2;

    FTM0_SC |= FTM_SC_CLKS(1); 

    _time_delay(500);

    FTM0_SC &= ~FTM_SC_CLKS(1); 

    FTM0_C4V = MODULO;

    FTM0_SC |= FTM_SC_CLKS(1); 

Writing to the FTM0_OUTMASK register didn't update the FTM0_C4V register, nor did other registers that I tried. 

myke

470 Views
myke_predko
Senior Contributor III

Hi Junior,

Thank you for the reply.

For my application, I do not require the complementary mode - I was just trying to get two individual PWM channels working.

I did find the FTM0_OUTMASK = 0; statement - it is used in the ENABLE_PWM_OUTPUT_PADS() macro.  Thanx for the pointer.

FTM0_PWMLOAD = 0x200; does NOT allow writing to the _C4V/_C5V registers.  The only thing that I have found that does allow writing is the FTM0_C#SC |= FTM_CnSC_MSB_MASK; statement (where "#" is "4" and "5" for my application) which puts the channel into PWM mode.  This actually makes sense when I look at the datasheet because otherwise I don't see how the FT channels are in PWM mode (unless you know something better). 


In any case, the code above works and can hopefully be a reference point for other people looking for simple PWM outputs.


myke


0 Kudos

470 Views
Martin_
NXP Employee
NXP Employee

Hi Myke, check AN4376 and DRM135. DRM135SW zip has source codes, there is FTM0_init() in peripherals.c - Flex Timer 0 initialization for 3-phase PWM signal generation.

0 Kudos