How Interrupts are Handled in Arduinos

Bad Interrupts are the pop ads that are closed out immediately when browsing online.

Good interrupts I associate with MCUs - Like the ones I originally discovered in a PIC Microcontroller. In particular, the PIC16F72-I/SP-ND (added to our system in 2002 and still active). To get a 1 second sports timer to work properly, I had to set the right bits, using assembly language, in the INTCON register, the OPTION register configuring the Prescaler and by using the right crystal frequency (4.096MHz – X082-ND) I was able to get the correct timing for my display. All too complicated these days and much easier using an Arduino. Below, SparkFun Electronics presents Interrupts tutorial on the Arduino. Also, linked is a good video presented by Shawn Hymel. Check it out below!

Interrupts - what are they? They are people that intermittently prevent you from doing your current work. Ha! Well maybe… but what we really want to know is what they are in the context of embedded electronics and microprocessors.

So let’s ask that again - what is an interrupt? In a nutshell, there is a method by which a processor can execute its normal program while continuously monitoring for some kind of event, or interrupt. There are two types of interrupts:

Hardware Interrupts - These occur in response to an external event, like a pin going high or low.

Software Interrupts - These occur in response to a software instruction.

Generally speaking, most 8-bit AVR microcontrollers (i.e. Arduinos) aren’t innately capable of software interrupts, so for the purposes of this tutorial, we will focus on hardware interrupts.

Suggested Reading

If you aren’t familiar with the following concepts, we recommend checking out these tutorials before continuing.

When the event or interrupt happens, the processor takes immediate notice, saves its execution state, runs a small chunk of code (often called the interrupt handler or interrupt service routine), and then returns back to whatever it was doing before.

The programmer defines the code that is to be executed when a particular interrupt occurs within the program itself. In Arduino, we use a function called attachInterrupt() to do this and the recommended syntax looks similar to the output below.

Copy Code

attachInterrupt(digitalPinToInterrupt(pin), ISR, mode)

This function takes three parameters:

First Parameter (i.e. digitalPinToInterrupt(pin)) - Pin number of the interrupt, which tells the microprocessor which pin to monitor. The pin depends on the microcontroller being used.

Second Parameter (i.e. ISR) - The location of code we want to execute if this interrupt is triggered.

Third Parameter (i.e.mode) - Tells it what type of trigger to look for: a logic high, a logic low, or a transition between the two.

For more information and some example code if you’d like to read further, check out Arduino’s attachInterrupt() page.

In the following sections, we’ll look at a simple example to make more sense of interrupts and how they work. If you’d like to follow along, grab a Sparkfun RedBoard, an LED, a button, 330Ω resistor, jumper wires, and a cable to power it all.

Connect the LED to pin 13 and the button to pin 2 as you see in the Fritzing diagram below:

If you take a good look at what you’ve just hooked up, you’ll notice that the LED is actually redundant. We could just use the built-in LED on pin 13, but for visual purposes, we added the external LED.

Example: Simple Interrupt

Note: This example assumes you are using the latest version of the Arduino IDE on your desktop. If this is your first time using Arduino, please review our tutorial on installing the Arduino IDE.

Now that we’ve got our hardware hooked up, let’s look at a simple example that continuously sends an “Off” signal to an LED. We’ll attach an interrupt to pin 2; this pin will monitor a button that will send an “On” signal to the LED when pressed and increment a counter.

Most Arduinos have 2 external interrupts built in: interrupt0 (on digital pin 2) and interrupt1 (on digital pin 3). Some boards have more (like the Arduino Mega 2560) - refer to the user manual or datasheet for more information on what your specific board supports. Arduino also has more details on a handful of boards on their attachInterrupt() page. Since we are using a RedBoard here, this example uses pin 2 to monitor for interrupts.

Simple Interrupt Example 1

Select the board and COM port for the RedBoard. Then upload the following.

The main loop of this program sends an “OFF” signal to the LED every 3 seconds. Meanwhile, this program watches digital pin 2 (which corresponds to interrupt 0) for a rising edge. In other words, it looks for a voltage change going from logic low (0V) to logic high (5V), which happens when the button is pressed. When this happens the function increment is called. The code within this function is executed, variable x is incremented, and the LED is turned on. Then the program returns to wherever it was in the main loop.

If you play around with it, you’ll notice that the LED stays on for seemingly random amounts of time but never longer than 3 seconds. How long the LED stays on depends on where you interrupted the code in the main loop. For example, if the interrupt was triggered right in the exact middle of the delay function, the LED would remain lit for about 1.5 seconds after you hit the button.

Managing Bounce

