# External and Pin-change Interrupts — Arduino & Embedded C

Source: https://www.geekswithgeeks.com/en/arduino-embedded-c/interrupts

> attachInterrupt, short ISRs and shared data.

## Keep ISRs short and data safe

An **interrupt** makes the CPU pause `loop()`, run an **interrupt service routine (ISR)** and then resume. On the Uno R3, **external interrupts** are available on pins 2 and 3 through `attachInterrupt(digitalPinToInterrupt(pin), isr, mode)` with modes such as `RISING`, `FALLING` or `CHANGE`; the ATmega328P also has **pin-change interrupts** on most pins, configured through registers (PCICR, PCMSK0-2) or a library. Other boards allow interrupts on most pins; check your board's documentation. Rules: keep ISRs **short** (set a flag or count, do the work in `loop()`), avoid `delay()` and `Serial` printing inside them, and declare shared variables **`volatile`**. On an 8-bit MCU, reading a multi-byte variable takes several instructions, so an interrupt can change it halfway: copy it with interrupts briefly disabled (**atomic access**).

## Reacting instantly, sleeping the rest of the time

Interrupts let hardware events pause the main loop; timers keep time without the CPU; sleep modes save energy.

![Three ideas: interrupts, hardware timers, sleep modes.](assets/figures/arduino-embedded-c/section-7-map.svg) — Figure 7.1 — Event, interrupt service routine and back to the main loop.

## Counting pulses safely

Uno R3: pulse source (e.g. a sensor output) on pin 2, sharing GND.

```cpp
const uint8_t PULSE_PIN = 2;          // external interrupt pin on Uno R3
volatile uint32_t pulseCount = 0;      // shared with the ISR

void onPulse() {
  pulseCount++;                        // short: just count
}

void setup() {
  Serial.begin(115200);
  pinMode(PULSE_PIN, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(PULSE_PIN), onPulse, FALLING);
}

void loop() {
  static unsigned long last = 0;
  if (millis() - last >= 1000) {
    last = millis();
    noInterrupts();                    // 32-bit read is not atomic on AVR
    uint32_t count = pulseCount;
    pulseCount = 0;
    interrupts();
    Serial.print(F("Pulses/s: "));
    Serial.println(count);
  }
}
```

## A doorbell

Polling is checking the front door every few seconds; an interrupt is a doorbell. When it rings you note who came and go back to what you were doing, rather than holding a long conversation on the doorstep.

**Quiz:** Why should a variable shared between an ISR and loop() be declared volatile?

- [ ] So it becomes atomic automatically
- [ ] So it is stored in EEPROM
- [ ] So the ISR runs faster
- [x] So the compiler re-reads it from memory instead of using a cached copy

*Answer:* So the compiler re-reads it from memory instead of using a cached copy. volatile prevents caching; atomic access still needs interrupts disabled for multi-byte values on AVR.
