Frequency sweep

Hi
I am trying to generate a excitation frequency sweep to excite a resonant coil and measure the resonant frequency.
First step: generate frequency sweep
I am trying to generate a frequency sweep (50% duty cycle square wave) with range 500Hz - 5000Hz.
For starters I need one period of each frequency component (maybe 1.5-2 periods of each freq component : will be determined later upon the tests to find desired sweep approach to get best response).
The sweep should step through the frequency in steps of 1Hz(sequential 500,501,502...4999,5000 Hz) or say 5Hz steps(a variable value) so the sweep will be 500,505,510,.....4990,4995,5000 Hz.

What would be the best way to do this?
I've looked at bit banging digital pins,using tone functions, using the PWM freq generation.
I think PWM is probably the way to go.
I've got a 16Mhz arduino nano , below is the code I've managed to scrounge from looking at several replies/similar posts in this wonderful forum:

void freq_sweep()
{
  int half_cycle count = 2 // for 1 freq period fOCnx=fclk/2*N*TOP
  //Apparently the TOP value needs to be TOP/2 because of dual slope particularity ??
  // so i calculated the increment for 1 frequency increments, the TOP deccreases by 64 (63.75)
TCCR1A=0;//reset the register
TCCR1B=0;//reset the register
TCNT1=0;
TCCR1A=0b01010011;//COM1A0,COM1B0 are 1, COM1A1, COM1B1 are 0
//also WGM11, WGM10 are 1
TCCR1B=0b00010001;//WGM13 is 1 and WGM12 is 0 with no prescaler CS10 is 1
for (int i= 32000;i>=3200;i=i-64) // TOP value needs to be decremented by 64 for each freq period increment of 1Hz
{
OCR1A=i;// compare value used to create a frequency sweep
}

// turn off the freq sweep when done
TCCR1A &= ~(1 << COM1A0);
}

  • Does this code make sense ? Is this the right approach? Alternative better approach?
  • Is this an acceptable way to create a frequency sweep? How is frequency sweep usually generated?
  • Is there a way to create a complimentary waveform of the above in another pin?

Once the sweep is sorted I will be working at doing the frequency measurement (will need to amplify the resonant freq response using op-amps (as it will be very weak ac signal in mV and do a pulse count of say 100 pulses and get the time taken as resultant freq). But its for later.

Beyond my detailed knowledge. You can inspect the figures used and look for what resolution You get. That 1 Hz step, with precision, looks hard to me.

Hi, @eaterbugs
Welcome to the forum.
Have you looked at the Tone Library and its examples?
https://www.arduino.cc/reference/en/language/functions/advanced-io/tone/

Tom.. :grinning: :+1: :coffee: :australia:

If you can find two potentiometers, I would use them for coarse and fine frequency.
frequency = 100 + analogRead(A0) * 500 + analogRead(A1);

That will give you a range from 100 Hz to 6238 Hz which will easily cover your desired range. The coarse knob will jump by 500's and the fine knob will step by 1 Hz.

You can use tone(pin, frequency); to play the tone and display on Serial Monitor ever couple of seconds.

Here is with 2 buttons


int buzzer = A5;

void setup()
{
  Serial.begin(19200);
  pinMode(buzzer, OUTPUT); //setting up buzzer pin as output
  pinMode(A3, INPUT_PULLUP);
  pinMode(A4, INPUT_PULLUP);
}
void loop()
{

  int i;
  if (digitalRead(A3) == 0) {
    i++ ;
  }
  if (digitalRead(A4) == 0) {
    i-- ;
  }

  //tone(buzzer,1000);  //freq 1000 Hz,
  tone(buzzer, i);

  Serial.println(i);
  /*
     lcd.setCursor(0,1);
     lcd.print(' ');
     lcd.print(i);
  */
}

I am trying to do a continuous sweep . each sweep will be completed within few hundred milliseconds. it will go through square waves between 500-5000Hz.
image
something like this
I thought of using PWM instead of tone library because I thought I can more efficiently control the sweep nature.
after the sweep is completed I need to wait around a hundred ms to measure the resonant freq.

Replace in my code buttons by blink led function and you will have sweep.

Here is a Frequency Sweep example I wrote a while back, modified slightly to better fit your requirements.

const uint32_t HighestFrequency = 5000;
const uint32_t LowestFrequency = 500;

const uint32_t LowestDivisor = (F_CPU / HighestFrequency) / 2;
const uint32_t HighestDivisor = (F_CPU / LowestFrequency) / 2;

const uint32_t TotalSteps = HighestDivisor - LowestDivisor;
const uint32_t MicrosecondsForSweep = 500000ul;

void setup()
{
  Serial.begin(115200);
  delay(200);

  // Stop Timer/Counter1
  TCCR1A = 0;  // Timer/Counter1 Control Register A
  TCCR1B = 0;  // Timer/Counter1 Control Register B
  TIMSK1 = 0;   // Timer/Counter1 Interrupt Mask Register

  // Set to Timer/Counter1 to Waveform Generation Mode 12: CTC, TOP in ICR1
  TCCR1B |= (1 << WGM13) | (1 << WGM12);

  // Enable OCR1A (Pin 9) Toggle on Compare match
  pinMode(9, OUTPUT);
  TCCR1A |= (1 << COM1A0);
  OCR1A = 8;
}

void loop()
{
  const uint32_t MAX_TOP = 0xFFFFUL;

  for (uint32_t divisor = HighestDivisor; divisor > LowestDivisor; divisor--)
  {
    // Clear the clock select bits
    TCCR1B &= ~((1 << CS12) | (1 << CS11) | (1 << CS10));

    if (divisor <= MAX_TOP)
    {
      ICR1 = divisor;
      TCCR1B |= (1 << CS10);
    }
    else if ((divisor / 8) <= MAX_TOP)
    {
      ICR1 = divisor / 8;
      TCCR1B |= (1 << CS11);
    }
    else if ((divisor / 64) <= MAX_TOP)
    {
      ICR1 = divisor / 64;
      TCCR1B |= (1 << CS11) | (1 << CS10);
    }
    else if ((divisor / 256) <= MAX_TOP)
    {
      ICR1 = divisor / 256;
      TCCR1B |= (1 << CS12);
    }
    else
    {
      ICR1 = divisor / 1024;
      TCCR1B |= (1 << CS12) | (1 << CS10);
    }

    delayMicroseconds(MicrosecondsForSweep / TotalSteps);  // Adjust to get the desired sweep time.
  }
}

Thank you for the code :slight_smile:
I did try uploading it and seeing the output with my logic analyzer (don't have an oscilloscope with me).


AS you can see there are 3 breaks in this sweep.

The next one has no visible breaks in the sweep.

I am assuming these intermittent breaks are due to the prescaler being changed. Or do you think its an issue caused by my logic analyzer ans should not appear??
Is there a way to get smoother transitions?
Also is it possible to get the same complimentary wave in another pin 11? If so how would we go about doing it?

I don't know where the breaks might be coming from.

You can have Pin 10 (OC1B) toggle at the same time and if you set the two pins to opposite values you should get the complimentary wave.

  // Enable OCR1A (Pin 9) Toggle on Compare match
  pinMode(9, OUTPUT);
  digitalWrite(9, LOW);
  TCCR1A |= (1 << COM1A0);
  OCR1A = 8;

  // Enable OCR1B (Pin 10) Toggle on Compare match
  pinMode(10, OUTPUT);
  digitalWrite(10, HIGH);  // Opposite phase
  TCCR1A |= (1 << COM1B0);
  OCR1B = 8;