diff --git a/embassy-rp/src/gpio.rs b/embassy-rp/src/gpio.rs index 5fdb2b0c..12b9f6ac 100644 --- a/embassy-rp/src/gpio.rs +++ b/embassy-rp/src/gpio.rs @@ -86,7 +86,6 @@ pub struct Output<'d, T: Pin> { } impl<'d, T: Pin> Output<'d, T> { - // TODO opendrain pub fn new(pin: impl Unborrow + 'd, initial_output: Level) -> Self { unborrow!(pin); @@ -155,6 +154,89 @@ impl<'d, T: Pin> Drop for Output<'d, T> { } } +/// GPIO output open-drain. +pub struct OutputOpenDrain<'d, T: Pin> { + pin: T, + phantom: PhantomData<&'d mut T>, +} + +impl<'d, T: Pin> OutputOpenDrain<'d, T> { + #[inline] + pub fn new(pin: impl Unborrow + 'd, initial_output: Level) -> Self { + unborrow!(pin); + + unsafe { + let val = 1 << pin.pin(); + pin.io().ctrl().write(|w| { + w.set_funcsel(pac::io::vals::Gpio0CtrlFuncsel::SIO_0.0); + }); + pin.sio_out().value_clr().write_value(val); + + match initial_output { + Level::High => { + // For Open Drain High, disable the output pin. + pin.sio_oe().value_clr().write_value(val); + } + Level::Low => { + // For Open Drain Low, enable the output pin. + pin.sio_oe().value_set().write_value(val); + } + } + } + + Self { + pin, + phantom: PhantomData, + } + } + + /// Set the output as high. + #[inline] + pub fn set_high(&mut self) { + // For Open Drain High, disable the output pin. + unsafe { + self.pin + .sio_oe() + .value_clr() + .write_value(1 << self.pin.pin()); + } + } + + /// Set the output as low. + #[inline] + pub fn set_low(&mut self) { + // For Open Drain Low, enable the output pin. + unsafe { + self.pin + .sio_oe() + .value_set() + .write_value(1 << self.pin.pin()); + } + } + + /// Is the output level high? + #[inline] + pub fn is_set_high(&self) -> bool { + let val = 1 << self.pin.pin(); + unsafe { (self.pin.sio_oe().value().read() & val) == 0 } + } + + /// Is the output level low? + #[inline] + pub fn is_set_low(&self) -> bool { + !self.is_set_high() + } + + /// Toggle pin output + #[inline] + pub fn toggle(&mut self) { + let val = 1 << self.pin.pin(); + unsafe { + self.pin.sio_out().value_xor().write_value(val); + } + } +} + pub(crate) mod sealed { use super::*; @@ -322,6 +404,38 @@ mod eh02 { Ok(self.toggle()) } } + + impl<'d, T: Pin> embedded_hal_02::digital::v2::OutputPin for OutputOpenDrain<'d, T> { + type Error = Infallible; + + #[inline] + fn set_high(&mut self) -> Result<(), Self::Error> { + Ok(self.set_high()) + } + + #[inline] + fn set_low(&mut self) -> Result<(), Self::Error> { + Ok(self.set_low()) + } + } + + impl<'d, T: Pin> embedded_hal_02::digital::v2::StatefulOutputPin for OutputOpenDrain<'d, T> { + fn is_set_high(&self) -> Result { + Ok(self.is_set_high()) + } + + fn is_set_low(&self) -> Result { + Ok(self.is_set_low()) + } + } + + impl<'d, T: Pin> embedded_hal_02::digital::v2::ToggleableOutputPin for OutputOpenDrain<'d, T> { + type Error = Infallible; + #[inline] + fn toggle(&mut self) -> Result<(), Self::Error> { + Ok(self.toggle()) + } + } } #[cfg(feature = "unstable-traits")]