From 209f713e066ea79133dc22eaba59bc95517f6b94 Mon Sep 17 00:00:00 2001 From: Finomnis Date: Wed, 21 Sep 2022 00:31:11 +0200 Subject: [PATCH 1/6] Prototype open drain io pin state --- nrf-hal-common/src/gpio.rs | 178 +++++++++++++++++++++++++++++++++++++ 1 file changed, 178 insertions(+) diff --git a/nrf-hal-common/src/gpio.rs b/nrf-hal-common/src/gpio.rs index 3cde62d9..6f03dd86 100644 --- a/nrf-hal-common/src/gpio.rs +++ b/nrf-hal-common/src/gpio.rs @@ -25,6 +25,14 @@ pub struct PushPull; /// Open drain output (type state). pub struct OpenDrain; +/// Input/Output combined mode (type state). +pub struct InOut { + _mode: PhantomData, +} + +/// 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 +292,40 @@ impl Pin { pin } + /// Convert the pin to be an open-drain input/output. + /// + /// Similar to [`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 +353,56 @@ 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; + + /// Set the output as high. + fn set_high(&mut self) -> Result<(), Self::Error> { + // NOTE(unsafe) atomic write to a stateless register - TODO(AJM) verify? + // TODO - I wish I could do something like `.pins$i()`... + unsafe { + self.block().outset.write(|w| w.bits(1u32 << self.pin())); + } + Ok(()) + } + + /// Set the output as low. + fn set_low(&mut self) -> Result<(), Self::Error> { + // NOTE(unsafe) atomic write to a stateless register - TODO(AJM) verify? + // TODO - I wish I could do something like `.pins$i()`... + unsafe { + self.block().outclr.write(|w| w.bits(1u32 << self.pin())); + } + Ok(()) + } +} + +impl StatefulOutputPin for Pin> { + /// Is the output pin set as high? + fn is_set_high(&self) -> Result { + self.is_set_low().map(|v| !v) + } + + /// Is the output pin set as low? + fn is_set_low(&self) -> Result { + // NOTE(unsafe) atomic read with no side effects - TODO(AJM) verify? + // TODO - I wish I could do something like `.pins$i()`... + Ok(self.block().out.read().bits() & (1 << self.pin()) == 0) + } +} + impl OutputPin for Pin> { type Error = Void; @@ -406,6 +498,8 @@ macro_rules! gpio { PullDown, PullUp, PushPull, + InOut, + OpenDrainIO, PhantomData, $PX @@ -555,6 +649,44 @@ macro_rules! gpio { pin } + /// Convert the pin to be an open-drain input/output + /// + /// Similar to [`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 +718,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() @@ -625,6 +769,40 @@ macro_rules! gpio { Ok(unsafe { ((*$PX::ptr()).out.read().bits() & (1 << $i)) == 0 }) } } + + impl OutputPin for $PXi> { + type Error = Void; + + /// Set the output as high + fn set_high(&mut self) -> Result<(), Self::Error> { + // NOTE(unsafe) atomic write to a stateless register - TODO(AJM) verify? + // TODO - I wish I could do something like `.pins$i()`... + unsafe { (*$PX::ptr()).outset.write(|w| w.bits(1u32 << $i)); } + Ok(()) + } + + /// Set the output as low + fn set_low(&mut self) -> Result<(), Self::Error> { + // NOTE(unsafe) atomic write to a stateless register - TODO(AJM) verify? + // TODO - I wish I could do something like `.pins$i()`... + unsafe { (*$PX::ptr()).outclr.write(|w| w.bits(1u32 << $i)); } + Ok(()) + } + } + + impl StatefulOutputPin for $PXi> { + /// Is the output pin set as high? + fn is_set_high(&self) -> Result { + self.is_set_low().map(|v| !v) + } + + /// Is the output pin set as low? + fn is_set_low(&self) -> Result { + // NOTE(unsafe) atomic read with no side effects - TODO(AJM) verify? + // TODO - I wish I could do something like `.pins$i()`... + Ok(unsafe { ((*$PX::ptr()).out.read().bits() & (1 << $i)) == 0 }) + } + } )+ } } From 9500e979b5f57e13c49cb33f2a26e53de001be38 Mon Sep 17 00:00:00 2001 From: Finomnis Date: Wed, 21 Sep 2022 00:42:33 +0200 Subject: [PATCH 2/6] Rewrite open drain IO proposal to use Output --- nrf-hal-common/src/gpio.rs | 86 ++------------------------------------ 1 file changed, 4 insertions(+), 82 deletions(-) diff --git a/nrf-hal-common/src/gpio.rs b/nrf-hal-common/src/gpio.rs index 6f03dd86..38b0125e 100644 --- a/nrf-hal-common/src/gpio.rs +++ b/nrf-hal-common/src/gpio.rs @@ -25,11 +25,6 @@ pub struct PushPull; /// Open drain output (type state). pub struct OpenDrain; -/// Input/Output combined mode (type state). -pub struct InOut { - _mode: PhantomData, -} - /// Open drain input/output (type state). pub struct OpenDrainIO; @@ -302,7 +297,7 @@ impl Pin { self, config: OpenDrainConfig, initial_output: Level, - ) -> Pin> { + ) -> Pin> { let mut pin = Pin { _mode: PhantomData, pin_port: self.pin_port, @@ -353,7 +348,7 @@ impl InputPin for Pin> { } } -impl InputPin for Pin> { +impl InputPin for Pin> { type Error = Void; fn is_high(&self) -> Result { @@ -365,44 +360,6 @@ impl InputPin for Pin> { } } -impl OutputPin for Pin> { - type Error = Void; - - /// Set the output as high. - fn set_high(&mut self) -> Result<(), Self::Error> { - // NOTE(unsafe) atomic write to a stateless register - TODO(AJM) verify? - // TODO - I wish I could do something like `.pins$i()`... - unsafe { - self.block().outset.write(|w| w.bits(1u32 << self.pin())); - } - Ok(()) - } - - /// Set the output as low. - fn set_low(&mut self) -> Result<(), Self::Error> { - // NOTE(unsafe) atomic write to a stateless register - TODO(AJM) verify? - // TODO - I wish I could do something like `.pins$i()`... - unsafe { - self.block().outclr.write(|w| w.bits(1u32 << self.pin())); - } - Ok(()) - } -} - -impl StatefulOutputPin for Pin> { - /// Is the output pin set as high? - fn is_set_high(&self) -> Result { - self.is_set_low().map(|v| !v) - } - - /// Is the output pin set as low? - fn is_set_low(&self) -> Result { - // NOTE(unsafe) atomic read with no side effects - TODO(AJM) verify? - // TODO - I wish I could do something like `.pins$i()`... - Ok(self.block().out.read().bits() & (1 << self.pin()) == 0) - } -} - impl OutputPin for Pin> { type Error = Void; @@ -498,7 +455,6 @@ macro_rules! gpio { PullDown, PullUp, PushPull, - InOut, OpenDrainIO, PhantomData, @@ -659,7 +615,7 @@ macro_rules! gpio { config: OpenDrainConfig, initial_output: Level, ) - -> $PXi> + -> $PXi> { let mut pin = $PXi { _mode: PhantomData, @@ -718,7 +674,7 @@ macro_rules! gpio { } } - impl InputPin for $PXi> { + impl InputPin for $PXi> { type Error = Void; fn is_high(&self) -> Result { @@ -769,40 +725,6 @@ macro_rules! gpio { Ok(unsafe { ((*$PX::ptr()).out.read().bits() & (1 << $i)) == 0 }) } } - - impl OutputPin for $PXi> { - type Error = Void; - - /// Set the output as high - fn set_high(&mut self) -> Result<(), Self::Error> { - // NOTE(unsafe) atomic write to a stateless register - TODO(AJM) verify? - // TODO - I wish I could do something like `.pins$i()`... - unsafe { (*$PX::ptr()).outset.write(|w| w.bits(1u32 << $i)); } - Ok(()) - } - - /// Set the output as low - fn set_low(&mut self) -> Result<(), Self::Error> { - // NOTE(unsafe) atomic write to a stateless register - TODO(AJM) verify? - // TODO - I wish I could do something like `.pins$i()`... - unsafe { (*$PX::ptr()).outclr.write(|w| w.bits(1u32 << $i)); } - Ok(()) - } - } - - impl StatefulOutputPin for $PXi> { - /// Is the output pin set as high? - fn is_set_high(&self) -> Result { - self.is_set_low().map(|v| !v) - } - - /// Is the output pin set as low? - fn is_set_low(&self) -> Result { - // NOTE(unsafe) atomic read with no side effects - TODO(AJM) verify? - // TODO - I wish I could do something like `.pins$i()`... - Ok(unsafe { ((*$PX::ptr()).out.read().bits() & (1 << $i)) == 0 }) - } - } )+ } } From ab9b73eecfa63acedc5e1435febba48036466746 Mon Sep 17 00:00:00 2001 From: Finomnis Date: Wed, 21 Sep 2022 00:45:23 +0200 Subject: [PATCH 3/6] Fix typo --- nrf-hal-common/src/gpio.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nrf-hal-common/src/gpio.rs b/nrf-hal-common/src/gpio.rs index 38b0125e..667b6b49 100644 --- a/nrf-hal-common/src/gpio.rs +++ b/nrf-hal-common/src/gpio.rs @@ -289,7 +289,7 @@ impl Pin { /// Convert the pin to be an open-drain input/output. /// - /// Similar to [`open_drain_output`], but can also be read from. + /// 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. @@ -607,7 +607,7 @@ macro_rules! gpio { /// Convert the pin to be an open-drain input/output /// - /// Similar to [`open_drain_output`], but can also be read from. + /// 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. From da24389ad439321b63dd6e6df9b246e9df892ba0 Mon Sep 17 00:00:00 2001 From: Finomnis Date: Wed, 21 Sep 2022 01:02:44 +0200 Subject: [PATCH 4/6] Implement IoPin for OpenDrainIO --- nrf-hal-common/src/gpio.rs | 30 ++++++++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/nrf-hal-common/src/gpio.rs b/nrf-hal-common/src/gpio.rs index 667b6b49..a94fa16b 100644 --- a/nrf-hal-common/src/gpio.rs +++ b/nrf-hal-common/src/gpio.rs @@ -85,7 +85,7 @@ use crate::pac::P1; #[cfg(feature = "5340-net")] use crate::pac::P1_NS as P1; -use crate::hal::digital::v2::{InputPin, OutputPin, StatefulOutputPin}; +use crate::hal::digital::v2::{InputPin, IoPin, OutputPin, PinState, StatefulOutputPin}; use void::Void; impl Pin { @@ -360,6 +360,19 @@ impl InputPin for Pin> { } } +impl IoPin>, Pin>> for Pin> { + type Error = Void; + + fn into_input_pin(self) -> Result>, Self::Error> { + Ok(self) + } + + fn into_output_pin(mut self, state: PinState) -> Result>, Self::Error> { + self.set_state(state)?; + Ok(self) + } +} + impl OutputPin for Pin> { type Error = Void; @@ -461,7 +474,7 @@ macro_rules! gpio { $PX }; - use crate::hal::digital::v2::{OutputPin, StatefulOutputPin, InputPin}; + use crate::hal::digital::v2::{InputPin, IoPin, OutputPin, PinState, StatefulOutputPin}; use void::Void; @@ -686,6 +699,19 @@ macro_rules! gpio { } } + impl IoPin<$PXi>, $PXi>> for $PXi> { + type Error = Void; + + fn into_input_pin(self) -> Result<$PXi>, Self::Error> { + Ok(self) + } + + fn into_output_pin(mut self, state: PinState) -> Result<$PXi>, Self::Error> { + self.set_state(state)?; + Ok(self) + } + } + impl From<$PXi> for Pin { fn from(value: $PXi) -> Self { value.degrade() From 4ca24cf655c0f4a8d239e8a145787b468c6ebcd7 Mon Sep 17 00:00:00 2001 From: Finomnis Date: Wed, 21 Sep 2022 01:21:24 +0200 Subject: [PATCH 5/6] Beautify IoPin impls --- nrf-hal-common/src/gpio.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/nrf-hal-common/src/gpio.rs b/nrf-hal-common/src/gpio.rs index a94fa16b..900db556 100644 --- a/nrf-hal-common/src/gpio.rs +++ b/nrf-hal-common/src/gpio.rs @@ -360,14 +360,14 @@ impl InputPin for Pin> { } } -impl IoPin>, Pin>> for Pin> { +impl IoPin for Pin> { type Error = Void; - fn into_input_pin(self) -> Result>, Self::Error> { + fn into_input_pin(self) -> Result { Ok(self) } - fn into_output_pin(mut self, state: PinState) -> Result>, Self::Error> { + fn into_output_pin(mut self, state: PinState) -> Result { self.set_state(state)?; Ok(self) } @@ -699,14 +699,14 @@ macro_rules! gpio { } } - impl IoPin<$PXi>, $PXi>> for $PXi> { + impl IoPin for $PXi> { type Error = Void; - fn into_input_pin(self) -> Result<$PXi>, Self::Error> { + fn into_input_pin(self) -> Result { Ok(self) } - fn into_output_pin(mut self, state: PinState) -> Result<$PXi>, Self::Error> { + fn into_output_pin(mut self, state: PinState) -> Result { self.set_state(state)?; Ok(self) } From 2520dd4f6182a2d65e0b71b539be3947bc69819b Mon Sep 17 00:00:00 2001 From: Finomnis Date: Wed, 21 Sep 2022 01:28:40 +0200 Subject: [PATCH 6/6] Remove IoPin again, as it was removed from embedded-hal --- nrf-hal-common/src/gpio.rs | 30 ++---------------------------- 1 file changed, 2 insertions(+), 28 deletions(-) diff --git a/nrf-hal-common/src/gpio.rs b/nrf-hal-common/src/gpio.rs index 900db556..667b6b49 100644 --- a/nrf-hal-common/src/gpio.rs +++ b/nrf-hal-common/src/gpio.rs @@ -85,7 +85,7 @@ use crate::pac::P1; #[cfg(feature = "5340-net")] use crate::pac::P1_NS as P1; -use crate::hal::digital::v2::{InputPin, IoPin, OutputPin, PinState, StatefulOutputPin}; +use crate::hal::digital::v2::{InputPin, OutputPin, StatefulOutputPin}; use void::Void; impl Pin { @@ -360,19 +360,6 @@ impl InputPin for Pin> { } } -impl IoPin for Pin> { - type Error = Void; - - fn into_input_pin(self) -> Result { - Ok(self) - } - - fn into_output_pin(mut self, state: PinState) -> Result { - self.set_state(state)?; - Ok(self) - } -} - impl OutputPin for Pin> { type Error = Void; @@ -474,7 +461,7 @@ macro_rules! gpio { $PX }; - use crate::hal::digital::v2::{InputPin, IoPin, OutputPin, PinState, StatefulOutputPin}; + use crate::hal::digital::v2::{OutputPin, StatefulOutputPin, InputPin}; use void::Void; @@ -699,19 +686,6 @@ macro_rules! gpio { } } - impl IoPin for $PXi> { - type Error = Void; - - fn into_input_pin(self) -> Result { - Ok(self) - } - - fn into_output_pin(mut self, state: PinState) -> Result { - self.set_state(state)?; - Ok(self) - } - } - impl From<$PXi> for Pin { fn from(value: $PXi) -> Self { value.degrade()