Zbotic Logo Zbotic Logo
  • Home
  • Shop
  • Sale
  • 3D Print Service
  • PCB Service
  • B2B
  • Blogs
  • Contact Us
0 0

View Wishlist Add all to cart

0 0
0 Shopping Cart
Shopping cart (0)
Subtotal: ₹0.00

View cartCheckout

  • Shop
  • About Us
  • Contact Us
  • Reseller
  • Blogs
020 69134444
1800 209 0998
[email protected]
Help Desk
Facebook Twitter Instagram Linkedin YouTube
Zbotic Logo Zbotic Logo
0 0

View Wishlist Add all to cart

0 0
0 Shopping Cart
Shopping cart (0)
Subtotal: ₹0.00

View cartCheckout

All departments
  • 3D Print Service
  • 3D Printer
  • Batteries & Chargers
  • Development Boards
  • Drone Parts
  • EBike parts
  • Sensor Modules
  • Electronic Components
  • Electronic Modules
  • IoT and Wireless
  • Mechanical Parts and Workbench Tools
  • Motors & Drivers & Pumps & Actuators
  • DIY and Robot Kits
  • Show more
  • Home
  • Shop
  • Sale
  • 3D Print Service
  • PCB Service
  • B2B
  • Blogs
  • Contact Us
Return to previous page
Home Arduino & Microcontrollers

Arduino Interrupt Pins: External and Timer Interrupts Explained

Arduino Interrupt Pins: External and Timer Interrupts Explained

March 11, 2026 /Posted byJayesh Jain / 0

Understanding Arduino interrupts is the key to writing truly responsive embedded code. Without interrupts, your Arduino must constantly poll for events in the main loop — meaning it can miss brief signals, introduce latency, or waste CPU cycles. This arduino interrupts tutorial covers everything: which pins support external interrupts, how to use pin-change interrupts, how timer interrupts work, and best practices for writing safe interrupt service routines that keep your projects reliable.

Table of Contents

  • What Are Interrupts and Why Do They Matter?
  • External Interrupts: INT0 and INT1
  • Pin-Change Interrupts (PCINT)
  • Timer Interrupts
  • ISR Best Practices
  • Common Pitfalls and Debugging
  • Frequently Asked Questions

What Are Interrupts and Why Do They Matter?

An interrupt is a hardware signal that tells the CPU to stop whatever it is doing, save its current state, and execute a special function called an Interrupt Service Routine (ISR). When the ISR finishes, the CPU resumes normal execution exactly where it left off.

Consider a button that needs to be detected instantly. If you poll the button in loop(), you might miss a press that lasts only 50 ms while the loop is busy with other tasks. An interrupt fires within microseconds of the button press, regardless of what the main loop is doing.

Interrupts are essential for:

  • Detecting fast digital pulses (rotary encoders, IR receivers)
  • Implementing precise timing without polling
  • Receiving UART data without a buffer overflow
  • Waking the MCU from sleep on an external event
  • Measuring frequency or pulse width with high accuracy
Recommended: Arduino Uno R3 Beginners Kit — The Arduino Uno’s ATmega328P is the ideal platform for learning interrupts. It includes all the components needed to wire up interrupt-driven buttons, encoders, and sensors as you follow along with this tutorial.

External Interrupts: INT0 and INT1

The ATmega328P (used in the Arduino Uno and Nano) has two dedicated hardware external interrupt pins:

  • INT0 → Digital Pin 2 (D2)
  • INT1 → Digital Pin 3 (D3)

The Arduino Mega has more: INT0–INT5 on pins 2, 3, 21, 20, 19, 18 respectively.

Trigger Modes

External interrupts can be triggered on four conditions:

  • LOW — fires continuously while pin is LOW (use carefully — can cause ISR to fire in a loop)
  • CHANGE — fires on any transition (LOW→HIGH or HIGH→LOW)
  • FALLING — fires when pin goes HIGH→LOW
  • RISING — fires when pin goes LOW→HIGH

attachInterrupt() Syntax

void setup() {
  pinMode(2, INPUT_PULLUP);  // Enable internal pull-up for button
  attachInterrupt(
    digitalPinToInterrupt(2),  // Convert pin number to interrupt number
    buttonISR,                  // ISR function name
    FALLING                     // Trigger on falling edge (button press)
  );
}

