atsamd_hal/async_hal/mod.rs
1//! # Asynchronous HAL APIs
2//!
3//! This HAL provides a comprehensive and efficient way to interact with
4//! the underlying peripherals in an asynchronous fashion, enabling concurrent
5//! and non-blocking programming through the use of `async`/`await` APIs.
6//!
7//! This module provides the basis for interacting with peripherals through
8//! `async` APIs. Notably, in order to function correctly and wake an `await`ing
9//! [`Future`](core::future::Future), peripherals must be able to signal when
10//! their respective interrupts fire. Traditionally, the user manually writes
11//! their own interrupt handlers. When using `async` APIs, the peripherals
12//! effectively take control of their own interrupt handlers in order to wake
13//! tasks at the appropriate time.
14//!
15//! ## Using the `async` APIs
16//!
17//! To use the asynchronous APIs provided by the HAL, enable the `async` Cargo
18//! feature. Note that this uses a certain amount of static RAM in order to
19//! initialize wakers for each peripheral.
20//!
21//! ## Supported peripherals
22//!
23//! * [`UART`](crate::sercom::uart)
24//! * [`SPI`](crate::sercom::spi)
25//! * [`I2C`](crate::sercom::i2c)
26//! * [`DMAC`](crate::dmac)
27//! * [`EIC`](crate::eic) (external GPIO interrupts)
28//! * [`Timers`](crate::timer)
29//!
30//! **Note**: The asynchronous APIs for the individual peripherals are provided
31//! in their respective modules. This module only deals with the generalities of
32//! using `async` constructs throughout the HAL.
33//!
34//! ## Declaring interrupt bindings
35//!
36//! In order for the peripherals to wake their respective tasks, the interrupt
37//! sources must be bound to their handler at compile time. A struct that
38//! implements [`Binding`](self::interrupts::Binding) must be passed to an async
39//! peripheral in order to prove to the compiler that the correct interrupt
40//! handlers have been bound.
41//!
42//! This module provides convenient macros that generate the interrupt bindings.
43//! Use [`bind_interrupts`](crate::bind_interrupts) to bind single interrupt
44//! sources to handlers. See also [Declaring multiple interrupt source
45//! bindings](#declaring-multiple-interrupt-source-bindings).
46//!
47//! ## Declaring multiple interrupt source bindings
48//!
49//! For some `thumbv7em` peripherals, there are multiple interrupt sources used
50//! by a single peripheral. In these cases, we must provide a binding to an
51//! interrupt handler for each of these interrupt sources in order for the
52//! peripheral driver to function properly. The HAL defines only one interrupt
53//! "source" per peripheral. Your job is to tell it where to find all the
54//! relevant interrupts it must use to operate the peripheral properly. Use
55//! [`bind_multiple_interrupts`](crate::bind_multiple_interrupts) to bind
56//! multiple interrupts to a single handler.
57//!
58//! Currently, the supported peripherals which have multiple interrupts per
59//! peripheral (**thumbv7em targets only**):
60//!
61//! * `SERCOMx: [SERCOMx_0, SERCOMx_1, SERCOMx_2, SERCOMx_OTHER]`
62//! * `DMAC: [DMAC_0, DMAC_1, DMAC_2, DMAC_OTHER]`
63//!
64//! ## Complete example using the `feather_m0` BSP and the [Embassy executor](https://crates.io/crates/embassy-executor)
65//! ```no_run
66//! #![no_std]
67//! #![no_main]
68//!
69//! use defmt_rtt as _;
70//! use panic_probe as _;
71//!
72//! use bsp::hal;
73//! use bsp::pac;
74//! use feather_m0 as bsp;
75//! use fugit::MillisDuration;
76//! use hal::{
77//! clock::GenericClockController,
78//! dmac::{DmaController, PriorityLevel},
79//! prelude::*,
80//! sercom::Sercom4,
81//! };
82//! use rtic_monotonics::systick::Systick;
83//!
84//! atsamd_hal::bind_interrupts!(struct Irqs {
85//! SERCOM4 => atsamd_hal::sercom::spi::InterruptHandler<Sercom4>;
86//! DMAC => atsamd_hal::dmac::InterruptHandler;
87//! });
88//!
89//! #[embassy_executor::main]
90//! async fn main(_s: embassy_executor::Spawner) {
91//! let mut peripherals = pac::Peripherals::take().unwrap();
92//! let _core = pac::CorePeripherals::take().unwrap();
93//!
94//! let mut clocks = GenericClockController::with_external_32kosc(
95//! peripherals.gclk,
96//! &mut peripherals.pm,
97//! &mut peripherals.sysctrl,
98//! &mut peripherals.nvmctrl,
99//! );
100//!
101//! let pins = bsp::Pins::new(peripherals.port);
102//!
103//! // Take SPI pins
104//! let (miso, mosi, sclk) = (pins.miso, pins.mosi, pins.sclk);
105//!
106//! // Initialize DMA Controller
107//! let dmac = DmaController::init(peripherals.dmac, &mut peripherals.pm);
108//!
109//! // Turn dmac into an async controller
110//! let mut dmac = dmac.into_future(Irqs);
111//! // Get individual handles to DMA channels
112//! let channels = dmac.split();
113//!
114//! // Initialize DMA Channels 0 and 1
115//! let channel0 = channels.0.init(PriorityLevel::Lvl0);
116//! let channel1 = channels.1.init(PriorityLevel::Lvl0);
117//!
118//! let mut spi = bsp::spi_master(
119//! &mut clocks,
120//! 100.kHz(),
121//! peripherals.sercom4,
122//! &mut peripherals.pm,
123//! sclk,
124//! mosi,
125//! miso,
126//! )
127//! .into_future(Irqs)
128//! .with_dma_channels(channel0, channel1);
129//!
130//! loop {
131//! defmt::info!("Sending 0x00 to SPI device...");
132//! spi.write(&[0x00]).await.unwrap();
133//!
134//! defmt::info!("Sent 0x00.");
135//!
136//! let mut buffer = [0xff; 4];
137//! spi.read(&mut buffer).await.unwrap();
138//! defmt::info!("Read buffer: {:#x}", buffer);
139//! Systick::delay(MillisDuration::<u32>::from_ticks(500).convert()).await;
140//! }
141//! }
142//! ```
143
144pub mod interrupts;
145
146/// Bind interrupt sources to a single handler each.
147///
148/// This defines the right interrupt handlers, creates a unit struct (like
149/// `struct Irqs;`) and implements the right
150/// [`Binding`](crate::async_hal::interrupts::Binding)s for it. You can pass
151/// this struct to drivers to prove at compile-time that the right interrupts
152/// have been bound.
153///
154/// Refer to the module-level documentation for details on when to use
155/// [`bind_interrupts`](crate::bind_interrupts) vs
156/// [`bind_multiple_interrupts`](crate::bind_multiple_interrupts).
157///
158/// ## Example
159/// ```
160/// use atsamd_hal::{dmac, sercom, bind_interrupts};
161///
162/// bind_interrupts!(struct Irqs {
163/// SERCOM0 => sercom::i2c::InterruptHandler;
164/// DMAC => dmac::InterruptHandler;
165/// });
166/// ```
167#[macro_export]
168macro_rules! bind_interrupts {
169 ($vis:vis struct $name:ident { $($irq:ident => $($handler:ty),*;)* }) => {
170 #[derive(Copy, Clone)]
171 $vis struct $name;
172
173 $(
174 #[allow(non_snake_case)]
175 #[no_mangle]
176 unsafe extern "C" fn $irq() {
177 $(
178 <$handler as $crate::async_hal::interrupts::Handler<$crate::async_hal::interrupts::$irq>>::on_interrupt();
179 )*
180 }
181
182 $(
183 unsafe impl $crate::async_hal::interrupts::Binding<$crate::async_hal::interrupts::$irq, $handler> for $name
184 where $crate::async_hal::interrupts::$irq: $crate::async_hal::interrupts::SingleInterruptSource
185 {}
186 )*
187 )*
188 };
189}
190
191/// Bind multiple interrupt sources to the same interrupt handler.
192///
193/// This defines the right interrupt handlers, creates a unit struct (like
194/// `struct Irqs;`) and implements the right
195/// [`Binding`](crate::async_hal::interrupts::Binding)s for it. You can pass
196/// this struct to drivers to prove at compile-time that the right interrupts
197/// have been bound.
198///
199/// Due to limitations in the macro's implementation, only one binding per macro
200/// call is supported. Call [`bind_multiple_interrupts`] as many times as you
201/// need multiple-interrupt bindings.
202///
203/// Refer to the module-level documentation for details on when to use
204/// [`bind_interrupts`] vs
205/// [`bind_multiple_interrupts`].
206///
207/// ## Example
208/// ```
209/// atsamd_hal::bind_multiple_interrupts!(struct DmacIrqs {
210/// DMAC: [DMAC_0, DMAC_1, DMAC_2, DMAC_OTHER] => atsamd_hal::dmac::InterruptHandler;
211/// });
212///
213/// atsamd_hal::bind_multiple_interrupts!(struct SpiIrqs {
214/// SERCOM0: [SERCOM0_0, SERCOM0_1, SERCOM0_2, SERCOM0_OTHER] => atsamd_hal::sercom::spi::InterruptHandler;
215/// });
216/// ```
217///
218/// [`bind_interrupts`]: crate::bind_interrupts
219/// [`bind_multiple_interrupts`]: crate::bind_multiple_interrupts
220#[macro_export]
221macro_rules! bind_multiple_interrupts {
222 ($vis:vis struct $name:ident { $int_source:ident: [ $($irq:ident),+ ] => $handler:ty; }) => {
223 #[derive(Copy, Clone)]
224 $vis struct $name;
225
226 $(
227 #[allow(non_snake_case)]
228 #[no_mangle]
229 unsafe extern "C" fn $irq() {
230 <$handler as $crate::async_hal::interrupts::Handler<$crate::async_hal::interrupts::$int_source>>::on_interrupt();
231 }
232 )+
233
234 unsafe impl $crate::async_hal::interrupts::Binding<$crate::async_hal::interrupts::$int_source, $handler> for $name
235 where $crate::async_hal::interrupts::$int_source: $crate::async_hal::interrupts::MultipleInterruptSources
236 {}
237 };
238}