atsamd_hal/async_hal/interrupts.rs
1//! # Async interrupts
2//!
3//! This module provides APIs specific to working with interrupts in an async
4//! peripheral context.
5//!
6//! Asynchronous programming relies on tasks that can be paused and resumed
7//! without blocking the entire program. When an async task is waiting for a
8//! particular event, such as data from a peripheral, it enters a suspended
9//! state. It is crucial that the task is properly woken up when the expected
10//! event occurs to resume its execution.
11//!
12//! By having peripherals take interrupts, they can signal the occurrence of
13//! relevant events, effectively waking up the associated async tasks. This
14//! ensures that the async runtime can schedule and resume tasks in a timely
15//! manner, providing the responsiveness required in embedded systems.
16//!
17//! ## Typelevel and enum-level interrupts
18//!
19//! There are two main ways of representing interrupts in the HAL: either by
20//! using [`pac::Interrupt`], where each interrupt is represented as an enum
21//! variant, or by using the typelevel interrupts defined in this module. Each
22//! interrupt source *that is usable with async peripherals* is declared as a
23//! struct with the same name of the corresponsing [`pac::Interrupt`] variant.
24//! Therefore, two distinct traits are needed to perform basic tasks on
25//! interrupt types:
26//!
27//! * Use [`Interrupt`] when dealing with the typelevel interrupt types defined
28//! in this module;
29//! * Use [`InterruptExt`] when dealing with enum-level interrupt types defined
30//! in [`pac`].
31//!
32//! [`pac::Interrupt`]: crate::pac::Interrupt
33//! [`Interrupt`]: crate::async_hal::interrupts::Interrupt
34//! [`InterruptExt`]: crate::async_hal::interrupts::InterruptExt
35//! [`pac`]: crate::pac
36
37pub use crate::interrupt::*;
38
39use crate::typelevel::Sealed;
40use atsamd_hal_macros::hal_cfg;
41
42/// Marker trait indicating that an interrupt source has one binding and
43/// one handler.
44///
45/// May not be implemented outside of this HAL.
46pub trait SingleInterruptSource: Sealed {}
47
48/// Marker trait indicating that an interrupt source has multiple bindings and
49/// handlers.
50///
51/// May not be implemented outside of this HAL.
52pub trait MultipleInterruptSources: Sealed {}
53
54macro_rules! declare_interrupts {
55 ($($(#[$cfg:meta])* $irqs:ident),* $(,)?) => {
56 $(
57 $(#[$cfg])*
58 #[allow(non_camel_case_types)]
59 #[doc=stringify!($irqs)]
60 #[doc=" typelevel interrupt."]
61 pub enum $irqs {}
62
63 $(#[$cfg])*
64 impl $crate::typelevel::Sealed for $irqs{}
65
66 $(#[$cfg])*
67 impl $crate::async_hal::interrupts::Interrupt for $irqs {
68 const IRQ: crate::pac::Interrupt = crate::pac::Interrupt::$irqs;
69 }
70
71 $(#[$cfg])*
72 impl $crate::async_hal::interrupts::SingleInterruptSource for $irqs {}
73 )*
74 }
75}
76
77/// Useful when we need to bind multiple interrupt sources to the same handler.
78/// Calling the `InterruptSource` methods on the created struct will act on all
79/// interrupt sources at once.
80// Lint allowed because the macro is unused for thumbv6 devices.
81#[allow(unused_macros)]
82macro_rules! declare_multiple_interrupts {
83 ($(#[$cfg:meta])* $name:ident: [ $($irq:ident),+ $(,)? ]) => {
84 ::paste::paste! {
85 $(#[$cfg])*
86 pub enum $name {}
87
88 $(#[$cfg])*
89 impl $crate::typelevel::Sealed for $name {}
90
91 $(#[$cfg])*
92 impl $crate::async_hal::interrupts::InterruptSource for $name {
93 unsafe fn enable() {
94 unsafe {
95 $($crate::pac::Interrupt::$irq.enable();)+
96 }
97 }
98
99 fn disable() {
100 $($crate::pac::Interrupt::$irq.disable();)+
101 }
102
103 fn unpend() {
104 $($crate::pac::Interrupt::$irq.unpend();)+
105 }
106
107 fn set_priority(prio: $crate::async_hal::interrupts::Priority){
108 $($crate::pac::Interrupt::$irq.set_priority(prio);)+
109 }
110 }
111
112 $(#[$cfg])*
113 impl $crate::async_hal::interrupts::MultipleInterruptSources for $name {}
114 }
115 };
116}
117
118// ---------- DMAC Interrupts ---------- //
119#[cfg(feature = "dma")]
120#[hal_cfg("dmac-d5x")]
121declare_multiple_interrupts!(DMAC: [DMAC_0, DMAC_1, DMAC_2, DMAC_OTHER]);
122
123#[cfg(feature = "dma")]
124#[hal_cfg(any("dmac-d11", "dmac-d21"))]
125declare_interrupts!(DMAC);
126
127// ---------- SERCOM Interrupts ---------- //
128#[hal_cfg(any("sercom0-d11", "sercom0-d21"))]
129declare_interrupts!(SERCOM0);
130
131#[hal_cfg(any("sercom1-d11", "sercom1-d21"))]
132declare_interrupts!(SERCOM1);
133
134#[hal_cfg(any("sercom2-d11", "sercom2-d21"))]
135declare_interrupts!(SERCOM2);
136
137#[hal_cfg("sercom3-d21")]
138declare_interrupts!(SERCOM3);
139
140#[hal_cfg("sercom4-d21")]
141declare_interrupts!(SERCOM4);
142
143#[hal_cfg("sercom5-d21")]
144declare_interrupts!(SERCOM5);
145
146#[hal_cfg("sercom0-d5x")]
147declare_multiple_interrupts!(SERCOM0: [SERCOM0_0, SERCOM0_1, SERCOM0_2, SERCOM0_OTHER ]);
148
149#[hal_cfg("sercom1-d5x")]
150declare_multiple_interrupts!(SERCOM1: [SERCOM1_0, SERCOM1_1, SERCOM1_2, SERCOM1_OTHER ]);
151
152#[hal_cfg("sercom2-d5x")]
153declare_multiple_interrupts!(SERCOM2: [SERCOM0_2, SERCOM2_1, SERCOM2_2, SERCOM2_OTHER ]);
154
155#[hal_cfg("sercom3-d5x")]
156declare_multiple_interrupts!(SERCOM3: [SERCOM3_0, SERCOM3_1, SERCOM3_2, SERCOM3_OTHER ]);
157
158#[hal_cfg("sercom4-d5x")]
159declare_multiple_interrupts!(SERCOM4: [SERCOM4_0, SERCOM4_1, SERCOM4_2, SERCOM4_OTHER ]);
160
161#[hal_cfg("sercom5-d5x")]
162declare_multiple_interrupts!(SERCOM5: [SERCOM5_0, SERCOM5_1, SERCOM5_2, SERCOM5_OTHER ]);
163
164#[hal_cfg("sercom6-d5x")]
165declare_multiple_interrupts!(SERCOM6: [SERCOM6_0, SERCOM6_1, SERCOM6_2, SERCOM6_OTHER ]);
166
167#[hal_cfg("sercom7-d5x")]
168declare_multiple_interrupts!(SERCOM7: [SERCOM7_0, SERCOM7_1, SERCOM7_2, SERCOM7_OTHER ]);
169
170// ---------- TC Interrupts ---------- //
171
172#[hal_cfg("tc0")]
173declare_interrupts!(TC0);
174
175#[hal_cfg("tc1")]
176declare_interrupts!(TC1);
177
178#[hal_cfg("tc2")]
179declare_interrupts!(TC2);
180
181#[hal_cfg("tc3")]
182declare_interrupts!(TC3);
183
184#[hal_cfg("tc4")]
185declare_interrupts!(TC4);
186
187#[hal_cfg("tc5")]
188declare_interrupts!(TC5);
189
190#[hal_cfg("tc6")]
191declare_interrupts!(TC6);
192
193#[hal_cfg("tc7")]
194declare_interrupts!(TC7);
195
196// ---------- EIC Interrupt ---------- //
197#[hal_cfg(any("eic-d11", "eic-d21"))]
198declare_interrupts!(EIC);
199
200#[hal_cfg("eic-d5x")]
201seq_macro::seq!(N in 0..= 15 {
202 paste::paste! {
203 declare_interrupts! {
204 EIC_EXTINT_~N
205 }
206
207 }
208});
209
210// ---------- ADC Interrupt ---------- //
211#[hal_cfg("adc-d5x")]
212declare_multiple_interrupts!(ADC0: [ADC0_RESRDY, ADC0_OTHER]);
213#[hal_cfg("adc-d5x")]
214declare_multiple_interrupts!(ADC1: [ADC1_RESRDY, ADC1_OTHER]);
215#[hal_cfg(any("adc-d11", "adc-d21"))]
216declare_interrupts!(ADC);
217/// An interrupt source that may have one or many interrupt bindings.
218///
219/// This trait may implemented directly when multiple interrupt sources are
220/// needed to operate a single peripheral (eg, SERCOM and DMAC for thumbv7
221/// devices). If using one interrupt source per peripheral,
222/// implement [`Interrupt`] instead. When implemented on a type that handles
223/// multiple interrupt sources, the methods will act on all interrupt sources at
224/// once.
225///
226/// May not be implemented outside of this HAL.
227pub trait InterruptSource: crate::typelevel::Sealed {
228 /// Enable the interrupt.
229 ///
230 /// # Safety
231 ///
232 /// Do not enable any interrupt inside a critical section.
233 unsafe fn enable();
234
235 /// Disable the interrupt.
236 fn disable();
237
238 /// Unset interrupt pending.
239 fn unpend();
240
241 /// Set the interrupt priority.
242 fn set_priority(prio: Priority);
243}
244
245impl<T: Interrupt> InterruptSource for T {
246 unsafe fn enable() {
247 unsafe {
248 Self::enable();
249 }
250 }
251
252 fn disable() {
253 Self::disable();
254 }
255
256 fn unpend() {
257 Self::unpend();
258 }
259
260 fn set_priority(prio: Priority) {
261 Self::set_priority(prio);
262 }
263}
264
265/// Type-level interrupt.
266///
267/// This trait is implemented for all typelevel single interrupt types defined
268/// in this module. May not be implemented outside of this HAL.
269pub trait Interrupt: crate::typelevel::Sealed {
270 /// Interrupt enum variant.
271 ///
272 /// This allows going from typelevel interrupts (one type per interrupt,
273 /// defined in [`this module`](self)) to non-typelevel interrupts (a
274 /// single [`Interrupt`](crate::pac::Interrupt) enum type, with one
275 /// variant per interrupt).
276 const IRQ: crate::pac::Interrupt;
277
278 /// Enable the interrupt.
279 ///
280 /// # Safety
281 ///
282 /// Do not enable any interrupt inside a critical section.
283 #[inline]
284 unsafe fn enable() {
285 unsafe { Self::IRQ.enable() }
286 }
287
288 /// Disable the interrupt.
289 #[inline]
290 fn disable() {
291 Self::IRQ.disable()
292 }
293
294 /// Check if interrupt is enabled.
295 #[inline]
296 fn is_enabled() -> bool {
297 Self::IRQ.is_enabled()
298 }
299
300 /// Check if interrupt is pending.
301 #[inline]
302 fn is_pending() -> bool {
303 Self::IRQ.is_pending()
304 }
305
306 /// Set interrupt pending.
307 #[inline]
308 fn pend() {
309 Self::IRQ.pend()
310 }
311
312 /// Unset interrupt pending.
313 #[inline]
314 fn unpend() {
315 Self::IRQ.unpend()
316 }
317
318 /// Get the priority of the interrupt.
319 #[inline]
320 fn get_priority() -> Priority {
321 Self::IRQ.get_priority()
322 }
323
324 /// Set the interrupt priority.
325 #[inline]
326 fn set_priority(prio: Priority) {
327 Self::IRQ.set_priority(prio)
328 }
329
330 /// Set the interrupt priority with an already-acquired critical section.
331 ///
332 /// Equivalent to [`set_priority`](Self::set_priority), except you pass a
333 /// [`CriticalSection`] to prove you've already acquired a critical
334 /// section. This prevents acquiring another one, which saves code size.
335 ///
336 /// [`CriticalSection`]: critical_section::CriticalSection
337 #[inline]
338 fn set_priority_with_cs(cs: critical_section::CriticalSection, prio: Priority) {
339 Self::IRQ.set_priority_with_cs(cs, prio)
340 }
341}
342
343/// Interrupt handler.
344///
345/// Drivers that need to handle interrupts implement this trait.
346/// The user must ensure `on_interrupt()` is called every time the interrupt
347/// fires. Drivers must use use [`Binding`] to assert at compile time that the
348/// user has done so.
349pub trait Handler<I: InterruptSource>: Sealed {
350 /// Interrupt handler function.
351 ///
352 /// Must be called every time the `I` interrupt fires, synchronously from
353 /// the interrupt handler context.
354 ///
355 /// # Safety
356 ///
357 /// This function must ONLY be called from the interrupt handler for `I`.
358 unsafe fn on_interrupt();
359}
360
361/// Compile-time assertion that an interrupt has been bound to a handler.
362///
363/// For the vast majority of cases, you should use the `bind_interrupts!`
364/// macro instead of writing `unsafe impl`s of this trait.
365///
366/// # Safety
367///
368/// By implementing this trait, you are asserting that you have arranged for
369/// `H::on_interrupt()` to be called every time the `I` interrupt fires.
370///
371/// This allows drivers to check bindings at compile-time.
372pub unsafe trait Binding<I: InterruptSource, H: Handler<I>> {}