Arduino Due Digital Counter for X-Ray Spettroscopy

Hi everyone,
I'm implementating a XRF (x-ray fluorecence) mapping method based on Arduino.
Recently I moved to Arduino DUE because I needed a pure analog output but I found that the Arduino UNO program wasnt running any more.
The problem is that the library I used does not support Arduino DUE and so I'm very disappointed.
The program I wrote for Arduino UNO works properly and it have to count digital signal coming from a spectrometer in order to "integrate" the signal.
I post down here the original program.
Now, how can I replace the library FreqCount.h with something similir for Arduino DUE and make the program working again?
I would love to have a good counter up to frequency of 1MHz.
Thanks a lot for who is gonna help me!

#include <FreqCount.h>

unsigned long count = 0;
unsigned long prior_count=0;
int digPin = 5;  //pin di trigger
int valPin = 6; //pin su cui sputa la tensione 
int trigPin1 = A1; // trigger analogico
unsigned long startTime=0;
 int ciclo=0;
float tens=0;


void setup() {
  Serial.begin(4800);
  FreqCount.begin(100);
  pinMode(digPin, INPUT);
  pinMode(valPin, OUTPUT);
  
}

void loop() {
  //int trig = digitalRead(trigPin);
  int trig1 = analogRead(trigPin1);
 
 //Serial.println(trig1);
  
  if(trig1<100)
  {
    
    while(ciclo<1)
    {
    //Serial.println(ciclo);
    startTime = millis();
    //Serial.println(startTime);
            while(millis()-startTime < 10000)
            {
                 if (FreqCount.available()){
                  count = count + FreqCount.read();
                        if (count != prior_count) {
                              prior_count = count;
                              //Serial.println(count);
                              tens = map(count,900,1100,0,255);
                              //tens=(tens/5)*255;
                              //Serial.println(tens);
                              
                              }}
                   if (millis()-startTime>10000){
                  break;
                  }
             }
      ciclo=1;
      Serial.print(count);
      Serial.print(" - ");
      Serial.println(tens);
      analogWrite(valPin, tens);
      }}
     else if (trig1>100)
     {ciclo=0;
     count=0;} 
     }
      
     //else{ciclo=0;
     //count=0;}

Where did you get the FreqCount library ?

there is it!
https://www.arduino.cc/reference/en/libraries/freqcount/
But unfortunatly it doesnt work for Arduino DUE

The library says that it is compatible with all Arduino architectures, but the documentation says that the frequency input pin varies depending on the board and doesn't mention the Due.

You might need to dig into the library code to find out which pin is used.

An example of an Input Capture code of a PWM frequency (3.5 MHz but obviously can work for lower frequencies):

/*************************************************************************************************/
/*  a jumper needs to be installed between pin 2 (TIOA0) and pin A7 (TC0 channel 1 TIOA1 pin)    */
/*************************************************************************************************/

void setup() {

  Serial.begin(250000);

  /*  This code simulates a PWM pulse   */
  /*************  Timer Counter 0 Channel 0 to generate PWM pulses thru TIOA0  ************/
  PMC->PMC_PCER0 |= PMC_PCER0_PID27;                      // Timer Counter 0 channel 0 IS TC0
  PIOB->PIO_PDR |= PIO_PDR_P25 | PIO_PDR_P27;
  PIOB->PIO_ABSR |= PIO_ABSR_P25 | PIO_ABSR_P27;

  TC0->TC_CHANNEL[0].TC_CMR = TC_CMR_TCCLKS_TIMER_CLOCK1  // MCK/2, clk on rising edge
                              | TC_CMR_WAVE               // Waveform mode
                              | TC_CMR_WAVSEL_UP_RC       // UP mode with automatic trigger on RC Compare
                              | TC_CMR_ACPA_SET           // Set TIOA0
                              | TC_CMR_ACPC_CLEAR;        // clear TIOA0


  TC0->TC_CHANNEL[0].TC_RC = 12;//<*********************  Frequency = (Mck/2)/TC_RC  Hz
  TC0->TC_CHANNEL[0].TC_RA = 1;//<********************   Duty cycle = (TC_RA/TC_RC) * 100  %

  TC0->TC_CHANNEL[0].TC_CCR = TC_CCR_SWTRG | TC_CCR_CLKEN; // Reset TC0 counter and enable

  /*  This part is an Input Capture of the PWM frequency   */
  /*************  Timer Counter 0 Channel 1 to capture PWM pulses thru TIOA1  ************/
  PMC->PMC_PCER0 |= PMC_PCER0_PID28;                      // Timer Counter 0 channel 1 IS TC1

  TC0->TC_CHANNEL[1].TC_CMR = TC_CMR_TCCLKS_TIMER_CLOCK1 // capture mode, MCK/2, clk on rising edge
                              //  | TC_CMR_ABETRG              // TIOA1 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_CCR = TC_CCR_SWTRG | TC_CCR_CLKEN; // Reset TC1 counter and enable

}

