 # Sample square wave frequency with Arduino Due

Hello,

I need to sample square wave (image attached) with Arduino and calculate its frequency.

What are my options?

Thanks.

Don't sample it, as that would make your arduino busy for much of the time. Find which digital input pins do interrupts ( eg on my nano it is 2 or 3 ), and read the reference section on interrupts. Set up your interrupt handler function to do two things: increment a counter byte, and get presentmicros=micros(). Every now and then in your loop(), or when counter gets to enough, P=(presentmicros-oldmicros)/count; oldmicros=presentmicros; count=0; F=1000000/P;

That method of relying on interrupts is good to a couple of microseconds. If you need more precision and can afford a longer averaging time then using an int for the counter instead of a byte might do that. If you need better than microsecond precision or faster than 1 cycle response then arduino is not the right hardware so you should get some external box.

The Arduino Due board has powerful interrupt capabilities that allows you to attach an interrupt function on all available pins. You can directly specify the pin number in attachInterrupt().

I think all the digital pins will do. Avoid the ones that are "dedicated" (The ones marked on the board different from the bare number)

Regards.

I wrote the following code (including code to write to L CD screen):

``````/* LiquidCrystal - this library Demonstrates the use a 16x2 LCD display and works with all LCD displays that are compatible with the
Hitachi HD44780 driver. */
#include <LiquidCrystal.h>

// initialize the library with the numbers of the interface pins
LiquidCrystal lcd(12, 11, 5, 4, 3, 2);
const String suffix = " km/h"; // for the LCD display

// the pin that gets the F signal from the sensor
int FinPin = 6;

// general variables for the frequency sensing logic
unsigned long lastTime;
boolean lastValueLow;
const double kmhFactor = 19.36;
const double noFrequency = -1;

void setup()
{
// sets the pin as an input
pinMode(FinPin, INPUT);
// initialize general variables
lastTime = micros();
lastValueLow = true;

// set up the LCD's number of columns and rows:
lcd.begin(16, 2);
// Print a message to the LCD.
lcd.print("speed is:");
}

void loop()
{
int frequency = GetFrequency();
// if there is a new frequency (other than -1) writes it to the lcd screen
if(frequency > noFrequency)
{
// gets the speed in kmh from the frequency
int kmhSpeed = frequency/kmhFactor;
// wirtes the speed to the LCD
PrintSpeed(kmhSpeed);
}
}

// checks if there is a change in the input signal since last chack.
// if there is - calculates the period and the frequency at the same moment.
// if there isn't - there is no point in sampling the same signal again, so just returns '-1'.
int GetFrequency()
{
double frequency = noFrequency;
unsigned long nowTime;
double duration;
{
if(lastValueLow == true)
{
lastValueLow = false;
nowTime = micros();
duration = nowTime - lastTime;
lastTime = nowTime;
frequency = 1/(duration/1000000);
}
}
else
{
lastValueLow = true;
}
return frequency;
}

// dispaly the speed on the LCD
void PrintSpeed(int sp)
{
String prefix = "";
if(sp < 10)
{
prefix = "00";
}
else if(sp < 100)
{
prefix = "0";
}
lcd.setCursor(0,1);
lcd.print(prefix + sp);
}
``````

But it doesn’t measure the frequency accurately.

This is what you meant?

Thanks.

And of course it can't measure frequency accurately because you are polling without filtering on edges.

You need to wait for an edge to get its timestamp, not just observe the value is high now and was low last time getFrequency() was called, because that could be a while back (due to printing results or whatever else loop() does).

Interrupts much more accurate. They won't miss edges due to the sketch being busy either.

BTW

1/(duration/1000000) == 1000000/duration

Yes you’re right, I used interrupts and it measures frequencies from 500Hz pretty accurately.

This is the code;

``````/* LiquidCrystal - this library Demonstrates the use a 16x2 LCD display and works with all LCD displays that are compatible with the
Hitachi HD44780 driver. */
#include <LiquidCrystal.h>

// initialize the library with the numbers of the interface pins
LiquidCrystal lcd(12, 11, 5, 4, 3, 2);

// the pin that gets the frequency signal from the sensor
int FinPin = 6;

// period of pulse accumulation and serial output, milliseconds
#define MainPeriod 10
// will store last time of the cycle end
unsigned long previousMillis = 0;
// accumulates pulse width
volatile unsigned long duration=0;
volatile unsigned int pulsecount=0;
volatile unsigned long previousMicros=0;

// general variables for the frequency sensing logic and speed calculation
const double kmhFactor = 19.49;
const String suffix = " kmh";

void setup()
{
Serial.begin(9600);
attachInterrupt(FinPin, myinthandler, RISING);

// set up the LCD's number of columns and rows:
lcd.begin(16, 2);
// print a message to the LCD.
lcd.print("Speed is:");
}

void loop()
{
delay(100);
unsigned long currentMillis = millis();
if (currentMillis - previousMillis >= MainPeriod)
{
previousMillis = currentMillis;
// need to bufferize to avoid glitches
unsigned long _duration = duration;
unsigned long _pulsecount = pulsecount;
// clear counters
duration = 0;
pulsecount = 0;
// calculate the frequency
float Freq = 1e6 / float(_duration);
Freq *= _pulsecount;
// calculate and present the speed
double kmhSpeed = (double)Freq / kmhFactor;
PrintSpeed(kmhSpeed);

// output time and frequency data to RS232
Serial.print(currentMillis);
Serial.print(" "); // separator!
Serial.print(Freq);
Serial.print(" ");
Serial.print(_pulsecount);
Serial.print(" ");
Serial.println(_duration);

}
}

void myinthandler() // interrupt handler
{
unsigned long currentMicros = micros();
duration += currentMicros - previousMicros;
previousMicros = currentMicros;
pulsecount++;
}

// dispaly the speed on the LCD
void PrintSpeed(int sp)
{
String prefix = "";
if(sp < 10)
{
prefix = "00";
}
else if(sp < 100)
{
prefix = "0";
}
lcd.setCursor(0,1);
lcd.print(prefix + sp + " " + suffix);
}
``````

Now it measuring pretty slow (about half a second to get accurate result), is there a way that I can get it to measure faster on frequency change?

Thanks!