Frequency Counter Library

Hi guys,

This is my first post and actually this is my first project with Arduino. I am trying to understand how that library works and there are some things that I do not understand inside the ISR:

ISR(TIMER2_COMPA_vect) 
{
  // multiple 2ms = gate time = 100 ms
  if (FreqCounter::f_tics >= FreqCounter::f_period) 
  {         	
      // end of gate time, measurement ready
      // GateCalibration Value, set to zero error with reference frequency counter
      //  delayMicroseconds(FreqCounter::f_comp); // 0.01=1/ 0.1=12 / 1=120 sec 
      delayMicroseconds(FreqCounter::f_comp);
      TCCR1B = TCCR1B & ~7;   		    // Gate Off  / Counter T1 stopped 
      TIMSK2 &= ~(1<<OCIE2A);    	    // disable Timer2 Interrupt
      TIMSK0 |=(1<<TOIE0);     		    // enable Timer0 again // millis and delay
      FreqCounter::f_ready=1;               // set global flag for end count period
      
      // calculate now frequeny value
      FreqCounter::f_freq=0x10000 * FreqCounter::f_mlt;  // mult #overflows by 65636
      FreqCounter::f_freq += TCNT1;      	// add counter1 value
      FreqCounter::f_mlt=0;    
    }
    FreqCounter::f_tics++;            	  // count number of interrupt events
    if (TIFR1 & 1)                        // if Timer/Counter 1 overflow flag
    {          			
      FreqCounter::f_mlt++;               // count number of Counter1 overflows
      TIFR1 =(1<<TOV1);        		  // clear Timer/Counter 1 overflow flag
    }
    // PORTB = PORTB ^ 32;  		  // int activity test
}

1.- Timer2 interrupt is disabled but it is never enabled again. Is it automatically enabled after the ISR?
2.- Counter T1 is stopped… what makes it run again? The input signal?
3.- What does “delayMicroseconds(FreqCounter::f_comp);” do? I mean, why do we need that delay and why is f_comp set to 1? Shall I change that value depending on the application?
4.- Why is timer0 enabled every time that the frequency is measured?

Thanks so much!

dc42:
Here is an improved version of the frequency counter sketch. There were a couple of problems with the previous version:

// Frequency counter sketch, for measuring frequencies low enough to execute an interrupt for each cycle

// Connect the frequency source to the INT0 pin (digital pin 2 on an Arduino Uno)

volatile unsigned long firstPulseTime;
volatile unsigned long lastPulseTime;
volatile unsigned long numPulses;

void isr()
{
  unsigned long now = micros();
  if (numPulses == 1)
  {
    firstPulseTime = now;
  }
  else
  {
    lastPulseTime = now;
  }
  ++numPulses;
}

void setup()
{
  Serial.begin(19200);    // this is here so that we can print the result
  pinMode(3, OUTPUT);    // put a PWM signal on pin 3, then we can connect pin 3 to pin 2 to test the counter
  analogWrite(3, 128);
}

// Measure the frequency over the specified sample time in milliseconds, returning the frequency in Hz
float readFrequency(unsigned int sampleTime)
{
  numPulses = 0;                      // prime the system to start a new reading
  attachInterrupt(0, isr, RISING);    // enable the interrupt
  delay(sampleTime);
  detachInterrupt(0);
  return (numPulses < 3) ? 0 : (1000000.0 * (float)(numPulses - 2))/(float)(lastPulseTime - firstPulseTime);
}

void loop()
{
  float freq = readFrequency(1000);
  Serial.println(freq);
  delay(1000);
}

Sorry for dragging up an old thread but what is the value of the variable sampleTime in this sketch, is it 0 or is there a standard value for this? I’m slightly confused! I realize I could run the sketch and print the value to the serial port but I don’t have access to my Arduino for a week and it’s bugging me!

Hello! Is there any method to make the frequency be a "double"? Thanks!

double, or long/unsigned long, instead of a float?

DevilMoo: Hello! Is there any method to make the frequency be a "double"? Thanks!

On an AVR [UNO/MEGA] a double equals a float (32 bit IEEE754 float), other platforms like ARM based DUE might have 64bit double,

Hello guys,

I have a small question about the frequency counter sketch, from the example of DC42.
Here is “delay(sampleTime)” used.
I dont want to use delay.

Is it possible to make a timing with millis() or something instead of delay for this sketch?
Thanks in advance.

// Measure the frequency over the specified sample time in milliseconds, returning the frequency in Hz
float readFrequency(unsigned int sampleTime)
{
numPulses = 0; // prime the system to start a new reading
attachInterrupt(0, isr, RISING); // enable the interrupt
delay(sampleTime);
detachInterrupt(0);
return (numPulses < 3) ? 0 : (1000000.0 * (float)(numPulses - 2))/(float)(lastPulseTime - firstPulseTime);
}

Check the - blink without delay - example sketch. It shows how to write delay less

Thanks for your reply Rob, But when i tried it with the millis timer, I get a overflow of the reading?? It is maybe something simple, but i don't see the problem.

// Frequency counter sketch, for measuring frequencies low enough to execute an interrupt for each cycle
// Connect the frequency source to the INT0 pin (digital pin 2 on an Arduino Uno)

volatile unsigned long firstPulseTime;
volatile unsigned long lastPulseTime;
volatile unsigned long numPulses;
unsigned long currentMilis;
unsigned long lastMilis = 0;
float Hz;


void isr()
{
  unsigned long now = micros();
  if (numPulses == 1) {
    firstPulseTime = now;
  }
  else {
    lastPulseTime = now;
  }
  ++numPulses;
}


void setup()
{
  Serial.begin(19200);    // this is here so that we can print the result
}




void loop()
{
  currentMilis = millis();
  attachInterrupt(2, isr, RISING);    // enable the interrupt
  
  if (currentMilis - lastMilis > 1000) // instead of using delay(1000)
  {
    detachInterrupt(2);
    lastMilis = currentMilis;
    numPulses = 0;                      // prime the system to start a new reading
    Hz =  (1000000.0 * (float)(numPulses - 2)) / (float)(lastPulseTime - firstPulseTime);
  }
  Serial.println(Hz);


}

I found the problem! It was not an overflow, but divided with 0.

When my if statement was true, i first set the numPulses to 0, and then calculate my Hz. numPulses have to be set 0 after my calculation, so it is solved now.

The complete code for a accurate frequency measurement without using delay:

// Frequency counter sketch, for measuring frequencies low enough to execute an interrupt for each cycle
// Connect the frequency source to the INT0 pin (digital pin 2 on an Arduino Uno)

volatile unsigned long firstPulseTime;
volatile unsigned long lastPulseTime;
volatile unsigned long numPulses;
unsigned long currentMilis;
unsigned long lastMilis = 0;
float Hz;



void isr()
{
  unsigned long now = micros();
  if (numPulses == 1) {
    firstPulseTime = now;
  }
  else {
    lastPulseTime = now;
  }
  ++numPulses;
}


void setup()
{
  Serial.begin(19200);    // this is here so that we can print the result
attachInterrupt(2, isr, RISING);    // enable the interrupt
}




void loop()
{
currentMilis = millis();
 attachInterrupt(2, isr, RISING);    // enable the interrupt
  
  if (currentMilis - lastMilis > 1000) // instead of using delay(1000)
  {
   detachInterrupt(2);
   lastMilis = currentMilis;
   Hz =  (1000000.0 * (float)(numPulses - 2)) / (float)(lastPulseTime - firstPulseTime);
  
     numPulses = 0;  
    attachInterrupt(2, isr, RISING);    // enable the interrupt  
}
   Serial.println(Hz);

}

Hello guys !!!

Just a small error in the previous code writted by HBO88... You mixed up the number of the interruption and the number of PIN !!!! Be carefull

