Frequency sweep, signal different from expected

Hello,

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.

Thanks for your help

alnilam

    delay(0.005);  //not obligatory

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.

you try to do tone(Pin, frequency, [color=red]1000/frequency[/color]);  //duration of 1 period

The signature of tone() is tone(pin, frequency, duration) where duration is unsigned long.

frequency will sweep from 1200 to 3500 so is always larger than 1000, so in integer math 1000/frequency is always 0.

the duration of the tone needs to be expressed in milliseconds

(and by the way the frequency of the tone in hertz represented as an unsigned int)

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
}

Thank you for your answers.

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.

I will try your solution.

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