Interrupts

~15 min

Polling vs Interrupts

Polling

With polling, the CPU constantly checks the state of a peripheral in a loop. This is simple to implement but wastes CPU cycles — the processor is busy-waiting even when nothing is happening.

#![allow(unused)]
fn main() {
loop {
    if button.is_low() {
        // react to button press
    }
    // CPU is busy-waiting here, doing nothing useful
}
}

Interrupts

With interrupts, the hardware notifies the CPU when an event occurs. The CPU can sleep or do other work in the meantime, only waking when needed. This is more efficient but requires additional setup.

#![allow(unused)]
fn main() {
loop {
    // CPU can sleep or do other work
    // it is idle until an event occurs
}

// Hardware triggers this handler automatically
#[handler]
fn gpio_handler() {
    // react to the event
}
}

Components of Interrupt Code

Interrupt code is formed of three components:

  1. Global Shared Data — Any data that needs to be shared between the main thread and the interrupt handler
  2. Interrupt Setup — Configuring the interrupt per peripheral (what event to listen for, enabling it, moving data to shared state)
  3. Interrupt Service Routine (ISR) — The code that reacts to the interrupt event
// 1. Global Shared Data
static SHARED: Mutex<RefCell<Option<Input>>> =
    Mutex::new(RefCell::new(None));

fn main() {
    // ... peripheral setup ...

    // 2. Interrupt Setup
    button.listen(Event::FallingEdge);
    critical_section::with(|cs| {
        SHARED.borrow_ref_mut(cs).replace(button);
    });
}

// 3. Interrupt Service Routine
#[handler]
fn gpio_handler() {
    critical_section::with(|cs| {
        // handle event, clear interrupt
    });
}

Setup Happens in the Configure Stage

Setup entails three steps:

  1. Configuring the interrupt — What do we want to listen to?

    • Edge events (rising, falling, any)
    • Level events (high, low)
  2. Enabling the interrupt — Allowing the interrupt events to go through

    • Peripheral-level enable
    • Interrupt controller enable
  3. Configuring global shared data — Setting up shared state

    • Mutex<RefCell<Option<T>>> pattern
    • Moving peripherals into global statics via critical_section