diff --git a/CHANGELOG.md b/CHANGELOG.md index 5bad28bff..c0a43d873 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/Cargo.toml b/Cargo.toml index d7b8bec8d..4e6162b41 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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" diff --git a/src/futures/delay.rs b/src/futures/delay.rs new file mode 100644 index 000000000..f517add2d --- /dev/null +++ b/src/futures/delay.rs @@ -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(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> + '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>; +} diff --git a/src/futures/digital.rs b/src/futures/digital.rs new file mode 100644 index 000000000..b703f5e6d --- /dev/null +++ b/src/futures/digital.rs @@ -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

(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(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> + '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> + '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> + '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> + '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> + '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>; +} diff --git a/src/futures/i2c.rs b/src/futures/i2c.rs new file mode 100644 index 000000000..f9aa6c715 --- /dev/null +++ b/src/futures/i2c.rs @@ -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 { + /// Error type + type Error; + /// The future associated with the `read` method. + type ReadFuture<'a>: Future> + '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 { + /// Error type + type Error; + /// The future associated with the `write` method. + type WriteFuture<'a>: Future> + '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 { + /// Error type + type Error; + /// The future associated with the `write_read` method. + type WriteReadFuture<'a>: Future> + '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>; +} diff --git a/src/futures/mod.rs b/src/futures/mod.rs new file mode 100644 index 000000000..4e59c9da0 --- /dev/null +++ b/src/futures/mod.rs @@ -0,0 +1,9 @@ +//! Asynchronous APIs +//! +//! This traits use `core::future::Future` and generic associated types. + +pub mod delay; +pub mod digital; +pub mod i2c; +pub mod serial; +pub mod spi; diff --git a/src/futures/serial.rs b/src/futures/serial.rs new file mode 100644 index 000000000..64b32b7df --- /dev/null +++ b/src/futures/serial.rs @@ -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 { + /// Read error + type Error; + + /// The future associated with the `read` method. + type ReadFuture<'a>: Future> + '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 { + /// Write error + type Error; + + /// The future associated with the `write` method. + type WriteFuture<'a>: Future> + 'a + where + Self: 'a; + + /// The future associated with the `flush` method. + type FlushFuture<'a>: Future> + '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>; +} diff --git a/src/futures/spi.rs b/src/futures/spi.rs new file mode 100644 index 000000000..52597fb60 --- /dev/null +++ b/src/futures/spi.rs @@ -0,0 +1,73 @@ +//! Serial Peripheral Interface + +use core::future::Future; + +/// Async transfer +pub trait Transfer { + /// Error type + type Error; + + /// Associated future for the `transfer` method. + type TransferFuture<'a>: Future> + 'a + where + Self: 'a; + + /// Writes and reads simultaneously. `write` is written to the slave on MOSI and + /// words received on MISO are stored in `read`. + /// + /// It is allowed for `read` and `write` to have different lengths, even zero length. + /// The transfer runs for `max(read.len(), write.len())` words. If `read` is shorter, + /// incoming words after `read` has been filled will be discarded. If `write` is shorter, + /// the value of words sent in MOSI after all `write` has been sent is implementation defined, + /// typically `0x00`, `0xFF`, or configurable. + fn transfer<'a>(&'a mut self, write: &'a [W], read: &'a mut [W]) -> Self::TransferFuture<'a>; +} + +/// Async transfer in place. +pub trait TransferInPlace { + /// Error type + type Error; + + /// Associated future for the `transfer_inplace` method. + type TransferInPlaceFuture<'a>: Future> + 'a + where + Self: 'a; + + /// Writes `words` to the slave from the `readwrite` buffer and reads words into the same buffer. + /// This method uses a single `readwrite` buffer. + /// + /// The returned buffer is the initialized `readwrite` buffer. + fn transfer_inplace<'a>(&'a mut self, words: &'a mut [W]) -> Self::TransferInPlaceFuture<'a>; +} + +/// Async write +pub trait Write { + /// Error type + type Error; + + /// Associated future for the `write` method. + type WriteFuture<'a>: Future> + 'a + where + Self: 'a; + + /// Writes `words` to the slave, ignoring all the incoming words + fn write<'a>(&'a mut self, write: &'a [W]) -> Self::WriteFuture<'a>; +} + +/// Async read +pub trait Read { + /// Error type + type Error; + + /// Associated future for the `read` method. + type ReadFuture<'a>: Future> + 'a + where + Self: 'a; + + /// Reads words from the slave without specifying any data to write. + /// The SPI hardware will send data, though what data it sends is not defined + /// by this trait. Some hardware can configure what values (e.g. 0x00, 0xFF), some cannot. + /// + /// The returned buffer is the initialized `words` buffer. + fn read<'a>(&'a mut self, read: &'a mut [W]) -> Self::ReadFuture<'a>; +} diff --git a/src/lib.rs b/src/lib.rs index 1dbb6dd49..7c5fe811c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -408,9 +408,12 @@ #![doc(html_root_url = "https://docs.rs/embedded-hal/1.0.0-alpha.4")] #![deny(missing_docs)] #![no_std] +#![cfg_attr(feature = "unstable-futures", feature(generic_associated_types))] pub mod blocking; pub mod fmt; +#[cfg(feature = "unstable-futures")] +pub mod futures; pub mod nb; mod private {