Skip to content

Async #334

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

Closed
wants to merge 8 commits into from
Closed

Async #334

Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
**/*.rs.bk
.#*
/target/
target
Cargo.lock
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,12 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
### Added
- `Error` traits for Can, SPI, I2C and Serial are implemented for Infallible

### Changed
- `spi`: traits now enforce all impls on the same struct (eg `Transfer` and `Write`) have the same `Error` type.
- `spi/blocking`: unified traits into `Read`, `Write`, `ReadWrite`.
- `spi/blocking`: renamed Transactional `exec` to `batch`.
- `spi/blocking`: Added `read_batch`, `write_batch` methods.

## [v1.0.0-alpha.6] - 2021-11-19

*** This is (also) an alpha release with breaking changes (sorry) ***
Expand Down
15 changes: 15 additions & 0 deletions embedded-hal-async/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
[package]
name = "embedded-hal-async"
version = "0.1.0"
edition = "2021"
categories = ["asynchronous", "embedded", "hardware-support", "no-std"]
description = " A Hardware Abstraction Layer (HAL) for embedded systems, async version"
documentation = "https://docs.rs/embedded-hal-async"
keywords = ["hal", "IO", "async"]
license = "MIT OR Apache-2.0"
readme = "README.md"
repository = "https://github.com/rust-embedded/embedded-hal"

[dependencies]
nb = "1"
embedded-hal = { version = "1.0.0-alpha.6", path = ".." }
52 changes: 52 additions & 0 deletions embedded-hal-async/src/delay.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
//! Delays

use core::future::Future;

/// Microsecond delay
pub trait DelayUs {
/// Enumeration of errors
type Error: core::fmt::Debug;

/// The future returned by the `delay_us` function.
type DelayUsFuture<'a>: Future<Output = Result<(), Self::Error>> + 'a
where
Self: 'a;

/// Pauses execution for at minimum `us` microseconds. Pause can be longer
/// if the implementation requires it due to precision/timing issues.
fn delay_us(&mut self, us: u32) -> Self::DelayUsFuture<'_>;

/// The future returned by the `delay_ms` function.
type DelayMsFuture<'a>: Future<Output = Result<(), Self::Error>> + 'a
where
Self: 'a;

/// Pauses execution for at minimum `ms` milliseconds. Pause can be longer
/// if the implementation requires it due to precision/timing issues.
fn delay_ms(&mut self, ms: u32) -> Self::DelayMsFuture<'_>;
}

impl<T> DelayUs for &mut T
where
T: DelayUs,
{
type Error = T::Error;

type DelayUsFuture<'a>
where
Self: 'a,
= T::DelayUsFuture<'a>;

fn delay_us(&mut self, us: u32) -> Self::DelayUsFuture<'_> {
T::delay_us(self, us)
}

type DelayMsFuture<'a>
where
Self: 'a,
= T::DelayMsFuture<'a>;

fn delay_ms(&mut self, ms: u32) -> Self::DelayMsFuture<'_> {
T::delay_ms(self, ms)
}
}
100 changes: 100 additions & 0 deletions embedded-hal-async/src/digital.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
//! Asynchronous digital I/O
//!
//! # Example
//!
//! ```rust
//! # use embedded_hal_async::digital::WaitForHigh;
//! /// Asynchronously wait until the `ready_pin` becomes high.
//! async fn wait_until_ready<P>(ready_pin: &mut P)
//! where
//! P: WaitForHigh,
//! {
//! ready_pin
//! .wait_for_high()
//! .await
//! .expect("failed to await input pin")
//! }
//! ```

use core::future::Future;

/// Asynchronously wait for a pin to be high.
pub trait WaitForHigh {
/// Enumeration of errors.
type Error: core::fmt::Debug;

/// The future returned by the `wait_for_high` function.
type WaitForHighFuture<'a>: Future<Output = Result<(), Self::Error>> + 'a
where
Self: 'a;

/// Returns a future that resolves when this pin _is_ high. If the pin
/// is already high, the future resolves immediately.
///
/// # Note for implementers
/// The pin may have switched back to low before the task was run after
/// being woken. The future should still resolve in that case.
fn wait_for_high<'a>(&'a mut self) -> Self::WaitForHighFuture<'a>;
}

/// Asynchronously wait for a pin to be low.
pub trait WaitForLow {
/// Enumeration of errors.
type Error: core::fmt::Debug;

/// The future returned by `wait_for_low`.
type WaitForLowFuture<'a>: Future<Output = Result<(), Self::Error>> + 'a
where
Self: 'a;

/// Returns a future that resolves when this pin _is_ low. If the pin
/// is already low, the future resolves immediately.
///
/// # Note for implementers
/// The pin may have switched back to high before the task was run after
/// being woken. The future should still resolve in that case.
fn wait_for_low<'a>(&'a mut self) -> Self::WaitForLowFuture<'a>;
}

/// Wait for a rising edge (transition from low to high).
pub trait WaitForRisingEdge {
/// Enumeration of errors.
type Error: core::fmt::Debug;

/// The future returned from `wait_for_rising_edge`.
type WaitForRisingEdgeFuture<'a>: Future<Output = Result<(), Self::Error>> + 'a
where
Self: 'a;

/// Returns a future that resolves when this pin transitions from low to high.
fn wait_for_rising_edge<'a>(&'a mut self) -> Self::WaitForRisingEdgeFuture<'a>;
}

/// Wait for a falling edge (transition from high to low).
pub trait WaitForFallingEdge {
/// Enumeration of errors.
type Error: core::fmt::Debug;

/// The future returned from `wait_for_falling_edge`.
type WaitForFallingEdgeFuture<'a>: Future<Output = Result<(), Self::Error>> + 'a
where
Self: 'a;

/// Returns a future that resolves when this pin transitions from high to low.
fn wait_for_falling_edge<'a>(&'a mut self) -> Self::WaitForFallingEdgeFuture<'a>;
}

/// Wait for any edge (transition from low to high OR high to low).
pub trait WaitForAnyEdge {
/// Enumeration of errors.
type Error: core::fmt::Debug;

/// The future returned from `wait_for_any_edge`.
type WaitForAnyEdgeFuture<'a>: Future<Output = Result<(), Self::Error>> + '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>;
}
163 changes: 163 additions & 0 deletions embedded-hal-async/src/i2c.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
//! 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 embedded_hal::i2c::{
AddressMode, Error, ErrorKind, NoAcknowledgeSource, SevenBitAddress, TenBitAddress,
};

/// Async read
pub trait Read<A: AddressMode = SevenBitAddress> {
/// Error type
type Error: 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, read: &'a mut [u8]) -> Self::ReadFuture<'a>;
}

impl<A: AddressMode, T: Read<A>> Read<A> for &mut T {
type Error = T::Error;

type ReadFuture<'a>
where
Self: 'a,
= T::ReadFuture<'a>;

fn read<'a>(&'a mut self, address: A, buffer: &'a mut [u8]) -> Self::ReadFuture<'a> {
T::read(self, address, buffer)
}
}

/// Async write
pub trait Write<A: AddressMode = SevenBitAddress> {
/// Error type
type Error: 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, write: &'a [u8]) -> Self::WriteFuture<'a>;
}

impl<A: AddressMode, T: Write<A>> Write<A> for &mut T {
type Error = T::Error;

type WriteFuture<'a>
where
Self: 'a,
= T::WriteFuture<'a>;

fn write<'a>(&'a mut self, address: A, bytes: &'a [u8]) -> Self::WriteFuture<'a> {
T::write(self, address, bytes)
}
}

/// Async write + read
pub trait WriteRead<A: AddressMode = SevenBitAddress> {
/// Error type
type Error: 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 `read` *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,
write: &'a [u8],
read: &'a mut [u8],
) -> Self::WriteReadFuture<'a>;
}

impl<A: AddressMode, T: WriteRead<A>> WriteRead<A> for &mut T {
type Error = T::Error;

type WriteReadFuture<'a>
where
Self: 'a,
= T::WriteReadFuture<'a>;

fn write_read<'a>(
&'a mut self,
address: A,
bytes: &'a [u8],
buffer: &'a mut [u8],
) -> Self::WriteReadFuture<'a> {
T::write_read(self, address, bytes, buffer)
}
}
13 changes: 13 additions & 0 deletions embedded-hal-async/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#![feature(generic_associated_types)]
#![no_std]
#![deny(missing_docs)]

//! Asynchronous APIs
//!
//! This traits use `core::future::Future` and generic associated types.

pub mod delay;
pub mod digital;
pub mod i2c;
pub mod serial;
pub mod spi;
Loading