Skip to content

Commit

Permalink
Add asynchronous versions of most HAL traits
Browse files Browse the repository at this point in the history
  • Loading branch information
dflemstr committed Apr 23, 2020
1 parent 181d44b commit 7dc6f28
Show file tree
Hide file tree
Showing 26 changed files with 1,336 additions and 0 deletions.
3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,12 @@ repository = "https://github.com/rust-embedded/embedded-hal"
version = "0.2.3"

[dependencies]
futures = { version = "0.3.4", optional = true }
nb = { version = "0.1.1", features = ["unstable"] }

[dev-dependencies]
stm32f3 = { version = "0.8", features = ["stm32f303", "rt"] }
futures = "0.1.17"

[features]
asynchronous = ["futures"]
201 changes: 201 additions & 0 deletions src/asynchronous/gpio.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,201 @@
//! General input/output pins.
//!
//! The [`InputPin`] and [`OutputPin`] traits define pins that can be read and written digitally
//! (i.e. either in a low or high state).
//!
//! There are additionally various `Into*` traits that allow users to re-configure pins to switch
//! between different modes of operation, e.g. [`IntoFloatingInputPin`] turns a pin into an
//! [`InputPin`] that does not employ any pull-up or pull-down resistors.
use core::pin;
use core::task;

pub mod get;
pub mod set;

/// A generic pin that can't be interacted with.
pub trait Pin {
/// The common error type for all pin operations.
///
/// A single error type for all operations is enforced for simplicity.
type Error;
}

/// A pin that can be read from.
pub trait InputPin: Pin {
/// Polls a read operation of this pin to completion.
fn poll_get(
self: pin::Pin<&mut Self>,
cx: &mut task::Context<'_>,
) -> task::Poll<Result<bool, Self::Error>>;
}

/// Extension functions for instances of [`InputPin`].
pub trait InputPinExt: InputPin {
/// Gets the current high or low state of this pin.
fn get(&mut self) -> get::Get<Self>
where
Self: Unpin,
{
get::get(self)
}
}

impl<A> InputPinExt for A where A: InputPin {}

/// A pin that can be written to.
pub trait OutputPin: Pin {
/// Polls a write operation of this pin to completion.
fn poll_set(
self: pin::Pin<&mut Self>,
cx: &mut task::Context<'_>,
high: bool,
) -> task::Poll<Result<(), Self::Error>>;
}

/// Extension functions for instances of [`OutputPin`].
pub trait OutputPinExt: OutputPin {
/// Sets the current high or low state of this pin.
fn set(&mut self, high: bool) -> set::Set<Self>
where
Self: Unpin,
{
set::set(self, high)
}
}

impl<A> OutputPinExt for A where A: OutputPin {}

/// A pin that can be turned into an [`InputPin`] that does not employ any pull-up or pull-down
/// resistors.
pub trait IntoFloatingInputPin: Pin {
/// The type of an [`InputPin`] that does not employ any pull-up or pull-down resistors.
type FloatingInputPin: InputPin<Error = Self::Error> + Unpin;

/// Attempts to re-configure this pin into the new mode.
fn into_floating_input_pin(self) -> Result<Self::FloatingInputPin, Self::Error>;
}

/// A pin that can be turned into an [`InputPin`] that has a pull-up resistor attached.
pub trait IntoPullUpInputPin: Pin {
/// The type of an [`InputPin`] that has a pull-up resistor attached.
type PullUpInputPin: InputPin<Error = Self::Error> + Unpin;

/// Attempts to re-configure this pin into the new mode.
fn into_pull_up_input_pin(self) -> Result<Self::PullUpInputPin, Self::Error>;
}

/// A pin that can be turned into an [`InputPin`] that has a pull-down resistor attached.
pub trait IntoPullDownInputPin: Pin {
/// The type of an [`InputPin`] that has a pull-down resistor attached.
type PullDownInputPin: InputPin<Error = Self::Error> + Unpin;

/// Attempts to re-configure this pin into the new mode.
fn into_pull_down_input_pin(self) -> Result<Self::PullDownInputPin, Self::Error>;
}

/// A pin that can be turned into an [`OutputPin`] that is in open drain mode.
pub trait IntoOpenDrainOutputPin: Pin {
/// The type of an [`OutputPin`] that is in open drain mode.
type OpenDrainOutputPin: OutputPin<Error = Self::Error> + Unpin;

/// Attempts to re-configure this pin into the new mode.
fn into_open_drain_output_pin(
self,
initial_high: bool,
) -> Result<Self::OpenDrainOutputPin, Self::Error>;
}

