diff --git a/nrf-hal-common/src/gpio.rs b/nrf-hal-common/src/gpio.rs index 3cde62d9..667b6b49 100644 --- a/nrf-hal-common/src/gpio.rs +++ b/nrf-hal-common/src/gpio.rs @@ -25,6 +25,9 @@ pub struct PushPull; /// Open drain output (type state). pub struct OpenDrain; +/// Open drain input/output (type state). +pub struct OpenDrainIO; + /// Represents a digital input or output level. #[derive(Debug, Eq, PartialEq)] pub enum Level { @@ -284,6 +287,40 @@ impl Pin { pin } + /// Convert the pin to be an open-drain input/output. + /// + /// Similar to [`into_open_drain_output`](Self::into_open_drain_output), but can also be read from. + /// + /// This method currently does not support configuring an internal pull-up or pull-down + /// resistor. + pub fn into_open_drain_input_output( + self, + config: OpenDrainConfig, + initial_output: Level, + ) -> Pin> { + let mut pin = Pin { + _mode: PhantomData, + pin_port: self.pin_port, + }; + + match initial_output { + Level::Low => pin.set_low().unwrap(), + Level::High => pin.set_high().unwrap(), + } + + // This is safe, as we restrict our access to the dedicated register for this pin. + self.conf().write(|w| { + w.dir().output(); + w.input().connect(); + w.pull().disabled(); + w.drive().variant(config.variant()); + w.sense().disabled(); + w + }); + + pin + } + /// Disconnects the pin. /// /// In disconnected mode the pin cannot be used as input or output. @@ -311,6 +348,18 @@ impl InputPin for Pin> { } } +impl InputPin for Pin> { + type Error = Void; + + fn is_high(&self) -> Result { + self.is_low().map(|v| !v) + } + + fn is_low(&self) -> Result { + Ok(self.block().in_.read().bits() & (1 << self.pin()) == 0) + } +} + impl OutputPin for Pin> { type Error = Void; @@ -406,6 +455,7 @@ macro_rules! gpio { PullDown, PullUp, PushPull, + OpenDrainIO, PhantomData, $PX @@ -555,6 +605,44 @@ macro_rules! gpio { pin } + /// Convert the pin to be an open-drain input/output + /// + /// Similar to [`into_open_drain_output`](Self::into_open_drain_output), but can also be read from. + /// + /// This method currently does not support configuring an + /// internal pull-up or pull-down resistor. + pub fn into_open_drain_input_output(self, + config: OpenDrainConfig, + initial_output: Level, + ) + -> $PXi> + { + let mut pin = $PXi { + _mode: PhantomData, + }; + + match initial_output { + Level::Low => pin.set_low().unwrap(), + Level::High => pin.set_high().unwrap(), + } + + // This is safe, as we restrict our access to the + // dedicated register for this pin. + let pin_cnf = unsafe { + &(*$PX::ptr()).pin_cnf[$i] + }; + pin_cnf.write(|w| { + w.dir().output(); + w.input().connect(); + w.pull().disabled(); + w.drive().variant(config.variant()); + w.sense().disabled(); + w + }); + + pin + } + /// Disconnects the pin. /// /// In disconnected mode the pin cannot be used as input or output. @@ -586,6 +674,18 @@ macro_rules! gpio { } } + impl InputPin for $PXi> { + type Error = Void; + + fn is_high(&self) -> Result { + self.is_low().map(|v| !v) + } + + fn is_low(&self) -> Result { + Ok(unsafe { ((*$PX::ptr()).in_.read().bits() & (1 << $i)) == 0 }) + } + } + impl From<$PXi> for Pin { fn from(value: $PXi) -> Self { value.degrade()