nanosecond timer (capable of +/- 100 nanosecond resolution)

Status
Not open for further replies.

dpmanthei

Member
Hi all,

I recently picked up a Teensy 3.6. I've used Teensy and Arduino for several other projects but I've never pushed timing as hard as I need to right now so I'm looking for some guidance.

My program will be measuring the time it takes to discharge the energy stored in a coil, usually around 100 microseconds. The program needs to do the following:


digital read pins 11 & 12 "continuously"
When pin 11 goes high, start a timer with an accuracy of +/-0.1 microseconds (100 nano, or better if possible)
When pin 12 goes high, stop the timer with the same accuracy
Pass the measured value into an if statement to turn on LED's, write out to an LCD screen, compare measured value to upper and lower bounds, etc (this can all be done in a leisurely 3-5 seconds)


What I'm looking for is help with the nanosecond timer. What's the easiest way to obtain timing accuracy of +/- 100 nanoseconds or less? I've already used an oscilloscope and found a digitalRead() to take a pretty steady 100 nanoseconds...so two of these instructions means I have a total measurement error of at least 200 nano. An additional 100 nano of error in the timer itself would bring overall error up to 300 nano which is acceptable...but not much more than that.

Suggestions?

Thank you!
Dylan
 
Use the hardware event capture timing libraries FreqMeasureMulti. The hardware will capture the timer values at the same bus cycle as the pin goes high, or perhaps after a filter delay of 3 clocks, but both signals will have the same delay so interval is constant if input signal is clean. No need for a sample loop with digital reads. You program simply waits for the next capture event.
 
I'd go with a low density FPGA perhaps, then use the T3.6 to process the information.
FPGA would only need to be capable of timing the pulses, and relay those values to the T3.6.
I'd have to look a bit closer, but I think that you could memory map the FPGA, which would make reading the pulse value at BUS speed + however many wait states.
Another idea....
I know on some Kinetis devices there is also an actual pulse timer (the PWT), that can be triggered on either or both edges.
I've been porting a lot of the Teensy3 core to KE1xF which has the PWT module.
If ran at BUS speed, you could get a very high resolution, ~5.952380952381ns @ 168MHz.
The PWT can also be clocked externally.
 
Great suggestions, thanks to both of you! I'll start with the library and see where it takes me. I expect this project to evolve through a few different revisions, so I may end up exploring all of the above.
 
What's the easiest way to obtain timing accuracy of +/- 100 nanoseconds or less?

The best way is FTM input capture. On Teensy 3.6 the default for F_BUS is 60 MHz, so this way can give you 16.7 ns precision.

You'll need to use 2 channels from the same FTM timer, so pins 11 and 12 aren't possible. But you could use pins 9 and 10, since they're both FTM timer channels.
 
The best way is FTM input capture. On Teensy 3.6 the default for F_BUS is 60 MHz, so this way can give you 16.7 ns precision.

You'll need to use 2 channels from the same FTM timer, so pins 11 and 12 aren't possible. But you could use pins 9 and 10, since they're both FTM timer channels.

Great, thank you very much for the reply Paul! It's very helpful to hear what you believe is most appropriate and what results to expect. I'll read up on this as I'm not familar with the FTM timer...I expect I'll be learning some new C.

Thanks again,
Dylan
 
Sorry to bump, but I'm struggling with input capture. I'm actually a mechanical engineer by day and even the more basic Arduino programming is fairly new to me.

I've done a number of Serial.print lines to try to figure out my issue. It appears no interrupts are being generated. Does anyone see the issue in the code? Sorry, my code is probably ugly and not following good practices...like I said I'm still learning. Full disclosure, this code is largely copied from http://www.digitalmisery.com/2013/06/timer-input-capture-on-teensy-3-0/ which I thought I could learn from and adapt to the Teensy 3.6 for my application.


Code:
// Dylan Manthei
// 6/6/2017
// "Coil Tester"
// Code largely based on [url]http://www.digitalmisery.com/2013/06/timer-input-capture-on-teensy-3-0/[/url]

// Purpose:
/*  Approximately every 2.8 seconds, an external device 
 *  (stator coil tester) generates a 3.3v output.  This 
 *  output goes high "exactly" when a known amount of 
 *  energy begins discharging from the coil.  The 
 *  output goes low "exactly" when the coil is 
 *  completely discharged.  The Teensy is being used to 
 *  capture a timestamp when this signal goes high and 
 *  low with the greatest accuracy possible, then print
 *  the difference (pulse width) to the serial port for
 *  display on a laptop (for now, LCD in future).
 *  
 *  A future iteration will need to have a 'pause' 
 *  button, a 'save the next measurement' button, 
 *  compare the result against upper and lower limits,
 *  and also display "pass" or "fail" or energize LED's
 *  to show the operator the result. SD card or 
 *  webserver logging would be cool too, but way over 
 *  my head at this point.
 *   
 */
 
