Arduino Due high precision tick/timestamp taking and diff functions

I am excited about the Due hardware features and its high precision timestamp taking at 11,9ns level (84MHz clock count). I made even use of that to determine speed of light (as 56313km/s or 72968km/s – which is correct order of magnitude of c=299792km/s, but a factor 4.1 off due to electronic delay measurement distortion – will post new thread on this soon).

Here is just a small sketch providing “take_ticks()” and “take_timestamp()” functions, as well as diff functions for ticks (for <1ms durations) and timestamps (for <51130.563ms).

The sample sketch determines maximal number of clock cycles needed by “diff_timestamps()” call experimentally as 160 clock cycles, to be sure assembler analysis would be needed. My numbers measured in sketch comment are obtained by compilation with -O3 (for speed) instead of -Os (for size).

Taking second tick t0 and (if needed) doing second “millis()” call is required to deal with roll over correctly (SysTick->VAL counts down to 0, generates interrupt that increases millisecond counter and restarts with 84000).

I hope these functions may be useful for others as well:

/*
   84MHz CPU clock precision (11.9ns) take_timestamp() function 
   [ maximum clock cycles for function call taken: 160 ]

   duration determination for ticks and timestamps 

start ....
0 41 1:82215
41 136 1:51652
136 141 3:74057
141 160 9:3
*/

// only for durations <1ms
//
uint32_t ticks_diff(uint32_t t0, uint32_t t1) {
  return ((t0 < t1) ? 84000 + t0 : t0) - t1;
}

// only for durations <51130.563ms
//
uint32_t timestamp_diff(uint32_t ms0, uint32_t t0,
                        uint32_t ms1, uint32_t t1) {                        
  return 84000*(ms1 - ms0 - ((t0<t1)?1:0))
        +ticks_diff(t0,t1);
}

#define take_ticks(ticks)  (ticks=SysTick->VAL)

void take_timestamp(uint32_t& ms, uint32_t& ticks) {
  ticks = SysTick->VAL;
  ms = millis();
  uint32_t t0 = SysTick->VAL;
  if (ticks < t0) {
    if (millis() == ms) {
      --ms;
    }
  }
}

uint32_t max=0;

void setup() {
  Serial.begin(57600);
  while(!Serial) {}
  Serial.println("start ....");
}

void loop() {
  uint32_t t0, t1, ms, ticks, dt1;
  
  take_ticks(t0);
  take_timestamp(ms, ticks);
  take_ticks(t1);
  
  dt1 = ticks_diff(t0,t1) - 1;  // - "take_ticks() clock cycle"
 
  if (dt1 > max) 
  {
    Serial.print(max);
    Serial.print(" ");
    Serial.print(dt1);
    Serial.print(" ");
    Serial.print(ms);
    Serial.print(":");
    Serial.println(ticks);
    max = dt1;
  }
  
  delayMicroseconds(203);
}

Hermann.

sketch_jun06b.ino (1.36 KB)

The DUE timestamp precision is certainly good but when you want to measure the speed of light, it is not so good.

To achieve a reasonable precision for this experiment, you need some ""ticks" to capture the light coming back from a mirror after a 15 meters journey in your living room , right ?

Since 15 meters require about 50 ns, i.e. 4 ticks at 84 M Hz, it might be useful to inline some assembly code and clock your DUE at 234 MHz.

Hi,

I did overclock Due in the past, but only up to 114MHz https://www.youtube.com/watch?v=iuj38HiWcXY

I was not aware that 234MHz is possible, can you explain how?

Hermann.

Hi HermannSW

The function SystemInit () does some basic initialization for you during the boot. So what we need is to somehow change clock frequency from 12MHz to 236 MHz instead of 84 MHz.

For that there is Divider and PLL (phase lock loop, see datasheet 28.15). The PMC Clock Generator PLLA Register has to be modified : set CKGR_PLLAR_MULA(value) with the appropriate value, 38 instead of 13.

Here is the code I successfully tested on my Sam3x8e, it gives 236 MHz. I say on MY uc because at this clock speed, you are at the border of its capabilities, so it can work for me and not for you.

The datasheet is great, but sometimes nothing beats some trial-and-error to get things working as they should…

void setup() {
  
uint32_t SCC;

  // 38 for 234 M Hz 
  Clock_System(38);
               
        SCC = SystemCoreClock;
        
  // 13 for 84 M Hz restore initial clock speed   
  Clock_System(13);   
  Serial.begin(250000);
  Serial.print(" SystemCoreClock = "); 
  Serial.print(SCC/pow(10,6)); Serial.println(" M Hz"); 

}

void loop() {
  
}

void Clock_System(uint8_t clock_mul) {

#define SYS_BOARD_PLLAR     (CKGR_PLLAR_ONE \
                            | CKGR_PLLAR_MULA(clock_mul) \ 
                            | CKGR_PLLAR_PLLACOUNT(0x3fUL) \
                            | CKGR_PLLAR_DIVA(0x1UL))
#define SYS_BOARD_MCKR      ( PMC_MCKR_PRES_CLK_2|PMC_MCKR_CSS_PLLA_CLK)   

/* Initialize PLLA to X MHz */
PMC->CKGR_PLLAR = SYS_BOARD_PLLAR;
while (!(PMC->PMC_SR & PMC_SR_LOCKA));
 /* Setting up prescaler */
PMC->PMC_MCKR = SYS_BOARD_MCKR;
while (!(PMC->PMC_SR & PMC_SR_MCKRDY));

 /* Update SystemCoreClock */
SystemCoreClockUpdate();  

/* For your experiment you don't need to re-synchronize UART 
 so don't serial print when your uc is running at 236 M Hz */
}

Apart clock speed, an ADC PDC DMA may speed up the recording of the rising edge from the light sensor, maybe better than an interrupt function.

Furthermore an ADC PDC DMA would free at 95% the CPU but of course you would need previously to calibrate the recording speed.

Hi there,

I think the fastest way to measure time using the DUE is taking advantage of the capture modes of TC (Timer/Counter) modules. You can do that easily using tc_lib library available at:

https://github.com/antodom/tc_lib

Example capture_test.ino shows how to use a capture object. I have been able to measure 1-2 Mhz using the capture objects, but I have not had the necessity to test fasters signals.

The accuracy you can get with TC modules in capture mode are bounded by the clock the ATSAM3X8E is using, in this case a 84 Hhz clock for the DUE (tc_lib uses the fastest one 84Mhz/2). Evidently, a faster clock will increase the accuracy.

I hope it helps.

Hi ard_newbie,

I did play with your sketch, the 1 Due I tested was able for 216MHz, but blink program only works up to 192MHz, so I will go with that (needed to replace "delay()" which did not work at 192MHz): http://forum.arduino.cc/index.php?topic=406165.msg2793958#msg2793958

Hermann.