Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add asynchronous versions of most embedded-hal traits using GATs #285

Closed
wants to merge 32 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
e736fb5
First attempt at futures module
lachlansneff Jun 21, 2021
ff085fa
Fix some of the docs
lachlansneff Jun 21, 2021
7eb5fd1
Add 'unstable-gats' feature to enable futures module
lachlansneff Jun 22, 2021
3a30a33
Add async i2c module
lachlansneff Jun 22, 2021
d9d08fa
Remove futures::spi::FullDuplex
lachlansneff Jun 22, 2021
1928f77
Make futures::spi::{Read, Write, ReadWrite, ReadWriteInPlace} traits
lachlansneff Jun 22, 2021
5cdd987
Rename the futures feature to unstable-features
lachlansneff Jun 22, 2021
eecf646
Remove commented-out futures::spi::Transactional trait
lachlansneff Jun 22, 2021
6298952
Remove MaybeUninit from futures::spi::ReadWriteInPlace
lachlansneff Jun 22, 2021
bac1603
Remove futures::i2c::Transactional and remove required min_type_alias…
lachlansneff Jun 22, 2021
92aa33b
Remove some unncessary bounds from futures::spi traits
lachlansneff Jun 22, 2021
582ebad
Return initialized buffers from method that read into an uninitialize…
lachlansneff Jun 22, 2021
a61008b
Update spi trait names
lachlansneff Jun 29, 2021
306b187
Remove MaybeUninit from futures::spi
lachlansneff Jun 29, 2021
92e0da4
Update changelog
lachlansneff Jun 29, 2021
7ebd9c5
Merge branch 'master' into master
lachlansneff Jun 30, 2021
b79bf16
Expand the CHANGELOG.md addition
lachlansneff Jul 6, 2021
4d02ea1
Add futures::Delay trait
lachlansneff Jul 6, 2021
41af589
Add futures::digital::AsyncInputPin trait
lachlansneff Jul 6, 2021
48ab3a8
Switch futures::Delay to take core::time::Duration and add futures::d…
lachlansneff Jul 6, 2021
85accbd
Update changelog
lachlansneff Jul 6, 2021
eb2ff8a
Fix some docs in the futures module
lachlansneff Jul 6, 2021
d9174d2
Respond to feedback
lachlansneff Jul 6, 2021
941c8bb
Change uart interface to read into a slice
lachlansneff Jul 6, 2021
5617082
Mention what happens when the write and read slices in futures::spi::…
lachlansneff Jul 6, 2021
c002a23
Add more futures::digital traits
lachlansneff Jul 6, 2021
e686e6e
Change some associated future trait type names
lachlansneff Jul 6, 2021
2e16b79
Formatting
lachlansneff Jul 6, 2021
55cbca1
Remove MaybeUninit from futures::i2c
lachlansneff Jul 6, 2021
fee1b99
fix typo
lachlansneff Jul 6, 2021
1df5604
Add error associated types to futures::digital traits
lachlansneff Jul 6, 2021
38ba051
Expand names of generic associated future types
lachlansneff Nov 22, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
### Added
- Added `IoPin` trait for pins that can change between being inputs or outputs
dynamically.
- Added `futures` module that contains asynchronous traits (using currently unstable GATs) for I2C, RNG, Serial, SPI, digital pins, and delays. These traits are behind the feature flag `unstable-futures`. The `futures` module currently needs Rust nightly and it is not included in `embedded-hal`'s SemVer guarantees. We may release breaking changes in any patch release. If you use this module, please use an `=1.x.x` crate version specification.

### Changed
- Swap PWM channel arguments to references
Expand Down
5 changes: 5 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,11 @@ readme = "README.md"
repository = "https://github.com/rust-embedded/embedded-hal"
version = "1.0.0-alpha.4" # remember to update html_root_url

[features]
# Enabling this feature enables the `futures` module using generic associated types (GATs), which are still unstable.
# Therefore, this feature requires compiling on nightly.
unstable-futures = []

