Arduino Timer Interrupts – Explained with Timer1 and Timer2 Examples

Interrupts are used to handle events that do not happen during the sequential execution of a program. There are two types of interrupts for the Arduino microcontroller. Hardware or external interrupt and Timer interrupt. In a previous tutorial, we looked at Arduino external interrupt where the interrupt was caused by is caused by the external hardware module. However, in this user guide, we will focus on Timer interrupts of Arduino, its introduction, and how to configure them in our Arduino. This guide is highly useful for people who want to increase the productivity of their program sketch by incorporating timer interrupts to enhance the capabilities of their microcontroller.

Interrupts

Interrupts are used to handle events that do not happen during the sequential execution of a program. For example, we want to perform certain tasks and these tasks execute sequentially in your Arduino program. But there are few tasks that only execute when a special event occurs such as an external trigger signal to the digital input pin of a microcontroller.

Interrupts are the section of hardware and software on a microcontroller which is capable of monitoring the external event on the input pin when any external event is monitored then the program stops sequential execution at this point and jumps into their ISR function which is an interrupt handler then after executing the ISR function, it goes back to the point where the external event is monitor than it completes sequential execution.

How interrupt works
Interrupt Process

To learn how to generate external interrupts with Arduino follow the tutorial given below:

Arduino Timer Interrupts

Timer interrupts in Arduino pause the sequential execution of a program loop() function for a predefined number of seconds (timed intervals) to execute a different set of commands. After the set commands are executed, the program resumes again from the same position. The Arduino comes with three timers known as Timer0 (8-bit timer), Timer1 (16-bit timer), and Timer2 (8-bit timer). They act as a clock and are used to keep track of time based events. These timers will be programmed using registers which we will learn about.

Our user guide will focus on learning how to generate Timer1 and Timer2 interrupts of Arduino.

How do Timer Interrupts work?

As Timer1 and Timer2 are 16-bit and 8-bit timers respectively hence they can count from 0-65537 for Timer1 and 0-255 for Timer2. Depending on the timer mode which is selected the timer will start by increasing its value until it reaches its maximum count then go back to 0. This creates a triangle shaped curve which the timer follows. This will cause the interrupt to occur as the value increases from 0-255 in the case of Timer2 and then goes back to 0 and repeats again.

How are timer interrupts caused.

Each of the three timers are able to generate different interrupts.

Timer1 can generate Compare Match, Overflow and Input Capture interruptions. In compare match, the interrupt is triggered when the timer value in the register is equal to the compare value. If we set the compare value equal to 50 then whenever the Timer1 will reach this value an interrupt will be called. The next type of interrupt is overflow. This is triggered whenever the timer value reaches its maximum value. The last type of interrupt is input capture. Whenever an external trigger occurs, the value gets saved in a different register.

On the other hand Timer0 and Timer2 can generate Compare Match and Overflow interruptions only.

Timer Speed

One important aspect of the timer clock is its speed at which it increments the counter. It is directly related to the prescaler we will set for our timer. The prescaler can take values from 1, 8, 64, 256 or 1024 which is then used to divide the maximum Arduino clock speed i.e 16MHz to find the maximum timer speed.

Timer speed (Hz) = Arduino clock speed (16MHz) / prescaler

If the prescaler is set to 1 that means the timer will increase the counter at 16MHz on the other hand if the prescaler is set o 8 then the counter will increment at 2MHz. Thus, the prescaler will be able to control the speed of incrementation of the timer counter.

The following table shows the clock generation for both Timer1 and Timer2 at 16MHz:

Timer1Timer2
Prescaler1, 8, 32, 64, 128, 256, 10241, 8, 32, 64, 128, 256, 1024
Max Timer Period (ns)0.016, 0.128, 0.512, 1.024, 2.048, 4.096, 16.3844.098, 32.784, 131.136, 262.272, 524.544, 1049.088, 4196.352
Resolution (ms)0.063, 0.5, 2, 4, 8, 16, 640.063, 0.5, 2, 4, 8, 16, 64
Timer1 and Timer2 Clock Generation at 16MHz frequency

Registers for Timer1

As we already know Timer1 is able to generate Output Compare Match, Overflow and Input Capture interrupts. For Output Compare Match enablement, OCIE1B and OCIE1A bits are used. Likewise, for overflow interrupt enablement, TOIE1 bit is used. Similarly, we use ICIE1 bit for input capture interrupt.

Timer1 consists of two major registers TCCR1A and TCCR1B which control the timers where TCCR1A is responsible for PWM and TCCR1B is used to set the prescalar value. We will set all the bits in the TCCR1A register to 0 as we will not be using it.

Bit76543210
(0x80)COM1A1COM1A0COM1B1COM1B0WGM11WGM10
Read/WriteR/WR/WR/WR/WRRR/WR/W
Initial Value00000000
TCCR1A Timer1 Control Register A

However, for TCCR1B, the first three bits are used to set the prescalar value. These are known as CS10, CS11 and CS12 bits.

Bit76543210
(0x81)ICNC1ICES1WGM13WGM12CS12CS11CS10
Read/WriteWWRR/WR/WR/WR/WR/W
Initial Value00000000
TCCR1B Timer1 Control Register B

The following table shows the bits for CS12, CS11 and CS10 respectively to set a prescalar value.

CS12CS11CS10Description
000Timer stop
001Prescalar=1
010Prescalar=8
011Prescalar=64
100Prescalar=256
101Prescalar=1024
110Falling edge
111Rising edge
Clock Select Bit Description

To set the operating mode, refer the table below:

WGM13WGM12WGM11WGM10ModeTOP
0000Normal0xFFFF
0100CTCOCR1A

Registers for Timer2

Now for Timer2 we already know Timer2 is able to generate Output Compare Match and Overflow interrupts. For Output Compare Match enablement, OCIE2B and OCIE2A bits are used. Likewise, for overflow interrupt enablement, TOIE2 bit is used.

Timer2 consists of two major registers TCCR2A and TCCR2B which control the timers where TCCR2A is responsible for PWM and TCCR2B is used to set the prescalar value. We will set all the bits in the TCCR2A register to 0 as we will not be using it.

Bit76543210
(0xB0)COM2A1COM2A0COM2B1COM2B0WGM21WGM20
Read/WriteR/WR/WR/WR/WRRR/WR/W
Initial Value00000000
TCCR2A Timer2 Control Register A

However, for TCCR2B, the first three bits are used to set the prescalar value. These are known as CS20, CS21 and CS22 bits.

Bit76543210
(0xB1)FOC2AFOC2BWGM22CS22CS21CS20
Read/WriteWWRRR/WR/WR/WR/W
Initial Value00000000
TCCR2B Timer2 Control Register B

The following table shows the bits for CS22, CS21 and CS20 respectively to set a prescalar value.

CS22CS21CS20Description
000Timer stop
001Prescalar=1
010Prescalar=8
011Prescalar=32
100Prescalar=64
101Prescalar=128
110Prescalar=256
111Prescalar=1024
Clock Select Bit Description

To set the operating mode, refer the table below:

WGM22WGM21WGM20ModeTOP
000Normal0xFF
010CTCOCR2A

Testing Timer Interrupts: Timer1 & Timer2

Now let us test the Timer1 and Timer2 interrupts. For Timer1 we will blink the onboard LED of Arduino UNO after every second. For Timer2 we will blink the onboard LED of Arduino UNO after every 0.5 s.

Calculations to find the Count value

  • For Timer1:

System clock is 16Mhz and prescalar is 256 for generating clock of 1s

Speed of Timer1 = 16MHz/256 = 62.5 KHz

Pulse Time= 1/62.5 KHz = 16us

Hence value of OCR Register will be set to: 1s/16us = 62500

  • For Timer2:

System clock is 16Mhz and prescalar is 1024 for generating clock of 10ms

Speed of Timer2 = 16MHz/1024 = 15.625 KHz

Pulse Time = 1/15.625 KHz = 64 us

Hence value of OCR register will be set to: 10ms/64us = 156.25 ->156 (whole number)

Arduino Sketch: Blinking onboard LED every second using Timer1 Interrupt

Open your Arduino IDE and go to File > New to open a new file. Copy the code given below in that file.

In the following sketch, Timer1 should generate an interrupt after every one second. The onboard LED will blink every second.

#include <avr/io.h>
#include <avr/interrupt.h>

const int LED_pin = 13; 
unsigned int reload = 0xF424; 
volatile int count;

ISR(TIMER1_COMPA_vect)
{
count++;
flash();
}
void setup()
{
Serial.begin(115200);
pinMode(LED_pin, OUTPUT);
digitalWrite(LED_pin, LOW);
cli();
TCCR1A = 0;
TCCR1B = 0; 
OCR1A = reload;
TCCR1B = (1<<WGM12) | (1<<CS12); 
TIMSK1 = (1<<OCIE1A); 
sei(); 
Serial.println("TIMER1 Setup Finished.");
}
void loop()
{
Serial.println(count); // do anything
delay(200);
}
void flash()
{
static boolean output = HIGH;
digitalWrite(LED_pin, output);
output = !output;
}

How the Code Works?

In the ISR we will increase the count variable by 1 and toggle the state of the LED.

ISR(TIMER1_COMPA_vect)
{
count++;
flash();
}

void flash()
{
static boolean output = HIGH;
digitalWrite(LED_pin, output);
output = !output;
}

Inside, the setup() function, we will open the serial communication at a baud rate of 115200 and configure the onboard LED pin as an output. initially, we will set its state to LOW so that it will be OFF.

Serial.begin(115200);
pinMode(LED_pin, OUTPUT);
digitalWrite(LED_pin, LOW);

Then we will initialize the Timer1 registers. Reset the control registers by resetting TCCR1A and TCCR1B to 0.

TCCR1A = 0;
TCCR1B = 0; 

The timer1 will be in CTC mode. We will set the value of compare register A to 0xF424 = 62500 as calculated previously. The ‘reload’ variable is defined at the start.

unsigned int reload = 0xF424;
OCR1A = reload;

Also, we will set the prescalar value by changing the bits of CS10, CS11 and CS12 in the following way.

TCCR1B = (1<<WGM12) | (1<<CS12); 
TIMSK1 = (1<<OCIE1A); 

Inside, the loop() function we will display the count variable in the serial monitor after every 200ms.

void loop()
{
Serial.println(count); 
delay(200);
}

Demonstration

To see the demonstration of the above code, upload the code to Arduino. But, before uploading code, make sure to select the Arduino board from Tools > Board and also select the correct COM port to which the Arduino board is connected from Tools > Port.

select Arduino uno

Once the code is uploaded to Arduino, open the serial monitor and set the baud rate to 115200.

Arduino Timer1 serial monitor demo
Serial Monitor

The onboard LED will start blinking every second.

Arduino UNO LED blink time interrupts

Arduino Sketch: Blinking onboard LED every 0.5s using Timer2 Interrupt

Open your Arduino IDE and go to File > New to open a new file. Copy the code given below in that file.

In the following sketch, Timer2 should generate an interrupt after every 10 ms. The onboard LED will blink every half second.

#include <avr/io.h>
#include <avr/interrupt.h>

const int LED_pin = 13; 
volatile byte count;
byte reload = 0x9C; 

ISR(TIMER2_COMPA_vect)
{
count++;
OCR2A = reload;
}

void setup()
{
Serial.begin(115200);
pinMode(LED_pin, OUTPUT);
digitalWrite(LED_pin, LOW);
cli();
TCCR0B = 0; 
OCR2A = reload;
TCCR2A = 1<<WGM21;
TCCR2B = (1<<CS22) | (1<<CS21) | (1<<CS20);
TIMSK2 = (1<<OCIE2A);
sei();
Serial.print("OCR2A: "); 
Serial.println(OCR2A, HEX);
Serial.print("TCCR2A: "); 
Serial.println(TCCR2A, HEX);
Serial.print("TCCR2B: ");
Serial.println(TCCR2B, HEX);
Serial.print("TIMSK2: "); 
Serial.println(TIMSK2, HEX);
Serial.println("TIMER2 Setup Finished.");
}
void loop()
{
if (count == 50)
{
flash();
Serial.print(".");
count = 0;
}
}
void flash()
{
static boolean output = HIGH;
digitalWrite(LED_pin, output);
output = !output;
}

How the Code Works?

In the ISR we will increase the count variable by 1 and set the value of compare register A OCR2A to reload. The ‘reload’ variable was initially defined as ‘0xF424’ that is equal to 62500. This was calculated previously.

unsigned int reload = 0xF424; 

ISR(TIMER2_COMPA_vect)
{
count++;
OCR2A = reload;
}

Inside, the setup() function, we will open the serial communication at a baud rate of 115200 and configure the onboard LED pin as an output. initially, we will set its state to LOW so that it will be OFF.

Serial.begin(115200);
pinMode(LED_pin, OUTPUT);
digitalWrite(LED_pin, LOW);

Then we will initialize the Timer2 registers and set it in CTC mode. Also, we will set the prescalar value by changing the bits of CS20, CS21 and CS22 as well.

TCCR0B = 0; 
OCR2A = reload;
TCCR2A = 1<<WGM21;
TCCR2B = (1<<CS22) | (1<<CS21) | (1<<CS20);
TIMSK2 = (1<<OCIE2A);

In the serial monitor, we will display the bytes of Timer2 registers once the setup finishes.

Serial.print("OCR2A: "); 
Serial.println(OCR2A, HEX);
Serial.print("TCCR2A: "); 
Serial.println(TCCR2A, HEX);
Serial.print("TCCR2B: ");
Serial.println(TCCR2B, HEX);
Serial.print("TIMSK2: "); 
Serial.println(TIMSK2, HEX);
Serial.println("TIMER2 Setup Finished.");

Inside the loop() function we will toggle the LED after every 0.5 s or 500ms. This will be achieved through an if statement which will check if the count has reached 50 or not. The count increments whenever an interrupt is caused after every 10ms. Thus when the count equals 50 that means the time is (50x10ms) = 500ms = 0.5s

void loop()
{
if (count == 50)
{
flash();
Serial.print(".");
count = 0;
}
}
void flash()
{
static boolean output = HIGH;
digitalWrite(LED_pin, output);
output = !output;
}

Demonstration

To see the demonstration of the above code, upload the code to Arduino. But, before uploading code, make sure to select the Arduino board from Tools > Board and also select the correct COM port to which the Arduino board is connected from Tools > Port.

select Arduino uno

Once the code is uploaded to Arduino, open the serial monitor and set the baud rate to 115200.

Arduino Timer2 serial monitor demo
Serial Monitor

The onboard LED will start blinking every second.

Arduino UNO LED blink time interrupts

Conclusion

In this guide, we have learned about Arduino timer interrupts and how to use Timer1 and Timer2 interrupts using Arduino IDE. We dedicated two sketches one for Timer1 and another for Timer2 where we demonstrated the interrupts through toggling the onboard LED. For Timer1, we set the clock to 1s. Every time the interruptions took place the state of the onboard LED was toggled. For Timer2, we set the clock to 10ms and toggled the LED after every 50 interruptions i.e. after every 0.5 seconds.

4 thoughts on “Arduino Timer Interrupts – Explained with Timer1 and Timer2 Examples”

  1. If I am not mistaking you mixed timer1 and timer2 in the table with the ‘Max Timer Period (ns)” and “Resolution (ms)” since timer1 is the one with 16bits

    Reply
  2. I found this tutorial and examples very enlightening. In conjunction with the
    ATMEGA328 data sheet I’ve managed to achieve my objectives with the Timer/Counters.

    As an inexperienced user though I noticed a few issues:
    1. The first table has Timer1 and Timer2 swapped. Timer1 has a 16 bit counter and hence longer periods while Timer2 only has an 8 bit counter. Also, prescaler values 32 and 128 are not available in Timer1.

    2. In the examples, the two “#include avr/…. ” compiler directives don’t appear to be necessary and should probably be of the form “#ifndef …. include …..”as the Arduino interface appears to already “#include” them.
    3. As there are many abbreviations to take in it took me a while to work out that WGM12 and WGM21 set the timers in CTC mode. The data sheet implies that CTC1 may be an equivalent macro to WGM12 (Data sheet, Table 16.4, p121), but I haven’t tried that. (Doesn’t mention CTC2, table 18.8 though.)
    4. In the Timer2 example, you set OCR2A in the ISR. It is not necessary to reload OCR2A as it is not affected by the CTC interrupt. At any Clear Timer on Compare interrupt, TCNTn is set to 0 and it all starts again with the existing Output Compare value, unless it is changed.
    5. After the cli();, TCCR0B = 0; This refers to Timer0 and has no effect on Timer2 (but could probably interfere with other timer-based Arduino functions that rely on Timer0. You probably need a TCCR2B = 0 and TCCR2A = 0 for consistency.
    6. I also observed that the timer-based macros (the ones in capitals) must have all their bits set/unset in the one command line else any settings made in previous commands are lost.

    All in all, I enjoyed playing with the timers and this page was the most informative I found.

    Reply

Leave a Comment