Curious stuff about the Flex Timer Module in Teensy 3.x (3.1 at least)

Status
Not open for further replies.

robsoles

Well-known member
I wanted a free running 32 bit (unsigned, of course) timer with greater 'granularity' than micros(); so I started looking and settled on FTM1 as being the thing to use.

First curiosity: If the FTM_SC_TOF bit is not reset very early in the interrupt handler routine the interrupt fires again for a reason I have not yet been able to become positive about - I think tho that it is more likely related to FTM_SC_TOF than anything else.

Second curiosity: This one is really curious to me; at first glance it seems that the inclusion of a line which, afaict, does not even get executed and simply reads FTM1_CNT into a variable (or would, if it were being executed :p) also makes it stop re-firing the extra interrupt event regardless of where/when FTM_SC_TOF is reset in the handler routine.

Because the ftm1_isr is an all inclusive interrupt I made the (tester) handler check for other events firing it, in a very limited way at first (flagOdd on its own) and then decided to expand that to try to identify what the extraneous event(s) was(/were) - nothing became apparent, I think it must be the interrupt controller re-firing because of FTM_SC_TOF but it remains curious to me.

Code:
#define JOHNNY_BE_STRANGE 0
#define MAKE_IT_GO_AWAY 0

typedef union {
	uint32_t value;
	struct {
		uint16_t lo;
		uint16_t hi;
	};
} convert32to16;


volatile convert32to16 timer1;
volatile uint32_t flagOdd=0,flagEven=0;
volatile uint16_t flagVal=0;


void ftm1_isr(void)
{
  if(FTM1_SC&FTM_SC_TOF)
  {
#if MAKE_IT_GO_AWAY!=0
    FTM1_SC&=~FTM_SC_TOF;
    timer1.hi++;
    // flagEven++; // the value of timer1.value being close to 12000000 indicates interrupt firing no probs.
#else
    timer1.hi++;
    flagEven++; // even with this line removed, under circumstances where flagOdd advances at all, flagOdd still advances.
    FTM1_SC&=~FTM_SC_TOF;
#endif
  } else if(FTM1_FMS&191) {
    flagOdd|=((FTM1_FMS&255)<<8); // never seen this go off but leaving it here just in case.
  } else {
    flagOdd++;
#if JOHNNY_BE_STRANGE!=0 // really seems strange to me that the presence of the following line
    flagVal=FTM1_CNT; // seems to make the second interrupt event fail to occur even though
#endif // I cannot see how this line executes in light of flagOdd never advancing when this line
  } // is included in compilation.
}

elapsedMillis freddy;

void setup()
{
  Serial.begin(Serial.baud());
  delay(5000);
  FTM1_MODE=FTM_MODE_WPDIS;
  FTM1_MODE|=FTM_MODE_FTMEN;
  FTM1_SC = 0; // disabled while I write some values to it.
  FTM1_CNT=0;
  FTM1_MOD=0xFFFF;
  FTM1_C0SC=0; // these three lines are only here
  FTM1_C1SC=0; // as part of my initial (vague-ish) 
  FTM1_QDCTRL=0; // attempt to kill the extra interrupt.
  
  FTM1_SC = FTM_SC_TOIE|(1<<3)|2; // (use system clock, divide clock by 4, have an overflow interrupt)
  FTM1_MODE&=~FTM_MODE_FTMEN;
  // Higher priority for my purpose.
  NVIC_SET_PRIORITY(IRQ_FTM1, 64);
  NVIC_ENABLE_IRQ(IRQ_FTM1);
}


