0

I have been trying to generate a PWM wave of 38 Khz (13 microsec= ON period , 13 microsec= OFF period). Currently I am using Timer 0 to generate the PWM wave , however i am confused as to how to prescale the 16Mhz input clock frequency so that i can get 38 Khz from it .

The code is as follows -

#include<avr/io.h>
#include<util/delay.h>

uint8_t  dutycycle;
void pwm_init()
{
        // Configure PB.7 as output pin
        DDRB |= (1<<PB7);
        DDRG |= (1<<PG5);

        // Clear o/p on Match and Fast PWM Mode
        TCCR0A |=(1<<COM0A1) | (1<<WGM01) | (1<<WGM00) | (1<<COM0B1);// COM0A1/0b1 are the ouput ports , and WGM01 and WGM02 are used for fast PWM mode
        TCCR0B |=(1<<WGM02) | (1<<CS01);//WGM02 is used to set the fast pwm mode with max value stored in OCRA. CS01 is used to prescale the clock frequency to 16Mhz % 8 . 
        OCR0A = 52;

}
void dutycycleA(uint8_t dutycycle)
{
    OCR0B = (dutycycle * 52)/100 ;
}
int main()
{
        pwm_init();

        while(1)
        {
            dutycycle=50;
            dutycycleA(dutycycle);
            _delay_ms(1000);
        }
}

So , the code is supposed to keep checking the value of OCR0B with the timer value and if it is less than that , then the output is logic HIGH , and if it is same as the timer value , then it'll be logic LOW . the duty cycle is given as 50 so that a pulse waveform of 50% duty cycle is reached .

However I am unable to obtain 38Khz output .

Please help me figure out where I have gone wrong and also suggest corrections to my approach , if wrong .

Thank you .

PS - I am using Eclipse Neon platform.

1

It appears that you didn't set any Clock Select bits. Per datasheet,

clkT0 can be generated from an external or internal clock source, selected by the Clock Select bits (CS02:0). When no clock source is selected (CS02:0 = 0) the timer is stopped.

That could account for getting no output at all, if that's what's happening. [I had overlooked (1<<CS01) in your TCCR0B setup.]

You have the COM0A1 and COM0B1 bits set, and you apparently assume COM0A0 and COM0B0 are clear. (Note that in the code below, I say TCCR1A = ... and TCCR1B = ... instead of using |=, because |= is not reasonable to use for initial setup). In the #7 fast PWM mode that you selected via WGM bits, this would lead to OC0A and OC0B clearing on their respective Compare Matches and setting at BOTTOM, which looks more or less ok for the OC0B pin, if you have OCR0B set to about half of OCR0A. If the OC0A pin clears, however, it probably gets set again a cycle later, if I understand correctly. (However, I'd want to actually test the code, with oscilloscope connected, to say for sure.)

Note that the OCR0A count of 52 and prescaler of 8 will set up a counting frequency of 1600000/(52+1)/8 Hz, ie about 37735.85 Hz, while a count of 51 would give 38461.54 Hz. If accuracy matters, you can use simpler and more accurate code like the following. It uses a prescale of 1 and count of 209, to generate a toggle frequency of 1600000/(219+1) Hz, ie about 76190.48 Hz, and a square wave frequency of about 38095.24 Hz. Note, this code is written for timer 1; it should be straightforward to adapt to timer 0, because 209 is less than 255, the upper limit of TCNT0.

// Generate a constant frequency on PB1, using Fast PWM - jw May 2017
enum { OC1Apin = 9 };    // Arduino digital pin #9 = OC1A = PB1

void setup() {  // Set up timer 1
  noInterrupts();               // Turn off interrupts during setting
  TIMSK1 = 0;                   // Turn off timer 1 interrupts
  // Set timer 1 to Fast Pwm mode with OC1A (dig pin 9) toggling
  TCNT1 = 0;                    // Zero the counter
  TCCR1A = 1<<WGM11 | 1<<WGM10  // for Fast Pwm with TOP from OCR1A
    | 1<<COM1A0;                // To toggle OC1A PB1 D9 on Compare Match
  TCCR1B = 1<<WGM13 | 1<<WGM12// for Fast Pwm with TOP from OCR1A
    | 1;                      // Start timer 1 with prescale 1
  // Set OCR1A *after* setting up TCCR1A & TCCR1B
  OCR1A = 209;                // Gen. about 38095.24 Hz
  pinMode(OC1Apin, OUTPUT);
  interrupts();
}    
void loop() {}

Edit 1: Here are some of the differences between this code and that shown in the question, aside from using timer 1 instead of 0:

• Initialization of TCCRxA and TCCRxB registers here is done using Register = expression forms, rather than Register |= expression forms. Just OR-ing your bits in may be a problem if some other CS bits are set. For example, in the Arduino environment, initialization for millis() will set a prescale factor of 64, by setting both of the CS00 and CS01 bits, so that when you say TCCR0B |=(1<<WGM02) | (1<<CS01); you end up with both of the CS00 and CS01 bits still set, and a prescale of 64 instead of 8.

