Time between interrupts (without micros/millis)

Hi!

I want to measure the frequency of a signal. The way I have tried is to measure the time between two pulses (interrupt) by using the micros function.

It is working fine but the micros function has a accuracy of 4us. It would be nice with a 1us accuracy. Not a big deal if the frequency is low but at higher frequency it does matter.

I have thought about creating a new micros function by using a timer that trigger a interrupt every 1us and then increment a variable inside the ISR function but it does not feel like a good solution.

I do not want to use a premade library like freqmeasure because I plan of using the code with a Atmega8. The signal I want to measure is 1-100000hz. I do not mind if the solution involve "AVR C".

/Olof

An interrupt every microsecond (16 instruction cycles) wouldn't give a great deal of time to do anything else.

Yes, I came to the same insight when I tried that :)

Must exist a better solution. By the way maybe this thread should be moved to "Programming Questions"?

for low freqs you can use micros for high an external counter is better a simple cmos is very fast, and reading is possible with a atmega

olof_n: Hi!

I want to measure the frequency of a signal. The way I have tried is to measure the time between two pulses (interrupt) by using the micros function.

For better accuracy, average the pulse length over multiple pulses.

Frequency is measured in Hertz (Hz) not in seconds (or µs). Frequency is the reciprocal of period, so the relationship is simple, but the usual approach to measuring frequency is to count cycles over some relatively long period of time (e.g. one second), not to measure the period of individual cycles. So which is it that you're interested in?

Thanks for all answers.

I am building a function generator with a XR2206 IC and want to display the frequency on a 7-seg display.

I have tried to count pulses over time but that method is not the best solution for low frequency. One way is to count times between pulses for low frequency and count over time for higher frequency.

I will google the CMOS counter IC, maybe that is a better way.

Is it not possible to use a hardware timer? Just an idea: 1. First pulse: Clear timer registry, start timer that increment every 16 clock ticks. 2. Second pulse: Stop timer, read registry and get the number of clock ticks between pulses

I am building a function generator with a XR2206 IC and want to display the frequency on a 7-seg display.

Don't you tell the function generator what frequency you want? Why do you have to measure it?

The XR2206 is a analog IC, you adjust the frequency with pots and caps. The user of my function generator should see what frequency it is outputing without a oscilloskop.

olof_n: I have tried to count pulses over time but that method is not the best solution for low frequency. One way is to count times between pulses for low frequency and count over time for higher frequency.

Try the code I gave at http://forum.arduino.cc/index.php?topic=64219.msg1433217#msg1433217. You define the gate time you want to measure over, then it measures the time taken by as many full cycles as it sees during that gate time.

olof_n: I am building a function generator with a XR2206 IC and want to display the frequency on a 7-seg display. I have tried to count pulses over time but that method is not the best solution for low frequency. One way is to count times between pulses for low frequency and count over time for higher frequency.

OK I'm with you now. Maybe using both techniques is the way to go, with some logic that decides which to use. Have you looked at the input capture unit (e.g. Timer1 on ATmega328P) for the low-frequency measurement?

But I don't see a need for microsecond resolution unless the frequency needs to be displayed to more than three or four sig figs.

One pitfall I see is to keep the input capture ISR very skinny or even to turn it off for higher frequencies. If input capture is generating interrupts at 100kHz, I'd be concerned about the CPU capacity that is going to suck up, it might not leave much time for other processing. If the function generator has a range switch of any kind, I might use that as an input to determine which measurement mode to use.

The way I measure today:

Start ISP
Measure 2 pulses to get the frequency and then stops the ISP.

Every 0,5sec I start the measurment again (if the last is finished).
It is non blocking and do not consume much CPU.

Later tonight I will test to use TIMER1 instead of micros.

Pseudo code I should try:

//Pulse one
//Prescaler to 8
TCCR1B |= (1 << CS11);
TCNT1 = 0; // Reset timer

//Second pulse
//Read value from TCNT1 registry
unsigned int iTimerValue = TCNT1;
microsValue=iTimerValue / 2
//Stops timer
TCCR1B = 0;

Should give great accuracy I think.
If I want to measure low frequency I could use the overflow interrupt and add that to microsValue.

I tried the following code in my interrupt function that is triggered by each pulse.

void pulse()
{
  if (state==0)
  {
     state=1;
    //Set Initial Timer value
    TCNT1=0;
     //Prescaler to 8  
    TCCR1B |= (1 << CS11);

  }
  else
  {
    iTimerValue = TCNT1;
    //Stop timer
    TCCR1B = 0;
    state=0;
    //Indicate that we are done
   //Interrupts should be disabled
    bCounting=false;
  }
}

