Arduino Interrupts Tutorial

Arduino Interrupts Tutorial

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

  1. Arduino Uno (or a compatible board)
  2. Push button
  3. 10k-ohm resistor (for pull-down configuration)
  4. LED and 220-ohm resistor (optional)
  5. Breadboard and jumper wires
  6. 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

  1. External Interrupts: Triggered by events on specific pins (e.g., pin 2 or 3 on Arduino Uno).
  2. Pin Change Interrupts: Triggered by a change on any digital pin.
  3. 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, or LOW).
  • 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

  1. Keep ISRs Short: Avoid delays or complex calculations in ISRs to ensure quick response.
  2. Avoid Serial Communication in ISRs: Serial functions may not work reliably inside an ISR.
  3. Use volatile for Shared Variables: Mark variables shared between ISRs and the main program as volatile to prevent compiler optimization issues.
  4. Debounce Inputs: Handle debounce logic in software or hardware for noisy signals like button presses.
  5. Disable Interrupts During Critical Sections: Use noInterrupts() and interrupts() to protect critical code sections.

Applications of Interrupts

  1. Handling button presses without polling
  2. Reading rotary encoders
  3. Timing critical events (e.g., precise motor control)
  4. Scheduling periodic tasks
  5. 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!

Leave a comment

Notice an Issue? Have a Suggestion?
If you encounter a problem or have an idea for a new feature, let us know! Report a problem or request a feature here.