TCCR1B = ... | 1; // Start timer 1 with prescale 1 sets the CS00 bit and leaves the CS01 and CS02 bits clear, selecting prescale 1, while TCCR0B |= ... | (1<<CS01); selects a prescale factor greater than 1.

• The code shown here generates a square wave because it toggles a pin at a constant rate. The code in the question sets OCR0B to 26, which will give 27 clock-cycles to half of each output cycle, and 26 clock-cycles to the other half.

• The code shown here generates a frequency of about 38095 Hz, which is about 0.25% high, vs a frequency 0.7% low for the code in the question.


Edit 2: One way to get a fixed number of pulses is to set up an interrupt handler that is invoked once per toggle (or, once per cycle if appropriate). It can decrement a counter, and shut off an output pin when the counter gets to zero. Or could set the output pin low and the CS bits to 0 to stop the counter. For example, if timer 1 is in use:

volatile byte TogCounter;
...
// In setup():
TIMSK1 = 1<<TOIE1;  // Enable timer 1 overflow interrupt at TOP
...
// Define a timer 1 ISR
ISR(TIMER1_OVF_vect) {      // ISR for timer 1 overflows
  if (TogCounter) {         // Don't decrease if not counting
    --TogCounter;
    if (!TogCounter) {
      pinMode (TogPin, INPUT); // Disable output when count runs out
      // If desired, set a volatile wavetrain-is-done flag here
    }
  }
}

To output a specified number of cycles n, set the mode of TogPin to OUTPUT and set TogCounter to 2·n. Note, waveforms will be of more-consistent length if you also clear the counter register just before starting any pulse train.

To generate a sequence like m1 cycles on, n1 off, m2 cycles on, n2 off, etc, you could set up an array containing m1, n1, m2, n2, etc; then each time TogCounter runs down, toggle TogPin from INPUT to OUTPUT or vice versa, and load the next array entry into TogCounter.

  • I have analysed your code and to me it seems that , both are identical . Just that you have used timer 1 with no prescaler and therefore OCR1A=209 , whereas I have used Timer 0 with Prescaler of 8 and therfore OCR0A = 51 (corrected) . Have i missed any changes that you have added ? – Boudhayan Dev May 19 '17 at 6:32
  • another thing that i have noticed is , you are setting the timer frquency to 2*38 Khz , so that on match you will just toggle the level ( using COM1A0) . I have done it differently by settng timer freq =38 Khz and instead of toggling , I am just setting it to 0 once half of that freq i.e 38 Khz is reached . thus , i will get On and Off freq = 38*2 Khz. thereby achieving what you have tried to do . Only difference is I am using OCR0B to output the pwm , as i did not want to change the value of OCR0A from the setup() part . I don't see anything that I have done differently than yours . – Boudhayan Dev May 19 '17 at 6:39
  • @BoudhayanDev, see edit 1 – James Waldby - jwpat7 May 19 '17 at 7:09
  • Okay so besides the Or operation mentioned in first point , what i need to change is - select Toggle mode using CS10/11/12 . I'll give it a try. Also , i was thinking , I'll use ICR1 register to hold the Top value instead of OCR1A . so in that way OCR1A will be free to output the pwm wave . Is it fine if i do it or should i continue with OCR1A to hold the Top value and OCR1B to output the pwm wave ? – Boudhayan Dev May 19 '17 at 7:32
  • I'm not sure what you mean by “output the PWM wave”; I thought you were trying to output a 38KHz square wave, which is what the code I posted should do. With that code, one sets up the square wave frequency by the value in OCR1A, after which it rolls out a square wave on OC1A with no further program interaction needed. Are you instead trying to put varying PWM modulation onto a 38KHz carrier, rather than genning a square wave? – James Waldby - jwpat7 May 19 '17 at 8:46
0

A few ways to get there. 38khz has a period of 421 ticks on a 16mhz Mcu. So

Approach 1. Use the pwm module to Generate it.

Approach 2. Use the timer overflow interrupt to generate it. Simply flip the output pin in the isr.

Approach 3. Use the compare interrupt to generate it. In the isr increment the compare match register by 120 and then flip the output pin.

All of them are fully supported by your chip. The first approach once set consumes no CPU processing power.

edit: this link, https://dannyelectronics.wordpress.com/2017/02/08/driving-multiple-servos-with-avr-timer2/, will hopefully provide some inspiration to you. It is for a different application but the gist of the problem is the same.

here is the output, adopting it to generate a 38Khz (37,679Hz to be exact) pulse train on a user specified pin using timer2 compare ch b interrupt. enter image description here

i think you will benefit a lot in getting the code going on your own but if you really struggle, I can provide you the code as well. but you get a lot more if you try to get it going on your own.

  • Can you elaborate your Approach 2 and 3 wit certain code examples ? – Boudhayan Dev May 19 '17 at 13:12

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.