atsamd_hal/gpio/
dynpin.rs

1//! # Type-erased, value-level module for GPIO pins
2//!
3//! Although the type-level API is generally preferred, it is not suitable in
4//! all cases. Because each pin is represented by a distinct type, it is not
5//! possible to store multiple pins in a homogeneous data structure. The
6//! value-level API solves this problem by erasing the type information and
7//! tracking the pin at run-time.
8//!
9//! Value-level pins are represented by the [`DynPin`] type. [`DynPin`] has two
10//! fields, `id` and `mode` with types [`DynPinId`] and [`DynPinMode`]
11//! respectively. The implementation of these types closely mirrors the
12//! type-level API.
13//!
14//! Instances of [`DynPin`] cannot be created directly. Rather, they must be
15//! created from their type-level equivalents using [`From`]/[`Into`].
16//!
17//! ```
18//! // Move a pin out of the Pins struct and convert to a DynPin
19//! let pa27: DynPin = pins.pa27.into();
20//! ```
21//!
22//! Conversions between pin modes use a value-level version of the type-level
23//! API.
24//!
25//! ```
26//! // Use one of the literal function names
27//! pa27.into_floating_input();
28//! // Use a method and a DynPinMode variant
29//! pa27.into_mode(DYN_FLOATING_INPUT);
30//! ```
31//!
32//! Because the pin state cannot be tracked at compile-time, many [`DynPin`]
33//! operations become fallible. Run-time checks are inserted to ensure that
34//! users don't try to, for example, set the output level of an input pin.
35//!
36//! Users may try to convert value-level pins back to their type-level
37//! equivalents. However, this option is fallible, because the compiler cannot
38//! guarantee the pin has the correct ID or is in the correct mode at
39//! compile-time. Use [`TryFrom`]/[`TryInto`] for this conversion.
40//!
41//! ```
42//! // Convert to a `DynPin`
43//! let pa27: DynPin = pins.pa27.into();
44//! // Change pin mode
45//! pa27.into_floating_input();
46//! // Convert back to a `Pin`
47//! let pa27: Pin<PA27, FloatingInput> = pa27.try_into().unwrap();
48//! ```
49//!
50//! # Embedded HAL traits
51//!
52//! This module implements all of the embedded HAL GPIO traits for [`DynPin`].
53//! However, whereas the type-level API uses
54//! `Error = core::convert::Infallible`, the value-level API can return a real
55//! error. If the [`DynPin`] is not in the correct [`DynPinMode`] for the
56//! operation, the trait functions will return
57//! [`InvalidPinType`](Error::InvalidPinType).
58
59#![allow(clippy::bool_comparison)]
60
61use atsamd_hal_macros::{hal_cfg, hal_macro_helper};
62
63use crate::ehal::digital::{ErrorKind, ErrorType, InputPin, OutputPin, StatefulOutputPin};
64use paste::paste;
65
66use super::pin::*;
67use super::reg::RegisterInterface;
68
69//==============================================================================
70//  DynPinMode configurations
71//==============================================================================
72
73/// Value-level `enum` for disabled configurations
74#[derive(PartialEq, Eq, Clone, Copy)]
75pub enum DynDisabled {
76    Floating,
77    PullDown,
78    PullUp,
79}
80
81/// Value-level `enum` for input configurations
82#[derive(PartialEq, Eq, Clone, Copy)]
83pub enum DynInput {
84    Floating,
85    PullDown,
86    PullUp,
87}
88
89/// Value-level `enum` for interrupt configurations
90#[derive(PartialEq, Eq, Clone, Copy)]
91pub enum DynInterrupt {
92    Floating,
93    PullDown,
94    PullUp,
95}
96
97/// Value-level `enum` for output configurations
98#[derive(PartialEq, Eq, Clone, Copy)]
99pub enum DynOutput {
100    PushPull,
101    Readable,
102}
103
104/// Value-level `enum` for alternate peripheral function configurations
105#[hal_macro_helper]
106#[derive(PartialEq, Eq, Clone, Copy)]
107pub enum DynAlternate {
108    B,
109    C,
110    D,
111    E,
112    F,
113    G,
114    #[hal_cfg(any("port-d21", "port-d5x"))]
115    H,
116    #[hal_cfg("port-d5x")]
117    I,
118    #[hal_cfg("port-d5x")]
119    J,
120    #[hal_cfg("port-d5x")]
121    K,
122    #[hal_cfg("port-d5x")]
123    L,
124    #[hal_cfg("port-d5x")]
125    M,
126    #[hal_cfg("port-d5x")]
127    N,
128}
129
130//==============================================================================
131//  DynPinMode
132//==============================================================================
133
134/// Value-level `enum` representing pin modes
135#[derive(PartialEq, Eq, Clone, Copy)]
136pub enum DynPinMode {
137    Disabled(DynDisabled),
138    Input(DynInput),
139    Interrupt(DynInterrupt),
140    Output(DynOutput),
141    Alternate(DynAlternate),
142}
143
144/// Value-level variant of [`DynPinMode`] for floating disabled mode
145pub const DYN_FLOATING_DISABLED: DynPinMode = DynPinMode::Disabled(DynDisabled::Floating);
146/// Value-level variant of [`DynPinMode`] for pull-down disabled mode
147pub const DYN_PULL_DOWN_DISABLED: DynPinMode = DynPinMode::Disabled(DynDisabled::PullDown);
148/// Value-level variant of [`DynPinMode`] for pull-up disabled mode
149pub const DYN_PULL_UP_DISABLED: DynPinMode = DynPinMode::Disabled(DynDisabled::PullUp);
150
151/// Value-level variant of [`DynPinMode`] for floating input mode
152pub const DYN_FLOATING_INPUT: DynPinMode = DynPinMode::Input(DynInput::Floating);
153/// Value-level variant of [`DynPinMode`] for pull-down input mode
154pub const DYN_PULL_DOWN_INPUT: DynPinMode = DynPinMode::Input(DynInput::PullDown);
155/// Value-level variant of [`DynPinMode`] for pull-up input mode
156pub const DYN_PULL_UP_INPUT: DynPinMode = DynPinMode::Input(DynInput::PullUp);
157
158/// Value-level variant of [`DynPinMode`] for floating interrupt mode
159pub const DYN_FLOATING_INTERRUPT: DynPinMode = DynPinMode::Interrupt(DynInterrupt::Floating);
160/// Value-level variant of [`DynPinMode`] for pull-down interrupt mode
161pub const DYN_PULL_DOWN_INTERRUPT: DynPinMode = DynPinMode::Interrupt(DynInterrupt::PullDown);
162/// Value-level variant of [`DynPinMode`] for pull-up interrupt mode
163pub const DYN_PULL_UP_INTERRUPT: DynPinMode = DynPinMode::Interrupt(DynInterrupt::PullUp);
164
165/// Value-level variant of [`DynPinMode`] for push-pull output mode
166pub const DYN_PUSH_PULL_OUTPUT: DynPinMode = DynPinMode::Output(DynOutput::PushPull);
167/// Value-level variant of [`DynPinMode`] for readable push-pull output mode
168pub const DYN_READABLE_OUTPUT: DynPinMode = DynPinMode::Output(DynOutput::Readable);
169
170macro_rules! dyn_alternate {
171    ( $($Letter:ident),+ ) => {
172        paste! {
173            $(
174                #[
175                    doc = "Value-level variant of [`DynPinMode`] for alternate "
176                    "peripheral function " $Letter
177                ]
178                pub const [<DYN_ALTERNATE_ $Letter>]: DynPinMode =
179                DynPinMode::Alternate(DynAlternate::$Letter);
180            )+
181        }
182    };
183}
184
185dyn_alternate!(B, C, D, E, F, G);
186#[hal_cfg(any("port-d21", "port-d5x"))]
187dyn_alternate!(H);
188#[hal_cfg("port-d5x")]
189dyn_alternate!(I, J, K, L, M, N);
190
191//==============================================================================
192//  DynGroup & DynPinId
193//==============================================================================
194
195/// Value-level `enum` for pin groups
196#[derive(PartialEq, Clone, Copy)]
197#[hal_macro_helper]
198pub enum DynGroup {
199    A,
200    #[hal_cfg("pin-group-b")]
201    B,
202    #[hal_cfg("pin-group-c")]
203    C,
204    #[hal_cfg("pin-group-d")]
205    D,
206}
207
208/// Value-level `struct` representing pin IDs
209#[derive(PartialEq, Clone, Copy)]
210pub struct DynPinId {
211    pub group: DynGroup,
212    pub num: u8,
213}
214
215//==============================================================================
216//  DynRegisters
217//==============================================================================
218
219/// Provide a safe register interface for [`DynPin`]s
220///
221/// This `struct` takes ownership of a [`DynPinId`] and provides an API to
222/// access the corresponding regsiters.
223struct DynRegisters {
224    id: DynPinId,
225}
226
227// [`DynRegisters`] takes ownership of the [`DynPinId`], and [`DynPin`]
228// guarantees that each pin is a singleton, so this implementation is safe.
229unsafe impl RegisterInterface for DynRegisters {
230    #[inline]
231    fn id(&self) -> DynPinId {
232        self.id
233    }
234}
235
236impl DynRegisters {
237    /// Create a new instance of [`DynRegisters`]
238    ///
239    /// # Safety
240    ///
241    /// Users must never create two simultaneous instances of this `struct` with
242    /// the same [`DynPinId`]
243    #[inline]
244    unsafe fn new(id: DynPinId) -> Self {
245        DynRegisters { id }
246    }
247}
248
249//==============================================================================
250//  Error
251//==============================================================================
252
253/// GPIO error type
254///
255/// [`DynPin`]s are not tracked and verified at compile-time, so run-time
256/// operations are fallible. This `enum` represents the corresponding errors.
257#[derive(Debug)]
258#[cfg_attr(feature = "defmt", derive(defmt::Format))]
259pub enum Error {
260    /// The pin did not have the correct ID or mode for the requested operation
261    InvalidPinType,
262}
263
264impl crate::ehal::digital::Error for Error {
265    fn kind(&self) -> crate::ehal::digital::ErrorKind {
266        ErrorKind::Other
267    }
268}
269
270//==============================================================================
271//  DynPin
272//==============================================================================
273
274/// A value-level pin, parameterized by [`DynPinId`] and [`DynPinMode`]
275///
276/// This type acts as a type-erased version of [`Pin`]. Every pin is represented
277/// by the same type, and pins are tracked and distinguished at run-time.
278pub struct DynPin {
279    regs: DynRegisters,
280    mode: DynPinMode,
281}
282
283impl DynPin {
284    /// Create a new [`DynPin`]
285    ///
286    /// # Safety
287    ///
288    /// Each [`DynPin`] must be a singleton. For a given [`DynPinId`], there
289    /// must be at most one corresponding [`DynPin`] in existence at any given
290    /// time.  Violating this requirement is `unsafe`.
291    #[inline]
292    unsafe fn new(id: DynPinId, mode: DynPinMode) -> Self {
293        DynPin {
294            regs: DynRegisters::new(id),
295            mode,
296        }
297    }
298
299    /// Return a copy of the pin ID
300    #[inline]
301    pub fn id(&self) -> DynPinId {
302        self.regs.id
303    }
304
305    /// Return a copy of the pin mode
306    #[inline]
307    pub fn mode(&self) -> DynPinMode {
308        self.mode
309    }
310
311    /// Convert the pin to the requested [`DynPinMode`]
312    #[inline]
313    pub fn into_mode(&mut self, mode: DynPinMode) {
314        // Only modify registers if we are actually changing pin mode
315        if mode != self.mode {
316            self.regs.change_mode(mode);
317            self.mode = mode;
318        }
319    }
320
321    /// Disable the pin and set it to float
322    #[inline]
323    pub fn into_floating_disabled(&mut self) {
324        self.into_mode(DYN_FLOATING_DISABLED);
325    }
326
327    /// Disable the pin and set it to pull down
328    #[inline]
329    pub fn into_pull_down_disabled(&mut self) {
330        self.into_mode(DYN_PULL_DOWN_DISABLED);
331    }
332
333    /// Disable the pin and set it to pull up
334    #[inline]
335    pub fn into_pull_up_disabled(&mut self) {
336        self.into_mode(DYN_PULL_UP_DISABLED);
337    }
338
339    /// Configure the pin to operate as a floating input
340    #[inline]
341    pub fn into_floating_input(&mut self) {
342        self.into_mode(DYN_FLOATING_INPUT);
343    }
344
345    /// Configure the pin to operate as a pulled down input
346    #[inline]
347    pub fn into_pull_down_input(&mut self) {
348        self.into_mode(DYN_PULL_DOWN_INPUT);
349    }
350
351    /// Configure the pin to operate as a pulled up input
352    #[inline]
353    pub fn into_pull_up_input(&mut self) {
354        self.into_mode(DYN_PULL_UP_INPUT);
355    }
356
357    /// Configure the pin to operate as a floating interrupt
358    #[inline]
359    pub fn into_floating_interrupt(&mut self) {
360        self.into_mode(DYN_FLOATING_INTERRUPT);
361    }
362
363    /// Configure the pin to operate as a pulled down interrupt
364    #[inline]
365    pub fn into_pull_down_interrupt(&mut self) {
366        self.into_mode(DYN_PULL_DOWN_INTERRUPT);
367    }
368
369    /// Configure the pin to operate as a pulled up interrupt
370    #[inline]
371    pub fn into_pull_up_interrupt(&mut self) {
372        self.into_mode(DYN_PULL_UP_INTERRUPT);
373    }
374
375    /// Configure the pin to operate as a push-pull output
376    #[inline]
377    pub fn into_push_pull_output(&mut self) {
378        self.into_mode(DYN_PUSH_PULL_OUTPUT);
379    }
380
381    /// Configure the pin to operate as a readable push pull output
382    #[inline]
383    pub fn into_readable_output(&mut self) {
384        self.into_mode(DYN_READABLE_OUTPUT);
385    }
386
387    /// Configure the pin to operate as the corresponding peripheral function.
388    ///
389    /// The `config` argument indicates the desired peripheral function.
390    #[inline]
391    pub fn into_alternate(&mut self, config: DynAlternate) {
392        self.into_mode(DynPinMode::Alternate(config));
393    }
394
395    /// Read the current drive strength of the pin.
396    ///
397    /// The drive strength is reset to normal on every change in pin mode.
398    #[inline]
399    pub fn get_drive_strength(&self) -> bool {
400        self.regs.read_drive_strength()
401    }
402
403    /// Set the drive strength for the pin.
404    ///
405    /// The drive strength is reset to normal on every change in pin mode.
406    #[inline]
407    pub fn set_drive_strength(&mut self, stronger: bool) {
408        self.regs.write_drive_strength(stronger);
409    }
410
411    #[inline]
412    fn _read(&self) -> Result<bool, Error> {
413        match self.mode {
414            DynPinMode::Input(_) | DYN_READABLE_OUTPUT => Ok(self.regs.read_pin()),
415            _ => Err(Error::InvalidPinType),
416        }
417    }
418    #[inline]
419    fn _write(&mut self, bit: bool) -> Result<(), Error> {
420        match self.mode {
421            DynPinMode::Output(_) => {
422                self.regs.write_pin(bit);
423                Ok(())
424            }
425            _ => Err(Error::InvalidPinType),
426        }
427    }
428    #[inline]
429    fn _toggle(&mut self) -> Result<(), Error> {
430        match self.mode {
431            DynPinMode::Output(_) => {
432                self.regs.toggle_pin();
433                Ok(())
434            }
435            _ => Err(Error::InvalidPinType),
436        }
437    }
438    #[inline]
439    fn _read_out(&self) -> Result<bool, Error> {
440        match self.mode {
441            DYN_READABLE_OUTPUT => Ok(self.regs.read_out_pin()),
442            _ => Err(Error::InvalidPinType),
443        }
444    }
445    #[inline]
446    fn _is_low(&self) -> Result<bool, Error> {
447        Ok(self._read()? == false)
448    }
449    #[inline]
450    fn _is_high(&self) -> Result<bool, Error> {
451        Ok(self._read()? == true)
452    }
453    #[inline]
454    fn _set_low(&mut self) -> Result<(), Error> {
455        self._write(false)
456    }
457    #[inline]
458    fn _set_high(&mut self) -> Result<(), Error> {
459        self._write(true)
460    }
461    #[inline]
462    fn _is_set_low(&self) -> Result<bool, Error> {
463        Ok(self._read_out()? == false)
464    }
465    #[inline]
466    fn _is_set_high(&self) -> Result<bool, Error> {
467        Ok(self._read_out()? == true)
468    }
469}
470
471//==============================================================================
472//  Convert between Pin and DynPin
473//==============================================================================
474
475impl<I, M> From<Pin<I, M>> for DynPin
476where
477    I: PinId,
478    M: PinMode,
479{
480    /// Erase the type-level information in a [`Pin`] and return a value-level
481    /// [`DynPin`]
482    #[inline]
483    fn from(_pin: Pin<I, M>) -> Self {
484        // The `Pin` is consumed, so it is safe to replace it with the
485        // corresponding `DynPin`
486        unsafe { DynPin::new(I::DYN, M::DYN) }
487    }
488}
489
490impl<I, M> TryFrom<DynPin> for Pin<I, M>
491where
492    I: PinId,
493    M: PinMode,
494{
495    type Error = Error;
496
497    /// Try to recreate a type-level [`Pin`] from a value-level [`DynPin`]
498    ///
499    /// There is no way for the compiler to know if the conversion will be
500    /// successful at compile-time. We must verify the conversion at run-time
501    /// or refuse to perform it.
502    #[inline]
503    fn try_from(pin: DynPin) -> Result<Self, Error> {
504        if pin.regs.id == I::DYN && pin.mode == M::DYN {
505            // The `DynPin` is consumed, so it is safe to replace it with the
506            // corresponding `Pin`
507            Ok(unsafe { Self::new() })
508        } else {
509            Err(Error::InvalidPinType)
510        }
511    }
512}
513
514//==============================================================================
515// Embedded HAL v1 traits
516//==============================================================================
517impl ErrorType for DynPin {
518    type Error = Error;
519}
520
521impl OutputPin for DynPin {
522    #[inline]
523    fn set_high(&mut self) -> Result<(), Self::Error> {
524        self._set_high()
525    }
526    #[inline]
527    fn set_low(&mut self) -> Result<(), Self::Error> {
528        self._set_low()
529    }
530}
531
532impl InputPin for DynPin {
533    #[inline]
534    fn is_high(&mut self) -> Result<bool, Self::Error> {
535        self._is_high()
536    }
537    #[inline]
538    fn is_low(&mut self) -> Result<bool, Self::Error> {
539        self._is_low()
540    }
541}
542
543impl StatefulOutputPin for DynPin {
544    #[inline]
545    fn is_set_high(&mut self) -> Result<bool, Self::Error> {
546        self._is_set_high()
547    }
548    #[inline]
549    fn is_set_low(&mut self) -> Result<bool, Self::Error> {
550        self._is_set_low()
551    }
552}
553
554//==============================================================================
555// Embedded HAL v0.2 traits
556//==============================================================================
557
558impl crate::ehal_02::digital::v2::OutputPin for DynPin {
559    type Error = Error;
560    #[inline]
561    fn set_high(&mut self) -> Result<(), Self::Error> {
562        self._set_high()
563    }
564    #[inline]
565    fn set_low(&mut self) -> Result<(), Self::Error> {
566        self._set_low()
567    }
568}
569
570impl crate::ehal_02::digital::v2::InputPin for DynPin {
571    type Error = Error;
572    #[inline]
573    fn is_high(&self) -> Result<bool, Self::Error> {
574        self._is_high()
575    }
576    #[inline]
577    fn is_low(&self) -> Result<bool, Self::Error> {
578        self._is_low()
579    }
580}
581
582impl crate::ehal_02::digital::v2::ToggleableOutputPin for DynPin {
583    type Error = Error;
584    #[inline]
585    fn toggle(&mut self) -> Result<(), Self::Error> {
586        self._toggle()
587    }
588}
589
590impl crate::ehal_02::digital::v2::StatefulOutputPin for DynPin {
591    #[inline]
592    fn is_set_high(&self) -> Result<bool, Self::Error> {
593        self._is_set_high()
594    }
595    #[inline]
596    fn is_set_low(&self) -> Result<bool, Self::Error> {
597        self._is_set_low()
598    }
599}