Some notes about AVR timers

Some people asked about how to arrive at the timer settings used in the IR library port. Here’s my quick guide on how to set the timer parameters to have an interrupt service routine called every 50 microseconds.

1. ATtiny4313 / ATtiny2313 (TIMER1, 16 Bit)

Test program:

ISR(TIMER1_OVF_vect)
{
    // we want to arrive here every 50 microseconds
    PORTD ^= _BV(PD5);
}

void setup_timer1(void)
{
    TCCR1A = 0;
    TCCR1B = _BV(CS11);  // prescale /8
    TIMSK |= _BV(TOIE1); // enable overflow interrupt
    sei();               // enable interrupts
}

int main(void)
{
    DDRD = _BV(PD5);
    setup_timer1();
    for (;;) {_delay_ms(500); }
    return 0;
}

The TIMER1 on the attiny4313 is a 16-bit timer. Hence the timer counter register TCNT1 runs from zero to MAX = 2^16 – 1 = 65535. When it reaches the MAX value of 65535 the timer overflow interrupt service (ISR) routine gets called and then the timer will start over counting from zero to MAX. How frequent is the ISR called?

  • We use an external 16 Mhz crystal
  • We set the timer clock prescaler to /8
  • The TCNT1 is incremented 16 000 000 / 8  = 2 000 000 times per second
  • We need 65535 increments to have the timer overflow and the ISR called

65 535 / 2 000 000 = 0.0327675 seconds

The ISR is called every 32.7675 milliseconds. We toggle PD5 in the ISR so let’s check this pin with a logic analyzer:

16bit-1

Now what if we want our ISR to be called every 50 microseconds? We need to adjust the start value to something greater than zero so that we do not need to wait for the full 65535 increments to occur. How many timer increments per microsecond?

  • clock frequency is 16 000 000
  • timer clock prescaler is system clock /8
  • 1 000 000 microseconds per second

16 000 000 / 8 / 1 000 000 = 2 timer increments per microsecond

We can now adjust the initial timer start value to 65535 – 2 * 50 = 65435 and have to do this adjustment every time the timer overflows, too:

ISR(TIMER1_OVF_vect)
{
    PORTD ^= _BV(PD5);
    TCNT1 = 65435U;
}

void setup_timer1(void)
{
    TCCR1A = 0;
    TCCR1B = _BV(CS11);  // prescale /8
    TIMSK |= _BV(TOIE1); // enable overflow interrupt
    TCNT1 = 65435U;      // initial start value
    sei();
}

The timer will now run from 65435 to 65535, i.e. 100 increments in total and one microseconds passes every two increments.  The ISR is now called every 50 microseconds.

Let’s see this in the logic analyzer:

16bit-2

We are almost there. To get closer to 50 microseconds, we need to adjust for the time we spend inside the ISR itself. We could look at assembly code and check how long every single instruction takes, or we can just guess a “fudge factor” and measure:

#define CLKFUDGE 3

ISR(TIMER1_OVF_vect)
{
    PORTD ^= _BV(PD5);
    TCNT1 = 65435U + CLKFUDGE;
}

16bit-3

That’s close enough. You can increase the precision by reducing the timer prescale value, but you will also reduce the maximum interval in between calls to the ISR if you do so.

2. ATtiny85 (TIMER1, 8 Bit)

What changes?

The main difference is that the atttiny85 has an 8-bit TIMER1. Thus, the counter runs from zero to 255 and then wraps around. The names of some registers change and the attiny85 has two prescaler units; one using the internal system clock and another one for the fast peripheral clock. So be careful and consult the datasheet for the exact bits to set in the timer control register. I also switched from pin PD5 to PB0.

ISR(TIMER1_OVF_vect)
{
    PORTB ^= _BV(PB0);
}

void setup_timer1(void)
{
    TCCR1 = _BV(CS12);   // prescale by using peripheral clock /8
    TIMSK |= _BV(TOIE1); // enable overflow interrupt
    sei();
}

Let’s follow the same steps as before. Without any adjustments we can expect an interrupt every 127.5 microseconds.

  • We use an external 16 Mhz crystal
  • We set the timer clock prescaler to PCK/8
  • The TCNT1 is incremented 16 000 000 / 8  = 2 000 000 times per second
  • We need 255 increments to have the timer overflow and the ISR called

255 / 2 000 000 = 0.0001275 seconds = 127.5 microseconds

and indeed:

attiny85-1

To get to 50 microseconds, we adjust the start value in almost the same way as before. Instead of subtracting from the maximum 16 bit value of 65535 (= 0xFFFF), we use the maximum 8 bit value instead, i.e. 255 (= 0xFF).

16 000 000 / 8 / 1 000 000 = 2 timer increments per microsecond

 255 – 2 * 50 = 155 = new start value

let’s try:

attiny85-2

again, applying a “fudge factor”:

#define CLKFUDGE 3

ISR(TIMER1_OVF_vect)
{
    PORTB ^= _BV(PB0);
    TCNT1 = 155U + CLKFUDGE;
}

void setup_timer1(void)
{
     TCCR1 = _BV(CS12);   // prescale by using peripheral clock /8
     TIMSK |= _BV(TOIE1); // enable overflow interrupt
     TCNT1 = 155U + CLKFUDGE;
     sei();
}

attiny85-3

3. ATmega328p

You can use TIMER1 (16 bit) or TIMER2 (8 bit). Check the datasheet for the exact register names and how to set the prescaler.

Advertisements
This entry was posted in avr, timer and tagged , , . Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s