/// A pin that can be turned into an [`OutputPin`] that is in push-pull mode.
pub trait IntoPushPullOutputPin: Pin {
/// The type of an [`OutputPin`] that is in push-pull mode.
type PushPullOutputPin: OutputPin<Error = Self::Error> + Unpin;

/// Attempts to re-configure this pin into the new mode.
fn into_push_pull_output_pin(
self,
initial_high: bool,
) -> Result<Self::PushPullOutputPin, Self::Error>;
}

/// A virtual pin that is not actually connected to a physical pin.
///
/// The pin will always read a fixed value, can be configured to be in any mode, and will always
/// have writes result in no-ops.
#[derive(Clone, Copy, Debug)]
pub struct NoConnect(bool);

impl NoConnect {
/// Creates a new [`NoConnect`] that will always read the specified high/low value.
pub fn new(value: bool) -> Self {
NoConnect(value)
}
}

impl Pin for NoConnect {
type Error = futures::never::Never;
}

impl InputPin for NoConnect {
fn poll_get(
self: pin::Pin<&mut Self>,
_cx: &mut task::Context<'_>,
) -> task::Poll<Result<bool, Self::Error>> {
task::Poll::Ready(Ok(false))
}
}

impl OutputPin for NoConnect {
fn poll_set(
self: pin::Pin<&mut Self>,
_cx: &mut task::Context<'_>,
_high: bool,
) -> task::Poll<Result<(), Self::Error>> {
task::Poll::Ready(Ok(()))
}
}

impl IntoFloatingInputPin for NoConnect {
type FloatingInputPin = Self;

fn into_floating_input_pin(self) -> Result<Self::FloatingInputPin, Self::Error> {
Ok(self)
}
}

impl IntoPullUpInputPin for NoConnect {
type PullUpInputPin = Self;

fn into_pull_up_input_pin(self) -> Result<Self::PullUpInputPin, Self::Error> {
Ok(self)
}
}

impl IntoPullDownInputPin for NoConnect {
type PullDownInputPin = Self;

fn into_pull_down_input_pin(self) -> Result<Self::PullDownInputPin, Self::Error> {
Ok(self)
}
}

impl IntoOpenDrainOutputPin for NoConnect {
type OpenDrainOutputPin = Self;

fn into_open_drain_output_pin(
self,
_initial_high: bool,
) -> Result<Self::OpenDrainOutputPin, Self::Error> {
Ok(self)
}
}

impl IntoPushPullOutputPin for NoConnect {
type PushPullOutputPin = Self;

fn into_push_pull_output_pin(
self,
_initial_high: bool,
) -> Result<Self::PushPullOutputPin, Self::Error> {
Ok(self)
}
}
34 changes: 34 additions & 0 deletions src/asynchronous/gpio/get.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
//! Defines futures for getting the value off of a GPIO pin.
use core::future;
use core::pin;
use core::task;

/// A future which reads the value off a GPIO pin.
#[derive(Debug)]
#[must_use = "futures do nothing unless you `.await` or poll them"]
pub struct Get<'a, A>
where
A: super::InputPin + Unpin + ?Sized,
{
pin: &'a mut A,
}

/// Creates a new [`Get`] for the provided GPIO pin.
pub fn get<A>(pin: &mut A) -> Get<A>
where
A: super::InputPin + Unpin + ?Sized,
{
Get { pin }
}

impl<A> future::Future for Get<'_, A>
where
A: super::InputPin + Unpin + ?Sized,
{
type Output = Result<bool, A::Error>;

fn poll(mut self: pin::Pin<&mut Self>, cx: &mut task::Context<'_>) -> task::Poll<Self::Output> {
let this = &mut *self;
pin::Pin::new(&mut *this.pin).poll_get(cx)
}
}
36 changes: 36 additions & 0 deletions src/asynchronous/gpio/set.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
//! Defines futures for setting the value of a GPIO pin.
use core::future;
use core::pin;
use core::task;

/// A future which sets the value of a GPIO pin.
#[derive(Debug)]
#[must_use = "futures do nothing unless you `.await` or poll them"]
pub struct Set<'a, A>
where
A: super::OutputPin + Unpin + ?Sized,
{
pin: &'a mut A,
high: bool,
}

/// Creates a new [`Set`] for the provided GPIO pin, that, when polled, will drive it to the
/// specified high or low value.
pub fn set<A>(pin: &mut A, high: bool) -> Set<A>
where
A: super::OutputPin + Unpin + ?Sized,
{
Set { pin, high }
}

impl<A> future::Future for Set<'_, A>
where
A: super::OutputPin + Unpin + ?Sized,
{
type Output = Result<(), A::Error>;

fn poll(mut self: pin::Pin<&mut Self>, cx: &mut task::Context<'_>) -> task::Poll<Self::Output> {
let this = &mut *self;
pin::Pin::new(&mut *this.pin).poll_set(cx, this.high)
}
}
Loading

0 comments on commit 7dc6f28

Please sign in to comment.