Forum Rule: Always post complete source code & details to reproduce any issue!
Results 1 to 5 of 5

Thread: Howto use timer capture mode? Teensy 3.5

  1. #1

    Howto use timer capture mode? Teensy 3.5

    Hi,
    According to the data sheet it must be possible to create the following:
    Set up a timer to capture mode at rising and falling edges of an input. (Goal is to measure pulse length.) And to have an interrupt routine to calculate and store the last pulse length. Is there some code as a template, where I could find some information? While I am quite sure, that this must be possible, I am a bit overwhelmed by all the possibilities of the timers....
    I only need about 30ms of resolution, but an edge must not be totally missed.
    Thanks for some hints

  2. #2
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    21,815
    The timers are indeed very powerful but complex.

    Before jumping into the details, you might consider whether a much simpler approach with attachInterrupt() and micros() will meet your needs. The simplest way gives about 2 to 3 us accuracy, and it's usually not hard to get to 1 us accuracy by just raising one of the port change interrupt priorities. Since you said "only need about 30ms of resolution", this way might be worthwhile to try first. Just use attachInterrupt() to run a function when the pin changes, and in that function record the micros() time to a variable, and set a flag. Like with all interrupts, the variables need to be "volatile" and care is needed when reading them from the main program.

    However, that approach is sensitive to interrupt latency. If other code temporarily disable interrupts, or a higher priority interrupt is running when the pulse edge is detected, your code gets delayed until your interrupt can run. So you get the micros() timestamp somewhat later. Usually this sort of error is only a few microseconds unless you're running something bad like Adafruit_Neopixel which blocks interrupts for long times. While these errors are undesirable, if all you need is 30 ms, then a couple microseconds is almost nothing.

    The timers can do the capture all in hardware, so your interrupt can read a number from the precise moment the edge was detected. This gives you accuracy to within 1 cycle of F_BUS, which defaults to 60 MHz on Teensy 3.5.

    As far as example code, the FreqMeasure and PulsePosition libraries are the best place to start. Both of these use input capture.

    The full documentation can be found in the reference manual, at this page:

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

    The FTM timer chapter is huge and loaded with very complex details. But the vast majority is the many special PWM features, so the first step is to ignore all that complicated stuff in the latter part of that chapter.

    Using input capture usually involves 3 steps.

    1: First you need to configure the timer to increment continuously from 0 to 65535 and roll over automatically. If you use a prescaler of 1 it increments at 60 MHz, which means it rolls over every 1.09 ms. To measure longer times, the simplest approach is to use a slower prescaler, but then you get less resolution and you might be only as good as using attachInterrupt.

    2: Next you configure one of the 8 channels (or 2 channels if using FTM1 or FTM2) to capture the counter. This is where you use the big table on page 964. While this looks complex, the thing to remember is each channel is only 2 registers. One for config and one for the actual captured count. All you need to do is write the correct config and the timer will automatically start capturing the rapidly incrementing counter when those edges happen.

    This question was about Teensy 3.5... but if using Teensy LC (as in, someone else later finding this message by search), the very similar "TPM" timers have a quirk where you have to set the config to zero and either wait a brief delay or repeatedly read it back until it's really zero, and only then can you successfully change the channel config. The TPM timers have the ability to run from very different clocks (much faster or slower than the CPU) so they have this extra step. The FTM timers on Teensy 3.x don't have this limitation, but they are limited to only running from F_BUS which is always an integer division of the main F_CPU clock.

    3: Finally, you need to handle the interrupts. You want to get that counter value read ASAP. Then figuring out what to actually do with it is the challenge. Usually you would subtract it from the prior reading. If using 16 bit unsigned math, this automatically handles the roll over and gives you an elapsed time at a 16 bit number. Usually you'd store this to a volatile variable, so some sort of volatile flag so your program can know it's new data, and get out of the interrupt as quickly as you can. The longer you remain running the interrupt, the longer you block other interrupts and your ability to get the next edge.

    One drawback is you only get 16 bits of resolution, which spans only 1.09 ms if the timer counts at 60 MHz. It's possible to also respond to overflow interrupts and increment an extended 16 bits, then combine it with the 16 bit count to give you 32 bits of resolution. This is very tricky to get right in the case where these 2 interrupts happen very close in time, so I highly recommend you look at the FreqMeasure code. It's been well tested, so use it if you need more than 16 bits resolution.

    But again, while you can get 16.7 ns precision by using the timer input capture, if you only need 30 ms you might do well to save yourself a lot of trouble and just go with attachInterrupt() and micros(). The timers have incredible capability, but it is tricky to use properly.

  3. #3

  4. #4
    Thank you very much, Paul and manitou, for your inputs! I will try the attachinterrupt and micros() approach first and see if this works good enough for this project.
    Christof

  5. #5
    Hi, and thanks again.
    your input helped to modify an existing library:
    https://forum.pjrc.com/threads/53122...ary-is-working

    This actually works with "attachInterrupt() and millis() ".
    :-) Christof :-)

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •