Interrupts in Arduino allow you to execute specific pieces of code immediately when a particular event occurs, regardless of what the main program is doing.
They are essential for creating responsive and time-sensitive applications.
This tutorial explains how to use interrupts in Arduino and demonstrates practical examples.
Table of Contents
1. What Are Interrupts in Arduino?
Interrupts are mechanisms that pause the main program flow and execute a specific piece of code (an interrupt service routine, or ISR) when a defined event occurs. Once the ISR is complete, the main program resumes.
2. How Do Interrupts Work?
- Trigger an Event: An interrupt is triggered by hardware events such as a change in pin state or a timer overflow.
- Execute ISR: The ISR runs immediately and completes as quickly as possible.
- Resume Main Code: After the ISR finishes, the main program continues.
3. Types of Interrupts
3.1 External Interrupts
- Triggered by events on specific external pins (INT0 and INT1 on most Arduino boards).
- Use attachInterrupt() to configure them.
Available Interrupt Pins (Common Boards)
Board | Pins |
---|---|
Arduino Uno | 2 (INT0), 3 (INT1) |
Arduino Mega | 2, 3, 18, 19, 20, 21 |
Arduino Nano | 2, 3 |
3.2 Pin Change Interrupts
- Triggered by changes on any GPIO pin.
- Requires the EnableInterrupt library for easy implementation.
4. Functions for Interrupts
4.1 attachInterrupt()
- Configures an external interrupt.
- Syntax:
attachInterrupt(digitalPinToInterrupt(pin), ISR, mode);
- pin: The pin number (use digitalPinToInterrupt(pin) to convert it to the interrupt number).
- ISR: The interrupt service routine function to execute.
- mode: Trigger condition (LOW, CHANGE, RISING, FALLING).
4.2 detachInterrupt()
- Disables an interrupt.
- Syntax:
detachInterrupt(digitalPinToInterrupt(pin));
5. Practical Examples
5.1 Button Press Interrupt
Use an interrupt to toggle an LED when a button is pressed.
const int buttonPin = 2; // Interrupt pin const int ledPin = 13; // LED pin volatile bool ledState = false; void setup() { pinMode(buttonPin, INPUT_PULLUP); // Configure button pin with pull-up resistor pinMode(ledPin, OUTPUT); // Configure LED pin attachInterrupt(digitalPinToInterrupt(buttonPin), toggleLED, FALLING); } void loop() { digitalWrite(ledPin, ledState); // Set LED state } // ISR to toggle LED state void toggleLED() { ledState = !ledState; }
5.2 Pulse Counting
Count the number of pulses received on a pin (e.g., from a rotary encoder or sensor).
const int pulsePin = 2; // Interrupt pin volatile int pulseCount = 0; void setup() { Serial.begin(9600); pinMode(pulsePin, INPUT_PULLUP); // Configure pin with pull-up resistor attachInterrupt(digitalPinToInterrupt(pulsePin), countPulse, RISING); } void loop() { Serial.print("Pulse Count: "); Serial.println(pulseCount); delay(1000); // Print every second } // ISR to increment pulse count void countPulse() { pulseCount++; }
5.3 Toggle LED Using a Timer Interrupt
Use a timer interrupt to toggle an LED periodically. This requires the TimerOne library.
- Install the TimerOne Library:
- Go to Tools > Manage Libraries.
- Search for “TimerOne” and install it.
- Code Example:
#include <TimerOne.h> const int ledPin = 13; // LED pin volatile bool ledState = false; void setup() { pinMode(ledPin, OUTPUT); Timer1.initialize(1000000); // Set timer to 1 second (1,000,000 microseconds) Timer1.attachInterrupt(toggleLED); // Attach interrupt } void loop() { digitalWrite(ledPin, ledState); // Set LED state } // ISR to toggle LED state void toggleLED() { ledState = !ledState; }
5.4 Random LED Blinking Using Interrupt
Use interrupts to randomly blink LEDs when triggered by a button press.
const int buttonPin = 2; // Interrupt pin const int ledPins[] = {3, 4, 5}; // LED pins const int numLeds = 3; void setup() { pinMode(buttonPin, INPUT_PULLUP); for (int i = 0; i < numLeds; i++) { pinMode(ledPins[i], OUTPUT); } attachInterrupt(digitalPinToInterrupt(buttonPin), randomBlink, FALLING); } void loop() { // Main loop does nothing; all work is handled in the ISR } // ISR to randomly blink an LED void randomBlink() { int randomLed = random(0, numLeds); digitalWrite(ledPins[randomLed], HIGH); delay(200); digitalWrite(ledPins[randomLed], LOW); }
6. Best Practices for Using Interrupts
- Keep ISRs Short:
- Avoid delays or complex operations in ISRs. Only set flags or increment counters.
void ISR() { // Avoid using Serial.print or delay() here flag = true; // Use flags for further processing in the main loop }
- Disable Unused Interrupts:
- Use detachInterrupt() to disable interrupts when no longer needed.
- Minimize Shared Resources:
- Avoid accessing variables shared between ISRs and the main program. Use volatile for shared variables.
- Debounce Buttons:
- Add debouncing logic to prevent multiple triggers from button noise.
- Test Interrupt Priority:
- Understand that some interrupts may take priority over others. Ensure critical tasks are prioritized.
- Use External Pull-Up Resistors When Necessary:
- Ensure clean signal transitions for stable interrupt triggers.
Conclusion
Interrupts in Arduino are powerful tools for creating responsive and real-time applications.
They allow you to handle events like button presses, sensor pulses, or timer overflows with minimal delay.
By following the examples and best practices in this tutorial, you can confidently use interrupts in your Arduino projects.
For more details, visit the official Arduino reference.