I'm trying to generate a frequency sweep from 1200 to 3500 Hz using a Arduino Uno and the Tone instruction (5V square signal).
My code is as follows :
const int Pin = 11;
const double freqmax = 3500;
const double freqmin = 1200;
int i = 0;
double frequency;
const int number = 44; //number of periods
const double step = (freqmax-freqmin)/(number-1); //next frequency
void setup() {
pinMode(Pin, OUTPUT);
}
void loop() {
frequency = freqmin;
for (i=1; i<=number; i++){
tone(Pin, frequency, 1000/frequency); //duration of 1 period
delay(0.005); //not obligatory
frequency = frequency + step ; //next frequency
}
noTone(Pin);
delay(20); //20 ms before next sweep
}
The problem is that on an oscilloscope, I get the signal on the picture attached, with only 19 periods (and one that seems to be missing). The frequencies are more or less accurate (around 1300 for the first one, 3364 for the last two), whereas I get a very accurate 1198 Hz signal when I just do tone(Pin, 1200) with a constant frequency and no duration instruction, so the problem does not come from the oscilloscope.
I wonder if the problem comes from my code, from the time management (I haven't taken into account the time used for the instructions, but except from a small delay between the periods, I don't see why this would modify the signal), or from a technical limitation of the arduino uno that I may not be aware of.
And useless, since delay() takes an integral value.
You should test one frequency at a time, in setup(). It seems pointless to sweep through the frequencies almost as fast as possible, over and over, while trying to debug.
BTW - Apparently you want only 1 tick/period at a given frequency - so you don't need tone at all, you can just do something like this (not tested)
// JUST CONCEPT CODE
// ATTENTION - EVERYTHING HARDCODED FOR PIN 11
const double freqmax = 3500.0;
const double freqmin = 1200.0;
const int number = 44; //number of periods
void freqSweepPin11(double minF, double maxF, int nbSteps)
{
unsigned long halfONperiod, halfOFFperiod;
double stepF = (maxF - minF) / (nbSteps - 1.0); //next frequency
double frequency = minF;
for (int i = 1; i <= nbSteps; i++) {
// ideally you would fine tune the half periods to take into account the lost microseconds
// spent in excuting other functions or looping the for loop. considered negligible here
halfONperiod = (unsigned long) ((double) 500000.0 / frequency);
halfOFFperiod = halfONperiod;
// PORTB maps to Arduino digital pins 8 to 13
// turn pin 11 ON
PORTB |= B00001000; // much faster than digitalWrite(11, HIGH);
delayMicroseconds(halfONperiod);
// turn pin 11 OFF
PORTB &= B11110111; // much faster than digitalWrite(11, LOW);
delayMicroseconds(halfOFFperiod);
// go to next frequency
frequency = frequency + stepF;
}
}
void setup() {
pinMode(11, OUTPUT);
}
void loop() {
freqSweepPin11(freqmin, freqmax, number);
delay(50); // before next sweep
}
I already tried the combination digitalwrite High; delay; digitalwrite Low, but I had a constant frequency. After reading your answers, I think I had the same problem of using the wrong type in delay.
digitalwrite High; delay; digitalwrite Low will be too slow to be precise. the constant frequency you were seeing was that your delay was largely impacted by the cost of the digitalWrite (or in my approach you would also need to take those into account in the halfONPeriod/halfOFFPeriod too), hence why I used PORT