[dependencies]
nb = "1"

Expand Down
36 changes: 36 additions & 0 deletions src/futures/delay.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
//! Asynchronous Delays
//!
//! # What's the difference this trait and the `timer::CountDown` trait?
//!
//! The `Delay` trait provides an asynchronous delay abstraction and it's meant to be used either
//! to build higher-level abstractions like I/O timeouts or by itself.

use core::{future::Future, time::Duration};

/// Asynchronously wait a duration of time.
///
/// # Example
/// ```rust
/// # use embedded_hal::futures::delay::Delay;
/// use core::time::Duration;
///
/// async fn wait_100_micros<D: Delay>(timer: &D) {
/// timer.delay(Duration::from_micros(100))
/// .await
/// .expect("failed to await on timer");
/// }
/// ```
pub trait Delay {
/// Enumeration of `Delay` errors.
type Error;

/// The future returned from `delay`.
type DelayFuture<'a>: Future<Output = Result<(), Self::Error>> + 'a
where
Self: 'a;

/// Returns a future that will resolve when `duration` has passed.
/// It is not guaranteed that _exactly_ `duration` will pass, but it will
/// be `duration` or longer.
fn delay<'a>(&'a mut self, duration: Duration) -> Self::DelayFuture<'a>;
}
121 changes: 121 additions & 0 deletions src/futures/digital.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
//! Asynchronous digital I/O
//!
//! # Examples
//! ```rust
//! # use embedded_hal::futures::digital::AsyncInputPin;
//! //! Asynchronously wait until the `ready_pin` becomes high.
//! async fn wait_until_ready<P>(ready_pin: &P)
//! where
//! P: WaitFor,
//! {
//! ready_pin
//! .wait_for_high()
//! .await
//! .expect("failed to await input pin")
//! }
//! ```
//!
//! ```rust,ignore
//! # use embedded_hal::futures::digital::WaitForHigh;
//! # use embedded_hal::futures::delay::Delay;
//! use core::time::Duration;
//!
//! //! Wait until the `ready_pin` is high or timeout after 1 millisecond.
//! //! Returns true if the pin became high or false if it timed-out.
//! async fn wait_until_ready_or_timeout<P, D>(ready_pin: &P, delay: &mut D) -> bool
//! where
//! P: WaitForHigh,
//! D: Delay,
//! {
//! futures::select_biased! {
//! x => ready_pin.wait_for_high() => {
//! x.expect("failed to await input pin");
//! true
//! },
//! _ => delay.delay(Duration::from_millis(1)) => false, // ignore the error
//! }
//! }
//! ```

use core::future::Future;

/// Asynchronously wait for a pin to be high.
pub trait WaitForHigh {
/// Enumeration of errors.
type Error;

/// The future returned by the `wait_for_high` function.
type WaitForHighFuture<'a>: Future<Output = Result<(), Self::Error>> + 'a
where
Self: 'a;

/// Returns a future that resolves when this pin _is_ high. If the pin
/// is already high, the future resolves immediately.
///
/// # Note for implementers
/// The pin may have switched back to low before the task was run after
/// being woken. The future should still resolve in that case.
fn wait_for_high<'a>(&'a mut self) -> Self::WaitForHighFuture<'a>;
}

/// Asynchronously wait for a pin to be low.
pub trait WaitForLow {
/// Enumeration of errors.
type Error;

/// The future returned by `wait_for_low`.
type WaitForLowFuture<'a>: Future<Output = Result<(), Self::Error>> + 'a
where
Self: 'a;

/// Returns a future that resolves when this pin _is_ low. If the pin
/// is already low, the future resolves immediately.
///
/// # Note for implementers
/// The pin may have switched back to high before the task was run after
/// being woken. The future should still resolve in that case.
fn wait_for_low<'a>(&'a mut self) -> Self::WaitForLowFuture<'a>;
}

