Arduino Interrupts Explained: How to Use External Interrupts in Code

Arduino Interrupts Explained: How to Use External Interrupts in Code

When working with electronics projects, sometimes you need the microcontroller to respond to certain events immediately, no matter what it’s currently doing. This is where interrupts come into play. In this guide, we’ll explain Arduino interrupts, particularly external interrupts, and show you how to use them in your code to make your projects more responsive and efficient.

What Are Interrupts in Arduino?

An interrupt is a signal that tells the microcontroller to pause its current task and immediately execute a specific block of code known as an Interrupt Service Routine (ISR). Once the ISR is completed, the microcontroller resumes its original task.

Interrupts are especially useful for handling time-sensitive tasks or responding to external events like button presses, sensor signals, or encoder inputs.

Types of Interrupts in Arduino:

  • External Interrupts: Triggered by an external signal, such as a change in voltage on a specific pin.
  • Timer Interrupts: Triggered by internal timers for precise timing operations.
  • Pin Change Interrupts: Triggered by a change in the state of a pin (available on some boards).

This guide will focus on external interrupts, which are triggered by changes on specific pins.

When to Use Interrupts

Here are some common scenarios where interrupts are useful:

  • Button debouncing: Detecting a button press reliably without constantly checking the button’s state in the loop.
  • Handling rotary encoders: Using interrupts to capture every movement of an encoder.
  • Time-sensitive sensor inputs: Triggering an action when a sensor output changes, without missing a signal.

Arduino External Interrupt Pins

Not all pins on an Arduino board support interrupts. Here’s a list of interrupt-capable pins on some popular Arduino boards:

  • Arduino Uno, Nano, Mini: Pins 2 and 3 support external interrupts.
  • Arduino Mega 2560: Pins 2, 3, 18, 19, 20, and 21 support external interrupts.
  • Arduino Leonardo and Micro: Pins 0, 1, 2, 3, and 7 support external interrupts.

Types of Interrupt Triggers

Arduino allows you to set an interrupt to trigger on the following events:

  • LOW: Trigger when the pin goes LOW (0V).
  • CHANGE: Trigger when the pin changes from HIGH to LOW or from LOW to HIGH.
  • RISING: Trigger when the pin changes from LOW to HIGH.
  • FALLING: Trigger when the pin changes from HIGH to LOW.

How to Use External Interrupts in Arduino

To use external interrupts in Arduino, the function attachInterrupt() is used. Here’s the basic syntax:

attachInterrupt(digitalPinToInterrupt(pin), ISR, mode);
  • digitalPinToInterrupt(pin): Converts the pin number to the corresponding interrupt number.
  • ISR: The name of the function to be called when the interrupt occurs (Interrupt Service Routine).
  • mode: The condition under which the interrupt will trigger (LOW, CHANGE, RISING, FALLING).

Example 1: Using External Interrupt to Detect a Button Press

Here’s an example where we use an external interrupt to detect when a button is pressed. The button will trigger an interrupt on pin 2, and an LED connected to pin 13 will toggle its state.

const int buttonPin = 2;   // Pin connected to the button
const int ledPin = 13;     // Pin connected to the LED
volatile bool ledState = false;  // Variable to store LED state
void setup() {
  pinMode(buttonPin, INPUT_PULLUP);  
  pinMode(ledPin, OUTPUT);           // Set the LED pin as an output
  // Attach interrupt to the button pin (pin 2), triggering on the FALLING edge
  attachInterrupt(digitalPinToInterrupt(buttonPin), toggleLED, FALLING);
}
void loop() {
  // The main loop does nothing, as the LED toggling is handled by the interrupt
}
// Interrupt Service Routine (ISR) to toggle the LED state
void toggleLED() {
  ledState = !ledState;           // Toggle the LED state
  digitalWrite(ledPin, ledState);  // Set the LED to the new state
}

In this example:

  • attachInterrupt() is used to detect when the button on pin 2 is pressed.
  • When the button is pressed (FALLING edge), the toggleLED() ISR is called, which toggles the state of the LED.

Example 2: Using External Interrupt for Rotary Encoder

Rotary encoders are common in projects that require precise position control, such as volume knobs. Here’s an example of using external interrupts to track the position of a rotary encoder.

const int clkPin = 2;  // Pin connected to the encoder's clock signal
const int dtPin = 3;   // Pin connected to the encoder's data signal
volatile int counter = 0;  // Variable to track the encoder position
void setup() {
  Serial.begin(9600);
  pinMode(clkPin, INPUT_PULLUP);  // Set clock pin as input with pull-up
  pinMode(dtPin, INPUT_PULLUP);   // Set data pin as input with pull-up
  // Attach interrupts for the rotary encoder pins
  attachInterrupt(digitalPinToInterrupt(clkPin), updateEncoder, CHANGE);
}
void loop() {
  Serial.println(counter);  // Print the encoder position
  delay(100);
}
// ISR to update the encoder position
void updateEncoder() {
  if (digitalRead(clkPin) == digitalRead(dtPin)) {
    counter++;  // Clockwise rotation
  } else {
    counter--;  // Counterclockwise rotation
  }
}

In this example:

  • External interrupts are used to track the rotation of the encoder by attaching interrupts to pin 2 (the clock signal).
  • The ISR updateEncoder() updates the position of the rotary encoder based on the direction of rotation.

Best Practices for Using Interrupts

  1. Keep ISRs short: Interrupt Service Routines should be as short as possible to avoid delaying other critical tasks in your code.
  2. Avoid using delay() in ISRs: Since ISRs need to execute quickly, avoid functions like delay() that introduce unnecessary pauses.
  3. Use volatile variables: Variables shared between the main program and the ISR should be declared as volatile to ensure they are correctly handled by the compiler.
  4. Detach interrupts when necessary: You can disable an interrupt using detachInterrupt() if the interrupt is no longer needed during certain parts of your program.

Conclusion: How to Use External Interrupts in Arduino

Interrupts are a powerful tool in Arduino programming, allowing your project to respond immediately to external events. By using external interrupts, you can make your projects more responsive and efficient, whether you’re tracking a button press, reading a rotary encoder, or handling other time-sensitive tasks. By following the best practices and examples in this guide, you’ll be able to use interrupts effectively in your Arduino projects.

FAQ

  1. Can I use interrupts on any Arduino pin?
    No, only specific pins on the Arduino support external interrupts. For example, on the Arduino Uno, only pins 2 and 3 can be used for external interrupts.
  2. What is the difference between attachInterrupt() and detachInterrupt()?
    attachInterrupt() enables an interrupt on a specific pin, while detachInterrupt() disables the interrupt for that pin.
  3. Can I have multiple interrupts in my code?
    Yes, you can have multiple interrupts, but be mindful of performance as too many interrupts can slow down your main program.
  4. Can interrupts interfere with other tasks in my program?
    Yes, if the Interrupt Service Routine takes too long, it can delay other tasks. Keeping ISRs short and efficient helps prevent this.
  5. How do I stop an interrupt from running?
    You can stop an interrupt using the detachInterrupt() function, which disables the interrupt on a specific pin.