//! # Type-level module for GPIO pins //! //! This module provides a type-level API for GPIO pins. It uses the type system //! to track the state of pins at compile-time. Representing GPIO pins in this //! manner incurs no run-time overhead. Each [`Pin`] struct is zero-sized, so //! there is no data to copy around. Instead, real code is generated as a side //! effect of type transformations, and the resulting assembly is nearly //! identical to the equivalent, hand-written C. //! //! To track the state of pins at compile-time, this module uses traits to //! represent [type classes] and types as instances of those type classes. For //! example, the trait [`InputConfig`] acts as a [type-level enum] of the //! available input configurations, and the types [`Floating`], [`PullDown`] and //! [`PullUp`] are its type-level variants. //! //! Type-level [`Pin`]s are parameterized by two type-level enums, [`PinId`] and //! [`PinMode`]. //! //! ``` //! pub struct Pin //! where //! I: PinId, //! M: PinMode, //! { //! // ... //! } //! ``` //! //! A [`PinId`] identifies a pin by it's group (A, B) and pin number. Each //! [`PinId`] instance is named according to its datasheet identifier, e.g. //! PA02. //! //! A `PinMode` represents the various pin modes. The available `PinMode` //! variants are [`Input`], [`Output`] and [`Alternate`], each with its own corresponding //! configurations. //! //! It is not possible for users to create new instances of a [`Pin`]. Singleton //! instances of each pin are made available to users through the [`PinsA`] and //! [`PinsB`] //! struct. //! //! To create the [`PinsA`] or [`PinsB`] struct, users must supply the PAC //! [`PORTA`](crate::pac::PORTA) or [`PORTB`](crate::pac::PORTB)peripheral. //! The struct takes ownership of the port and provides the corresponding pins. Each [`Pin`] //! within the [`PinsA`] or [`PinsB`] struct can be moved out and used individually. //! The pins structure can also consume the [`IOCONFIG`] structure optionally by //! passing it as an option //! //! ``` //! let mut peripherals = Peripherals::take().unwrap(); //! let pins = PinsA::new(&mut dp.SYSCONFIG, Some(dp.IOCONFIG), dp.PORTA); //! ``` //! //! Pins can be converted between modes using several different methods. //! //! ``` //! // Use one of the literal function names //! let pa0 = pins.pa0.into_floating_input(); //! // Use a generic method and one of the `PinMode` variant types //! let pa0 = pins.pa0.into_mode::(); //! // Specify the target type and use `From`/`Into` //! let pa0: Pin = pins.pa0.into(); //! ``` //! //! # Embedded HAL traits //! //! This module implements all of the embedded HAL GPIO traits for each [`Pin`] //! in the corresponding [`PinMode`]s, namely: [`InputPin`], [`OutputPin`], //! [`ToggleableOutputPin`]. //! //! For example, you can control the logic level of an `OutputPin` like so //! //! ``` //! use atsamd_hal::pac::Peripherals; //! use atsamd_hal::gpio::v2::Pins; //! use embedded_hal::digital::v2::OutputPin; //! //! let mut peripherals = Peripherals::take().unwrap(); //! let mut pins = Pins::new(&mut dp.SYSCONFIG, Some(dp.IOCONFIG), dp.PORTA); //! pins.pa0.set_high(); //! ``` //! //! # Type-level features //! //! This module also provides additional, type-level tools to work with GPIO //! pins. //! //! The [`AnyPin`] trait defines an //! [`AnyKind`](https://docs.rs/atsamd-hal/0.13.0/atsamd_hal/typelevel/index.html) type class //! for all `Pin` types. use super::dynpins::{DynAlternate, DynGroup, DynInput, DynOutput, DynPinId, DynPinMode}; use super::reg::RegisterInterface; use crate::{ pac::{IOCONFIG, IRQSEL, PORTA, PORTB, SYSCONFIG}, typelevel::Is, utility::IrqCfg, Sealed, }; use core::convert::Infallible; use core::marker::PhantomData; use embedded_hal::digital::v2::{InputPin, OutputPin, ToggleableOutputPin}; use paste::paste; //================================================================================================== // Errors and Definitions //================================================================================================== #[derive(Debug, PartialEq, Eq)] pub enum InterruptEdge { HighToLow, LowToHigh, BothEdges, } #[derive(Debug, PartialEq, Eq)] pub enum InterruptLevel { Low = 0, High = 1, } #[derive(Debug, PartialEq, Eq)] pub enum PinState { Low = 0, High = 1, } /// GPIO error type #[derive(Debug, PartialEq, Eq)] pub enum PinError { /// The pin did not have the correct ID or mode for the requested operation. /// [`DynPin`](crate::gpio::DynPin)s are not tracked and verified at compile-time, so run-time /// operations are fallible. InvalidPinType, IsMasked, } //================================================================================================== // Input configuration //================================================================================================== /// Type-level enum for input configurations /// /// The valid options are [`Floating`], [`PullDown`] and [`PullUp`]. pub trait InputConfig: Sealed { /// Corresponding [`DynInput`](super::DynInput) const DYN: DynInput; } pub enum Floating {} pub enum PullDown {} pub enum PullUp {} impl InputConfig for Floating { const DYN: DynInput = DynInput::Floating; } impl InputConfig for PullDown { const DYN: DynInput = DynInput::PullDown; } impl InputConfig for PullUp { const DYN: DynInput = DynInput::PullUp; } impl Sealed for Floating {} impl Sealed for PullDown {} impl Sealed for PullUp {} /// Type-level variant of [`PinMode`] for floating input mode pub type InputFloating = Input; /// Type-level variant of [`PinMode`] for pull-down input mode pub type InputPullDown = Input; /// Type-level variant of [`PinMode`] for pull-up input mode pub type InputPullUp = Input; /// Type-level variant of [`PinMode`] for input modes /// /// Type `C` is one of three input configurations: [`Floating`], [`PullDown`] or /// [`PullUp`] pub struct Input { cfg: PhantomData, } impl Sealed for Input {} #[derive(Debug, PartialEq, Eq)] pub enum FilterType { SystemClock = 0, DirectInputWithSynchronization = 1, FilterOneClockCycle = 2, FilterTwoClockCycles = 3, FilterThreeClockCycles = 4, FilterFourClockCycles = 5, } pub use crate::clock::FilterClkSel; //================================================================================================== // Output configuration //================================================================================================== pub trait OutputConfig: Sealed { const DYN: DynOutput; } /// Type-level variant of [`OutputConfig`] for a push-pull configuration pub enum PushPull {} /// Type-level variant of [`OutputConfig`] for an open drain configuration pub enum OpenDrain {} /// Type-level variant of [`OutputConfig`] for a readable push-pull configuration pub enum ReadablePushPull {} /// Type-level variant of [`OutputConfig`] for a readable open-drain configuration pub enum ReadableOpenDrain {} impl Sealed for PushPull {} impl Sealed for OpenDrain {} impl Sealed for ReadableOpenDrain {} impl Sealed for ReadablePushPull {} impl OutputConfig for PushPull { const DYN: DynOutput = DynOutput::PushPull; } impl OutputConfig for OpenDrain { const DYN: DynOutput = DynOutput::OpenDrain; } impl OutputConfig for ReadablePushPull { const DYN: DynOutput = DynOutput::ReadablePushPull; } impl OutputConfig for ReadableOpenDrain { const DYN: DynOutput = DynOutput::ReadableOpenDrain; } /// Type-level variant of [`PinMode`] for output modes /// /// Type `C` is one of four output configurations: [`PushPull`], [`OpenDrain`] or /// their respective readable versions pub struct Output { cfg: PhantomData, } impl Sealed for Output {} /// Type-level variant of [`PinMode`] for push-pull output mode pub type PushPullOutput = Output; /// Type-level variant of [`PinMode`] for open drain output mode pub type OutputOpenDrain = Output; pub type OutputReadablePushPull = Output; pub type OutputReadableOpenDrain = Output; //================================================================================================== // Alternate configurations //================================================================================================== /// Type-level enum for alternate peripheral function configurations pub trait AlternateConfig: Sealed { const DYN: DynAlternate; } pub enum Funsel1 {} pub enum Funsel2 {} pub enum Funsel3 {} impl AlternateConfig for Funsel1 { const DYN: DynAlternate = DynAlternate::Funsel1; } impl AlternateConfig for Funsel2 { const DYN: DynAlternate = DynAlternate::Funsel2; } impl AlternateConfig for Funsel3 { const DYN: DynAlternate = DynAlternate::Funsel3; } impl Sealed for Funsel1 {} impl Sealed for Funsel2 {} impl Sealed for Funsel3 {} /// Type-level variant of [`PinMode`] for alternate peripheral functions /// /// Type `C` is an [`AlternateConfig`] pub struct Alternate { cfg: PhantomData, } impl Sealed for Alternate {} pub type AltFunc1 = Alternate; pub type AltFunc2 = Alternate; pub type AltFunc3 = Alternate; /// Type alias for the [`PinMode`] at reset pub type Reset = InputFloating; //================================================================================================== // Pin modes //================================================================================================== /// Type-level enum representing pin modes /// /// The valid options are [`Input`], [`Output`] and [`Alternate`]. pub trait PinMode: Sealed { /// Corresponding [`DynPinMode`](super::DynPinMode) const DYN: DynPinMode; } impl PinMode for Input { const DYN: DynPinMode = DynPinMode::Input(C::DYN); } impl PinMode for Output { const DYN: DynPinMode = DynPinMode::Output(C::DYN); } impl PinMode for Alternate { const DYN: DynPinMode = DynPinMode::Alternate(C::DYN); } //================================================================================================== // Pin IDs //================================================================================================== /// Type-level enum for pin IDs pub trait PinId: Sealed { /// Corresponding [`DynPinId`](super::DynPinId) const DYN: DynPinId; } macro_rules! pin_id { ($Group:ident, $Id:ident, $NUM:literal) => { // Need paste macro to use ident in doc attribute paste! { #[doc = "Pin ID representing pin " $Id] pub enum $Id {} impl Sealed for $Id {} impl PinId for $Id { const DYN: DynPinId = DynPinId { group: DynGroup::$Group, num: $NUM, }; } } }; } //================================================================================================== // Pin //================================================================================================== /// A type-level GPIO pin, parameterized by [`PinId`] and [`PinMode`] types pub struct Pin { pub(in crate::gpio) regs: Registers, mode: PhantomData, } impl Sealed for Pin {} impl AnyPin for Pin { type Id = I; type Mode = M; } macro_rules! common_reg_if_functions { () => { paste!( #[inline] pub fn datamask(&self) -> bool { self.regs.datamask() } #[inline] pub fn clear_datamask(self) -> Self { self.regs.clear_datamask(); self } #[inline] pub fn set_datamask(self) -> Self { self.regs.set_datamask(); self } #[inline] pub fn is_high_masked(&self) -> Result { self.regs.read_pin_masked() } #[inline] pub fn is_low_masked(&self) -> Result { self.regs.read_pin_masked().map(|v| !v) } #[inline] pub fn set_high_masked(&mut self) -> Result<(), PinError> { self.regs.write_pin_masked(true) } #[inline] pub fn set_low_masked(&mut self) -> Result<(), PinError> { self.regs.write_pin_masked(false) } fn irq_enb( &mut self, irq_cfg: crate::utility::IrqCfg, syscfg: Option<&mut va108xx::SYSCONFIG>, irqsel: Option<&mut va108xx::IRQSEL>, ) { if syscfg.is_some() { crate::clock::enable_peripheral_clock( syscfg.unwrap(), crate::clock::PeripheralClocks::Irqsel, ); } self.regs.enable_irq(); if let Some(irqsel) = irqsel { if irq_cfg.route { match self.regs.id().group { // Set the correct interrupt number in the IRQSEL register DynGroup::A => { irqsel.porta[self.regs.id().num as usize] .write(|w| unsafe { w.bits(irq_cfg.irq as u32) }); } DynGroup::B => { irqsel.portb[self.regs.id().num as usize] .write(|w| unsafe { w.bits(irq_cfg.irq as u32) }); } } } } } ); }; } pub(crate) use common_reg_if_functions; impl Pin { /// Create a new [`Pin`] /// /// # Safety /// /// Each [`Pin`] must be a singleton. For a given [`PinId`], there must be /// at most one corresponding [`Pin`] in existence at any given time. /// Violating this requirement is `unsafe`. #[inline] pub(crate) unsafe fn new() -> Pin { Pin { regs: Registers::new(), mode: PhantomData, } } /// Convert the pin to the requested [`PinMode`] #[inline] pub fn into_mode(mut self) -> Pin { // Only modify registers if we are actually changing pin mode // This check should compile away if N::DYN != M::DYN { self.regs.change_mode::(); } // Safe because we drop the existing Pin unsafe { Pin::new() } } /// Configure the pin for function select 1. See Programmer Guide p.40 for the function table #[inline] pub fn into_funsel_1(self) -> Pin { self.into_mode() } /// Configure the pin for function select 2. See Programmer Guide p.40 for the function table #[inline] pub fn into_funsel_2(self) -> Pin { self.into_mode() } /// Configure the pin for function select 3. See Programmer Guide p.40 for the function table #[inline] pub fn into_funsel_3(self) -> Pin { self.into_mode() } /// Configure the pin to operate as a floating input #[inline] pub fn into_floating_input(self) -> Pin { self.into_mode() } /// Configure the pin to operate as a pulled down input #[inline] pub fn into_pull_down_input(self) -> Pin { self.into_mode() } /// Configure the pin to operate as a pulled up input #[inline] pub fn into_pull_up_input(self) -> Pin { self.into_mode() } /// Configure the pin to operate as a push-pull output #[inline] pub fn into_push_pull_output(self) -> Pin { self.into_mode() } /// Configure the pin to operate as a readable push-pull output #[inline] pub fn into_readable_push_pull_output(self) -> Pin { self.into_mode() } /// Configure the pin to operate as a readable open-drain output #[inline] pub fn into_readable_open_drain_output(self) -> Pin { self.into_mode() } common_reg_if_functions!(); #[inline] pub(crate) fn _set_high(&mut self) { self.regs.write_pin(true) } #[inline] pub(crate) fn _set_low(&mut self) { self.regs.write_pin(false) } #[inline] pub(crate) fn _toggle(&mut self) { self.regs.toggle(); } #[inline] pub(crate) fn _is_low(&self) -> bool { !self.regs.read_pin() } #[inline] pub(crate) fn _is_high(&self) -> bool { self.regs.read_pin() } } pub type SpecificPin

