Changing Timer2 prescaler inside a main loop

Go To Last Post
7 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hello everybody!

 

I've started my long way into MCU from Arduino, but now I'm trying to move away from some of its slow programming. So now I'm looking into timers, but I cannot understand some part. This code works exactly the way I suppose it to:

 

void setup()
{
  DDRB |= _BV(5);

  cli();

  TCCR2A = 0; //If I don't clear these registers, they seem to retain their value even after re-flashing!
  TCCR2B = 0;

  TCCR2B |= (_BV(CS21) | _BV(CS20)); //Setting the prescaler to 32
  TCCR2A |= _BV(WGM21);             //Setting the mode to CTC
  OCR2A = 99;                       //The frequency should be 16000000 / (32 * (1 + 99)) = 5000 Hz
  TIMSK2 |= _BV(OCIE2A);            //Allowing interrupts on match
  sei();
}

ISR(TIMER2_COMPA_vect)
{
  PORTB ^= _BV(5);              //Just toggle the pin
}

void loop()
{

}

It generates a PWM with exactly 50% duty cycle, and period time is exactly 200 us (32/(16 000 000*(1+99)). So nothing strange here. But the following code:

 

void setup()
{
  DDRB |= _BV(5);
  DDRD &= ~_BV(7);

  cli();
  TCCR2A = 0;
  TCCR2B = 0;
  TCCR2B |= (_BV(CS21) | _BV(CS20));
  TCCR2A |= _BV(WGM21);
  OCR2A = 99;
  TIMSK2 |= _BV(OCIE2A);
  sei();
}

ISR(TIMER2_COMPA_vect)
{
  PORTB ^= _BV(5);
}

void loop()
{
  if (PIND & _BV(7))
    {
      TCCR2B = 0;                           //Reset register
      TCCR2B |= (_BV(CS21) | _BV(CS20));    //Set prescaler to 32
    }
    else
    {
      TCCR2B = 0;                           //Reset register
      TCCR2B |= _BV(CS22);                  //Set prescaler to 64
    }
}

Doesn't work well. When PD7 is at +5V, the output on PB5 is 50% PWM, but with period time is 375 us, and when I set PD7 to GND the output changes to 700 us. What is the reason of it, and is ot possible to change the timer's prescaler on the fly inside the program?

 

UPD.: Oh, sorry, must add, that if I change the OCR2A value inside the main loop based on the pin status, then the frequency is correct. The real problem is, that I want to use a single timer to be run on different frequencies depend upon user input, and the range is from ~20 Hz to 20 000 Hz. It is only possible if I would change both OCR2A register and a prescaler.

 

P.S. Sorry for posting it in avr instead of Arduino, but basically there is no Arduino code at all...

Last Edited: Tue. Dec 6, 2016 - 09:08 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

you only or bit on , never clear them!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

sparrow2 wrote:

you only or bit on , never clear them!

 

Not sure what you mean exactly, sorry... If you mean this paert:

 

TCCR2B = 0;                           //Reset register
TCCR2B |= (_BV(CS21) | _BV(CS20));    //Set prescaler to 32

Than (I guess) I first set register to 0, then configure it to prescaler 32... Or should I do it different way?

 

UPD.: Changed it the following way:

void loop()
{
  if (PIND & _BV(7))
  {
    TCCR2B &= ~_BV(CS22);
    TCCR2B |= (_BV(CS21) | _BV(CS20));
  }
  else
  {
    TCCR2B = ~(_BV(CS20) | _BV(CS21));
    TCCR2B |= _BV(CS22);
  }
}

Now working!!!!! Thanks!!!!

Last Edited: Tue. Dec 6, 2016 - 09:20 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Surely it is easier, wiser, less confusing, ... to just use =

 

TCCR2B = (_BV(CS21) | _BV(CS20));    //Set prescaler to 32

Personally,  I find it clearer with:

TCCR2B = (3 << CS20);    //Set prescaler to 32

Only use |= and &=~ when appropriate.

 

Oh,   if you are using Arduino software,   remember to initialise a Timer properly.    The Arduino startup code has already set the registers for its own purposes.  So you can NOT assume that the registers have the RESET values.

 

David.

Last Edited: Tue. Dec 6, 2016 - 09:32 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

From my poit of view 

TCCR2B = (3 << CS20);    //Set prescaler to 32

Is not obvious at all, but that's probably I'm a newbie. 3 in binary is 00000011, CS20 is 0, so the result of 3<<CS20 is 3 again, i.e. 00000011. So we can further simplify it to TCCR2B = 3.

 

david.prentice wrote:
Oh,   if you are using Arduino software,   remember to initialise a Timer properly.    The Arduino startup code has already set the registers for its own purposes.  So you can NOT assume that the registers have the RESET values.

 

That explains much, thanks!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

+1 

I hate the multi bit shift setup.

 

(personally I would prefer it binary)

 

And the use of = make me nervous next time there are an other bit that should stay. 

 

 

Last Edited: Tue. Dec 6, 2016 - 10:56 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Look at different Special Function Registers.  They have different Special bits.   In different Special bit Positions.

Sometimes you have group of bits in adjacent bit-positions.  e.g.

TCCR2A has "Compare Match Output A Mode" in bits 6,7

 

So you would choose one of four different Modes.

Or TCCR2B has got "Clock Select" in bits 0,1,2.   So you would choose one of eight Clock Select modes.

 

Yes,  I could work out which bits are 1 and which are 0.    I find Mode# easier to remember.

Of course,   this theory collapses when it comes to the PWM modes.   WGM22 is in TCCR2B and WGM21,20 are in TCCR2A.

 

Group-numbers and group-positions is my personal preference.

Use whichever style you are happiest with.   However my point about = and |= is important.

 

David.