void loop()
{
  
  if(Serial.available())
  {
    switch(Serial.read())
    {
    case 'a':
      Serial.printf("FTM1_MODE: %u\n",FTM1_MODE);
      Serial.printf("FTM1_C0SC: %u\n",FTM1_C0SC);
      Serial.printf("FTM1_C1SC: %u\n",FTM1_C1SC);
      Serial.printf("FTM1_STATUS: %u\n",FTM1_STATUS);
      Serial.println();
    break;
    }
  }
  if(freddy>999)
  {
    timer1.lo=FTM1_CNT;
    uint32_t jebus=freddy;
    freddy=0;
    FTM1_CNT=0;
    Serial.printf("Millis: %u, FTM1_CNT: %u (%u,%u,%u)\n",jebus,timer1.value,flagEven,flagOdd,flagVal);
    timer1.value=0;
    flagEven=0;
    flagOdd=0;
    flagVal=0;
  }
}
So, if you make JOHNNY_BE_STRANGE non zero flagOdd stops indicating any hits, let alone the same number of hits as flagEven. If instead MAKE_IT_GO_AWAY is made non zero it doesn't fire the unidentifiable interrupt either.

My final version of the interrupt handler is based on these findings
Code:
void ftm1_isr(void)
{

	if(FTM1_SC&FTM_SC_TOF)
	{
		FTM1_SC&=~FTM_SC_TOF;
		Timer.hi++;
	}

}
So, anybody willing to take a stab at curiosity #2?

I'm pretty sure about the first curiosity but anybody with a firmer idea of why resetting the overflow flag earliest in isr execution makes the extraneous event stop occuring would be very cool to hear from. Seems to have to be first (after checking it is set) thing done, I've seen { timer1.hi++; FTM1_SC&=~FTM_SC_TOF; } do the same re-fire as { timer1.hi++; FTM1_SC&=~FTM_SC_TOF; flagEven++; }
 
Last edited:
It seems like the secondary interrupt triggers whenever the instruction to clear the interrupt flag is the last instruction in the ISR. This is probably due to the latency for the write to FTMx_SC to trickle out through the crossbar switch and peripheral bridge (a few cycles), through the FTM and back to the NVIC, while the instruction to return from the ISR is the very next thing executed. If the interrupt signal to the NVIC isn't de-asserted before the interrupt returns, then it will set the interrupt to pending again and re-execute (from section 6.2.2 of the cortex-M4 tech ref manual):

For level interrupts, if the signal is not deasserted before the return from the interrupt routine,
the interrupt again enters the pending state and re-activates. This is particularly useful for FIFO
and buffer-based devices because it ensures that they drain either by a single ISR or by repeated
invocations, with no extra work. This means that the device holds the signal in assert until the
device is empty.

If you do any bus reads after you write the FTMx_SC_TOF flag, then the core stalls until the read completes (a few cycles for the peripheral/RAM to respond back through the crossbar switch and possibly the peripheral bridge). This gives the flag write time to propagate back through the FTM and de-assert the interrupt flag at the NVIC before the ISR returns.

Edit: RAM reads/writes don't go through the peripheral bridge, so it is lower latency than writing to the FTM, which is probably why incrementing flageven after clearing the FTM flag still results in the interrupt executing again.
 
Last edited:
teensy 3.0 has slightly different behavior, slower memory ???

Code:
         teensy 3.1
        flags 0
        Millis: 1000, FTM1_CNT: 11999988 (183,183,0)
        Millis: 1000, FTM1_CNT: 11999997 (183,183,0)
        STRANGE 1
        Millis: 1000, FTM1_CNT: 11999996 (183,0,0)
        Millis: 1000, FTM1_CNT: 11999988 (183,0,0)
        GOAWAY 1
        Millis: 1000, FTM1_CNT: 11999993 (0,0,0)
        Millis: 1000, FTM1_CNT: 11999997 (0,0,0)

         teensy 3.0
        flags 0
        Millis: 1000, FTM1_CNT: 11999991 (183,0,0)  differs from 3.1
        Millis: 1000, FTM1_CNT: 11999987 (183,0,0)
        STRANGE 1
        Millis: 1000, FTM1_CNT: 11999992 (183,0,0)
        Millis: 1000, FTM1_CNT: 11999995 (183,0,0)
        GOAWAY 1
        Millis: 1000, FTM1_CNT: 11999993 (0,0,0)
        Millis: 1000, FTM1_CNT: 11999993 (0,0,0)
 
