Problem when changing pwm duty cycle Timer2

Hello everyone

Can you tell me what is wrong with this code?
I thought I could change the pin11 duty cycle trough OCR2B.. However, I change the number and it doesn't happen anything. This code is generating approximately 20KHz and 50% duty cycle.
Here's the code:

TCCR2A = _BV(COM2A0) | _BV(WGM21) | _BV(WGM20);  
OCR2A = 50;
OCR2B = 12; 
TCCR2B = TCCR2B & 0b00111000 | 0x2;

Thanks in advanced

Since you aren't using the macros to set the bits in TCCR2B, what bits are you setting in that register?

Why are you setting them AFTER the compare registers?

2A is pin 11, 2B is pin 3.

Which mode are you trying to use - you don't explicitly set all the WGM bits, so I can't tell if its mode 3 or 7.

Thanks for the quick responses!

James C4S I changed the code, because I saw more examples and usually the TCCR2B is declared before the compare registers like you implied.
And as it is right now, is less complicated.

TCCR2B = _BV(WGM22) |_BV(CS21);  //define prescale 8
TCCR2A = _BV(COM2A0) | _BV(WGM21) | _BV(WGM20);  //enables pin 11
OCR2A = 50;  // frequency almost 20KHz

MarkT:
2A is pin 11, 2B is pin 3.

Which mode are you trying to use - you don't explicitly set all the WGM bits, so I can't tell if its mode 3 or 7.

MarkT I don't understand your question about the WGM mode bits.. As far as I know (and I confirmed with an oscilloscope),
I'm using fast PWM on pin11 with a prescale = 8.
I can't understand, why can't I change the duty cycle on pin11, as I did with pin3? Is it possible to do that?

Here's the code I'm using for pin3:

TCCR2A = _BV(COM2B1) | _BV(WGM21) | _BV(WGM20);  //Pin 3 
TCCR2B = _BV(WGM22) | _BV(CS21); // prescale 8
OCR2A = 100; // frequency = 20KHz
OCR2B = 75;  //  duty cycle = 75%

Reading through the datasheet, I'm not sure I if your comments match the registers themselves. The control bits are split across the two registers for your conditions for 2B not only set prescale 8 but also setup which mode you are operating.

TCCR2A = _BV(COM2A0) | _BV(WGM21) | _BV(WGM20);  //enables pin 11

TCCR2B = _BV(WGM22) |_BV(CS21);

WGM22, 21, and 20 are all set. Which enables Fast PWM. Top is OCRA and update occurs at Bottom.
CS21 is enabled which is a /8 prescaler
COM2A0 enabled means Toggle OC2A on Compare Match which enables pin 11.

Which is not what I think you want.

I think you meant to not set WGM22.
Set COM2A1 instead of COM2A0.

This (i think) enables Fast PWM. The pin will clear on match and reset at the botom. (enable both COM2A0 and COM2A1 if you want it to be inverted.)

Again, your comments don't match what the registers are doing, which is why you set Pin 3 more like what you probably wanted.

I read your comment carefully, but I don't know if I'm missing something..

That's exactly what I wanted. I want to make the same thing with pin11, as I did with pin3:

TCCR2A = _BV(COM2B1) | _BV(WGM21) | _BV(WGM20);  //Pin 3 
TCCR2B = _BV(WGM22) | _BV(CS21); // prescale 8

The only thing I did, was replace COM2B1 by COM2A0 to enable pin11 instead of pin3.
The problem I'm having is that I cannot change the value of the duty cycle on pin11 as I manage on pin3.. It's always fixed on a 50% duty cycle.
It's important to be able to do this, in order to control the speed of the motor I'm using in different directions (clockwise = pin3, anti-clockwise = pin11)

Myabe if I use an analogWrite I will be able to do that:

TCCR2A = _BV(COM2A0) | _BV(WGM21) | _BV(WGM20);  //enables pin 11
TCCR2B = _BV(WGM22) |_BV(CS21);  //define prescale 8
OCR2A = 50;  // frequency almost 20KHz
analogWrite(pin11, 25) // duty cycle = 25%

Do you think it will work? (I will only have an oscilloscope to test that tomorrow)
However, even if it works I can't understand why i couldn't change the duty cycle with OCR2B register..

Ziwdon:
The only thing I did, was replace COM2B1 by COM2A0 to enable pin11 instead of pin3.

Don't you think you should have used COM2A1 instead so that it would have toggled the pin? :wink: You have to manually set the pin to OUTPUT mode also.

Thanks for the tip afremont.
I already set the pin11 to OUTPUT, and James C4S also mentioned that I should use COM2A1 instead of COM2A0.

Let me see if I can get this straight.. I read that setting the COM2Ax bits to 01 sets timer/counter2 to toggle OC2A (pin 11) from high to low when the counter matches the 8-bit value stored in OCR2A. And the value stored is OCR2A = 50 (frequency almost 20KHz because of the prescaler=8).

