Pages: [1] 2   Go Down
Author Topic: Creating Square Waves with Programmable Frequencies?  (Read 1562 times)
0 Members and 1 Guest are viewing this topic.
Offline Offline
Jr. Member
**
Karma: 0
Posts: 51
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

I'm looking to drive a speedometer, which requires a square wave form in the 5-50Hz range.  Anyone have suggestions (or good libraries) that would help adjust frequencies in this range on the fly?
Logged

Global Moderator
Dallas
Offline Offline
Shannon Member
*****
Karma: 200
Posts: 12782
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset


tone works to 32 Hz.

I believe @teckel claims support for low frequencies in toneAC...
http://arduino.cc/forum/index.php/topic,142097.0.html

At that low of a frequency, blink-without-delay is a good choice.
Logged

Southern California
Offline Offline
Full Member
***
Karma: 0
Posts: 108
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Write your own function like this:

Code:
void squareWave(int freq) {
  float x = 500/freq;
  digitalWrite(speedoPin, HIGH);
  delay(x);
  digitalWrite(speedoPin, LOW);
  delay(x);
}

This code will give you a square wave with 50% duty cycle
Logged

Offline Offline
Jr. Member
**
Karma: 0
Posts: 51
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

I'll be using attachInterrupt to monitor the speed, so I'm worried that will throw off the timing of a programming in my own squarewave using delay(xx)....    When the ISR is executing, the square wave will get thrown off, won't it?
Logged

Pittsburgh, PA, USA
Offline Offline
Faraday Member
**
Karma: 96
Posts: 4781
I learn a bit every time I visit the forum.
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

You will be changing a pin state to make a square wave edge? Why would it differ?

Logged

I find it harder to express logic in English than in Code.
Sometimes an example says more than many times as many words.

Offline Offline
Jr. Member
**
Karma: 0
Posts: 51
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

You will be changing a pin state to make a square wave edge? Why would it differ?



Sorry, I'm not following your point?   I will be using an attachInterrupt to monitor the original speedometer pulse, then looking to program a PWM output frequency to drive a speedometer guage....
Logged

North Queensland, Australia
Offline Offline
Edison Member
*
Karma: 66
Posts: 2116
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Switch around your logic then, give the pin change job to a timer interrupt at your known interval, then in your loop check the speedo.
The timer then has 'higher priority' over the speedo check.

Also if you continually run the timer, you can disable timer0 ( millis / delay... ) and count intervals of your own timer unless you need the finer resolution.

Using PWM will be easier I think, the lost microseconds can just be fixed every couple of seconds/minutes
Logged


Pittsburgh, PA, USA
Offline Offline
Faraday Member
**
Karma: 96
Posts: 4781
I learn a bit every time I visit the forum.
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

PWM runs at about 490 Hz and gives more or less time to HIGH than LOW. To change the PWM frequency as opposed to the PWM duty cycle you must change some clocks you might not want changed.

At the speeds you give, it'd be easier to toggle a pin by repeating time-out code and then maybe varying the time-out.

5 Hz is 200 ms, 50 Hz is 20 ms.
1 ms is 5 analog reads and processing. 1 ms is 500 digital reads with processing. 1 ms may be 100 to 1000 times through loop().
And you want to change a pin every 20 to 200 ms. Yeah, you can do that. You need 8 bytes RAM per timer though.

There are other ways. If you use intervals of ms in powers of 2 then you can mask the top bits of millis() off and rollover to 0 will come sooner. millis() & 0x0000007F rolls over every 128 ms.
Logged

I find it harder to express logic in English than in Code.
Sometimes an example says more than many times as many words.

Offline Offline
Jr. Member
**
Karma: 0
Posts: 51
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Ok, after a little testing, it seems the original frequency can range from about 5Hz to as fast as about 300Hz.  The idea is measure that frequency, adjust it a bit based on the setting of a trim pot, and create an adjusted frequency to output.  With that as the goal, please comment on this as an approach:

Code:
//measures timing of square wave on pin 20 (interrupt 3)
//adjustes that speed based on setting of pot on pin A0
//creates a square wave with adjusted frequency on pin 21

volatile int adjustedFreq;  
volatile int latestSpeed;
volatile int lastSpeed;
unsigned long startTime;
unsigned long now;
int speedoPin =21;


void setup() {
  pinMode(speedoPin, OUTPUT);
  attachInterrupt(3, iSr, CHANGE);}
 
 
void loop()
//creates square
{
  startTime=micros(); now=startTime;
  while (now-startTime<adjustedFreq) {speedoPin=HIGH; now=micros();}
  startTime=micros(); now=startTime;
  while (now-startTime<adjustedFreq) {speedoPin=LOW; now=micros();}
}
  
void iSr()
//calculates length of most recent pulse and adjusts it based on pot setting on A0
{
  latestSpeed=micros();
  adjustedFreq=(latestSpeed-lastSpeed)*(analogRead(A0)/512);  
  lastSpeed=latestSpeed;
}  


Moderator edit: [code] [/code] tags added.
« Last Edit: February 14, 2013, 02:14:01 am by Coding Badly » Logged

Pittsburgh, PA, USA
Offline Offline
Faraday Member
**
Karma: 96
Posts: 4781
I learn a bit every time I visit the forum.
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

If you want to change a pin, you have to write to the pin. At least that looks like what you might think you're coding with speedoPin=HIGH (funny, it started as 21) and LOW.

You might want to slow down a little and check your syntax against actual C/C++.
Logged

I find it harder to express logic in English than in Code.
Sometimes an example says more than many times as many words.

UK
Offline Offline
Shannon Member
****
Karma: 223
Posts: 12579
-
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

The tone library seems like the easiest way to produce a variable frequency square wave.
Logged

I only provide help via the forum - please do not contact me for private consultancy.

Pittsburgh, PA, USA
Offline Offline
Faraday Member
**
Karma: 96
Posts: 4781
I learn a bit every time I visit the forum.
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

To generate a 300 Hz wave you need to switch LOW to HIGH to LOW etc 600 times a second, once every 1,666 usec. 5 Hz, every 100,000 usec.

pseudocode:
loop()
{
  now = micros();
  if ( now - lastTime >= halfCycle )
  {
     // change the pin 
      lastTime=now;
  }
  // any other code like watching for serial available or sensor data goes here
}

Learn BlinkWithoutDelay to learn to switch things on and off at proper times together. You want to generate frequency and monitor it at the same time, the basics are in BlinkWithoutDelay.

But for your use, change millis() timing to micros(). Accuracy will be 4 usec but how much does that matter when the interval is well over 1000?

Logged

I find it harder to express logic in English than in Code.
Sometimes an example says more than many times as many words.

Offline Offline
Jr. Member
**
Karma: 0
Posts: 51
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Ok, using that logic from BlinkWithoutDelay, how about coding it like this?  Based on the math posted above, it would seem that the procesor is plenty fast enough to handle an interrupt 300 times per second while still executing the loop code, right? 

Code:

const int trimPot = A0;            //voltage divider to adjust speedometer output
const int speedPin =  21;          //pin for new pulse going to speedometer
int pulseState = LOW;             //newPulseState used to set the pulse
unsigned long currentMicros = 0;   //to measure new pulse   
unsigned long previousMicros= 0;   //to measure new pulse 
unsigned long currentSpeed = 0;    //to measure incoming pulse
unsigned long previousSpeed = 0;   //to measure incoming pulse
unsigned long interval = 0;        //calculated new pulse length

void setup()
{
pinMode(speedPin, OUTPUT);   
attachInterrupt (3, iSr, CHANGE);
}

void loop()
{
   currentMicros = micros();  //get the current micros count
   if(currentMicros - previousMicros > interval) //check to see if it's time to change
    {
    previousMicros = currentMicros;   //keep track of the last time pulse changed
    if (pulseState == LOW) pulseState = HIGH; else pulseState = LOW;
    digitalWrite(speedPin, pulseState);  //flip the state of the new sqaure wave
    }
}
 
void iSr()
{
  currentSpeed=micros();
  interval=(currentSpeed-previousSpeed)*(analogRead(trimPot)/512); 
  previousSpeed=currentSpeed;
}

Logged

Pittsburgh, PA, USA
Offline Offline
Faraday Member
**
Karma: 96
Posts: 4781
I learn a bit every time I visit the forum.
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

You need to make variables accessed by the ISR volatile, as in volatile unsigned long currentMicros.

A 104 usec analog read in an ISR? Oh no. Interrupts are off inside an ISR.

How about set a flag in the ISR that tells your regular code it's time to cook the interval?

if(currentMicros - previousMicros >= interval)

Logged

I find it harder to express logic in English than in Code.
Sometimes an example says more than many times as many words.

Offline Offline
Jr. Member
**
Karma: 0
Posts: 51
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Appreciate your help.  How about like this:

Code:
const int trimPot = A0;            //voltage divider to adjust speedometer output
const int speedPin = 21;          //pin for new speed pulse train
int pulseState = LOW;             // state used to set the pulse
volatile unsigned long currentMicros = 0;   //to measure new pulse   
volatile unsigned long previousMicros= 0;   //to mark prior pulse 
unsigned long currentSpeed = 0;    //to measure incoming pulse
unsigned long previousSpeed = 0;   //to mark prior pulse
unsigned long interval = 1;        //original pulse length
float adjustPercent = 1; //adjustment to original pulse length

void setup()
{
pinMode(speedPin, OUTPUT);   
pinMode(13, OUTPUT);
attachInterrupt (3, iSr, CHANGE);
}

void loop()
{
   currentMicros = micros();  //get the current micros count
   adjustPercent = (analogRead(trimPot)/512);
   interval=interval*adjustPercent;
   if(currentMicros - previousMicros > interval) //check to see if it's time to change
    {
    previousMicros = currentMicros;   //keep track of the last time pulse changed
    if (pulseState == LOW) pulseState = HIGH; else pulseState = LOW;
    digitalWrite(13, pulseState); //show pulse train on built in LED
    digitalWrite(speedPin, pulseState);  //flip the state of the new sqaure wave
    }
}
 
void iSr()
{
  currentSpeed=micros();
  interval=(currentSpeed-previousSpeed);  //how long since last change
  previousSpeed=currentSpeed;
}

Logged

Pages: [1] 2   Go Up
Jump to: