From e736fb5233a2e1fdd767f1f1fb1c9e413f5fe276 Mon Sep 17 00:00:00 2001 From: Lachlan Sneff Date: Mon, 21 Jun 2021 17:20:11 -0400 Subject: [PATCH 01/31] First attempt at futures module --- src/futures/adc.rs | 99 ++++++++++++++ src/futures/capture.rs | 101 ++++++++++++++ src/futures/mod.rs | 23 ++++ src/futures/rng.rs | 19 +++ src/futures/serial.rs | 42 ++++++ src/futures/spi.rs | 295 +++++++++++++++++++++++++++++++++++++++++ src/futures/timer.rs | 101 ++++++++++++++ src/lib.rs | 2 + 8 files changed, 682 insertions(+) create mode 100644 src/futures/adc.rs create mode 100644 src/futures/capture.rs create mode 100644 src/futures/mod.rs create mode 100644 src/futures/rng.rs create mode 100644 src/futures/serial.rs create mode 100644 src/futures/spi.rs create mode 100644 src/futures/timer.rs diff --git a/src/futures/adc.rs b/src/futures/adc.rs new file mode 100644 index 000000000..b07b3e4cb --- /dev/null +++ b/src/futures/adc.rs @@ -0,0 +1,99 @@ +//! Analog-digital conversion traits + +use core::future::Future; + +/// A marker trait to identify MCU pins that can be used as inputs to an ADC channel. +/// +/// This marker trait denotes an object, i.e. a GPIO pin, that is ready for use as an input to the +/// ADC. As ADCs channels can be supplied by multiple pins, this trait defines the relationship +/// between the physical interface and the ADC sampling buffer. +/// +/// ``` +/// # use core::marker::PhantomData; +/// # use embedded_hal::nb::adc::Channel; +/// +/// struct Adc1; // Example ADC with single bank of 8 channels +/// struct Gpio1Pin1(PhantomData); +/// struct Analog(()); // marker type to denote a pin in "analog" mode +/// +/// // GPIO 1 pin 1 can supply an ADC channel when it is configured in Analog mode +/// impl Channel for Gpio1Pin1 { +/// type ID = u8; // ADC channels are identified numerically +/// +/// fn channel(&self) -> Self::ID { +/// 7_u8 // GPIO pin 1 is connected to ADC channel 7 +/// } +/// } +/// +/// struct Adc2; // ADC with two banks of 16 channels +/// struct Gpio2PinA(PhantomData); +/// struct AltFun(()); // marker type to denote some alternate function mode for the pin +/// +/// // GPIO 2 pin A can supply an ADC channel when it's configured in some alternate function mode +/// impl Channel for Gpio2PinA { +/// type ID = (u8, u8); // ADC channels are identified by bank number and channel number +/// +/// fn channel(&self) -> Self::ID { +/// (0, 3) // bank 0 channel 3 +/// } +/// } +/// ``` +pub trait Channel { + /// Channel ID type + /// + /// A type used to identify this ADC channel. For example, if the ADC has eight channels, this + /// might be a `u8`. If the ADC has multiple banks of channels, it could be a tuple, like + /// `(u8: bank_id, u8: channel_id)`. + type ID: Copy; + + /// Get the specific ID that identifies this channel, for example `0_u8` for the first ADC + /// channel, if Self::ID is u8. + fn channel(&self) -> Self::ID; +} + +/// ADCs that sample on single channels per request, and do so at the time of the request. +/// +/// This trait is the interface to an ADC that is configured to read a specific channel at the time +/// of the request (in contrast to continuous asynchronous sampling). +/// +/// ``` +/// use embedded_hal::nb::adc::{Channel, OneShot}; +/// +/// struct MyAdc; // 10-bit ADC, with 5 channels +/// # impl MyAdc { +/// # pub fn power_up(&mut self) {} +/// # pub fn power_down(&mut self) {} +/// # pub fn do_conversion(&mut self, chan: u8) -> u16 { 0xAA55_u16 } +/// # } +/// +/// impl OneShot for MyAdc +/// where +/// WORD: From, +/// PIN: Channel, +/// { +/// type Error = (); +/// +/// fn read(&mut self, pin: &mut PIN) -> nb::Result { +/// let chan = 1 << pin.channel(); +/// self.power_up(); +/// let result = self.do_conversion(chan); +/// self.power_down(); +/// Ok(result.into()) +/// } +/// } +/// ``` +pub trait OneShot> { + /// Error type returned by ADC methods + type Error; + + /// The future associated with the `read` method. + type ReadFuture<'a>: Future> + 'a + where + Self: 'a; + + /// Request that the ADC begin a conversion on the specified pin + /// + /// This method takes a `Pin` reference, as it is expected that the ADC will be able to sample + /// whatever channel underlies the pin. + fn read<'a>(&'a mut self, pin: &'a mut Pin) -> Self::ReadFuture<'a>; +} diff --git a/src/futures/capture.rs b/src/futures/capture.rs new file mode 100644 index 000000000..39d53ea4c --- /dev/null +++ b/src/futures/capture.rs @@ -0,0 +1,101 @@ +//! Input capture + +use core::future::Future; + +/// Input capture +/// +/// # Examples +/// +/// You can use this interface to measure the period of (quasi) periodic signals +/// / events +/// +/// ``` +/// extern crate embedded_hal as hal; +/// #[macro_use(block)] +/// extern crate nb; +/// +/// use hal::prelude::*; +/// +/// fn main() { +/// let mut capture: Capture1 = { +/// // .. +/// # Capture1 +/// }; +/// +/// capture.set_resolution(1.ms()).unwrap(); +/// +/// let before = block!(capture.capture(Channel::_1)).unwrap(); +/// let after = block!(capture.capture(Channel::_1)).unwrap(); +/// +/// let period = after.wrapping_sub(before); +/// +/// println!("Period: {} ms", period); +/// } +/// +/// # use core::convert::Infallible; +/// # struct MilliSeconds(u32); +/// # trait U32Ext { fn ms(self) -> MilliSeconds; } +/// # impl U32Ext for u32 { fn ms(self) -> MilliSeconds { MilliSeconds(self) } } +/// # struct Capture1; +/// # enum Channel { _1 } +/// # impl hal::nb::capture::Capture for Capture1 { +/// # type Error = Infallible; +/// # type Capture = u16; +/// # type Channel = Channel; +/// # type Time = MilliSeconds; +/// # fn capture(&mut self, _: Channel) -> ::nb::Result { Ok(0) } +/// # fn disable(&mut self, _: Channel) -> Result<(), Self::Error> { unimplemented!() } +/// # fn enable(&mut self, _: Channel) -> Result<(), Self::Error> { unimplemented!() } +/// # fn get_resolution(&self) -> Result { unimplemented!() } +/// # fn set_resolution(&mut self, _: T) -> Result<(), Self::Error> where T: Into { Ok(()) } +/// # } +/// ``` +// unproven reason: pre-singletons API. With singletons a `CapturePin` (cf. `PwmPin`) trait seems more +// appropriate +pub trait Capture { + /// Enumeration of `Capture` errors + /// + /// Possible errors: + /// + /// - *overcapture*, the previous capture value was overwritten because it + /// was not read in a timely manner + type Error; + + /// Enumeration of channels that can be used with this `Capture` interface + /// + /// If your `Capture` interface has no channels you can use the type `()` + /// here + type Channel; + + /// A time unit that can be converted into a human time unit (e.g. seconds) + type Time; + + /// The type of the value returned by `capture` + type Capture; + + /// The future associated with the `capture` method. + type CaptureFuture<'a>: Future> + 'a + where + Self: 'a; + + /// "Waits" for a transition in the capture `channel` and returns the value + /// of counter at that instant + /// + /// NOTE that you must multiply the returned value by the *resolution* of + /// this `Capture` interface to get a human time unit (e.g. seconds) + fn capture<'a>(&'a mut self, channel: Self::Channel) -> Self::CaptureFuture<'a>; + + /// Disables a capture `channel` + fn disable(&mut self, channel: Self::Channel) -> Result<(), Self::Error>; + + /// Enables a capture `channel` + fn enable(&mut self, channel: Self::Channel) -> Result<(), Self::Error>; + + /// Returns the current resolution + fn get_resolution(&self) -> Result; + + /// Sets the resolution of the capture timer + fn set_resolution(&mut self, resolution: R) -> Result<(), Self::Error> + where + R: Into; +} diff --git a/src/futures/mod.rs b/src/futures/mod.rs new file mode 100644 index 000000000..ee3df2ae3 --- /dev/null +++ b/src/futures/mod.rs @@ -0,0 +1,23 @@ +//! Non-blocking API +//! +//! These traits make use of the [`nb`] crate +//! (*please go read that crate documentation before continuing*) to abstract over +//! the execution model and to also provide an optional blocking operation mode. +//! +//! The `nb::Result` enum is used to add an [`Error::WouldBlock`] variant to the errors +//! of the traits. Using this it is possible to execute actions in a non-blocking +//! way. +//! +//! `block!`, `Result` and `Error` from the [`nb`] crate are re-exported here to avoid +//! crate version mismatches. These should be used instead of importing the `nb` crate +//! directly again in dependent crates. +//! +//! [`nb`]: https://crates.io/crates/nb + +pub use nb::{block, Error, Result}; +pub mod adc; +pub mod capture; +pub mod rng; +pub mod serial; +pub mod spi; +pub mod timer; diff --git a/src/futures/rng.rs b/src/futures/rng.rs new file mode 100644 index 000000000..e00dc255d --- /dev/null +++ b/src/futures/rng.rs @@ -0,0 +1,19 @@ +//! Random Number Generator Interface + +use core::future::Future; + +/// Nonblocking stream of random bytes. +pub trait Read { + /// An enumeration of RNG errors. + /// + /// For infallible implementations, will be `Infallible` + type Error; + + /// The future associated with the `read` method. + type ReadFuture<'a>: Future> + 'a + where + Self: 'a; + + /// Get a number of bytes from the RNG. + fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReadFuture<'a>; +} diff --git a/src/futures/serial.rs b/src/futures/serial.rs new file mode 100644 index 000000000..95f5a192a --- /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 a single word from the serial interface + fn read<'a>(&'a mut self) -> 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..029783b1d --- /dev/null +++ b/src/futures/spi.rs @@ -0,0 +1,295 @@ +//! Serial Peripheral Interface + +use core::future::Future; + +/// Full duplex (master mode) +/// +/// # Notes +/// +/// - It's the task of the user of this interface to manage the slave select lines +/// +/// - Due to how full duplex SPI works each `read` call must be preceded by a `write` call. +/// +/// - `read` calls only return the data received with the last `write` call. +/// Previously received data is discarded +/// +/// - Data is only guaranteed to be clocked out when the `read` call succeeds. +/// The slave select line shouldn't be released before that. +/// +/// - Some SPIs can work with 8-bit *and* 16-bit words. You can overload this trait with different +/// `Word` types to allow operation in both modes. +pub trait FullDuplex { + /// An enumeration of SPI errors + type Error; + + /// The future associated with the `read` method. + type ReadFuture<'a>: Future> + 'a + where + Self: 'a; + + /// The future associated with the `write` method. + type WriteFuture<'a>: Future> + 'a + where + Self: 'a; + + /// Reads the word stored in the shift register + /// + /// **NOTE** A word must be sent to the slave before attempting to call this + /// method. + fn read<'a>(&'a mut self) -> Self::ReadFuture<'a>; + + /// Writes a word to the slave + fn write<'a>(&'a mut self, word: Word) -> Self::WriteFuture<'a>; +} + +/// Clock polarity +#[derive(Clone, Copy, PartialEq, Eq)] +pub enum Polarity { + /// Clock signal low when idle + IdleLow, + /// Clock signal high when idle + IdleHigh, +} + +/// Clock phase +#[derive(Clone, Copy, PartialEq, Eq)] +pub enum Phase { + /// Data in "captured" on the first clock transition + CaptureOnFirstTransition, + /// Data in "captured" on the second clock transition + CaptureOnSecondTransition, +} + +/// SPI mode +#[derive(Clone, Copy, PartialEq, Eq)] +pub struct Mode { + /// Clock polarity + pub polarity: Polarity, + /// Clock phase + pub phase: Phase, +} + +/// Helper for CPOL = 0, CPHA = 0 +pub const MODE_0: Mode = Mode { + polarity: Polarity::IdleLow, + phase: Phase::CaptureOnFirstTransition, +}; + +/// Helper for CPOL = 0, CPHA = 1 +pub const MODE_1: Mode = Mode { + polarity: Polarity::IdleLow, + phase: Phase::CaptureOnSecondTransition, +}; + +/// Helper for CPOL = 1, CPHA = 0 +pub const MODE_2: Mode = Mode { + polarity: Polarity::IdleHigh, + phase: Phase::CaptureOnFirstTransition, +}; + +/// Helper for CPOL = 1, CPHA = 1 +pub const MODE_3: Mode = Mode { + polarity: Polarity::IdleHigh, + phase: Phase::CaptureOnSecondTransition, +}; + +/// Async transfer +pub trait Transfer { + /// Error type + type Error; + + /// Associated future for the `transfer` method. + type TransferFuture<'a>: Future> + 'a + where + Self: 'a; + + /// Writes `words` to the slave. Returns the `words` received from the slave + fn transfer<'a>(&'a mut self, words: &'a mut [Word]) -> Self::TransferFuture<'a> + where + Self::Error: '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, words: &'a [W]) -> Self::WriteFuture<'a>; +} + +/// Async write (iterator version) +pub trait WriteIter { + /// Error type + type Error; + + /// Associated future for the `write_iter` method. + type WriteFuture<'a, WI>: Future> + 'a + where + Self: 'a, + WI: 'a; + + /// Writes `words` to the slave, ignoring all the incoming words + fn write_iter<'a, WI>(&'a mut self, words: WI) -> Self::WriteFuture<'a, WI> + where + WI: IntoIterator + 'a; +} + +/// Async transfer +pub mod transfer { + use core::future::Future; + + /// Default implementation of `futures::spi::Transfer` for implementers of + /// `futures::spi::FullDuplex` + pub trait Default: super::FullDuplex {} + + impl super::Transfer for S + where + S: Default, + Word: Clone + 'static, + { + type Error = S::Error; + + type TransferFuture<'a> where Self: 'a = impl Future> + 'a; + + fn transfer<'w>(&'w mut self, words: &'w mut [Word]) -> Self::TransferFuture<'w> + where + S::Error: 'w + { + async move { + for word in words.iter_mut() { + self.write(word.clone()).await?; + *word = self.read().await?; + } + + Ok(words as &'w [Word]) + } + } + } +} + +/// Blocking write +pub mod write { + use core::future::Future; + + /// Default implementation of `futures::spi::Write` for implementers + /// of `futures::spi::FullDuplex` + pub trait Default: super::FullDuplex {} + + impl super::Write for S + where + S: Default, + W: Clone + 'static, + { + type Error = S::Error; + + type WriteFuture<'a> where Self: 'a = impl Future> + 'a; + + fn write<'a>(&'a mut self, words: &'a [W]) -> Self::WriteFuture<'a> { + async move { + for word in words { + self.write(word.clone()).await?; + self.read().await?; + } + + Ok(()) + } + } + } +} + +// /// Blocking write (iterator version) +// pub mod write_iter { +// use core::future::Future; + +// /// Default implementation of `blocking::spi::WriteIter` for implementers of +// /// `nonblocking::spi::FullDuplex` +// pub trait Default: super::FullDuplex {} + +// impl super::WriteIter for S +// where +// S: Default, +// W: Clone + 'static, +// { +// type Error = S::Error; + +// type WriteFuture<'a, WI> where Self: 'a, WI: 'a = impl Future> + 'a; + +// fn write_iter<'a, WI>(&'a mut self, words: WI) -> Self::WriteFuture<'a, WI> +// where +// WI: IntoIterator + 'a, +// { +// async move { +// for word in words.into_iter() { +// self.write(word.clone()).await?; +// self.read().await?; +// } + +// Ok(()) +// } +// } +// } +// } + +// /// Operation for transactional SPI trait +// /// +// /// This allows composition of SPI operations into a single bus transaction +// #[derive(Debug, PartialEq)] +// pub enum Operation<'a, W: 'static> { +// /// Write data from the provided buffer, discarding read data +// Write(&'a [W]), +// /// Write data out while reading data into the provided buffer +// Transfer(&'a mut [W]), +// } + +// /// Transactional trait allows multiple actions to be executed +// /// as part of a single SPI transaction +// pub trait Transactional { +// /// Associated error type +// type Error; +// /// Future associated with the `exec` method. +// type ExecFuture<'a>: Future> + 'a +// where +// Self: 'a; + +// /// Execute the provided transactions +// fn exec<'a>(&'a mut self, operations: &'a mut [Operation<'a, W>]) -> Self::ExecFuture<'a>; +// } + +// /// Blocking transactional impl over spi::Write and spi::Transfer +// pub mod transactional { +// use core::future::Future; + +// use super::{Operation, Transfer, Write}; + +// /// Default implementation of `blocking::spi::Transactional` for implementers of +// /// `spi::Write` and `spi::Transfer` +// pub trait Default: Write + Transfer {} + +// impl super::Transactional for S +// where +// S: self::Default + Write + Transfer, +// W: Copy + Clone, +// { +// type Error = E; +// type ExecFuture<'a> where Self: 'a = impl Future> + 'a; + +// fn exec<'a>(&'a mut self, operations: &'a mut [super::Operation<'a, W>]) -> Self::ExecFuture<'a> { +// async move { +// for op in operations { +// match op { +// Operation::Write(w) => self.write(w).await?, +// Operation::Transfer(t) => self.transfer(t).await.map(|_| ())?, +// } +// } + +// Ok(()) +// } +// } +// } +// } diff --git a/src/futures/timer.rs b/src/futures/timer.rs new file mode 100644 index 000000000..37b181a75 --- /dev/null +++ b/src/futures/timer.rs @@ -0,0 +1,101 @@ +//! Timers + +use core::future::Future; + +/// A count down timer +/// +/// # Contract +/// +/// - `self.start(count); block!(self.wait());` MUST block for AT LEAST the time specified by +/// `count`. +/// +/// *Note* that the implementer doesn't necessarily have to be a *downcounting* timer; it could also +/// be an *upcounting* timer as long as the above contract is upheld. +/// +/// # Examples +/// +/// You can use this timer to create delays +/// +/// ``` +/// extern crate embedded_hal as hal; +/// #[macro_use(block)] +/// extern crate nb; +/// +/// use hal::prelude::*; +/// +/// fn main() { +/// let mut led: Led = { +/// // .. +/// # Led +/// }; +/// let mut timer: Timer6 = { +/// // .. +/// # Timer6 +/// }; +/// +/// Led.on(); +/// timer.start(1.s()).unwrap(); +/// block!(timer.wait()); // blocks for 1 second +/// Led.off(); +/// } +/// +/// # use core::convert::Infallible; +/// # struct Seconds(u32); +/// # trait U32Ext { fn s(self) -> Seconds; } +/// # impl U32Ext for u32 { fn s(self) -> Seconds { Seconds(self) } } +/// # struct Led; +/// # impl Led { +/// # pub fn off(&mut self) {} +/// # pub fn on(&mut self) {} +/// # } +/// # struct Timer6; +/// # impl hal::nb::timer::CountDown for Timer6 { +/// # type Error = Infallible; +/// # type Time = Seconds; +/// # fn start(&mut self, _: T) -> Result<(), Self::Error> where T: Into { Ok(()) } +/// # fn wait(&mut self) -> ::nb::Result<(), Infallible> { Ok(()) } +/// # } +/// ``` +pub trait CountDown { + /// An enumeration of `CountDown` errors. + /// + /// For infallible implementations, will be `Infallible` + type Error; + + /// The unit of time used by this timer + type Time; + + /// The future associated with the `wait` method. + type WaitFuture<'a>: Future> + 'a + where + Self: 'a; + + /// Starts a new count down + fn start(&mut self, count: T) -> Result<(), Self::Error> + where + T: Into; + + /// Asyncronously waits until the count down finishes. + /// + /// # Contract + /// + /// - If `Self: Periodic`, the timer will start a new count down right after the last one + /// finishes. + /// - Otherwise the behavior of calling `wait` after the last call returned `Ok` is UNSPECIFIED. + /// Implementers are suggested to panic on this scenario to signal a programmer error. + fn wait<'a>(&'a mut self) -> Self::WaitFuture<'a>; +} + +/// Marker trait that indicates that a timer is periodic +pub trait Periodic {} + +/// Trait for cancelable countdowns. +pub trait Cancel: CountDown { + /// Tries to cancel this countdown. + /// + /// # Errors + /// + /// An error will be returned if the countdown has already been canceled or was never started. + /// An error is also returned if the countdown is not `Periodic` and has already expired. + fn cancel(&mut self) -> Result<(), Self::Error>; +} diff --git a/src/lib.rs b/src/lib.rs index 2ce5b13a2..b495d79b6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -407,10 +407,12 @@ #![doc(html_root_url = "https://docs.rs/embedded-hal/1.0.0-alpha.4")] #![deny(missing_docs)] #![no_std] +#![feature(generic_associated_types, min_type_alias_impl_trait)] pub mod blocking; pub mod fmt; pub mod nb; +pub mod futures; pub mod prelude; mod private { From ff085facf01cd07e5b8e8069c8e6a6fcf15860cb Mon Sep 17 00:00:00 2001 From: Lachlan Sneff Date: Mon, 21 Jun 2021 17:28:39 -0400 Subject: [PATCH 02/31] Fix some of the docs --- src/futures/mod.rs | 17 ++--------------- 1 file changed, 2 insertions(+), 15 deletions(-) diff --git a/src/futures/mod.rs b/src/futures/mod.rs index ee3df2ae3..f25f499af 100644 --- a/src/futures/mod.rs +++ b/src/futures/mod.rs @@ -1,20 +1,7 @@ -//! Non-blocking API +//! Asynchronous APIs //! -//! These traits make use of the [`nb`] crate -//! (*please go read that crate documentation before continuing*) to abstract over -//! the execution model and to also provide an optional blocking operation mode. -//! -//! The `nb::Result` enum is used to add an [`Error::WouldBlock`] variant to the errors -//! of the traits. Using this it is possible to execute actions in a non-blocking -//! way. -//! -//! `block!`, `Result` and `Error` from the [`nb`] crate are re-exported here to avoid -//! crate version mismatches. These should be used instead of importing the `nb` crate -//! directly again in dependent crates. -//! -//! [`nb`]: https://crates.io/crates/nb +//! This traits use `core::future::Future` and generic associated types. -pub use nb::{block, Error, Result}; pub mod adc; pub mod capture; pub mod rng; From 7eb5fd1ca5b795f6f129445b1f726d8d9890458c Mon Sep 17 00:00:00 2001 From: Lachlan Sneff Date: Tue, 22 Jun 2021 09:58:12 -0400 Subject: [PATCH 03/31] Add 'unstable-gats' feature to enable futures module --- Cargo.toml | 4 ++++ src/lib.rs | 3 ++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index d7b8bec8d..d74524022 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,6 +15,10 @@ 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. +unstable-gats = [] + [dependencies] nb = "1" diff --git a/src/lib.rs b/src/lib.rs index b495d79b6..4186f484d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -407,11 +407,12 @@ #![doc(html_root_url = "https://docs.rs/embedded-hal/1.0.0-alpha.4")] #![deny(missing_docs)] #![no_std] -#![feature(generic_associated_types, min_type_alias_impl_trait)] +#![cfg_attr(feature = "unstable-gats", feature(generic_associated_types, min_type_alias_impl_trait))] pub mod blocking; pub mod fmt; pub mod nb; +#[cfg(feature = "unstable-gats")] pub mod futures; pub mod prelude; From 3a30a339faa2a6c3f6a8660695956584bdd188d3 Mon Sep 17 00:00:00 2001 From: Lachlan Sneff Date: Tue, 22 Jun 2021 14:49:48 -0400 Subject: [PATCH 04/31] Add async i2c module --- Cargo.toml | 4 +- src/futures/adc.rs | 99 ---------------------- src/futures/capture.rs | 101 ----------------------- src/futures/i2c.rs | 182 +++++++++++++++++++++++++++++++++++++++++ src/futures/mod.rs | 3 +- src/futures/rng.rs | 6 +- src/futures/spi.rs | 110 +------------------------ src/lib.rs | 4 +- 8 files changed, 193 insertions(+), 316 deletions(-) delete mode 100644 src/futures/adc.rs delete mode 100644 src/futures/capture.rs create mode 100644 src/futures/i2c.rs diff --git a/Cargo.toml b/Cargo.toml index d74524022..eae0705f6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,7 +17,9 @@ 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. -unstable-gats = [] +# Therefore, this feature requires compiling on nightly. +futures = [] +default = ["futures"] [dependencies] nb = "1" diff --git a/src/futures/adc.rs b/src/futures/adc.rs deleted file mode 100644 index b07b3e4cb..000000000 --- a/src/futures/adc.rs +++ /dev/null @@ -1,99 +0,0 @@ -//! Analog-digital conversion traits - -use core::future::Future; - -/// A marker trait to identify MCU pins that can be used as inputs to an ADC channel. -/// -/// This marker trait denotes an object, i.e. a GPIO pin, that is ready for use as an input to the -/// ADC. As ADCs channels can be supplied by multiple pins, this trait defines the relationship -/// between the physical interface and the ADC sampling buffer. -/// -/// ``` -/// # use core::marker::PhantomData; -/// # use embedded_hal::nb::adc::Channel; -/// -/// struct Adc1; // Example ADC with single bank of 8 channels -/// struct Gpio1Pin1(PhantomData); -/// struct Analog(()); // marker type to denote a pin in "analog" mode -/// -/// // GPIO 1 pin 1 can supply an ADC channel when it is configured in Analog mode -/// impl Channel for Gpio1Pin1 { -/// type ID = u8; // ADC channels are identified numerically -/// -/// fn channel(&self) -> Self::ID { -/// 7_u8 // GPIO pin 1 is connected to ADC channel 7 -/// } -/// } -/// -/// struct Adc2; // ADC with two banks of 16 channels -/// struct Gpio2PinA(PhantomData); -/// struct AltFun(()); // marker type to denote some alternate function mode for the pin -/// -/// // GPIO 2 pin A can supply an ADC channel when it's configured in some alternate function mode -/// impl Channel for Gpio2PinA { -/// type ID = (u8, u8); // ADC channels are identified by bank number and channel number -/// -/// fn channel(&self) -> Self::ID { -/// (0, 3) // bank 0 channel 3 -/// } -/// } -/// ``` -pub trait Channel { - /// Channel ID type - /// - /// A type used to identify this ADC channel. For example, if the ADC has eight channels, this - /// might be a `u8`. If the ADC has multiple banks of channels, it could be a tuple, like - /// `(u8: bank_id, u8: channel_id)`. - type ID: Copy; - - /// Get the specific ID that identifies this channel, for example `0_u8` for the first ADC - /// channel, if Self::ID is u8. - fn channel(&self) -> Self::ID; -} - -/// ADCs that sample on single channels per request, and do so at the time of the request. -/// -/// This trait is the interface to an ADC that is configured to read a specific channel at the time -/// of the request (in contrast to continuous asynchronous sampling). -/// -/// ``` -/// use embedded_hal::nb::adc::{Channel, OneShot}; -/// -/// struct MyAdc; // 10-bit ADC, with 5 channels -/// # impl MyAdc { -/// # pub fn power_up(&mut self) {} -/// # pub fn power_down(&mut self) {} -/// # pub fn do_conversion(&mut self, chan: u8) -> u16 { 0xAA55_u16 } -/// # } -/// -/// impl OneShot for MyAdc -/// where -/// WORD: From, -/// PIN: Channel, -/// { -/// type Error = (); -/// -/// fn read(&mut self, pin: &mut PIN) -> nb::Result { -/// let chan = 1 << pin.channel(); -/// self.power_up(); -/// let result = self.do_conversion(chan); -/// self.power_down(); -/// Ok(result.into()) -/// } -/// } -/// ``` -pub trait OneShot> { - /// Error type returned by ADC methods - type Error; - - /// The future associated with the `read` method. - type ReadFuture<'a>: Future> + 'a - where - Self: 'a; - - /// Request that the ADC begin a conversion on the specified pin - /// - /// This method takes a `Pin` reference, as it is expected that the ADC will be able to sample - /// whatever channel underlies the pin. - fn read<'a>(&'a mut self, pin: &'a mut Pin) -> Self::ReadFuture<'a>; -} diff --git a/src/futures/capture.rs b/src/futures/capture.rs deleted file mode 100644 index 39d53ea4c..000000000 --- a/src/futures/capture.rs +++ /dev/null @@ -1,101 +0,0 @@ -//! Input capture - -use core::future::Future; - -/// Input capture -/// -/// # Examples -/// -/// You can use this interface to measure the period of (quasi) periodic signals -/// / events -/// -/// ``` -/// extern crate embedded_hal as hal; -/// #[macro_use(block)] -/// extern crate nb; -/// -/// use hal::prelude::*; -/// -/// fn main() { -/// let mut capture: Capture1 = { -/// // .. -/// # Capture1 -/// }; -/// -/// capture.set_resolution(1.ms()).unwrap(); -/// -/// let before = block!(capture.capture(Channel::_1)).unwrap(); -/// let after = block!(capture.capture(Channel::_1)).unwrap(); -/// -/// let period = after.wrapping_sub(before); -/// -/// println!("Period: {} ms", period); -/// } -/// -/// # use core::convert::Infallible; -/// # struct MilliSeconds(u32); -/// # trait U32Ext { fn ms(self) -> MilliSeconds; } -/// # impl U32Ext for u32 { fn ms(self) -> MilliSeconds { MilliSeconds(self) } } -/// # struct Capture1; -/// # enum Channel { _1 } -/// # impl hal::nb::capture::Capture for Capture1 { -/// # type Error = Infallible; -/// # type Capture = u16; -/// # type Channel = Channel; -/// # type Time = MilliSeconds; -/// # fn capture(&mut self, _: Channel) -> ::nb::Result { Ok(0) } -/// # fn disable(&mut self, _: Channel) -> Result<(), Self::Error> { unimplemented!() } -/// # fn enable(&mut self, _: Channel) -> Result<(), Self::Error> { unimplemented!() } -/// # fn get_resolution(&self) -> Result { unimplemented!() } -/// # fn set_resolution(&mut self, _: T) -> Result<(), Self::Error> where T: Into { Ok(()) } -/// # } -/// ``` -// unproven reason: pre-singletons API. With singletons a `CapturePin` (cf. `PwmPin`) trait seems more -// appropriate -pub trait Capture { - /// Enumeration of `Capture` errors - /// - /// Possible errors: - /// - /// - *overcapture*, the previous capture value was overwritten because it - /// was not read in a timely manner - type Error; - - /// Enumeration of channels that can be used with this `Capture` interface - /// - /// If your `Capture` interface has no channels you can use the type `()` - /// here - type Channel; - - /// A time unit that can be converted into a human time unit (e.g. seconds) - type Time; - - /// The type of the value returned by `capture` - type Capture; - - /// The future associated with the `capture` method. - type CaptureFuture<'a>: Future> + 'a - where - Self: 'a; - - /// "Waits" for a transition in the capture `channel` and returns the value - /// of counter at that instant - /// - /// NOTE that you must multiply the returned value by the *resolution* of - /// this `Capture` interface to get a human time unit (e.g. seconds) - fn capture<'a>(&'a mut self, channel: Self::Channel) -> Self::CaptureFuture<'a>; - - /// Disables a capture `channel` - fn disable(&mut self, channel: Self::Channel) -> Result<(), Self::Error>; - - /// Enables a capture `channel` - fn enable(&mut self, channel: Self::Channel) -> Result<(), Self::Error>; - - /// Returns the current resolution - fn get_resolution(&self) -> Result; - - /// Sets the resolution of the capture timer - fn set_resolution(&mut self, resolution: R) -> Result<(), Self::Error> - where - R: Into; -} diff --git a/src/futures/i2c.rs b/src/futures/i2c.rs new file mode 100644 index 000000000..98fcc1395 --- /dev/null +++ b/src/futures/i2c.rs @@ -0,0 +1,182 @@ +//! 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. + +use core::future::Future; +pub use crate::blocking::i2c::{AddressMode, SevenBitAddress, TenBitAddress, Operation}; + +/// 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, buffer: &'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, bytes: &'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 `buffer` *in a + /// single transaction* + /// + /// # 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, + bytes: &'a [u8], + buffer: &'a mut [u8], + ) -> Self::WriteReadFuture<'a>; +} + +/// Transactional I2C interface. +/// +/// This allows combining operations within an I2C transaction. +pub trait Transactional { + /// Error type + type Error; + /// The future associated with the `exec` method. + type ExecFuture<'a>: Future> + 'a + where + Self: 'a; + + /// Execute the provided operations on the I2C bus. + /// + /// Transaction contract: + /// - Before executing the first operation an ST is sent automatically. This is followed by SAD+R/W as appropriate. + /// - Data from adjacent operations of the same type are sent after each other without an SP or SR. + /// - Between adjacent operations of a different type an SR and SAD+R/W is sent. + /// - After executing the last operation an SP is sent automatically. + /// - If the last operation is a `Read` the master does not send an acknowledge for the last byte. + /// + /// - `ST` = start condition + /// - `SAD+R/W` = slave address followed by bit 1 to indicate reading or 0 to indicate writing + /// - `SR` = repeated start condition + /// - `SP` = stop condition + fn exec<'a>(&'a mut self, address: A, operations: &'a mut [Operation<'a>]) + -> Self::ExecFuture<'a>; +} + +/// Default implementation of `futures::i2c::Transactional` for `futures::i2c::{Read, Write}` implementers. +/// +/// If you implement `futures::i2c::Read` and `futures::i2c::Write` for your I2C peripheral, +/// you can use this default implementation so to automatically implement +/// `futures::i2c::Transactional` as well. +pub mod transactional { + use super::{Future, AddressMode, Operation, Read, Transactional, Write}; + + /// Default implementation of `futures::i2c::Write`, `futures::i2c::Read` and + /// `futures::i2c::WriteRead` traits for `futures::i2c::Transactional` implementers. + pub trait Default: Read + Write {} + + impl Transactional for S + where + A: AddressMode + Copy + 'static, + S: Default + Read + Write, + E: 'static, + { + type Error = E; + + type ExecFuture<'a> where Self: 'a = impl Future> + 'a; + + fn exec<'a>(&'a mut self, address: A, operations: &'a mut [Operation<'a>]) -> Self::ExecFuture<'a> { + async move { + for op in operations { + match op { + Operation::Read(buffer) => self.read(address, buffer).await?, + Operation::Write(buffer) => self.write(address, buffer).await?, + } + } + + Ok(()) + } + } + } +} diff --git a/src/futures/mod.rs b/src/futures/mod.rs index f25f499af..36ccf74a2 100644 --- a/src/futures/mod.rs +++ b/src/futures/mod.rs @@ -2,8 +2,7 @@ //! //! This traits use `core::future::Future` and generic associated types. -pub mod adc; -pub mod capture; +pub mod i2c; pub mod rng; pub mod serial; pub mod spi; diff --git a/src/futures/rng.rs b/src/futures/rng.rs index e00dc255d..6326b2fad 100644 --- a/src/futures/rng.rs +++ b/src/futures/rng.rs @@ -1,6 +1,6 @@ //! Random Number Generator Interface -use core::future::Future; +use core::{future::Future, mem::MaybeUninit}; /// Nonblocking stream of random bytes. pub trait Read { @@ -10,10 +10,10 @@ pub trait Read { type Error; /// The future associated with the `read` method. - type ReadFuture<'a>: Future> + 'a + type ReadFuture<'a>: Future> + 'a where Self: 'a; /// Get a number of bytes from the RNG. - fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReadFuture<'a>; + fn read<'a>(&'a mut self, buf: &'a mut [MaybeUninit]) -> Self::ReadFuture<'a>; } diff --git a/src/futures/spi.rs b/src/futures/spi.rs index 029783b1d..2a2b24fa0 100644 --- a/src/futures/spi.rs +++ b/src/futures/spi.rs @@ -42,57 +42,6 @@ pub trait FullDuplex { fn write<'a>(&'a mut self, word: Word) -> Self::WriteFuture<'a>; } -/// Clock polarity -#[derive(Clone, Copy, PartialEq, Eq)] -pub enum Polarity { - /// Clock signal low when idle - IdleLow, - /// Clock signal high when idle - IdleHigh, -} - -/// Clock phase -#[derive(Clone, Copy, PartialEq, Eq)] -pub enum Phase { - /// Data in "captured" on the first clock transition - CaptureOnFirstTransition, - /// Data in "captured" on the second clock transition - CaptureOnSecondTransition, -} - -/// SPI mode -#[derive(Clone, Copy, PartialEq, Eq)] -pub struct Mode { - /// Clock polarity - pub polarity: Polarity, - /// Clock phase - pub phase: Phase, -} - -/// Helper for CPOL = 0, CPHA = 0 -pub const MODE_0: Mode = Mode { - polarity: Polarity::IdleLow, - phase: Phase::CaptureOnFirstTransition, -}; - -/// Helper for CPOL = 0, CPHA = 1 -pub const MODE_1: Mode = Mode { - polarity: Polarity::IdleLow, - phase: Phase::CaptureOnSecondTransition, -}; - -/// Helper for CPOL = 1, CPHA = 0 -pub const MODE_2: Mode = Mode { - polarity: Polarity::IdleHigh, - phase: Phase::CaptureOnFirstTransition, -}; - -/// Helper for CPOL = 1, CPHA = 1 -pub const MODE_3: Mode = Mode { - polarity: Polarity::IdleHigh, - phase: Phase::CaptureOnSecondTransition, -}; - /// Async transfer pub trait Transfer { /// Error type @@ -104,9 +53,7 @@ pub trait Transfer { Self: 'a; /// Writes `words` to the slave. Returns the `words` received from the slave - fn transfer<'a>(&'a mut self, words: &'a mut [Word]) -> Self::TransferFuture<'a> - where - Self::Error: 'a; + fn transfer<'a>(&'a mut self, words: &'a mut [Word]) -> Self::TransferFuture<'a>; } /// Async write @@ -123,23 +70,6 @@ pub trait Write { fn write<'a>(&'a mut self, words: &'a [W]) -> Self::WriteFuture<'a>; } -/// Async write (iterator version) -pub trait WriteIter { - /// Error type - type Error; - - /// Associated future for the `write_iter` method. - type WriteFuture<'a, WI>: Future> + 'a - where - Self: 'a, - WI: 'a; - - /// Writes `words` to the slave, ignoring all the incoming words - fn write_iter<'a, WI>(&'a mut self, words: WI) -> Self::WriteFuture<'a, WI> - where - WI: IntoIterator + 'a; -} - /// Async transfer pub mod transfer { use core::future::Future; @@ -157,10 +87,7 @@ pub mod transfer { type TransferFuture<'a> where Self: 'a = impl Future> + 'a; - fn transfer<'w>(&'w mut self, words: &'w mut [Word]) -> Self::TransferFuture<'w> - where - S::Error: 'w - { + fn transfer<'w>(&'w mut self, words: &'w mut [Word]) -> Self::TransferFuture<'w> { async move { for word in words.iter_mut() { self.write(word.clone()).await?; @@ -203,39 +130,6 @@ pub mod write { } } -// /// Blocking write (iterator version) -// pub mod write_iter { -// use core::future::Future; - -// /// Default implementation of `blocking::spi::WriteIter` for implementers of -// /// `nonblocking::spi::FullDuplex` -// pub trait Default: super::FullDuplex {} - -// impl super::WriteIter for S -// where -// S: Default, -// W: Clone + 'static, -// { -// type Error = S::Error; - -// type WriteFuture<'a, WI> where Self: 'a, WI: 'a = impl Future> + 'a; - -// fn write_iter<'a, WI>(&'a mut self, words: WI) -> Self::WriteFuture<'a, WI> -// where -// WI: IntoIterator + 'a, -// { -// async move { -// for word in words.into_iter() { -// self.write(word.clone()).await?; -// self.read().await?; -// } - -// Ok(()) -// } -// } -// } -// } - // /// Operation for transactional SPI trait // /// // /// This allows composition of SPI operations into a single bus transaction diff --git a/src/lib.rs b/src/lib.rs index 4186f484d..2c5a7a3b5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -407,12 +407,12 @@ #![doc(html_root_url = "https://docs.rs/embedded-hal/1.0.0-alpha.4")] #![deny(missing_docs)] #![no_std] -#![cfg_attr(feature = "unstable-gats", feature(generic_associated_types, min_type_alias_impl_trait))] +#![cfg_attr(feature = "futures", feature(generic_associated_types, min_type_alias_impl_trait))] pub mod blocking; pub mod fmt; pub mod nb; -#[cfg(feature = "unstable-gats")] +#[cfg(feature = "futures")] pub mod futures; pub mod prelude; From d9d08fa60e6341efce3bebc0fa9aa40b2f5f00a4 Mon Sep 17 00:00:00 2001 From: Lachlan Sneff Date: Tue, 22 Jun 2021 14:54:32 -0400 Subject: [PATCH 05/31] Remove futures::spi::FullDuplex --- src/futures/spi.rs | 100 --------------------------------------------- 1 file changed, 100 deletions(-) diff --git a/src/futures/spi.rs b/src/futures/spi.rs index 2a2b24fa0..de9703b06 100644 --- a/src/futures/spi.rs +++ b/src/futures/spi.rs @@ -2,46 +2,6 @@ use core::future::Future; -/// Full duplex (master mode) -/// -/// # Notes -/// -/// - It's the task of the user of this interface to manage the slave select lines -/// -/// - Due to how full duplex SPI works each `read` call must be preceded by a `write` call. -/// -/// - `read` calls only return the data received with the last `write` call. -/// Previously received data is discarded -/// -/// - Data is only guaranteed to be clocked out when the `read` call succeeds. -/// The slave select line shouldn't be released before that. -/// -/// - Some SPIs can work with 8-bit *and* 16-bit words. You can overload this trait with different -/// `Word` types to allow operation in both modes. -pub trait FullDuplex { - /// An enumeration of SPI errors - type Error; - - /// The future associated with the `read` method. - type ReadFuture<'a>: Future> + 'a - where - Self: 'a; - - /// The future associated with the `write` method. - type WriteFuture<'a>: Future> + 'a - where - Self: 'a; - - /// Reads the word stored in the shift register - /// - /// **NOTE** A word must be sent to the slave before attempting to call this - /// method. - fn read<'a>(&'a mut self) -> Self::ReadFuture<'a>; - - /// Writes a word to the slave - fn write<'a>(&'a mut self, word: Word) -> Self::WriteFuture<'a>; -} - /// Async transfer pub trait Transfer { /// Error type @@ -70,66 +30,6 @@ pub trait Write { fn write<'a>(&'a mut self, words: &'a [W]) -> Self::WriteFuture<'a>; } -/// Async transfer -pub mod transfer { - use core::future::Future; - - /// Default implementation of `futures::spi::Transfer` for implementers of - /// `futures::spi::FullDuplex` - pub trait Default: super::FullDuplex {} - - impl super::Transfer for S - where - S: Default, - Word: Clone + 'static, - { - type Error = S::Error; - - type TransferFuture<'a> where Self: 'a = impl Future> + 'a; - - fn transfer<'w>(&'w mut self, words: &'w mut [Word]) -> Self::TransferFuture<'w> { - async move { - for word in words.iter_mut() { - self.write(word.clone()).await?; - *word = self.read().await?; - } - - Ok(words as &'w [Word]) - } - } - } -} - -/// Blocking write -pub mod write { - use core::future::Future; - - /// Default implementation of `futures::spi::Write` for implementers - /// of `futures::spi::FullDuplex` - pub trait Default: super::FullDuplex {} - - impl super::Write for S - where - S: Default, - W: Clone + 'static, - { - type Error = S::Error; - - type WriteFuture<'a> where Self: 'a = impl Future> + 'a; - - fn write<'a>(&'a mut self, words: &'a [W]) -> Self::WriteFuture<'a> { - async move { - for word in words { - self.write(word.clone()).await?; - self.read().await?; - } - - Ok(()) - } - } - } -} - // /// Operation for transactional SPI trait // /// // /// This allows composition of SPI operations into a single bus transaction From 1928f77b4155293e3d5c828e63176e3082e2174e Mon Sep 17 00:00:00 2001 From: Lachlan Sneff Date: Tue, 22 Jun 2021 15:16:17 -0400 Subject: [PATCH 06/31] Make futures::spi::{Read, Write, ReadWrite, ReadWriteInPlace} traits --- src/futures/spi.rs | 44 ++++++++++++++++++++++++++++++++++++++------ 1 file changed, 38 insertions(+), 6 deletions(-) diff --git a/src/futures/spi.rs b/src/futures/spi.rs index de9703b06..981352999 100644 --- a/src/futures/spi.rs +++ b/src/futures/spi.rs @@ -1,19 +1,35 @@ //! Serial Peripheral Interface -use core::future::Future; +use core::{future::Future, mem::MaybeUninit}; -/// Async transfer -pub trait Transfer { +/// Async read + write +pub trait ReadWrite { /// Error type type Error; /// Associated future for the `transfer` method. - type TransferFuture<'a>: Future> + 'a + type ReadWriteFuture<'a>: Future> + 'a where Self: 'a; - /// Writes `words` to the slave. Returns the `words` received from the slave - fn transfer<'a>(&'a mut self, words: &'a mut [Word]) -> Self::TransferFuture<'a>; + /// Writes `words` to the slave from the `write` buffer. Puts the words returned in the `read` buffer. + /// This method uses separate `write` and `read` buffers. + fn readwrite<'a>(&'a mut self, write: &'a [Word], read: &'a mut [MaybeUninit]) -> Self::ReadWriteFuture<'a>; +} + +/// Async read + write in place. +pub trait ReadWriteInPlace { + /// Error type + type Error; + + /// Associated future for the `transfer` method. + type ReadWriteInPlaceFuture<'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. + fn readwrite_inplace<'a>(&'a mut self, readwrite: &'a mut [MaybeUninit]) -> Self::ReadWriteInPlaceFuture<'a>; } /// Async write @@ -30,6 +46,22 @@ pub trait Write { fn write<'a>(&'a mut self, words: &'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. all zeroes, all ones), some cannot. + fn read<'a>(&'a mut self, words: &'a mut [MaybeUninit]) -> Self::ReadFuture<'a>; +} + // /// Operation for transactional SPI trait // /// // /// This allows composition of SPI operations into a single bus transaction From 5cdd9876e78bf3df7720f076f1a15a32f6cbce3d Mon Sep 17 00:00:00 2001 From: Lachlan Sneff Date: Tue, 22 Jun 2021 15:17:52 -0400 Subject: [PATCH 07/31] Rename the futures feature to unstable-features --- Cargo.toml | 3 +-- src/lib.rs | 4 ++-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index eae0705f6..4e6162b41 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,8 +18,7 @@ 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. -futures = [] -default = ["futures"] +unstable-futures = [] [dependencies] nb = "1" diff --git a/src/lib.rs b/src/lib.rs index 2c5a7a3b5..93c0c72fc 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -407,12 +407,12 @@ #![doc(html_root_url = "https://docs.rs/embedded-hal/1.0.0-alpha.4")] #![deny(missing_docs)] #![no_std] -#![cfg_attr(feature = "futures", feature(generic_associated_types, min_type_alias_impl_trait))] +#![cfg_attr(feature = "unstable-futures", feature(generic_associated_types, min_type_alias_impl_trait))] pub mod blocking; pub mod fmt; pub mod nb; -#[cfg(feature = "futures")] +#[cfg(feature = "unstable-futures")] pub mod futures; pub mod prelude; From eecf646503cb7c8507cefbdf627be3a6e6aa4e79 Mon Sep 17 00:00:00 2001 From: Lachlan Sneff Date: Tue, 22 Jun 2021 15:24:59 -0400 Subject: [PATCH 08/31] Remove commented-out futures::spi::Transactional trait --- src/futures/spi.rs | 58 ---------------------------------------------- 1 file changed, 58 deletions(-) diff --git a/src/futures/spi.rs b/src/futures/spi.rs index 981352999..c3bff478f 100644 --- a/src/futures/spi.rs +++ b/src/futures/spi.rs @@ -61,61 +61,3 @@ pub trait Read { /// by this trait. Some hardware can configure what values (e.g. all zeroes, all ones), some cannot. fn read<'a>(&'a mut self, words: &'a mut [MaybeUninit]) -> Self::ReadFuture<'a>; } - -// /// Operation for transactional SPI trait -// /// -// /// This allows composition of SPI operations into a single bus transaction -// #[derive(Debug, PartialEq)] -// pub enum Operation<'a, W: 'static> { -// /// Write data from the provided buffer, discarding read data -// Write(&'a [W]), -// /// Write data out while reading data into the provided buffer -// Transfer(&'a mut [W]), -// } - -// /// Transactional trait allows multiple actions to be executed -// /// as part of a single SPI transaction -// pub trait Transactional { -// /// Associated error type -// type Error; -// /// Future associated with the `exec` method. -// type ExecFuture<'a>: Future> + 'a -// where -// Self: 'a; - -// /// Execute the provided transactions -// fn exec<'a>(&'a mut self, operations: &'a mut [Operation<'a, W>]) -> Self::ExecFuture<'a>; -// } - -// /// Blocking transactional impl over spi::Write and spi::Transfer -// pub mod transactional { -// use core::future::Future; - -// use super::{Operation, Transfer, Write}; - -// /// Default implementation of `blocking::spi::Transactional` for implementers of -// /// `spi::Write` and `spi::Transfer` -// pub trait Default: Write + Transfer {} - -// impl super::Transactional for S -// where -// S: self::Default + Write + Transfer, -// W: Copy + Clone, -// { -// type Error = E; -// type ExecFuture<'a> where Self: 'a = impl Future> + 'a; - -// fn exec<'a>(&'a mut self, operations: &'a mut [super::Operation<'a, W>]) -> Self::ExecFuture<'a> { -// async move { -// for op in operations { -// match op { -// Operation::Write(w) => self.write(w).await?, -// Operation::Transfer(t) => self.transfer(t).await.map(|_| ())?, -// } -// } - -// Ok(()) -// } -// } -// } -// } From 62989522a445fbfac362d11dea63d8445c98fff8 Mon Sep 17 00:00:00 2001 From: Lachlan Sneff Date: Tue, 22 Jun 2021 15:26:45 -0400 Subject: [PATCH 09/31] Remove MaybeUninit from futures::spi::ReadWriteInPlace --- src/futures/spi.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/futures/spi.rs b/src/futures/spi.rs index c3bff478f..f4f9a2186 100644 --- a/src/futures/spi.rs +++ b/src/futures/spi.rs @@ -29,7 +29,7 @@ pub trait ReadWriteInPlace { /// Writes `words` to the slave from the `readwrite` buffer and reads words into the same buffer. /// This method uses a single `readwrite` buffer. - fn readwrite_inplace<'a>(&'a mut self, readwrite: &'a mut [MaybeUninit]) -> Self::ReadWriteInPlaceFuture<'a>; + fn readwrite_inplace<'a>(&'a mut self, readwrite: &'a mut [Word]) -> Self::ReadWriteInPlaceFuture<'a>; } /// Async write From bac16031a402bd196aeaef41ea2fe48fba15c45d Mon Sep 17 00:00:00 2001 From: Lachlan Sneff Date: Tue, 22 Jun 2021 15:45:37 -0400 Subject: [PATCH 10/31] Remove futures::i2c::Transactional and remove required min_type_alias_impl_trait feature as a result --- src/futures/i2c.rs | 79 ++++------------------------------------------ src/lib.rs | 2 +- 2 files changed, 8 insertions(+), 73 deletions(-) diff --git a/src/futures/i2c.rs b/src/futures/i2c.rs index 98fcc1395..358e42dee 100644 --- a/src/futures/i2c.rs +++ b/src/futures/i2c.rs @@ -16,8 +16,8 @@ //! 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. -use core::future::Future; -pub use crate::blocking::i2c::{AddressMode, SevenBitAddress, TenBitAddress, Operation}; +use core::{future::Future, mem::MaybeUninit}; +pub use crate::blocking::i2c::{AddressMode, SevenBitAddress, TenBitAddress}; /// Async read pub trait Read { @@ -46,7 +46,7 @@ pub trait Read { /// - `MAK` = master acknowledge /// - `NMAK` = master no acknowledge /// - `SP` = stop condition - fn read<'a>(&'a mut self, address: A, buffer: &'a mut [u8]) -> Self::ReadFuture<'a>; + fn read<'a>(&'a mut self, address: A, read: &'a mut [MaybeUninit]) -> Self::ReadFuture<'a>; } /// Async write @@ -74,7 +74,7 @@ pub trait Write { /// - `SAK` = slave acknowledge /// - `Bi` = ith byte of data /// - `SP` = stop condition - fn write<'a>(&'a mut self, address: A, bytes: &'a [u8]) -> Self::WriteFuture<'a>; + fn write<'a>(&'a mut self, address: A, write: &'a [u8]) -> Self::WriteFuture<'a>; } /// Async write + read @@ -86,7 +86,7 @@ pub trait WriteRead { where Self: 'a; - /// Writes bytes to slave with address `address` and then reads enough bytes to fill `buffer` *in a + /// Writes bytes to slave with address `address` and then reads enough bytes to fill `read` *in a /// single transaction* /// /// # I2C Events (contract) @@ -111,72 +111,7 @@ pub trait WriteRead { fn write_read<'a>( &'a mut self, address: A, - bytes: &'a [u8], - buffer: &'a mut [u8], + write: &'a [u8], + read: &'a mut [MaybeUninit], ) -> Self::WriteReadFuture<'a>; } - -/// Transactional I2C interface. -/// -/// This allows combining operations within an I2C transaction. -pub trait Transactional { - /// Error type - type Error; - /// The future associated with the `exec` method. - type ExecFuture<'a>: Future> + 'a - where - Self: 'a; - - /// Execute the provided operations on the I2C bus. - /// - /// Transaction contract: - /// - Before executing the first operation an ST is sent automatically. This is followed by SAD+R/W as appropriate. - /// - Data from adjacent operations of the same type are sent after each other without an SP or SR. - /// - Between adjacent operations of a different type an SR and SAD+R/W is sent. - /// - After executing the last operation an SP is sent automatically. - /// - If the last operation is a `Read` the master does not send an acknowledge for the last byte. - /// - /// - `ST` = start condition - /// - `SAD+R/W` = slave address followed by bit 1 to indicate reading or 0 to indicate writing - /// - `SR` = repeated start condition - /// - `SP` = stop condition - fn exec<'a>(&'a mut self, address: A, operations: &'a mut [Operation<'a>]) - -> Self::ExecFuture<'a>; -} - -/// Default implementation of `futures::i2c::Transactional` for `futures::i2c::{Read, Write}` implementers. -/// -/// If you implement `futures::i2c::Read` and `futures::i2c::Write` for your I2C peripheral, -/// you can use this default implementation so to automatically implement -/// `futures::i2c::Transactional` as well. -pub mod transactional { - use super::{Future, AddressMode, Operation, Read, Transactional, Write}; - - /// Default implementation of `futures::i2c::Write`, `futures::i2c::Read` and - /// `futures::i2c::WriteRead` traits for `futures::i2c::Transactional` implementers. - pub trait Default: Read + Write {} - - impl Transactional for S - where - A: AddressMode + Copy + 'static, - S: Default + Read + Write, - E: 'static, - { - type Error = E; - - type ExecFuture<'a> where Self: 'a = impl Future> + 'a; - - fn exec<'a>(&'a mut self, address: A, operations: &'a mut [Operation<'a>]) -> Self::ExecFuture<'a> { - async move { - for op in operations { - match op { - Operation::Read(buffer) => self.read(address, buffer).await?, - Operation::Write(buffer) => self.write(address, buffer).await?, - } - } - - Ok(()) - } - } - } -} diff --git a/src/lib.rs b/src/lib.rs index 93c0c72fc..e90b2fa6c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -407,7 +407,7 @@ #![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, min_type_alias_impl_trait))] +#![cfg_attr(feature = "unstable-futures", feature(generic_associated_types))] pub mod blocking; pub mod fmt; From 92aa33b1232e80b45a3252c67d3a0b281ad03178 Mon Sep 17 00:00:00 2001 From: Lachlan Sneff Date: Tue, 22 Jun 2021 15:53:06 -0400 Subject: [PATCH 11/31] Remove some unncessary bounds from futures::spi traits --- src/futures/spi.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/futures/spi.rs b/src/futures/spi.rs index f4f9a2186..e037fa93b 100644 --- a/src/futures/spi.rs +++ b/src/futures/spi.rs @@ -3,7 +3,7 @@ use core::{future::Future, mem::MaybeUninit}; /// Async read + write -pub trait ReadWrite { +pub trait ReadWrite { /// Error type type Error; @@ -18,7 +18,7 @@ pub trait ReadWrite { } /// Async read + write in place. -pub trait ReadWriteInPlace { +pub trait ReadWriteInPlace { /// Error type type Error; @@ -33,7 +33,7 @@ pub trait ReadWriteInPlace { } /// Async write -pub trait Write { +pub trait Write { /// Error type type Error; @@ -43,11 +43,11 @@ pub trait Write { Self: 'a; /// Writes `words` to the slave, ignoring all the incoming words - fn write<'a>(&'a mut self, words: &'a [W]) -> Self::WriteFuture<'a>; + fn write<'a>(&'a mut self, words: &'a [Word]) -> Self::WriteFuture<'a>; } /// Async read -pub trait Read { +pub trait Read { /// Error type type Error; @@ -59,5 +59,5 @@ pub trait Read { /// 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. all zeroes, all ones), some cannot. - fn read<'a>(&'a mut self, words: &'a mut [MaybeUninit]) -> Self::ReadFuture<'a>; + fn read<'a>(&'a mut self, words: &'a mut [MaybeUninit]) -> Self::ReadFuture<'a>; } From 582ebada81b276de356456a55dffd5515125fbc7 Mon Sep 17 00:00:00 2001 From: Lachlan Sneff Date: Tue, 22 Jun 2021 18:01:26 -0400 Subject: [PATCH 12/31] Return initialized buffers from method that read into an uninitialized buffer --- src/futures/i2c.rs | 4 ++-- src/futures/rng.rs | 2 +- src/futures/spi.rs | 18 +++++++++++------- 3 files changed, 14 insertions(+), 10 deletions(-) diff --git a/src/futures/i2c.rs b/src/futures/i2c.rs index 358e42dee..c7872da28 100644 --- a/src/futures/i2c.rs +++ b/src/futures/i2c.rs @@ -82,12 +82,12 @@ pub trait WriteRead { /// Error type type Error; /// The future associated with the `write_read` method. - type WriteReadFuture<'a>: Future> + 'a + 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* + /// single transaction*. The returned buffer is the initialized `read` buffer. /// /// # I2C Events (contract) /// diff --git a/src/futures/rng.rs b/src/futures/rng.rs index 6326b2fad..5a6cb0628 100644 --- a/src/futures/rng.rs +++ b/src/futures/rng.rs @@ -14,6 +14,6 @@ pub trait Read { where Self: 'a; - /// Get a number of bytes from the RNG. + /// Get a number of bytes from the RNG. The returned buffer is the initialized `buf`. fn read<'a>(&'a mut self, buf: &'a mut [MaybeUninit]) -> Self::ReadFuture<'a>; } diff --git a/src/futures/spi.rs b/src/futures/spi.rs index e037fa93b..55362dd9a 100644 --- a/src/futures/spi.rs +++ b/src/futures/spi.rs @@ -3,12 +3,12 @@ use core::{future::Future, mem::MaybeUninit}; /// Async read + write -pub trait ReadWrite { +pub trait ReadWrite { /// Error type type Error; /// Associated future for the `transfer` method. - type ReadWriteFuture<'a>: Future> + 'a + type ReadWriteFuture<'a>: Future> + 'a where Self: 'a; @@ -18,17 +18,19 @@ pub trait ReadWrite { } /// Async read + write in place. -pub trait ReadWriteInPlace { +pub trait ReadWriteInPlace { /// Error type type Error; /// Associated future for the `transfer` method. - type ReadWriteInPlaceFuture<'a>: Future> + 'a + type ReadWriteInPlaceFuture<'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 readwrite_inplace<'a>(&'a mut self, readwrite: &'a mut [Word]) -> Self::ReadWriteInPlaceFuture<'a>; } @@ -47,17 +49,19 @@ pub trait Write { } /// Async read -pub trait Read { +pub trait Read { /// Error type type Error; /// Associated future for the `read` method. - type ReadFuture<'a>: Future> + 'a + 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. all zeroes, all ones), some cannot. + /// 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, words: &'a mut [MaybeUninit]) -> Self::ReadFuture<'a>; } From a61008b1db1f7ce116e0de52491e7d0bc9fc5e2f Mon Sep 17 00:00:00 2001 From: Lachlan Sneff Date: Tue, 29 Jun 2021 14:51:12 -0400 Subject: [PATCH 13/31] Update spi trait names --- src/futures/spi.rs | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/futures/spi.rs b/src/futures/spi.rs index 55362dd9a..331b90c0b 100644 --- a/src/futures/spi.rs +++ b/src/futures/spi.rs @@ -2,28 +2,28 @@ use core::{future::Future, mem::MaybeUninit}; -/// Async read + write -pub trait ReadWrite { +/// Async transfer +pub trait Transfer { /// Error type type Error; /// Associated future for the `transfer` method. - type ReadWriteFuture<'a>: Future> + 'a + type TransferFuture<'a>: Future> + 'a where Self: 'a; /// Writes `words` to the slave from the `write` buffer. Puts the words returned in the `read` buffer. /// This method uses separate `write` and `read` buffers. - fn readwrite<'a>(&'a mut self, write: &'a [Word], read: &'a mut [MaybeUninit]) -> Self::ReadWriteFuture<'a>; + fn transfer<'a>(&'a mut self, write: &'a [Word], read: &'a mut [MaybeUninit]) -> Self::TransferFuture<'a>; } -/// Async read + write in place. -pub trait ReadWriteInPlace { +/// Async transfer in place. +pub trait TransferInPlace { /// Error type type Error; - /// Associated future for the `transfer` method. - type ReadWriteInPlaceFuture<'a>: Future> + 'a + /// Associated future for the `transfer_inplace` method. + type TransferInPlaceFuture<'a>: Future> + 'a where Self: 'a; @@ -31,7 +31,7 @@ pub trait ReadWriteInPlace { /// This method uses a single `readwrite` buffer. /// /// The returned buffer is the initialized `readwrite` buffer. - fn readwrite_inplace<'a>(&'a mut self, readwrite: &'a mut [Word]) -> Self::ReadWriteInPlaceFuture<'a>; + fn transfer_inplace<'a>(&'a mut self, readwrite: &'a mut [Word]) -> Self::TransferInPlaceFuture<'a>; } /// Async write @@ -54,7 +54,7 @@ pub trait Read { type Error; /// Associated future for the `read` method. - type ReadFuture<'a>: Future> + 'a + type ReadFuture<'a>: Future> + 'a where Self: 'a; From 306b18795922c5ab809c6da99825b8ae71084f50 Mon Sep 17 00:00:00 2001 From: Lachlan Sneff Date: Tue, 29 Jun 2021 15:09:44 -0400 Subject: [PATCH 14/31] Remove MaybeUninit from futures::spi --- src/futures/spi.rs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/futures/spi.rs b/src/futures/spi.rs index 331b90c0b..22e355e75 100644 --- a/src/futures/spi.rs +++ b/src/futures/spi.rs @@ -1,9 +1,9 @@ //! Serial Peripheral Interface -use core::{future::Future, mem::MaybeUninit}; +use core::future::Future; /// Async transfer -pub trait Transfer { +pub trait Transfer { /// Error type type Error; @@ -14,11 +14,11 @@ pub trait Transfer { /// Writes `words` to the slave from the `write` buffer. Puts the words returned in the `read` buffer. /// This method uses separate `write` and `read` buffers. - fn transfer<'a>(&'a mut self, write: &'a [Word], read: &'a mut [MaybeUninit]) -> Self::TransferFuture<'a>; + fn transfer<'a>(&'a mut self, write: &'a [W], read: &'a mut [W]) -> Self::TransferFuture<'a>; } /// Async transfer in place. -pub trait TransferInPlace { +pub trait TransferInPlace { /// Error type type Error; @@ -31,11 +31,11 @@ pub trait TransferInPlace { /// This method uses a single `readwrite` buffer. /// /// The returned buffer is the initialized `readwrite` buffer. - fn transfer_inplace<'a>(&'a mut self, readwrite: &'a mut [Word]) -> Self::TransferInPlaceFuture<'a>; + fn transfer_inplace<'a>(&'a mut self, readwrite: &'a mut [W]) -> Self::TransferInPlaceFuture<'a>; } /// Async write -pub trait Write { +pub trait Write { /// Error type type Error; @@ -45,11 +45,11 @@ pub trait Write { Self: 'a; /// Writes `words` to the slave, ignoring all the incoming words - fn write<'a>(&'a mut self, words: &'a [Word]) -> Self::WriteFuture<'a>; + fn write<'a>(&'a mut self, words: &'a [W]) -> Self::WriteFuture<'a>; } /// Async read -pub trait Read { +pub trait Read { /// Error type type Error; @@ -63,5 +63,5 @@ pub trait Read { /// 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, words: &'a mut [MaybeUninit]) -> Self::ReadFuture<'a>; + fn read<'a>(&'a mut self, words: &'a mut [W]) -> Self::ReadFuture<'a>; } From 92e0da4c9f0d452323ef657063a3770d5694aed3 Mon Sep 17 00:00:00 2001 From: Lachlan Sneff Date: Tue, 29 Jun 2021 15:31:52 -0400 Subject: [PATCH 15/31] Update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2616fa700..a628522bd 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 async traits (using unstable GATs) for I2C, RNG, Serial, SPI, and Timers. ### Changed - Swap PWM channel arguments to references From b79bf162940e5b24be9dcf3a5c75237ba533e293 Mon Sep 17 00:00:00 2001 From: Lachlan Sneff Date: Tue, 6 Jul 2021 09:44:44 -0400 Subject: [PATCH 16/31] Expand the CHANGELOG.md addition Co-authored-by: Diego Barrios Romero --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 28ae3bb47..52e07df3d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,7 +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 async traits (using unstable GATs) for I2C, RNG, Serial, SPI, and Timers. +- Added `futures` module that contains asynchronous traits (using currently unstable GATs) for I2C, RNG, Serial, SPI, and Timers. 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 From 4d02ea138c74e3c4665c7ab321e17996f86ce8d4 Mon Sep 17 00:00:00 2001 From: Lachlan Sneff Date: Tue, 6 Jul 2021 10:17:06 -0400 Subject: [PATCH 17/31] Add futures::Delay trait --- src/futures/delay.rs | 27 ++++++++++++ src/futures/mod.rs | 3 +- src/futures/rng.rs | 19 -------- src/futures/spi.rs | 6 +-- src/futures/timer.rs | 101 ------------------------------------------- 5 files changed, 31 insertions(+), 125 deletions(-) create mode 100644 src/futures/delay.rs delete mode 100644 src/futures/rng.rs delete mode 100644 src/futures/timer.rs diff --git a/src/futures/delay.rs b/src/futures/delay.rs new file mode 100644 index 000000000..be6fc547c --- /dev/null +++ b/src/futures/delay.rs @@ -0,0 +1,27 @@ +//! 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; + +/// Asynchronously wait a duration of time. +pub trait Delay { + /// Enumeration of `Delay` errors. + type Error; + + /// The future returned from `delay`. + type DelayFuture<'a>: Future> + 'a + where + Self: 'a; + + /// The unit of time used by this delay timer. + type Time; + + /// 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>(&mut self, duration: impl Into) -> Self::DelayFuture<'a>; +} diff --git a/src/futures/mod.rs b/src/futures/mod.rs index 36ccf74a2..1a94ed258 100644 --- a/src/futures/mod.rs +++ b/src/futures/mod.rs @@ -3,7 +3,6 @@ //! This traits use `core::future::Future` and generic associated types. pub mod i2c; -pub mod rng; pub mod serial; pub mod spi; -pub mod timer; +pub mod delay; diff --git a/src/futures/rng.rs b/src/futures/rng.rs deleted file mode 100644 index 5a6cb0628..000000000 --- a/src/futures/rng.rs +++ /dev/null @@ -1,19 +0,0 @@ -//! Random Number Generator Interface - -use core::{future::Future, mem::MaybeUninit}; - -/// Nonblocking stream of random bytes. -pub trait Read { - /// An enumeration of RNG errors. - /// - /// For infallible implementations, will be `Infallible` - type Error; - - /// The future associated with the `read` method. - type ReadFuture<'a>: Future> + 'a - where - Self: 'a; - - /// Get a number of bytes from the RNG. The returned buffer is the initialized `buf`. - fn read<'a>(&'a mut self, buf: &'a mut [MaybeUninit]) -> Self::ReadFuture<'a>; -} diff --git a/src/futures/spi.rs b/src/futures/spi.rs index 22e355e75..daf72d4cd 100644 --- a/src/futures/spi.rs +++ b/src/futures/spi.rs @@ -31,7 +31,7 @@ pub trait TransferInPlace { /// This method uses a single `readwrite` buffer. /// /// The returned buffer is the initialized `readwrite` buffer. - fn transfer_inplace<'a>(&'a mut self, readwrite: &'a mut [W]) -> Self::TransferInPlaceFuture<'a>; + fn transfer_inplace<'a>(&'a mut self, words: &'a mut [W]) -> Self::TransferInPlaceFuture<'a>; } /// Async write @@ -45,7 +45,7 @@ pub trait Write { Self: 'a; /// Writes `words` to the slave, ignoring all the incoming words - fn write<'a>(&'a mut self, words: &'a [W]) -> Self::WriteFuture<'a>; + fn write<'a>(&'a mut self, write: &'a [W]) -> Self::WriteFuture<'a>; } /// Async read @@ -63,5 +63,5 @@ pub trait Read { /// 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, words: &'a mut [W]) -> Self::ReadFuture<'a>; + fn read<'a>(&'a mut self, read: &'a mut [W]) -> Self::ReadFuture<'a>; } diff --git a/src/futures/timer.rs b/src/futures/timer.rs deleted file mode 100644 index 37b181a75..000000000 --- a/src/futures/timer.rs +++ /dev/null @@ -1,101 +0,0 @@ -//! Timers - -use core::future::Future; - -/// A count down timer -/// -/// # Contract -/// -/// - `self.start(count); block!(self.wait());` MUST block for AT LEAST the time specified by -/// `count`. -/// -/// *Note* that the implementer doesn't necessarily have to be a *downcounting* timer; it could also -/// be an *upcounting* timer as long as the above contract is upheld. -/// -/// # Examples -/// -/// You can use this timer to create delays -/// -/// ``` -/// extern crate embedded_hal as hal; -/// #[macro_use(block)] -/// extern crate nb; -/// -/// use hal::prelude::*; -/// -/// fn main() { -/// let mut led: Led = { -/// // .. -/// # Led -/// }; -/// let mut timer: Timer6 = { -/// // .. -/// # Timer6 -/// }; -/// -/// Led.on(); -/// timer.start(1.s()).unwrap(); -/// block!(timer.wait()); // blocks for 1 second -/// Led.off(); -/// } -/// -/// # use core::convert::Infallible; -/// # struct Seconds(u32); -/// # trait U32Ext { fn s(self) -> Seconds; } -/// # impl U32Ext for u32 { fn s(self) -> Seconds { Seconds(self) } } -/// # struct Led; -/// # impl Led { -/// # pub fn off(&mut self) {} -/// # pub fn on(&mut self) {} -/// # } -/// # struct Timer6; -/// # impl hal::nb::timer::CountDown for Timer6 { -/// # type Error = Infallible; -/// # type Time = Seconds; -/// # fn start(&mut self, _: T) -> Result<(), Self::Error> where T: Into { Ok(()) } -/// # fn wait(&mut self) -> ::nb::Result<(), Infallible> { Ok(()) } -/// # } -/// ``` -pub trait CountDown { - /// An enumeration of `CountDown` errors. - /// - /// For infallible implementations, will be `Infallible` - type Error; - - /// The unit of time used by this timer - type Time; - - /// The future associated with the `wait` method. - type WaitFuture<'a>: Future> + 'a - where - Self: 'a; - - /// Starts a new count down - fn start(&mut self, count: T) -> Result<(), Self::Error> - where - T: Into; - - /// Asyncronously waits until the count down finishes. - /// - /// # Contract - /// - /// - If `Self: Periodic`, the timer will start a new count down right after the last one - /// finishes. - /// - Otherwise the behavior of calling `wait` after the last call returned `Ok` is UNSPECIFIED. - /// Implementers are suggested to panic on this scenario to signal a programmer error. - fn wait<'a>(&'a mut self) -> Self::WaitFuture<'a>; -} - -/// Marker trait that indicates that a timer is periodic -pub trait Periodic {} - -/// Trait for cancelable countdowns. -pub trait Cancel: CountDown { - /// Tries to cancel this countdown. - /// - /// # Errors - /// - /// An error will be returned if the countdown has already been canceled or was never started. - /// An error is also returned if the countdown is not `Periodic` and has already expired. - fn cancel(&mut self) -> Result<(), Self::Error>; -} From 41af5891a78bba78dd741348c29da4c61ce8cc88 Mon Sep 17 00:00:00 2001 From: Lachlan Sneff Date: Tue, 6 Jul 2021 10:31:34 -0400 Subject: [PATCH 18/31] Add futures::digital::AsyncInputPin trait --- src/futures/digital.rs | 18 ++++++++++++++++++ src/futures/mod.rs | 1 + 2 files changed, 19 insertions(+) create mode 100644 src/futures/digital.rs diff --git a/src/futures/digital.rs b/src/futures/digital.rs new file mode 100644 index 000000000..3d86cc097 --- /dev/null +++ b/src/futures/digital.rs @@ -0,0 +1,18 @@ +//! Asynchronous digital I/O + +use core::future::Future; + +/// Asynchronously wait for a pin to become high or low. +pub trait AsyncInputPin { + /// The future returned by the `until_high` function. + type UntilHighFuture<'a>: Future + 'a; + + /// The future returned by the `until_low` function. + type UntilLowFuture<'a>: Future + 'a; + + /// Returns a future that resolves when this pin becomes high. + fn until_high<'a>(&self) -> Self::UntilHighFuture<'a>; + + /// Returns a future that resolves when this pin becomes high. + fn until_low<'a>(&self) -> Self::UntilLowFuture<'a>; +} diff --git a/src/futures/mod.rs b/src/futures/mod.rs index 1a94ed258..607584831 100644 --- a/src/futures/mod.rs +++ b/src/futures/mod.rs @@ -6,3 +6,4 @@ pub mod i2c; pub mod serial; pub mod spi; pub mod delay; +pub mod digital; From 48ab3a8c94c851277ec1ee6f4c66f9d3f7343877 Mon Sep 17 00:00:00 2001 From: Lachlan Sneff Date: Tue, 6 Jul 2021 15:17:14 -0400 Subject: [PATCH 19/31] Switch futures::Delay to take core::time::Duration and add futures::digital docs --- src/futures/delay.rs | 7 ++----- src/futures/digital.rs | 34 +++++++++++++++++++++++++++++++++- 2 files changed, 35 insertions(+), 6 deletions(-) diff --git a/src/futures/delay.rs b/src/futures/delay.rs index be6fc547c..5e2bf4581 100644 --- a/src/futures/delay.rs +++ b/src/futures/delay.rs @@ -5,7 +5,7 @@ //! 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; +use core::{future::Future, time::Duration}; /// Asynchronously wait a duration of time. pub trait Delay { @@ -17,11 +17,8 @@ pub trait Delay { where Self: 'a; - /// The unit of time used by this delay timer. - type Time; - /// 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>(&mut self, duration: impl Into) -> Self::DelayFuture<'a>; + fn delay<'a>(&mut self, duration: Duration) -> Self::DelayFuture<'a>; } diff --git a/src/futures/digital.rs b/src/futures/digital.rs index 3d86cc097..28eb7ce61 100644 --- a/src/futures/digital.rs +++ b/src/futures/digital.rs @@ -2,8 +2,40 @@ use core::future::Future; +use crate::blocking::digital::InputPin; + /// Asynchronously wait for a pin to become high or low. -pub trait AsyncInputPin { +/// +/// # 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: AsyncInputPin, +/// { +/// ready_pin.until_high().await +/// } +/// ``` +/// +/// ```rust,ignore +/// # use embedded_hal::futures::digital::AsyncInputPin; +/// # use embedded_hal::futures::delay::Delay; +/// # use core::time::Duration; +/// /// Wait until the `ready_pin` is high or timeout after 1 millisecond. +/// /// Returns true is 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: AsyncInputPin, +/// D: Delay, +/// { +/// futures::select_biased! { +/// _ => ready_pin.until_high() => true, +/// _ => delay.delay(Duration::from_millis(1)) => false, +/// } +/// } +/// ``` +pub trait AsyncInputPin: InputPin { /// The future returned by the `until_high` function. type UntilHighFuture<'a>: Future + 'a; From 85accbd0a87a75b63a1a6fea6169d56a7a0d805c Mon Sep 17 00:00:00 2001 From: Lachlan Sneff Date: Tue, 6 Jul 2021 15:18:04 -0400 Subject: [PATCH 20/31] Update changelog --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 52e07df3d..c0a43d873 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,7 +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, and Timers. 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. +- 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 From eb2ff8a8c33fcce23f1ef37ae132a74482782627 Mon Sep 17 00:00:00 2001 From: Lachlan Sneff Date: Tue, 6 Jul 2021 15:37:44 -0400 Subject: [PATCH 21/31] Fix some docs in the futures module --- src/futures/delay.rs | 12 ++++++++++++ src/futures/digital.rs | 19 +++++++++++++------ 2 files changed, 25 insertions(+), 6 deletions(-) diff --git a/src/futures/delay.rs b/src/futures/delay.rs index 5e2bf4581..beb66925d 100644 --- a/src/futures/delay.rs +++ b/src/futures/delay.rs @@ -8,6 +8,18 @@ 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; diff --git a/src/futures/digital.rs b/src/futures/digital.rs index 28eb7ce61..99c32da9e 100644 --- a/src/futures/digital.rs +++ b/src/futures/digital.rs @@ -14,14 +14,18 @@ use crate::blocking::digital::InputPin; /// where /// P: AsyncInputPin, /// { -/// ready_pin.until_high().await +/// ready_pin +/// .until_high() +/// .await +/// .expect("failed to await input pin") /// } /// ``` /// /// ```rust,ignore /// # use embedded_hal::futures::digital::AsyncInputPin; /// # use embedded_hal::futures::delay::Delay; -/// # use core::time::Duration; +/// use core::time::Duration; +/// /// /// Wait until the `ready_pin` is high or timeout after 1 millisecond. /// /// Returns true is the pin became high or false if it timed-out. /// async fn wait_until_ready_or_timeout(ready_pin: &P, delay: &mut D) -> bool @@ -30,17 +34,20 @@ use crate::blocking::digital::InputPin; /// D: Delay, /// { /// futures::select_biased! { -/// _ => ready_pin.until_high() => true, -/// _ => delay.delay(Duration::from_millis(1)) => false, +/// x => ready_pin.until_high() => { +/// x.expect("failed to await input pin"); +/// true +/// }, +/// _ => delay.delay(Duration::from_millis(1)) => false, // ignore the error /// } /// } /// ``` pub trait AsyncInputPin: InputPin { /// The future returned by the `until_high` function. - type UntilHighFuture<'a>: Future + 'a; + type UntilHighFuture<'a>: Future> + 'a; /// The future returned by the `until_low` function. - type UntilLowFuture<'a>: Future + 'a; + type UntilLowFuture<'a>: Future> + 'a; /// Returns a future that resolves when this pin becomes high. fn until_high<'a>(&self) -> Self::UntilHighFuture<'a>; From d9174d2da688151d879785f4632ca63ee23d586a Mon Sep 17 00:00:00 2001 From: Lachlan Sneff Date: Tue, 6 Jul 2021 15:59:23 -0400 Subject: [PATCH 22/31] Respond to feedback --- src/futures/delay.rs | 2 +- src/futures/digital.rs | 27 ++++++++++++++++++--------- 2 files changed, 19 insertions(+), 10 deletions(-) diff --git a/src/futures/delay.rs b/src/futures/delay.rs index beb66925d..f517add2d 100644 --- a/src/futures/delay.rs +++ b/src/futures/delay.rs @@ -32,5 +32,5 @@ pub trait Delay { /// 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>(&mut self, duration: Duration) -> Self::DelayFuture<'a>; + fn delay<'a>(&'a mut self, duration: Duration) -> Self::DelayFuture<'a>; } diff --git a/src/futures/digital.rs b/src/futures/digital.rs index 99c32da9e..21ca1e786 100644 --- a/src/futures/digital.rs +++ b/src/futures/digital.rs @@ -12,10 +12,10 @@ use crate::blocking::digital::InputPin; /// /// Asynchronously wait until the `ready_pin` becomes high. /// async fn wait_until_ready

(ready_pin: &P) /// where -/// P: AsyncInputPin, +/// P: WaitFor, /// { /// ready_pin -/// .until_high() +/// .wait_for_high() /// .await /// .expect("failed to await input pin") /// } @@ -30,11 +30,11 @@ use crate::blocking::digital::InputPin; /// /// Returns true is 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: AsyncInputPin, +/// P: WaitFor, /// D: Delay, /// { /// futures::select_biased! { -/// x => ready_pin.until_high() => { +/// x => ready_pin.wait_for_high() => { /// x.expect("failed to await input pin"); /// true /// }, @@ -42,16 +42,25 @@ use crate::blocking::digital::InputPin; /// } /// } /// ``` -pub trait AsyncInputPin: InputPin { +pub trait WaitFor: InputPin { /// The future returned by the `until_high` function. type UntilHighFuture<'a>: Future> + 'a; /// The future returned by the `until_low` function. type UntilLowFuture<'a>: Future> + 'a; - /// Returns a future that resolves when this pin becomes high. - fn until_high<'a>(&self) -> Self::UntilHighFuture<'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::UntilHighFuture<'a>; - /// Returns a future that resolves when this pin becomes high. - fn until_low<'a>(&self) -> Self::UntilLowFuture<'a>; + /// Returns a future that resolves when this pin becomes low. + /// + /// # 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::UntilLowFuture<'a>; } From 941c8bb0533f9329a3c4b5dee2c27f85b2dae1a2 Mon Sep 17 00:00:00 2001 From: Lachlan Sneff Date: Tue, 6 Jul 2021 16:00:48 -0400 Subject: [PATCH 23/31] Change uart interface to read into a slice --- src/futures/serial.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/futures/serial.rs b/src/futures/serial.rs index 95f5a192a..c10d7d41c 100644 --- a/src/futures/serial.rs +++ b/src/futures/serial.rs @@ -11,12 +11,12 @@ pub trait Read { type Error; /// The future associated with the `read` method. - type ReadFuture<'a>: Future> + 'a + type ReadFuture<'a>: Future> + 'a where Self: 'a; - /// Reads a single word from the serial interface - fn read<'a>(&'a mut self) -> Self::ReadFuture<'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 From 5617082e9da1c726ef02b4733a0224d3ce0bbab5 Mon Sep 17 00:00:00 2001 From: Lachlan Sneff Date: Tue, 6 Jul 2021 16:06:18 -0400 Subject: [PATCH 24/31] Mention what happens when the write and read slices in futures::spi::Transfer are different lengths --- src/futures/spi.rs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/futures/spi.rs b/src/futures/spi.rs index daf72d4cd..52597fb60 100644 --- a/src/futures/spi.rs +++ b/src/futures/spi.rs @@ -12,8 +12,14 @@ pub trait Transfer { where Self: 'a; - /// Writes `words` to the slave from the `write` buffer. Puts the words returned in the `read` buffer. - /// This method uses separate `write` and `read` buffers. + /// 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>; } From c002a237913d162e786bb29683d1c3bed2d6731a Mon Sep 17 00:00:00 2001 From: Lachlan Sneff Date: Tue, 6 Jul 2021 16:23:29 -0400 Subject: [PATCH 25/31] Add more futures::digital traits --- src/futures/digital.rs | 138 ++++++++++++++++++++++++++--------------- 1 file changed, 89 insertions(+), 49 deletions(-) diff --git a/src/futures/digital.rs b/src/futures/digital.rs index 21ca1e786..db46b0d14 100644 --- a/src/futures/digital.rs +++ b/src/futures/digital.rs @@ -1,53 +1,50 @@ //! 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 is 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; -use crate::blocking::digital::InputPin; - -/// Asynchronously wait for a pin to become high or low. -/// -/// # 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::AsyncInputPin; -/// # use embedded_hal::futures::delay::Delay; -/// use core::time::Duration; -/// -/// /// Wait until the `ready_pin` is high or timeout after 1 millisecond. -/// /// Returns true is 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: WaitFor, -/// 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 -/// } -/// } -/// ``` -pub trait WaitFor: InputPin { - /// The future returned by the `until_high` function. - type UntilHighFuture<'a>: Future> + 'a; - - /// The future returned by the `until_low` function. - type UntilLowFuture<'a>: Future> + 'a; +/// Asynchronously wait for a pin to be high. +pub trait WaitForHigh { + /// The future returned by the `wait_for_high` function. + type WaitForHigh<'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. @@ -55,12 +52,55 @@ pub trait WaitFor: InputPin { /// # 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::UntilHighFuture<'a>; + fn wait_for_high<'a>(&'a mut self) -> Self::WaitForHigh<'a>; +} + +/// Asynchronously wait for a pin to be low. +pub trait WaitForLow { + /// The future returned by `wait_for_low`. + type WaitForLow<'a>: Future + 'a + where + Self: 'a; - /// Returns a future that resolves when this pin becomes low. + /// 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::UntilLowFuture<'a>; + fn wait_for_low<'a>(&'a mut self) -> Self::WaitForLow<'a>; +} + +/// Wait for a rising edge (transition from low to high). +pub trait WaitForRisingEdge { + /// 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 { + /// 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 { + /// 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>; } From e686e6e83edf181099e27f75005fdee97731a640 Mon Sep 17 00:00:00 2001 From: Lachlan Sneff Date: Tue, 6 Jul 2021 16:25:27 -0400 Subject: [PATCH 26/31] Change some associated future trait type names --- src/futures/digital.rs | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/futures/digital.rs b/src/futures/digital.rs index db46b0d14..208328332 100644 --- a/src/futures/digital.rs +++ b/src/futures/digital.rs @@ -42,7 +42,7 @@ use core::future::Future; /// Asynchronously wait for a pin to be high. pub trait WaitForHigh { /// The future returned by the `wait_for_high` function. - type WaitForHigh<'a>: Future + 'a + type HighFuture<'a>: Future + 'a where Self: 'a; @@ -52,13 +52,13 @@ pub trait WaitForHigh { /// # 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::WaitForHigh<'a>; + fn wait_for_high<'a>(&'a mut self) -> Self::HighFuture<'a>; } /// Asynchronously wait for a pin to be low. pub trait WaitForLow { /// The future returned by `wait_for_low`. - type WaitForLow<'a>: Future + 'a + type LowFuture<'a>: Future + 'a where Self: 'a; @@ -68,39 +68,39 @@ pub trait WaitForLow { /// # 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::WaitForLow<'a>; + fn wait_for_low<'a>(&'a mut self) -> Self::LowFuture<'a>; } /// Wait for a rising edge (transition from low to high). pub trait WaitForRisingEdge { /// The future returned from `wait_for_rising_edge`. - type WaitForRisingEdgeFuture<'a>: Future + 'a + type RisingFuture<'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>; + fn wait_for_rising_edge<'a>(&'a mut self) -> Self::RisingFuture<'a>; } /// Wait for a falling edge (transition from high to low). pub trait WaitForFallingEdge { /// The future returned from `wait_for_falling_edge`. - type WaitForFallingEdgeFuture<'a>: Future + 'a + type FallingFuture<'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>; + fn wait_for_falling_edge<'a>(&'a mut self) -> Self::FallingFuture<'a>; } /// Wait for any edge (transition from low to high OR high to low). pub trait WaitForAnyEdge { /// The future returned from `wait_for_any_edge`. - type WaitForAnyEdgeFuture<'a>: Future + 'a + type EdgeFuture<'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>; + fn wait_for_any_edge<'a>(&'a mut self) -> Self::EdgeFuture<'a>; } From 2e16b795ef77751f69ee370ed6b46710c87fb0ea Mon Sep 17 00:00:00 2001 From: Lachlan Sneff Date: Tue, 6 Jul 2021 16:26:44 -0400 Subject: [PATCH 27/31] Formatting --- src/futures/digital.rs | 2 +- src/futures/i2c.rs | 2 +- src/futures/mod.rs | 4 ++-- src/futures/serial.rs | 6 +++--- src/lib.rs | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/futures/digital.rs b/src/futures/digital.rs index 208328332..c08e1cdf2 100644 --- a/src/futures/digital.rs +++ b/src/futures/digital.rs @@ -42,7 +42,7 @@ use core::future::Future; /// Asynchronously wait for a pin to be high. pub trait WaitForHigh { /// The future returned by the `wait_for_high` function. - type HighFuture<'a>: Future + 'a + type HighFuture<'a>: Future + 'a where Self: 'a; diff --git a/src/futures/i2c.rs b/src/futures/i2c.rs index c7872da28..d0e14e50a 100644 --- a/src/futures/i2c.rs +++ b/src/futures/i2c.rs @@ -16,8 +16,8 @@ //! 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. -use core::{future::Future, mem::MaybeUninit}; pub use crate::blocking::i2c::{AddressMode, SevenBitAddress, TenBitAddress}; +use core::{future::Future, mem::MaybeUninit}; /// Async read pub trait Read { diff --git a/src/futures/mod.rs b/src/futures/mod.rs index 607584831..4e59c9da0 100644 --- a/src/futures/mod.rs +++ b/src/futures/mod.rs @@ -2,8 +2,8 @@ //! //! 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; -pub mod delay; -pub mod digital; diff --git a/src/futures/serial.rs b/src/futures/serial.rs index c10d7d41c..64b32b7df 100644 --- a/src/futures/serial.rs +++ b/src/futures/serial.rs @@ -11,7 +11,7 @@ pub trait Read { type Error; /// The future associated with the `read` method. - type ReadFuture<'a>: Future> + 'a + type ReadFuture<'a>: Future> + 'a where Self: 'a; @@ -25,12 +25,12 @@ pub trait Write { type Error; /// The future associated with the `write` method. - type WriteFuture<'a>: Future> + 'a + type WriteFuture<'a>: Future> + 'a where Self: 'a; /// The future associated with the `flush` method. - type FlushFuture<'a>: Future> + 'a + type FlushFuture<'a>: Future> + 'a where Self: 'a; diff --git a/src/lib.rs b/src/lib.rs index 1120802a6..7c5fe811c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -412,9 +412,9 @@ pub mod blocking; pub mod fmt; -pub mod nb; #[cfg(feature = "unstable-futures")] pub mod futures; +pub mod nb; mod private { use crate::blocking::i2c::{SevenBitAddress, TenBitAddress}; From 55cbca114f297d70c2d82020169354a15cf57e6a Mon Sep 17 00:00:00 2001 From: Lachlan Sneff Date: Tue, 6 Jul 2021 16:29:23 -0400 Subject: [PATCH 28/31] Remove MaybeUninit from futures::i2c --- src/futures/i2c.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/futures/i2c.rs b/src/futures/i2c.rs index d0e14e50a..f9aa6c715 100644 --- a/src/futures/i2c.rs +++ b/src/futures/i2c.rs @@ -17,7 +17,7 @@ //! `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, mem::MaybeUninit}; +use core::future::Future; /// Async read pub trait Read { @@ -46,7 +46,7 @@ pub trait Read { /// - `MAK` = master acknowledge /// - `NMAK` = master no acknowledge /// - `SP` = stop condition - fn read<'a>(&'a mut self, address: A, read: &'a mut [MaybeUninit]) -> Self::ReadFuture<'a>; + fn read<'a>(&'a mut self, address: A, read: &'a mut [u8]) -> Self::ReadFuture<'a>; } /// Async write @@ -112,6 +112,6 @@ pub trait WriteRead { &'a mut self, address: A, write: &'a [u8], - read: &'a mut [MaybeUninit], + read: &'a mut [u8], ) -> Self::WriteReadFuture<'a>; } From fee1b993bc913f6f40609f7afab62b6ca1b98318 Mon Sep 17 00:00:00 2001 From: Lachlan Sneff Date: Tue, 6 Jul 2021 16:32:08 -0400 Subject: [PATCH 29/31] fix typo --- src/futures/digital.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/futures/digital.rs b/src/futures/digital.rs index c08e1cdf2..f5dd1e7ca 100644 --- a/src/futures/digital.rs +++ b/src/futures/digital.rs @@ -21,7 +21,7 @@ //! use core::time::Duration; //! //! //! Wait until the `ready_pin` is high or timeout after 1 millisecond. -//! //! Returns true is the pin became high or false if it timed-out. +//! //! 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, From 1df5604e2b4df82e28b63db92c1e49d759830762 Mon Sep 17 00:00:00 2001 From: Lachlan Sneff Date: Tue, 6 Jul 2021 16:38:54 -0400 Subject: [PATCH 30/31] Add error associated types to futures::digital traits --- src/futures/digital.rs | 35 +++++++++++++++++++++++++---------- 1 file changed, 25 insertions(+), 10 deletions(-) diff --git a/src/futures/digital.rs b/src/futures/digital.rs index f5dd1e7ca..8cf4d1615 100644 --- a/src/futures/digital.rs +++ b/src/futures/digital.rs @@ -41,8 +41,11 @@ 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 HighFuture<'a>: Future + 'a + type Fut<'a>: Future> + 'a where Self: 'a; @@ -52,13 +55,16 @@ pub trait WaitForHigh { /// # 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::HighFuture<'a>; + fn wait_for_high<'a>(&'a mut self) -> Self::Fut<'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 LowFuture<'a>: Future + 'a + type Fut<'a>: Future> + 'a where Self: 'a; @@ -68,39 +74,48 @@ pub trait WaitForLow { /// # 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::LowFuture<'a>; + fn wait_for_low<'a>(&'a mut self) -> Self::Fut<'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 RisingFuture<'a>: Future + 'a + type Fut<'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::RisingFuture<'a>; + fn wait_for_rising_edge<'a>(&'a mut self) -> Self::Fut<'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 FallingFuture<'a>: Future + 'a + type Fut<'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::FallingFuture<'a>; + fn wait_for_falling_edge<'a>(&'a mut self) -> Self::Fut<'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 EdgeFuture<'a>: Future + 'a + type Fut<'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::EdgeFuture<'a>; + fn wait_for_any_edge<'a>(&'a mut self) -> Self::Fut<'a>; } From 38ba051a8c36d9c826443ac4044b3dd1c2115cab Mon Sep 17 00:00:00 2001 From: Lachlan Sneff Date: Mon, 22 Nov 2021 17:52:34 -0500 Subject: [PATCH 31/31] Expand names of generic associated future types --- src/futures/digital.rs | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/futures/digital.rs b/src/futures/digital.rs index 8cf4d1615..b703f5e6d 100644 --- a/src/futures/digital.rs +++ b/src/futures/digital.rs @@ -45,7 +45,7 @@ pub trait WaitForHigh { type Error; /// The future returned by the `wait_for_high` function. - type Fut<'a>: Future> + 'a + type WaitForHighFuture<'a>: Future> + 'a where Self: 'a; @@ -55,7 +55,7 @@ pub trait WaitForHigh { /// # 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::Fut<'a>; + fn wait_for_high<'a>(&'a mut self) -> Self::WaitForHighFuture<'a>; } /// Asynchronously wait for a pin to be low. @@ -64,7 +64,7 @@ pub trait WaitForLow { type Error; /// The future returned by `wait_for_low`. - type Fut<'a>: Future> + 'a + type WaitForLowFuture<'a>: Future> + 'a where Self: 'a; @@ -74,7 +74,7 @@ pub trait WaitForLow { /// # 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::Fut<'a>; + fn wait_for_low<'a>(&'a mut self) -> Self::WaitForLowFuture<'a>; } /// Wait for a rising edge (transition from low to high). @@ -83,12 +83,12 @@ pub trait WaitForRisingEdge { type Error; /// The future returned from `wait_for_rising_edge`. - type Fut<'a>: Future> + 'a + 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::Fut<'a>; + fn wait_for_rising_edge<'a>(&'a mut self) -> Self::WaitForRisingEdgeFuture<'a>; } /// Wait for a falling edge (transition from high to low). @@ -97,12 +97,12 @@ pub trait WaitForFallingEdge { type Error; /// The future returned from `wait_for_falling_edge`. - type Fut<'a>: Future> + 'a + 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::Fut<'a>; + 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). @@ -111,11 +111,11 @@ pub trait WaitForAnyEdge { type Error; /// The future returned from `wait_for_any_edge`. - type Fut<'a>: Future> + 'a + 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::Fut<'a>; + fn wait_for_any_edge<'a>(&'a mut self) -> Self::WaitForAnyEdgeFuture<'a>; }