void buttonISR() {
  // Keep this short!
  buttonPressed = true;
}

void loop() {
  if (buttonPressed) {
    buttonPressed = false;
    // Handle button press here
  }
}

Always use digitalPinToInterrupt(pin) rather than hardcoding the interrupt number. On the Uno, INT0 = interrupt 0 and INT1 = interrupt 1, but on the Mega the mapping is different. Using digitalPinToInterrupt() makes your code portable.

Volatile Variables

Variables shared between an ISR and the main code must be declared volatile. This tells the compiler not to cache the variable in a register — the main loop must always read it from RAM where the ISR may have modified it:

volatile bool buttonPressed = false;
volatile unsigned long pulseCount = 0;

Debouncing in Interrupt Context

Mechanical buttons bounce — a single press generates multiple transitions within 5–20 ms. A simple software debounce inside the ISR:

volatile unsigned long lastInterruptTime = 0;

void buttonISR() {
  unsigned long now = millis();
  if (now - lastInterruptTime > 50) {  // 50ms debounce
    buttonPressed = true;
    lastInterruptTime = now;
  }
}

Note: millis() works inside ISRs on AVR Arduino boards since Timer 0 ISR updates the millis counter independently.

Recommended: Arduino Mega 2560 R3 Board — The Mega 2560 provides 6 external interrupt pins (INT0–INT5) on pins 2, 3, 18, 19, 20, 21 — perfect for projects that need to track multiple interrupt sources simultaneously, such as multi-axis encoder systems.

Pin-Change Interrupts (PCINT)

External interrupts on D2 and D3 are hardware-dedicated and highly capable, but you are limited to just two pins. When you need interrupt capability on more pins, use Pin-Change Interrupts (PCINT).

On the ATmega328P, ALL digital pins support pin-change interrupts. They are grouped into three banks:

  • PCINT0 (PCICR bit 0) — Pins D8–D13
  • PCINT1 (PCICR bit 1) — Pins A0–A5
  • PCINT2 (PCICR bit 2) — Pins D0–D7

The limitation: each bank shares a single ISR vector. If you have three buttons on D8, D9, and D10, they all call the same ISR — you must manually determine which pin changed by comparing to a saved state.

Direct Register Approach

volatile uint8_t lastPIND;

void setup() {
  PCICR |= (1 << PCIE2);      // Enable PCINT2 (D0–D7)
  PCMSK2 |= (1 << PCINT20);   // Enable interrupt on D4
  PCMSK2 |= (1 << PCINT21);   // Enable interrupt on D5
  lastPIND = PIND;
}

ISR(PCINT2_vect) {
  uint8_t changed = PIND ^ lastPIND;
  if (changed & (1 << 4)) {
    // D4 changed
  }
  if (changed & (1 << 5)) {
    // D5 changed
  }
  lastPIND = PIND;
}

EnableInterrupt Library

For a friendlier interface, the EnableInterrupt library extends Arduino’s attachInterrupt() syntax to work on all pins:

#include <EnableInterrupt.h>

enableInterrupt(4, pin4ISR, CHANGE);
enableInterrupt(5, pin5ISR, FALLING);

The library handles the PCINT bank management automatically.

Timer Interrupts

Timer interrupts fire based on the internal timer counters, not external pin events. They are perfect for generating precise periodic callbacks without blocking the main loop.

The ATmega328P has three timers:

  • Timer 0 — 8-bit, used by Arduino’s millis(), micros(), and PWM on D5/D6. Avoid modifying.
  • Timer 1 — 16-bit, used by PWM on D9/D10. Most flexible for custom timer interrupts.
  • Timer 2 — 8-bit, used by PWM on D11/D3. Can be used for precise intervals.

Timer1 Compare Match Interrupt

The following example fires an ISR every 1 ms using Timer 1 in CTC (Clear Timer on Compare) mode:

#include <avr/interrupt.h>

void setup() {
  cli();  // Disable interrupts during setup
  
  // Timer 1 CTC mode (WGM12 = 1)
  TCCR1A = 0;
  TCCR1B = 0;
  TCNT1 = 0;
  
  // Compare match at 1 ms: 16000000 / (prescaler * freq) - 1
  // Prescaler 64, target 1000 Hz: 16000000 / (64 * 1000) - 1 = 249
  OCR1A = 249;
  
  TCCR1B |= (1 << WGM12);   // CTC mode
  TCCR1B |= (1 << CS11) | (1 << CS10);  // Prescaler 64
  TIMSK1 |= (1 << OCIE1A);  // Enable compare match A interrupt
  
  sei();  // Re-enable interrupts
}

ISR(TIMER1_COMPA_vect) {
  // This fires every 1 ms — ideal for scheduler ticks, PID loops, etc.
  millisCounter++;
}

MsTimer2 and TimerOne Libraries

For easier timer interrupt setup, use:

  • TimerOne library — Controls Timer 1 with simple Timer1.initialize() and Timer1.attachInterrupt() API
  • MsTimer2 library — Controls Timer 2 for millisecond-precision callbacks
#include <TimerOne.h>

void setup() {
  Timer1.initialize(1000);  // 1000 microseconds = 1 ms
  Timer1.attachInterrupt(timerISR);
}

void timerISR() {
  // Called every 1 ms
}
Recommended: Arduino Frequency Counter Kit with 16×2 LCD Display — A practical project that uses external interrupts to count input pulses and display frequency on an LCD — ideal for seeing interrupts working in a real application.

ISR Best Practices

ISRs run in a privileged context with all other interrupts disabled (by default on AVR). Poor ISR design causes missed interrupts, timing glitches, and hard-to-debug crashes.

Keep ISRs Short

The ISR should do the absolute minimum: set a flag, store a value, or increment a counter. Complex operations, Serial.print(), String allocation, and function calls that themselves use interrupts must never appear in an ISR.

No Blocking Calls

Never call delay(), Serial.print(), or any blocking function inside an ISR. These functions either use interrupts internally (and will hang since global interrupts are disabled in the ISR) or waste time that delays normal interrupt processing.

Atomic Access for Multi-Byte Variables

Reading a 16-bit or 32-bit variable that an ISR modifies can cause a race condition. On an 8-bit processor, reading a 32-bit integer takes multiple cycles — the ISR could fire halfway through and corrupt the read. Use atomic read:

#include <util/atomic.h>

unsigned long safeCount;
ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
  safeCount = isrCounter;  // Read 32-bit variable atomically
}

Re-entrant Safety

AVR Arduino ISRs are non-re-entrant by default (global interrupt flag cleared on entry). Do not enable global interrupts inside an ISR (sei()) unless you have a specific reason and understand the consequences fully.

Common Pitfalls and Debugging

Forgetting volatile

The most common bug. If a variable shared with an ISR is not volatile, the compiler may optimise it into a register and the main loop will never see the ISR’s updates. Always declare shared variables volatile.

Interrupt Flooding

If your interrupt fires too frequently (e.g., using LOW trigger mode or reading a bouncing button), the ISR can consume all CPU time and starve the main loop. The program appears frozen. Add debounce logic or use FALLING/RISING instead of CHANGE/LOW.

Conflicts with Libraries

Many Arduino libraries use interrupts internally: Servo, SoftwareSerial, Wire (I2C), and SD all use timer or pin-change interrupts. Using the same timer for a custom interrupt and a library that also uses it will cause conflicts. TimerOne uses Timer 1 — so will conflict with Servo (which also uses Timer 1 on Uno).

Using Serial Inside ISR

Serial communication relies on its own interrupt (UDRE interrupt for TX, RXCI for RX). Calling Serial.print() inside an ISR will hang because the Serial TX interrupt cannot fire while the global interrupt flag is cleared in your ISR.

Recommended: Multifunction Shield for Arduino Uno / Leonardo — Features four buttons, a 4-digit 7-segment display, and a buzzer — a ready-made platform for practising external interrupt-driven button handling and timer interrupt display refreshing without any wiring.

Frequently Asked Questions

How many interrupt pins does the Arduino Uno have?

The Arduino Uno has 2 dedicated external interrupt pins: D2 (INT0) and D3 (INT1). However, using pin-change interrupts, ALL digital and analog pins can trigger an interrupt — but they share ISR vectors per bank and require more manual coding to identify which pin triggered.

Can I use interrupts and delay() together?

Yes, but carefully. delay() in the main loop() will not block interrupts — external interrupts and timer interrupts will still fire during a delay(). The problem is the reverse: calling delay() inside an ISR will hang the program because the Timer 0 interrupt (which increments millis) cannot fire while global interrupts are disabled inside your ISR.