It do not work :(, I do not get any values from TCNT1.
If I remove “TCNT1=0;” I get values but they are obviously wrong beacuse the registry do not start from 0.

Have you tried the code I linked to in reply #9? It is very easy to use if you already have the input signal generating an interrupt, does not require you to take over any timers, and is accurate at both high and low frequencies.

dc42, thanks for your link.

I don't know if I understand your code right, but it contains 2 things that I don't like:

"delay(sampleTime);" would block the execution of code? My program will do more things than measuring frequency.

Also if I am measuring a 100khz signal the ISP would be executed 100000 times and waste a lot of CPU? (does not matter if the code is blocked by delay).

It is pros and cons between measuring pulses over time and time between pulses. My approach is non blocking and do not use much CPU power. The cons is that it is inaccurate because of the micros function.

olof_n:
dc42, thanks for your link.

I don’t know if I understand your code right, but it contains 2 things that I don’t like:

“delay(sampleTime);” would block the execution of code? My program will do more things than measuring frequency.

You can easily change it to be non-blocking. Just replace function readFrequency by 2 functions:

void startCounter()
{
  numPulses = 0;                      // prime the system to start a new reading
  attachInterrupt(0, isr, RISING);    // enable the interrupt
}

float getFrequency()
{
  detachInterrupt(0);
  return (numPulses < 3) ? 0 : (1000000.0 * (float)(numPulses - 2))/(float)(lastPulseTime - firstPulseTime);
}

Then call startCounter to start the frequency measurement, and call getFrequency a suitable amount of time later in blink-without-delay style to stop the measurement and return the frequency.

olof_n:
Also if I am measuring a 100khz signal the ISP would be executed 100000 times and waste a lot of CPU?

If you use a gate time of 1 second, then yes. But to measure 100kHz, you can use a much shorter gate time. For example, if you measure over 1ms then you are measuring 100 cycles, and the error due to the inaccuracy of the micros function is reduced by a factor of 100.

btw another cause of inaccuracy with both your code and mine is that the servicing of the interrupt from the first or last pulse may be delayed because of another interrupt already executing (e.g. the timer 0 interrupt or the hardware serial interrupt). With my code, once again the error will be reduced by a factor of the number of cycles counted.

dc42, yes you have right. Maybe I have to measure over some time if the frequency is high.

Still I am intrested in knowing why my code with timer1 does not work.

olof_n: Still I am intrested in knowing why my code with timer1 does not work.

You should completely define the values of TCCR1A and TCCR1B in your code, to fully define what WGM mode you want and what prescaler you want, rather than just setting one bit in TCCR1B. I haven't checked, but I suspect the Arduino core may be setting up timer 1 for PWM.

Yes! That was the problem :slight_smile:
Thanks everyone for all the help, especially dc42!

If I add TCCR1A =0; everything works :slight_smile:
The PWM stuff truncated TCCR1A to 8bit.

Simple test code if someone wants to test without using micros. I know the code is not perfect but I will add all suggestions later.

unsigned long ulCounter=0;
unsigned long ulMillis=0;
volatile unsigned long ulTimerValue;
volatile float freq;
volatile byte bCounting=false;
volatile byte state=0;
volatile int iTimerOverflow=0;

void setup()
{
  Serial.begin(115200);
    
  attachInterrupt(0, pulse, RISING);
  //TCNT1 is truncated to 8bit by the "Arduino core"
    // initialize Timer1
    TCCR1A = 0;        // set entire TCCR1A register to 0
    TCCR1B = 0;
 
    // enable Timer1 overflow interrupt:
    TIMSK1 = (1 << TOIE1);
}

void loop()
{
  ulMillis=millis();
  if ((ulMillis-ulCounter>=500) && (bCounting==false))
  {
    freq = 1000000.0 / (ulTimerValue / 2.0);
    Serial.println(freq);
    ulCounter=ulMillis;
    bCounting=true;
    sei();
  }
}

void pulse()
{
  if (state==0)
  {
    iTimerOverflow=0;
     state=1;
    //Prescaler to 8  
    TCCR1B |= (1 << CS11);
    //Set Initial Timer value
    TCNT1=0;
  }
  else
  {
    ulTimerValue = TCNT1 + (iTimerOverflow*65535);
    TCCR1B = 0;
    state=0;
    bCounting=false;
    cli();
  }
}

ISR (TIMER1_OVF_vect)  // timer1 overflow interrupt
{
    iTimerOverflow++;
}