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}