1

I would like to measure frequency with an Arduino 2560 board. The approach is to use a timer interrupt set to one second and pin interrupt to count interrupts in one second.

The problem I have is that I get the wrong frequency. On my frequency generator I set up a frequency of 1000 Hz in my program I get 1006 Hz instead of 1000 Hz.

What am I doing wrong?

Here is my program. In the stup method I have declared a timer. When the signal comes on pin 2, the counter is increased by 1. After one second passed the flag bData is raised. If the flag is true then I print out a frequency on serial port.

#include <SPI.h>
#include <Ethernet.h>
#include <Math.h>

byte mac[] = {  0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED};
IPAddress ip(192, 168, 1, 177);
EthernetServer server(80);
const byte interruptPinTemp = 2;
const byte interruptPinSlanost = 3;
//konstante za SBE-3
const double g = 4.85442486e-003;
const double h = 6.77300393e-004;
const double i = 2.65502731e-005;
const double j = 2.06782794e-006;
const double f = 1000.0;
volatile int cnt = 0;
volatile int cnt1 = 0;
volatile double slanost = 0;
volatile double slanost1 = 0;
int timer1_counter;
boolean bData = false;
volatile int dvesek = 1;

void beriTemperatura() {
  cnt++;
}

void beriSlanost() {
  slanost++;
}

double calcTemp(double frekvenca) {
  double logRes = log(f / frekvenca);
  return 1.0 / (g + (h * logRes) + (i * pow(logRes, 2.0)) + (j * pow(logRes, 3.0))) - 273.15;
}

void setup() {
  Serial.begin(9600);       // inicializacija serijskega porta
  noInterrupts();           // disable all interrupts
  // start the Ethernet connection and the server:
  Ethernet.begin(mac, ip);
  server.begin();
  Serial.print("server is at ");
  Serial.println(Ethernet.localIP());
  pinMode(interruptPinTemp, INPUT_PULLUP);
  pinMode(interruptPinSlanost, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(interruptPinTemp), beriTemperatura, CHANGE);
  attachInterrupt(digitalPinToInterrupt(interruptPinSlanost), beriSlanost, CHANGE);
  TCCR1A = 0;
  TCCR1B = 0;
  timer1_counter = 34286;   // preload timer 65536-16MHz/256/2Hz
  TCNT1 = timer1_counter;   // preload timer
  TCCR1B |= (1 << CS12);    // 256 prescaler
  TIMSK1 |= (1 << TOIE1);   // enable timer overflow interrupt
  interrupts();
}

//Timer interrupt na 1 sekundo
ISR(TIMER1_OVF_vect) {
  // interrupt service routine
  TCNT1 = timer1_counter;   // preload timer
  cnt1 = cnt ;               // prepisemo vrednost v cnt1
  bData = true;             // postavimo flag
  cnt = 0;  // resetiramo counter
}

void loop() {
  // če je postavljen flag, da so podatki jih izpišemo ali naredimo karkoli drugega
  if (bData == true) {
    bData = false;
    Serial.println(calcTemp(cnt1), 3);
    Serial.println(cnt1);
  }
}

Here is the frequency from my generator:

enter image description here

And here is a frequency measured with my board:

enter image description here

What is wrong that I get 1006 Hz instead of 1000 Hz?

If I raise the frequency for example to 5 kHz then I get 5032 Hz in my program (32 Hz to0 much).

Thank you for any advice.

2 Answers 2

1

The Arduino Mega 2560 is clocked off a ceramic resonator, which should typically have about 0.5% frequency tolerance. Your readings are off by 0.6%, which is slightly more than the typical resonator tolerance. I would them assume there is something wrong with your code, check dannyf's answer.

Now, if you want significantly better accuracy, I would argue than relying on the Arduino's internal clock is not a good idea. Even if your code is perfect you cannot expect good accuracy. You could have better results if you calibrate that clock instead of just assuming it runs at 16 MHz. But even then, ceramic resonators are notorious for having a very significant frequency drift. Or you may get an Arduino clocked off a quartz crystal instead, as these are far more stable.

1

TCNT1 = timer1_counter; // preload timer

When you load the offset (timer1_counter), there are some values in the timer1 prescaler (and potentially TCNT1 if the prescaler isn't sufficient large, or the input pulse train is fast). By assigning a value to TCNT1, you would have written over the values in the prescaler / TCNT1 -> read the datasheet on that.

The solution?

  1. Optimal: use a timer with auto-reload function. a timer with output compare can be adopted here as well.

  2. Increment the counter: TCNT1 += timer1_counter; // preload timer, if timer has a 1:1 prescaler.

Or to use the timer to count the pulse and another to provide the time base.

3
  • Hi DannyF.. do you have some example? I'am new to microcontrollers (will check on google for auto-reload)
    – Ferguson
    Jan 29, 2017 at 21:51
  • i'm happy to report that your code got accurate results when I drove it to 10Khz. the issue is likely the oscillator -> if they had indeed used a ceramic oscillator, they made a mistake. with a few cents more, they could have used a crystal instead.
    – dannyf
    Feb 1, 2017 at 20:19
  • with that said, it is generally desirable to NOT write to the counter. the optimal solution is to use auto reloading; if not available, try to increment it -> that typically can get the result down to two - three ticks.
    – dannyf
    Feb 1, 2017 at 20:20

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.