Interrupts are a powerful feature of microcontrollers that allow you to handle events asynchronously. Unlike polling, which continuously checks for events, interrupts respond immediately when a specific event occurs, such as a button press or a timer overflow. This tutorial will guide you through understanding and using interrupts with Arduino.
What You Will Need
- Arduino Uno (or a compatible board)
- Push button
- 10k-ohm resistor (for pull-down configuration)
- LED and 220-ohm resistor (optional)
- Breadboard and jumper wires
- Arduino IDE installed on your computer
Step 1: What Are Interrupts?
An interrupt temporarily halts the main program’s execution to handle a specific event. Once the event is processed, the program resumes where it left off. Interrupts are managed using special functions called Interrupt Service Routines (ISRs).
Types of Interrupts in Arduino
- External Interrupts: Triggered by events on specific pins (e.g., pin 2 or 3 on Arduino Uno).
- Pin Change Interrupts: Triggered by a change on any digital pin.
- Timer Interrupts: Triggered by timer overflows or compare matches.
Step 2: Using External Interrupts
The Arduino Uno supports external interrupts on pins 2 and 3. You can configure these interrupts to trigger on:
- RISING: Signal goes from LOW to HIGH.
- FALLING: Signal goes from HIGH to LOW.
- CHANGE: Signal changes state (LOW to HIGH or HIGH to LOW).
- LOW: Signal remains LOW.
Example Code: Detecting a Button Press
This example toggles an LED when a button connected to pin 2 is pressed.
#define buttonPin 2 // Interrupt pin
#define ledPin 13 // Built-in LED
volatile bool ledState = false; // Shared variable between ISR and main code
void handleInterrupt() {
ledState = !ledState; // Toggle LED state
digitalWrite(ledPin, ledState);
}
void setup() {
pinMode(buttonPin, INPUT_PULLUP); // Enable internal pull-up resistor
pinMode(ledPin, OUTPUT);
attachInterrupt(digitalPinToInterrupt(buttonPin), handleInterrupt, FALLING); // Trigger on button press
}
void loop() {
// Main loop does nothing; interrupt handles the LED
}
Explanation
-
attachInterrupt(digitalPinToInterrupt(pin), ISR, mode)
: Configures the interrupt.-
pin
: The interrupt pin (e.g., pin 2 or 3 on Arduino Uno). -
ISR
: The Interrupt Service Routine to execute. -
mode
: The triggering condition (RISING
,FALLING
,CHANGE
, orLOW
).
-
-
digitalPinToInterrupt(pin)
: Converts a pin number to its interrupt number.
Step 3: Using Pin Change Interrupts
Pin change interrupts allow you to detect changes on any digital pin. This requires additional libraries, such as EnableInterrupt.
Example Code: Pin Change Interrupt
#include <EnableInterrupt.h>
#define pin 4 // Pin to monitor
void handlePinChange() {
Serial.println("Pin state changed!");
}
void setup() {
Serial.begin(9600);
enableInterrupt(pin, handlePinChange, CHANGE); // Trigger on any state change
}
void loop() {
// Main loop does nothing; interrupt handles the event
}
Library Installation
To use pin change interrupts, install the EnableInterrupt library via the Arduino Library Manager.
Step 4: Timer Interrupts
Timer interrupts are useful for scheduling tasks at precise intervals. Refer to the Arduino Timer Tutorial for details on configuring timer interrupts.
Example: Blinking an LED with Timer1 Interrupt
#define ledPin 13
void setup() {
pinMode(ledPin, OUTPUT);
// Configure Timer1
noInterrupts(); // Disable interrupts during setup
TCCR1A = 0;
TCCR1B = 0;
TCNT1 = 0;
OCR1A = 15624; // Compare match value for 1Hz (1-second interval)
TCCR1B |= (1 << WGM12); // CTC mode
TCCR1B |= (1 << CS12) | (1 << CS10); // 1024 prescaler
TIMSK1 |= (1 << OCIE1A); // Enable Timer1 compare interrupt
interrupts(); // Enable interrupts
}
ISR(TIMER1_COMPA_vect) {
digitalWrite(ledPin, !digitalRead(ledPin)); // Toggle LED
}
void loop() {
// Main loop does nothing; Timer1 handles the LED
}
Step 5: Best Practices for Using Interrupts
- Keep ISRs Short: Avoid delays or complex calculations in ISRs to ensure quick response.
- Avoid Serial Communication in ISRs: Serial functions may not work reliably inside an ISR.
-
Use
volatile
for Shared Variables: Mark variables shared between ISRs and the main program asvolatile
to prevent compiler optimization issues. - Debounce Inputs: Handle debounce logic in software or hardware for noisy signals like button presses.
-
Disable Interrupts During Critical Sections: Use
noInterrupts()
andinterrupts()
to protect critical code sections.
Applications of Interrupts
- Handling button presses without polling
- Reading rotary encoders
- Timing critical events (e.g., precise motor control)
- Scheduling periodic tasks
- Reacting to external signals (e.g., sensors, communication events)
Troubleshooting
- Interrupt not triggering: Ensure the correct pin and mode are configured.
- Unstable behavior: Debounce noisy signals and avoid long ISRs.
- Conflicts with libraries: Some libraries use interrupts internally (e.g., Servo, PWM). Ensure no conflict with your code.
Conclusion
Interrupts enable responsive and efficient handling of events in Arduino projects. By learning to use external, pin change, and timer interrupts, you can create robust and precise applications. Experiment with different types of interrupts to enhance your projects and optimize performance!