/// Wait for a rising edge (transition from low to high).
pub trait WaitForRisingEdge {
/// Enumeration of errors.
type Error;

/// The future returned from `wait_for_rising_edge`.
type WaitForRisingEdgeFuture<'a>: Future<Output = Result<(), Self::Error>> + 'a
where
Self: 'a;

/// Returns a future that resolves when this pin transitions from low to high.
fn wait_for_rising_edge<'a>(&'a mut self) -> Self::WaitForRisingEdgeFuture<'a>;
}

/// Wait for a falling edge (transition from high to low).
pub trait WaitForFallingEdge {
/// Enumeration of errors.
type Error;

/// The future returned from `wait_for_falling_edge`.
type WaitForFallingEdgeFuture<'a>: Future<Output = Result<(), Self::Error>> + 'a
where
Self: 'a;

/// Returns a future that resolves when this pin transitions from high to low.
fn wait_for_falling_edge<'a>(&'a mut self) -> Self::WaitForFallingEdgeFuture<'a>;
}

/// Wait for any edge (transition from low to high OR high to low).
pub trait WaitForAnyEdge {
/// Enumeration of errors.
type Error;

/// The future returned from `wait_for_any_edge`.
type WaitForAnyEdgeFuture<'a>: Future<Output = Result<(), Self::Error>> + 'a
where
Self: 'a;

/// Returns a future that resolves when this pin undergoes any transition, e.g.
/// low to high OR high to low.
fn wait_for_any_edge<'a>(&'a mut self) -> Self::WaitForAnyEdgeFuture<'a>;
}
117 changes: 117 additions & 0 deletions src/futures/i2c.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
//! Async I2C API
//!
//! This API supports 7-bit and 10-bit addresses. Traits feature an `AddressMode`
//! marker type parameter. Two implementation of the `AddressMode` exist:
//! `SevenBitAddress` and `TenBitAddress`.
//!
//! Through this marker types it is possible to implement each address mode for
//! the traits independently in `embedded-hal` implementations and device drivers
//! can depend only on the mode that they support.
//!
//! Additionally, the I2C 10-bit address mode has been developed to be fully
//! backwards compatible with the 7-bit address mode. This allows for a
//! software-emulated 10-bit addressing implementation if the address mode
//! is not supported by the hardware.
//!
//! Since 7-bit addressing is the mode of the majority of I2C devices,
//! `SevenBitAddress` has been set as default mode and thus can be omitted if desired.

pub use crate::blocking::i2c::{AddressMode, SevenBitAddress, TenBitAddress};
use core::future::Future;

/// Async read
pub trait Read<A: AddressMode = SevenBitAddress> {
/// Error type
type Error;
/// The future associated with the `read` method.
type ReadFuture<'a>: Future<Output = Result<(), Self::Error>> + 'a
where
Self: 'a;

/// Reads enough bytes from slave with `address` to fill `buffer`
///
/// # I2C Events (contract)
///
/// ``` text
/// Master: ST SAD+R MAK MAK ... NMAK SP
/// Slave: SAK B0 B1 ... BN
/// ```
///
/// Where
///
/// - `ST` = start condition
/// - `SAD+R` = slave address followed by bit 1 to indicate reading
/// - `SAK` = slave acknowledge
/// - `Bi` = ith byte of data
/// - `MAK` = master acknowledge
/// - `NMAK` = master no acknowledge
/// - `SP` = stop condition
fn read<'a>(&'a mut self, address: A, read: &'a mut [u8]) -> Self::ReadFuture<'a>;
}

/// Async write
pub trait Write<A: AddressMode = SevenBitAddress> {
/// Error type
type Error;
/// The future associated with the `write` method.
type WriteFuture<'a>: Future<Output = Result<(), Self::Error>> + 'a
where
Self: 'a;

/// Writes bytes to slave with address `address`
///
/// # I2C Events (contract)
///
/// ``` text
/// Master: ST SAD+W B0 B1 ... BN SP
/// Slave: SAK SAK SAK ... SAK
/// ```
///
/// Where
///
/// - `ST` = start condition
/// - `SAD+W` = slave address followed by bit 0 to indicate writing
/// - `SAK` = slave acknowledge
/// - `Bi` = ith byte of data
/// - `SP` = stop condition
fn write<'a>(&'a mut self, address: A, write: &'a [u8]) -> Self::WriteFuture<'a>;
}

/// Async write + read
pub trait WriteRead<A: AddressMode = SevenBitAddress> {
/// Error type
type Error;
/// The future associated with the `write_read` method.
type WriteReadFuture<'a>: Future<Output = Result<&'a [u8], Self::Error>> + 'a
where
Self: 'a;

/// Writes bytes to slave with address `address` and then reads enough bytes to fill `read` *in a
/// single transaction*. The returned buffer is the initialized `read` buffer.
///
/// # I2C Events (contract)
///
/// ``` text
/// Master: ST SAD+W O0 O1 ... OM SR SAD+R MAK MAK ... NMAK SP
/// Slave: SAK SAK SAK ... SAK SAK I0 I1 ... IN
/// ```
///
/// Where
///
/// - `ST` = start condition
/// - `SAD+W` = slave address followed by bit 0 to indicate writing
/// - `SAK` = slave acknowledge
/// - `Oi` = ith outgoing byte of data
/// - `SR` = repeated start condition
/// - `SAD+R` = slave address followed by bit 1 to indicate reading
/// - `Ii` = ith incoming byte of data
/// - `MAK` = master acknowledge
/// - `NMAK` = master no acknowledge
/// - `SP` = stop condition
fn write_read<'a>(
&'a mut self,
address: A,
write: &'a [u8],
read: &'a mut [u8],
) -> Self::WriteReadFuture<'a>;
}
9 changes: 9 additions & 0 deletions src/futures/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
//! Asynchronous APIs
//!
//! This traits use `core::future::Future` and generic associated types.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The whole futures approach should be documented here. Including examples, executors, waking, etc.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree with those — that being said I don't think it's quite sorted out how HALs are going to support this. I think they definitely can (and will), but it's going to require some thought as to how HALs should install interrupt handlers and whatnot for async operations to work.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure. There is no "one true answer" as for how all operations should concert in all situations. However, the different alternatives and considerations about how to implement and consume these traits as well as what the expectations are about how the operations will run should be explained here.


pub mod delay;
pub mod digital;
pub mod i2c;
pub mod serial;
pub mod spi;
42 changes: 42 additions & 0 deletions src/futures/serial.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
//! Serial interface

use core::future::Future;

/// Read half of a serial interface
///
/// Some serial interfaces support different data sizes (8 bits, 9 bits, etc.);
/// This can be encoded in this trait via the `Word` type parameter.
pub trait Read<Word> {
/// Read error
type Error;

/// The future associated with the `read` method.
type ReadFuture<'a>: Future<Output = Result<(), Self::Error>> + 'a
where
Self: 'a;

/// Reads words from the serial interface into the supplied slice.
fn read<'a>(&'a mut self, read: &'a mut [Word]) -> Self::ReadFuture<'a>;
}

/// Write half of a serial interface
pub trait Write<Word> {
/// Write error
type Error;

/// The future associated with the `write` method.
type WriteFuture<'a>: Future<Output = Result<(), Self::Error>> + 'a
where
Self: 'a;

/// The future associated with the `flush` method.
type FlushFuture<'a>: Future<Output = Result<(), Self::Error>> + 'a
where
Self: 'a;

/// Writes a single word to the serial interface
fn write<'a>(&'a mut self, word: Word) -> Self::WriteFuture<'a>;

/// Ensures that none of the previously written words are still buffered
fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a>;
}
Loading