Arduino Due counter

I need to measure time between two events, say START and STOP, with a resolution of not worse than 20 nanoseconds. I have thought of building a 50/100 MHz oscillator to drive the input of an Arduino Due counter.

  1. How fast can the Arduino Due counter count? 50MHz? 100MHz?

  2. Is there a library that will allow me to start and stop a counter and obtain the count of pulses being fed to it externally? Paul Stoffregen has written a library FreqCount.h which even though it does not support the Due yet, has a FreqCount.begin(gateTimeIn_ms), FreqCount.end() and FreqCount.read(), to count the pulses within a gate time specified in milliseconds. However I am not looking for a gate time. I want to capture the pulses counted between two events, say START and STOP. Knowing the number of pulses counted between the two events and the period of one pulse, I can get a nanosecond period timer. Say a 100MHz clock were to be connected to the counter input. Starting the count at the START event and stopping it at the STOP event, will give me a count of 10 ns pulses. The time between the START and STOP events will be (count * 10) ns.

Can someone help me with this problem?

The Due works at 84MHz. How would you expect it to count 100MHz? It can't.

What kind of duration are you looking at? The onboard crystal on the Due is not especially accurate. If you're trying to time seconds, then the accuracy of the crystal is worse than your timing specification.

Maybe a dedicated timer module will be needed to meet the accuracy specification.

AFAIK counters are not synchronous with the uC clock. In fact, counters do not require a clock to count, only an edge transition. And can count at much higher rates than the uC that hosts them.

rajivpersonal:
AFAIK counters are not synchronous with the uC clock. In fact, counters do not require a clock to count, only an edge transition. And can count at much higher rates than the uC that hosts them.

In general yes, but not with SAM3X arduino DUE has. Synchronization logic makes it possible to count at 42MHz max.

#define   SMP_RATE              1UL           // 1 Hz ( 1 sec. gate)
#define   CLK_MAIN       84000000UL
#define   TMR_CNTR       CLK_MAIN / (2 *SMP_RATE)

// Name - Port - Pin(DUE board) 
// TCLK1 = PA4 = AD5
// TIOA1 = PA2 = AD7
// TIOB1 = PA3 = AD6

volatile uint32_t freq = 0;  
volatile uint32_t flag = 0;  

         int debug_osm = 0; // debug over serial monitor 
         int debug_cnt = 0; // debug variable duty period 

void setup()
{
  Serial.begin (115200); 

  Atmr_setup();         
  Btmr_setup();         

  pio_TIOA0();  // Gate OUT, Dig. 2 conect Dig-2 to AN-7.
  pio_TCLK1();  // Counter IN, AN - 5 (max 3.3V -> keep safe !!!)
  pio_TIOA1();  // Gate IN, AN - 7.
  pinMode( 7, OUTPUT); // OUT, Digital 7 -> connect to AN-5 for Testing
}

void loop() 
{
  char in_Byte;
  
  if( Serial.available() > 0 ) {
    in_Byte = Serial.read();
    if( in_Byte == 'd' ) { //debug           
      debug_osm = 1 - debug_osm;
      if (debug_osm) Serial.println(F("\n\tDebug activated"));
      else           Serial.println(F("\n\tDebug de-activ."));
      }
    }

  if( flag && debug_osm ) {
       Serial.print("\n\tfreq: ");
       Serial.print(freq,  DEC);  
       flag = 0;
    }

  debug_cnt += 10;
  if( debug_cnt > 255 ) debug_cnt = 0;
  analogWrite( 7, debug_cnt);
}


void TC1_Handler(void)
{
if ((TC_GetStatus(TC0, 1) & TC_SR_LDRBS) == TC_SR_LDRBS) {
  uint32_t dummy = TC0->TC_CHANNEL[1].TC_RA;
           freq  = TC0->TC_CHANNEL[1].TC_RB;
           flag  = 1;
  }
}