What is the difference between attachInterrupt() and direct register manipulation?

attachInterrupt() is the high-level Arduino API — easy to use, portable across boards. Direct register manipulation (writing to EIMSK, EICRA, etc.) gives you more control, slightly less overhead, and access to features not exposed through the API. For most projects, attachInterrupt() is the right choice.

My interrupt fires randomly even without any input. Why?

A floating input pin can pick up electromagnetic noise and trigger spurious interrupts. Always connect an appropriate pull-up or pull-down resistor (or enable the internal pull-up with pinMode(pin, INPUT_PULLUP)) on interrupt pins. Also add a 100 nF capacitor from the pin to GND for additional noise filtering.

Can I have an interrupt inside an interrupt?

By default, no — the AVR automatically clears the global interrupt enable flag (I-bit in SREG) when entering an ISR. You can re-enable interrupts inside an ISR with sei() to allow nested interrupts, but this requires careful design to avoid stack overflow and re-entrant bugs. It is almost never necessary in typical Arduino projects.

Mastering interrupts transforms your Arduino projects from simple polling loops into truly responsive, professional-grade embedded systems. External interrupts give you instant reaction to hardware events; timer interrupts enable precise scheduling without blocking delays. With the best practices outlined here — short ISRs, volatile variables, atomic access, and careful library conflict avoidance — you will build reliable interrupt-driven applications that perform exactly as designed.

Shop Arduino Boards and Accessories at Zbotic →

Tags: Arduino interrupts, arduino tutorial, attachInterrupt, ISR, pin change interrupt, timer interrupt
Share Post
  • Facebook
  • Linkedin
  • Whatsapp
PlatformIO IDE Setup: The Bett...
blog platformio ide setup the better arduino development environment 594648
blog debugging arduino code serial monitor logic analyzer and advanced tips 594652
Debugging Arduino Code: Serial...

Related posts

Svg%3E
Read more

Arduino Batch Programming: Flash Multiple Boards Quickly

April 1, 2026 0
Table of Contents Introduction Components and Hardware Setup Wiring Diagram and Connections Complete Code with Explanation Customization and Improvements Troubleshooting... Continue reading
Svg%3E
Read more

Arduino Based Radar System with Ultrasonic Sensor

April 1, 2026 0
Table of Contents Introduction Components and Hardware Setup Wiring Diagram and Connections Complete Code with Explanation Customization and Improvements Troubleshooting... Continue reading
Svg%3E
Read more

Arduino Automatic Plant Monitor: Sunlight, Moisture, Temperature

April 1, 2026 0
Table of Contents Introduction Components and Hardware Setup Wiring Diagram and Connections Complete Code with Explanation Customization and Improvements Troubleshooting... Continue reading
Svg%3E
Read more

Arduino Lie Detector: GSR Sensor Polygraph Project

April 1, 2026 0
Table of Contents Introduction Components and Hardware Setup Wiring Diagram and Connections Complete Code with Explanation Customization and Improvements Troubleshooting... Continue reading
Svg%3E
Read more

Arduino Metal Detector: Build a Treasure Finder

April 1, 2026 0
Table of Contents Introduction Components and Hardware Setup Wiring Diagram and Connections Complete Code with Explanation Customization and Improvements Troubleshooting... Continue reading

Add comment Cancel reply

Your email address will not be published. Required fields are marked

Facebook Twitter Instagram Pinterest Linkedin Youtube

Get the latest deals and more.

Download on Google Play Download on the App Store

Call us: 020 69134444 / 1800 209 0998

Monday - Saturday 09:30 AM - 06:00 PM
For Technical Supports Email: [email protected]
For Sales / Enquiries Email: [email protected]

  • My Account

    • Cart

    • Wishlist

    • Checkout

    • My Orders

    • Track Order

    • My Account

  • Information

    • FAQs

    • Blogs

    • Career

    • About Us

    • Contact Us

    • Payment Options

  • Policies

    • Privacy Policy

    • Terms & Conditions

    • GST Input Tax Credit

    • Shipping Return Policy

    • E-Waste Collection Points

    • Our Sitemap

© Zbotic.in is registered trademark of Moxie Supply Pvt Ltd – All Rights Reserved
Login
Use Phone Number
Use Email Address
Not a member yet? Register Now
Reset Password
Use Phone Number
Use Email Address
Register
Already a member? Login Now