Due code review (about 60 Lines)

Hey everyone,

I am not only new to programming the arduino due but to arduino in general, so I would be really happy if you could review my code real quick. Especially the direct port access parts (I/O, T/C, DAC). I would appreciate some feedback before I take my 1,5h journey to a place were I can test it with a function generator.

For the direct port access I kind of patched some code together which i found in different forum threads due to my lack of experience.

It's supposed to count the frequency of a non-periodic square wave input signal[1-6MHz] by counting the number of timer counter pulses inbetween the input pulses.

PS: It compiles without errors.

// This program calculates the frequency of TTL Pulses by measuring the time in between pulses
// The program reads in TTL Pulses via the I/O Pins and outputs the frequency by the DAC

// Operating system: Windows 10 Home
// Software: Arduino IDE
// Hardware: Arduino due, AT91SAM3X8E
// Version: 1.0
// Released: DD.MM.YYYY

 //#define TC_PERIOD 0.00000002380952381  // 23,80952381 ns
 #define TC_CLK 42  //timer counter clock is half of MCK (84MHz)--> 42MHz
 #define freqNum 5  // number of frequencies to calculate the mean frequency

  // global variables
  int inputPin = 51;           // Pin which reads in TTL signal (94/C.12/D51)(PC12 on SAM3X8E)
  int outputPin = 66;          // DAC0 PB15
  uint32_t counterValue;       // variable to store the current counter value
  uint32_t lastFreq[freqNum];  // latest frequencies
  uint32_t meanFreq;           // mean frequency
  uint8_t freqPos;             // Frequency Position in Array
  bool isHigh;                 // boolean variable for edge detection
  bool p12;                    // boolean to check input pin state
  

void setup() {  // put your setup code here, to run once
  //*****************************************************************************************************************************************************************************************
  // setup variables
  freqPos = 0;
  isHigh = true;
  
  // setup I/O
  pinMode(inputPin, INPUT);  // set pin51 = PC12 to input

  // setup T/C0
  PMC->PMC_PCER0 = 1<<27;          // Peripheral clock enable reg.: Enable Timer TC0
  TC0->TC_CHANNEL[0].TC_CMR = 0;   // Control mode reg.: set Clock to MCK/2 = 42 MHz  
  TC0->TC_CHANNEL[0].TC_CCR = 1;   // Channel control reg.: activate Timer --> Start + Enable
    
  // setup DAC
  pinMode(outputPin, OUTPUT);                   // set pin66 = PB15 as output
  analogWriteResolution(10);                    // set resolution to 10-bit (1024)
  PMC->PMC_PCER1 = PMC_PCER1_PID38;             // DACC power ON
  DACC->DACC_CR = DACC_CR_SWRST ;               // Reset DACC
  DACC->DACC_MR = DACC_MR_TRGEN_DIS             // Hardware trigger disable = free running mode
                  | DACC_MR_USER_SEL_CHANNEL0   // select channel 1
 //                 | DACC_MR_REFRESH (1)  // needed???
                  | DACC_MR_STARTUP_0           // zero seconds start up time
                  | DACC_MR_MAXS;               // set DAC to max speed

  DACC->DACC_CHER = DACC_CHER_CH0;  // enable channel 0 = DAC0
}