Last edited:
Regarding to TOF, there's an interesting errata.

e6484: FTM: The process of clearing the FTMx_SC[TOF] bit does not work as expected
under a certain condition when the FTM counter reaches FTM_MOD value.
Errata type:
Errata
Description:
The process of clearing the TOF bit does not work as expected when
FTMx_CONF[NUMTOF] != 0 and the current TOF count is less than FTMx_CONF[NUMTOF],
if the FTM counter reaches the FTM_MOD value between the reading of the TOF bit and the
writing of 0 to the TOF bit. If the above condition is met, the TOF bit remains set, and if the
TOF interrupt is enabled (FTMx_SC[TOIE] = 1), the TOF interrupt also remains asserted.
Workaround:
Two possible workarounds exist for this erratum and the decision on which one to use is based
on the requirements of your particular application.
1) Repeat the clearing sequence mechanism until the TOF bit is cleared.
Below is a pseudo-code snippet that would need to be included in the TOF interrupt routine.
while (FTM_SC[TOF]!=0)
{
void FTM_SC() ; // Read SC register
FTM_SC[TOF]=0 ; // Write 0 to TOF bit
}
2) With FTMx_CONF[TOFNUM] = 0 and a variable in the software, count the number of times
that the TOF bit is set. In the TOF interrupt routine, clear the TOF bit and increment the
variable that counts the number of times that the TOF bit was set.
 
Teensy 3.1 at different speeds:

Result with 24 MHz:
Millis: 1000, FTM1_CNT: 5999992 (91,0,0)

Result with 168 MHz:
Millis: 1000, FTM1_CNT: 13999425 (213,213,0)
 
Thanks for showing me those, whollender & Frank, I hope somebody has a go at #2 but clarification of #1 is good and you've both pointed out important parts of available documentation I hadn't paid enough attention to yet. That is interesting, the result you get at 168Mhz Frank. The result for 24Mhz is unsurprising tho.

@manitou: Edit: Oh, closer read and I see you are comparing both to each other, I actually don't see those lists as being very different - seen over a longer period (the average of results for 100 seconds) would be more revealing but it doesn't seem unreasonable that they are a little different and probably differ as much between a pair of Teensy 3.1s as much as between Teensy 3.1 and 3.0 Sorry, probably should have posted the contents of my terminal window after executing the code a couple of different ways (at least one) so you would have seen that those results you are getting look quite normal to me - between a couple of cycles to reset the variables and a couple of cycles lost as far as the FTM1_CNT is concerned (could have reordered so read and reset of FTM1_CNT were executed closer to each other in timing) I'm impressed how often it does result in 12000000 when you don't do what Frank did with the core clock :)
 
Last edited:
those results you are getting look quite normal to me

I thought the point of interest for the teensy 3.0 would be that it is NOT showing double interrupts with flags 0. (flagOdd stays 0)
 
Oh! Wow I need to look at some posts closer before attempting a response to them, even the second time I looked (and edited in blue) I didn't notice your specification of 'STRANGE' and 'GOAWAY' there :insert-seriously-embarrassed-emoticon-here:

Yes, that is a real point of interest, but I don't think I can make a 'value add' comment about that difference yet!

@Frank B: Would you please specify whether or not you used the same 'STRANGE' and 'GOAWAY' settings for both results you posted at different clock speeds?
 
Last edited:
@robsoles -- I am also puzzled about the FlexTimer. I am trying to build a 32-bit counter with it (10 MHz input on its clock, 1 Hz on channel 0). What I am stuck on is how to synchronize my overflow counter (for the 16 bit counter in the FlexTimer) with the interrupt from an edge on the channel. I expect that these interrupt sources can fire independently of each other .