void loop() {

  const uint32_t _F_TC1 = F_CPU / 2; //<************ =  TC1 clock
  uint32_t CaptureCountA, CaptureCountB, Period, CaptureFlag;
  register uint32_t _CaptureCountA, counter ;
  register uint32_t Frequency;
  register uint32_t status;

  while (true) {
    status = TC0->TC_CHANNEL[1].TC_SR;

    if (status & TC_SR_LDRAS) {
      CaptureCountA = TC0->TC_CHANNEL[1].TC_RA;  // get data from capture register A of TC0 channel 0
      Period = CaptureCountA - _CaptureCountA;

      if (counter++ > 100000) {                  // write Frequency on Serial Monitor every 100000 samples
        Frequency = _F_TC1 / Period  ;           //  (Mck/2 is TC1 clock) F in Hz
        printf(" F = %d  Hz\n", Frequency);
        counter = 0;
      }
      _CaptureCountA = CaptureCountA;
    }
  }
}

1 Like

Thank you for your reply.
I saw this code on the forum but it cant fit for my scope: I need an object that can increment a counter each time a rising edge of a digital signal is registered.
In other words I need something able to "integrate" the digital signals over a fixed period of time.
I dont understand how to fit those function for my scope.
thank you for the time you will spend on this!

Firstly try to understand how this code works.

How about adding a DAC to your UNO?

https://www.mouser.de/Search/Refine?N=7283677

@ard_newbie
thank you a lot for the reply.
I tried to figure out how the code is working, I've read the amtel datasheet and everything sound a littler familiar.
this is how i modified it

/*************************************************************************************************/
/*  Capture pin A7 (TC0 channel 1 TIOA1 pin)    */
/*************************************************************************************************/
int trigPin1 = A1; // trigger analogico
unsigned long startTime=0;
int valPin = 6; //pin su cui sputa la tensione 
int ciclo=0;
float tens=0;
  
void setup() {

  Serial.begin(250000);
  /*  This part is an Input Capture of the PWM frequency   */
  /*************  Timer Counter 0 Channel 1 to capture PWM pulses thru TIOA1  ************/
  PMC->PMC_PCER0 |= PMC_PCER0_PID28;                      // Timer Counter 0 channel 1 IS TC1

  TC0->TC_CHANNEL[1].TC_CMR = TC_CMR_TCCLKS_TIMER_CLOCK1 // capture mode, MCK/2, clk on rising edge
                              //  | TC_CMR_ABETRG              // TIOA1 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_CCR = TC_CCR_SWTRG | TC_CCR_CLKEN; // Reset TC1 counter and enable

}

void loop() {

  const uint32_t _F_TC1 = F_CPU / 2; //<************ =  TC1 clock
  uint32_t CaptureCountA, CaptureCountB, CaptureFlag;
  register uint32_t _CaptureCountA;
  register uint32_t counter=0;
  register uint32_t status;
  int trig1 = analogRead(trigPin1);
 // Serial.println(trig1);

 if(trig1<1000)
  {
    while(ciclo<1)
    {
    startTime = millis();
    while(millis()-startTime < 10000)
       {
         while (true) {
              status = TC0->TC_CHANNEL[1].TC_SR;
              if (status & TC_SR_LDRAS) {
                CaptureCountA = TC0->TC_CHANNEL[1].TC_RA;  // get data from capture register A of TC0 channel 0
                counter++; 
                printf(" Count = %d  \n", counter);
                _CaptureCountA = CaptureCountA;
                }
              }                       
       }
    if (millis()-startTime>10000){
  break;}
 
    ciclo=1;
    Serial.print(" - ");
    Serial.println(tens);
    analogWrite(valPin, tens);
     }
   }
     else if (trig1>100)
    {ciclo=0;
    counter=0;} 
     }

I don't understand why it ignore the commands about the trigger value: I want the counter to reset when a digital input comes in and after, it have to start counting for 10seconds and show the result.
It seems like it ignore the first if(trig1<1000).
(I use an external wave generator connected to pin A7 with a pulse height of 3V and various test frequencies)
Any suggestion?
thanks

That was an option but my research group got the Arduino DUE so I have to use this....

Hi everybody,
finally solved. I post the code down here.
It's able to count digital signals in a range of period (IntegrationTime) if triggered by a digital signal measured on analog pin A1.
The only problem is that I was beliving Arduino Due were able to count well up to severall MHz but, from my test, it starts to lose counts at around 100kHz. Any suggestion?
Again thanks @ard_newbie for the hints.

/*************************************************************************************************/
/*  ATTENZIONE!!!
  /*  Arduino DUE accetta segnali TTL max 3,3V (5V bruciano la scheda!)
  /*  Contatore digitale pin A7 (TC0 channel 1 TIOA1 pin)
  /*  TRIGGER PIN A1 - segnale alto si ferma, basso acquisisce
  /*  DAC1 forniscer un valore di tensione tra 0,5 e 2,75V
  /*************************************************************************************************/
int trigPin1 = A1;            // trigger analogico
int valPin = 6;               // pin su cui sputa la tensione
unsigned long startTime = 0;
int ciclo = 0;
float tens = 0;
float counter = 0;
int IntTime = 10000;   //integration time, in millisecondi

void setup() {
  analogWriteResolution(12);
  pinMode(trigPin1, INPUT);
  Serial.begin(4800);

  /*  Inizializzazione del modulo Timer Counter 0 Channel 1  */
  /*  per registrare valori digitali attraverso TIOA1        */
  PMC->PMC_PCER0 |= PMC_PCER0_PID28;                         // Timer Counter 0 channel 1 IS TC1
  TC0->TC_CHANNEL[1].TC_CMR = TC_CMR_TCCLKS_TIMER_CLOCK1     // capture mode, MCK/2, clk on rising edge
                              //  | TC_CMR_ABETRG            // TIOA1 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_CCR = TC_CCR_SWTRG | TC_CCR_CLKEN;   // Reset TC1 counter and enable
}

void loop() {
  const uint32_t _F_TC1 = F_CPU / 2;   //<************ =  TC1 clock
  uint32_t CaptureCountA;
  register uint32_t _CaptureCountA;
  register uint32_t status;
  int trig1 = analogRead(trigPin1);
  //Serial.println(trig1);            //Abilitare questa riga per valutare su che sogli impostare il trigger

  if (trig1 < 10)             //valore di trigger da aggiustare (0V-->0, 3.3V-->1022)
  { while (ciclo < 1)
    { startTime = millis();
      while (millis() - startTime < IntTime)
      { status = TC0->TC_CHANNEL[1].TC_SR;
        if (status & TC_SR_LDRAS) {
          CaptureCountA = TC0->TC_CHANNEL[1].TC_RA;  // get data from capture register A of TC0 channel 0
          counter++;
          _CaptureCountA = CaptureCountA;
        }
        if (millis() - startTime > IntTime) {
          break;
        }
      }
      ciclo = 1;
      tens = map(counter, 0, 1000000, 0, 4096); //mappare il valore di tensione in uscita nel range migliore possibile
      Serial.print(counter);                    //tra un lowValue, maxValue per avere la risoluzione migliore possibile
      Serial.print("...");                      //utilizzati 12bit quindi mapping tra 0 e 4096
      Serial.println(tens);
      analogWrite(DAC1, tens);
    }
  }
  else if (trig1 > 10)
  { Serial.println(".");
    ciclo = 0;
    counter = 0;
  }
}

Hi there @polminuts,

I imagine you have already solved your problem, but in any case, you might have look to tc_lib for measuring a digital signal using timer TC modules available in DUE's ATSAM3X8E microcontroller. You can find the library here: https://github.com/antodom/tc_lib.

@HermannSW has done some nice work measuring tc_lib's limits in this post: here

I hope it helps.