Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

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

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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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 = []
lachlansneff marked this conversation as resolved.
Show resolved Hide resolved

[dependencies]
nb = "1"

Expand Down
99 changes: 99 additions & 0 deletions src/futures/adc.rs
Original file line number Diff line number Diff line change
@@ -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<MODE>(PhantomData<MODE>);
/// 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<Adc1> for Gpio1Pin1<Analog> {
/// 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<MODE>(PhantomData<MODE>);
/// 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<Adc2> for Gpio2PinA<AltFun> {
/// 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<ADC> {
/// 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<WORD, PIN> OneShot<MyAdc, WORD, PIN> for MyAdc
/// where
/// WORD: From<u16>,
/// PIN: Channel<MyAdc, ID=u8>,
/// {
/// type Error = ();
///
/// fn read(&mut self, pin: &mut PIN) -> nb::Result<WORD, Self::Error> {
/// let chan = 1 << pin.channel();
/// self.power_up();
/// let result = self.do_conversion(chan);
/// self.power_down();
/// Ok(result.into())
/// }
/// }
/// ```
pub trait OneShot<ADC, Word, Pin: Channel<ADC>> {
/// Error type returned by ADC methods
type Error;

/// The future associated with the `read` method.
type ReadFuture<'a>: Future<Output = Result<Word, Self::Error>> + '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>;
}
101 changes: 101 additions & 0 deletions src/futures/capture.rs
Original file line number Diff line number Diff line change
@@ -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<u16, Self::Error> { 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<MilliSeconds, Self::Error> { unimplemented!() }
/// # fn set_resolution<T>(&mut self, _: T) -> Result<(), Self::Error> where T: Into<MilliSeconds> { 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<Output = Result<Self::Capture, Self::Error>> + '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<Self::Time, Self::Error>;

/// Sets the resolution of the capture timer
fn set_resolution<R>(&mut self, resolution: R) -> Result<(), Self::Error>
where
R: Into<Self::Time>;
}
10 changes: 10 additions & 0 deletions src/futures/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
//! Asynchronous APIs
//!
//! This traits use `core::future::Future` and generic associated types.
Copy link
Member

Choose a reason for hiding this comment

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

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

Copy link
Contributor Author

Choose a reason for hiding this comment

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

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

Copy link
Member

Choose a reason for hiding this comment

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

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


pub mod adc;
pub mod capture;
pub mod rng;
pub mod serial;
pub mod spi;
pub mod timer;
19 changes: 19 additions & 0 deletions src/futures/rng.rs
Original file line number Diff line number Diff line change
@@ -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<Output=Result<usize, Self::Error>> + '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>;
}
42 changes: 42 additions & 0 deletions src/futures/serial.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
//! Serial interface

use core::future::Future;

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

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

/// Reads a single word from the serial interface
fn read<'a>(&'a mut self) -> Self::ReadFuture<'a>;
lachlansneff marked this conversation as resolved.
Show resolved Hide resolved
}

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

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

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

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

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