-
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 4 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,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<A: AddressMode = SevenBitAddress> { | ||
/// Error type | ||
type Error; | ||
/// The future associated with the `read` method. | ||
type ReadFuture<'a>: Future<Output = Result<(), Self::Error>> + 'a | ||
where | ||
Self: 'a; | ||
|
||
/// Reads enough bytes from slave with `address` to fill `buffer` | ||
/// | ||
/// # I2C Events (contract) | ||
/// | ||
/// ``` text | ||
/// Master: ST SAD+R MAK MAK ... NMAK SP | ||
/// Slave: SAK B0 B1 ... BN | ||
/// ``` | ||
/// | ||
/// Where | ||
/// | ||
/// - `ST` = start condition | ||
/// - `SAD+R` = slave address followed by bit 1 to indicate reading | ||
/// - `SAK` = slave acknowledge | ||
/// - `Bi` = ith byte of data | ||
/// - `MAK` = master acknowledge | ||
/// - `NMAK` = master no acknowledge | ||
/// - `SP` = stop condition | ||
fn read<'a>(&'a mut self, address: A, buffer: &'a mut [u8]) -> Self::ReadFuture<'a>; | ||
} | ||
|
||
/// Async write | ||
pub trait Write<A: AddressMode = SevenBitAddress> { | ||
/// Error type | ||
type Error; | ||
/// The future associated with the `write` method. | ||
type WriteFuture<'a>: Future<Output = Result<(), Self::Error>> + 'a | ||
where | ||
Self: 'a; | ||
|
||
/// Writes bytes to slave with address `address` | ||
/// | ||
/// # I2C Events (contract) | ||
/// | ||
/// ``` text | ||
/// Master: ST SAD+W B0 B1 ... BN SP | ||
/// Slave: SAK SAK SAK ... SAK | ||
/// ``` | ||
/// | ||
/// Where | ||
/// | ||
/// - `ST` = start condition | ||
/// - `SAD+W` = slave address followed by bit 0 to indicate writing | ||
/// - `SAK` = slave acknowledge | ||
/// - `Bi` = ith byte of data | ||
/// - `SP` = stop condition | ||
fn write<'a>(&'a mut self, address: A, bytes: &'a [u8]) -> Self::WriteFuture<'a>; | ||
} | ||
|
||
/// Async write + read | ||
pub trait WriteRead<A: AddressMode = SevenBitAddress> { | ||
/// Error type | ||
type Error; | ||
/// The future associated with the `write_read` method. | ||
type WriteReadFuture<'a>: Future<Output = Result<(), Self::Error>> + '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<A: AddressMode = SevenBitAddress> { | ||
/// Error type | ||
type Error; | ||
/// The future associated with the `exec` method. | ||
type ExecFuture<'a>: Future<Output = Result<(), Self::Error>> + '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<A: AddressMode>: Read<A> + Write<A> {} | ||
|
||
impl<A, E, S> Transactional<A> for S | ||
where | ||
A: AddressMode + Copy + 'static, | ||
S: Default<A> + Read<A, Error = E> + Write<A, Error = E>, | ||
E: 'static, | ||
{ | ||
type Error = E; | ||
|
||
type ExecFuture<'a> where Self: 'a = impl Future<Output = Result<(), Self::Error>> + '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(()) | ||
} | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
//! Asynchronous APIs | ||
//! | ||
//! This traits use `core::future::Future` and generic associated types. | ||
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 i2c; | ||
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, 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<Output=Result<&'a [u8], Self::Error>> + 'a | ||
where | ||
Self: 'a; | ||
|
||
/// Get a number of bytes from the RNG. | ||
fn read<'a>(&'a mut self, buf: &'a mut [MaybeUninit<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.