= Pin<

::Id,

::Mode>; //================================================================================================== // AnyPin //================================================================================================== /// Type class for [`Pin`] types /// /// This trait uses the [`AnyKind`](https://docs.rs/atsamd-hal/0.13.0/atsamd_hal/typelevel/index.html) /// trait pattern to create a [type class] for /// [`Pin`] types. See the `AnyKind` documentation for more details on the /// pattern. pub trait AnyPin: Is> { type Id: PinId; type Mode: PinMode; } impl AsRef for Pin { #[inline] fn as_ref(&self) -> &Self { self } } impl AsMut for Pin { #[inline] fn as_mut(&mut self) -> &mut Self { self } } //================================================================================================== // Additional functionality //================================================================================================== impl Pin> { pub fn interrupt_edge( mut self, edge_type: InterruptEdge, irq_cfg: IrqCfg, syscfg: Option<&mut SYSCONFIG>, irqsel: Option<&mut IRQSEL>, ) -> Self { self.regs.interrupt_edge(edge_type); self.irq_enb(irq_cfg, syscfg, irqsel); self } pub fn interrupt_level( mut self, level_type: InterruptLevel, irq_cfg: IrqCfg, syscfg: Option<&mut SYSCONFIG>, irqsel: Option<&mut IRQSEL>, ) -> Self { self.regs.interrupt_level(level_type); self.irq_enb(irq_cfg, syscfg, irqsel); self } } impl Pin> { /// See p.53 of the programmers guide for more information. /// Possible delays in clock cycles: /// - Delay 1: 1 /// - Delay 2: 2 /// - Delay 1 + Delay 2: 3 #[inline] pub fn delay(self, delay_1: bool, delay_2: bool) -> Self { self.regs.delay(delay_1, delay_2); self } /// See p.52 of the programmers guide for more information. /// When configured for pulse mode, a given pin will set the non-default state for exactly /// one clock cycle before returning to the configured default state pub fn pulse_mode(self, enable: bool, default_state: PinState) -> Self { self.regs.pulse_mode(enable, default_state); self } pub fn interrupt_edge( mut self, edge_type: InterruptEdge, irq_cfg: IrqCfg, syscfg: Option<&mut SYSCONFIG>, irqsel: Option<&mut IRQSEL>, ) -> Self { self.regs.interrupt_edge(edge_type); self.irq_enb(irq_cfg, syscfg, irqsel); self } pub fn interrupt_level( mut self, level_type: InterruptLevel, irq_cfg: IrqCfg, syscfg: Option<&mut SYSCONFIG>, irqsel: Option<&mut IRQSEL>, ) -> Self { self.regs.interrupt_level(level_type); self.irq_enb(irq_cfg, syscfg, irqsel); self } } impl Pin> { /// See p.37 and p.38 of the programmers guide for more information. #[inline] pub fn filter_type(self, filter: FilterType, clksel: FilterClkSel) -> Self { self.regs.filter_type(filter, clksel); self } } //================================================================================================== // Embedded HAL traits //================================================================================================== impl OutputPin for Pin> { type Error = Infallible; #[inline] fn set_high(&mut self) -> Result<(), Self::Error> { self._set_high(); Ok(()) } #[inline] fn set_low(&mut self) -> Result<(), Self::Error> { self._set_low(); Ok(()) } } impl ToggleableOutputPin for Pin> { type Error = Infallible; #[inline] fn toggle(&mut self) -> Result<(), Self::Error> { self._toggle(); Ok(()) } } impl InputPin for Pin> { type Error = Infallible; #[inline] fn is_high(&self) -> Result { Ok(self._is_high()) } #[inline] fn is_low(&self) -> Result { Ok(self._is_low()) } } impl InputPin for Pin { type Error = Infallible; #[inline] fn is_high(&self) -> Result { Ok(self._is_high()) } #[inline] fn is_low(&self) -> Result { Ok(self._is_low()) } } impl InputPin for Pin { type Error = Infallible; #[inline] fn is_high(&self) -> Result { Ok(self._is_high()) } #[inline] fn is_low(&self) -> Result { Ok(self._is_low()) } } //================================================================================================== // Registers //================================================================================================== /// Provide a safe register interface for [`Pin`]s /// /// This `struct` takes ownership of a [`PinId`] and provides an API to /// access the corresponding registers. pub(in crate::gpio) struct Registers { id: PhantomData, } // [`Registers`] takes ownership of the [`PinId`], and [`Pin`] guarantees that // each pin is a singleton, so this implementation is safe. unsafe impl RegisterInterface for Registers { #[inline] fn id(&self) -> DynPinId { I::DYN } } impl Registers { /// Create a new instance of [`Registers`] /// /// # Safety /// /// Users must never create two simultaneous instances of this `struct` with /// the same [`PinId`] #[inline] unsafe fn new() -> Self { Registers { id: PhantomData } } /// Provide a type-level equivalent for the /// [`RegisterInterface::change_mode`] method. #[inline] pub(in crate::gpio) fn change_mode(&mut self) { RegisterInterface::change_mode(self, M::DYN); } } //================================================================================================== // Pin definitions //================================================================================================== macro_rules! pins { ( $Port:ident, $PinsName:ident, $($Id:ident,)+, ) => { paste!( /// Collection of all the individual [`Pin`]s for a given port (PORTA or PORTB) pub struct $PinsName { iocfg: Option, port: $Port, $( #[doc = "Pin " $Id] pub [<$Id:lower>]: Pin<$Id, Reset>, )+ } impl $PinsName { /// Create a new struct containing all the Pins. Passing the IOCONFIG peripheral /// is optional because it might be required to create pin definitions for both /// ports. #[inline] pub fn new( syscfg: &mut SYSCONFIG, iocfg: Option, port: $Port ) -> $PinsName { syscfg.peripheral_clk_enable.modify(|_, w| { w.[<$Port:lower>]().set_bit(); w.gpio().set_bit(); w.ioconfig().set_bit() }); $PinsName { iocfg, port, // Safe because we only create one `Pin` per `PinId` $( [<$Id:lower>]: unsafe { Pin::new() }, )+ } } /// Get the peripheral ID /// Safety: Read-only register pub fn get_perid() -> u32 { let port = unsafe { &(*$Port::ptr()) }; port.perid.read().bits() } /// Consumes the Pins struct and returns the port definitions pub fn release(self) -> (Option, $Port) { (self.iocfg, self.port) } } ); } } macro_rules! declare_pins { ( $Group:ident, $PinsName:ident, $Port:ident, [$(($Id:ident, $NUM:literal),)+] ) => { pins!($Port, $PinsName, $($Id,)+,); $( pin_id!($Group, $Id, $NUM); )+ } } declare_pins!( A, PinsA, PORTA, [ (PA0, 0), (PA1, 1), (PA2, 2), (PA3, 3), (PA4, 4), (PA5, 5), (PA6, 6), (PA7, 7), (PA8, 8), (PA9, 9), (PA10, 10), (PA11, 11), (PA12, 12), (PA13, 13), (PA14, 14), (PA15, 15), (PA16, 16), (PA17, 17), (PA18, 18), (PA19, 19), (PA20, 20), (PA21, 21), (PA22, 22), (PA23, 23), (PA24, 24), (PA25, 25), (PA26, 26), (PA27, 27), (PA28, 28), (PA29, 29), (PA30, 30), (PA31, 31), ] ); declare_pins!( B, PinsB, PORTB, [ (PB0, 0), (PB1, 1), (PB2, 2), (PB3, 3), (PB4, 4), (PB5, 5), (PB6, 6), (PB7, 7), (PB8, 8), (PB9, 9), (PB10, 10), (PB11, 11), (PB12, 12), (PB13, 13), (PB14, 14), (PB15, 15), (PB16, 16), (PB17, 17), (PB18, 18), (PB19, 19), (PB20, 20), (PB21, 21), (PB22, 22), (PB23, 23), ] );