Flextimers and other timing routines + finding sourcecode for libs/functions

educa

Active member
Hi,

I'm very new to teensy, but not that new to microcontrollers.
I'd like to create a gcode interpreter on the teensy and therefore I need to port some timing code too.

Now I could start banging this forum with all kinds of questions, but it looks fair to me that I study the K66 manual (developing for teensy 3.6) first.

Having 4 Flextimer counters, I believe I could use at least 3 of these for my timing specific code. (If I'm not wrong, then for example FTM0 has 8 channels, but they do all share the same 16 bit counter?

In my code I have to be able to enable 3 pins on the teensy (doesn't matter where they are on the pinout) by using the following principle. (as I think I read on the datasheet)

- I have to use 1 FTM per pin, because I want to start and stop them individually (really start/stop the whole 16bit counter, not just a channel)
- I set 1 pin high by assigning the correct ALT pinmux for for example FTM0_CH0
- I set an initial counter value in the 16bit counter for ftm0 and then also a compare value.
- Starting the counter, the external teensy pin is brought high and upon compare, the counter must automatically stop (1 shot mode?) and disable the pin.
- next time I want to enable the pin, I just (re)start the ftm0 counter?


I'm still reading up on this (a lot of data in the datasheet) and trying to get stuff working. As far as I understand (and recall from the m3 cpu on an arduino due) I don't really use interrupts for this. With this I mean that I don't install an ISR, but the counter should be directly configured in its registers so that on start, its external pin is set high and on compare, the counter is stopped and the pin is set low. It would be nice if on compare you can also directly let the counter be zeroed, but still need to find if that is possible.

Using this technique, it should be quite easy to have 3 external pins which I can !!start!! and they they should go high for respectively 10µS, 10µS and 3ms and auto-go-low without interrupting my software. It is perfectly possible on the arduino due so I can't imagine why I would not work on the teensy3.6


Now I do have a question for the savy people on here ;) . There is a possibility that I would like to use millis(), micros(), elapsedMillis() and elapsedMicros()
Do these functions use some kind of timers internally (which I then better NOT touch in my lover level stuff) ?
Can I somewhere search the source code for the library functions made available ? I have found somewhere the code for timerone and timerthree libs and that gives a nice insight. Maybe it could be very valuable if I could somewhere find the source of the other libs/function calls. My common sense tells me they are available somewhere, but I just don't know where.



Kind regards,
Bart
 
Maybe an important question to ask, but when I compile a very simple sketch doing absolutely NOTHING, are there already ISR routines defined in the library which run always?

Maybe a systick interrupt or so to do timekeeping ?
 
Yes. There are already interrupts running, i.e. for the systick. A quick look into the core files of the Teensyduino add-on will tell you more.
 
then for example FTM0 has 8 channels, but they do all share the same 16 bit counter?

Yes, the pins associated with each FTM share a single 16 bit counter in that FTM. Details here (scroll down to "PWM Frequency"):

https://www.pjrc.com/teensy/td_pulse.html


- I have to use 1 FTM per pin, because I want to start and stop them individually (really start/stop the whole 16bit counter, not just a channel)

Yes, if you must stop the entire timer. Maybe there's some other way to accomplish your goal without stopping the timer, but that's hard to say without an understanding of what you're trying to do with the pin.

Perhaps look at the PulsePosition library for ideas on controlling the pins while leaving the timer running...

- I set 1 pin high by assigning the correct ALT pinmux for for example FTM0_CH0
- I set an initial counter value in the 16bit counter for ftm0 and then also a compare value.
- Starting the counter, the external teensy pin is brought high and upon compare, the counter must automatically stop (1 shot mode?) and disable the pin.
- next time I want to enable the pin, I just (re)start the ftm0 counter?

That's one way, yes.

You could also interrupt an interrupt at the compare, and as long as the pulse you intend to generate is wider than the interrupt latency, you can reprogram the compare to force the pin low at a desired future time. That's how PulsePosition works, allowing all 8 channels to operate very independently without stopping the timer.

There's also a dual compare mode which combines 2 channels to a single pin, so you can set precise high and low times. Again, you can trigger interrupts and reprogram these compare points while the timer is still running.

but the counter should be directly configured in its registers so that on start, its external pin is set high and on compare, the counter is stopped and the pin is set low.

Yup, that's pretty standing set on compare. All the channel support this.

It would be nice if on compare you can also directly let the counter be zeroed, but still need to find if that is possible.

The channels don't have the ability to zero the count, but you can configure the MOD register so the timer automatically rolls over.

Using this technique, it should be quite easy to have 3 external pins which I can !!start!! and they they should go high for respectively 10µS, 10µS and 3ms and auto-go-low without interrupting my software. It is perfectly possible on the arduino due so I can't imagine why I would not work on the teensy3.6

I do not recall seeing any sort of one-shot mode, where the FTM stops automatically. You might need to use and interrupt or DMA channel to get it to stop. The overall FTM design is meant to be constantly running. I'm sure with some effort you can make it stop, but it might be worthwhile to adjust your approach for continuously running on the 16 bit counter.

There is a possibility that I would like to use millis(), micros(), elapsedMillis() and elapsedMicros()
Do these functions use some kind of timers internally (which I then better NOT touch in my lover level stuff) ?

Those uses the SysTick timer in the ARM core. It's unrelated to the FTM timers.


Can I somewhere search the source code for the library functions made available ?

Sure. Teensyduino installs all the libraries into hardware/teensy/avr/libraries, and the core library into hardware/teensy/avr/cores/teensy3.

FreqMeasure and PulsePosition are the libraries that do the most interesting things with the FTM timers.

Also, the audio library's PWM output uses a DMA channel to reprogram PWM on every cycle without interrupts. Maybe that will be interesting?


Maybe an important question to ask, but when I compile a very simple sketch doing absolutely NOTHING, are there already ISR routines defined in the library which run always?

Yes, SysTick and the USB interrupt are active by default.

Might also be worth mentioning SysTick defaults to priority level 32 and USB defaults to 112. Lower numbers mean higher priority. If you configure your interrupts to priority zero (highest), they will be able to interrupt these interrupts thanks to the NVIC nested priority levels (and the fact that most Teensy libraries have sensible defaults for interrupt priorities).
 
Last edited:
OK, so I did quite some research today and came up with working code to set a pin high and then use FTM0 Channel0 to set the pin low again 100µS later

Code:
void ftm0_isr(void) 
{
  if(FTM0_C0SC & FTM_CSC_CHF) {
    // There has been an interrupt with channel 0 of FTM0
    FTM0_C0SC &= ~FTM_CSC_CHF; // First clear the channel flag because if you don't, then this ISR will continue to be called (K66 manual p 1148) 
    digitalWriteFast(13,LOW);
    } 
}


void setup() {
  pinMode(13, OUTPUT);         // Configure pin 13 (led) as an output
  FTM0_MODE |= FTM_MODE_WPDIS; // Configure Features Mode Selection : Write Protection Disable (K66 manual p 1154) 
  FTM0_MODE |= FTM_MODE_FTMEN; // Configure Features Mode Selection : Flex Timer module enable (K66 manual p 1154) 
  FTM0_MODE |= FTM_MODE_INIT;  // Configure Features Mode Selection : Perform init functions (K66 manual p 1153) 
  FTM0_SC = 0x00;              // Configure Status And Control : Set this to zero before changing the modulus (K66 manual p 1144)
  FTM0_CNTIN = 0x0000;         // Configure Counter Initial Value : Set initial value of flextimer 0 to 0 (K66 manual p 1150) 
  FTM0_CNT = 0x0000;           // Configure Counter : Reset the count to zero. It doesn't matter what value you write to _CNT since te counter will be loaded with FTM0_CNT whenever you write to this register (K66 manual p 1150) 
  FTM0_MOD = 0xFFFF;           // Configure Counter Modulo : max modulus = 65535, so counter will run from 0 to 65535 and then overflow to 0 and count on (K66 manual p 1146) 
  FTM0_SC |= FTM_SC_CLKS(0b1); // Configure Status And Control : Set Clock Source to be 01: System Clock, which makes the counter run (K66 manual p 1145)
  FTM0_SC |= FTM_SC_PS(0b111); // Configure Status And Control : Set Prescale Factor Selection to 111, which divides the clock by 128 (K66 manual p 1145)
  FTM0_C0SC &= ~FTM_CSC_MSB;   // Configure Channel Status And Control: Enable Output Compare Mode (K66 manual p 1149) 
  FTM0_C0SC |= FTM_CSC_MSA;    // Configure Channel Status And Control: Enable Output Compare Mode (K66 manual p 1149) 
  NVIC_ENABLE_IRQ(IRQ_FTM0);   // Enable the IRQ Interrupt for FTM0
  FTM0_C0V = 0;                // Set initial compare to 0 (K66 manual p 1147) , but this actually not even needed in this example
}


void loop() 
{
  delay(100);
  digitalWriteFast(13,HIGH);
  FTM0_CNT = 0x0000;           // Configure Counter : Reset the count to zero. It doesn't matter what value you write to _CNT since te counter will be loaded with FTM0_CNT whenever you write to this register (K66 manual p 1150) 
  FTM0_C0V = 47;
  FTM0_C0SC |= FTM_CSC_CHIE;   // Configure Channel Status And Control: Enable Channel Compare Interrupt (K66 manual p 1148)
}


But.... although this code works perfectly, I'm looking for possible refinement.
This code . uses digitalWriteFast to set and reset pin 13 (ledpin) and this works fine.
Upon compare, ftm0_isr is called, which resets the correct interrupt flag and also sets pin 13 low again.


The main purpose for this code is to be able to set a pin high in mijn main loop somewhere and then rely on ftm0 to make the pin go low again 100µS later


I do understand that this code above works (tested with logic analyzer) and is a way to do it, but....

This code is using FTM0 on Channel 0 and FTM0_CH0 is exposed on pin 22 -> B11 (alt 4)

Could somebody help me out a little about HOW I could connect this so that instead of having to call digitalWriteFast, I could have pin 22 be toggled on and off based on this channel 0?

Could I eliminate the ftm0_isr routine completely ?


I know on arduino Due I was able to configure the system so that on start of the timer/channel , the pin gets set high and on compare it is automatically set low + the interrupt enable for that channel is disabled. (so you have to reenable the interrupt after each shot, which is wanted behaviour)


Hopefully somebody can help me out a little bit. I tried to document about every line in my code. Could be handy for other people reading and also for me in future of course ;)


PS Don't look too much at my main loop. It is just setup for testing purposes so my main loop sets up a pulse on pin13 10 times a second and then asks the ISR routine connected to FTM0 CHANNEL0 to set it low again 100µS laser
 
Last edited:
Just coming across this thread and your well documented code helped me out. So thanks educa!

To answer your last question, I think you need to configure the port mux to link the FTM channel to the corresponding pin.

For example, I'm using the Teensy3.2, so I reference the MK20DX256vlh7 datasheet and see under section 8.1 Pinout: K20 Signal Multiplexing and Pin Assignments, that pin 44 on the chip (Arduino pin 22) needs to use "ALT4" to select the FTM0_CH0 as a driving source.

Then, using the K20 Reference manual, in section 11.14.1, you find the "Pin Mux Control" bit setting required in the Pin Control Register (PORTx_PCRn) to select "ALT4".

I use the following line and voilà!
Code:
  CORE_PIN22_CONFIG = PORT_PCR_MUX(4);  //FTM0-CH0

EASY! :p
 
Back
Top