Simple PWM pulse string on KEA128

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

Simple PWM pulse string on KEA128

Jump to solution
2,030 Views
tomsparks
Contributor III

I'm having an issue getting this to work on a FRDM-KEAZ1284Q80 evaluation board.
I'm attempting to make a simple unending pulse train of square waves at 1kHz.
It does make pulses but in an odd pattern. 

It also will not produce a complementary pulse train on channel 0, only channel 1.
Here is the code I'm using:

/* START CODE HERE:*/


/* SET UP CLOCKS, GPIO, AND FTM TO MAKE A SIMPLE PULSE TRAIN*/


#include "derivative.h" // include KEA128 declarations

int main(void)
{

GPIOC_PIDR |= 0x030000; // PTC0 and PTC1 are NOT inputs
GPIOC_PDDR |= 0x030000; // PTC0 and PTC1 ARE outputs
/*--------------------------------------------------------------*/
SIM_PINSEL1 &= ~(0x0F); // [FTM2PS1] [FTM2PS0] Output to PTC1 AND PTC0
SIM_SCGC |= 0x80; // [FTM2] Provide system clock to FTM2
/*--------------------------------------------------------------*/
FTM2_MODE |= (0x04); // [WPDIS] Disable write-protect
/*--------------------------------------------------------------*/
FTM2_MODE &= ~(0x01); // [FTMEN] Make sure TPM is selected
FTM2_MODE |= 0x02; // [INIT] Initialize output
FTM2_SC |= 0x0F; // [CLKS] [PS] Clock Select PreScaler set to divide by 128
FTM2_CONF |= 0x0C0; // [BDMMODE] Run in debug mode
FTM2_CNTIN |= 0x00; // [INIT] Initial Value Of The FTM Counter
FTM2_CNT |= 0x01; // [COUNT] Initialize CNTIN
FTM2_OUTINIT |= 0x03; // [CH0OI](0) [CH1OI](1) Set initial values in channels
FTM2_MOD |= 0x0FFFF; // [MOD] Modulo value beyond match value so no flag set
FTM2_C0SC|= 0x2F; // [MSB] [MSA] [ELSB] [ELSA] CH0
FTM2_C1SC |= 0x2F; // [MSB] [MSA] [ELSB] [ELSA] CH1
FTM2_C0V |= 0x0177; // [VAL] Match value
FTM2_C1V |= 0x0177; // [VAL] Match value
/*--------------------------------------------------------------*/
FTM2_FMS |= 0x40; // [WPEN] Re-enable write-protect

for(;;) {} // Infinite loop

return 0;
}


/* END CODE HERE*/

I've tried many variations upon what you see here with only slightly different results.

I've tried commenting out all code that did not significantly change the result. Nothing different happened. 

It seems like I'm missing something pretty fundamental about the KE/KEA or the FTM/TPM, but I've searched all over the Reference Manual and I can't figure out what it is.  Does anyone see anything wrong here? 

Thank you for reading.

Tags (3)
0 Kudos
1 Solution
1,822 Views
tomsparks
Contributor III

I did figure this one out.  It came partly from reading a sentence in the reference manual about the order in which the CNT, and MOD registers should be initialized, and another discussion here where an engineer was having trouble getting the FTM to consistently initialize on a KE02.  Again, that had to do with the order in which things needed to be initialized.  I cannot find the post, but The NXP engineer said he initialized the CnV continuously and it worked fine.  So I did the same with a line of code in the endless loop at the bottom of my code, and it worked fine.  I got to thinking it had something to do with a dependency the CoV had with other registers.   

Once I got it working dependably and predictably, I designed a set of tests to figure out what orders of initialization were important, and which were not.  First, the NOT important one.  I saw a few references in posts here to when the clock should be gated to the FTM module via SIM_SCGC.  All advice said the gate should be opened at the end.  I tested that,  I found it didn't matter.  I put it right after main() and at the very last instruction before the an endless loop.  It worked both ways.  It seems the FTM module didn't care as long as it got a clock somewhere in the code.  At this time I also went trough my code and tested what was absolutely necessary and what was not. Since I had selected a mode that was more like the TPM than the FTM, there was no write protection so all that went out.  I started my timer from 0 each overflow, so I didn't need the CNTIN to tell CNT where to start each time.  And on it went, commenting out and checking function.  It really takes a surprisingly small bit of code to make a nice square wave on this thing.

