Due: Wrong count for Falling Edge Interrupt Counter

I wrote code for an Arduino Uno to count clock edges but have switched to the Arduino Due because I thought the faster processor speed (84MHz) would allow me to sample a faster signal. I tried different experiments running a 2MHz or a 16Mhz 0-5V square wave to count the edges but my counts seem to be off. To test, I input a square wave into pin 2 from a signal generator, turn off the square wave output, then pull pin 4 low to print the count. To restart the test upload the same program and begin again.

If I run a 1MHz signal in for a few seconds my count is near where I expect it to be. If I run a 2Mhz signal in for a long time, say a minute or more, my count comes back as 1. I thought this must be some type of overflow but the "long" variable types should be 32 bit and be able to handle numbers up to 4294967296 which would run for over an hour. My counts at 16MHz are also way off but perhaps this is too fast for the Due to count, or maybe I made a mistake in my code:

volatile long count_1 = 0;     //counter 1
int count_3 = 0;      //counter to trigger serial print

void increment_1() {      // ISR
  count_1++;
}

void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600);          //
  open the serial port at 9600 bps
  pinMode(4, INPUT_PULLUP);
  pinMode(2, INPUT_PULLUP);
  attachInterrupt(2, increment_1, FALLING);   //Enable Interrupt on pin 2
}

void loop() {
  int sensorVal = digitalRead(4);   //read pin 4

  if (sensorVal == LOW) {
    count_3++;                      //increase count so program enters while loop once
  }

  while (count_3 == 1) {
    count_3++;                    //increase count3 so it never prints again
    noInterrupts();
    long localCount1 = count_1;
    interrupts();
    Serial.print('\n');
    Serial.println(localCount1);
  }

}

ONLY if is UNSIGNED long!

1 Like

I used interrupts to generate square waves from a Due, and it worked well up to 1M/sec (500K wave, 2 interrupts per cycle). Above that I started losing interrupts (the output frequency actually declined). That seems to correlate with your experiences. There is a significant amount of processor activity responding to each interrupt, even if the time inside the interrupt is minimal.

The timers on the Due are also counters. I am not sure if what you are looking for is a count or duration or something else, but a quick perusal of the datasheet for the SAM3X seems to indicate it should be able to count up to somewhere around 33 MHz. And leave your processor available to do other things.

2 Likes

All I'm looking to count the number of falling edges. Didn't know interrupts had that much overhead. Is there a name for using counters in this way so I can look up an example? Is this what polling is?

There are timer functions like TIOA0 which is accessible at D2. But I don't know of any offhand examples or libraries for what you want to do. They can all be accessed by writing to various bits of registers that start with PIO (as described in the ATMEL data sheet (here). I am only beginning MY adventures with the timers, and I haven't attempted that here yet.
There are 9 total timing channels (3 timers, 3 channels each), so by using one to generate an interrupt at some useful interval and reading/manipulating the one you are using for a counter during that interrupt might be a way to approach your problem.

Hi @freebird4446

As @weshowe says, it's possible to use the Arduino Due's TC timers as counters.

The following example code uses the Due's PWM controller to generate a 500kHz square wave test output on the DAC1 pin. The TC0 timer's TCLK0 input on digital pin D22 is configured to count the incoming pulses on the falling edges and output the result every 10ms to the console. Just connect DAC1 to D22:

// Set-up TC0 to count pulses (falling edges) on digital pin D22, (with 500kHz test frequency on DAC1)
void setup() { 
  SerialUSB.begin(115200);                            // Initialise the native USB port
  while(SerialUSB);                                   // Wait for the serial port to be ready
  PMC->PMC_PCER1 |= PMC_PCER1_PID36;                  // Enable PWM output at 500kHz
  PIOB->PIO_ABSR |= PIO_ABSR_P16;                     // Switch multiplexer to peripheral B for PWM, channel 0 on pin DAC1
  PIOB->PIO_PDR |= PIO_PDR_P16;                       // Disable the GPIO on the corresponding DAC1 pin
  PWM->PWM_CLK = PWM_CLK_PREA(0) | PWM_CLK_DIVA(1);   // Set the PWM clock rate to 84MHz (84MHz/1) 
  PWM->PWM_CH_NUM[0].PWM_CMR = PWM_CMR_CPRE_CLKA;     // Enable single slope PWM and set the clock source as CLKA
  PWM->PWM_CH_NUM[0].PWM_CPRD = 168;                  // Set the PWM frequency 84MHz/168 = 500kHz 
  PWM->PWM_CH_NUM[0].PWM_CDTY = 84;                   // Set the PWM duty cycle 50%
  PWM->PWM_ENA = PWM_ENA_CHID0;                       // Enable the PWM channel
  
  PMC->PMC_PCER0 |= PMC_PCER0_PID27;                  // Enable peripheral TC0 (TC0 Channel 0)
  PIOB->PIO_ABSR |= PIO_ABSR_P22;                     // Switch the multiplexer to peripheral B for TCLK0 on D22
  PIOB->PIO_PDR |= PIO_PDR_P22;                       // Disable the GPIO on the corresponding pin 
  TC0->TC_CHANNEL[0].TC_CMR = TC_CMR_CLKI |           // Count falling edges
                              TC_CMR_TCCLKS_XC0;      // Enable TC0 external clock
  TC0->TC_CHANNEL[0].TC_CCR |= TC_CCR_CLKEN;          // Start timer TC0
}

void loop() {
  TC0->TC_CHANNEL[0].TC_CCR |= TC_CCR_SWTRG;          // Reset timer TC0 to 0
  delay(10);                                          // Wait for 10ms (100Hz)
  SerialUSB.println(TC0->TC_CHANNEL[0].TC_CV);        // Print the number of pulses counted
}
1 Like

The interrupt has to save the state of the processor and all critical registers, execute the interrupt routine, then restore everything.

Use a hardware timer with external clock input (Input Capture Mode). Even the Arduino Uno can count up to 6.7 MHz (CPU clock/2.4).

1 Like

Hey, thanks for the example code! After reading @freebird4446 post, I started wondering if I could use this capability for something else. I have a page filled with notes from studying the datasheet, your example looks like it will help a lot.

  • Wes

EDIT: I tested the counter half using an external signal generator, and it works great (square, sine or triangle waves) up to 34 Mhz. I get about a 0.001% difference (345 in 34M) between my low-end sig-gen readout and the Due, but I don't have any resources here to validate which is off, although the error rate is pretty consistent from kilohertz through megahertz.

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