This code seems to work, but it shouldn't ! I think I really need to look at FTM1_C0V, and if it is close to 65536, wait until FTM1_CNT is > (say) 100, at which point the overflow counter should have incremented, but I haven't tried this yet.

This is my ISR

Code:
extern "C" void FASTRUN ftm1_isr(void) { // Process CHF first, unless counter is close to overflow
  if (FTM1_C0SC & FTM_CSC_CHF) { // rate = 1 pps
    if (FTM1_CNT > 65536 - 100) return; // avoid processing if counter is too close to OV
    if (FTM1_CNT <         100) return; // 10 may be safe
    CountValue = (FTM1CountOVFlow << 16) + FTM1_C0V; // How do I know that FTM1CountOVFlow incremented ?
    FTM1_C0SC &= ~FTM_CSC_CHF;  // clear interrupt from channel
    CaptureFlag = true;
  } // CHF0

 if (FTM1_SC & FTM_SC_TOF) { // read the timer overflow flag (TOF in FTM1_SC) rate = 10 MHz/65536 ~ 152 Hz, 6.6 ms
    FTM1_SC &= ~FTM_SC_TOF;   // clear interrupt from timer overflow
    FTM1CountOVFlow++;       
  } // TOF
}

There is some code in Paul's FreqMeasure.cpp which appears to be trying to do the same thing, but I don't understand it either:

Code:
if (capture_event()) {
		capture = capture_read();
		if (capture <= 0xE000 || !inc) {
			capture |= (capture_msw << 16);
		} else {
			capture |= ((capture_msw - 1) << 16);
		}

does anyone have a robust example of how to effectively get a 32-bit FlexTimer ?
 
It is a while since I touched this much, I have lifted it out of a complete program which I am not at liberty to share in whole and I haven't tried to compile this copy so I apologise if it isn't as intact and complete an example as I hope to be posting you;
Code:
typedef union {
	uint32_t value;
	struct {
		uint16_t lo;
		uint16_t hi;
	};
} convert32to16;

convert32to16 mTimer;

void setupTimer(void)
{
	FTM1_MODE=FTM_MODE_WPDIS;
	FTM1_MODE|=FTM_MODE_FTMEN;
	FTM1_SC = 0; // disabled while I write some values to it.
	FTM1_CNT=0;
	FTM1_MOD=0xFFFF;
	FTM1_C0SC=0;
	FTM1_C1SC=0;
	FTM1_QDCTRL=0;

	FTM1_SC = FTM_SC_TOIE|(1<<3)|2; // (use system clock, divide clock by 4, have an overflow interrupt)
	FTM1_MODE&=~FTM_MODE_FTMEN;
	// Higher priority for my purpose.
	NVIC_SET_PRIORITY(IRQ_FTM1, 64);
	NVIC_ENABLE_IRQ(IRQ_FTM1);
}

void ftm1_isr(void)
{
	if(FTM1_SC&FTM_SC_TOF)
	{
		mTimer.hi++;
		FTM1_SC&=~FTM_SC_TOF;
	}

}

uint32_t readTimer(void)
{
	mTimer.lo=FTM1_CNT;
	return mTimer.value;
}

I use it in such code that never clears any element of mTimer, nor does anything to FTM1 but read FTM1_CNT using the 'readTimer' function - it seems reliably accurate enough to time events pretty accurately; I used it for a non-FFT acoustic tuner.

The original code compiled happily in 1.6.3 and 1.6.7/1.27, I have not tried to compile the original source in anything after 1.6.7/1.27

HTH.
 
Thanks @robsoles. I see what you are doing, but that won't work for me -- I trigger a capture with the FTM Channel 0. I am leaning towards not generating an interrupt with a Channel 0 event, just polling it at each FTM! interrupt, but I don't have that working properly yet.

Your code possibly has the problem I am trying to avoid -- in your readTimer(), if an interrupt occurs between the 1st and 2nd lines, the result will be incorrect:
Say FTM1_CNT is 65535 just as readTimer() is called. mTimer.lo will be set to 65535. Then (before the next line executes) if FTM1_CNT rolls over; an interrupt will occur, mTimer.hi will increment, and FTM1_CNT will be 0. Your return value however will have the incremented .hi value and the old .lo value -- it will be 65535 counts too high. If another read occurs soon after, FTM1_CNT will be low, and the result will be correct; however the output will also have been non-monotonic.
 
When I initially wrote it I thought that testing and debugging would corner me into disabling interrupts and storing mTimer.value, to return with after re-enabling interrupts, but it never became an issue due to what I did with the returned values; taken as a group of 8 samples, find samples within sample_tolerance_% of each other and discard others, turf the group if more than 3 samples are discarded, average remaining samples if not turfing group, compare to previous avg_result if within avg_result_tolerance_% accept result as given if so ('report this result to the caller', if you will - if no result can be accepted before TIMEOUT report '0' as result...).

My code doesn't seem to suffer from this as a problem. Contemplating reviewing my results now tho :)

I wonder if closer study of Paul's use of ADC in the Audio Library could help you achieve your goal?
 
Last edited:
@robsoles -- I am also puzzled about the FlexTimer. I am trying to build a 32-bit counter with it (10 MHz input on its clock, 1 Hz on channel 0). What I am stuck on is how to synchronize my overflow counter (for the 16 bit counter in the FlexTimer) with the interrupt from an edge on the channel. I expect that these interrupt sources can fire independently of each other .

This code seems to work, but it shouldn't !
Why not? It looks like it would work ok, but wouldn't be the most efficient way.
There is some code in Paul's FreqMeasure.cpp which appears to be trying to do the same thing, but I don't understand it either:
Code:
if (capture_event()) {
		capture = capture_read();
		if (capture <= 0xE000 || !inc) {
			capture |= (capture_msw << 16);
		} else {
			capture |= ((capture_msw - 1) << 16);
		}
Paul's code transformed a bit with some comments:
Code:
    uint32_t capture;
    bool ftm_overflowed = false;

    if (capture_overflow()) {
        capture_overflow_reset();
        capture_msw++;
        ftm_overflowed = true;
    }
    if (capture_event()) {
        capture = capture_read();
        if(ftm_overflowed) {
            // we have both capture and overflow interrupt flags set
            // capture must have occured close in time to overflow
            if(capture < 0xE000) {
                // our capture value is 'small', overflow
                // must have happened before the capture, since there 
                // would have been a lot of time for the overflow 
                // interrupt otherwise
                capture |= (capture_msw << 16);
            } else {
                // with this big value, there would be a huge amount
                // of time for the overflow interrupt to happen if
                // it occured before the capture --> overflow 
                // must have occured after capture 
                // --> our capture_msw increment doesn't apply for
                //     this capture
                capture |= ((capture_msw - 1) << 16);
            }
        } else { // ftm_overflowed == false
            capture |= (capture_msw << 16);
        }
 
I can tell you this was the difficult part of FreqMeasure. The 0xE000 constant is a bit arbitrary, based on assumptions of interrupt latency.
 
I can tell you this was the difficult part of FreqMeasure. The 0xE000 constant is a bit arbitrary, based on assumptions of interrupt latency.
See my code in thread https://forum.pjrc.com/threads/30822-Teensy-3-1-and-Flextimer(s)-Counting-external-pulses-accurately -- checking for 24 h with 15 MHz on pin3 and 20 Hz on pin A2 shows no errors. It does 'waste' ~ 10 us in the ISR routine though which I'd prefer not to do.

*EDIT* -- I'm trying to use the PIT to schedule a 2nd interrupt about halfway through the count (2 ms after FTM interrupt). That will give the most flexibility.
 
Last edited:
Status
Not open for further replies.
Back
Top