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}