void Atmr_setup() // Gate 1 sec., conect Dig-2 to AN-7.
{
  pmc_enable_periph_clk (TC_INTERFACE_ID + 0 *3 + 0);
  TcChannel * t = &(TC0->TC_CHANNEL)[0];
  t->TC_CCR = TC_CCR_CLKDIS;
  t->TC_IDR = 0xFFFFFFFF;   
  t->TC_SR;                 
  t->TC_CMR = TC_CMR_TCCLKS_TIMER_CLOCK1 | 
              TC_CMR_WAVE |                
              TC_CMR_WAVSEL_UP_RC |        
              TC_CMR_EEVT_XC0 |    
              TC_CMR_ACPA_CLEAR | TC_CMR_ACPC_CLEAR |
              TC_CMR_BCPB_CLEAR | TC_CMR_BCPC_CLEAR;
  t->TC_RC = TMR_CNTR;
  t->TC_RA = TMR_CNTR /2;
  t->TC_CMR = (t->TC_CMR & 0xFFF0FFFF) | TC_CMR_ACPA_CLEAR | TC_CMR_ACPC_SET; 
  t->TC_CCR = TC_CCR_CLKEN | TC_CCR_SWTRG; 
}

void Btmr_setup()  // Counter
{
  pmc_enable_periph_clk (TC_INTERFACE_ID + 0 *3 + 1);

  uint32_t reg_val = TC_BMR_TC1XC1S_TCLK1; // Input Capture Pin, AN-5.
  TC0->TC_BMR |= reg_val; 

  TcChannel * t = &(TC0->TC_CHANNEL)[1];
  t->TC_CCR = TC_CCR_CLKDIS;
  t->TC_IDR = 0xFFFFFFFF;   
  t->TC_SR;   
  t->TC_CMR = TC_CMR_TCCLKS_XC1   
             | TC_CMR_LDRA_RISING     
             | TC_CMR_LDRB_FALLING    
             | TC_CMR_ABETRG          
             | TC_CMR_ETRGEDG_FALLING;
             
  t->TC_CCR = TC_CCR_CLKEN | TC_CCR_SWTRG; 

  t->TC_IER =  TC_IER_LDRBS;
  t->TC_IDR = ~TC_IER_LDRBS;

  NVIC_DisableIRQ(TC1_IRQn);
  NVIC_ClearPendingIRQ(TC1_IRQn);
  NVIC_SetPriority(TC1_IRQn, 0); 
  NVIC_EnableIRQ(TC1_IRQn);
}

void pio_TIOA0()
{
  PIOB->PIO_PDR   = PIO_PB25B_TIOA0;  
  PIOB->PIO_IDR   = PIO_PB25B_TIOA0;  
  PIOB->PIO_ABSR |= PIO_PB25B_TIOA0;
}

void pio_TCLK1() 
{
  PIOA->PIO_PDR = PIO_PA4A_TCLK1;  
  PIOA->PIO_IDR = PIO_PA4A_TCLK1;
}

void pio_TIOA1() 
{
  PIOA->PIO_PDR = PIO_PA2A_TIOA1;  
  PIOA->PIO_IDR = PIO_PA2A_TIOA1;
}

Oh wow! That must've taken you some time! Thank you... :slight_smile:

I do not need a gate period. I need to start the counter on receipt of one interrupt and stop the counter on the receipt of a second interrupt. And retrieve the count.

(count * period_of_one_count) will give me the time between the two interrupts. 42 MHz will work fine for me - each count will signify 23.81 ns

What needs to change to effect the above functionality?

Thanks again... <3

Rajiv

Ok, timer has to be driven by internal clock, and gated by external events, what I may see as a problem is using an interrupt in classic meaning. To go into interrupt subroutine arduino could spend a lot of time, and it is not specified. Better to do all in hardware, so gating again. I have another sketch, that measure period, but to make it work with two events you have to add an logic IC that 'd combine two lines into one, or modified sketch. To help you start with.

/*
 Test Sketch for an Arduino DUE board, 
 Period/Duty Cycle measurements on PWM signals.
 Created by Anatoly Kuzmenko, August 2015
 Released into the public domain.
 k_anatoly@hotmail.com
*/

// Name - Port - Pin(DUE board) 
// TIOA1 = PA2 = AD7

volatile uint32_t tmr_RA = 0;  
volatile uint32_t tmr_RB = 0;  
volatile uint32_t read_s = 0;  

         int debug_osm = 0; // debug over serial monitor 
         int debug_cnt = 0; // debug variable duty period 

void setup()
{
  Serial.begin (115200); 
  Btmr_setup();         
  pio_TIOA1();  // IN, AN - 7 (max 3.3V -> keep safe !!!).
  pinMode( 7, OUTPUT); // OUT, Digital 7 -> connect to AN-7 for Testing
}

