diff --git a/embedded-hal-async/src/digital.rs b/embedded-hal-async/src/digital.rs index 5efe3d18..b7f1f4da 100644 --- a/embedded-hal-async/src/digital.rs +++ b/embedded-hal-async/src/digital.rs @@ -1,5 +1,12 @@ //! Asynchronous digital I/O. //! +//! The [`OutputPin`], [`StatefulOutputPin`] and [`InputPin`] traits are `async` variants +//! of the [blocking traits](embedded_hal::digital). These traits are useful for when +//! digital I/O may block execution, such as access through an I/O expander or over some +//! other transport. +//! +//! The [`Wait`] trait allows asynchronously waiting for a change in pin level. +//! //! # Example //! //! ```rust @@ -15,7 +22,134 @@ //! .expect("failed to await input pin") //! } //! ``` -pub use embedded_hal::digital::{Error, ErrorKind, ErrorType}; +//! +//! # For HAL authors +//! +//! If the digital I/O is implemented using memory mapped I/O and acts immediately, then the async traits +//! (except for [`Wait`]) can be implemented by calling the blocking traits and wrapping the result in +//! [`Poll::Ready`](core::task::Poll::Ready). +pub use embedded_hal::digital::{Error, ErrorKind, ErrorType, PinState}; + +/// Asynchronous single digital push-pull output pin. +pub trait OutputPin: ErrorType { + /// Drives the pin low. + /// + /// This returns [`Ready`](core::task::Poll::Ready) when the pin has been driven low. + /// + /// *NOTE* the actual electrical state of the pin may not actually be low, e.g. due to external + /// electrical sources. + async fn set_low(&mut self) -> Result<(), Self::Error>; + + /// Drives the pin high. + /// + /// This returns [`Ready`](core::task::Poll::Ready) when the pin has been driven high. + /// + /// *NOTE* the actual electrical state of the pin may not actually be high, e.g. due to external + /// electrical sources. + async fn set_high(&mut self) -> Result<(), Self::Error>; + + /// Drives the pin high or low depending on the provided value. + /// + /// This returns [`Ready`](core::task::Poll::Ready) when the pin has been driven to the provided state. + /// + /// *NOTE* the actual electrical state of the pin may not actually be high or low, e.g. due to external + /// electrical sources. + #[inline] + async fn set_state(&mut self, state: PinState) -> Result<(), Self::Error> { + match state { + PinState::Low => self.set_low().await, + PinState::High => self.set_high().await, + } + } +} + +impl OutputPin for &mut T { + #[inline] + async fn set_low(&mut self) -> Result<(), Self::Error> { + T::set_low(self).await + } + + #[inline] + async fn set_high(&mut self) -> Result<(), Self::Error> { + T::set_high(self).await + } + + #[inline] + async fn set_state(&mut self, state: PinState) -> Result<(), Self::Error> { + T::set_state(self, state).await + } +} + +/// Asynchronous push-pull output pin that can read its output state. +pub trait StatefulOutputPin: OutputPin { + /// Is the pin in drive high mode? + /// + /// This returns [`Ready`](core::task::Poll::Ready) when the pin's drive mode been read. + /// + /// *NOTE* this does *not* read the electrical state of the pin. + async fn is_set_high(&mut self) -> Result; + + /// Is the pin in drive low mode? + /// + /// This returns [`Ready`](core::task::Poll::Ready) when the pin's drive mode been read. + /// + /// *NOTE* this does *not* read the electrical state of the pin. + async fn is_set_low(&mut self) -> Result; + + /// Toggle pin output. + /// + /// This returns [`Ready`](core::task::Poll::Ready) when the pin has been toggled. + async fn toggle(&mut self) -> Result<(), Self::Error> { + let was_low: bool = self.is_set_low().await?; + self.set_state(PinState::from(was_low)).await + } +} + +impl StatefulOutputPin for &mut T { + #[inline] + async fn is_set_high(&mut self) -> Result { + T::is_set_high(self).await + } + + #[inline] + async fn is_set_low(&mut self) -> Result { + T::is_set_low(self).await + } + + #[inline] + async fn toggle(&mut self) -> Result<(), Self::Error> { + T::toggle(self).await + } +} + +/// Asynchronous single digital input pin. +pub trait InputPin: ErrorType { + /// Is the input pin high? + /// + /// This returns [`Ready`](core::task::Poll::Ready) when the pin's electrical state has been read. + /// + /// *NOTE* the input state of the pin may have changed before the future is polled. + async fn is_high(&mut self) -> Result; + + /// Is the input pin low? + /// + /// This returns [`Ready`](core::task::Poll::Ready) when the pin's electrical state has been read. + /// + /// *NOTE* the input state of the pin may have changed before the future is polled. + async fn is_low(&mut self) -> Result; +} + +impl InputPin for &mut T { + #[inline] + async fn is_high(&mut self) -> Result { + T::is_high(self).await + } + + #[inline] + async fn is_low(&mut self) -> Result { + T::is_low(self).await + } +} /// Asynchronously wait for GPIO pin state. pub trait Wait: ErrorType { diff --git a/embedded-hal/src/digital.rs b/embedded-hal/src/digital.rs index a38040c0..581f2ca3 100644 --- a/embedded-hal/src/digital.rs +++ b/embedded-hal/src/digital.rs @@ -1,4 +1,4 @@ -//! Digital I/O. +//! Blocking Digital I/O. use core::ops::Not;