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
85#[hal_cfg("eic-d5x")]
86use super::clock::v2::{self, gclk::GclkId, osculp32k::OscUlp32kId, pclk::Pclk, rtcosc::RtcOsc};
87
88pub type Sense = pac::eic::config::Sense0select;
89
90/// Trait representing an EXTINT channel ID.
91pub trait ChId {
92    const ID: usize;
93}
94
95/// Marker type that represents an EXTINT channel capable of doing async
96/// operations.
97#[cfg(feature = "async")]
98pub enum EicFuture {}
99
100/// Trait representing a GPIO pin which can be used as an external interrupt.
101pub trait EicPin: AnyPin + Sealed {
102    type Floating;
103    type PullUp;
104    type PullDown;
105
106    type ChId: ChId;
107
108    #[hal_cfg("eic-d5x")]
109    #[cfg(feature = "async")]
110    type InterruptSource: crate::async_hal::interrupts::InterruptSource;
111
112    /// Configure a pin as a floating external interrupt
113    fn into_floating_ei(self, chan: Channel<Self::ChId>) -> Self::Floating;
114
115    /// Configure a pin as pulled-up external interrupt
116    fn into_pull_up_ei(self, chan: Channel<Self::ChId>) -> Self::PullUp;
117
118    /// Configure a pin as pulled-down external interrupt
119    fn into_pull_down_ei(self, chan: Channel<Self::ChId>) -> Self::PullDown;
120}
121
122/// A numbered external interrupt, which can be used to sense state changes on
123/// its pin.
124pub struct ExtInt<P, Id, F = NoneT>
125where
126    P: EicPin,
127    Id: ChId,
128{
129    chan: Channel<Id, F>,
130    pin: Pin<P::Id, P::Mode>,
131}
132
133impl<P, Id, F> ExtInt<P, Id, F>
134where
135    P: EicPin,
136    Id: ChId,
137{
138    /// Release the underlying resources: [`Pin`] and [`Channel`].
139    pub fn free(self) -> (Pin<P::Id, P::Mode>, Channel<Id, F>) {
140        (self.pin, self.chan)
141    }
142
143    /// Construct pad from the appropriate pin in any mode.
144    /// You may find it more convenient to use the `into_pad` trait
145    /// and avoid referencing the pad type.
146    fn new(pin: P, chan: Channel<Id, F>) -> Self {
147        ExtInt {
148            pin: pin.into(),
149            chan,
150        }
151    }
152
153    #[cfg(all(doc, feature = "async"))]
154    #[hal_cfg(not("eic-d5x"))]
155    /// This method is not present with the selected feature set, defined for
156    /// documentation only
157    pub fn into_future(self) {
158        unimplemented!()
159    }
160}
161
162/// EIC channel.
163///
164/// Use this struct to create an [`ExtInt`] by calling
165/// [`with_pin`](Self::with_pin).
166pub struct Channel<Id: ChId, F = NoneT> {
167    eic: core::mem::ManuallyDrop<pac::Eic>,
168    _id: PhantomData<Id>,
169    _irqs: PhantomData<F>,
170}
171
172impl<Id: ChId, F> Channel<Id, F> {
173    /// Assign a pin to this [`Channel`], and turn it into an [`ExtInt`], which
174    /// is capable of sensing state changes on the pin.
175    pub fn with_pin<P: EicPin<ChId = Id>>(self, pin: P) -> ExtInt<P, Id, F> {
176        ExtInt::new(pin, self)
177    }
178
179    fn new(eic: pac::Eic) -> Self {
180        Self {
181            eic: core::mem::ManuallyDrop::new(eic),
182            _id: PhantomData,
183            _irqs: PhantomData,
184        }
185    }
186
187    #[hal_cfg("eic-d5x")]
188    #[cfg(feature = "async")]
189    fn change_mode<N>(self) -> Channel<Id, N> {
190        Channel {
191            eic: self.eic,
192            _id: self._id,
193            _irqs: PhantomData,
194        }
195    }
196}
197
198/// External Interrupt Controller.
199///
200/// Use [`split`](Self::split) to split the struct into individual channels,
201/// which can then be used to create [`ExtInt`]s, by calling
202/// [`Channel::with_pin`].
203pub struct Eic<I = NoneT> {
204    eic: pac::Eic,
205    _irqs: PhantomData<I>,
206}
207
208impl Eic {
209    /// Create a new [`Eic`] and initialize it.
210    #[hal_cfg(any("eic-d11", "eic-d21"))]
211    pub fn new(pm: &mut pac::Pm, _clock: EicClock, eic: pac::Eic) -> Self {
212        pm.apbamask().modify(|_, w| w.eic_().set_bit());
213
214        // Reset the EIC
215        eic.ctrl().modify(|_, w| w.swrst().set_bit());
216        while eic.ctrl().read().swrst().bit_is_set() {
217            core::hint::spin_loop();
218        }
219
220        eic.ctrl().modify(|_, w| w.enable().set_bit());
221        while eic.status().read().syncbusy().bit_is_set() {
222            cortex_m::asm::nop();
223        }
224        Self {
225            eic,
226            _irqs: PhantomData,
227        }
228    }
229
230    /// Create and initialize a new [`Eic`], and wire it up to the
231    /// ultra-low-power 32kHz clock source.
232    #[hal_cfg("eic-d5x")]
233    pub fn new(mclk: &mut pac::Mclk, _clock: &EicClock, eic: pac::Eic) -> Self {
234        mclk.apbamask().modify(|_, w| w.eic_().set_bit());
235
236        let mut eic = Self {
237            eic,
238            _irqs: PhantomData,
239        };
240
241        // Reset the EIC
242        eic.swreset();
243
244        // Use the low-power 32k clock and enable.
245        eic.eic.ctrla().modify(|_, w| {
246            w.cksel().set_bit();
247            w.enable().set_bit()
248        });
249
250        while eic.eic.syncbusy().read().enable().bit_is_set() {
251            core::hint::spin_loop();
252        }
253
254        eic
255    }
256
257    #[hal_cfg("eic-d5x")]
258    /// Switch the EIC to use the OSC32K clock
259    /// as a source. This enables it to run in deep-sleep
260    ///
261    /// In this mode, the maximum event frequency is limited to 16Khz
262    pub fn switch_to_osc32k(&mut self, _rtc: &RtcOsc<OscUlp32kId>) {
263        self.eic.ctrla().write(|w| w.enable().clear_bit());
264        self.sync();
265        self.eic.ctrla().write(|w| {
266            w.cksel().clk_ulp32k();
267            w.enable().set_bit()
268        });
269        self.sync();
270    }
271
272    #[hal_cfg("eic-d5x")]
273    /// Switch the EIC to use a GCLK source as its clock
274    /// source.
275    ///
276    /// In this mode, the peripheral cannot run in deep-sleep,
277    /// but its maximum event frequency is `F_GCLK/2`
278    pub fn switch_to_gclk<S: GclkId>(&mut self, _gclk: &Pclk<v2::pclk::ids::Eic, S>) {
279        self.eic.ctrla().write(|w| w.enable().clear_bit());
280        self.sync();
281        self.eic.ctrla().write(|w| {
282            w.cksel().clk_gclk();
283            w.enable().set_bit()
284        });
285        self.sync();
286    }
287
288    #[hal_cfg("eic-d5x")]
289    fn sync(&self) {
290        while self.eic.syncbusy().read().enable().bit_is_set() {
291            core::hint::spin_loop();
292        }
293    }
294
295    #[cfg(all(doc, feature = "async"))]
296    #[hal_cfg(not(any("eic-d11", "eic-d21")))]
297    /// This method is not present with the selected feature set, defined for
298    /// documentation only
299    pub fn into_future(self) {
300        unimplemented!()
301    }
302
303    /// Release the EIC and return the register block.
304    ///
305    /// **Note**: The [`Channels`] struct is consumed by this method. This means
306    /// that any [`Channel`] obtained by [`split`](Eic::split) must be
307    /// moved back into the [`Channels`] struct before being able to pass it
308    /// into [`free`](Eic::free).
309    pub fn free(mut self, _channels: Channels) -> pac::Eic {
310        self.swreset();
311
312        self.eic
313    }
314}
315
316impl<F> Eic<F> {
317    /// Reset the EIC
318    #[atsamd_hal_macros::hal_macro_helper]
319    fn swreset(&mut self) {
320        #[hal_cfg(any("eic-d11", "eic-d21"))]
321        let ctrl = self.eic.ctrl();
322
323        #[hal_cfg("eic-d5x")]
324        let ctrl = self.eic.ctrla();
325
326        ctrl.modify(|_, w| w.swrst().set_bit());
327        while ctrl.read().swrst().bit_is_set() {
328            core::hint::spin_loop();
329        }
330    }
331}
332
333#[cfg(feature = "async")]
334impl Eic<EicFuture> {
335    /// Release the EIC and return the register block.
336    ///
337    /// **Note**: The [`Channels`] struct is consumed by this method. This means
338    /// that any [`Channel`] obtained by [`split`](Eic::split) must be
339    /// moved back into the [`Channels`] struct before being able to pass it
340    /// into [`free`](Eic::free).
341    pub fn free(mut self, _channels: FutureChannels) -> pac::Eic {
342        self.swreset();
343        self.eic
344    }
345}
346
347#[hal_cfg("eic-d11")]
348macro_rules! with_num_channels {
349    ($some_macro:ident) => {
350        $some_macro! {8}
351    };
352}
353
354#[hal_cfg(any("eic-d5x", "eic-d21"))]
355macro_rules! with_num_channels {
356    ($some_macro:ident) => {
357        $some_macro! {16}
358    };
359}
360
361macro_rules! get {
362    ($literal:literal) => {
363        $literal
364    };
365}
366
367/// The number of EXTINT channels on this chip.
368pub const NUM_CHANNELS: usize = with_num_channels!(get);
369
370macro_rules! define_channels_struct {
371    ($num_channels:literal) => {
372        seq!(N in 0..$num_channels {
373            #(
374                /// Type alias for a channel number
375                pub enum Ch~N {}
376
377                impl ChId for Ch~N {
378                    const ID: usize = N;
379                }
380            )*
381
382            /// Struct generating individual handles to each EXTINT channel
383            pub struct Channels(
384                #(
385                    pub Channel<Ch~N>,
386                )*
387            );
388        });
389    };
390}
391
392with_num_channels!(define_channels_struct);
393
394#[cfg(feature = "async")]
395macro_rules! define_channels_struct_future {
396    ($num_channels:literal) => {
397        seq!(N in 0..$num_channels {
398            /// Struct generating individual handles to each EXTINT channel for `async` operation
399            pub struct FutureChannels(
400                #(
401                    pub Channel<Ch~N, EicFuture>,
402                )*
403            );
404        });
405    };
406}
407
408#[cfg(feature = "async")]
409with_num_channels!(define_channels_struct_future);
410
411macro_rules! define_split {
412    ($num_channels:literal) => {
413        seq!(N in 0..$num_channels {
414            /// Split the EIC into individual channels.
415            #[inline]
416            pub fn split(self) -> Channels {
417                Channels(
418                    #(
419                       unsafe { Channel::new(core::ptr::read(&self.eic as *const _)) },
420                    )*
421                )
422            }
423
424        });
425    };
426}
427
428impl Eic {
429    with_num_channels!(define_split);
430}
431
432#[cfg(feature = "async")]
433macro_rules! define_split_future {
434    ($num_channels:literal) => {
435        seq!(N in 0..$num_channels {
436            /// Split the EIC into individual channels
437            #[inline]
438            pub fn split(self) -> FutureChannels {
439                FutureChannels(
440                    #(
441                        unsafe { Channel::new(core::ptr::read(&self.eic as *const _)) },
442                    )*
443                )
444            }
445        });
446    };
447}
448
449#[cfg(feature = "async")]
450impl Eic<EicFuture> {
451    with_num_channels!(define_split_future);
452}