What DID matter was the placement of the CnV within the code.  It needs to go after the MOD is initialized.  It doesn't matter where, it just needed to be somewhere AFTER the MOD was initialized. However, I did find one thing a little puzzling about the CnV register.  I found that  S32DS (through the EmbSys plugin) did not show the contents of the CnV register if the CnV was initialized before the clock was gated to the module. So from that standpoint, you could say the clock gating should be initialized first. But it would produce perfectly controllable pulses that could be set to any frequency and duty cycle desired no matter where the clock was gated to the module.

To summarize:

1)  Open the clock gate first. ( SIM_SCGC [FTMx] )

2)  Set the CNT register   ( FTMx_CNT [COUNT] )

3)  Set the MOD register  ( FTMx_MOD [MOD] )

4)  Set the CnV register last  ( FTMx_CnV [VAL] )

Thanks,

Tom

View solution in original post

6 Replies
1,823 Views
tomsparks
Contributor III

I did figure this one out.  It came partly from reading a sentence in the reference manual about the order in which the CNT, and MOD registers should be initialized, and another discussion here where an engineer was having trouble getting the FTM to consistently initialize on a KE02.  Again, that had to do with the order in which things needed to be initialized.  I cannot find the post, but The NXP engineer said he initialized the CnV continuously and it worked fine.  So I did the same with a line of code in the endless loop at the bottom of my code, and it worked fine.  I got to thinking it had something to do with a dependency the CoV had with other registers.   

Once I got it working dependably and predictably, I designed a set of tests to figure out what orders of initialization were important, and which were not.  First, the NOT important one.  I saw a few references in posts here to when the clock should be gated to the FTM module via SIM_SCGC.  All advice said the gate should be opened at the end.  I tested that,  I found it didn't matter.  I put it right after main() and at the very last instruction before the an endless loop.  It worked both ways.  It seems the FTM module didn't care as long as it got a clock somewhere in the code.  At this time I also went trough my code and tested what was absolutely necessary and what was not. Since I had selected a mode that was more like the TPM than the FTM, there was no write protection so all that went out.  I started my timer from 0 each overflow, so I didn't need the CNTIN to tell CNT where to start each time.  And on it went, commenting out and checking function.  It really takes a surprisingly small bit of code to make a nice square wave on this thing.

What DID matter was the placement of the CnV within the code.  It needs to go after the MOD is initialized.  It doesn't matter where, it just needed to be somewhere AFTER the MOD was initialized. However, I did find one thing a little puzzling about the CnV register.  I found that  S32DS (through the EmbSys plugin) did not show the contents of the CnV register if the CnV was initialized before the clock was gated to the module. So from that standpoint, you could say the clock gating should be initialized first. But it would produce perfectly controllable pulses that could be set to any frequency and duty cycle desired no matter where the clock was gated to the module.

To summarize:

1)  Open the clock gate first. ( SIM_SCGC [FTMx] )

2)  Set the CNT register   ( FTMx_CNT [COUNT] )

3)  Set the MOD register  ( FTMx_MOD [MOD] )

4)  Set the CnV register last  ( FTMx_CnV [VAL] )

Thanks,

Tom

1,822 Views
tomsparks
Contributor III

Thank you for your reply, but none of that seemed to work for me.

However, I WAS addressing the wrong part of GPIO for Port C:

The part:

GPIOC_PIDR |= 0x030000;       // PTC0 and PTC1 are NOT inputs
GPIOC_PDDR |= 0x030000;     // PTC0 and PTC1 ARE outputs

It should have been:

GPIOA_PIDR |= 0x030000;        // PTC0 and PTC1 are NOT inputs
GPIOA_PDDR |= 0x030000;      // PTC0 and PTC1 ARE outputs

But strangely, when that was changed, the same thing came out of the output.

This leads me to suspect what I'm getting out of the output has little relation to my code.


I also added this part for complementary output:

FTM2_COMBINE |= ~(0x020);  // [COMP0] Make channels complementary
FTM2_COMBINE |= 0x01;        // [COMBINE0] Combine channels

It made no difference


There were a few other register bits I corrected for the combine PWM to function:

FTM2_C0SC|= 0x2F;             // [MSB] [MSA] [ELSB] [ELSA] CH0
FTM2_C1SC |= 0x2F;            // [MSB] [MSA] [ELSB] [ELSA] CH1

Became:

FTM2_C0SC|= 0x08;            // [MSB] [MSA] [ELSB] [ELSA] CH0
FTM2_C1SC |= 0x08;           // [MSB] [MSA] [ELSB] [ELSA] CH1

but nothing I did gave the expected result.


I managed to try another FRDM-KEAZ12Q80 board, and I had exactly the same result. So, it's likely the board is OK.


I did find an Application Note (AN5303 - Features and Operation Modes of FlexTimer Module on S32K ) and while the code was not compatible, it did explain the FTM more completely than the Reference Manual.

Thanks again for your help,

Tom

0 Kudos
1,822 Views
bobpaddock
Senior Contributor III

" nothing I did gave the expected result."

Use of OR in |= is not going to give the expected results.
Most of these should be assignments with '='
or in some cases &= when '~' is involved.

NXP consistently and incorrectly uses '|=' in many of their examples.
At best this wastes code space and at worse is dangerous if it clears a status bit at the wrong time.

0 Kudos
1,822 Views
tomsparks
Contributor III

So, if I read you correctly, since OR-EQUALS has certain pitfalls, the entire register should be copied, tested, modified if need be, and replaced whole using an "=" operator as if, for example, you wanted to set bit seven in SOME_REG:

...

/*       SETTING BIT 7 IN SOME_REG WITHOUT USING "|="      */

uint32_t BIT_MASK_7 = 0x080;                   // 0x080 = 0b1000 0000 = 128

uint32_t HELPFUL_VAR = 0;                       // CONTAINER FOR COPY OF SOME_REG

HELPFUL_VAR = SOME_REG;                  // MAKE COPY OF SOME_REG

HELPFUL_VAR &= ~( BIT_MASK_7 )         // CLEAR BIT ON COPY

if ( HELPFUL_VAR == SOME_REG ) {      // TEST WHETHER COPY == ORIGINAL AFTER CLEARING BIT 7

   SOME_REG += BIT_MASK_7; }            // IF SAME THEN BIT 7 ON SOME_REG IS NOT SET SO SET IT

                                                                   // BUT IF ALREADY SET THEN JUST MOVE ON

Is that something like what you had in mind?  Is there a shorter way to do this?

I understand that sometimes bits in one register can be set simply by writing to another register. And sometimes bits in one register can be cleared simply by setting a bit in another register.  But just out of curiosity, how would a status bit potentially get cleared by the OR-EQUAL method?  It seems pretty specific in the bits that it changes and in the direction it changes them. That is, from clear to set or set to no effect.

0 Kudos
1,822 Views
bobpaddock
Senior Contributor III

Using |= with registers that are 'Write-1-to-clear' (W1C in the data sheet data fields) is where the issue comes up.
Using |= for the example you just gave is fine.
|= 0x80U will set Bit-7.

|= will cause a, usually unintentional, read of the register.
This read could impact status bits that require a certain sequence to clear them.
This comes up mostly in interrupt handling, especially with nested interrupts (always best to avoid those).
It is also a waste of time and code space when clearing a W1C bit.

For a W1C Bit-7:

reg = 0x80U;

and

reg |= 0x80U

will both clear the bit.
The assignment is smaller, faster, less dangerous than the |= version.

0 Kudos
1,822 Views
nxf56274
NXP Employee
NXP Employee

Hi,

FTM2_SC |= 0x0F, you should set the clock at last step. When set the clock, the ftm will start counting.My suggestion is that you can use the K64's SDK about FTM example to check where you make the mistake.

Have a great day,
TIC

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

- We are following threads for 7 weeks after the last post, later replies are ignored
Please open a new thread and refer to the closed one, if you have a related question at a later point in time.
-------------------------------------------------------------------------------

0 Kudos