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

Thread: T 4.1 nanosecond counter?

  1. #1
    Junior Member
    Join Date
    Jun 2022
    Location
    Edmond, OK
    Posts
    12

    T 4.1 nanosecond counter?

    I'm converting C code from a PI 4 to a Teensy 4.1. For the PI I use this to get the full time to nanoseconds:

    Code:
    struct timespec gettime_now;
    long long  f1_on_time;
    
    clock_gettime(CLOCK_REALTIME, &gettime_now);
    f1_on_time = (((long long)gettime_now.tv_sec*1000000000)+(long long)gettime_now.tv_nsec);
    Is there a nanosecond resolution counter I can access that i can combine with others to get a rollover proof time counter?

    Although the above is given in unixtime, I don't need any reference to real world time, just a continuous counter with high resolution.
    Last edited by ekartgo; 06-24-2022 at 08:37 PM.

  2. #2
    Senior Member+ defragster's Avatar
    Join Date
    Feb 2015
    Posts
    16,188
    See this: github.com/TeensyUser/doc/wiki/Implementing-a-high-resolution-Teensy-clock

    Teensy has a processor clock based count of cycles: ARM_DWT_CYCCNT

    At 600 MHz it overflows 32 bits after 7+ seconds.

    That code example when called often enough can count the clock cycles into a 64 bit variable for long term reference of 975 years.

  3. #3
    ARM processors have a running counter of clock cycles. For T4.x, all you have to do is read ARM_DWT_CYCCNT. Here is an example program that shows how to use it. Cycles are converted to nanoseconds using macro F_CPU, which is nominally 600M for T4.x, but changes when you modify clock frequency via the Arduino IDE.

    Code:
    #define CYCLES_TO_NS(cycles)  ((cycles)*(1E9/F_CPU))
    
    uint32_t ms;
    uint32_t cycles;
    
    void setup() {
      Serial.begin( 115200 );
      while (!Serial) {}
      ms = millis();
      cycles = ARM_DWT_CYCCNT;        // ARM cycles at F_CPU
    }
    
    // every 1000 ms, read cycle counter and compute delta cycles and ns
    void loop() {
      uint32_t new_ms = millis();
      if ((new_ms - ms) >= 1000) {
        uint32_t new_cycles = ARM_DWT_CYCCNT;
        uint32_t delta_cycles = new_cycles - cycles;
        uint32_t delta_ns = CYCLES_TO_NS(delta_cycles); 
        Serial.printf( "cycles = %1lu   ns = %1lu\n", delta_cycles, delta_ns );
        ms = new_ms;
        cycles = new_cycles;
      }
    }

  4. #4
    Quote Originally Posted by defragster View Post
    See this: github.com/TeensyUser/doc/wiki/Implementing-a-high-resolution-Teensy-clock

    Teensy has a processor clock based count of cycles: ARM_DWT_CYCCNT

    At 600 MHz it overflows 32 bits after 7+ seconds.

    That code example when called often enough can count the clock cycles into a 64 bit variable for long term reference of 975 years.
    Didn't see this until after I had posted my reply. :-)

  5. #5
    Junior Member
    Join Date
    Jun 2022
    Location
    Edmond, OK
    Posts
    12
    Awesome - thanks everyone. I had found bits and pieces but not that reference to put it all together.

    Moving to a micro-controller environment with a single program running is a bit like going back to my early programming on my TI-99/4A in K&R C (super macro assembler).
    One program with nothing (much) to get in the way and you have to be very efficient.

    This is to avoid the Pi system interrupt issues I was facing, even when the code was running on its own core giving me mostly regular timings - but enough slow loops above 25uS to cause issues at high speed (motor):

    < 500 ns count is 0
    < 750 ns count is 987494
    < 1000 ns count is 666
    < 1500 ns count is 29
    < 2000 ns count is 738
    < 2500 ns count is 3
    < 5000 ns count is 204
    < 10 us count is 68
    < 25 us count is 9910
    < 50 us count is 848
    < 100 uS count is 40
    < 250 uS count is 0
    < 500 uS count is 0

    Bryan

  6. #6
    Senior Member+ defragster's Avatar
    Join Date
    Feb 2015
    Posts
    16,188
    Below is the code that resolves micros() given the last milli second timer update, and the cycle count stored then.

    If the math were bumped to all 64 bit and the scaling to nano adjusted from scale_cpu_cycles_to_microseconds it would track with millis, and not roll over after 71 minutes.

    With only 600 cpu cycles per 1,000 ns it won't be able to count/see every one.

    The two values in the do{}while(); are an interrupt safe/accepting read to get the last millis count and the ARM_cyccnt at that time.

    As written this code takes ~36 to ~38 cpu cycles to run, adding other 64 bit math will extend that a bit.

    Code:
    // Returns the number of microseconds since your program started running.
    // This 32 bit number will roll back to zero after about 71 minutes and
    // 35 seconds.  For a simpler way to build delays or timeouts, consider
    // using elapsedMicros.
    uint32_t micros(void)
    {
    	uint32_t smc, scc;
    	do {
    		__LDREXW(&systick_safe_read);
    		smc = systick_millis_count;
    		scc = systick_cycle_count;
    	} while ( __STREXW(1, &systick_safe_read));
    	uint32_t cyccnt = ARM_DWT_CYCCNT;
    	asm volatile("" : : : "memory");
    	uint32_t ccdelta = cyccnt - scc;
    	uint32_t frac = ((uint64_t)ccdelta * scale_cpu_cycles_to_microseconds) >> 32;
    	if (frac > 1000) frac = 1000;
    	uint32_t usec = 1000*smc + frac;
    	return usec;
    }

  7. #7
    Junior Member
    Join Date
    Jun 2022
    Location
    Edmond, OK
    Posts
    12
    Thanks, that looks like it will work great. I've integrated the code changes and will give it a test and post the results after the board gets here next week.

    I also have a second version of the code now using both hardware interrupts and timer interrupts instead of running a hard loop. It will be interesting to see which works better.

    Bryan

  8. #8
    Junior Member
    Join Date
    Jun 2022
    Location
    Edmond, OK
    Posts
    12
    Board arrived today!

    The timing looks much better. Pleased so far.

    < 500 ns count is 999999
    < 750 ns count is 1
    < 1000 ns count is 0
    < 1500 ns count is 0
    ....
    Sum of counts is 1000000

    Bryan

Posting Permissions

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