One common problem with interrupts is they often can trigger multiple times for a single event. When you look at the serial output of the code in example 1, you’ll notice that even if you press the button just once, x will increment many times. To explore why this happens, we have to take a look at the signal itself. If we took an oscilloscope to monitor the voltage of the pin at the moment we pressed the button, it would look something like this:

While the main transition of the pin is from low to high, during the process, there are several spikes which can cause multiple interrupts. This is referred to as noise or bounce. A button push might seem like a single step, but in reality the mechanical parts within that button come into contact multiple times before settling into a particular state. There are several ways to remedy this. Often you can fix bounce issues with hardware by adding an appropriate RC filter to smooth the transition. Another option is to address it in software by temporarily ignoring further interrupts for a small time frame after the first interrupt is triggered. Going back to our old example, let’s add in a fix that allows the variable x to only be incremented once each button press instead of multiple times.

Simple Interrupt Example 2

Select the board and COM port for the RedBoard if you have not already. Then upload the following.

Let’s look again at the serial output as you press the button. Open ther serial monitor set at 9600 baud. Note that increment only gets called once for each button press. This fix works because each time the interrupt handler is executed, it compares the current time retrieved by the millis() function with the time the handler was last called. If it’s within a certain defined window of time, in this case a fourth of a second, the processor immediately goes back to what it was doing. If not, it executes the code within the if statement updating the variable x, turning on the LED and updating the last_button_time variable so the function has a new value to compare to when it's triggered in the future.

Interrupt Priority Levels

What happens when two interrupts occur at the same time? Most AVRs do not support what we call interrupt priority levels. Should two interrupts occur simultaneously or there are two or more interrupts waiting in a queue, the priority is determined by the order of their vector addresses. Lower vector addresses are serviced first, Reset will take precedence over all interrupt requests. Again, your datasheet will have more information on your specific board.

Example: Interrupting an LED Sequence

Interrupts can also come in handy when dealing with long sequences of things. Let’s look at another simple example with LEDs - let’s say that we are going to use the built-in RGB LED on a LilyPad USB Plus to cycle through a sequence of colors, fading each color on and off. The fade cycle time for each color is 10 seconds and we have a number of these boards sewn into costumes on stage. What happens if one of these costumes gets out of sync?

Rather than having to wait for the cycle to end and trying to reset the board in sync with the other boards, we can add an interrupt to pin 10 (this is interrupt 0 on the LilyPad USB Plus board). When the button is pressed, the interrupt is triggered and we move on to the next color. This allows us to get the offending costume in sync faster and the show can go on.

Let’s make this happen for ourselves. If you’d like to follow along, grab a LilyPad USB Plus. You’ll need the buttons, jumpers, and power supply from the previous experiment. You will also need a few alligator clip to pigtail wires to connect to the LilyPad sew tabs.

// Unlike digitalWrite, which can be only HIGH (on) or LOW (off), // analogWrite lets you smoothly change the brightness from 0 (off) to 255 (fully on). // When analogWrite is used with the RGB LED, you can create millions of colors!

FadeColor();}

// Interrupt service routine for interrupt 0void increment() {

button_time = millis();

//check to see if increment() was called in the last 250 milliseconds if (button_time - last_button_time > 250) { //increment counter x++;

//turn led off analogWrite(RGB_red,0); analogWrite(RGB_green,0); analogWrite(RGB_blue,0);

Serial.println(x, DEC); //print x to serial monitor delay(10000);

//set the last button time to the current button time last_button_time = button_time;

Note that each time you press the button, you switch to the next color. Perhaps not the most common use case, but visually more obvious how interrupts address immediate needs.

What Are the Advantages?

At this point you might wonder, “Why use an interrupt at all? Why not just occasionally use a digitalRead() on pin 2 to check its status? Won’t that do the same thing?”

The answer depends on the situation. If you only cared what the status of the pin was at a certain point in your code or time frame, then a digitalRead() will probably suffice. If you wanted to continuously monitor the pin, you could poll the pin frequently with digitalRead()'s. However, you could easily miss data between reads. This missed information could be vital in many real time systems. Not to mention, the more often you’re polling for data, the more processor time that is being wasted doing that rather than executing useful code.

Let’s look at the system that monitors and controls the anti-lock braking of a car as a critical timing example. If a sensor detects the car starting to lose traction, you really don’t care about what part of program is currently being executed, because something needs to be done about this situation immediately to assure the car retains traction and hopefully avoids an accident or worse. If you were just polling the sensor in this situation, the sensor may be polled too late and the event could be missed entirely. The beauty of interrupts is that they can prompt execution immediately, when it’s necessary.

Resources and Going Further

Now that we have a bit better idea of how interrupts work, can you use them in your next project? Or if you want or need more information on interrupts, you can check out some of the links below: