//! General Purpose Input / Output use core::convert::Infallible; use core::marker::PhantomData; use crate::rcc::Rcc; /// Extension trait to split a GPIO peripheral in independent pins and registers pub trait GpioExt { /// The parts to split the GPIO into type Parts; /// Splits the GPIO block into independent pins and registers fn split(self, rcc: &mut Rcc) -> Self::Parts; } trait GpioRegExt { fn is_low(&self, pos: u8) -> bool; fn is_set_low(&self, pos: u8) -> bool; fn set_high(&self, pos: u8); fn set_low(&self, pos: u8); } /// Alternate function 0 pub struct AF0; /// Alternate function 1 pub struct AF1; /// Alternate function 2 pub struct AF2; /// Alternate function 3 pub struct AF3; /// Alternate function 4 pub struct AF4; /// Alternate function 5 pub struct AF5; /// Alternate function 6 pub struct AF6; /// Alternate function 7 pub struct AF7; /// Alternate function mode (type state) pub struct Alternate { _mode: PhantomData, } /// Input mode (type state) pub struct Input { _mode: PhantomData, } /// Floating input (type state) pub struct Floating; /// Pulled down input (type state) pub struct PullDown; /// Pulled up input (type state) pub struct PullUp; /// Open drain input or output (type state) pub struct OpenDrain; /// Analog mode (type state) pub struct Analog; /// Output mode (type state) pub struct Output { _mode: PhantomData, } /// Push pull output (type state) pub struct PushPull; use embedded_hal::digital::v2::{toggleable, InputPin, OutputPin, StatefulOutputPin}; /// Fully erased pin pub struct Pin { i: u8, port: *const dyn GpioRegExt, _mode: PhantomData, } // NOTE(unsafe) The only write acess is to BSRR, which is thread safe unsafe impl Sync for Pin {} // NOTE(unsafe) this only enables read access to the same pin from multiple // threads unsafe impl Send for Pin {} impl StatefulOutputPin for Pin> { #[inline(always)] fn is_set_high(&self) -> Result { self.is_set_low().map(|v| !v) } #[inline(always)] fn is_set_low(&self) -> Result { Ok(unsafe { (*self.port).is_set_low(self.i) }) } } impl OutputPin for Pin> { type Error = Infallible; #[inline(always)] fn set_high(&mut self) -> Result<(), Self::Error> { unsafe { (*self.port).set_high(self.i) }; Ok(()) } #[inline(always)] fn set_low(&mut self) -> Result<(), Self::Error> { unsafe { (*self.port).set_low(self.i) } Ok(()) } } impl toggleable::Default for Pin> {} impl InputPin for Pin> { type Error = Infallible; #[inline(always)] fn is_high(&self) -> Result { self.is_low().map(|v| !v) } #[inline(always)] fn is_low(&self) -> Result { Ok(unsafe { (*self.port).is_low(self.i) }) } } impl InputPin for Pin> { type Error = Infallible; #[inline(always)] fn is_high(&self) -> Result { self.is_low().map(|v| !v) } #[inline(always)] fn is_low(&self) -> Result { Ok(unsafe { (*self.port).is_low(self.i) }) } } macro_rules! gpio_trait { ($gpiox:ident) => { impl GpioRegExt for crate::pac::$gpiox::RegisterBlock { fn is_low(&self, pos: u8) -> bool { // NOTE(unsafe) atomic read with no side effects self.idr.read().bits() & (1 << pos) == 0 } fn is_set_low(&self, pos: u8) -> bool { // NOTE(unsafe) atomic read with no side effects self.odr.read().bits() & (1 << pos) == 0 } fn set_high(&self, pos: u8) { // NOTE(unsafe) atomic write to a stateless register unsafe { self.bsrr.write(|w| w.bits(1 << pos)) } } fn set_low(&self, pos: u8) { // NOTE(unsafe) atomic write to a stateless register unsafe { self.bsrr.write(|w| w.bits(1 << (pos + 16))) } } } }; } gpio_trait!(gpioa); gpio_trait!(gpiof); macro_rules! gpio { ([$($GPIOX:ident, $gpiox:ident, $iopxenr:ident, $PXx:ident, $gate:meta => [ $($PXi:ident: ($pxi:ident, $i:expr, $MODE:ty),)+ ]),+]) => { $( /// GPIO #[cfg($gate)] pub mod $gpiox { use core::marker::PhantomData; use core::convert::Infallible; use embedded_hal::digital::v2::{InputPin, OutputPin, StatefulOutputPin, toggleable}; use crate::{ rcc::Rcc, pac::$GPIOX }; use cortex_m::interrupt::CriticalSection; use super::{ Alternate, Analog, Floating, GpioExt, Input, OpenDrain, Output, PullDown, PullUp, PushPull, AF0, AF1, AF2, AF3, AF4, AF5, AF6, AF7, Pin, GpioRegExt, }; /// GPIO parts pub struct Parts { $( /// Pin pub $pxi: $PXi<$MODE>, )+ } impl GpioExt for $GPIOX { type Parts = Parts; fn split(self, rcc: &mut Rcc) -> Parts { rcc.regs.ahbenr.modify(|_, w| w.$iopxenr().set_bit()); Parts { $( $pxi: $PXi { _mode: PhantomData }, )+ } } } fn _set_alternate_mode (index:usize, mode: u32) { let offset = 2 * index; let offset2 = 4 * index; unsafe { let reg = &(*$GPIOX::ptr()); if offset2 < 32 { reg.afrl.modify(|r, w| { w.bits((r.bits() & !(0b1111 << offset2)) | (mode << offset2)) }); } else { let offset2 = offset2 - 32; reg.afrh.modify(|r, w| { w.bits((r.bits() & !(0b1111 << offset2)) | (mode << offset2)) }); } reg.moder.modify(|r, w| { w.bits((r.bits() & !(0b11 << offset)) | (0b10 << offset)) }); } } $( /// Pin pub struct $PXi { _mode: PhantomData, } impl $PXi { /// Configures the pin to operate in AF0 mode pub fn into_alternate_af0( self, _cs: &CriticalSection ) -> $PXi> { _set_alternate_mode($i, 0); $PXi { _mode: PhantomData } } /// Configures the pin to operate in AF1 mode pub fn into_alternate_af1( self, _cs: &CriticalSection ) -> $PXi> { _set_alternate_mode($i, 1); $PXi { _mode: PhantomData } } /// Configures the pin to operate in AF2 mode pub fn into_alternate_af2( self, _cs: &CriticalSection ) -> $PXi> { _set_alternate_mode($i, 2); $PXi { _mode: PhantomData } } /// Configures the pin to operate in AF3 mode pub fn into_alternate_af3( self, _cs: &CriticalSection ) -> $PXi> { _set_alternate_mode($i, 3); $PXi { _mode: PhantomData } } /// Configures the pin to operate in AF4 mode pub fn into_alternate_af4( self, _cs: &CriticalSection ) -> $PXi> { _set_alternate_mode($i, 4); $PXi { _mode: PhantomData } } /// Configures the pin to operate in AF5 mode pub fn into_alternate_af5( self, _cs: &CriticalSection ) -> $PXi> { _set_alternate_mode($i, 5); $PXi { _mode: PhantomData } } /// Configures the pin to operate in AF6 mode pub fn into_alternate_af6( self, _cs: &CriticalSection ) -> $PXi> { _set_alternate_mode($i, 6); $PXi { _mode: PhantomData } } /// Configures the pin to operate in AF7 mode pub fn into_alternate_af7( self, _cs: &CriticalSection ) -> $PXi> { _set_alternate_mode($i, 7); $PXi { _mode: PhantomData } } /// Configures the pin to operate as a floating input pin pub fn into_floating_input( self, _cs: &CriticalSection ) -> $PXi> { let offset = 2 * $i; unsafe { let reg = &(*$GPIOX::ptr()); reg.pupdr.modify(|r, w| { w.bits((r.bits() & !(0b11 << offset)) | (0b00 << offset)) }); reg.moder.modify(|r, w| { w.bits((r.bits() & !(0b11 << offset)) | (0b00 << offset)) }); } $PXi { _mode: PhantomData } } /// Configures the pin to operate as a pulled down input pin pub fn into_pull_down_input( self, _cs: &CriticalSection ) -> $PXi> { let offset = 2 * $i; unsafe { let reg = &(*$GPIOX::ptr()); reg.pupdr.modify(|r, w| { w.bits((r.bits() & !(0b11 << offset)) | (0b10 << offset)) }); reg.moder.modify(|r, w| { w.bits((r.bits() & !(0b11 << offset)) | (0b00 << offset)) }); } $PXi { _mode: PhantomData } } /// Configures the pin to operate as a pulled up input pin pub fn into_pull_up_input( self, _cs: &CriticalSection ) -> $PXi> { let offset = 2 * $i; unsafe { let reg = &(*$GPIOX::ptr()); reg.pupdr.modify(|r, w| { w.bits((r.bits() & !(0b11 << offset)) | (0b01 << offset)) }); reg.moder.modify(|r, w| { w.bits((r.bits() & !(0b11 << offset)) | (0b00 << offset)) }); } $PXi { _mode: PhantomData } } /// Configures the pin to operate as an analog pin pub fn into_analog( self, _cs: &CriticalSection ) -> $PXi { let offset = 2 * $i; unsafe { let reg = &(*$GPIOX::ptr()); reg.pupdr.modify(|r, w| { w.bits((r.bits() & !(0b11 << offset)) | (0b00 << offset)) }); reg.moder.modify(|r, w| { w.bits((r.bits() & !(0b11 << offset)) | (0b11 << offset)) }); } $PXi { _mode: PhantomData } } /// Configures the pin to operate as an open drain output pin pub fn into_open_drain_output( self, _cs: &CriticalSection ) -> $PXi> { let offset = 2 * $i; unsafe { let reg = &(*$GPIOX::ptr()); reg.pupdr.modify(|r, w| { w.bits((r.bits() & !(0b11 << offset)) | (0b00 << offset)) }); reg.otyper.modify(|r, w| { w.bits(r.bits() | (0b1 << $i)) }); reg.moder.modify(|r, w| { w.bits((r.bits() & !(0b11 << offset)) | (0b01 << offset)) }); } $PXi { _mode: PhantomData } } /// Configures the pin to operate as an push pull output pin pub fn into_push_pull_output( self, _cs: &CriticalSection ) -> $PXi> { let offset = 2 * $i; unsafe { let reg = &(*$GPIOX::ptr()); reg.pupdr.modify(|r, w| { w.bits((r.bits() & !(0b11 << offset)) | (0b00 << offset)) }); reg.otyper.modify(|r, w| { w.bits(r.bits() & !(0b1 << $i)) }); reg.moder.modify(|r, w| { w.bits((r.bits() & !(0b11 << offset)) | (0b01 << offset)) }); } $PXi { _mode: PhantomData } } /// Configures the pin to operate as an push pull output pin with quick fall /// and rise times pub fn into_push_pull_output_hs( self, _cs: &CriticalSection ) -> $PXi> { let offset = 2 * $i; unsafe { let reg = &(*$GPIOX::ptr()); reg.pupdr.modify(|r, w| { w.bits((r.bits() & !(0b11 << offset)) | (0b00 << offset)) }); reg.otyper.modify(|r, w| { w.bits(r.bits() & !(0b1 << $i)) }); reg.ospeedr.modify(|r, w| { w.bits(r.bits() & !(0b1 << $i)) }); reg.moder.modify(|r, w| { w.bits((r.bits() & !(0b11 << offset)) | (0b01 << offset)) }); } $PXi { _mode: PhantomData } } } impl $PXi> { /// Enables / disables the internal pull up pub fn internal_pull_up(&mut self, _cs: &CriticalSection, on: bool) { let offset = 2 * $i; let value = if on { 0b01 } else { 0b00 }; unsafe { let reg = &(*$GPIOX::ptr()); reg.pupdr.modify(|r, w| { w.bits((r.bits() & !(0b11 << offset)) | (value << offset)) }); } } } impl $PXi> { /// Enables / disables the internal pull up pub fn internal_pull_up(self, _cs: &CriticalSection, on: bool) -> Self { let offset = 2 * $i; let value = if on { 0b01 } else { 0b00 }; unsafe { let reg = &(*$GPIOX::ptr()); reg.pupdr.modify(|r, w| { w.bits((r.bits() & !(0b11 << offset)) | (value << offset)) }); } self } } impl $PXi> { /// Turns pin alternate configuration pin into open drain pub fn set_open_drain(self, _cs: &CriticalSection) -> Self { let offset = $i; unsafe { let reg = &(*$GPIOX::ptr()); reg.otyper.modify(|r, w| { w.bits(r.bits() | (1 << offset)) }); } self } } impl $PXi> { /// Erases the pin number from the type /// /// This is useful when you want to collect the pins into an array where you /// need all the elements to have the same type pub fn downgrade(self) -> Pin> { Pin { i: $i, port: $GPIOX::ptr() as *const dyn GpioRegExt, _mode: self._mode, } } } impl StatefulOutputPin for $PXi> { fn is_set_high(&self) -> Result { self.is_set_low().map(|v| !v) } fn is_set_low(&self) -> Result { Ok(unsafe { (*$GPIOX::ptr()).is_set_low($i) }) } } impl OutputPin for $PXi> { type Error = Infallible; fn set_high(&mut self) -> Result<(), Self::Error> { Ok(unsafe { (*$GPIOX::ptr()).set_high($i) }) } fn set_low(&mut self) -> Result<(), Self::Error> { Ok(unsafe { (*$GPIOX::ptr()).set_low($i) }) } } impl toggleable::Default for $PXi> {} impl InputPin for $PXi> { type Error = Infallible; fn is_high(&self) -> Result { self.is_low().map(|v| !v) } fn is_low(&self) -> Result { Ok(unsafe { (*$GPIOX::ptr()).is_low($i) }) } } impl $PXi> { /// Erases the pin number from the type /// /// This is useful when you want to collect the pins into an array where you /// need all the elements to have the same type pub fn downgrade(self) -> Pin> { Pin { i: $i, port: $GPIOX::ptr() as *const dyn GpioRegExt, _mode: self._mode, } } } impl InputPin for $PXi> { type Error = Infallible; fn is_high(&self) -> Result { self.is_low().map(|v| !v) } fn is_low(&self) -> Result { Ok(unsafe { (*$GPIOX::ptr()).is_low($i) }) } } )+ } )+ } } gpio!([ GPIOA, gpioa, iopaen, PA, any( feature = "device-selected" ) => [ PA0: (pa0, 0, Input), PA1: (pa1, 1, Input), PA2: (pa2, 2, Input), PA3: (pa3, 3, Input), PA4: (pa4, 4, Input), PA5: (pa5, 5, Input), PA6: (pa6, 6, Input), PA7: (pa7, 7, Input), PA8: (pa8, 8, Input), PA9: (pa9, 9, Input), PA10: (pa10, 10, Input), PA11: (pa11, 11, Input), PA12: (pa12, 12, Input), PA13: (pa13, 13, Input), PA14: (pa14, 14, Input), PA15: (pa15, 15, Input), ], GPIOB, gpiob, iopben, PB, any( feature = "device-selected" ) => [ PB0: (pb0, 0, Input), PB1: (pb1, 1, Input), PB2: (pb2, 2, Input), PB3: (pb3, 3, Input), PB4: (pb4, 4, Input), PB5: (pb5, 5, Input), PB6: (pb6, 6, Input), PB7: (pb7, 7, Input), PB8: (pb8, 8, Input), PB9: (pb9, 9, Input), PB10: (pb10, 10, Input), PB11: (pb11, 11, Input), PB12: (pb12, 12, Input), PB13: (pb13, 13, Input), PB14: (pb14, 14, Input), PB15: (pb15, 15, Input), ], GPIOC, gpioc, iopcen, PC, any( feature = "stm32f031", feature = "stm32f038", feature = "stm32f042", feature = "stm32f048" ) => [ PC13: (pc13, 13, Input), PC14: (pc14, 14, Input), PC15: (pc15, 15, Input), ], GPIOC, gpioc, iopcen, PC, any( feature = "stm32f030", feature = "stm32f051", feature = "stm32f058", feature = "stm32f070", feature = "stm32f071", feature = "stm32f072", feature = "stm32f078", feature = "stm32f091", feature = "stm32f098" ) => [ PC0: (pc0, 0, Input), PC1: (pc1, 1, Input), PC2: (pc2, 2, Input), PC3: (pc3, 3, Input), PC4: (pc4, 4, Input), PC5: (pc5, 5, Input), PC6: (pc6, 6, Input), PC7: (pc7, 7, Input), PC8: (pc8, 8, Input), PC9: (pc9, 9, Input), PC10: (pc10, 10, Input), PC11: (pc11, 11, Input), PC12: (pc12, 12, Input), PC13: (pc13, 13, Input), PC14: (pc14, 14, Input), PC15: (pc15, 15, Input), ], GPIOD, gpiod, iopden, PD, any( feature = "stm32f030", feature = "stm32f051", feature = "stm32f058", feature = "stm32f070" ) => [ PD2: (pd2, 2, Input), ], GPIOD, gpiod, iopden, PD, any( feature = "stm32f071", feature = "stm32f072", feature = "stm32f078", feature = "stm32f091", feature = "stm32f098" ) => [ PD0: (pd0, 0, Input), PD1: (pd1, 1, Input), PD2: (pd2, 2, Input), PD3: (pd3, 3, Input), PD4: (pd4, 4, Input), PD5: (pd5, 5, Input), PD6: (pd6, 6, Input), PD7: (pd7, 7, Input), PD8: (pd8, 8, Input), PD9: (pd9, 9, Input), PD10: (pd10, 10, Input), PD11: (pd11, 11, Input), PD12: (pd12, 12, Input), PD13: (pd13, 13, Input), PD14: (pd14, 14, Input), PD15: (pd15, 15, Input), ], GPIOE, gpioe, iopeen, PE, any( feature = "stm32f071", feature = "stm32f072", feature = "stm32f078", feature = "stm32f091", feature = "stm32f098" ) => [ PE0: (pe0, 0, Input), PE1: (pe1, 1, Input), PE2: (pe2, 2, Input), PE3: (pe3, 3, Input), PE4: (pe4, 4, Input), PE5: (pe5, 5, Input), PE6: (pe6, 6, Input), PE7: (pe7, 7, Input), PE8: (pe8, 8, Input), PE9: (pe9, 9, Input), PE10: (pe10, 10, Input), PE11: (pe11, 11, Input), PE12: (pe12, 12, Input), PE13: (pe13, 13, Input), PE14: (pe14, 14, Input), PE15: (pe15, 15, Input), ], GPIOF, gpiof, iopfen, PF, any( feature = "stm32f030x4", feature = "stm32f030x6", feature = "stm32f030x8", feature = "stm32f051", feature = "stm32f058", ) => [ PF0: (pf0, 0, Input), PF1: (pf1, 1, Input), PF4: (pf4, 4, Input), PF5: (pf5, 5, Input), PF6: (pf6, 6, Input), PF7: (pf7, 7, Input), ], GPIOF, gpiof, iopfen, PF, any( feature = "stm32f030xc", feature = "stm32f070" ) => [ PF0: (pf0, 0, Input), PF1: (pf1, 1, Input), ], GPIOF, gpiof, iopfen, PF, any( feature = "stm32f031", feature = "stm32f038" ) => [ PF0: (pf0, 0, Input), PF1: (pf1, 1, Input), PF6: (pf6, 6, Input), PF7: (pf7, 7, Input), ], GPIOF, gpiof, iopfen, PF, any( feature = "stm32f042", feature = "stm32f048" ) => [ PF0: (pf0, 0, Input), PF1: (pf1, 1, Input), PF11: (pf11, 11, Input), ], GPIOF, gpiof, iopfen, PF, any( feature = "stm32f071", feature = "stm32f072", feature = "stm32f078", feature = "stm32f091", feature = "stm32f098", ) => [ PF0: (pf0, 0, Input), PF1: (pf1, 1, Input), PF2: (pf2, 2, Input), PF3: (pf3, 3, Input), PF6: (pf6, 6, Input), PF9: (pf9, 9, Input), PF10: (pf10, 10, Input), ] ]);