atsamd_hal/peripherals/
eic.rs

1//! # External Interrupt Controller
2//!
3//! This module provides typesafe APIs for interacting with the EIC peripheral,
4//! which is used to generate interrupts based on the state of a GPIO.
5//!
6//! Each chip has a number of EXTINT channels:
7//!
8//! * SAMD11: 8 channels
9//! * SAMD21/SAMx5x: 16 channels
10//!
11//! Each channel can operate independently, and sense state changes for a single
12//! GPIO pin at a time. Refer to the datasheet for GPIO pin/EXTINT channel
13//! compatibility. In this module, an [`ExtInt`] represents an EXTINT channel
14//! which is tied to a GPIO [`Pin`], and is capable of sensing state changes.
15//!
16//! ## Steps to create an [`ExtInt`]
17//!
18//! 1. Start by creating an [`Eic`] struct, by calling [`Eic::new`]. This
19//!    initializes the EIC peripheral and sets up the correct clocking.
20//!
21//! 1. Turn the [`Eic`] into a tuple of [`Channel`]s by calling [`Eic::split`].
22//!    Each channel represents a single EXTINT channel.
23//!
24//! 1. Assign a pin to a channel by calling [`Channel::with_pin`]. This returns
25//!    a fully configured and ready to use [`ExtInt`]. A [`Pin`] can also be
26//!    directly converted into an [`ExtInt`] by calling one of the methods
27//!    provided by the [`EicPin`] trait.
28//!
29//! ### Example setup
30//!
31//! ```no_run
32//! let eic_clock = clocks.eic(&gclk0).unwrap();
33//! // Initialize the EIC peripheral
34//! let eic = Eic::new(&mut peripherals.pm, eic_clock, peripherals.eic);
35//! // Split into channels
36//! let eic_channels = eic.split();
37//!
38//! // Take the pin that we want to use
39//! let button: Pin<_, PullUpInterrupt> = pins.d10.into();
40//!
41//! // Turn the EXTINT[2] channel into an ExtInt struct
42//! let mut extint = eic_channels.2.with_pin(button);
43//! ```
44//!
45//! ## `async` operation <span class="stab portability" title="Available on crate feature `async` only"><code>async</code></span>
46//!
47//! [`ExtInt`]s can be used for async operations. Configuring the [`Eic`] in
48//! async mode is relatively simple:
49//!
50//! * Bind the corresponding `EIC` interrupt source to the SPI
51//!   [`InterruptHandler`] (refer to the module-level
52//!   [`async_hal`](crate::async_hal) documentation for more information).
53//!
54//! * SAMD11/SAMD21: Turn an [`Eic`] into an async-enabled [`Eic`] by calling
55//!   [`Eic::into_future`]. Since there is only a single interrupt handler for
56//!   the EIC peripheral, all EXTINT channels must be turned into async channels
57//!   at once.
58//! * SAMx5x: Turn an individuel [`ExtInt`] into an async-enabled [`ExtInt`] by
59//!   calling [`ExtInt::into_future`]. Each channel has a dedicated interrupt
60//!   source, therefore you must individually choose which channels to turn into
61//!   async channels.
62//! * Use the provided [`wait`](ExtInt::wait) method. async-enabled [`ExtInt`]s
63//!   also implement [`embedded_hal_async::digital::Wait`].
64
65use core::marker::PhantomData;
66
67use atsamd_hal_macros::{hal_cfg, hal_module};
68use seq_macro::seq;
69
70use crate::{
71    clock::EicClock,
72    gpio::{AnyPin, Pin},
73    pac,
74    typelevel::{NoneT, Sealed},
75};
76
77#[hal_module(
78    any("eic-d11", "eic-d21") => "eic/d11/mod.rs",
79    "eic-d5x" => "eic/d5x/mod.rs",
80)]
81mod impls {}
82#[cfg(feature = "async")]
83pub use impls::async_api::*;
84
85pub type Sense = pac::eic::config::Sense0select;
86
87/// Trait representing an EXTINT channel ID.
88pub trait ChId {
89    const ID: usize;
90}
91
92/// Marker type that represents an EXTINT channel capable of doing async
93/// operations.
94#[cfg(feature = "async")]
95pub enum EicFuture {}
96
97/// Trait representing a GPIO pin which can be used as an external interrupt.
98pub trait EicPin: AnyPin + Sealed {
99    type Floating;
100    type PullUp;
101    type PullDown;
102
103    type ChId: ChId;
104
105    #[hal_cfg("eic-d5x")]
106    #[cfg(feature = "async")]
107    type InterruptSource: crate::async_hal::interrupts::InterruptSource;
108
109    /// Configure a pin as a floating external interrupt
110    fn into_floating_ei(self, chan: Channel<Self::ChId>) -> Self::Floating;
111
112    /// Configure a pin as pulled-up external interrupt
113    fn into_pull_up_ei(self, chan: Channel<Self::ChId>) -> Self::PullUp;
114
115    /// Configure a pin as pulled-down external interrupt
116    fn into_pull_down_ei(self, chan: Channel<Self::ChId>) -> Self::PullDown;
117}
118
119/// A numbered external interrupt, which can be used to sense state changes on
120/// its pin.
121pub struct ExtInt<P, Id, F = NoneT>
122where
123    P: EicPin,
124    Id: ChId,
125{
126    chan: Channel<Id, F>,
127    pin: Pin<P::Id, P::Mode>,
128}
129
130impl<P, Id, F> ExtInt<P, Id, F>
131where
132    P: EicPin,
133    Id: ChId,
134{
135    /// Release the underlying resources: [`Pin`] and [`Channel`].
136    pub fn free(self) -> (Pin<P::Id, P::Mode>, Channel<Id, F>) {
137        (self.pin, self.chan)
138    }
139
140    /// Construct pad from the appropriate pin in any mode.
141    /// You may find it more convenient to use the `into_pad` trait
142    /// and avoid referencing the pad type.
143    fn new(pin: P, chan: Channel<Id, F>) -> Self {
144        ExtInt {
145            pin: pin.into(),
146            chan,
147        }
148    }
149
150    #[cfg(all(doc, feature = "async"))]
151    #[hal_cfg(not("eic-d5x"))]
152    /// This method is not present with the selected feature set, defined for
153    /// documentation only
154    pub fn into_future(self) {
155        unimplemented!()
156    }
157}
158
159/// EIC channel.
160///
161/// Use this struct to create an [`ExtInt`] by calling
162/// [`with_pin`](Self::with_pin).
163pub struct Channel<Id: ChId, F = NoneT> {
164    eic: core::mem::ManuallyDrop<pac::Eic>,
165    _id: PhantomData<Id>,
166    _irqs: PhantomData<F>,
167}
168
169impl<Id: ChId, F> Channel<Id, F> {
170    /// Assign a pin to this [`Channel`], and turn it into an [`ExtInt`], which
171    /// is capable of sensing state changes on the pin.
172    pub fn with_pin<P: EicPin<ChId = Id>>(self, pin: P) -> ExtInt<P, Id, F> {
173        ExtInt::new(pin, self)
174    }
175
176    fn new(eic: pac::Eic) -> Self {
177        Self {
178            eic: core::mem::ManuallyDrop::new(eic),
179            _id: PhantomData,
180            _irqs: PhantomData,
181        }
182    }
183
184    #[hal_cfg("eic-d5x")]
185    #[cfg(feature = "async")]
186    fn change_mode<N>(self) -> Channel<Id, N> {
187        Channel {
188            eic: self.eic,
189            _id: self._id,
190            _irqs: PhantomData,
191        }
192    }
193}
194
195/// External Interrupt Controller.
196///
197/// Use [`split`](Self::split) to split the struct into individual channels,
198/// which can then be used to create [`ExtInt`]s, by calling
199/// [`Channel::with_pin`].
200pub struct Eic<I = NoneT> {
201    eic: pac::Eic,
202    _irqs: PhantomData<I>,
203}
204
205impl Eic {
206    /// Create a new [`Eic`] and initialize it.
207    #[hal_cfg(any("eic-d11", "eic-d21"))]
208    pub fn new(pm: &mut pac::Pm, _clock: EicClock, eic: pac::Eic) -> Self {
209        pm.apbamask().modify(|_, w| w.eic_().set_bit());
210
211        // Reset the EIC
212        eic.ctrl().modify(|_, w| w.swrst().set_bit());
213        while eic.ctrl().read().swrst().bit_is_set() {
214            core::hint::spin_loop();
215        }
216
217        eic.ctrl().modify(|_, w| w.enable().set_bit());
218        while eic.status().read().syncbusy().bit_is_set() {
219            cortex_m::asm::nop();
220        }
221        Self {
222            eic,
223            _irqs: PhantomData,
224        }
225    }
226
227    /// Create and initialize a new [`Eic`], and wire it up to the
228    /// ultra-low-power 32kHz clock source.
229    #[hal_cfg("eic-d5x")]
230    pub fn new(mclk: &mut pac::Mclk, _clock: EicClock, eic: pac::Eic) -> Self {
231        mclk.apbamask().modify(|_, w| w.eic_().set_bit());
232
233        let mut eic = Self {
234            eic,
235            _irqs: PhantomData,
236        };
237
238        // Reset the EIC
239        eic.swreset();
240
241        // Use the low-power 32k clock and enable.
242        eic.eic.ctrla().modify(|_, w| {
243            w.cksel().set_bit();
244            w.enable().set_bit()
245        });
246
247        while eic.eic.syncbusy().read().enable().bit_is_set() {
248            core::hint::spin_loop();
249        }
250
251        eic
252    }
253
254    #[cfg(all(doc, feature = "async"))]
255    #[hal_cfg(not(any("eic-d11", "eic-d21")))]
256    /// This method is not present with the selected feature set, defined for
257    /// documentation only
258    pub fn into_future(self) {
259        unimplemented!()
260    }
261
262    /// Release the EIC and return the register block.
263    ///
264    /// **Note**: The [`Channels`] struct is consumed by this method. This means
265    /// that any [`Channel`] obtained by [`split`](Eic::split) must be
266    /// moved back into the [`Channels`] struct before being able to pass it
267    /// into [`free`](Eic::free).
268    pub fn free(mut self, _channels: Channels) -> pac::Eic {
269        self.swreset();
270
271        self.eic
272    }
273}
274
275impl<F> Eic<F> {
276    /// Reset the EIC
277    #[atsamd_hal_macros::hal_macro_helper]
278    fn swreset(&mut self) {
279        #[hal_cfg(any("eic-d11", "eic-d21"))]
280        let ctrl = self.eic.ctrl();
281
282        #[hal_cfg("eic-d5x")]
283        let ctrl = self.eic.ctrla();
284
285        ctrl.modify(|_, w| w.swrst().set_bit());
286        while ctrl.read().swrst().bit_is_set() {
287            core::hint::spin_loop();
288        }
289    }
290}
291
292#[cfg(feature = "async")]
293impl Eic<EicFuture> {
294    /// Release the EIC and return the register block.
295    ///
296    /// **Note**: The [`Channels`] struct is consumed by this method. This means
297    /// that any [`Channel`] obtained by [`split`](Eic::split) must be
298    /// moved back into the [`Channels`] struct before being able to pass it
299    /// into [`free`](Eic::free).
300    pub fn free(mut self, _channels: FutureChannels) -> pac::Eic {
301        self.swreset();
302        self.eic
303    }
304}
305
306#[hal_cfg("eic-d11")]
307macro_rules! with_num_channels {
308    ($some_macro:ident) => {
309        $some_macro! {8}
310    };
311}
312
313#[hal_cfg(any("eic-d5x", "eic-d21"))]
314macro_rules! with_num_channels {
315    ($some_macro:ident) => {
316        $some_macro! {16}
317    };
318}
319
320macro_rules! get {
321    ($literal:literal) => {
322        $literal
323    };
324}
325
326/// The number of EXTINT channels on this chip.
327pub const NUM_CHANNELS: usize = with_num_channels!(get);
328
329macro_rules! define_channels_struct {
330    ($num_channels:literal) => {
331        seq!(N in 0..$num_channels {
332            #(
333                /// Type alias for a channel number
334                pub enum Ch~N {}
335
336                impl ChId for Ch~N {
337                    const ID: usize = N;
338                }
339            )*
340
341            /// Struct generating individual handles to each EXTINT channel
342            pub struct Channels(
343                #(
344                    pub Channel<Ch~N>,
345                )*
346            );
347        });
348    };
349}
350
351with_num_channels!(define_channels_struct);
352
353#[cfg(feature = "async")]
354macro_rules! define_channels_struct_future {
355    ($num_channels:literal) => {
356        seq!(N in 0..$num_channels {
357            /// Struct generating individual handles to each EXTINT channel for `async` operation
358            pub struct FutureChannels(
359                #(
360                    pub Channel<Ch~N, EicFuture>,
361                )*
362            );
363        });
364    };
365}
366
367#[cfg(feature = "async")]
368with_num_channels!(define_channels_struct_future);
369
370macro_rules! define_split {
371    ($num_channels:literal) => {
372        seq!(N in 0..$num_channels {
373            /// Split the EIC into individual channels.
374            #[inline]
375            pub fn split(self) -> Channels {
376                Channels(
377                    #(
378                       unsafe { Channel::new(core::ptr::read(&self.eic as *const _)) },
379                    )*
380                )
381            }
382
383        });
384    };
385}
386
387impl Eic {
388    with_num_channels!(define_split);
389}
390
391#[cfg(feature = "async")]
392macro_rules! define_split_future {
393    ($num_channels:literal) => {
394        seq!(N in 0..$num_channels {
395            /// Split the EIC into individual channels
396            #[inline]
397            pub fn split(self) -> FutureChannels {
398                FutureChannels(
399                    #(
400                        unsafe { Channel::new(core::ptr::read(&self.eic as *const _)) },
401                    )*
402                )
403            }
404        });
405    };
406}
407
408#[cfg(feature = "async")]
409impl Eic<EicFuture> {
410    with_num_channels!(define_split_future);
411}