-
Notifications
You must be signed in to change notification settings - Fork 235
Add asynchronous versions of most embedded-hal traits using GATs #285
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
Changes from 3 commits
e736fb5
ff085fa
7eb5fd1
3a30a33
d9d08fa
1928f77
5cdd987
eecf646
6298952
bac1603
92aa33b
582ebad
a61008b
306b187
92e0da4
7ebd9c5
b79bf16
4d02ea1
41af589
48ab3a8
85accbd
eb2ff8a
d9174d2
941c8bb
5617082
c002a23
e686e6e
2e16b79
55cbca1
fee1b99
1df5604
38ba051
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
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>; | ||
} |
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>; | ||
} |
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. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The whole There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. There was a problem hiding this comment. Choose a reason for hiding this commentThe 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; |
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>; | ||
} |
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>; | ||
} |
Uh oh!
There was an error while loading. Please reload this page.