esp_hal/interrupt/
mod.rs

1//! # Interrupt support
2//!
3//! ## Overview
4//! This module routes one or more peripheral interrupt sources to any one
5//! of the CPU’s peripheral interrupts.
6//!
7//! ## Configuration
8//! Usually peripheral drivers offer a mechanism to register your interrupt
9//! handler. e.g. the systimer offers `set_interrupt_handler` to register a
10//! handler for a specific alarm. Other drivers might take an interrupt handler
11//! as an optional parameter to their constructor.
12//!
13//! This is the preferred way to register handlers.
14//!
15//! There are additional ways to register interrupt handlers which are generally
16//! only meant to be used in very special situations (mostly internal to the HAL
17//! or the supporting libraries). Those are outside the scope of this
18//! documentation.
19//!
20//! It is even possible, but not recommended, to bind an interrupt directly to a
21//! CPU interrupt. This can offer lower latency, at the cost of more complexity
22//! in the interrupt handler. See the `direct_vectoring.rs` example
23//!
24//! We reserve a number of CPU interrupts, which cannot be used; see
25//! [`RESERVED_INTERRUPTS`].
26//!
27//! ## Examples
28//!
29//! ### Using the peripheral driver to register an interrupt handler
30//!
31//! ```rust, no_run
32#![doc = crate::before_snippet!()]
33//! let mut sw_int =
34//!     SoftwareInterruptControl::new(peripherals.SW_INTERRUPT);
35//! critical_section::with(|cs| {
36//!     sw_int
37//!         .software_interrupt0
38//!         .set_interrupt_handler(swint0_handler);
39//!     SWINT0
40//!         .borrow_ref_mut(cs)
41//!         .replace(sw_int.software_interrupt0);
42//! });
43//!
44//! critical_section::with(|cs| {
45//!     if let Some(swint) = SWINT0.borrow_ref(cs).as_ref(){
46//!         swint.raise();
47//!     }
48//! });
49//! #
50//! # loop {}
51//! # }
52//!
53//! # use core::cell::RefCell;
54//! #
55//! # use critical_section::Mutex;
56//! # use esp_hal::interrupt::software::{SoftwareInterrupt, SoftwareInterruptControl};
57//! # use esp_hal::interrupt::Priority;
58//! # use esp_hal::interrupt::InterruptHandler;
59//! #
60//! static SWINT0: Mutex<RefCell<Option<SoftwareInterrupt<0>>>> =
61//!     Mutex::new(RefCell::new(None));
62//!
63//! #[handler(priority = Priority::Priority1)]
64//! fn swint0_handler() {
65//!     println!("SW interrupt0");
66//!     critical_section::with(|cs| {
67//!         if let Some(swint) = SWINT0.borrow_ref(cs).as_ref() {
68//!             swint.reset();
69//!         }
70//!     });
71//! }
72//! ```
73
74use core::ops::BitAnd;
75
76#[cfg(riscv)]
77pub use self::riscv::*;
78#[cfg(xtensa)]
79pub use self::xtensa::*;
80
81#[cfg(riscv)]
82mod riscv;
83#[cfg(xtensa)]
84mod xtensa;
85
86pub mod software;
87
88#[unsafe(no_mangle)]
89extern "C" fn EspDefaultHandler(_interrupt: crate::peripherals::Interrupt) {
90    panic!("Unhandled interrupt: {:?}", _interrupt);
91}
92
93/// Default (unhandled) interrupt handler
94pub const DEFAULT_INTERRUPT_HANDLER: InterruptHandler = InterruptHandler::new(
95    unsafe { core::mem::transmute::<*const (), extern "C" fn()>(EspDefaultHandler as *const ()) },
96    Priority::min(),
97);
98
99/// Trait implemented by drivers which allow the user to set an
100/// [InterruptHandler]
101pub trait InterruptConfigurable: crate::private::Sealed {
102    #[cfg_attr(
103        not(multi_core),
104        doc = "Registers an interrupt handler for the peripheral."
105    )]
106    #[cfg_attr(
107        multi_core,
108        doc = "Registers an interrupt handler for the peripheral on the current core."
109    )]
110    #[doc = ""]
111    /// Note that this will replace any previously registered interrupt
112    /// handlers. Some peripherals offer a shared interrupt handler for
113    /// multiple purposes. It's the users duty to honor this.
114    ///
115    /// You can restore the default/unhandled interrupt handler by using
116    /// [DEFAULT_INTERRUPT_HANDLER]
117    fn set_interrupt_handler(&mut self, handler: InterruptHandler);
118}
119
120/// An interrupt handler
121#[cfg_attr(
122    multi_core,
123    doc = "**Note**: Interrupts are handled on the core they were setup on, if a driver is initialized on core 0, and moved to core 1, core 0 will still handle the interrupt."
124)]
125#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
126#[cfg_attr(feature = "defmt", derive(defmt::Format))]
127pub struct InterruptHandler {
128    f: extern "C" fn(),
129    prio: Priority,
130}
131
132impl InterruptHandler {
133    /// Creates a new [InterruptHandler] which will call the given function at
134    /// the given priority.
135    pub const fn new(f: extern "C" fn(), prio: Priority) -> Self {
136        Self { f, prio }
137    }
138
139    /// The function to be called
140    #[inline]
141    pub fn handler(&self) -> extern "C" fn() {
142        self.f
143    }
144
145    /// Priority to be used when registering the interrupt
146    #[inline]
147    pub fn priority(&self) -> Priority {
148        self.prio
149    }
150}
151
152#[cfg(large_intr_status)]
153const STATUS_WORDS: usize = 3;
154
155#[cfg(very_large_intr_status)]
156const STATUS_WORDS: usize = 4;
157
158#[cfg(not(any(large_intr_status, very_large_intr_status)))]
159const STATUS_WORDS: usize = 2;
160
161/// Representation of peripheral-interrupt status bits.
162#[derive(Clone, Copy, Default, Debug)]
163pub struct InterruptStatus {
164    status: [u32; STATUS_WORDS],
165}
166
167impl InterruptStatus {
168    const fn empty() -> Self {
169        InterruptStatus {
170            status: [0u32; STATUS_WORDS],
171        }
172    }
173
174    #[cfg(large_intr_status)]
175    const fn from(w0: u32, w1: u32, w2: u32) -> Self {
176        Self {
177            status: [w0, w1, w2],
178        }
179    }
180
181    #[cfg(very_large_intr_status)]
182    const fn from(w0: u32, w1: u32, w2: u32, w3: u32) -> Self {
183        Self {
184            status: [w0, w1, w2, w3],
185        }
186    }
187
188    #[cfg(not(any(large_intr_status, very_large_intr_status)))]
189    const fn from(w0: u32, w1: u32) -> Self {
190        Self { status: [w0, w1] }
191    }
192
193    /// Is the given interrupt bit set
194    pub fn is_set(&self, interrupt: u8) -> bool {
195        (self.status[interrupt as usize / 32] & (1 << (interrupt % 32))) != 0
196    }
197
198    /// Set the given interrupt status bit
199    pub fn set(&mut self, interrupt: u8) {
200        self.status[interrupt as usize / 32] |= 1 << (interrupt % 32);
201    }
202
203    /// Return an iterator over the set interrupt status bits
204    pub fn iterator(&self) -> InterruptStatusIterator {
205        InterruptStatusIterator {
206            status: *self,
207            idx: 0,
208        }
209    }
210}
211
212impl BitAnd for InterruptStatus {
213    type Output = InterruptStatus;
214
215    fn bitand(self, rhs: Self) -> Self::Output {
216        #[cfg(large_intr_status)]
217        return Self::Output {
218            status: [
219                self.status[0] & rhs.status[0],
220                self.status[1] & rhs.status[1],
221                self.status[2] & rhs.status[2],
222            ],
223        };
224
225        #[cfg(very_large_intr_status)]
226        return Self::Output {
227            status: [
228                self.status[0] & rhs.status[0],
229                self.status[1] & rhs.status[1],
230                self.status[2] & rhs.status[2],
231                self.status[3] & rhs.status[3],
232            ],
233        };
234
235        #[cfg(not(any(large_intr_status, very_large_intr_status)))]
236        return Self::Output {
237            status: [
238                self.status[0] & rhs.status[0],
239                self.status[1] & rhs.status[1],
240            ],
241        };
242    }
243}
244
245/// Iterator over set interrupt status bits
246pub struct InterruptStatusIterator {
247    status: InterruptStatus,
248    idx: usize,
249}
250
251impl Iterator for InterruptStatusIterator {
252    type Item = u8;
253
254    fn next(&mut self) -> Option<Self::Item> {
255        for i in self.idx..STATUS_WORDS {
256            if self.status.status[i] != 0 {
257                let bit = self.status.status[i].trailing_zeros();
258                self.idx = i;
259                self.status.status[i] &= !1 << bit;
260                return Some((bit + 32 * i as u32) as u8);
261            }
262        }
263        self.idx = usize::MAX;
264        None
265    }
266}