Pages: [1]   Go Down
Author Topic: Sample square wave frequency with Arduino Due  (Read 746 times)
0 Members and 1 Guest are viewing this topic.
Offline Offline
Newbie
*
Karma: 0
Posts: 7
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Hello,

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

What are my options?

Thanks.


* IMG_0044.jpg (144.63 KB, 816x612 - viewed 25 times.)
Logged

Offline Offline
Full Member
***
Karma: 7
Posts: 139
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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.
Logged

Logroño - Spain
Offline Offline
Sr. Member
****
Karma: 4
Posts: 318
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
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.
Logged

Offline Offline
Newbie
*
Karma: 0
Posts: 7
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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

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);
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(digitalRead(FinPin) == HIGH)
  {
    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.
Logged

0
Offline Offline
Shannon Member
****
Karma: 216
Posts: 12556
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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
Logged

[ I won't respond to messages, use the forum please ]

Offline Offline
Newbie
*
Karma: 0
Posts: 7
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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

This is the code;

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!
Logged

Pages: [1]   Go Up
Jump to: