Offline
Newbie
Karma: 0
Posts: 41
|
 |
« on: February 12, 2013, 10:39:21 pm » |
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
Online
Shannon Member
Karma: 118
Posts: 10163
|
 |
« Reply #1 on: February 12, 2013, 10:43:36 pm » |
tone works to 32 Hz. I believe @teckel claims support for low frequencies in toneAC... http://arduino.cc/forum/index.php/topic,142097.0.htmlAt that low of a frequency, blink-without-delay is a good choice.
|
|
|
|
|
Logged
|
|
|
|
|
Southern California
Offline
Full Member
Karma: 0
Posts: 105
|
 |
« Reply #2 on: February 12, 2013, 11:32:24 pm » |
Write your own function like this: 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
Newbie
Karma: 0
Posts: 41
|
 |
« Reply #3 on: February 13, 2013, 12:24:02 am » |
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
Faraday Member
Karma: 30
Posts: 2927
I only know some basic electricity....
|
 |
« Reply #4 on: February 13, 2013, 12:33:35 am » |
You will be changing a pin state to make a square wave edge? Why would it differ?
|
|
|
|
|
Logged
|
Examples can be found at Learning in the Main Site and at the Playground
|
|
|
|
Offline
Newbie
Karma: 0
Posts: 41
|
 |
« Reply #5 on: February 13, 2013, 12:39:30 am » |
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
Online
Edison Member
Karma: 31
Posts: 1182
|
 |
« Reply #6 on: February 13, 2013, 01:07:20 am » |
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
Faraday Member
Karma: 30
Posts: 2927
I only know some basic electricity....
|
 |
« Reply #7 on: February 13, 2013, 01:23:04 am » |
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
|
Examples can be found at Learning in the Main Site and at the Playground
|
|
|
|
Offline
Newbie
Karma: 0
Posts: 41
|
 |
« Reply #8 on: February 14, 2013, 12:47:34 am » |
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: //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
Faraday Member
Karma: 30
Posts: 2927
I only know some basic electricity....
|
 |
« Reply #9 on: February 14, 2013, 04:48:16 am » |
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
|
Examples can be found at Learning in the Main Site and at the Playground
|
|
|
|
UK
Offline
Tesla Member
Karma: 89
Posts: 6388
-
|
 |
« Reply #10 on: February 14, 2013, 07:19:34 am » |
The tone library seems like the easiest way to produce a variable frequency square wave.
|
|
|
|
|
Logged
|
|
|
|
|
Pittsburgh, PA, USA
Offline
Faraday Member
Karma: 30
Posts: 2927
I only know some basic electricity....
|
 |
« Reply #11 on: February 14, 2013, 11:16:29 am » |
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
|
Examples can be found at Learning in the Main Site and at the Playground
|
|
|
|
Offline
Newbie
Karma: 0
Posts: 41
|
 |
« Reply #12 on: February 15, 2013, 02:17:42 pm » |
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? 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
Faraday Member
Karma: 30
Posts: 2927
I only know some basic electricity....
|
 |
« Reply #13 on: February 15, 2013, 03:08:38 pm » |
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
|
Examples can be found at Learning in the Main Site and at the Playground
|
|
|
|
Offline
Newbie
Karma: 0
Posts: 41
|
 |
« Reply #14 on: February 15, 2013, 06:32:16 pm » |
Appreciate your help. How about like this: 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
|
|
|
|
|
|