Understanding Arduino Timer Interrupts with Example Code

Arduinos are used in many IoT applications. From blinking LEDs to measuring the angular velocity of a wheel, Arduino is the microcontroller of choice not only for professional developers, but also for beginners who can use the board as a programming playground.

Now there are projects where timing is everything. Real-time applications are systems that require a timely response to external events. These responses must occur within a strict time constraint, often referred to as a “deadline.” If you miss the deadline, the system’s performance can be compromised, leading to potential failures. To avoid such failures and enhance real-time execution of code, we use interrupts.

So, today in this long, and a bit challenging blog, we will understand the use of timer interrupts in programming using Arduino.

Arduino Interrupts

Consider a simple Arduino project to blink an LED with a frequency of 1. A traditional method to accomplish this would be as follows:

// Constants
const int ledPin = 2;  // Pin number for the LED. On most Arduino boards, the built-in LED is connected to pin 13

void setup() {
  // Initialize the digital pin as an output.
  pinMode(ledPin, OUTPUT);
}

void loop() {
  // Turn the LED on (HIGH is the voltage level)
  digitalWrite(ledPin, HIGH);
  // Wait for 500 milliseconds
  delay(500);
  // Turn the LED off by making the voltage LOW
  digitalWrite(ledPin, LOW);
  // Wait for 500 milliseconds
  delay(500);
}

Now this is not false. However, in a scenario where real-time is of high significance (where a response with more than 50 ms delay is considered late), this code will fail to yield the desired outputs. How? In this code, we use some in-built functions, namely, digitalWrite, delay and pinMode. While these functions make the code readable and are used extensively, some amount of execution time is consumed when they are called. Moreover, the delay() function of Arduino is not accurate. When the signal is viewed under an oscilloscope, one would realize that the function have some level of error. This statement is better understood in this video:

To avoid such inaccuracies and ensure real-time execution, we use interrupts.

Arduino Timer Interrupts

Timer interrupts are such interrupts in the Arduino ecosystem, offering precise control over timing without bogging down your main program. Whether you’re blinking LEDs, generating PWM signals, or sampling sensors, mastering timer interrupts will allow your code to reach a new level of accuracy and real-time output.

Arduino Mega 2560 (the board I am using for this tutorial) consists of six timers: Timers 0 and 2 are eight-bit timers while timers 1,3,4 and 5 are 16-bit timers. 8 bits timers can contain counters values up to 255 while 16-bit timers can store values up to 65,536.

Another piece of information to note is the clock speed of Arduino which is 16 MHz. This means that Arduino, theoretically, can “run” 16,000,000 counters in one second. Hence, a single counter will take just 1/16,000,000 seconds to run. This leads to the realization that it takes ~16 microseconds for an 8-bit timer to overflow (256/16,000,000) while ~4 milliseconds for 16-bit timers (65,536/16,000,000).

Prescaler

For an event to occur as an interrupt, 16 microseconds and 4 ms is still too short a time frame. To make the clock “slower”, we divide the clock speed by a value called prescaler. For Arduino, one can set the prescaler value of either 8, 64, 256 or 1024.

[Note: Such definite values such as prescaler values, timer pins, interrupt pins, etc. can be found in the datasheet of the corresponding base microcontroller. In the case of Arduino Mega 2560, the base microcontroller is ATmega and its data sheet can be found here.]

Below is the equation you can use to scale down the clock using prescaler:

\(\text{Scaled Timer Speed (Hz)} = \frac{\text{Arduino Clock Speed (16 MHz)}}{\text{Prescaler}}\)

So, for instance, if you use 1024 as the prescaler, the scaled timer speed will be 15625 Hz; for prescaler = 256, scaled timer speed will be 62500 Hz, for prescaler = 64, new timer speed will be 250 kHz, and ultimately 2MHz for prescaler value of 8. For the sake of this example, we will consider the prescaler value of 1024, which gives us the new clock speed of 15625 Hz. Understand that it will now take one second to reach the counter value of 15624 (remember that counters are zero-indexed).

For an event to occur, we basically tell the timer that, “Hey, if you match this particular value, execute this piece of code.” Rephrasing the above idea a bit technically, the interrupt will occur when the timer counter matches the value of compare value. In the case above, if we want to blink our LED every one second, we set the compare value at that of the timer value, i.e. 15624. For the LED to blink twice per second, compare value will be half of the timer value -> 7812. Hence, we use Timer 1 for such case as it is 16-bit timer and can hold values up to 65,536 values.

This gives us a general formula to set compare values as follows:

\(\text{Compare Match Value} = \frac{\text{Arduino Clock Speed (16 MHz)}}{\text{Desired Interrupt Frequency (Hz) * Prescaler}} – 1\)

Let us now put this theory into code.

Arduino Code to control LED using Timer Interrupt

For the following example, it is recommended to try it out on an Arduino. But if you don’t have one, you can use this Arduino simulation website. You will find the circuit already built along with the code. Feel free to play around with the code later.

Also, the Arduino pin layout below helps us understand what pins are the elements connected to. When we program at the system level, we focus on ports or P-pins, not the digital pins. So in our example, the LED is connected to digital pin 2, or port pin E4. Now to the example code below!

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

int main(void)
{
	DDRE |= (1<<DDE4);
	TCCR1B = (1<<CS10) | (1<<CS12) | (1<<WGM12); // Prescaler 1024 and timer counter reset on compare value
	TCNT1 = 0; // set initial counter value to 0
	OCR1A = (16000000/(1024*1))-1; // set compare value to 15624
	TIMSK1 |= (1<<OCIE1A);

	sei();
	while (1) {
	}
}

ISR(TIMER1_COMPA_vect){
	PORTE ^= (1<<PE4);
}

Let us understand some important lines of the above code:

  • DDRE |= (1<<DDE4) : Here, we set the PORT E4 as output pin.
  • TCCR1B : This is the Timer/Counter Control Register for Timer 1. With the configuration (1<<CS10) | (1<<CS12) | (1<<WGM12), we set the prescaler to the value of 1024 and reset the counter whenever it matches the compare value.
  • OCR1A = (16000000/(1024*1))-1 : With this line of code, we set the value of Compare Register to 15624 (blinking LED with frequency of 1 Hz) using the formula we derived above.
  • TIMSK1 |= (1<<OCIE1A) : Ultimately, we activate the timer interrupt using the Timer Mask Register TIMSK1.

[Note: For a better understanding of the above registers, it is recommended to go through the datasheet of ATmega 2560- the main microcontroller on which Arduino is built. Click here!]

Summary

In this Arduino tutorial, we understood the importance of interrupts and how they are useful in realtime applications. In case of any issues with this blog, feel free to reach out to me on Instagram @machinelearningsite.

Also, reminding you once again to follow me on my social media where I post tips, tricks and sometimes memes on programming and machine learning. Why don’t you have a look and judge it by yourself:

If you enjoyed this blog (which I highly doubt considering the complexity of this topic), feel free to subscribe to my *FREE* newsletter where you receive monthly updates on such blogs with hands-on exercises and programming concepts.

Leave a Reply