Reading frequency on Uno vs Mega, differences

I’m using the following code to read a square wave.

http://www.gammon.com.au/forum/?id=11504

// Frequency timer
// Author: Nick Gammon
// Date: 10th February 2012

// Input: Pin D2

volatile boolean first;
volatile boolean triggered;
volatile unsigned long overflowCount;
volatile unsigned long startTime;
volatile unsigned long finishTime;

// here on rising edge
void isr () 
{
  unsigned int counter = TCNT1;  // quickly save it
  
  // wait until we noticed last one
  if (triggered)
    return;

  if (first)
    {
    startTime = (overflowCount << 16) + counter;
    first = false;
    return;  
    }
    
  finishTime = (overflowCount << 16) + counter;
  triggered = true;
  detachInterrupt(0);   
}  // end of isr

// timer overflows (every 65536 counts)
ISR (TIMER1_OVF_vect) 
{
  overflowCount++;
}  // end of TIMER1_OVF_vect


void prepareForInterrupts ()
  {
  // get ready for next time
  EIFR = bit (INTF0);  // clear flag for interrupt 0
  first = true;
  triggered = false;  // re-arm for next time
  attachInterrupt(0, isr, RISING);     
  }  // end of prepareForInterrupts
  

void setup () 
  {
  Serial.begin(115200);       
  Serial.println("Frequency Counter");
  
  // reset Timer 1
  TCCR1A = 0;
  TCCR1B = 0;
  // Timer 1 - interrupt on overflow
  TIMSK1 = bit (TOIE1);   // enable Timer1 Interrupt
  // zero it
  TCNT1 = 0;  
  overflowCount = 0;  
  // start Timer 1
  TCCR1B =  bit (CS10);  //  no prescaling

  // set up for interrupts
  prepareForInterrupts ();   
  
  } // end of setup

void loop () 
  {

  if (!triggered)
    return;
 
  unsigned long elapsedTime = finishTime - startTime;
  float freq = F_CPU / float (elapsedTime);  // each tick is 62.5 nS at 16 MHz
  
  Serial.print ("Took: ");
  Serial.print (elapsedTime);
  Serial.print (" counts. ");

  Serial.print ("Frequency: ");
  Serial.print (freq);
  Serial.println (" Hz. ");

  // so we can read it  
  delay (500);

  prepareForInterrupts ();   
}   // end of loop

This works perfectly for the UNO. With the MEGA it reads high.

Input 100Hz: Uno returns 100Hz, MEGA returns ~117Hz
Input 200Hz: Uno returns 200Hz, MEGA returns ~280Hz
Input 1000Hz: Uno returns 1000Hz, MEGA returns ~1700Hz

I tried a different Mega, an official one from the Arduino store. Same results.
I thought maybe there’s a problem with Timer 1 with the Mega? I tried switching to Timer 3, not sure if I did it right.

// Frequency timer
// Author: Nick Gammon
// Date: 10th February 2012

// Input: Pin D2

volatile boolean first;
volatile boolean triggered;
volatile unsigned long overflowCount;
volatile unsigned long startTime;
volatile unsigned long finishTime;

// here on rising edge
void isr () 
{
  unsigned int counter = TCNT3;  // quickly save it
  
  // wait until we noticed last one
  if (triggered)
    return;

  if (first)
    {
    startTime = (overflowCount << 16) + counter;
    first = false;
    return;  
    }
    
  finishTime = (overflowCount << 16) + counter;
  triggered = true;
  detachInterrupt(0);   
}  // end of isr

// timer overflows (every 65536 counts)
ISR (TIMER3_OVF_vect) 
{
  overflowCount++;
}  // end of TIMER1_OVF_vect


void prepareForInterrupts ()
  {
  // get ready for next time
  EIFR = bit (INTF0);  // clear flag for interrupt 0
  first = true;
  triggered = false;  // re-arm for next time
  attachInterrupt(0, isr, RISING);     
  }  // end of prepareForInterrupts
  

void setup () 
  {
  Serial.begin(115200);       
  Serial.println("Frequency Counter");
  
  // reset Timer 1
  TCCR3A = 0;
  TCCR3B = 0;
  // Timer 1 - interrupt on overflow
  TIMSK3 = bit (TOIE3);   // enable Timer1 Interrupt
  // zero it
  TCNT3 = 0;  
  overflowCount = 0;  
  // start Timer 1
  TCCR3B =  bit (CS10);  //  no prescaling

  // set up for interrupts
  prepareForInterrupts ();   
  
  } // end of setup