Correction: Please replace attachInterrupt(2, isr, RISING); by attachInterrupt(0, isr, RISING);

Otherwise congratulation for the rest!

Chris,

hi , i want ask some question. how about i want measure frequency from function generator by using arduino uno. can i connect direct from function generator to analog pin . thank you

full_throttle, you should start your own thread. You'll get a lot more answers that way.

And place this question in something like Project Guidance, or General Electronics.

@HBO88

I tried your modified version, but it's not working as expected on an UNO. A have an incoherent value and often "inf" or "ovf"

Any help ?

hi i wish to use freqcounter lib with pwm output, is it possible to use pwm when waiting to interrupt on timer2?

Anyone tried getting the FreqCount running a Zero? I'm looking to measure a input signal that varies from around 100kHz to 80kHz. Been trying to figure all the internal routing of the Zero to make this happen withou any success yet.

Can you please explain me why preamplifier scheme has such view and why there is such transistor and what this scheme do with signal (I've make it in Workbench and the signal from generator just shifts under zero level)

In the FrequencyCounter::start() function use 1000 instead of 100 if you want to get the exact frequency in Hz. because when you write 100 in start function, it takes it in ms in library running at the back end and will calculate frequency for 100ms which obviously will be 1/10th of original frequency in Hz. Similarly using 10 in the start function will give you the frequency 1/100th of the original frequency in Hz. Hopefully you understands it now.

I just wanted to thank the developer for this library. It was very useful on the project I created today. I hacked a digital anemometer which produced a pulsing signal, then used it to create a formula to translate the signal into windspeed in km/h.

I started to try to write something like this myself thank god I found this!

Hello All

I am trying to use dc42’s code to measure a low frequency speed from the flywheel of an internal combustion engine. The signal of interest is a 5V square wave in the order of 30 to 40Hz. However, using dc40’s code there are rogue high values (50 to 60Hz) appearing which is completely throwing my speed control system.

I am using a servo motor to control the throttle angle based on speed to keep the speed at a setpoint, using a PID library. Would this be throwing the ISR timer? Can the two work together? Where are these rogue values coming from? They appear randomly, with every 20 to 40 seconds of running:

#include <PID_v1.h> //include PID library in sketch
#include <Wire.h>
#include <Servo.h>

#define ECU2_RESET 8 //was 3
#define SERVO_PIN 9 //was 5
#define OVERSPEED_PIN 10 //overspeed to master
#define RELAY_PIN 11 //local overspeed relay

volatile unsigned long firstPulseTime;
volatile unsigned long lastPulseTime;
volatile unsigned long numPulses;

unsigned long over_speed_setpoint = 50; //define an over speed setpoint (i.e 3000rpm) here in correct units = 3000rpm
 double speed_setpoint = 41 ; // see engine calcs overview for this calc. or onenote To Do sheet. 48000=2500rpm, 50000=2400rpm
unsigned int sample_time = 100;
unsigned long num_iterations;




/////////////////////PID SETTINGS///////////////////////////////////////
double Kp = 1.0; //was o.008
double Ki = 0.7; // was 0.0022
double Kd=0.0;


////////////////////SERVO SETTINGS//////////////////////////////////////////////////
double throttle_setpoint;    // variable to store the desired throttle angle - WHAT SHOULD THIS BE INITIALISED AT?? 
int throttle_sig; //whats set to the servo = (90-throttle_setpoint) calculated by map() function
Servo myservo; //initalising servo as servo object

double freq_1=0; //variable for frequency readout


boolean engine_running = false; // this is false until the engine starter motor is engaged

PID myPID(&freq_1, &throttle_setpoint, &speed_setpoint, Kp, Ki, Kd, DIRECT); //PID setup using speed avg //REVERSE




void isr()
{
 unsigned long now = micros();
 if (numPulses == 1)
 {
   firstPulseTime = now;
 }
 else
 {
   lastPulseTime = now;
 }
 ++numPulses;
}

void setup()
{

Serial.begin(9600);
Serial.println("Setup Finished"); 
Serial.print("This  run we have :  ");
Serial.print("Kp = ");
Serial.print(Kp);
Serial.print(" Ki = ");
Serial.print(Ki);
Serial.print(" Kd = ");
Serial.print(Kd);
Serial.print("Sample time = ");
Serial.print(sample_time);
Serial.print("Speed setpoint = ");
Serial.println(speed_setpoint);

pinMode(21, INPUT);     //input for the HES signal
digitalWrite(21,HIGH); // added to prevent floating values???


pinMode(OVERSPEED_PIN, OUTPUT); //pin 4 will be HIGH when engine speed is too high and shutdown is required, LOW when ok to keep running.OVERSPEED PIN
pinMode(RELAY_PIN,OUTPUT); //pin 7 as local overspeed pin

digitalWrite(OVERSPEED_PIN,LOW); //initailly set this to low to say that there is no overspeed on system intialisation
digitalWrite(RELAY_PIN,HIGH); //initailly set this to low to say that there is no overspeed on system intialisation


myservo.attach(SERVO_PIN);//attaching the servo to PIN 9. 
myservo.write(8);// setting inital value of 85 to get it started on idle
myPID.SetMode(AUTOMATIC); //this sets the PID algoithm to automatic, i.e. it will compute every loop





}

// Measure the frequency over the specified sample time in milliseconds, returning the frequency in Hz
float readFrequency(unsigned int sampleTime)
{
 numPulses = 0;                      // prime the system to start a new reading
 attachInterrupt(2, isr, RISING);    // enable the interrupt
 delay(sampleTime);
 detachInterrupt(2);
 return (numPulses < 3) ? 0 : (1000000.0 * (float)(numPulses - 2))/(float)(lastPulseTime - firstPulseTime); // if the number of pulses is less than 3, then 0, otherwise - 


}

void loop()
{
  if(engine_running==true){
    num_iterations++;
  }

  
freq_1 = readFrequency(sample_time);


if(freq_1 > 5 && engine_running == false && freq_1 < 1000){ //if the engine is cranking then the engine_running variable is set to on so the PID controller is activated
  Serial.println("Engine is now running, ECU activated");
  engine_running = true;   
     }



  if(engine_running==true ){ //if closed loop control is need then the master controller will set pin 3 to HIGH AND the engine has started and the servo should be activated 
   myPID.Compute();//required for PID to work
   throttle_sig=(int)map(throttle_setpoint, 0, 255, 6, 87);// remapping the PID controller output (0-255), set by the PID setoutput function in the setup to 90 to 0 i.e. 90 degrees throttle_setpoint is actually 0 degrees throttle angle ---23.05.2016 was 0,255,0,90 changed to 87 as was conking on start
   myservo.write(throttle_sig); // this is writing the mapped PID output to the servo
//      Serial.print("The PID OUTPUT is: ");
   Serial.print(throttle_setpoint);
   Serial.print("\t");
//   Serial.print("The servo sig is: ");
   Serial.print(throttle_sig);
   Serial.print("\t");
//   Serial.print("The speed is is: ");
   Serial.print(freq_1);
   Serial.print("\t");
   Serial.print(freq_2);
   Serial.print("\t");
   Serial.println(millis());
  }

  if(freq_1 >= over_speed_setpoint){
  Serial.println(freq_1);
  digitalWrite(OVERSPEED_PIN,HIGH); //sending 5v signal to master to indicate overspeed
  digitalWrite(RELAY_PIN,LOW);      
  Serial.println("OVERSPEED");

  
  }
  else {
  digitalWrite(OVERSPEED_PIN,LOW);//telling master that there is no overspeed present
  digitalWrite(RELAY_PIN,HIGH); //keeping local overspeed relay switched on as there is no issues
  }

}

Hey,

I have to measure the netfrequenzy and i need a resolution of milli hertz 50.xxx Hz, is it possible to change the counter resolution that he will measure milli Hertz?