Interrupts and Shared State
Critical sections, Mutex<RefCell<T>>, and why static mut is banned.
An interrupt handler is a second thread of execution. Sharing data with main needs the same
discipline as any concurrency problem β and static mut is not it (itβs a hard error in
Rust 2024).
The classic pattern:
static LED: Mutex<RefCell<Option<LedPin>>> = Mutex::new(RefCell::new(None));
#[interrupt]
fn TIM2() {
critical_section::with(|cs| {
if let Some(led) = LED.borrow(cs).borrow_mut().as_mut() {
led.toggle();
}
});
}
critical_section::with disables interrupts for the duration, and the cs token is proof β
you cannot borrow the Mutex without one. The safety argument is encoded in the types.
Keep handlers short. Set a flag, push to a queue, and do the real work in main. A long
interrupt handler starves everything else on the chip.
If this state-sharing dance gets tedious, RTIC does it for you at compile time with no runtime cost.