// ##################################################
// Declare vars
const int ledPin = 13;
uint16_t FTM0Count = 0;
int timerOverflow = 0;
int overflowcount = 0;

// ##################################################
void setupFTM0() {           // Function for FlexTimer0
  FTM0_FILTER = 0x00;        // Disable the input filter (0 samples averaged)

  // FAULTIE=0, FAULTM=00, CAPTEST=0, PWMSYNC=0, WPDIS=1, INIT=0, FTMEN=1
  FTM0_MODE = 0x05;          // Set the write protection to disabled (write-enabled) and enable timer (FTMEN)

  // FLEXTimer0 configuration
  FTM0_SC = 0x00;            // Set this to zero before changing the modulus
  FTM0_CNT = 0x0000;         // Reset the count to zero
  FTM0_MOD = 0xFFFF;         // max modulus = 65535
  FTM0_SC = 0x11;            // TOF=0 TOIE=0 CPWMS=0 CLKS=10 (FF clock) PS=001 (divide by 2)
  FTM0_C0SC = 0x24;          // CHF=0 CHIE=1 (enable interrupt) MSB=0 MSA=0 ELSB=1 (input capture) ELSA=0 DMA=0
  NVIC_ENABLE_IRQ(IRQ_FTM0); // Enable FTM0 interrupt inside NVIC

    //   THE PIN TABLE BELOW IS CORRECT FOR TEENSY 3.6
    //   Pins that can be used for Input Capture:
    //   Teensy Port  Name  CPU Pin  Signal
    //   9            PTC3  46       FTM0_CH2
    //   10           PTC4  49       FTM0_CH3
    //   22           PTC1  44       FTM0_CH0
    //   23           PTC2  45       FTM0_CH1

    // PIN configuration, alternative function 4 on Pin 46 (Teensy 9) (FTM0_CH2)
    PORTC_PCR3 |= 0x400;
}
// ##################################################
extern "C" void ftm0_isr(void) {     // Interrupt Service Routine for FlexTimer0 Module
  FTM0Count = FTM0_C0V;              // Save current captured count value
  FTM0_CNT = 0x0000;                 // Reset count value

  if ((FTM0_SC&FTM_SC_TOF) != 0) {   // Read the timer overflow flag (TOF)
    timerOverflow = 1;               // Set variable to 1 if overflow occured
    overflowcount = overflowcount+1; // Keep a count of how many overflows occur
    FTM0_SC &= ~FTM_SC_TOF;          // Clear overflow flag
  }
  else
    timerOverflow = 0;               // Set variable to 0 if overflow has not occured
}
// ##################################################
void setup() {                       // Setup Loop
  pinMode(ledPin, OUTPUT);           // Set LED pin as output
  pinMode(9, INPUT);                 // Set Pin 9 (signal) to input
  Serial.begin(57600);               // Start serial comms
  setupFTM0();                       // Call the FTM0 function
}
// ##################################################
void loop() {
  if (millis() % 1000 < 25){           // If statement that's true for 25ms every 1 second
    digitalWrite(ledPin, HIGH);        // Blink the LED (visual that program is running)
  }
  else{
    digitalWrite(ledPin, LOW);         // If 1 second if statement isn't true, set LED low
  }
  if ((FTM0_C0SC&0x80) != 0) {            // Look for channel interrupt flag, if true:
    FTM0_C0SC &= ~0x80;                   // Clear channel interrupt flag
    Serial.print("Count: ");              // Print the word "Count: "
    Serial.print(FTM0Count/(31250.0/2));  // Calculate and print captured time
    Serial.print("    Overflowcount: ");  // Print some text
    Serial.println(overflowcount);        // Number of overflows counted between pulses
    delay(100);                           // Don't do anything for 100 millis, just throttles Serial.print in case input signal is noisy
  }
}
 
That code doesn't work for high-resolution measurements. FreqMeasureMulti has been suggested. It does FTM input capture and does it correctly.

Thank you. Initially I didn't know if that was a viable option since I was trying to accurately catch the rising edge on one input pin and the falling edge from a different pin. The architecture of my device has since changed so I have only one pin, so now I'm using the library with FREQMEASUREMULTI_MARK_ONLY with great results.
 
Status
Not open for further replies.
Back
Top