Timer interrupts

This article will discuss AVR and Arduino timers and how to use them in Arduino projects or custom AVR circuits.

What is a timer?

Like in real life, in microcontrollers a timer is something you set to trigger an alert at a certain point in the future. When that point arrives, that alert interrupts the microprocessor, reminding it to do something, like run a specific piece of code.

Timers, like external interrupts, run independently from your main program. Rather than running a loop or repeatedly calling millis(), you can let a timer do that work for you while your code does other things.

So suppose you have a device that needs to do something –like blink an LED every 5 seconds. If you are not using timers but just conventional code techniques, you’d have to set a variable with the next time the LED should blink, then check constantly to see if that time had arrived. With a timer interrupt, you can set up the interrupt, then turn on the timer. The LED will blink perfectly on time, regardless of what your main program was just doing

How do timers work?

Timers work by incrementing a counter variable known as a counter register. The counter register can count to a certain value, depending on its size. The timer increments this counter one step at a time until it reaches its maximum value, at which point the counter overflows, and resets back to zero. The timer normally sets a flag bit to let you know an overflow has occurred. You can check this flag manually, or you can also have the timer trigger an interrupt as soon as the flag is set. Like any other interrupt, you can specify an Interrupt Service Routine (ISR) to run code of your choice when the timer overflows. The ISR will reset the overflow flag behind the scenes, so using interrupts is usually your best option for simplicity and speed.

In order to increment the counter value at regular intervals, the timer must have access to a clock source.  The clock source generates a consistent repeating signal.  Every time the timer detects this signal, it increases its counter by one.

Because timers are dependent on the clock source, the smallest measurable unit of time will be the period of this clock.  If you provide a 1 MHz clock signal to a timer, the timer resolution (or timer period) is:

T = 1 / f  (f is the clock frequency)
T = 1 / 1 MHz = 1 / 10^6 Hz
T = (1 * 10^-6) s

The timer resolution thus is one millionth of a second.

Though you can supply an external clock source for use with timers, but in most cases the chip’s internal clock is used as the clock source.

Types of timers

In the standard Arduino variants or an 8-bit AVR chip, there are several timers at your disposal.

The ATmega168 and ATmega328 have three timers: Timer0, Timer1, and Timer2. They also have a watchdog timer, which can be used as a safeguard or a software reset mechanism. Here are a few details about each timer:

Timer0

Timer0 is an 8-bit timer, meaning its counter register can record a maximum value of 255 (the same as an unsigned 8-bit byte). Timer0 is used by native Arduino timing functions such as delay() and millis(), so it is best not to mess with it unless you’re comfortable with the consequences.

Timer1

Timer1 is a 16-bit timer, with a maximum counter value of 65535 (an unsigned 16-bit integer). The Arduino Servo library uses this timer, so be aware if you use it in your projects.

Timer2

Timer2 is an 8-bit timer that is very similar to Timer0. It is utilized by the Arduino tone() function.

Timer3, Timer4, Timer5

The AVR ATmega1280 and ATmega2560 (found in the Arduino Mega variants) have an additional three timers.  These are all 16-bit timers, and function similarly to Timer1.

Configuring the timer register

In order to use these timers the built-in timer registers on the AVR chip that store timer settings need to be configured.  There are a number of registers per timer.  Two of these registers –the Timer/Counter Control Registers- hold setup values, and are called TCCRxA and TCCRxB, where x is the timer number (TCCR1A and TCCR1B, etc.).   Each register holds 8 bits, and each bit stores a configuration value.  Here are the details from the ATmega328 datasheet:

TCCR1A
Bit 7 6 5 4 3 2 1 0 TCCR1A
0x80 COM1A1 COM1A0 COM1B1 COM1B0 - - WGM11 WGM10
ReadWrite RW RW RW RW R R RW RW
Initial Value 0 0 0 0 0 0 0 0
TCCR1B
Bit 7 6 5 4 3 2 1 0 TCCR1B
0x81 ICNC1 ICES1 - WGM13 WGM12 CS12 CS11 CS10
ReadWrite R/W R/W R R/W R/W R/W R/W R/w
Initial Value 0 0 0 0 0 0 0 0

The most important settings are the last three bits in TCCR1B, CS12, CS11, and CS10.  These determine the timer clock setting.  By setting these bits in various combinations, you can tell the timer to run at different speeds.  Here’s the relevant table from the datasheet:

Clock Select bit description
CS12 CS11 CS10 Description
0 0 0 No clock source (Timer/Counter stopped)
0 0 1 clki/o/1 (No prescaling)
0 1 0 clki/o/8 (From Prescaler)
0 1 1 clki/o/64 (From Prescaler)
1 0 0 clki/o/256 (From Prescaler)
1 0 1 clki/o/1024 (From Prescaler)
1 1 0 External clock source on T1 pin. Clock on falling edge
1 1 1 External clock source on T1 pin. Clock on rising edge

By default, these bits are set to zero.  Suppose you want to have Timer1 run at clock speed, with one count per clock cycle.  When it overflows, you want to run an Interrupt Service Routine (ISR) that toggles a LED tied to pin 2 on or off. We’ll write Arduino code for this example, though we’ll use avr-libc routines wherever they don’t make things overly complicated. AVR pros can adapt as they see fit.

First, initialize the timer:

// avr-libc library includes

#include <avr/io.h>

#include <avr/interrupt.h>

#define LEDPIN 2

void setup()

{

pinMode(LEDPIN, OUTPUT);

// initialize Timer1

cli();         // disable global interrupts

TCCR1A = 0;    // set entire TCCR1A register to 0

TCCR1B = 0;

// enable Timer1 overflow interrupt:

TIMSK1 | = (1 << TOIE1);

// Set CS10 bit so timer runs at clock speed: (no prescaling)

TCCR1B |= (1 << CS10); // Sets bit CS10 in TCCR1B
// This is achieved by shifting binary 1 (0b00000001)
// to the left by CS10 bits. This is then bitwise
// ORed into the current value of TCCR1B, which effectively set
// this one bit high. Similar: TCCR1B |= _BV(CS10);

// enable global interrupts:

sei();

}

The register TIMSK1 is the Timer/Counter1 Interrupt Mask Register. It controls which interrupts the timer can trigger. Setting the TOIE1 bit (=Timer1 Overflow Interrupt Enable) tells the timer to trigger an interrupt when the timer overflows. It can also be set to other bits to trigger other interrupts. More on that later.

When you set the CS10 bit, the timer is running, and since an overflow interrupt is enabled, it will call the ISR(TIMER1_OVF_vect) whenever the timer overflows.

Next define the ISR:

ISR(TIMER1_OVF_vect)

{

digitalWrite(LEDPIN, !digitalRead(LEDPIN));

}

Now you can define loop() and the LED will toggle on and off regardless of what’s happening in the main program. To turn the timer off, set TCCR1B = 0 at any time.

Using the code how fast will the LED blink?

Timer1 is set to interrupt on an overflow, and let’s assume you are using an ATmega328 with a 16MHz clock. Since Timer1 is 16 bits, it can hold a maximum value of (2^16 – 1), or 65535. At 16MHz, we’ll go through one clock cycle every 1/(16*10^6) seconds, or 6.25e-8 s. That means 65535 timer counts will pass in (65535 * 6.25e-8s) and the ISR will trigger in  about 0.0041 seconds. Then again and again, every four thousandths of a second after that. That is too fast to see it blink. If anything, we’ve created an extremely fast PWM signal for the LED that’s running at a 50% duty cycle, so it may appear to be constantly on but dimmer than normal. An experiment like this shows the amazing power of microprocessors – even an inexpensive 8-bit chip can process information far faster than we can detect.

Timer prescaling and preloading

To control this you can also set the timer to use a prescaler, which allows you to divide your clock signal by various powers of two, thereby increasing your timer period.  For example, if you want the LED blink at one second intervals. In the TCCR1B register, there are three CS bits to set a better timer resolution.  If you set CS10 and CS12 using:

TCCR1B |= (1 << CS10); and TCCR1B |= (1 << CS12);, the clock source is divided by 1024. This gives a timer resolution of 1/(16*10⁶ / 1024), or 0.000064 seconds (15625 Hz). Now the timer will overflow every (65535 * 6.4e-5s), or 4.194s.
If you would set only CS12 using TCCR1B |=(1<<CS12); (or just TCCR1B=4), the clock source is divided by 256. This gives a timer resolution of 1/(16*10⁶/256), or 0.000016 sec (62500 Hz) and the timer will overflow every (65535 *0.000016=) 1.04856 sec.
Suppose you do not want an 1.04856 sec interval but a 1 sec interval. It is clear to see that if the counter wasn’t 65535 but 62500 (being equal to the frequency), the timer would be set at 1sec. The counter thus is 65535-62500=3035 too high. To have more precise 1 second timer we need to change only one thing – timer’s start value saved by  TCNT1 register (Timer Counter ). We do this with TCNT1=0x0BDC; BDC being the hex value of 3035. A Value of 34286 for instance would give 0.5 sec ((65535-34286)/62500)

The code looks as follows:

// avr-libc library includes
#include <avr/io.h> //  can be omitted
#include <avr/interrupt.h> // can be omitted
#define LEDPIN 2

void setup()
{
pinMode(LEDPIN, OUTPUT);

// initialize Timer1
cli();         // disable global interrupts
TCCR1A = 0;    // set entire TCCR1A register to 0
TCCR1B = 0;    // set entire TCCR1A register to 0

// enable Timer1 overflow interrupt:
TIMSK1 |= (1 << TOIE1);
// Preload with value 3036
TCNT1=0x0BDC;
// Set CS10 bit so timer runs at clock speed: (no prescaling)
TCCR1B |= (1 << CS12); // Sets bit CS12 in TCCR1B
// This is achieved by shifting binary 1 (0b00000001)
// to the left by CS12 bits. This is then bitwise
// ORed into the current value of TCCR1B, which effectively set
// this one bit high. Similar: TCCR1B |= _BV(CS12);
//  or: TCCR1B= 0x04;

// enable global interrupts:
sei();
}

ISR(TIMER1_OVF_vect)
{
digitalWrite(LEDPIN, !digitalRead(LEDPIN));
}

void loop() {}

CTC

But there’s another mode of operation for AVR timers. This mode is called Clear Timer on Compare Match, or CTC. Instead of counting until an overflow occurs, the timer compares its count to a value that was previously stored in a register. When the count matches that value, the timer can either set a flag or trigger an interrupt, just like the overflow case.

To use CTC, you need to figure out how many counts you need to get to a one second interval. Assuming we keep the 1024 prescaler as before, we’ll calculate as follows:

(target time) = (timer resolution) * (# timer counts + 1)

and rearrange to get

(# timer counts + 1) = (target time) / (timer resolution)
(# timer counts + 1) = (1 s) / (6.4e-5 s)
(# timer counts + 1) = 15625
(# timer counts) = 15625 - 1 = 15624

You have to add the extra +1 to the number of timer counts because in CTC mode, when the timer matches the desired count it will reset itself to zero. This takes one clock cycle to perform, so that needs to be factored into the calculations. In many cases, one timer tick isn’t a huge deal, but if you have a time-critical application it can make all the difference in the world.

Now the setup() function to configure the timer for these settings is as follows:

void setup()
{

pinMode(LEDPIN, OUTPUT);

// initialize Timer1
cli();          // disable global interrupts
TCCR1A = 0;     // set entire TCCR1A register to 0
TCCR1B = 0;     // same for TCCR1B

// set compare match register to desired timer count:
OCR1A = 15624;

// turn on CTC mode:
TCCR1B |= (1 << WGM12);

// Set CS10 and CS12 bits for 1024 prescaler:
TCCR1B |= (1 << CS10);
TCCR1B |= (1 << CS12);

// enable timer compare interrupt:
TIMSK1 |= (1 << OCIE1A);
sei();          // enable global interrupts
}

And you need to replace the overflow ISR with a compare match version:

ISR(TIMER1_COMPA_vect)
{
digitalWrite(LEDPIN, !digitalRead(LEDPIN));
}

The LED will now blink on and off at precisely one second intervals. And you are free to do anything you want in loop(). As long as you don’t change the timer settings, it won’t interfere with the interrupts. With different mode and prescaler settings, there’s no limit to how you use timers.

Here’s the complete example in case you’d like to use it as a starting point for your own project.

// Arduino timer CTC interrupt example
//
// avr-libc library includes
#include <avr/io.h>
#include <avr/interrupt.h>
#define LEDPIN 2
void setup()
{
pinMode(LEDPIN, OUTPUT);
// initialize Timer1
cli();          // disable global interrupts
TCCR1A = 0;     // set entire TCCR1A register to 0
TCCR1B = 0;     // same for TCCR1B

// set compare match register to desired timer count:
OCR1A = 15624;

// turn on CTC mode:
TCCR1B |= (1 << WGM12);

// Set CS10 and CS12 bits for 1024 prescaler:
TCCR1B |= (1 << CS10);
TCCR1B |= (1 << CS12);

// enable timer compare interrupt:
TIMSK1 |= (1 << OCIE1A);

// enable global interrupts:
sei();
}

void loop()
{
// main program
}

ISR(TIMER1_COMPA_vect)
{
digitalWrite(LEDPIN, !digitalRead(LEDPIN));
}

Remember that you can use the built-in ISRs to extend timer functionality. For example, if you wanted to read a sensor every 10 seconds, there’s no timer setup that can go this long without overflowing. However, you can use the ISR to increment a counter variable in your program once per second, then read the sensor when the variable hits 10. Using the same CTC setup as in our previous example, the ISR would look something like this:

ISR(TIMER1_COMPA_vect)
{
seconds++;
if(seconds == 10)
{
seconds = 0;
readSensor();
}
}

For a variable to be modified within an ISR, it is good custom to declare it as volatile. In this case, you need to declare volatile byte seconds; or similar at the start of the program.

A word on the Atmega8

The Atmega8 seems to give people problems with use of the timers, one reason is that it doesnt have a TIMSK1 register, it does have a TIMSK register though. As I do not have an Atmega8 I can not test it, but if you encounter problems, maybe the following program will help:

// this code sets up counter0 with interrupts enabled on an Atmega8
#include <avr/io.h>
#include <avr/io.h>

int main(void)
{
DDRD &= ~(1 << DDD4); // Clear the PD4 pin
// PD0 is now an input

PORTD |= (1 << PORTD4); // turn On the Pull-up
// PD4 is now an input with pull-up enabled

TIMSK |= (1 << TOIE0); // enable timer interrupt

TCCR0 |= (1 << CS02) | (1 << CS01) | (1 << CS00);
// Turn on the counter, Clock on Rise

sei();

while (1)
{
// Stuff
}
}

ISR (TIMER0_OVF_vect)
{
// interrupt just fired, do stuff
}

More on timers here

here
here
here
here
here
here

About these ads

32 thoughts on “Timer interrupts

  1. Pingback: Utilizando interrupção interna ou por tempo no Arduino | daltonhioki

  2. Pingback: #2 – Touché meets Pd and Max/MSP « Stefano Trento

  3. Pingback: #2 – Touché meets Pd and Max/MSP |

  4. Pingback: lucadentella.it – Allegro A4988 e Arduino (3)

    • Ha Tuyen, sorry for my late reactio, Indeed the comment section of wordpress is not really suitable for code. The includes are: avr/io.h and avr/interrupt.h, both between ‘fishhooks’

  5. hi guys,

    i had a problem with this code using a Atmega8

    “Interrupt.cpp: In function ‘void setup()':
    Interrupt.pde:-1: error: ‘TIMSK1′ was not declared in this scope”

    i look on the datasheet and this register has the name TIMSK without “1” but it isn’t works…….someone can help me? thans

    • it is over a year that i replied you and looking back at my reply i may have been a bit too hasty and not addressed things well. You seem not to be the only one who is having trouble with TIMSK1 and the Atmega8.
      It may not help you anymore but maybe someone else wuith the same problem is helped by this code:
      in case the includes drop in the code due to wordpress peculiarities:
      they read avr/io.h and avr/interrupt.h, both between ‘fishhooks’
      // this code sets up counter0 and with interrupts enabled
      #include
      #include

      int main(void)
      {
      DDRD &= ~(1 << DDD4); // Clear the PD4 pin
      // PD0 is now an input

      PORTD |= (1 << PORTD4); // turn On the Pull-up
      // PD4 is now an input with pull-up enabled

      TIMSK |= (1 << TOIE0); // enable timer interrupt

      TCCR0 |= (1 << CS02) | (1 << CS01) | (1 << CS00);
      // Turn on the counter, Clock on Rise

      sei();

      while (1)
      {
      // we can read the value of TCNT0 hurray !!
      }
      }

      ISR (TIMER0_OVF_vect)
      {
      // interrupt just fired
      }

  6. Pingback: “Необычное” поведение режима CTC таймера1 | MyLinks

  7. Just wanted to drop you a line to say thanks for the best explanation I have found on the timer/interrupt features. Nice work.
    Saved me a lot of time rather than digging through the depths of the 448 page data sheet.

  8. Your explanation has propelled my understanding of timer interrupts like no other piece I’ve read. Thank you so much!

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