Interrupt Essentials
~10 min slides
Polling vs. Interrupts
POLLING: INTERRUPTS:
┌──────────────┐ ┌──────────────┐
│ loop { │ │ loop { │
│ if pressed │ ← CPU busy │ // sleep │ ← CPU idle
│ do_thing │ checking │ // or do │ until event
│ } │ constantly │ // other │
│ } │ │ // work │
└──────────────┘ └──────┬───────┘
│ INTERRUPT!
┌──────▼───────┐
│ handler() { │ ← CPU wakes up
│ do_thing │ handles event
│ } │ goes back
└──────────────┘
GPIO Interrupt Triggers
When should the interrupt fire?
| Trigger | When It Fires |
|---|---|
Event::FallingEdge | Pin goes from HIGH → LOW (button press with pull-up) |
Event::RisingEdge | Pin goes from LOW → HIGH (button release with pull-up) |
Event::AnyEdge | Any transition — both press and release |
The Interrupt Pattern in Rust
Interrupts introduce a challenge: the interrupt handler and your main code need to share state. In Rust, this means dealing with Send, Sync, and interior mutability.
The Standard Pattern
#![allow(unused)] fn main() { use core::cell::RefCell; use critical_section::Mutex; // 1. Shared state: wrap in Mutex<RefCell<Option<T>>> static BUTTON: Mutex<RefCell<Option<Input>>> = Mutex::new(RefCell::new(None)); // 2. In main: move the peripheral into the shared state critical_section::with(|cs| { BUTTON.borrow_ref_mut(cs).replace(button); }); // 3. The interrupt handler #[handler] fn gpio_handler() { critical_section::with(|cs| { let mut button = BUTTON.borrow_ref_mut(cs); if let Some(button) = button.as_mut() { // Handle the interrupt button.clear_interrupt(); } }); } }
Key Pieces
| Piece | Purpose |
|---|---|
Mutex<RefCell<Option<T>>> | Safe shared access between main code and interrupt handler |
critical_section::with | Temporarily disables interrupts to safely access shared state |
#[handler] | Marks a function as an interrupt handler (esp-hal attribute) |
clear_interrupt() | Must be called — tells the hardware you've handled the event |
In esp-hal 1.0, interrupts are behind the `unstable` feature flag. Make sure your `Cargo.toml` includes:
```toml
[dependencies]
esp-hal = { version = "1.0", features = ["unstable"] }
```admonish key-concept title="Keep handlers short"
Interrupt handlers should do as little as possible — set a flag, clear the interrupt, return. Do the heavy work in your main loop. This prevents blocking other interrupts and keeps the system responsive.