Implementing frequency regulation via potentiometer

Hello :slight_smile:

I need a variable square wave frequency generator with 50% Duty Cycle to drive a large halfbridge. I have already found this code which does exactly what I need:

//dynamic frequency generator
//for timer2
#include <avr/io.h>
#include <avr/interrupt.h>
unsigned long frequency = 1000000 ;
void setup(){
  pinMode(9,1);
  pinMode(10,1);
  DFG(frequency);
  Serial.begin(57600);
  
}


void loop(){

}

void DFG(unsigned long tempfreq){
  cli();//disable interupts
  TCCR1A = 0;//registers for timer 1
  TCCR1B = 0;
  TCNT1=0;
  TCCR1A |= _BV(COM1A0) + _BV(COM1B0);
  TCCR1B |=_BV(WGM12); 
  TCCR1C = _BV(FOC1A);
   if(tempfreq > 122 && tempfreq < 1000001){
  OCR1A = (8000000/tempfreq)-1;//#TIMER COUNTS
  TCCR1B |= _BV(CS10);
  }
  else if(tempfreq <= 122 && tempfreq > 15){
    OCR1A = (1000000/tempfreq)-1;
    TCCR1B |= _BV(CS11);
  }
  else if(tempfreq <= 15 && tempfreq > 4){
    OCR1A = (125000/tempfreq)-1;
    TCCR1B |= _BV(CS10) + _BV(CS11);
  }
  
  //TIMSK1 = _BV(OCIE1A);//TIMER1 COMPARE INTERUPT
  sei();//enable interupts
}

I found the code in this thread on page 2: 50% duty cycle square wave from 1hz to 1Mhz, easiest way? - General Electronics - Arduino Forum

As you can see, the frequency can be simply changed by writing the value after the "unsigned long frequency =" line.

My question now is, how can i implement a potentiometer via the analog inputs to change the frequency inside of an interval, for example 10-50kHz? I already tried different analogWrite functions instead of the value, but non of them worked.

Can someone please tell me how i need to change the code to change the output frequency with a potentiometer?

Greetings
Phoenix

My question now is, how can i implement a potentiometer via the analog inputs to change the frequency inside of an interval, for example 10-50kHz? I already tried different analogWrite functions instead of the value, but non of them worked.

Map the analogRead() values from 0-1023 to 10000-50000.

Then pass the value to DFG(mapped value)

What have you tried?

Thank you for your reply :slight_smile:

I tried to change the code like that, but it does not work. I can only get 10kHz output but not variable:

//dynamic frequency generator
//for timer2
#include <avr/io.h>
#include <avr/interrupt.h>
unsigned long frequency = 40000 ;
void setup(){
  pinMode(9,1);
  pinMode(10,1);
  pinMode(A0, INPUT);
  DFG(map(analogRead(A0), 0, 1023, 10000, 50000));
  Serial.begin(57600);
  
}


void loop(){
 
int val = analogRead(0);
  val = map(val, 0, 1023, 10000, 50000);

Can you please tell me what i am doing wrong? I am an absolute beginner in the field of programming.

Can you please tell me what i am doing wrong? I am an absolute beginner in the field of programming.

Your code does not compile.

When you get a version that does, try to pass val into DFG()

DFG(val);

I now managed to get a code that does compile and the frequency changes with the potentiometer, but the output on the oscilloscope looks very glitchy and the signal is rapidly changing frequency:

//dynamic frequency generator
//for timer2
#include <avr/io.h>
#include <avr/interrupt.h>

void setup(){
  pinMode(9,1);
  pinMode(10,1);
  Serial.begin(57600);

  
}


void loop() {
  Serial.println(analogRead(A0));
  int val = analogRead(A0);
  val = map(val, 0, 1023, 10000, 50000);
  analogWrite(9, val);
    DFG(val);

}

void DFG(unsigned long tempfreq){
  cli();//disable interupts
  TCCR1A = 0;//registers for timer 1
  TCCR1B = 0;
  TCNT1=0;
  TCCR1A |= _BV(COM1A0) + _BV(COM1B0);
  TCCR1B |=_BV(WGM12); 
  TCCR1C = _BV(FOC1A);
   if(tempfreq > 122 && tempfreq < 1000001){
  OCR1A = (8000000/tempfreq)-1;//#TIMER COUNTS
  TCCR1B |= _BV(CS10);
  }
  else if(tempfreq <= 122 && tempfreq > 15){
    OCR1A = (1000000/tempfreq)-1;
    TCCR1B |= _BV(CS11);
  }
  else if(tempfreq <= 15 && tempfreq > 4){
    OCR1A = (125000/tempfreq)-1;
    TCCR1B |= _BV(CS10) + _BV(CS11);
  }
  
  //TIMSK1 = _BV(OCIE1A);//TIMER1 COMPARE INTERUPT
  sei();//enable interupts
}

If you only need 50% duty cycle and you know that won't change in the future then use the tone() function.

That function is useful to set the timer once. It stops the timer while setting all the registers. You can change OCR1A afterwards, without stopping the timer.

Thank you for your advice, but i already tried the tone function. I can not use it, because if i go over 25kHz, the output becomes very glitchy which could cause cross conduction at the halfbridge.

I would not use analogWrite(). The DFG timer function should output on pins 9 and 10.

You might want to pass val into the DFG function only if the value has changed by some amount. analogRead() can vary by a bit even if the potentiometer position does not change, and its not clear that you want to pass those variations into the function.

You will need to keep track of the last reading and compare it to the current reading and only update DFG() if the change is greater than some amount.

You might also choose to perform the analogRead() at fixed intervals using a millis timer rather than every pass through loop. The interrupts used by analogRead() can influence other outputs in the code.