//! General Purpose Input / Output // TODO the pins here currently correspond to the LQFP-48 package. There should be Cargo features // that let you select different microcontroller packages use core::marker::PhantomData; use rcc::APB2; /// Extension trait to split a GPIO peripheral in independent pins and registers pub trait GpioExt { /// The to split the GPIO into type Parts; /// Splits the GPIO block into independent pins and registers fn split(self, apb2: &mut APB2) -> Self::Parts; } /// 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; /// Output mode (type state) pub struct Output { _mode: PhantomData, } /// Push pull output (type state) pub struct PushPull; /// Open drain output (type state) pub struct OpenDrain; /// Analog mode (type state) pub struct Analog; /// Alternate function pub struct Alternate { _mode: PhantomData, } macro_rules! gpio { ($GPIOX:ident, $gpiox:ident, $gpioy:ident, $iopxenr:ident, $iopxrst:ident, $PXx:ident, [ $($PXi:ident: ($pxi:ident, $i:expr, $MODE:ty, $CR:ident),)+ ]) => { /// GPIO pub mod $gpiox { use core::marker::PhantomData; use hal::digital::{InputPin, OutputPin, StatefulOutputPin, toggleable}; use stm32::{$gpioy, $GPIOX}; use rcc::APB2; use super::{ Alternate, Floating, GpioExt, Input, OpenDrain, Output, PullDown, PullUp, PushPull, Analog, }; /// GPIO parts pub struct Parts { /// Opaque CRL register pub crl: CRL, /// Opaque CRH register pub crh: CRH, $( /// Pin pub $pxi: $PXi<$MODE>, )+ } impl GpioExt for $GPIOX { type Parts = Parts; fn split(self, apb2: &mut APB2) -> Parts { apb2.enr().modify(|_, w| w.$iopxenr().set_bit()); apb2.rstr().modify(|_, w| w.$iopxrst().set_bit()); apb2.rstr().modify(|_, w| w.$iopxrst().clear_bit()); Parts { crl: CRL { _0: () }, crh: CRH { _0: () }, $( $pxi: $PXi { _mode: PhantomData }, )+ } } } /// Opaque CRL register pub struct CRL { _0: (), } impl CRL { // NOTE(allow) we get a warning on GPIOC because it only has 3 high pins #[allow(dead_code)] pub(crate) fn cr(&mut self) -> &$gpioy::CRL { unsafe { &(*$GPIOX::ptr()).crl } } } /// Opaque CRH register pub struct CRH { _0: (), } impl CRH { pub(crate) fn cr(&mut self) -> &$gpioy::CRH { unsafe { &(*$GPIOX::ptr()).crh } } } /// Partially erased pin pub struct $PXx { i: u8, _mode: PhantomData, } impl OutputPin for $PXx> { fn set_high(&mut self) { // NOTE(unsafe) atomic write to a stateless register unsafe { (*$GPIOX::ptr()).bsrr.write(|w| w.bits(1 << self.i)) } } fn set_low(&mut self) { // NOTE(unsafe) atomic write to a stateless register unsafe { (*$GPIOX::ptr()).bsrr.write(|w| w.bits(1 << (16 + self.i))) } } } impl InputPin for $PXx> { fn is_high(&self) -> bool { !self.is_low() } fn is_low(&self) -> bool { // NOTE(unsafe) atomic read with no side effects unsafe { (*$GPIOX::ptr()).idr.read().bits() & (1 << self.i) == 0 } } } impl StatefulOutputPin for $PXx> { fn is_set_high(&self) -> bool { !self.is_set_low() } fn is_set_low(&self) -> bool { // NOTE(unsafe) atomic read with no side effects unsafe { (*$GPIOX::ptr()).odr.read().bits() & (1 << self.i) == 0 } } } impl toggleable::Default for $PXx> {} impl InputPin for $PXx> { fn is_high(&self) -> bool { !self.is_low() } fn is_low(&self) -> bool { // NOTE(unsafe) atomic read with no side effects unsafe { (*$GPIOX::ptr()).idr.read().bits() & (1 << self.i) == 0 } } } $( /// Pin pub struct $PXi { _mode: PhantomData, } impl $PXi { /// Configures the pin to operate as an alternate function push pull output pin pub fn into_alternate_push_pull( self, cr: &mut $CR, ) -> $PXi> { let offset = (4 * $i) % 32; // Alternate function output push pull let cnf = 0b10; // Output mode, max speed 50 MHz let mode = 0b11; let bits = (cnf << 2) | mode; // input mode cr .cr() .modify(|r, w| unsafe { w.bits((r.bits() & !(0b1111 << offset)) | (bits << offset)) }); $PXi { _mode: PhantomData } } pub fn into_alternate_open_drain( self, cr: &mut $CR, ) -> $PXi> { let offset = (4 * $i) % 32; // Alternate function output open drain let cnf = 0b11; // Output mode, max speed 50 MHz let mode = 0b11; let bits = (cnf << 2) | mode; // input mode cr .cr() .modify(|r, w| unsafe { w.bits((r.bits() & !(0b1111 << offset)) | (bits << offset)) }); $PXi { _mode: PhantomData } } /// Configures the pin to operate as a floating input pin pub fn into_floating_input( self, cr: &mut $CR, ) -> $PXi> { let offset = (4 * $i) % 32; // Floating input let cnf = 0b01; // Input mode let mode = 0b00; let bits = (cnf << 2) | mode; // input mode cr .cr() .modify(|r, w| unsafe { w.bits((r.bits() & !(0b1111 << offset)) | (bits << offset)) }); $PXi { _mode: PhantomData } } /// Configures the pin to operate as a pulled down input pin pub fn into_pull_down_input( self, cr: &mut $CR, ) -> $PXi> { let offset = (4 * $i) % 32; // Pull up/down input let cnf = 0b10; // Input mode let mode = 0b00; let bits = (cnf << 2) | mode; //pull down: // NOTE(unsafe) atomic write to a stateless register unsafe { (*$GPIOX::ptr()).bsrr.write(|w| w.bits(1 << (16 + $i))) } // input mode cr .cr() .modify(|r, w| unsafe { w.bits((r.bits() & !(0b1111 << offset)) | (bits << offset)) }); $PXi { _mode: PhantomData } } /// Configures the pin to operate as a pulled up input pin pub fn into_pull_up_input( self, cr: &mut $CR, ) -> $PXi> { let offset = (4 * $i) % 32; // Pull up/down input let cnf = 0b10; // Input mode let mode = 0b00; let bits = (cnf << 2) | mode; //pull up: // NOTE(unsafe) atomic write to a stateless register unsafe { (*$GPIOX::ptr()).bsrr.write(|w| w.bits(1 << $i)) } // input mode cr .cr() .modify(|r, w| unsafe { w.bits((r.bits() & !(0b1111 << offset)) | (bits << offset)) }); $PXi { _mode: PhantomData } } /// Configures the pin to operate as an open drain output pin pub fn into_open_drain_output( self, cr: &mut $CR, ) -> $PXi> { let offset = (4 * $i) % 32; // General purpose output open-drain let cnf = 0b01; // Open-Drain Output mode, max speed 50 MHz let mode = 0b11; let bits = (cnf << 2) | mode; cr .cr() .modify(|r, w| unsafe { w.bits((r.bits() & !(0b1111 << offset)) | (bits << offset)) }); $PXi { _mode: PhantomData } } /// Configures the pin to operate as an push pull output pin pub fn into_push_pull_output( self, cr: &mut $CR, ) -> $PXi> { let offset = (4 * $i) % 32; // General purpose output push-pull let cnf = 0b00; // Output mode, max speed 50 MHz let mode = 0b11; let bits = (cnf << 2) | mode; cr .cr() .modify(|r, w| unsafe { w.bits((r.bits() & !(0b1111 << offset)) | (bits << offset)) }); $PXi { _mode: PhantomData } } /// Configures the pin to operate as an analog input pin pub fn into_analog(self, cr: &mut $CR) -> $PXi { let offset = (4 * $i) % 32; // Analog input let cnf = 0b00; // Input mode let mode = 0b00; let bits = (cnf << 2) | mode; // analog mode cr .cr() .modify(|r, w| unsafe { w.bits((r.bits() & !(0b1111 << offset)) | (bits << offset)) }); $PXi { _mode: PhantomData } } } 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) -> $PXx { $PXx { i: $i, _mode: self._mode, } } } impl OutputPin for $PXi> { fn set_high(&mut self) { // NOTE(unsafe) atomic write to a stateless register unsafe { (*$GPIOX::ptr()).bsrr.write(|w| w.bits(1 << $i)) } } fn set_low(&mut self) { // NOTE(unsafe) atomic write to a stateless register unsafe { (*$GPIOX::ptr()).bsrr.write(|w| w.bits(1 << (16 + $i))) } } } impl StatefulOutputPin for $PXi> { fn is_set_high(&self) -> bool { !self.is_set_low() } fn is_set_low(&self) -> bool { // NOTE(unsafe) atomic read with no side effects unsafe { (*$GPIOX::ptr()).odr.read().bits() & (1 << $i) == 0 } } } impl toggleable::Default for $PXi> {} impl OutputPin for $PXi> { fn set_high(&mut self) { // NOTE(unsafe) atomic write to a stateless register unsafe { (*$GPIOX::ptr()).bsrr.write(|w| w.bits(1 << $i)) } } fn set_low(&mut self) { // NOTE(unsafe) atomic write to a stateless register unsafe { (*$GPIOX::ptr()).bsrr.write(|w| w.bits(1 << (16 + $i))) } } } impl StatefulOutputPin for $PXi> { fn is_set_high(&self) -> bool { !self.is_set_low() } fn is_set_low(&self) -> bool { // NOTE(unsafe) atomic read with no side effects unsafe { (*$GPIOX::ptr()).odr.read().bits() & (1 << $i) == 0 } } } impl InputPin for $PXi> { fn is_high(&self) -> bool { !self.is_low() } fn is_low(&self) -> bool { // NOTE(unsafe) atomic read with no side effects unsafe { (*$GPIOX::ptr()).idr.read().bits() & (1 << $i) == 0 } } } impl InputPin for $PXi> { fn is_high(&self) -> bool { !self.is_low() } fn is_low(&self) -> bool { // NOTE(unsafe) atomic read with no side effects unsafe { (*$GPIOX::ptr()).idr.read().bits() & (1 << $i) == 0 } } } )+ } } } gpio!(GPIOA, gpioa, gpioa, iopaen, ioparst, PAx, [ PA0: (pa0, 0, Input, CRL), PA1: (pa1, 1, Input, CRL), PA2: (pa2, 2, Input, CRL), PA3: (pa3, 3, Input, CRL), PA4: (pa4, 4, Input, CRL), PA5: (pa5, 5, Input, CRL), PA6: (pa6, 6, Input, CRL), PA7: (pa7, 7, Input, CRL), PA8: (pa8, 8, Input, CRH), PA9: (pa9, 9, Input, CRH), PA10: (pa10, 10, Input, CRH), PA11: (pa11, 11, Input, CRH), PA12: (pa12, 12, Input, CRH), PA13: (pa13, 13, Input, CRH), PA14: (pa14, 14, Input, CRH), PA15: (pa15, 15, Input, CRH), ]); gpio!(GPIOB, gpiob, gpioa, iopben, iopbrst, PBx, [ PB0: (pb0, 0, Input, CRL), PB1: (pb1, 1, Input, CRL), PB2: (pb2, 2, Input, CRL), PB3: (pb3, 3, Input, CRL), PB4: (pb4, 4, Input, CRL), PB5: (pb5, 5, Input, CRL), PB6: (pb6, 6, Input, CRL), PB7: (pb7, 7, Input, CRL), PB8: (pb8, 8, Input, CRH), PB9: (pb9, 9, Input, CRH), PB10: (pb10, 10, Input, CRH), PB11: (pb11, 11, Input, CRH), PB12: (pb12, 12, Input, CRH), PB13: (pb13, 13, Input, CRH), PB14: (pb14, 14, Input, CRH), PB15: (pb15, 15, Input, CRH), ]); gpio!(GPIOC, gpioc, gpioa, iopcen, iopcrst, PCx, [ PC13: (pc13, 13, Input, CRH), PC14: (pc14, 14, Input, CRH), PC15: (pc15, 15, Input, CRH), ]);