void loop () 
  {

  if (!triggered)
    return;
 
  unsigned long elapsedTime = finishTime - startTime;
  float freq = F_CPU / float (elapsedTime);  // each tick is 62.5 nS at 16 MHz
  
  Serial.print ("Took: ");
  Serial.print (elapsedTime);
  Serial.print (" counts. ");

  Serial.print ("Frequency: ");
  Serial.print (freq);
  Serial.println (" Hz. ");

  // so we can read it  
  delay (500);

  prepareForInterrupts ();   
}   // end of loop

Still the same results. Not sure what’s going on, anyone ever encountered this when switching to a MEGA?

Have you checked the datasheet? The Mega will have different registers to Atmega328.

stoutfiles: Not sure what's going on, anyone ever encountered this when switching to a MEGA?

Maybe a problem with your signal: is signal ground and Arduino ground connected and at the same ground level?

What about switching to a different frequency counting code?

This one counts hardware interrupts (UNO example: pin-2, interrupt-0) using the micros() function for timing:

#define FREQPIN 2
#define FREQINT 0

// volative variables for interrupt handling
volatile uint16_t count_ISR;
volatile uint32_t time_ISR;

void freqCounter() // interrupt handling
{
  count_ISR++;
  time_ISR=micros();
}

float readFrequency() {  // calculating frequency from counts and time
  static uint32_t lastFreqTime;
  noInterrupts();
  uint16_t freqCount= count_ISR;
  count_ISR=0;
  uint32_t freqTime= time_ISR-lastFreqTime;
  lastFreqTime= time_ISR;
  interrupts();
  return 1000000.0*freqCount/freqTime;
}

void setup() {
  Serial.begin(9600);
  attachInterrupt(0,freqCounter,RISING);
  // Comment out the following line if you put a signal on pin-2
  tone(2,1000); // generate a test tone on pin-2
}

void loop() {
  static uint32_t lastTime;
  if (millis()-lastTime>=1000) // Calculate and show result once every second
  {
    lastTime+=1000;
    float f= readFrequency();
    Serial.println(f);
  }
}

No additional hardware timer needed. Limits: Should be working without any problems up to frequencies out of the audio spectrum.

[quote author=Nick Gammon link=msg=2140373 date=1426408986] Have you checked the datasheet? The Mega will have different registers to Atmega328. [/quote]

Ah yes, INTF0 does not apply to Pin D2. Changed to INTF4 and it now works.

jurs: Maybe a problem with your signal: is signal ground and Arduino ground connected and at the same ground level?

What about switching to a different frequency counting code?

This one counts hardware interrupts (UNO example: pin-2, interrupt-0) using the micros() function for timing:

#define FREQPIN 2
#define FREQINT 0

// volative variables for interrupt handling volatile uint16_t count_ISR; volatile uint32_t time_ISR;

void freqCounter() // interrupt handling {   count_ISR++;   time_ISR=micros(); }

float readFrequency() {  // calculating frequency from counts and time   static uint32_t lastFreqTime;   noInterrupts();   uint16_t freqCount= count_ISR;   count_ISR=0;   uint32_t freqTime= time_ISR-lastFreqTime;   lastFreqTime= time_ISR;   interrupts();   return 1000000.0*freqCount/freqTime; }

void setup() {   Serial.begin(9600);   attachInterrupt(0,freqCounter,RISING);   // Comment out the following line if you put a signal on pin-2   tone(2,1000); // generate a test tone on pin-2 }

void loop() {   static uint32_t lastTime;   if (millis()-lastTime>=1000) // Calculate and show result once every second   {     lastTime+=1000;     float f= readFrequency();     Serial.println(f);   } }



No additional hardware timer needed.
Limits: Should be working without any problems up to frequencies out of the audio spectrum.

I'm not quite sure how to explain this, but the device that gives me my signal has glitches where sometimes I'll get a frequency twice as high as I should. I can see that with Nick's code, and I can use a running median to filter it out.

Any other code I use averages those spikes in and it's always higher than it should be. At least that's what I think is happening.