Cannot get quadrature decoder to work on K64F FTM2 using KSDK PEx/BM MQX

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

Cannot get quadrature decoder to work on K64F FTM2 using KSDK PEx/BM MQX

Jump to solution
3,482 Views
dave408
Senior Contributor II

I've been trying everything I can think of to get quadrature decoding to work with my FRDM-K64F board, running KDS 2.0, KSDK 1.1, MQX, and a combination of PEx and bare metal code.

 

I started by using the fsl_ftm component with the following settings:

 

 

 

21525_21525.pngpastedImage_2.png

 

 

21527_21527.pngpastedImage_4.png

 

21528_21528.pngpastedImage_5.png

 

21529_21529.pngpastedImage_6.png

 

21530_21530.pngpastedImage_7.png

 

 

 

With these settings, FTM2_CNT is always 0.

 

I have confirmed that the encoder works because I have my scope hooked up to PTB18 and PTB19 and can see the signals there.

 

I then found another post here on the forum that outlined the bare metal configuration.  So I changed my task code to configure FTM2 (perhaps it can't be done in a task?), and the results are the same.  Here is the code I used.  Note that I only modified registers where the already-set value by PEx looked reasonable.

 

void qd_task( task_param_t initial_data)

{

    static uint16_t counter = 0;

    uint32_t sim_scgc6 = SIM_SCGC6;

    uint32_t ftm2_mode = FTM2_MODE;

    FTM2_CONF |= FTM_CONF_BDMMODE(3);

    uint32_t ftm2_conf = FTM2_CONF;

    FTM2_MOD = 4095;

    uint32_t ftm2_mod = FTM2_MOD;

    uint32_t ftm2_cntin = FTM2_CNTIN;

    uint32_t ftm2_qdctrl = FTM2_QDCTRL;

    FTM2_SC |= 0x08; // use system clock

    uint32_t ftm2_sc = FTM2_SC;

 

    while(1) {

        if( FTM2_CNT != counter) {

            counter = FTM2_CNT;

        }

    }

}

 

After running up to the while loop, here are the register values:

 

sim_scgc6    uint32_t    0x64000001  

ftm2_mode    uint32_t    0x5  

ftm2_conf    uint32_t    0xc0  

ftm2_mod    uint32_t    0x0  

ftm2_cntin    uint32_t    0x0  

ftm2_qdctrl    uint32_t    0xcd  

ftm2_sc    uint32_t    0x9  

 

Can anyone suggest something for me to try to debug this further?

Original Attachment has been moved to: qd_pex_mqx.zip

1 Solution
1,926 Views
dave408
Senior Contributor II

Ok, so I think I have it figured out.  At least to the point where I'll be able to make some more progress.  My original theory about why FMT2_CNT wasn't getting updated was correct, and it all came down to FMT2_MOD.  If it's 0, the counter isn't going to increment.  Although that does make sense, it would be nice to see this in a note in the reference manual because it'll probably help out some people.

But perhaps more importantly, I feel like it would have been nice to make the updating process of FMT2_MOD a little more clear.  I followed the flowchart and set the registers as indicated, but could never get the register to update.

In the end, I used the following method, which was so obvious and easy, yet it took me at least two days of banging my head against the wall to realize how it could be done.  And as far as I could tell, the manual doesn't offer this option in a note.  It's definitely implied, but sometimes just telling the user something directly is a nice thing to do.  :smileyhappy:

The solution is to simple disable FMT before setting FMTx_MOD.  That's it.

Here's all you have to do:

  • FMT2_MODE |= FTM_MODE_WPDIS_MASK (disable write protection)
  • FTM2_SC &= ~0x18 (disable FMT)
  • FTM2_MOD = 0xFFFF (set to max value)
  • SIM_SCGC6 |= SIM_SCGC6_FTM2_MASK (not sure which really enables FTM2 gate)
  • SIM_SCGC3 |= SIM_SCGC3_FTM2_MASK (not sure which really enables FTM2 gate)
  • PORTB_PCR18 = PORT_PCR_MUX(6) | PORT_PCR_PFE_MASK (PEx should have done this, though... need to check)
  • PORTB_PCR19 = PORT_PCR_MUX(6) | PORT_PCR_PFE_MASK
  • FTM2_SC |= 0x08 (enable FMT with system clock)

Now you'll be able to see FMT2_CNT change when you spin the encoder.

I still have to go back through my code and determine what PEx does for me, and what I have to explicitly change myself.  In addition, I need to write an ISR to handle the counter rollover conditions.

View solution in original post

0 Kudos
15 Replies
1,929 Views
ryanbrainard
Contributor I

Hi Dave408,

So, I have encountered the same issue and thanks to your post, I narrowed in on MOD=0 as well.

I did however find a way to use the PEx to fix it. There is a function called "FTM_DRV_CounterStart" that needs to be called prior to starting the quad encoder. This function will preset the MOD. But, it can only be done while the decode is not running (i.e. FTM_DRV_QuadDecodeStart has not yet been called or you stopped the decoder with FTM_DRV_QuadDecodeStop).

Step 1: Remove the auto initialization of the encoder

  • Uncheck the auto initialization for Quadrature Decode Initialization in PEx->Initialization.
    • Otherwise a FTM_DRV_QuadDecodeStart will be added to CPU.c and you can't write to MOD or start the counter.

Step 2: Manually initialize the encode.

  • Add a counter start, THEN the Quad Decoder Start to the main routine before you call your RTOS task

<code>

//in main.c -> main()

.....

  PE_low_level_init();

  /*** End of Processor Expert internal initialization.                    ***/

  FTM_DRV_CounterStart(Encoder_IDX, kCounting_FTM_UP, 0x0, 0xffff, false);

  FTM_DRV_QuadDecodeStart(Encoder_IDX, &Encoder_QdConfig0, &Encoder_QdConfig0, kFtmQuadCountAndDir);

.....

</code>

-Ry

1,929 Views
dave408
Senior Contributor II

Hi ryanbrainard​, thanks for sharing this.  I will give it a try when I get a chance and will post up my results.  This is great info!

0 Kudos
1,929 Views
auralcircuitry
Contributor II

So I'm in the same position now as you were before finding this solution, where I have set up Processor Expert to enable FTM2 for quadrature mode but the counter does not increment or decrement as I turn the encoder.

I've gone and added your code suggestions to my initialization procedure (I am also using FTM2 on the K64), and I can verify that FTM2_MOD is successfully written to and has the value 0xFFFF, yet the counter still doesn't update.  I'm sure there is a step that I'm missing but I'm not sure that your posted code contains the complete solution.

Did you ultimately not use the Processor Expert code for initialization and do everything through baremetal?  If so, would you be willing to share some of that code?  Alternatively, if you did keep the PEx settings, could you explain further what the actual issue is?

Any help is very much appreciated, thanks!

0 Kudos
1,929 Views
dave408
Senior Contributor II

Sorry for the late response, Kris!  Are you using KDS 2.0 or KDS 3.0?  I don't know how much of a difference it makes, but I am currently using 3.0 and my post above isn't entirely accurate.

I am using the PE configuration along with the bare metal "pre configuration" shown in my previous post.

To be honest, I'm not sure what the "External clock pin" setting is used for, yet.  I think it works either way, but my current project (3.0 based) actually has it enabled.  My KDS 2.0 project had it disabled.  More importantly though, in the initialization tab of the Component Inspector, did you enable QD initialization and also check the "Install interrupts" checkbox?  When using MQX, you *always* have to check that box so MQX can properly handle interrupts in the driver code.

1,929 Views
auralcircuitry
Contributor II

Thanks Dave, I am using KDS 3.0 with KSDK 1.2.  I agree that a clocking issue does seem like a possible culprit, though I'm not able to select the "External clock pin" setting in the PEx FTM block as that pin is already defined by the clock_manager to set up "System oscillator 0" to 120 MHz from a 12 MHz external crystal.

My assumption is that this same system clock should be available to the FTM blocks by default, but it's entirely possible that this needs to be set elsewhere.  I mean, we are explicitly setting the clock source with the line "FTM2_SC |= 0x08; //enable FMT with system clock)," so who knows.

When you say "pre configuration" do you mean that you are running your configuration settings before the Processor Expert init code happens?  I'm currently doing it after...  Will keep trying things.

0 Kudos
1,929 Views
dave408
Senior Contributor II

I meant before the OS starts running, so I put all of the code I showed here right after PE_low_level_init().  The clocking shouldn't be the problem since, as you have said, it is explicitly set.  Are you able to post your project here?

0 Kudos
1,927 Views
dave408
Senior Contributor II

Ok, so I think I have it figured out.  At least to the point where I'll be able to make some more progress.  My original theory about why FMT2_CNT wasn't getting updated was correct, and it all came down to FMT2_MOD.  If it's 0, the counter isn't going to increment.  Although that does make sense, it would be nice to see this in a note in the reference manual because it'll probably help out some people.

But perhaps more importantly, I feel like it would have been nice to make the updating process of FMT2_MOD a little more clear.  I followed the flowchart and set the registers as indicated, but could never get the register to update.

In the end, I used the following method, which was so obvious and easy, yet it took me at least two days of banging my head against the wall to realize how it could be done.  And as far as I could tell, the manual doesn't offer this option in a note.  It's definitely implied, but sometimes just telling the user something directly is a nice thing to do.  :smileyhappy:

The solution is to simple disable FMT before setting FMTx_MOD.  That's it.

Here's all you have to do:

  • FMT2_MODE |= FTM_MODE_WPDIS_MASK (disable write protection)
  • FTM2_SC &= ~0x18 (disable FMT)
  • FTM2_MOD = 0xFFFF (set to max value)
  • SIM_SCGC6 |= SIM_SCGC6_FTM2_MASK (not sure which really enables FTM2 gate)
  • SIM_SCGC3 |= SIM_SCGC3_FTM2_MASK (not sure which really enables FTM2 gate)
  • PORTB_PCR18 = PORT_PCR_MUX(6) | PORT_PCR_PFE_MASK (PEx should have done this, though... need to check)
  • PORTB_PCR19 = PORT_PCR_MUX(6) | PORT_PCR_PFE_MASK
  • FTM2_SC |= 0x08 (enable FMT with system clock)

Now you'll be able to see FMT2_CNT change when you spin the encoder.

I still have to go back through my code and determine what PEx does for me, and what I have to explicitly change myself.  In addition, I need to write an ISR to handle the counter rollover conditions.

0 Kudos
1,929 Views
egoodii
Senior Contributor III

Sorry I didn't catch your query in time to be of any help!  These register-order things can get mighty tricky....

If it helps, I have attached my full, current driver file.  Contains a fair bit of stuff that works with my larger context, but I think the ISRs might help you.  They include 'index edge capture' as well -- for my linear encoders, one-edge is sufficient.

You mention MQX -- I recommend you do NOT allow MQX to intercept this IRQ.  For both the 'rollover' and 'index edge' interrupt functions, you have NO protection from 'multiple hits', of which you 'might miss some', except for 'raw speed'.  I also place this interrupt at a higher priority than ANY other IRQs to insure response-time.  I have the 'nagging worry' that halting right at an index or rollover step could 'bounce' at ANY rate the electronics allow, and we have a limit to how fast we can 'keep up'.  To mitigate this potential A LITTLE, I preset my FTM counter (PRESUMED physical 'zero' point from power-up or 'zero' operation) at one-half of full-count.

1,929 Views
dave408
Senior Contributor II

Hi Earl, no worries, I just kept fighting through the reference manual and eventually had that revelation regarding disabling of the FTM before setting FTMx_MOD.  That ended up being the easiest solution for me that actually worked.

Thank you for attaching your project.  After I test my proof-of-concept code on another Kinetis part (K22 this time), I'll take a stab at using the ISR to handle rollover.  I don't need the index pulse (yet), but I expect your code to be extremely helpful.  And thank you for your advice regarding ISRs and the RTOS.  :smileyhappy:

0 Kudos
1,929 Views
dave408
Senior Contributor II

I just noticed that FTM2_MOD is still 0 even after I set it to 4095, so perhaps that's the problem.  I'll have to look up info on restrictions around writing this register.

0 Kudos
1,929 Views
mjbcswitzerland
Specialist V

Dave

I would check out this (concerning the MOD register):

Writes to it are saved to an intermediate register and only transferred to the MOD register when an update trigger becomes true.

Regards

Mark

0 Kudos
1,929 Views
dave408
Senior Contributor II

Before I left the office, I read that MOD updates depend upon CLKS, so I next will check into the MCG_C1 register to see what the value is at the time I try to update.  That will determine the method in which I update MOD.  There is a nice large flowchart that explains it.  Sure is a lot more complicated than I would have expected.  :smileyhappy:

pastedImage_1.png

0 Kudos
1,929 Views
dave408
Senior Contributor II

The interesting thing here is that I just checked CLKS[1:0] and it's 0.  According to the following table, doesn't that then mean that FTM2_MOD should update as soon as I write it?

pastedImage_141.png

0 Kudos
1,929 Views
mjbcswitzerland
Specialist V

Dave

In such cases I would expect a missing clock (as comparison: if one writes a pin to its UART MUX state but it stays at value '0' it gives the impressions that the MUX is not operating. But if the UART has a programmable clock that hasn't been set it does this - once the clock is configired/enabled/selected the pin then moves to its expected '1' level).

Another thing to look at is the compatibiity setting in the FlexTimer. The default is to work in backward compatibility mode - with TPM (which is a sub-set of FlexTimer) and the extended functions are only operational when this compatibility mode is disabled (or the advanced mode enabled).

Regards

Mark

0 Kudos
1,929 Views
dave408
Senior Contributor II

Thanks for the suggestions, Mark.  You could be right, but according to the registers I have looked at so far, everything looks right, so perhaps I missed one.  To summarize what I had posted earlier (and adding a couple of other registers), this is the breakdown:

sim_scgc6    uint32_t    0x64000001    (RTC access and interrupts enabled, FTM2 clock gate control enabled, flash memory clock gate control enabled)

ftm2_mode    uint32_t    0x5    (channel events occured on channel 2 & 0)

ftm2_conf    uint32_t    0xc0    (BDM mode 3 - strange since PEx is set to mode 0)

ftm2_synconf    uint32_t    0x1fb4    (need to read up on this, this indicates that hardware triggers are used for some reason, but I had those checkboxes unchecked in PEx)

syncmode    _Bool    0x1   

mcg_c1    uint8_t    0x6    (FLL uses slow internal reference clock, MCGIRCLK active)

ftm2_mod    uint32_t    0x0    (this is what I *think* needs to be set to a non-zero value for ftm2_cnt to increment)

ftm2_cntin    uint32_t    0x0   

ftm2_qdctrl    uint32_t    0xcd    (phase A filter enable, phase B filter enable, count and direction encoding mode, counting direction is increasing, quadrature decoder mode enable)

ftm2_sc    uint32_t    0x9 (system clock, divide by 2)

0 Kudos