Go Down

Topic: changing frequency on pwm in runtime . (Read 238 times) previous topic - next topic

PRB_tech

Apr 23, 2019, 06:29 pm Last Edit: Apr 24, 2019, 02:21 pm by PRB_tech
IS it possible to change the frequency of pwm (square wave) in runtime. I need two frequencies 197khz and 6khz  to be switched after particular delay. Please help me i tried all possible ways according to me.
Code: [Select]
#include <avr/io.h>
#include <util/delay.h>

int main(void)
{
  pinMode(3, OUTPUT); // output pin for OCR2B, this is Arduino pin number
  pinMode(5, OUTPUT); // output pin for OCR2B, this is Arduino pin number
  pinMode(6, OUTPUT); // output pin for OCR2B, this is Arduino pin number
  pinMode(9, OUTPUT); // output pin for OCR2B, this is Arduino pin number
  pinMode(10, OUTPUT); // output pin for OCR2B, this is Arduino pin number
  pinMode(11, OUTPUT); // output pin for OCR2B, this is Arduino pin number

  // In the next line of code, we:
  // 1. Set the compare output mode to clear OC2A and OC2B on compare match.
  //    To achieve this, we set bits COM2A1 and COM2B1 to high.
  // 2. Set the waveform generation mode to fast PWM (mode 3 in datasheet).
  //    To achieve this, we set bits WGM21 and WGM20 to high.
  //TCCR2A = _BV(COM2A1) | _BV(COM2B1) | _BV(WGM21) | _BV(WGM20);

  // In the next line of code, we:
  // 1. Set the waveform generation mode to fast PWM mode 7 --reset counter on
  //    OCR2A value instead of the default 255. To achieve this, we set bit
  //    WGM22 to high.
  // 2. Set the prescaler divisor to 1, so that our counter will be fed with
  //    the clock's full frequency (16MHz). To achieve this, we set CS20 to
  //    high (and keep CS21 and CS22 to low by not setting them).
 
 
 
// TCCR2B = _BV(WGM22) |_BV(CS21)| _BV(CS20);/*this working for 6khz frequency*/

//TCCR2B = _BV(WGM22) | _BV(CS20); //this work for generating 198khz frequency
 
 
  // OCR2A holds the top value of our counter, so it acts as a divisor to the
  // clock. When our counter reaches this, it resets. Counting starts from 0.
  // Thus 63 equals to 64 divs.
  OCR2A =80;//197.9khz
 
 
  // This is the duty cycle. Think of it as the last value of the counter our
  // output will remain high for. Can't be greater than OCR2A of course. A
  // value of 0 means a duty cycle of 1/64 in this case.
  OCR2B =40;

  // Just some code to change the duty cycle every 5 microseconds.
  while (1)
  {
 //   

  TCCR2A =0x00;
 
  TCCR2B = 0b00001011; //this gives me 6khz but only 6khz is shown on DSO

   TCCR2A = _BV(COM2A1) | _BV(COM2B1) | _BV(WGM21) | _BV(WGM20); //start

    delay(1000);




TCCR2A=0x00;

    TCCR2B =0b00001001; //this 197khz
 TCCR2A = _BV(COM2A1) | _BV(COM2B1) | _BV(WGM21) | _BV(WGM20); //start
    delay(1000);
   

   
   
  }
}



only 6khz is shown and frequency doesnot change to 197khz

MartinL

Hi PRB_tech,

The 8-bit timer2 doesn't have the capability to produce PWM output at your defined frequencies of 6kHz and 197kHz, to achieve this requires the 16-bit timer1.

It is however possible to use timer2 either in CTC mode or fast mode PWM mode with a toggled output. Using these modes it's possible to toggle the output at a fixed 50% duty-cycle and generate either 5.988kHz or 195.122kHz square waves.

PRB_tech

#2
Apr 24, 2019, 09:09 am Last Edit: Apr 24, 2019, 02:21 pm by PRB_tech
hello martin,
Thanks for your reply. I have used fast pwm mode and successfully generated two frequencies. I know i sound noob as i am ..  But please guide , is it possible to change the frequency on the go. i want to toggle between these two frequencies .
the above code is pasted generates desired two frequencies but i am able to achieve toggle pattern.


MartinL

Hi PRB_tech,

Here's some code that toggles a square wave between 5.988kHz and 195.122kHz on digital pin 11 (Arduino Uno):

Code: [Select]
// Toggle pin D11 square wave between 5.998kHz and 195.122kHz
void setup()
{
  pinMode(11, OUTPUT);                                // Set the timer2 OC2A to an output
  TCCR2A = _BV(COM2A0) | _BV(WGM21) | _BV(WGM20);     // Set up fast PWM with toggled output
  TCCR2B = _BV(WGM22) | _BV(CS21);                    // Set the prescaler to 8
  OCR2A = 166;                                        // Set the square wave output to 5.998kHz
  delay(1000);                                        // Wait 1 second
}

void loop()
{
  TCCR2B = _BV(WGM22) | _BV(CS20);      // Set the prescaler to 1
  OCR2A = 40;                           // Set the square wave output to 195.122kHz
  delay(1000);                          // Wait 1 second
  TCCR2B = _BV(WGM22) | _BV(CS21);      // Set the prescaler to 8
  OCR2A = 166;                          // Set the square wave output to 5.998kHz
  delay(1000);                          // Wait 1 second
}

However, due to the fact that timer2 is only a low resolution 8-bit timer and the disparity between the output frequencies is large, it's necessary to also change the timer prescaler during operation.

As the process of changing the prescaler (TCCR2B) and OCR2A registers is not atomic, plus the fact that the sketch's loop() is asynchronous with respect to the timer's cycle, there's the chance that you'll occasionally get a single glitch pulse during the switching process.

Due to the high output frequencies, it's not possible to overcome this by synchronizing the TCC2B and OCR2A registers to the timer cycle using an interrupt service routine (ISR). Running the ISR at 195kHz would simply overwhelm the CPU.

PRB_tech

Thanks for the code help. I also succeeded the toggling . I was not properly setting the  registers

i was  about to close the topic so i returned back

Go Up