void loop() {  // put your main code here, to run repeatedly:
//*******************************************************************************************************************************************************************************************
  // read I/O Pin
  p12 = !!(PIOC->PIO_PDSR & PIO_PC12); //read pin 12 on port C
  
  // if level change detected read TC
  if((p12==true)&&(!isHigh)) {
    
    // start/stop time
    counterValue = TC0->TC_CHANNEL[0].TC_CV;  // counter value reg.: read actual TC value
    TC0->TC_CHANNEL[0].TC_CCR = 4;            // reset TC value to zero and start clock (bit 3)

    // calculate latest frequency
    lastFreq[freqPos] = TC_CLK/counterValue;  // divide timer counter clock by nuber of counts [MHz]
    freqPos++;                                // increment array index
    if(freqPos > 4) {                         // prevent overflow and update array with new frequencies
      freqPos = 0;
    }
    // calculate mean frequency
    meanFreq = (lastFreq[0]+lastFreq[1]+lastFreq[2]+lastFreq[3]+lastFreq[4])/freqNum;  // [MHz]
    
    // output the frequency from 0V-3.3V
    DACC->DACC_CDR = meanFreq;           // use [Frequency = 5454545.454545 * voltage - 3000000] to get Frequency in Hz
    //analogWrite(outputPin, meanFreq);  // multiply measured voltage with factor 3636363,6364 to get Frequency in Hz

    isHigh = true;  // change isHigh so if statement is only accessed once per pulse
  }
    
  //****************************************************************************************************************************************************************************************
  // if pin changed to low reset boolean variable
  if ((p12==false)&&(isHigh)) {
    isHigh = false;
  }
}

Don’t you need some scaling of the frequency (up to 6e6) before outputting to the DAC (up to 4096)?

i'm not familiar with Atmel registers.
looks like when the code detects a rising edge, it captures some number of register clock cycle, converts it to a corresponding frequency, averages 4 values and outputs the value

a different approach is to simply count the number of pulses over some period of time and calculate the freq over that interval. the interval doesn't need to be fixed, checked each pulse and a measurement reported if the interval is long enough

another aspect of this approach the interval is measured from the last pulse of the previous interval -- the timing mechanism (e.g. micros() /millis() ) is never reset

instead of maintaining a separate flag (isHigh) indicating the state of the input, why not simply track the last state, p12Lst, check for a change, if (p12Lst != p12) and check that it's the desired edge, if (HIGH == p12)

it's not clear how frequently the output needs to be reported. the DAC is updated each pulse. it's unclear how the DAC output is used.

it's not clear how much averaging is necessary. leaky integration is another way to average with minimal complexity and delay

if you're concerned about latency, an interrupt could be used that increments a count and captures a timestamp

Oh yeah you're right, thanks!

averages 4 values and outputs the value

5 values to be correct

a different approach is to simply count the number of pulses over some period of time and calculate the freq over that interval

wouldn't the non periodicity of the input make this approach less useful?

another aspect of this approach the interval is measured from the last pulse of the previous interval – the timing mechanism (e.g. micros() /millis() ) is never reset

I'm not using micros() and millis() due to my high frequency requirements. I do a T/C software reset and immediatly after the reset the timer starts counting again.

leaky integration

I'm gonna have a look at that, thanks

again it's not clear how the DAC output is used. if the pulses are non-periodic, then it will fluctuate and there will be higher frequency noise.

averaging will of course make the output more stable

counting pulses over an interval and reporting it every ~interval is of course averaging. counting a large number of pulses over an interval inherently improves accuracy. not sure why micros() can't be used

i'm curious what is being measured and what is high frrequency - kHz, Mhz, gHz?

i’m curious what is being measured and what is high frequency - kHz, Mhz, gHz?

I'd like to determine the frequency of TTL pulses from a single photon counter. Depending on the number of photons detected the frequency varies from about 1MHz to 12MHz. My goal for the moment is to achieve accurate results at 2MHz but of course covering the whole range would be great.

how frequent do you need a count to be reported? currently you report it every pulse

somehow i doubt the code you posted can run at 2 MHz

the Arduino Due runs at 84 Mhz. that suggests there only 42 instruction cycles between pulse occurring every 2 MHz, 8 at 10 Mhz.

how frequent do you need a count to be reported? currently you report it every pulse

would a less frequent report improve the performance?

an ISR would require very few cycles to simply increment a counter. foreground processing could simply look at a change in the count and do some further processing which may capture a timestamp and report a frequency.

what if there was a report every second that reported the total number of counts in that second? it would do roughly the same processing once a second your code does each pulse ~2M/sec

This topic was automatically closed 120 days after the last reply. New replies are no longer allowed.