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}