So this line of code would have to be like this to be able to toggle (COM2Ax = 01) ?

TCCR2A = COM2A1 | _BV(COM2A0) | _BV(WGM21) | _BV(WGM20);

So this line of code would have to be like this to be able to toggle (COM2Ax = 01) ?

TCCR2A = COM2A1 | _BV(COM2A0) | _BV(WGM21) | _BV(WGM20);

No need to reference COM2A0 since you want it to be 0 anyway. You do need the _BV() doo-dad on the COM2A1 bit. I haven't checked it out, buy since you are wanting to toggle the other pin, you may need to set OCR2B to the max value and OCR2A to the duty cycle. In other words, it may be reversed.

TCCR2A = _BV(COM2A1) | _BV(WGM21) | _BV(WGM20);

I don't know what else to try..

When I use this:

TCCR2A = _BV(COM2A0) | _BV(WGM21) | _BV(WGM20);  
TCCR2B = _BV(WGM22) |_BV(CS21);  
OCR2A = 50;  // frequency almost 20KHz

I have exactly the value of pwm frequency I want, but I can't change the duty cycle like I said in my first post (it's always 50%, don't know why..)

When I use this (this code was made with your advices):

TCCR2A = _BV(COM2A1) | _BV(WGM21) | _BV(WGM20);
TCCR2B = _BV(CS21);
analogWrite (11, 100);

Like I expected, I have a frequency of 7.8KHz because I'm using a prescaler = 8 on pin11. And with analogWrite (I have to use analogWrite
because OCR2B doesn't change anything, and on pin 3 it changed the duty cycle) I'm able to change the duty cycle. However, I need a frequency of 20KHz..

It doesn't make any sense why this doesn't work like on pin3.

First off, I never advised you to use analogWrite() on any of this. :slight_smile: You can't really use that since you are modifying the timer control registers outside what the Arduino libraries expect. Here is some code that I wrote that generates a 25kHz Fast PWM signal on pin 3.

const int PWMPin = 3;
void setup() {
// generate 25kHz Fast PWM (mode 7) pulse rate on Pin 3
  pinMode(PWMPin, OUTPUT);   // OCR2B sets duty cycle
//  pinMode(11, OUTPUT);
// Set up Fast PWM on Pin 3
  TCCR2A = 0x23;     //0x23 COM2B1, WGM21, WGM20 
// Set prescaler  
  TCCR2B = 0x0A;   //0x0A WGM22, prescaler = /8
// Set TOP and initialize duty cycle to zero(0)
  OCR2A = 79;    // 79 TOP DO NOT CHANGE, SETS PWM PULSE RATE TO 25KHZ
  OCR2B = 39;    // duty cycle for Pin 3 (0-79) generates 1 500nS pulse even when 0 :(
}

void loop() {
}

I did some playing around and sure enough, it only generates a 100% duty cycle output on DP11. After reading the description of Fast PWM about 50 times, this is what I think is happening. I didn't put a scope on it, but I'm betting that pin 11 has a narrow spike where it drops out when the TCNT2 register matches OCR2A and then goes high again on the next clock tick as the TCNT2 resets to 0.

If you think about it, pin 11 is basically doing the same thing that pin 3 is doing. It is going HIGH while 0<=TCNT2<=OCR2A (pin 3 cares about OCR2B). When TCNT2 = OCR2A (TOP for both pins), I think the pin possibly drops for a clock tick then goes high again when TCNT2 resets to 0. It's effectively a 100% duty cycle since OCR2A is also the same as TOP.

Like you, if I try using the toggle option I only get half the pwm rate and a locked 50% duty cycle.

If you are willing to live with inexact PWM rates, you can generate two different duty cycles on pin 3 and pin 11 by using Mode 3 Fast PWM. In that case, TOP is 0xFF and OCR2A and OCR2B would determine the respective duty cycles of those pins.

void setup() {
  pinMode(3, OUTPUT);
  pinMode(11, OUTPUT);

  /* 
     WGM: 011  // Fast PWM
     COM2A and COM2B: 10   //non-invertered outputs A/B
     CS: 010  // divide by 8
  */
  
  TCCR2A = _BV(COM2A1) | _BV(COM2B1) | _BV(WGM21) | _BV(WGM20);
  TCCR2B = _BV(CS21);  // 8 prescaler is about 8kHz
  OCR2A = 180;  // duty cycle of pin 3 (output A)
  OCR2B = 10;  // duty cycle of pin 11 (output B)
}

void loop() {
}

afremont:
If you are willing to live with inexact PWM rates, you can generate two different duty cycles on pin 3 and pin 11 by using Mode 3 Fast PWM. In that case, TOP is 0xFF and OCR2A and OCR2B would determine the respective duty cycles of those pins.

Yep, exactly like I said. :wink: Having experience with both manufacturers now, I have to hand it to Microchip for documenting this stuff in a less confusing way. The Atmel documentation is vague at times and seems to imply that Mode7 Fast PWM should be able to generate a signal on Pin 11, but it won't.