void loop() 
{
  char in_Byte;
  
  if( Serial.available() > 0 ) {
    in_Byte = Serial.read();
    if( in_Byte == 'd' ) { //debug           
      debug_osm = 1 - debug_osm;
      if (debug_osm) Serial.println(F("\n\tDebug activated"));
      else           Serial.println(F("\n\tDebug de-activ."));
      }
    }

  if( read_s && debug_osm ) {
    uint32_t period = tmr_RB / 42;
    uint32_t duty_c = (100 * (tmr_RB - tmr_RA)) / tmr_RB;
    
    uint32_t freq = 42000000UL/ tmr_RB;
    
      Serial.print("\n\tPeriod: ");
      Serial.print(period, DEC); 
      Serial.print("\tmicroseconds");
      Serial.print("\n\tDuty Cycle: ");
      Serial.print(duty_c, DEC); 
      Serial.print("\t%");
      Serial.print("\n\tFrequency: ");
      Serial.print(freq, DEC); 
      read_s = 0;
    delay(1000);
    }

  debug_cnt += 10;
  if( debug_cnt > 255 ) debug_cnt = 0;
  analogWrite( 7, debug_cnt);
}

void TC1_Handler(void)
{
if ((TC_GetStatus(TC0, 1) & TC_SR_LDRBS) == TC_SR_LDRBS) {

  tmr_RA = TC0->TC_CHANNEL[1].TC_RA;
  tmr_RB = TC0->TC_CHANNEL[1].TC_RB;
  read_s = 1;
  }
}

void Btmr_setup()  // Counter
{
  pmc_enable_periph_clk (TC_INTERFACE_ID + 0 *3 + 1);

  TcChannel * t = &(TC0->TC_CHANNEL)[1];
  t->TC_CCR = TC_CCR_CLKDIS;
  t->TC_IDR = 0xFFFFFFFF;   
  t->TC_SR;   

  t->TC_CMR = TC_CMR_TCCLKS_TIMER_CLOCK1

             | TC_CMR_LDRA_RISING     
             | TC_CMR_LDRB_FALLING    
             | TC_CMR_ABETRG          
             | TC_CMR_ETRGEDG_FALLING;
             
  t->TC_CCR = TC_CCR_CLKEN | TC_CCR_SWTRG; 

  t->TC_IER =  TC_IER_LDRBS;
  t->TC_IDR = ~TC_IER_LDRBS;

  NVIC_DisableIRQ(TC1_IRQn);
  NVIC_ClearPendingIRQ(TC1_IRQn);
  NVIC_SetPriority(TC1_IRQn, 0); 
  NVIC_EnableIRQ(TC1_IRQn);
}

void pio_TIOA1() 
{
  PIOA->PIO_PDR = PIO_PA2A_TIOA1;  
  PIOA->PIO_IDR = PIO_PA2A_TIOA1;
}

Dear rajivpersonal and Magician,

I am trying to do something very similar: to use the Due as a Photon Counter

The idea is to write down in a "Buffer" the number of "Clock Ticks" since the "Reset/Trigger Signal" when a "TTL from Photo Multiplier" signal arrives.

I have done a diagram, see attached figure.

So, two physical inputs into the board: the "reset/trigger Signal" and the "TTL from Photo Multiplier"

I am assuming that the increment of "Clock Ticks" is done internally and does not need to be done in software.

I am also assuming that is possible to reset the clock to zero.

No maths involved, no averaging, no adding, nothing: just write an uint to a buffer.

The photons arrival can be at any moment, there is no particular pattern. However, given the limitation of my current detector, they will not be arriving faster than every 45ns. I would like to point out that if it is possible to know when the photon arrives with better time resolution, then this is even better!!!

Another tricky part, as my experiments with Arduino have show me, is getting the data to the computer, without loosing counts. There is something called DMA that is supposed to help with this, but I have not time to look into the details (OK, now is when you all guys are laughing right? I am not an electrical engineering, just a experimental physicists who got started with Arduino a few years ago )

Regards

Have a look at timer/counter mode for the hardware timers.

Something is missing in your description: the Clock frequency.

The Clock is given by the DUE internal clock.

I am reading right now the section 36 Timer Counter of the ATMEL documentation. It seems promising...

If you mean SysTick counter for the "DUE internal clock", this counter counts down from 84000 to 0 in 1 ms.

Hi there @rajivpersonal,

@HermannSW has already done some testing to know the limits of DUE'sATSAM3X8E for measuring digital signals. Using in this case, library tc_lib, which is just a wrapper around the TC modules available in the raw microcontroller. In this post you have a summary:

https://forum.arduino.cc/index.php?topic=393842.msg3086694#msg3086694

You can find tc_lib at: GitHub - antodom/tc_lib: This is a library to take advantage of different functionalities available on Timer Counter (TC) modules in Arduino Due's Atmel ATSAM3X8E microcontrollers

I think they can help you to have a better idea about DUE's ATSAM3X8E capacities, they report measurements of signals with frequencies greater than 20Mhz.

I hope it helps.

Hi all.

I have modified the code from Magician. If my understanding of what is going on is correct, the code below should put into the variable "CaptureCountA", the value of the clock (MCK/2 = 42MHz?) on the "rising edge of the trigger input", to quote the documentation.

Using a wavefunction generator, I am generating a square waveform of 0V-3V, with a 100us period. Such signal is send to the AnalogIn7 of the Arduino Due

When using the code below, I would have expected to get values spaced of 4200 (100us * 42MHz), however, I get non-sense (see the attached file, copied from the serial monitor).

Therefore, I am missing something... Any idea?

volatile uint32_t CaptureCountA, TimerCount;
volatile boolean CaptureFlag;
const int max_i = 64;
int i;

void setup() {
  Serial.begin(115200); 

  PMC->PMC_PCER0 |= PMC_PCER0_PID28;                      // Timer Counter 0 channel 1 IS TC1
  TC0->TC_CHANNEL[1].TC_CCR = TC_CCR_CLKDIS ;             // disable internal clocking while setup regs
  TC0->TC_CHANNEL[1].TC_CMR = TC_CMR_TCCLKS_TIMER_CLOCK1   // capture mode, MCK/2, clk on rising edge                           
                              | TC_CMR_ABETRG              // TIOA is used as the external trigger
                              | TC_CMR_LDRA_RISING ;       // load RA on rising edge of trigger input

  TC0->TC_CHANNEL[1].TC_IER |= TC_IER_LDRAS;               // Trigger interruption on Load RA and load RB
  TC0->TC_CHANNEL[1].TC_CCR = TC_CCR_SWTRG | TC_CCR_CLKEN ;// Reset TC counter and enable

  NVIC_EnableIRQ(TC1_IRQn);                             // Enable TC1 interrupts
}

void loop() {
    if (i < max_i){
        i+=1;  
        if (CaptureFlag) {
        CaptureFlag = 0;
        printf("\r %d , %d , %d \n", CaptureCountA);
        }
        else {
        Serial.println(TC0->TC_CHANNEL[1].TC_CV);
        }
    }
}
void TC1_Handler() {
  uint32_t status = TC0->TC_CHANNEL[1].TC_SR;       // Read & Save satus register -->Clear status register
  if ((status & TC_SR_LOVRS) == TC_SR_LOVRS) abort();
  if ((status & TC_SR_LDRAS) == TC_SR_LDRAS) {  // If ISR is fired by LDRAS then ....
    CaptureCountA = TC0->TC_CHANNEL[1].TC_RA;        // get data from capture register A for TC0 channel 1
  }
  CaptureFlag = 1;                      // set flag indicating a new capture value is present
}

I forgot to attach the file!

In order to produce a minimum number of interrupts, I would use a code close to the one used for an Quadrature decoder. If you program TC_SMMR to produce a Phase A square wave at a 42 MHz frequency, and use your Reset/Trigger Signal as an Index to reset TC_CV counter.

Use the Photo Multiplier Signal to trigger an interrupt (finally the only one), connect this signal to a PIO, log Clock Ticks counter value in the PIO Handler.

Same explanation with more details:

The Clock Ticks counter can be incremented at 42 MHz using the Quadrature Decoder sketch with e.g. QDEC1. TC_CV is incremented via pulses received thru TIOA6. TC_SMMR Phase A will be useful to output a pulse for TIOA6.

QDEC1:
Timer Counter 2 channel 0 / TIOA and TIOB of channel 0
PHA  TIOA6 = pin 5
PHB  TIOB6 = pin 4
Timer Counter 2 channel 1 / TIOB of channel 1
INDEX  TIOB7 = pin 10

Each time a pulse(INDEX) is received thru TIOB7, TC_CV is reset. This reset pulse is generated by your Reset/Trigger signal.

See this thread, reply 87:
https://forum.arduino.cc/index.php?topic=140205.75

Now you have to leverage the signal received from the Photo Multiplier. This signal received on a GPIO will trigger an interrupt. attachinterrupt() is a bit slow, so you will modify Winterrupts.h so that you can write your own PIOx_Handler() code, much faster to detect a pulse on a PIOx pin. First off, disable all interrupts from a PIO, except the one you want with PIO_IDR.

See this thread, reply 1:
https://forum.arduino.cc/index.php?topic=602555.0

Once an interrupt fires, the code enters in the PIOx_Handler() after 8 clock cycles (from 84 MHz), therefore you will copy TC_CV value in a buffer at that moment minus 4 (from 42 MHz).

Edit: Phase A of TC_SMMR maximum frequency will be 42 MHz/4, so use directly the output of a Timer Counter with the maximum frequency: 42 MHz

Update ( I thought of sharing it with you as I go along):

There was some silly mistakes in the code above. Now I am getting something that make sense with the following code:

volatile uint32_t CaptureCountA, CaptureCountB;
volatile boolean CaptureFlag
const int max_i = 64;
int i, Buff1[max_i];
void setup() {
  Serial.begin(115200);                                   // initilize serial port to 250000 baud

  PMC->PMC_PCER0 |= PMC_PCER0_PID28;                      // Timer Counter 0 channel 1 IS TC1

  TC0->TC_CHANNEL[1].TC_CCR = TC_CCR_CLKDIS ;                           // disable internal clocking while setup regs

  TC0->TC_CHANNEL[1].TC_CMR = TC_CMR_TCCLKS_TIMER_CLOCK1 // capture mode, MCK/2, clk on rising edge                           
                              | TC_CMR_ABETRG              // TIOA is used as the external trigger
                              | TC_CMR_LDRA_RISING         // load RA on rising edge of trigger input
                              | TC_CMR_LDRB_FALLING;       // load RB on falling edge of trigger input

  TC0->TC_CHANNEL[1].TC_IER |= TC_IER_LDRAS | TC_IER_LDRBS; // Trigger interruption on Load RA and load RB
  TC0->TC_CHANNEL[1].TC_CCR = TC_CCR_SWTRG | TC_CCR_CLKEN ; // Reset TC counter and enable

  //NVIC_DisableIRQ(TC1_IRQn);
  //NVIC_ClearPendingIRQ(TC1_IRQn);
  NVIC_SetPriority(TC1_IRQn, 0);                      // Give TC1 interrupt the highest urgency
  NVIC_EnableIRQ(TC1_IRQn);                             // Enable TC1 interrupts

  // print the startup header
  Serial.println("Timer Capture");
  i = 0;
}

void loop() {
  if (i < max_i){
    if (CaptureFlag) {
      i+=1;
      CaptureFlag = 0;
      Buff1[i] = CaptureCountA;
    }
  }
  else if(i==max_i){
    i+=1;
    Serial.println("The measurements are:");
    for (int j = 0; j <= max_i; j++) {
      printf("\r %d \n", Buff1[j]);
    }
  }
}

void TC1_Handler() {
  uint32_t status = TC0->TC_CHANNEL[1].TC_SR;       // Read & Save satus register -->Clear status register
  if ((status & TC_SR_LOVRS) == TC_SR_LOVRS) abort();

  if ((status & TC_SR_LDRAS) == TC_SR_LDRAS) {  // If ISR is fired by LDRAS then ....
    CaptureCountA = TC0->TC_CHANNEL[1].TC_RA;        // get data from capture register A for TC0 channel 1
  CaptureFlag = 1;                      // set flag indicating a new capture value is present
  }
  else if ((status & TC_SR_LDRBS) == TC_SR_LDRBS) { // If ISR is fired by LDRBS then ....
    CaptureCountB = TC0->TC_CHANNEL[1].TC_RB;         // get data from caputre register B for TC0 channel 1
  }
}

The above works up to 200KHz square wave. At 300KHz, I get twice the correct number most of the time, so it is missing one period.

Comments/Questions:

  • why the code is missing periods? My guess is that the interrupts are eating time. I imagine that this is linked to the point made by ard_newbie in reply 1 of:
    Bad digital IO speed? - Arduino Due - Arduino Forum

  • Another thing I do not understand is that, if I comment the:

  else if ((status & TC_SR_LDRBS) == TC_SR_LDRBS) {
    CaptureCountB = TC0->TC_CHANNEL[1].TC_RB;
  }

then the code stops. I tried to comment, in addition, | TC_CMR_LDRB_FALLING;
the same, the code stops.

Is there a way so the code only go to interrupts on the rising edge? It will halve the number of interrupts, and it will also imply less logic on the code...

Another option, again suggested by ard_newbie on the reply 1 of:
https://forum.arduino.cc/index.php?topic=602555.0

is to forget about the interrupts and do code blocking. However, mot sure how to do this in this context, not even sure if it is possible: the Atmel documentation call it "programmable event"... not sure how to interpret it in this context...

In any case, I still need to digest all the information provided by ard_newbie in his post and links... I really find my limited knowledge of C (I am a Fortran90 lover) as a barrier as sometimes it is just the code itself which confuse me...

In any case, work in progress...

The path I suggested in reply #14 is not fast enough. In fact, since the minimum timelapse between 2 consecutive Photo_Multiplier signals is 45 ns (~ 3 clock cycles at 84 MHz), and the maximum is ? (we don't know), it should be very complex to get a relevent measure.

The only path I can think of now is to leverage the CPU timer counter SysTick (SysTick increments at each and every clock cycle), and as far as possible avoid interrupts.

SysTick counts down from a Load value to 0 and so on. The default Load value upon a DUE is 84000, corresponding with an interrupt fired by SysTick_Handler() every 1 ms. To reduce the number of interrupts fired by SysTick (since I guess we don't need delay(), millis() and micros()), you can Load SysTick with a maximum value of 2 Pow 24. In fact I will choose a bit less (16716000 for precisely 199 ms).

You will have to modify Winterrupts.h to be able to write your own PIO_Handler code because attachinterrupts is too slow. I have explained how to do that in a previous reply#14.

The Reset/Trigger signal will trigger an interrupt, inside this interrupt there is a Reset of SysTick.

Use a blocking code to detect a Rising edge of the Photo_Multiplier pin and log the number of clock cycles since the last Reset/Trigger Signal.

Connect pull down resistors to the 2 input signals.

// Reset/Trigger signal connected to PC1
// Photo Multiplier connected to PD1
/*************************************************************************************
  void SysTick Handler ( void) {
  i f ( sysTickHook ( ) ) return ;
  . . .
  }
*************************************************************************************/
//#pragma GCC optimize ("-O3")
#define INT_MASK_TS   (PIO_PC1)
#define INT_MASK_PHM  (PIO_PD1)

volatile uint32_t msCounter;
// sysTickHook will be triggered by sysTick_Handler once per 199 ms
extern "C" {
  int sysTickHook(void) {
    msCounter++;
    return 0;
  }
}
void setup() {
  Serial.begin(250000);
  pinMode(26, INPUT);  // PD1
  SysTick->LOAD = 16716000; // For 199 ms
  pioc_setup();
}

void loop() {
  
  const uint32_t Offset = 1; // Adjust this value
  const uint32_t BufferSize = 100;
  static uint32_t Index;
  uint32_t Buffer[BufferSize];

  // Todo: print Buffer values after measurement
  while (true)
  {
    while (!(PIOD->PIO_PDSR & INT_MASK_PHM)); // Wait until Photo Multiplier is high
    Buffer[Index++]  = (msCounter * 16716000 ) + (16716000 - SysTick->VAL) - Offset;
  }
}
/*******************************************************************/
void pioc_setup()
{
  PMC->PMC_PCER0 = PMC_PCER0_PID13;   // PIOC power ON
  
  PIOC->PIO_IDR = ~(0ul);             // disable all PIOC interrupts
  PIOC->PIO_PER = INT_MASK_TS;        // enable paralel input - output
  PIOC->PIO_IFER = INT_MASK_TS;       // enable glitch filter (1/2 clock cycle glitches discarted)
  PIOC->PIO_AIMER =   INT_MASK_TS;    // The interrupt source is described in PIO_ELSR
  PIOC->PIO_FRLHSR = INT_MASK_TS;     // Interrupt source on a Rising edge detection
  PIOC->PIO_IER = INT_MASK_TS;        // enable interrupt trigger from INT_MASK_TS pin
  NVIC_EnableIRQ(PIOC_IRQn);
}

// Reset/Trigger signal connected to PC1
void PIOC_Handler()
{
  PIOC->PIO_ISR;
 // if (PIOC->PIO_PDSR & INT_MASK_TS)  // useless because the only one
 // {
    SysTick->VAL = 0; // Reset SysTick
    msCounter = 0;
 // }
}

I just came across this:

It is a bit old, but probably still valid. I imagine that what he/she means by "modified kernel ISR" is the same modification of the "winterrupts.c" as proposed by ard_newbie

Assuming that my measurement can only be done by interrupts, then I will never be at a 42MHz rate! At least I can aim for a 42MHz time resolution with much lower counting rates... but this kind that misses the point of what I was trying to achieve...

By the way, I only had to modify the line:
TC0->TC_CHANNEL[1].TC_IER |= TC_IER_LDRAS | TC_IER_LDRBS; // Trigger interruption on Load RA and load RB

To trigger interruption on Load RA

I still not know how to implement it without interrupts...

In any case, I feel have learn a fair amount about the DUE and its potentials in the last two days :wink:

Since you can't have 2 blocking codes and you need the best possible granularity (a 84 MHz counter = Systick Counter), at least one interrupt is necessary. The number of clock cycles to enter a PIO_Handler() is between 8 and 9 clock cycles, therefore the "delay" is more or less known.

A refinement for the sketch provided in reply #16 would be to copy continuously PIOD->PIO_PDSR into a status variable with a DMA and a linked list item, test this variable upon INT_MASK_PHM and maybe:

while (!(status & INT_MASK_PHM));

would be 2 or 3 clock cycles shorter.

Dear all,

I do not much time now, but a I though a quick update was in order.

The code using the code blocking (I did copy-paste) is given me non-sense: it is triggering far too often (see attached figure, 'counter_code_blocking.png'). (I did not implement the pull-down resistors as suggested, but I would be surprised if this is the reason...)
Any idea why? Do you really think that the pull-down resistor could help? In any case, it will have to wait until Monday now...

However, I have modified the version using interrupts and reduced the logic to a minimum and storing the SysTick to have a better time resolution. I have also introduced a while(True) to avoid the main Arduino Loop (it really made a difference!). With the code below, I was able to acquire with 12ns resolution a 1MHz square wave. There were some points that did not have the right counting, but overall is quite good (see attached figure 'counter_1MHz_12ns.png').

I have not introduced yet the reset of the counter by an external signal.

The code used is:

volatile uint32_t CaptureCountA;
const int max_i = 64;
int i, Buff[max_i];

void setup() {
  Serial.begin(250000);                                    // initilize serial port to 250000 baud

  PMC->PMC_PCER0 |= PMC_PCER0_PID28;                       // Timer Counter 0 channel 1 IS TC1
  TC0->TC_CHANNEL[1].TC_CCR = TC_CCR_CLKDIS ;                           // disable internal clocking while setup regs
  TC0->TC_CHANNEL[1].TC_CMR = TC_CMR_TCCLKS_TIMER_CLOCK1   // capture mode, MCK/2, clk on rising edge                           
                              | TC_CMR_ABETRG              // TIOA is used as the external trigger
                              | TC_CMR_LDRA_RISING         // load RA on rising edge of trigger input
                              | TC_CMR_LDRB_FALLING;       // load RB on falling edge of trigger input

  TC0->TC_CHANNEL[1].TC_IER |= TC_IER_LDRAS;               // Trigger interruption on Load RA and load RB
  TC0->TC_CHANNEL[1].TC_CCR = TC_CCR_SWTRG | TC_CCR_CLKEN ;// Reset TC counter and enable

  NVIC_SetPriority(TC1_IRQn, 0);                           // Give TC1 interrupt the highest urgency
  NVIC_EnableIRQ(TC1_IRQn);                                // Enable TC1 interrupts

  Serial.println("Timer Capture");
  i = 0;
  SysTick->LOAD = 16716000; // For 199 ms
}

void loop() {
while (true){
  if (i < max_i){
    if (CaptureCountA!=0) {
      Buff[i] = CaptureCountA;
      CaptureCountA = 0;
      i+=1;
    }
  }
  else if(i==max_i){
    i+=1;
    Serial.println("And...");
    for (int j = 0; j <= max_i; j++) {
      printf("\r %d \n", Buff[j]);
    }
  }
}
}
void TC1_Handler() {
    TC0->TC_CHANNEL[1].TC_SR;   // Read & Save satus register -->Clear status register
    TC0->TC_CHANNEL[1].TC_RA;   // get data from capture register A for TC0 channel 1
    CaptureCountA = SysTick->VAL;
}

Have a nice weekend!

counter_Code_Blocking.png

counter_1MHz_12ns.png