Arduino - Interrupts
Arduino - Understanding Interrupts
Section titled “Arduino - Understanding Interrupts”In microcontroller programming, an interrupt is a signal that temporarily halts the processor’s current task to execute a special piece of code in response to an important event. Once the special code is finished, the processor resumes its original task exactly where it left off.
Imagine you’re reading a book (your main loop() function), and the doorbell rings (the interrupt event). You place a bookmark, go answer the door (the Interrupt Service Routine), and then return to your book, picking up right where the bookmark was. Without the doorbell, you’d have to stop reading every few seconds to check the door, which is inefficient. Interrupts solve this by letting the event notify you.
Why Use Interrupts?
Section titled “Why Use Interrupts?”The primary advantage of interrupts is responsiveness. A microcontroller can perform long tasks (like managing a display or calculating data) while still being able to react instantly to critical events like:
- A button press from a user.
- Data arriving from a sensor.
- A signal from a rotary encoder.
- A timer reaching a specific value.
Without interrupts, you would have to constantly poll (check) the state of these inputs in your main loop, which can be slow and lead to missed events if the loop is busy.
External Interrupts on Arduino
Section titled “External Interrupts on Arduino”This tutorial focuses on external interrupts, which are triggered by a change in voltage on a specific digital pin. Most Arduino boards have a limited number of these dedicated interrupt pins.
| Board | Interrupt Pins |
|---|---|
| Arduino UNO, Nano, Mini | 2, 3 |
| Arduino Mega 2560 | 2, 3, 18, 19, 20, 21 |
| Arduino MKR & Zero Family | All digital pins can be used. |
| Raspberry Pi Pico (RP2040) | All GPIO pins can be used. |
Configuring an Interrupt
Section titled “Configuring an Interrupt”You configure an interrupt using the attachInterrupt() function. The modern, recommended syntax is:
attachInterrupt(digitalPinToInterrupt(pin), ISR, mode);-
digitalPinToInterrupt(pin): A helpful function that translates a board’s digital pin number (e.g.,2) into its specific internal interrupt number. Using this makes your code portable across different Arduino boards. -
ISR: The name of the special function—the Interrupt Service Routine—that will be called when the interrupt is triggered. This function must not take any parameters and must not return any value (void myISR()). -
mode: Defines what kind of signal change triggers the interrupt: -
LOW: Trigger whenever the pin is held at a LOW voltage. (Use with caution as this can trigger repeatedly). -
CHANGE: Trigger whenever the pin’s value changes (from LOW to HIGH or HIGH to LOW). -
RISING: Trigger when the pin’s value goes from LOW to HIGH. -
FALLING: Trigger when the pin’s value goes from HIGH to LOW. This is very common for button presses with a pull-up resistor.
Interrupt Service Routine (ISR) Best Practices
Section titled “Interrupt Service Routine (ISR) Best Practices”ISRs are special and have strict rules:
- Keep them short and fast. An ISR should do the absolute minimum work required, like setting a flag or updating a variable, then exit.
- Declare shared variables as
volatile. If a variable is used in both the ISR and the main loop, you must declare it with thevolatilekeyword (e.g.,volatile bool eventOccurred = false;). This tells the compiler that the variable’s value can change at any time, preventing optimizations that could cause bugs. - Avoid using
delay()andSerialfunctions. Functions that require timers (likedelay()) or other interrupts (likeSerial.print()) behave unreliably inside an ISR and should be avoided. Do your printing and delaying in the main loop.
Example: Toggling an LED with an Interrupt
Section titled “Example: Toggling an LED with an Interrupt”This sketch demonstrates how an interrupt can immediately respond to a button press to toggle an LED, even if the main loop is ‘busy’ in a long delay.
/* Interrupt Example: Responsive LED Toggle
This sketch demonstrates how an interrupt provides immediate responsiveness. A button press on pin 2 will instantly toggle the built-in LED, even though the main loop is stuck in a long delay.
Circuit: - A pushbutton connected between pin 2 and GND.*/
// Pin definitionsconst int interruptPin = 2;const int ledPin = LED_BUILTIN;
// This variable is shared between the ISR and the main loop, so it must be volatile.volatile bool ledState = LOW;
void setup() { // Set up the LED pin as an output pinMode(ledPin, OUTPUT);
// Set up the interrupt pin as an input with a pull-up resistor pinMode(interruptPin, INPUT_PULLUP);
// Attach the interrupt. // digitalPinToInterrupt() makes the code portable. // We trigger on the FALLING edge (when the button is pressed). attachInterrupt(digitalPinToInterrupt(interruptPin), toggleLed, FALLING);
Serial.begin(9600); Serial.println("System ready. Press the button.");}
void loop() { // The main loop can be doing other things. // We simulate a long, blocking task here. Serial.println("Main loop is busy..."); delay(5000);}
// --- Interrupt Service Routine (ISR) ---// This function is called automatically when the button is pressed.// It must be short, fast, and have a void return type with no parameters.void toggleLed() { // An ISR should do minimal work. Here, we just flip the state. ledState = !ledState; digitalWrite(ledPin, ledState);}Common Mistakes and Debugging
Section titled “Common Mistakes and Debugging”- Forgetting
volatile: If your shared variable isn’t updating correctly, you likely forgot to declare it asvolatile. - Long ISRs: If your program freezes or behaves erratically when the interrupt triggers, your ISR is probably doing too much work. Move
Serialcalls,delay(), or complex calculations into the mainloop()and use the ISR only to set a flag. - Button Bouncing: Mechanical buttons can physically ‘bounce’ when pressed, creating multiple high/low transitions in a few microseconds. This can cause your ISR to trigger multiple times for a single press. This is called ‘bouncing’. Solutions include adding a small capacitor to the circuit (hardware debouncing) or adding logic to ignore rapid triggers (software debouncing).