From 1d7998715e0f66b4164cced2f98e83d8c55fca3b Mon Sep 17 00:00:00 2001 From: Oliver Rockstedt Date: Tue, 13 Dec 2022 21:17:26 +0100 Subject: [PATCH 001/199] Implement From for bool --- embedded-hal/src/digital.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/embedded-hal/src/digital.rs b/embedded-hal/src/digital.rs index c1fb82fe6..c0fcd5754 100644 --- a/embedded-hal/src/digital.rs +++ b/embedded-hal/src/digital.rs @@ -55,6 +55,15 @@ impl Not for PinState { } } +impl From for bool { + fn from(value: PinState) -> bool { + match value { + PinState::Low => false, + PinState::High => true, + } + } +} + /// Single digital push-pull output pin pub trait OutputPin: ErrorType { /// Drives the pin low From 83dfca59b36123c985b20db088c3358d6d0d490b Mon Sep 17 00:00:00 2001 From: Martin Date: Tue, 4 Oct 2022 12:28:39 +0200 Subject: [PATCH 002/199] Raise MSRV for embedded-hal-async to Rust 1.65.0 Gives a hint for what is needed for generic_associated_types to build without errors without specifing it as a feature. Nightly is still required. --- embedded-hal-async/Cargo.toml | 1 + embedded-hal-async/README.md | 8 +++----- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/embedded-hal-async/Cargo.toml b/embedded-hal-async/Cargo.toml index 7078beb6e..0999c2c6b 100644 --- a/embedded-hal-async/Cargo.toml +++ b/embedded-hal-async/Cargo.toml @@ -12,6 +12,7 @@ name = "embedded-hal-async" readme = "README.md" repository = "https://github.com/rust-embedded/embedded-hal" version = "0.2.0-alpha.0" +rust-version = "1.65.0" [dependencies] embedded-hal = { version = "=1.0.0-alpha.9", path = "../embedded-hal" } diff --git a/embedded-hal-async/README.md b/embedded-hal-async/README.md index 819c13488..c29672ebd 100644 --- a/embedded-hal-async/README.md +++ b/embedded-hal-async/README.md @@ -1,9 +1,6 @@ [![crates.io](https://img.shields.io/crates/d/embedded-hal-async.svg)](https://crates.io/crates/embedded-hal-async) [![crates.io](https://img.shields.io/crates/v/embedded-hal-async.svg)](https://crates.io/crates/embedded-hal-async) [![Documentation](https://docs.rs/embedded-hal-async/badge.svg)](https://docs.rs/embedded-hal-async) - # `embedded-hal-async` @@ -23,8 +20,9 @@ This project is developed and maintained by the [HAL team](https://github.com/ru ## License From f41a73a94e2e90a33f98b08659fe152e9f9884fc Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 20 Dec 2022 22:43:13 +0100 Subject: [PATCH 003/199] update required nightly version in EHA readme. --- embedded-hal-async/README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/embedded-hal-async/README.md b/embedded-hal-async/README.md index c29672ebd..0541aa808 100644 --- a/embedded-hal-async/README.md +++ b/embedded-hal-async/README.md @@ -17,13 +17,13 @@ This project is developed and maintained by the [HAL team](https://github.com/ru [API reference]: https://docs.rs/embedded-hal-async - +Keep in mind Rust nightlies can make backwards-incompatible changes to unstable features +at any time. ## License From bbd884ed1b1a51ca35e15d8cabb5a011bb74dcdd Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 20 Dec 2022 21:52:08 +0100 Subject: [PATCH 004/199] gpio: add ErrorKind. --- embedded-hal/CHANGELOG.md | 2 ++ embedded-hal/src/digital.rs | 50 +++++++++++++++++++++++++++++++++++-- 2 files changed, 50 insertions(+), 2 deletions(-) diff --git a/embedded-hal/CHANGELOG.md b/embedded-hal/CHANGELOG.md index 7682542d5..da55f6d37 100644 --- a/embedded-hal/CHANGELOG.md +++ b/embedded-hal/CHANGELOG.md @@ -7,6 +7,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] +### Changed +- gpio: add `ErrorKind` enum for consistency with other traits and for future extensibility. No kinds are defined for now. ## [v1.0.0-alpha.9] - 2022-09-28 diff --git a/embedded-hal/src/digital.rs b/embedded-hal/src/digital.rs index c1fb82fe6..2fcb75e62 100644 --- a/embedded-hal/src/digital.rs +++ b/embedded-hal/src/digital.rs @@ -2,17 +2,63 @@ use core::{convert::From, ops::Not}; -/// GPIO error type trait +/// Error +pub trait Error: core::fmt::Debug { + /// Convert error to a generic error kind + /// + /// By using this method, errors freely defined by HAL implementations + /// can be converted to a set of generic errors upon which generic + /// code can act. + fn kind(&self) -> ErrorKind; +} + +impl Error for core::convert::Infallible { + fn kind(&self) -> ErrorKind { + match *self {} + } +} + +/// Error kind +/// +/// This represents a common set of operation errors. HAL implementations are +/// free to define more specific or additional error types. However, by providing +/// a mapping to these common errors, generic code can still react to them. +#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] +#[non_exhaustive] +pub enum ErrorKind { + /// A different error occurred. The original error may contain more information. + Other, +} + +impl Error for ErrorKind { + fn kind(&self) -> ErrorKind { + *self + } +} + +impl core::fmt::Display for ErrorKind { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + match self { + Self::Other => write!( + f, + "A different error occurred. The original error may contain more information" + ), + } + } +} + +/// Error type trait /// /// This just defines the error type, to be used by the other traits. pub trait ErrorType { /// Error type - type Error: core::fmt::Debug; + type Error: Error; } impl ErrorType for &T { type Error = T::Error; } + impl ErrorType for &mut T { type Error = T::Error; } From 02aade12246cd8d154fa241eb61d08b8c3f60526 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 28 Feb 2023 20:56:18 +0100 Subject: [PATCH 005/199] Fix CI. --- embedded-hal-nb/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embedded-hal-nb/Cargo.toml b/embedded-hal-nb/Cargo.toml index 6e8f49c71..e79f6f1f6 100644 --- a/embedded-hal-nb/Cargo.toml +++ b/embedded-hal-nb/Cargo.toml @@ -16,5 +16,5 @@ embedded-hal = { version = "=1.0.0-alpha.9", path = "../embedded-hal" } nb = "1" [dev-dependencies] -cortex-m-rt = "=0.7.1" # 0.7.2 bumped its MSRV higher than embedded-hal's +cortex-m-rt = "0.7" stm32f1 = { version = "0.15", features = ["stm32f103", "rt"] } From d36ddc200921d7a7eb42518994c5bfce54c8d7d1 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 28 Feb 2023 21:01:00 +0100 Subject: [PATCH 006/199] Bump MSRV to 1.59, switch to Edition 2021. --- .github/bors.toml | 6 +++--- .github/workflows/test.yml | 2 +- README.md | 2 +- embedded-can/Cargo.toml | 2 +- embedded-can/README.md | 4 ++-- embedded-hal-async/Cargo.toml | 2 +- embedded-hal-bus/Cargo.toml | 2 +- embedded-hal-bus/README.md | 4 ++-- embedded-hal-nb/Cargo.toml | 2 +- embedded-hal-nb/README.md | 4 ++-- embedded-hal/CHANGELOG.md | 2 +- embedded-hal/Cargo.toml | 2 +- embedded-hal/README.md | 4 ++-- 13 files changed, 19 insertions(+), 19 deletions(-) diff --git a/.github/bors.toml b/.github/bors.toml index cad44688f..0f213ce07 100644 --- a/.github/bors.toml +++ b/.github/bors.toml @@ -5,9 +5,9 @@ status = [ "test (stable, x86_64-unknown-linux-gnu)", "test (stable, thumbv6m-none-eabi)", "test (stable, thumbv7m-none-eabi)", - "test (1.54.0, x86_64-unknown-linux-gnu)", - "test (1.54.0, thumbv6m-none-eabi)", - "test (1.54.0, thumbv7m-none-eabi)", + "test (1.59.0, x86_64-unknown-linux-gnu)", + "test (1.59.0, thumbv6m-none-eabi)", + "test (1.59.0, thumbv7m-none-eabi)", "test (nightly, x86_64-unknown-linux-gnu)", "test (nightly, thumbv6m-none-eabi)", "test (nightly, thumbv7m-none-eabi)", diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 98650cdd6..d7ce3402d 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -16,7 +16,7 @@ jobs: # All generated code should be running on stable now rust: - stable - - 1.54.0 # MSRV + - 1.59.0 # MSRV - nightly # The default target we're compiling on and for diff --git a/README.md b/README.md index 46754d22c..6067b23b8 100644 --- a/README.md +++ b/README.md @@ -72,7 +72,7 @@ on crates.io. ## Minimum Supported Rust Version (MSRV) -This crate is guaranteed to compile on stable Rust 1.54 and up. It *might* +This crate is guaranteed to compile on stable Rust 1.59 and up. It *might* compile with older versions but that may change in any new patch release. See [here](docs/msrv.md) for details on how the MSRV may be upgraded. diff --git a/embedded-can/Cargo.toml b/embedded-can/Cargo.toml index 4ed2ede3c..22620d786 100644 --- a/embedded-can/Cargo.toml +++ b/embedded-can/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "embedded-can" version = "0.4.1" -edition = "2018" +edition = "2021" description = "HAL traits for Controller Area Network (CAN) devices." categories = ["embedded", "hardware-support", "no-std"] diff --git a/embedded-can/README.md b/embedded-can/README.md index 7c68b024c..3380c43f3 100644 --- a/embedded-can/README.md +++ b/embedded-can/README.md @@ -1,7 +1,7 @@ [![crates.io](https://img.shields.io/crates/d/embedded-can.svg)](https://crates.io/crates/embedded-can) [![crates.io](https://img.shields.io/crates/v/embedded-can.svg)](https://crates.io/crates/embedded-can) [![Documentation](https://docs.rs/embedded-can/badge.svg)](https://docs.rs/embedded-can) -![Minimum Supported Rust Version](https://img.shields.io/badge/rustc-1.54+-blue.svg) +![Minimum Supported Rust Version](https://img.shields.io/badge/rustc-1.59+-blue.svg) # `embedded-can` @@ -15,7 +15,7 @@ This project is developed and maintained by the [HAL team](https://github.com/ru ## Minimum Supported Rust Version (MSRV) -This crate is guaranteed to compile on stable Rust 1.54 and up. It *might* +This crate is guaranteed to compile on stable Rust 1.59 and up. It *might* compile with older versions but that may change in any new patch release. See [here](../docs/msrv.md) for details on how the MSRV may be upgraded. diff --git a/embedded-hal-async/Cargo.toml b/embedded-hal-async/Cargo.toml index 0999c2c6b..31b746e5b 100644 --- a/embedded-hal-async/Cargo.toml +++ b/embedded-hal-async/Cargo.toml @@ -5,7 +5,7 @@ authors = [ categories = ["asynchronous", "embedded", "hardware-support", "no-std"] description = "An asynchronous Hardware Abstraction Layer (HAL) for embedded systems" documentation = "https://docs.rs/embedded-hal-async" -edition = "2018" +edition = "2021" keywords = ["hal", "IO"] license = "MIT OR Apache-2.0" name = "embedded-hal-async" diff --git a/embedded-hal-bus/Cargo.toml b/embedded-hal-bus/Cargo.toml index b3f022fe9..54a9396e9 100644 --- a/embedded-hal-bus/Cargo.toml +++ b/embedded-hal-bus/Cargo.toml @@ -5,7 +5,7 @@ authors = [ categories = ["embedded", "hardware-support", "no-std"] description = "Bus/Device connection mechanisms for embedded-hal, a Hardware Abstraction Layer (HAL) for embedded systems" documentation = "https://docs.rs/embedded-hal-bus" -edition = "2018" +edition = "2021" keywords = ["hal", "IO"] license = "MIT OR Apache-2.0" name = "embedded-hal-bus" diff --git a/embedded-hal-bus/README.md b/embedded-hal-bus/README.md index b41570516..a94b03874 100644 --- a/embedded-hal-bus/README.md +++ b/embedded-hal-bus/README.md @@ -1,7 +1,7 @@ [![crates.io](https://img.shields.io/crates/d/embedded-hal-bus.svg)](https://crates.io/crates/embedded-hal-bus) [![crates.io](https://img.shields.io/crates/v/embedded-hal-bus.svg)](https://crates.io/crates/embedded-hal-bus) [![Documentation](https://docs.rs/embedded-hal-bus/badge.svg)](https://docs.rs/embedded-hal-bus) -![Minimum Supported Rust Version](https://img.shields.io/badge/rustc-1.54+-blue.svg) +![Minimum Supported Rust Version](https://img.shields.io/badge/rustc-1.59+-blue.svg) # `embedded-hal-bus` @@ -27,7 +27,7 @@ This project is developed and maintained by the [HAL team](https://github.com/ru ## Minimum Supported Rust Version (MSRV) -This crate is guaranteed to compile on stable Rust 1.54 and up. It *might* +This crate is guaranteed to compile on stable Rust 1.59 and up. It *might* compile with older versions but that may change in any new patch release. See [here](../docs/msrv.md) for details on how the MSRV may be upgraded. diff --git a/embedded-hal-nb/Cargo.toml b/embedded-hal-nb/Cargo.toml index e79f6f1f6..2c54b129d 100644 --- a/embedded-hal-nb/Cargo.toml +++ b/embedded-hal-nb/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "embedded-hal-nb" version = "1.0.0-alpha.1" -edition = "2018" +edition = "2021" categories = ["embedded", "hardware-support", "no-std"] description = "Non-blocking Hardware Abstraction Layer (HAL) for embedded systems using the `nb` crate." diff --git a/embedded-hal-nb/README.md b/embedded-hal-nb/README.md index b47a80dd8..f04b79475 100644 --- a/embedded-hal-nb/README.md +++ b/embedded-hal-nb/README.md @@ -1,7 +1,7 @@ [![crates.io](https://img.shields.io/crates/d/embedded-hal-nb.svg)](https://crates.io/crates/embedded-hal-nb) [![crates.io](https://img.shields.io/crates/v/embedded-hal-nb.svg)](https://crates.io/crates/embedded-hal-nb) [![Documentation](https://docs.rs/embedded-hal-nb/badge.svg)](https://docs.rs/embedded-hal-nb) -![Minimum Supported Rust Version](https://img.shields.io/badge/rustc-1.54+-blue.svg) +![Minimum Supported Rust Version](https://img.shields.io/badge/rustc-1.59+-blue.svg) # `embedded-hal-nb` @@ -17,7 +17,7 @@ This project is developed and maintained by the [HAL team](https://github.com/ru ## Minimum Supported Rust Version (MSRV) -This crate is guaranteed to compile on stable Rust 1.54 and up. It *might* +This crate is guaranteed to compile on stable Rust 1.59 and up. It *might* compile with older versions but that may change in any new patch release. See [here](../docs/msrv.md) for details on how the MSRV may be upgraded. diff --git a/embedded-hal/CHANGELOG.md b/embedded-hal/CHANGELOG.md index 7682542d5..24e4cdf82 100644 --- a/embedded-hal/CHANGELOG.md +++ b/embedded-hal/CHANGELOG.md @@ -40,7 +40,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). *** This is (also) an alpha release with breaking changes (sorry) *** ### Changed -- The Minimum Supported Rust Version (MSRV) is now 1.54.0 +- The Minimum Supported Rust Version (MSRV) is now 1.59.0 - `spi`: unify all traits into `SpiReadBus`, `SpiWriteBus` and `SpiBus` (read-write). - `spi`: Add `SpiDevice` trait to represent a single device in a (possibly shared) bus, with managed chip-select (CS) pin. - `spi`: Clarify that implementations are allowed to return before operations are finished, add `flush` to wait until finished. diff --git a/embedded-hal/Cargo.toml b/embedded-hal/Cargo.toml index 8f99da5db..ef7be8315 100644 --- a/embedded-hal/Cargo.toml +++ b/embedded-hal/Cargo.toml @@ -7,7 +7,7 @@ authors = [ categories = ["asynchronous", "embedded", "hardware-support", "no-std"] description = " A Hardware Abstraction Layer (HAL) for embedded systems " documentation = "https://docs.rs/embedded-hal" -edition = "2018" +edition = "2021" keywords = ["hal", "IO"] license = "MIT OR Apache-2.0" name = "embedded-hal" diff --git a/embedded-hal/README.md b/embedded-hal/README.md index 478b68608..902e64883 100644 --- a/embedded-hal/README.md +++ b/embedded-hal/README.md @@ -1,7 +1,7 @@ [![crates.io](https://img.shields.io/crates/d/embedded-hal.svg)](https://crates.io/crates/embedded-hal) [![crates.io](https://img.shields.io/crates/v/embedded-hal.svg)](https://crates.io/crates/embedded-hal) [![Documentation](https://docs.rs/embedded-hal/badge.svg)](https://docs.rs/embedded-hal) -![Minimum Supported Rust Version](https://img.shields.io/badge/rustc-1.54+-blue.svg) +![Minimum Supported Rust Version](https://img.shields.io/badge/rustc-1.59+-blue.svg) # `embedded-hal` @@ -15,7 +15,7 @@ This project is developed and maintained by the [HAL team](https://github.com/ru ## Minimum Supported Rust Version (MSRV) -This crate is guaranteed to compile on stable Rust 1.54 and up. It *might* +This crate is guaranteed to compile on stable Rust 1.59 and up. It *might* compile with older versions but that may change in any new patch release. See [here](../docs/msrv.md) for details on how the MSRV may be upgraded. From ed9e0c13f134abed81225ec24f15d9cca996378f Mon Sep 17 00:00:00 2001 From: Jan Niehusmann Date: Tue, 28 Feb 2023 20:16:53 +0000 Subject: [PATCH 007/199] Use github action dtolnay/rust-toolchain@master instead of actions-rs/toolchain@v1 actions-rs/toolchain@v1 uses deprecated features and has not been updated for years --- .github/workflows/clippy.yml | 4 +--- .github/workflows/rustfmt.yml | 4 +--- .github/workflows/test.yml | 4 +--- 3 files changed, 3 insertions(+), 9 deletions(-) diff --git a/.github/workflows/clippy.yml b/.github/workflows/clippy.yml index 19c7acbac..2dd5c88bc 100644 --- a/.github/workflows/clippy.yml +++ b/.github/workflows/clippy.yml @@ -9,12 +9,10 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - - uses: actions-rs/toolchain@v1 + - uses: dtolnay/rust-toolchain@master with: - profile: minimal # embedded-hal-async needs nightly. # Use a pinned version to avoid spontaneous breakages (new clippy lints are added often) toolchain: nightly-2022-11-22 - override: true components: clippy - run: cargo clippy -- --deny=warnings \ No newline at end of file diff --git a/.github/workflows/rustfmt.yml b/.github/workflows/rustfmt.yml index 7ffd30a99..ba2ea006f 100644 --- a/.github/workflows/rustfmt.yml +++ b/.github/workflows/rustfmt.yml @@ -10,10 +10,8 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - - uses: actions-rs/toolchain@v1 + - uses: dtolnay/rust-toolchain@master with: - profile: minimal toolchain: nightly - override: true components: rustfmt - run: cargo fmt --check diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index d7ce3402d..7b1242ea3 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -27,12 +27,10 @@ jobs: steps: - uses: actions/checkout@v2 - - uses: actions-rs/toolchain@v1 + - uses: dtolnay/rust-toolchain@master with: - profile: minimal toolchain: ${{ matrix.rust }} target: ${{ matrix.target }} - override: true - run: sed -i '/nightly-only/d' Cargo.toml if: matrix.rust != 'nightly' From 9e4d4794e91cf4aae5970470682bf4f24391ce9d Mon Sep 17 00:00:00 2001 From: Jan Niehusmann Date: Tue, 28 Feb 2023 20:23:37 +0000 Subject: [PATCH 008/199] Update github workflows to actions/checkout@v3 --- .github/workflows/clippy.yml | 2 +- .github/workflows/rustfmt.yml | 2 +- .github/workflows/test.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/clippy.yml b/.github/workflows/clippy.yml index 2dd5c88bc..fec8c2e0d 100644 --- a/.github/workflows/clippy.yml +++ b/.github/workflows/clippy.yml @@ -8,7 +8,7 @@ jobs: clippy: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - uses: dtolnay/rust-toolchain@master with: # embedded-hal-async needs nightly. diff --git a/.github/workflows/rustfmt.yml b/.github/workflows/rustfmt.yml index ba2ea006f..29049c24d 100644 --- a/.github/workflows/rustfmt.yml +++ b/.github/workflows/rustfmt.yml @@ -9,7 +9,7 @@ jobs: fmt: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - uses: dtolnay/rust-toolchain@master with: toolchain: nightly diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 7b1242ea3..4a12eeb6c 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -26,7 +26,7 @@ jobs: - thumbv7m-none-eabi steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - uses: dtolnay/rust-toolchain@master with: toolchain: ${{ matrix.rust }} From d3f8fe6b2f8b3cc380def25bf8be67a3d9700cb7 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 20 Dec 2022 21:58:10 +0100 Subject: [PATCH 009/199] delay: make infallible. --- embedded-hal-async/CHANGELOG.md | 3 +++ embedded-hal-async/src/delay.rs | 13 ++++--------- embedded-hal/CHANGELOG.md | 1 + embedded-hal/src/delay.rs | 17 +++++------------ 4 files changed, 13 insertions(+), 21 deletions(-) diff --git a/embedded-hal-async/CHANGELOG.md b/embedded-hal-async/CHANGELOG.md index dbed8a393..1d250d318 100644 --- a/embedded-hal-async/CHANGELOG.md +++ b/embedded-hal-async/CHANGELOG.md @@ -7,6 +7,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] +### Changed +- delay: make infallible. + ## [v0.2.0-alpha.0] - 2022-11-23 - Switch all traits to use [`async_fn_in_trait`](https://blog.rust-lang.org/inside-rust/2022/11/17/async-fn-in-trait-nightly.html) (AFIT). Requires `nightly-2022-11-22` or newer. diff --git a/embedded-hal-async/src/delay.rs b/embedded-hal-async/src/delay.rs index 9106dc4d7..4b0223930 100644 --- a/embedded-hal-async/src/delay.rs +++ b/embedded-hal-async/src/delay.rs @@ -2,29 +2,24 @@ /// Microsecond delay pub trait DelayUs { - /// Enumeration of errors - type Error: core::fmt::Debug; - /// Pauses execution for at minimum `us` microseconds. Pause can be longer /// if the implementation requires it due to precision/timing issues. - async fn delay_us(&mut self, us: u32) -> Result<(), Self::Error>; + async fn delay_us(&mut self, us: u32); /// Pauses execution for at minimum `ms` milliseconds. Pause can be longer /// if the implementation requires it due to precision/timing issues. - async fn delay_ms(&mut self, ms: u32) -> Result<(), Self::Error>; + async fn delay_ms(&mut self, ms: u32); } impl DelayUs for &mut T where T: DelayUs, { - type Error = T::Error; - - async fn delay_us(&mut self, us: u32) -> Result<(), Self::Error> { + async fn delay_us(&mut self, us: u32) { T::delay_us(self, us).await } - async fn delay_ms(&mut self, ms: u32) -> Result<(), Self::Error> { + async fn delay_ms(&mut self, ms: u32) { T::delay_ms(self, ms).await } } diff --git a/embedded-hal/CHANGELOG.md b/embedded-hal/CHANGELOG.md index 197c74c6e..3bba75f17 100644 --- a/embedded-hal/CHANGELOG.md +++ b/embedded-hal/CHANGELOG.md @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ### Changed - gpio: add `ErrorKind` enum for consistency with other traits and for future extensibility. No kinds are defined for now. +- delay: make infallible. ## [v1.0.0-alpha.9] - 2022-09-28 diff --git a/embedded-hal/src/delay.rs b/embedded-hal/src/delay.rs index 52c1fdd5a..7486f412f 100644 --- a/embedded-hal/src/delay.rs +++ b/embedded-hal/src/delay.rs @@ -3,21 +3,16 @@ /// Microsecond delay /// pub trait DelayUs { - /// Enumeration of `DelayUs` errors - type Error: core::fmt::Debug; - /// 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) -> Result<(), Self::Error>; + fn delay_us(&mut self, us: u32); /// 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) -> Result<(), Self::Error> { + fn delay_ms(&mut self, ms: u32) { for _ in 0..ms { - self.delay_us(1000)?; + self.delay_us(1000); } - - Ok(()) } } @@ -25,13 +20,11 @@ impl DelayUs for &mut T where T: DelayUs, { - type Error = T::Error; - - fn delay_us(&mut self, us: u32) -> Result<(), Self::Error> { + fn delay_us(&mut self, us: u32) { T::delay_us(self, us) } - fn delay_ms(&mut self, ms: u32) -> Result<(), Self::Error> { + fn delay_ms(&mut self, ms: u32) { T::delay_ms(self, ms) } } From e8199411d5246b6eca312f07bd7f860c17772d1f Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 20 Dec 2022 21:28:59 +0100 Subject: [PATCH 010/199] Add PWM SetDuty trait. --- embedded-hal/src/lib.rs | 1 + embedded-hal/src/pwm.rs | 77 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 78 insertions(+) create mode 100644 embedded-hal/src/pwm.rs diff --git a/embedded-hal/src/lib.rs b/embedded-hal/src/lib.rs index 8b65ded47..410305ac4 100644 --- a/embedded-hal/src/lib.rs +++ b/embedded-hal/src/lib.rs @@ -80,6 +80,7 @@ pub mod delay; pub mod digital; pub mod i2c; +pub mod pwm; pub mod serial; pub mod spi; diff --git a/embedded-hal/src/pwm.rs b/embedded-hal/src/pwm.rs new file mode 100644 index 000000000..d76127ecd --- /dev/null +++ b/embedded-hal/src/pwm.rs @@ -0,0 +1,77 @@ +//! Pulse Width Modulation (PWM) traits + +/// Error +pub trait Error: core::fmt::Debug { + /// Convert error to a generic error kind + /// + /// By using this method, errors freely defined by HAL implementations + /// can be converted to a set of generic errors upon which generic + /// code can act. + fn kind(&self) -> ErrorKind; +} + +impl Error for core::convert::Infallible { + fn kind(&self) -> ErrorKind { + match *self {} + } +} + +/// Error kind +/// +/// This represents a common set of operation errors. HAL implementations are +/// free to define more specific or additional error types. However, by providing +/// a mapping to these common errors, generic code can still react to them. +#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] +#[non_exhaustive] +pub enum ErrorKind { + /// A different error occurred. The original error may contain more information. + Other, +} + +impl Error for ErrorKind { + fn kind(&self) -> ErrorKind { + *self + } +} + +impl core::fmt::Display for ErrorKind { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + match self { + Self::Other => write!( + f, + "A different error occurred. The original error may contain more information" + ), + } + } +} + +/// Error type trait +/// +/// This just defines the error type, to be used by the other traits. +pub trait ErrorType { + /// Error type + type Error: Error; +} + +impl ErrorType for &mut T { + type Error = T::Error; +} + +/// Single PWM channel / pin +pub trait SetDuty: ErrorType { + /// Set the duty cycle. + /// + /// `duty` is the duty cycle. Valid values span the entire `u16` range: + /// + /// - `duty = 0` is considered 0% duty, which makes the pin permanently low. + /// - `duty = u16::MAX` is considered 100% duty, which makes the pin permanently high. + /// + /// Implementations must scale the duty value linearly to the range required by the hardware. + fn set_duty(&mut self, duty: u16) -> Self::Error; +} + +impl SetDuty for &mut T { + fn set_duty(&mut self, duty: u16) -> Self::Error { + T::set_duty(self, duty) + } +} From ff0b1ced6462c59d26b6b2d35bfc04ed9b34d3b9 Mon Sep 17 00:00:00 2001 From: Grant Miller Date: Sat, 21 Jan 2023 15:30:52 -0600 Subject: [PATCH 011/199] Add `get_max_duty()` and convenience methods --- embedded-hal/src/pwm.rs | 57 ++++++++++++++++++++++++++++++++++++----- 1 file changed, 50 insertions(+), 7 deletions(-) diff --git a/embedded-hal/src/pwm.rs b/embedded-hal/src/pwm.rs index d76127ecd..ce289cac9 100644 --- a/embedded-hal/src/pwm.rs +++ b/embedded-hal/src/pwm.rs @@ -59,19 +59,62 @@ impl ErrorType for &mut T { /// Single PWM channel / pin pub trait SetDuty: ErrorType { - /// Set the duty cycle. + /// Get the maximum duty cycle value. /// - /// `duty` is the duty cycle. Valid values span the entire `u16` range: + /// This value corresponds to a 100% duty cycle. + fn get_max_duty(&self) -> u16; + + /// Set the duty cycle to `duty / max_duty`. + /// + /// The caller is responsible for ensuring that the duty cycle value is less than or equal to the maximum duty cycle value, + /// as reported by `get_max_duty`. + fn set_duty(&mut self, duty: u16) -> Result<(), Self::Error>; + + /// Set the duty cycle to 0%, or always inactive. + fn set_off(&mut self) -> Result<(), Self::Error> { + self.set_duty(0) + } + + /// Set the duty cycle to 100%, or always active. + fn set_on(&mut self) -> Result<(), Self::Error> { + self.set_duty(self.get_max_duty()) + } + + /// Set the duty cycle to `num / denom`. /// - /// - `duty = 0` is considered 0% duty, which makes the pin permanently low. - /// - `duty = u16::MAX` is considered 100% duty, which makes the pin permanently high. + /// The caller is responsible for ensuring that `num` is less than or equal to `denom`, + /// and that `denom` is not zero. + fn set_fraction(&mut self, num: u16, denom: u16) -> Result<(), Self::Error> { + let duty = num as u32 * self.get_max_duty() as u32 / denom as u32; + self.set_duty(duty as u16) + } + + /// Set the duty cycle to `percent / 100` /// - /// Implementations must scale the duty value linearly to the range required by the hardware. - fn set_duty(&mut self, duty: u16) -> Self::Error; + /// The caller is responsible for ensuring that `percent` is less than or equal to 100. + fn set_percent(&mut self, percent: u8) -> Result<(), Self::Error> { + self.set_fraction(percent as u16, 100) + } } impl SetDuty for &mut T { - fn set_duty(&mut self, duty: u16) -> Self::Error { + fn get_max_duty(&self) -> u16 { + T::get_max_duty(self) + } + fn set_duty(&mut self, duty: u16) -> Result<(), Self::Error> { T::set_duty(self, duty) } + + fn set_off(&mut self) -> Result<(), Self::Error> { + T::set_off(self) + } + fn set_on(&mut self) -> Result<(), Self::Error> { + T::set_on(self) + } + fn set_fraction(&mut self, num: u16, denom: u16) -> Result<(), Self::Error> { + T::set_fraction(self, num, denom) + } + fn set_percent(&mut self, percent: u8) -> Result<(), Self::Error> { + T::set_percent(self, percent) + } } From 3f53ffc9dda88a16778d88e200b641483ad06597 Mon Sep 17 00:00:00 2001 From: Grant Miller Date: Sat, 21 Jan 2023 15:37:19 -0600 Subject: [PATCH 012/199] Add `#[inline]` to provided methods --- embedded-hal/src/pwm.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/embedded-hal/src/pwm.rs b/embedded-hal/src/pwm.rs index ce289cac9..39ee88ac9 100644 --- a/embedded-hal/src/pwm.rs +++ b/embedded-hal/src/pwm.rs @@ -71,11 +71,13 @@ pub trait SetDuty: ErrorType { fn set_duty(&mut self, duty: u16) -> Result<(), Self::Error>; /// Set the duty cycle to 0%, or always inactive. + #[inline] fn set_off(&mut self) -> Result<(), Self::Error> { self.set_duty(0) } /// Set the duty cycle to 100%, or always active. + #[inline] fn set_on(&mut self) -> Result<(), Self::Error> { self.set_duty(self.get_max_duty()) } @@ -84,6 +86,7 @@ pub trait SetDuty: ErrorType { /// /// The caller is responsible for ensuring that `num` is less than or equal to `denom`, /// and that `denom` is not zero. + #[inline] fn set_fraction(&mut self, num: u16, denom: u16) -> Result<(), Self::Error> { let duty = num as u32 * self.get_max_duty() as u32 / denom as u32; self.set_duty(duty as u16) @@ -92,6 +95,7 @@ pub trait SetDuty: ErrorType { /// Set the duty cycle to `percent / 100` /// /// The caller is responsible for ensuring that `percent` is less than or equal to 100. + #[inline] fn set_percent(&mut self, percent: u8) -> Result<(), Self::Error> { self.set_fraction(percent as u16, 100) } From d5f9747a060ca4109d215f512aa80a89ccb90df0 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 28 Feb 2023 20:40:33 +0100 Subject: [PATCH 013/199] pwm: Rename "duty" to "duty cycle". --- embedded-hal/src/pwm.rs | 54 ++++++++++++++++++++++------------------- 1 file changed, 29 insertions(+), 25 deletions(-) diff --git a/embedded-hal/src/pwm.rs b/embedded-hal/src/pwm.rs index 39ee88ac9..1ba9c4df0 100644 --- a/embedded-hal/src/pwm.rs +++ b/embedded-hal/src/pwm.rs @@ -58,28 +58,28 @@ impl ErrorType for &mut T { } /// Single PWM channel / pin -pub trait SetDuty: ErrorType { +pub trait SetDutyCycle: ErrorType { /// Get the maximum duty cycle value. /// /// This value corresponds to a 100% duty cycle. - fn get_max_duty(&self) -> u16; + fn get_max_duty_cycle(&self) -> u16; /// Set the duty cycle to `duty / max_duty`. /// /// The caller is responsible for ensuring that the duty cycle value is less than or equal to the maximum duty cycle value, /// as reported by `get_max_duty`. - fn set_duty(&mut self, duty: u16) -> Result<(), Self::Error>; + fn set_duty_cycle(&mut self, duty: u16) -> Result<(), Self::Error>; /// Set the duty cycle to 0%, or always inactive. #[inline] - fn set_off(&mut self) -> Result<(), Self::Error> { - self.set_duty(0) + fn set_duty_cycle_fully_off(&mut self) -> Result<(), Self::Error> { + self.set_duty_cycle(0) } /// Set the duty cycle to 100%, or always active. #[inline] - fn set_on(&mut self) -> Result<(), Self::Error> { - self.set_duty(self.get_max_duty()) + fn set_duty_cycle_fully_on(&mut self) -> Result<(), Self::Error> { + self.set_duty_cycle(self.get_max_duty_cycle()) } /// Set the duty cycle to `num / denom`. @@ -87,38 +87,42 @@ pub trait SetDuty: ErrorType { /// The caller is responsible for ensuring that `num` is less than or equal to `denom`, /// and that `denom` is not zero. #[inline] - fn set_fraction(&mut self, num: u16, denom: u16) -> Result<(), Self::Error> { - let duty = num as u32 * self.get_max_duty() as u32 / denom as u32; - self.set_duty(duty as u16) + fn set_duty_cycle_fraction(&mut self, num: u16, denom: u16) -> Result<(), Self::Error> { + let duty = num as u32 * self.get_max_duty_cycle() as u32 / denom as u32; + self.set_duty_cycle(duty as u16) } /// Set the duty cycle to `percent / 100` /// /// The caller is responsible for ensuring that `percent` is less than or equal to 100. #[inline] - fn set_percent(&mut self, percent: u8) -> Result<(), Self::Error> { - self.set_fraction(percent as u16, 100) + fn set_duty_cycle_percent(&mut self, percent: u8) -> Result<(), Self::Error> { + self.set_duty_cycle_fraction(percent as u16, 100) } } -impl SetDuty for &mut T { - fn get_max_duty(&self) -> u16 { - T::get_max_duty(self) +impl SetDutyCycle for &mut T { + fn get_max_duty_cycle(&self) -> u16 { + T::get_max_duty_cycle(self) } - fn set_duty(&mut self, duty: u16) -> Result<(), Self::Error> { - T::set_duty(self, duty) + + fn set_duty_cycle(&mut self, duty: u16) -> Result<(), Self::Error> { + T::set_duty_cycle(self, duty) } - fn set_off(&mut self) -> Result<(), Self::Error> { - T::set_off(self) + fn set_duty_cycle_fully_off(&mut self) -> Result<(), Self::Error> { + T::set_duty_cycle_fully_off(self) } - fn set_on(&mut self) -> Result<(), Self::Error> { - T::set_on(self) + + fn set_duty_cycle_fully_on(&mut self) -> Result<(), Self::Error> { + T::set_duty_cycle_fully_on(self) } - fn set_fraction(&mut self, num: u16, denom: u16) -> Result<(), Self::Error> { - T::set_fraction(self, num, denom) + + fn set_duty_cycle_fraction(&mut self, num: u16, denom: u16) -> Result<(), Self::Error> { + T::set_duty_cycle_fraction(self, num, denom) } - fn set_percent(&mut self, percent: u8) -> Result<(), Self::Error> { - T::set_percent(self, percent) + + fn set_duty_cycle_percent(&mut self, percent: u8) -> Result<(), Self::Error> { + T::set_duty_cycle_percent(self, percent) } } From 78a6f06322b8e649ce7762068b7f3236f1de3947 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 28 Feb 2023 20:40:48 +0100 Subject: [PATCH 014/199] Add changelog. --- embedded-hal/CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/embedded-hal/CHANGELOG.md b/embedded-hal/CHANGELOG.md index 3bba75f17..57f177103 100644 --- a/embedded-hal/CHANGELOG.md +++ b/embedded-hal/CHANGELOG.md @@ -7,6 +7,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] +### Added +- Added `pwm::SetDutyCycle` trait. + ### Changed - gpio: add `ErrorKind` enum for consistency with other traits and for future extensibility. No kinds are defined for now. - delay: make infallible. From 002786df3743052f1b84f0a0acaf4f5fd367b47e Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 8 Mar 2023 00:32:27 +0100 Subject: [PATCH 015/199] asyn/serial: add Write. --- embedded-hal-async/src/lib.rs | 1 + embedded-hal-async/src/serial.rs | 27 +++++++++++++++++++++++++++ embedded-hal/src/serial.rs | 4 +--- 3 files changed, 29 insertions(+), 3 deletions(-) create mode 100644 embedded-hal-async/src/serial.rs diff --git a/embedded-hal-async/src/lib.rs b/embedded-hal-async/src/lib.rs index 14748349c..cae21624a 100644 --- a/embedded-hal-async/src/lib.rs +++ b/embedded-hal-async/src/lib.rs @@ -15,4 +15,5 @@ pub mod delay; pub mod digital; pub mod i2c; +pub mod serial; pub mod spi; diff --git a/embedded-hal-async/src/serial.rs b/embedded-hal-async/src/serial.rs new file mode 100644 index 000000000..60ee1bc05 --- /dev/null +++ b/embedded-hal-async/src/serial.rs @@ -0,0 +1,27 @@ +//! Serial interface + +pub use embedded_hal::serial::{Error, ErrorKind, ErrorType}; + +/// Write half of a serial interface +pub trait Write: ErrorType { + /// Writes a slice, blocking until everything has been written. + /// + /// An implementation can choose to buffer the write, returning `Ok(())` + /// after the complete slice has been written to a buffer, but before all + /// words have been sent via the serial interface. To make sure that + /// everything has been sent, call [`flush`](Write::flush) after this function returns. + async fn write(&mut self, words: &[Word]) -> Result<(), Self::Error>; + + /// Ensures that none of the previously written data is still buffered + async fn flush(&mut self) -> Result<(), Self::Error>; +} + +impl, Word: 'static + Copy> Write for &mut T { + async fn write(&mut self, words: &[Word]) -> Result<(), Self::Error> { + T::write(self, words).await + } + + async fn flush(&mut self) -> Result<(), Self::Error> { + T::flush(self).await + } +} diff --git a/embedded-hal/src/serial.rs b/embedded-hal/src/serial.rs index f0c05f98b..ab9be722b 100644 --- a/embedded-hal/src/serial.rs +++ b/embedded-hal/src/serial.rs @@ -80,9 +80,7 @@ pub trait Write: ErrorType { /// An implementation can choose to buffer the write, returning `Ok(())` /// after the complete slice has been written to a buffer, but before all /// words have been sent via the serial interface. To make sure that - /// everything has been sent, call [`flush`] after this function returns. - /// - /// [`flush`]: #tymethod.flush + /// everything has been sent, call [`flush`](Write::flush) after this function returns. fn write(&mut self, buffer: &[Word]) -> Result<(), Self::Error>; /// Block until the serial interface has sent all buffered words From 336eb5aaa6bae5e213db3f35832aaa95138edb25 Mon Sep 17 00:00:00 2001 From: Diego Barrios Romero Date: Wed, 15 Mar 2023 09:18:58 +0100 Subject: [PATCH 016/199] Update changelog after #442 --- embedded-hal-async/CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/embedded-hal-async/CHANGELOG.md b/embedded-hal-async/CHANGELOG.md index 1d250d318..40e7cebbc 100644 --- a/embedded-hal-async/CHANGELOG.md +++ b/embedded-hal-async/CHANGELOG.md @@ -7,6 +7,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] +### Added +- Added a `serial::Write` trait. + ### Changed - delay: make infallible. From f43ab6e0ef49f6aa35eae6518c4ce78bc98f3150 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 7 Mar 2023 22:57:14 +0100 Subject: [PATCH 017/199] i2c: remove iter methods. --- embedded-hal/src/i2c.rs | 91 ----------------------------------------- 1 file changed, 91 deletions(-) diff --git a/embedded-hal/src/i2c.rs b/embedded-hal/src/i2c.rs index 67097ab2f..54295a644 100644 --- a/embedded-hal/src/i2c.rs +++ b/embedded-hal/src/i2c.rs @@ -37,26 +37,14 @@ //! // ... //! # Ok(()) //! } -//! fn write_iter>(&mut self, addr: u8, bytes: B) -> Result<(), Self::Error> { -//! // ... -//! # Ok(()) -//! } //! fn write_read(&mut self, addr: u8, bytes: &[u8], buffer: &mut [u8]) -> Result<(), Self::Error> { //! // ... //! # Ok(()) //! } -//! fn write_iter_read>(&mut self, addr: u8, bytes: B, buffer: &mut [u8]) -> Result<(), Self::Error> { -//! // ... -//! # Ok(()) -//! } //! fn transaction<'a>(&mut self, address: u8, operations: &mut [Operation<'a>]) -> Result<(), Self::Error> { //! // ... //! # Ok(()) //! } -//! fn transaction_iter<'a, O: IntoIterator>>(&mut self, address: u8, operations: O) -> Result<(), Self::Error> { -//! // ... -//! # Ok(()) -//! } //! } //! //! impl I2c for I2c0 @@ -69,26 +57,14 @@ //! // ... //! # Ok(()) //! } -//! fn write_iter>(&mut self, addr: u16, bytes: B) -> Result<(), Self::Error> { -//! // ... -//! # Ok(()) -//! } //! fn write_read(&mut self, addr: u16, bytes: &[u8], buffer: &mut [u8]) -> Result<(), Self::Error> { //! // ... //! # Ok(()) //! } -//! fn write_iter_read>(&mut self, addr: u16, bytes: B, buffer: &mut [u8]) -> Result<(), Self::Error> { -//! // ... -//! # Ok(()) -//! } //! fn transaction<'a>(&mut self, address: u16, operations: &mut [Operation<'a>]) -> Result<(), Self::Error> { //! // ... //! # Ok(()) //! } -//! fn transaction_iter<'a, O: IntoIterator>>(&mut self, address: u16, operations: O) -> Result<(), Self::Error> { -//! // ... -//! # Ok(()) -//! } //! } //! ``` //! @@ -307,15 +283,6 @@ pub trait I2c: ErrorType { /// - `SP` = stop condition fn write(&mut self, address: A, bytes: &[u8]) -> Result<(), Self::Error>; - /// Writes bytes to slave with address `address` - /// - /// # I2C Events (contract) - /// - /// Same as the `write` method - fn write_iter(&mut self, address: A, bytes: B) -> Result<(), Self::Error> - where - B: IntoIterator; - /// Writes bytes to slave with address `address` and then reads enough bytes to fill `buffer` *in a /// single transaction* /// @@ -345,21 +312,6 @@ pub trait I2c: ErrorType { buffer: &mut [u8], ) -> Result<(), Self::Error>; - /// Writes bytes to slave with address `address` and then reads enough bytes to fill `buffer` *in a - /// single transaction* - /// - /// # I2C Events (contract) - /// - /// Same as the `write_read` method - fn write_iter_read( - &mut self, - address: A, - bytes: B, - buffer: &mut [u8], - ) -> Result<(), Self::Error> - where - B: IntoIterator; - /// Execute the provided operations on the I2C bus. /// /// Transaction contract: @@ -378,23 +330,6 @@ pub trait I2c: ErrorType { address: A, operations: &mut [Operation<'a>], ) -> Result<(), Self::Error>; - - /// Execute the provided operations on the I2C bus (iterator version). - /// - /// 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 transaction_iter<'a, O>(&mut self, address: A, operations: O) -> Result<(), Self::Error> - where - O: IntoIterator>; } impl> I2c for &mut T { @@ -406,13 +341,6 @@ impl> I2c for &mut T { T::write(self, address, bytes) } - fn write_iter(&mut self, address: A, bytes: B) -> Result<(), Self::Error> - where - B: IntoIterator, - { - T::write_iter(self, address, bytes) - } - fn write_read( &mut self, address: A, @@ -422,18 +350,6 @@ impl> I2c for &mut T { T::write_read(self, address, bytes, buffer) } - fn write_iter_read( - &mut self, - address: A, - bytes: B, - buffer: &mut [u8], - ) -> Result<(), Self::Error> - where - B: IntoIterator, - { - T::write_iter_read(self, address, bytes, buffer) - } - fn transaction<'a>( &mut self, address: A, @@ -441,11 +357,4 @@ impl> I2c for &mut T { ) -> Result<(), Self::Error> { T::transaction(self, address, operations) } - - fn transaction_iter<'a, O>(&mut self, address: A, operations: O) -> Result<(), Self::Error> - where - O: IntoIterator>, - { - T::transaction_iter(self, address, operations) - } } From 6236f2baf14fc08efc06fccbbf788d7ecc65f75c Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 8 Mar 2023 01:29:29 +0100 Subject: [PATCH 018/199] i2c: remove useless lifetimes. --- embedded-hal-async/src/i2c.rs | 36 +++++++++++++++++------------------ embedded-hal/src/i2c.rs | 12 ++++++------ 2 files changed, 24 insertions(+), 24 deletions(-) diff --git a/embedded-hal-async/src/i2c.rs b/embedded-hal-async/src/i2c.rs index 4f84beb0d..0e505218c 100644 --- a/embedded-hal-async/src/i2c.rs +++ b/embedded-hal-async/src/i2c.rs @@ -41,7 +41,7 @@ pub trait I2c: ErrorType { /// - `MAK` = master acknowledge /// - `NMAK` = master no acknowledge /// - `SP` = stop condition - async fn read<'a>(&'a mut self, address: A, read: &'a mut [u8]) -> Result<(), Self::Error>; + async fn read(&mut self, address: A, read: &mut [u8]) -> Result<(), Self::Error>; /// Writes bytes to slave with address `address` /// @@ -59,7 +59,7 @@ pub trait I2c: ErrorType { /// - `SAK` = slave acknowledge /// - `Bi` = ith byte of data /// - `SP` = stop condition - async fn write<'a>(&'a mut self, address: A, write: &'a [u8]) -> Result<(), Self::Error>; + async fn write(&mut self, address: A, write: &[u8]) -> Result<(), Self::Error>; /// Writes bytes to slave with address `address` and then reads enough bytes to fill `read` *in a /// single transaction*. @@ -83,11 +83,11 @@ pub trait I2c: ErrorType { /// - `MAK` = master acknowledge /// - `NMAK` = master no acknowledge /// - `SP` = stop condition - async fn write_read<'a>( - &'a mut self, + async fn write_read( + &mut self, address: A, - write: &'a [u8], - read: &'a mut [u8], + write: &[u8], + read: &mut [u8], ) -> Result<(), Self::Error>; /// Execute the provided operations on the I2C bus as a single transaction. @@ -103,35 +103,35 @@ pub trait I2c: ErrorType { /// - `SAD+R/W` = slave address followed by bit 1 to indicate reading or 0 to indicate writing /// - `SR` = repeated start condition /// - `SP` = stop condition - async fn transaction<'a, 'b>( - &'a mut self, + async fn transaction( + &mut self, address: A, - operations: &'a mut [Operation<'b>], + operations: &mut [Operation<'_>], ) -> Result<(), Self::Error>; } impl> I2c for &mut T { - async fn read<'a>(&'a mut self, address: A, buffer: &'a mut [u8]) -> Result<(), Self::Error> { + async fn read(&mut self, address: A, buffer: &mut [u8]) -> Result<(), Self::Error> { T::read(self, address, buffer).await } - async fn write<'a>(&'a mut self, address: A, bytes: &'a [u8]) -> Result<(), Self::Error> { + async fn write(&mut self, address: A, bytes: &[u8]) -> Result<(), Self::Error> { T::write(self, address, bytes).await } - async fn write_read<'a>( - &'a mut self, + async fn write_read( + &mut self, address: A, - bytes: &'a [u8], - buffer: &'a mut [u8], + bytes: &[u8], + buffer: &mut [u8], ) -> Result<(), Self::Error> { T::write_read(self, address, bytes, buffer).await } - async fn transaction<'a, 'b>( - &'a mut self, + async fn transaction( + &mut self, address: A, - operations: &'a mut [Operation<'b>], + operations: &mut [Operation<'_>], ) -> Result<(), Self::Error> { T::transaction(self, address, operations).await } diff --git a/embedded-hal/src/i2c.rs b/embedded-hal/src/i2c.rs index 54295a644..2eccc2cfe 100644 --- a/embedded-hal/src/i2c.rs +++ b/embedded-hal/src/i2c.rs @@ -41,7 +41,7 @@ //! // ... //! # Ok(()) //! } -//! fn transaction<'a>(&mut self, address: u8, operations: &mut [Operation<'a>]) -> Result<(), Self::Error> { +//! fn transaction(&mut self, address: u8, operations: &mut [Operation<'_>]) -> Result<(), Self::Error> { //! // ... //! # Ok(()) //! } @@ -61,7 +61,7 @@ //! // ... //! # Ok(()) //! } -//! fn transaction<'a>(&mut self, address: u16, operations: &mut [Operation<'a>]) -> Result<(), Self::Error> { +//! fn transaction(&mut self, address: u16, operations: &mut [Operation<'_>]) -> Result<(), Self::Error> { //! // ... //! # Ok(()) //! } @@ -325,10 +325,10 @@ pub trait I2c: ErrorType { /// - `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 transaction<'a>( + fn transaction( &mut self, address: A, - operations: &mut [Operation<'a>], + operations: &mut [Operation<'_>], ) -> Result<(), Self::Error>; } @@ -350,10 +350,10 @@ impl> I2c for &mut T { T::write_read(self, address, bytes, buffer) } - fn transaction<'a>( + fn transaction( &mut self, address: A, - operations: &mut [Operation<'a>], + operations: &mut [Operation<'_>], ) -> Result<(), Self::Error> { T::transaction(self, address, operations) } From f644e8225f2af8c32875f8ac9a463aab45d0ab66 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 8 Mar 2023 01:31:18 +0100 Subject: [PATCH 019/199] i2c: rename args to write/read. --- embedded-hal-async/src/i2c.rs | 14 +++++------ embedded-hal/src/i2c.rs | 44 ++++++++++++++--------------------- 2 files changed, 24 insertions(+), 34 deletions(-) diff --git a/embedded-hal-async/src/i2c.rs b/embedded-hal-async/src/i2c.rs index 0e505218c..fdde6d582 100644 --- a/embedded-hal-async/src/i2c.rs +++ b/embedded-hal-async/src/i2c.rs @@ -111,21 +111,21 @@ pub trait I2c: ErrorType { } impl> I2c for &mut T { - async fn read(&mut self, address: A, buffer: &mut [u8]) -> Result<(), Self::Error> { - T::read(self, address, buffer).await + async fn read(&mut self, address: A, read: &mut [u8]) -> Result<(), Self::Error> { + T::read(self, address, read).await } - async fn write(&mut self, address: A, bytes: &[u8]) -> Result<(), Self::Error> { - T::write(self, address, bytes).await + async fn write(&mut self, address: A, write: &[u8]) -> Result<(), Self::Error> { + T::write(self, address, write).await } async fn write_read( &mut self, address: A, - bytes: &[u8], - buffer: &mut [u8], + write: &[u8], + read: &mut [u8], ) -> Result<(), Self::Error> { - T::write_read(self, address, bytes, buffer).await + T::write_read(self, address, write, read).await } async fn transaction( diff --git a/embedded-hal/src/i2c.rs b/embedded-hal/src/i2c.rs index 2eccc2cfe..c52bcdba0 100644 --- a/embedded-hal/src/i2c.rs +++ b/embedded-hal/src/i2c.rs @@ -29,15 +29,15 @@ //! # impl ErrorType for I2c0 { type Error = ErrorKind; } //! impl I2c for I2c0 //! { -//! fn read(&mut self, addr: u8, buffer: &mut [u8]) -> Result<(), Self::Error> { +//! fn read(&mut self, addr: u8, read: &mut [u8]) -> Result<(), Self::Error> { //! // ... //! # Ok(()) //! } -//! fn write(&mut self, addr: u8, bytes: &[u8]) -> Result<(), Self::Error> { +//! fn write(&mut self, addr: u8, write: &[u8]) -> Result<(), Self::Error> { //! // ... //! # Ok(()) //! } -//! fn write_read(&mut self, addr: u8, bytes: &[u8], buffer: &mut [u8]) -> Result<(), Self::Error> { +//! fn write_read(&mut self, addr: u8, write: &[u8], read: &mut [u8]) -> Result<(), Self::Error> { //! // ... //! # Ok(()) //! } @@ -49,15 +49,15 @@ //! //! impl I2c for I2c0 //! { -//! fn read(&mut self, addr: u16, buffer: &mut [u8]) -> Result<(), Self::Error> { +//! fn read(&mut self, addr: u16, write: &mut [u8]) -> Result<(), Self::Error> { //! // ... //! # Ok(()) //! } -//! fn write(&mut self, addr: u16, bytes: &[u8]) -> Result<(), Self::Error> { +//! fn write(&mut self, addr: u16, read: &[u8]) -> Result<(), Self::Error> { //! // ... //! # Ok(()) //! } -//! fn write_read(&mut self, addr: u16, bytes: &[u8], buffer: &mut [u8]) -> Result<(), Self::Error> { +//! fn write_read(&mut self, addr: u16, write: &[u8], read: &mut [u8]) -> Result<(), Self::Error> { //! // ... //! # Ok(()) //! } @@ -245,7 +245,7 @@ pub enum Operation<'a> { /// Blocking I2C pub trait I2c: ErrorType { - /// Reads enough bytes from slave with `address` to fill `buffer` + /// Reads enough bytes from slave with `address` to fill `read` /// /// # I2C Events (contract) /// @@ -263,7 +263,7 @@ pub trait I2c: ErrorType { /// - `MAK` = master acknowledge /// - `NMAK` = master no acknowledge /// - `SP` = stop condition - fn read(&mut self, address: A, buffer: &mut [u8]) -> Result<(), Self::Error>; + fn read(&mut self, address: A, read: &mut [u8]) -> Result<(), Self::Error>; /// Writes bytes to slave with address `address` /// @@ -281,9 +281,9 @@ pub trait I2c: ErrorType { /// - `SAK` = slave acknowledge /// - `Bi` = ith byte of data /// - `SP` = stop condition - fn write(&mut self, address: A, bytes: &[u8]) -> Result<(), Self::Error>; + fn write(&mut self, address: A, write: &[u8]) -> Result<(), Self::Error>; - /// 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) @@ -305,12 +305,7 @@ pub trait I2c: ErrorType { /// - `MAK` = master acknowledge /// - `NMAK` = master no acknowledge /// - `SP` = stop condition - fn write_read( - &mut self, - address: A, - bytes: &[u8], - buffer: &mut [u8], - ) -> Result<(), Self::Error>; + fn write_read(&mut self, address: A, write: &[u8], read: &mut [u8]) -> Result<(), Self::Error>; /// Execute the provided operations on the I2C bus. /// @@ -333,21 +328,16 @@ pub trait I2c: ErrorType { } impl> I2c for &mut T { - fn read(&mut self, address: A, buffer: &mut [u8]) -> Result<(), Self::Error> { - T::read(self, address, buffer) + fn read(&mut self, address: A, read: &mut [u8]) -> Result<(), Self::Error> { + T::read(self, address, read) } - fn write(&mut self, address: A, bytes: &[u8]) -> Result<(), Self::Error> { - T::write(self, address, bytes) + fn write(&mut self, address: A, write: &[u8]) -> Result<(), Self::Error> { + T::write(self, address, write) } - fn write_read( - &mut self, - address: A, - bytes: &[u8], - buffer: &mut [u8], - ) -> Result<(), Self::Error> { - T::write_read(self, address, bytes, buffer) + fn write_read(&mut self, address: A, write: &[u8], read: &mut [u8]) -> Result<(), Self::Error> { + T::write_read(self, address, write, read) } fn transaction( From 8b984d299a1926350d52fa2645a84190ef025dbe Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 8 Mar 2023 01:34:36 +0100 Subject: [PATCH 020/199] i2c: implement all operations in terms of `transaction`. --- embedded-hal-async/src/i2c.rs | 18 ++++++++++++--- embedded-hal/src/i2c.rs | 41 +++++++++++------------------------ 2 files changed, 28 insertions(+), 31 deletions(-) diff --git a/embedded-hal-async/src/i2c.rs b/embedded-hal-async/src/i2c.rs index fdde6d582..67712dd46 100644 --- a/embedded-hal-async/src/i2c.rs +++ b/embedded-hal-async/src/i2c.rs @@ -41,7 +41,10 @@ pub trait I2c: ErrorType { /// - `MAK` = master acknowledge /// - `NMAK` = master no acknowledge /// - `SP` = stop condition - async fn read(&mut self, address: A, read: &mut [u8]) -> Result<(), Self::Error>; + async fn read(&mut self, address: A, read: &mut [u8]) -> Result<(), Self::Error> { + self.transaction(address, &mut [Operation::Read(read)]) + .await + } /// Writes bytes to slave with address `address` /// @@ -59,7 +62,10 @@ pub trait I2c: ErrorType { /// - `SAK` = slave acknowledge /// - `Bi` = ith byte of data /// - `SP` = stop condition - async fn write(&mut self, address: A, write: &[u8]) -> Result<(), Self::Error>; + async fn write(&mut self, address: A, write: &[u8]) -> Result<(), Self::Error> { + self.transaction(address, &mut [Operation::Write(write)]) + .await + } /// Writes bytes to slave with address `address` and then reads enough bytes to fill `read` *in a /// single transaction*. @@ -88,7 +94,13 @@ pub trait I2c: ErrorType { address: A, write: &[u8], read: &mut [u8], - ) -> Result<(), Self::Error>; + ) -> Result<(), Self::Error> { + self.transaction( + address, + &mut [Operation::Write(write), Operation::Read(read)], + ) + .await + } /// Execute the provided operations on the I2C bus as a single transaction. /// diff --git a/embedded-hal/src/i2c.rs b/embedded-hal/src/i2c.rs index c52bcdba0..6b102d0af 100644 --- a/embedded-hal/src/i2c.rs +++ b/embedded-hal/src/i2c.rs @@ -29,18 +29,6 @@ //! # impl ErrorType for I2c0 { type Error = ErrorKind; } //! impl I2c for I2c0 //! { -//! fn read(&mut self, addr: u8, read: &mut [u8]) -> Result<(), Self::Error> { -//! // ... -//! # Ok(()) -//! } -//! fn write(&mut self, addr: u8, write: &[u8]) -> Result<(), Self::Error> { -//! // ... -//! # Ok(()) -//! } -//! fn write_read(&mut self, addr: u8, write: &[u8], read: &mut [u8]) -> Result<(), Self::Error> { -//! // ... -//! # Ok(()) -//! } //! fn transaction(&mut self, address: u8, operations: &mut [Operation<'_>]) -> Result<(), Self::Error> { //! // ... //! # Ok(()) @@ -49,18 +37,6 @@ //! //! impl I2c for I2c0 //! { -//! fn read(&mut self, addr: u16, write: &mut [u8]) -> Result<(), Self::Error> { -//! // ... -//! # Ok(()) -//! } -//! fn write(&mut self, addr: u16, read: &[u8]) -> Result<(), Self::Error> { -//! // ... -//! # Ok(()) -//! } -//! fn write_read(&mut self, addr: u16, write: &[u8], read: &mut [u8]) -> Result<(), Self::Error> { -//! // ... -//! # Ok(()) -//! } //! fn transaction(&mut self, address: u16, operations: &mut [Operation<'_>]) -> Result<(), Self::Error> { //! // ... //! # Ok(()) @@ -232,7 +208,7 @@ impl AddressMode for SevenBitAddress {} impl AddressMode for TenBitAddress {} -/// Transactional I2C operation. +/// I2C operation. /// /// Several operations can be combined as part of a transaction. #[derive(Debug, PartialEq, Eq)] @@ -263,7 +239,9 @@ pub trait I2c: ErrorType { /// - `MAK` = master acknowledge /// - `NMAK` = master no acknowledge /// - `SP` = stop condition - fn read(&mut self, address: A, read: &mut [u8]) -> Result<(), Self::Error>; + fn read(&mut self, address: A, read: &mut [u8]) -> Result<(), Self::Error> { + self.transaction(address, &mut [Operation::Read(read)]) + } /// Writes bytes to slave with address `address` /// @@ -281,7 +259,9 @@ pub trait I2c: ErrorType { /// - `SAK` = slave acknowledge /// - `Bi` = ith byte of data /// - `SP` = stop condition - fn write(&mut self, address: A, write: &[u8]) -> Result<(), Self::Error>; + fn write(&mut self, address: A, write: &[u8]) -> Result<(), Self::Error> { + self.transaction(address, &mut [Operation::Write(write)]) + } /// Writes bytes to slave with address `address` and then reads enough bytes to fill `read` *in a /// single transaction* @@ -305,7 +285,12 @@ pub trait I2c: ErrorType { /// - `MAK` = master acknowledge /// - `NMAK` = master no acknowledge /// - `SP` = stop condition - fn write_read(&mut self, address: A, write: &[u8], read: &mut [u8]) -> Result<(), Self::Error>; + fn write_read(&mut self, address: A, write: &[u8], read: &mut [u8]) -> Result<(), Self::Error> { + self.transaction( + address, + &mut [Operation::Write(write), Operation::Read(read)], + ) + } /// Execute the provided operations on the I2C bus. /// From 147aee2ffcd7c3fe6ae84d490df99f53447c89ef Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 8 Mar 2023 01:37:23 +0100 Subject: [PATCH 021/199] I2c: expand docs, document shared bus usage. --- embedded-hal/src/i2c-shared-bus.svg | 4 + embedded-hal/src/i2c.rs | 160 +++++++++++++++++++--------- 2 files changed, 114 insertions(+), 50 deletions(-) create mode 100644 embedded-hal/src/i2c-shared-bus.svg diff --git a/embedded-hal/src/i2c-shared-bus.svg b/embedded-hal/src/i2c-shared-bus.svg new file mode 100644 index 000000000..fad8cee6c --- /dev/null +++ b/embedded-hal/src/i2c-shared-bus.svg @@ -0,0 +1,4 @@ + + + +
SDA
SDA
SCL
SCL
MCU
MCU
SDA
SDA
SCL
SCL
I2C DEVICE 1
I2C DEVICE 1
SDA
SDA
SCL
SCL
I2C DEVICE 2
I2C DEVICE 2
Text is not SVG - cannot display
\ No newline at end of file diff --git a/embedded-hal/src/i2c.rs b/embedded-hal/src/i2c.rs index 6b102d0af..fdaf9e15c 100644 --- a/embedded-hal/src/i2c.rs +++ b/embedded-hal/src/i2c.rs @@ -1,8 +1,8 @@ //! Blocking 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`. +//! 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 @@ -14,80 +14,140 @@ //! 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. +//! [`SevenBitAddress`] has been set as default mode and thus can be omitted if desired. //! -//! ## Examples +//! # Bus sharing //! -//! ### `embedded-hal` implementation for an MCU -//! Here is an example of an embedded-hal implementation of the `Write` trait -//! for both modes: -//! ``` -//! # use embedded_hal::i2c::{ErrorKind, ErrorType, SevenBitAddress, TenBitAddress, I2c, Operation}; -//! /// I2C0 hardware peripheral which supports both 7-bit and 10-bit addressing. -//! pub struct I2c0; +//! I2C allows sharing a single bus between many I2C devices. The SDA and SCL lines are +//! wired in parallel to all devices. When starting a transfer an "address" is sent +//! so that the addressed device can respond and all the others can ignore the transfer. //! -//! # impl ErrorType for I2c0 { type Error = ErrorKind; } -//! impl I2c for I2c0 -//! { -//! fn transaction(&mut self, address: u8, operations: &mut [Operation<'_>]) -> Result<(), Self::Error> { -//! // ... -//! # Ok(()) -//! } -//! } +#![doc = include_str!("i2c-shared-bus.svg")] //! -//! impl I2c for I2c0 -//! { -//! fn transaction(&mut self, address: u16, operations: &mut [Operation<'_>]) -> Result<(), Self::Error> { -//! // ... -//! # Ok(()) -//! } -//! } -//! ``` +//! This bus sharing is common when having multiple I2C devices in the same board, since it uses fewer MCU +//! pins (`2` instead of `2*n`), and fewer MCU I2C peripherals (`1` instead of `n`). //! -//! ### Device driver compatible only with 7-bit addresses +//! This API supports bus sharing natively. Types implementing [`I2c`] are allowed +//! to represent either exclusive or shared access to an I2C bus. HALs typically +//! provide exclusive access implementations. Drivers shouldn't care which +//! kind they receive, they just do transactions on it and let the +//! underlying implementation share or not. +//! +//! The [`embedded-hal-bus`](https://docs.rs/embedded-hal-bus) crate provides several +//! implementations for sharing I2C buses. You can use them to take an exclusive instance +//! you've received from a HAL and "split" it into mulitple shared ones, to instantiate +//! several drivers on the same bus. +//! +//! # For driver authors +//! +//! Drivers can select the adequate address length with `I2c` or `I2c` depending +//! on the target device. If it can use either, the driver can +//! be generic over the address kind as well, though this is rare. +//! +//! Drivers should take the `I2c` instance as an argument to `new()`, and store it in their +//! struct. They **should not** take `&mut I2c`, the trait has a blanket impl for all `&mut T` +//! so taking just `I2c` ensures the user can still pass a `&mut`, but is not forced to. +//! +//! Drivers **should not** try to enable bus sharing by taking `&mut I2c` at every method. +//! This is much less ergonomic than owning the `I2c`, which still allows the user to pass an +//! implementation that does sharing behind the scenes +//! (from [`embedded-hal-bus`](https://docs.rs/embedded-hal-bus), or others). +//! +//! ## Device driver compatible only with 7-bit addresses //! //! For demonstration purposes the address mode parameter has been omitted in this example. //! //! ``` -//! # use embedded_hal::i2c::{I2c, Error}; -//! const ADDR: u8 = 0x15; +//! use embedded_hal::i2c::{I2c, Error}; +//! +//! const ADDR: u8 = 0x15; //! # const TEMP_REGISTER: u8 = 0x1; //! pub struct TemperatureSensorDriver { //! i2c: I2C, //! } //! -//! impl TemperatureSensorDriver -//! where -//! I2C: I2c, -//! { -//! pub fn read_temperature(&mut self) -> Result { +//! impl TemperatureSensorDriver { +//! pub fn new(i2c: I2C) -> Self { +//! Self { i2c } +//! } +//! +//! pub fn read_temperature(&mut self) -> Result { //! let mut temp = [0]; -//! self.i2c -//! .write_read(ADDR, &[TEMP_REGISTER], &mut temp) -//! .and(Ok(temp[0])) +//! self.i2c.write_read(ADDR, &[TEMP_REGISTER], &mut temp)?; +//! Ok(temp[0]) //! } //! } //! ``` //! -//! ### Device driver compatible only with 10-bit addresses +//! ## Device driver compatible only with 10-bit addresses //! //! ``` -//! # use embedded_hal::i2c::{Error, TenBitAddress, I2c}; -//! const ADDR: u16 = 0x158; +//! use embedded_hal::i2c::{Error, TenBitAddress, I2c}; +//! +//! const ADDR: u16 = 0x158; //! # const TEMP_REGISTER: u8 = 0x1; //! pub struct TemperatureSensorDriver { //! i2c: I2C, //! } //! -//! impl TemperatureSensorDriver -//! where -//! I2C: I2c, -//! { -//! pub fn read_temperature(&mut self) -> Result { +//! impl> TemperatureSensorDriver { +//! pub fn new(i2c: I2C) -> Self { +//! Self { i2c } +//! } +//! +//! pub fn read_temperature(&mut self) -> Result { //! let mut temp = [0]; -//! self.i2c -//! .write_read(ADDR, &[TEMP_REGISTER], &mut temp) -//! .and(Ok(temp[0])) +//! self.i2c.write_read(ADDR, &[TEMP_REGISTER], &mut temp)?; +//! Ok(temp[0]) +//! } +//! } +//! ``` +//! +//! # For HAL authors +//! +//! HALs **should not** include bus sharing mechanisms. They should expose a single type representing +//! exclusive ownership over the bus, and let the user use [`embedded-hal-bus`](https://docs.rs/embedded-hal-bus) +//! if they want to share it. (One exception is if the underlying platform already +//! supports sharing, such as Linux or some RTOSs.) +//! +//! Here is an example of an embedded-hal implementation of the `I2C` trait +//! for both addressing modes. All trait methods have have default implementations in terms of `transaction`. +//! As such, that is the only method that requires implementation in the HAL. +//! +//! ``` +//! use embedded_hal::i2c::{self, SevenBitAddress, TenBitAddress, I2c, Operation}; +//! +//! /// I2C0 hardware peripheral which supports both 7-bit and 10-bit addressing. +//! pub struct I2c0; +//! +//! #[derive(Debug, Copy, Clone, Eq, PartialEq)] +//! pub enum Error { +//! // ... +//! } +//! +//! impl i2c::Error for Error { +//! fn kind(&self) -> i2c::ErrorKind { +//! match *self { +//! // ... +//! } +//! } +//! } +//! +//! impl i2c::ErrorType for I2c0 { +//! type Error = Error; +//! } +//! +//! impl I2c for I2c0 { +//! fn transaction(&mut self, address: u8, operations: &mut [Operation<'_>]) -> Result<(), Self::Error> { +//! // ... +//! # Ok(()) +//! } +//! } +//! +//! impl I2c for I2c0 { +//! fn transaction(&mut self, address: u16, operations: &mut [Operation<'_>]) -> Result<(), Self::Error> { +//! // ... +//! # Ok(()) //! } //! } //! ``` From 7b42f2430f190d8ffca3635a36f6aebed4d27e0c Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 15 Mar 2023 12:08:28 +0100 Subject: [PATCH 022/199] i2c: changelog. --- embedded-hal-async/CHANGELOG.md | 2 ++ embedded-hal/CHANGELOG.md | 3 +++ 2 files changed, 5 insertions(+) diff --git a/embedded-hal-async/CHANGELOG.md b/embedded-hal-async/CHANGELOG.md index 1d250d318..fc4ed30c5 100644 --- a/embedded-hal-async/CHANGELOG.md +++ b/embedded-hal-async/CHANGELOG.md @@ -9,6 +9,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ### Changed - delay: make infallible. +- i2c: remove `_iter()` methods. +- i2c: add default implementations for all methods based on `transaction()`. ## [v0.2.0-alpha.0] - 2022-11-23 diff --git a/embedded-hal/CHANGELOG.md b/embedded-hal/CHANGELOG.md index 57f177103..b1e3e53d8 100644 --- a/embedded-hal/CHANGELOG.md +++ b/embedded-hal/CHANGELOG.md @@ -13,6 +13,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ### Changed - gpio: add `ErrorKind` enum for consistency with other traits and for future extensibility. No kinds are defined for now. - delay: make infallible. +- i2c: remove `_iter()` methods. +- i2c: add default implementations for all methods based on `transaction()`. +- i2c: document guidelines for shared bus usage. ## [v1.0.0-alpha.9] - 2022-09-28 From 37a8e614f03ef6c04dda1ccb7571e80dfcfb7958 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 15 Mar 2023 00:56:53 +0100 Subject: [PATCH 023/199] bus/i2c: add RefCell, CriticalSection and Mutex shared bus implementations. --- .github/workflows/clippy.yml | 2 +- .github/workflows/test.yml | 10 +-- embedded-hal-bus/CHANGELOG.md | 3 +- embedded-hal-bus/Cargo.toml | 4 ++ embedded-hal-bus/src/i2c/critical_section.rs | 70 ++++++++++++++++++++ embedded-hal-bus/src/i2c/mod.rs | 10 +++ embedded-hal-bus/src/i2c/mutex.rs | 59 +++++++++++++++++ embedded-hal-bus/src/i2c/refcell.rs | 59 +++++++++++++++++ embedded-hal-bus/src/lib.rs | 3 +- 9 files changed, 212 insertions(+), 8 deletions(-) create mode 100644 embedded-hal-bus/src/i2c/critical_section.rs create mode 100644 embedded-hal-bus/src/i2c/mod.rs create mode 100644 embedded-hal-bus/src/i2c/mutex.rs create mode 100644 embedded-hal-bus/src/i2c/refcell.rs diff --git a/.github/workflows/clippy.yml b/.github/workflows/clippy.yml index fec8c2e0d..9fa0c3bed 100644 --- a/.github/workflows/clippy.yml +++ b/.github/workflows/clippy.yml @@ -15,4 +15,4 @@ jobs: # Use a pinned version to avoid spontaneous breakages (new clippy lints are added often) toolchain: nightly-2022-11-22 components: clippy - - run: cargo clippy -- --deny=warnings \ No newline at end of file + - run: cargo clippy --features=embedded-hal-bus/std -- --deny=warnings \ No newline at end of file diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 4a12eeb6c..ff0013f53 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -13,17 +13,17 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - # All generated code should be running on stable now rust: - stable - 1.59.0 # MSRV - nightly - - # The default target we're compiling on and for target: - x86_64-unknown-linux-gnu - thumbv6m-none-eabi - thumbv7m-none-eabi + include: + - target: x86_64-unknown-linux-gnu + features: embedded-hal-bus/std steps: - uses: actions/checkout@v3 @@ -35,7 +35,7 @@ jobs: - run: sed -i '/nightly-only/d' Cargo.toml if: matrix.rust != 'nightly' - - run: cargo check --target=${{ matrix.target }} + - run: cargo check --target=${{ matrix.target }} --features=${{ matrix.features }} - - run: cargo test --target=${{ matrix.target }} + - run: cargo test --target=${{ matrix.target }} --features=${{ matrix.features }} if: contains(matrix.target, 'linux') diff --git a/embedded-hal-bus/CHANGELOG.md b/embedded-hal-bus/CHANGELOG.md index c43d3898e..a173d3769 100644 --- a/embedded-hal-bus/CHANGELOG.md +++ b/embedded-hal-bus/CHANGELOG.md @@ -7,7 +7,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] -... +### Added +- i2c: add bus sharing implementations. ## [v0.1.0-alpha.1] - 2022-09-28 diff --git a/embedded-hal-bus/Cargo.toml b/embedded-hal-bus/Cargo.toml index 54a9396e9..94d471848 100644 --- a/embedded-hal-bus/Cargo.toml +++ b/embedded-hal-bus/Cargo.toml @@ -13,5 +13,9 @@ readme = "README.md" repository = "https://github.com/rust-embedded/embedded-hal" version = "0.1.0-alpha.1" +[features] +std = [] + [dependencies] embedded-hal = { version = "=1.0.0-alpha.9", path = "../embedded-hal" } +critical-section = { version = "1.0" } diff --git a/embedded-hal-bus/src/i2c/critical_section.rs b/embedded-hal-bus/src/i2c/critical_section.rs new file mode 100644 index 000000000..32334cbcc --- /dev/null +++ b/embedded-hal-bus/src/i2c/critical_section.rs @@ -0,0 +1,70 @@ +use core::cell::RefCell; +use critical_section::Mutex; +use embedded_hal::i2c::{ErrorType, I2c}; + +/// `critical-section`-based shared bus [`I2c`] implementation. +/// +/// Sharing is implemented with a `critical-section` [`Mutex`](critical_section::Mutex). A critical section is taken for +/// the entire duration of a transaction. This allows sharing a single bus across multiple threads (interrupt priority levels). +/// The downside is critical sections typically require globally disabling interrupts, so `CriticalSectionDevice` will likely +/// negatively impact real-time properties, such as interrupt latency. If you can, prefer using +/// [`RefCellDevice`](super::RefCellDevice) instead, which does not require taking critical sections. +pub struct CriticalSectionDevice<'a, T> { + bus: &'a Mutex>, +} + +impl<'a, T> CriticalSectionDevice<'a, T> { + /// Create a new `CriticalSectionDevice` + pub fn new(bus: &'a Mutex>) -> Self { + Self { bus } + } +} + +impl<'a, T> ErrorType for CriticalSectionDevice<'a, T> +where + T: I2c, +{ + type Error = T::Error; +} + +impl<'a, T> I2c for CriticalSectionDevice<'a, T> +where + T: I2c, +{ + fn read(&mut self, address: u8, read: &mut [u8]) -> Result<(), Self::Error> { + critical_section::with(|cs| { + let bus = &mut *self.bus.borrow_ref_mut(cs); + bus.read(address, read) + }) + } + + fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Self::Error> { + critical_section::with(|cs| { + let bus = &mut *self.bus.borrow_ref_mut(cs); + bus.write(address, write) + }) + } + + fn write_read( + &mut self, + address: u8, + write: &[u8], + read: &mut [u8], + ) -> Result<(), Self::Error> { + critical_section::with(|cs| { + let bus = &mut *self.bus.borrow_ref_mut(cs); + bus.write_read(address, write, read) + }) + } + + fn transaction( + &mut self, + address: u8, + operations: &mut [embedded_hal::i2c::Operation<'_>], + ) -> Result<(), Self::Error> { + critical_section::with(|cs| { + let bus = &mut *self.bus.borrow_ref_mut(cs); + bus.transaction(address, operations) + }) + } +} diff --git a/embedded-hal-bus/src/i2c/mod.rs b/embedded-hal-bus/src/i2c/mod.rs new file mode 100644 index 000000000..8eaf2882f --- /dev/null +++ b/embedded-hal-bus/src/i2c/mod.rs @@ -0,0 +1,10 @@ +//! `I2c` shared bus implementations. + +mod refcell; +pub use refcell::*; +#[cfg(feature = "std")] +mod mutex; +#[cfg(feature = "std")] +pub use mutex::*; +mod critical_section; +pub use self::critical_section::*; diff --git a/embedded-hal-bus/src/i2c/mutex.rs b/embedded-hal-bus/src/i2c/mutex.rs new file mode 100644 index 000000000..56f922c8c --- /dev/null +++ b/embedded-hal-bus/src/i2c/mutex.rs @@ -0,0 +1,59 @@ +use embedded_hal::i2c::{ErrorType, I2c}; +use std::sync::Mutex; + +/// `std` `Mutex`-based shared bus [`I2c`] implementation. +/// +/// Sharing is implemented with an `std` [`Mutex`](std::sync::Mutex). It allows a single bus across multiple threads, +/// with finer-grained locking than [`CriticalSectionDevice`](super::CriticalSectionDevice). The downside is that +/// it is only available in `std` targets. +pub struct MutexDevice<'a, T> { + bus: &'a Mutex, +} + +impl<'a, T> MutexDevice<'a, T> { + /// Create a new `MutexDevice` + pub fn new(bus: &'a Mutex) -> Self { + Self { bus } + } +} + +impl<'a, T> ErrorType for MutexDevice<'a, T> +where + T: I2c, +{ + type Error = T::Error; +} + +impl<'a, T> I2c for MutexDevice<'a, T> +where + T: I2c, +{ + fn read(&mut self, address: u8, read: &mut [u8]) -> Result<(), Self::Error> { + let bus = &mut *self.bus.lock().unwrap(); + bus.read(address, read) + } + + fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Self::Error> { + let bus = &mut *self.bus.lock().unwrap(); + bus.write(address, write) + } + + fn write_read( + &mut self, + address: u8, + write: &[u8], + read: &mut [u8], + ) -> Result<(), Self::Error> { + let bus = &mut *self.bus.lock().unwrap(); + bus.write_read(address, write, read) + } + + fn transaction( + &mut self, + address: u8, + operations: &mut [embedded_hal::i2c::Operation<'_>], + ) -> Result<(), Self::Error> { + let bus = &mut *self.bus.lock().unwrap(); + bus.transaction(address, operations) + } +} diff --git a/embedded-hal-bus/src/i2c/refcell.rs b/embedded-hal-bus/src/i2c/refcell.rs new file mode 100644 index 000000000..cc72df692 --- /dev/null +++ b/embedded-hal-bus/src/i2c/refcell.rs @@ -0,0 +1,59 @@ +use core::cell::RefCell; +use embedded_hal::i2c::{ErrorType, I2c}; + +/// `RefCell`-based shared bus [`I2c`] implementation. +/// +/// Sharing is implemented with a `RefCell`. This means it has low overhead, but `RefCellDevice` instances are not `Send`, +/// so it only allows sharing within a single thread (interrupt priority level). If you need to share a bus across several +/// threads, use [`CriticalSectionDevice`](super::CriticalSectionDevice) instead. +pub struct RefCellDevice<'a, T> { + bus: &'a RefCell, +} + +impl<'a, T> RefCellDevice<'a, T> { + /// Create a new `RefCellDevice` + pub fn new(bus: &'a RefCell) -> Self { + Self { bus } + } +} + +impl<'a, T> ErrorType for RefCellDevice<'a, T> +where + T: I2c, +{ + type Error = T::Error; +} + +impl<'a, T> I2c for RefCellDevice<'a, T> +where + T: I2c, +{ + fn read(&mut self, address: u8, read: &mut [u8]) -> Result<(), Self::Error> { + let bus = &mut *self.bus.borrow_mut(); + bus.read(address, read) + } + + fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Self::Error> { + let bus = &mut *self.bus.borrow_mut(); + bus.write(address, write) + } + + fn write_read( + &mut self, + address: u8, + write: &[u8], + read: &mut [u8], + ) -> Result<(), Self::Error> { + let bus = &mut *self.bus.borrow_mut(); + bus.write_read(address, write, read) + } + + fn transaction( + &mut self, + address: u8, + operations: &mut [embedded_hal::i2c::Operation<'_>], + ) -> Result<(), Self::Error> { + let bus = &mut *self.bus.borrow_mut(); + bus.transaction(address, operations) + } +} diff --git a/embedded-hal-bus/src/lib.rs b/embedded-hal-bus/src/lib.rs index 566e74b61..09f36cb00 100644 --- a/embedded-hal-bus/src/lib.rs +++ b/embedded-hal-bus/src/lib.rs @@ -12,6 +12,7 @@ //! For further details on these traits, please consult the [`embedded-hal` documentation](https://docs.rs/embedded-hal). #![warn(missing_docs)] -#![no_std] +#![cfg_attr(not(feature = "std"), no_std)] +pub mod i2c; pub mod spi; From 16ac2e68df52d0376e1163ad64df332f1f1b8786 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 28 Mar 2023 13:31:11 +0200 Subject: [PATCH 024/199] bus: update docs, document features. --- embedded-hal-bus/README.md | 34 ++++++++++++++++++++-------------- embedded-hal-bus/src/lib.rs | 14 +------------- 2 files changed, 21 insertions(+), 27 deletions(-) diff --git a/embedded-hal-bus/README.md b/embedded-hal-bus/README.md index a94b03874..16ee78842 100644 --- a/embedded-hal-bus/README.md +++ b/embedded-hal-bus/README.md @@ -5,41 +5,47 @@ # `embedded-hal-bus` -Bus/Device connection mechanisms for [`embedded-hal`](https://crates.io/crates/embedded-hal), a Hardware Abstraction Layer (HAL) for embedded systems. +Bus sharing utilities for [`embedded-hal`](https://crates.io/crates/embedded-hal), a Hardware Abstraction Layer (HAL) for embedded systems. -It is possible to connect several peripherals to a bus like SPI or I2C. -To support this, `embedded-hal` provides the `SpiBus` and `SpiDevice` traits in the case of SPI, for example. +`embedded-hal` provides traits for SPI and I2C buses and devices. This crate provides hardware-independent adapters for sharing a single bus between multiple devices, compatible with the traits. -`embedded-hal` trait implementations for microcontrollers should implement the `...Bus` traits. -However, device drivers should use the `...Device` traits, _not the `...Bus` traits_ if at all possible +This project is developed and maintained by the [HAL team](https://github.com/rust-embedded/wg#the-hal-team). + +## SPI + +To support bus sharing, `embedded-hal` provides the `SpiBus` and `SpiDevice` traits. `SpiBus` represents an entire bus, +while `SpiDevice` represents a device on that bus. For further details on these traits, please consult the +[`embedded-hal` documentation](https://docs.rs/embedded-hal/1.0.0-alpha.9/embedded_hal/spi/index.html). + +`embedded-hal` trait implementations for microcontrollers should implement the `SpiBus` trait. +However, device drivers should use the `SpiDevice` traits, _not the `SpiBus` traits_ if at all possible in order to allow for sharing of the bus they are connected to. -This crate provides mechanisms to connect a `...Bus` and a `...Device`. +This crate provides mechanisms to connect a `SpiBus` and a `SpiDevice`. -For further details on these traits, please consult the [`embedded-hal` documentation](https://docs.rs/embedded-hal). +## I2C -This project is developed and maintained by the [HAL team](https://github.com/rust-embedded/wg#the-hal-team). +In the case of I2C, the same `I2c` `embedded-hal` trait represents either an entire bus, or a device on a bus. This crate +provides mechanisms to obtain multiple `I2c` instances out of a single `I2c` instance, sharing the bus. -## [API reference] +## Features -[API reference]: https://docs.rs/embedded-hal-bus +- `std`: enable shared bus implementations using `std::sync::Mutex`. ## Minimum Supported Rust Version (MSRV) - This crate is guaranteed to compile on stable Rust 1.59 and up. It *might* compile with older versions but that may change in any new patch release. See [here](../docs/msrv.md) for details on how the MSRV may be upgraded. - ## License Licensed under either of - Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or - http://www.apache.org/licenses/LICENSE-2.0) -- MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) + ) +- MIT license ([LICENSE-MIT](LICENSE-MIT) or ) at your option. diff --git a/embedded-hal-bus/src/lib.rs b/embedded-hal-bus/src/lib.rs index 09f36cb00..ca76c09c8 100644 --- a/embedded-hal-bus/src/lib.rs +++ b/embedded-hal-bus/src/lib.rs @@ -1,16 +1,4 @@ -//! Bus/Device connection mechanisms for [`embedded-hal`], a Hardware Abstraction Layer (HAL) for embedded systems. -//! -//! It is possible to connect several peripherals to a bus like SPI or I2C. -//! To support this, `embedded-hal` provides the `SpiBus` and `SpiDevice` traits in the case of SPI, for example. -//! -//! `embedded-hal` trait implementations for microcontrollers should implement the `...Bus` traits. -//! However, device drivers should use the `...Device` traits, _not the `...Bus` traits_ if at all possible -//! in order to allow for sharing of the bus they are connected to. -//! -//! This crate provides mechanisms to connect a `...Bus` and a `...Device`. -//! -//! For further details on these traits, please consult the [`embedded-hal` documentation](https://docs.rs/embedded-hal). - +#![doc = include_str!("../README.md")] #![warn(missing_docs)] #![cfg_attr(not(feature = "std"), no_std)] From 0584ebe8fb0a402dfeb414b2c119808573d6dc34 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 28 Mar 2023 13:57:56 +0200 Subject: [PATCH 025/199] Misc doc fixes. --- embedded-hal-async/README.md | 11 ++--------- embedded-hal-async/src/lib.rs | 10 +--------- embedded-hal-async/src/spi.rs | 14 +++++++------- embedded-hal-bus/src/spi.rs | 2 +- embedded-hal/src/lib.rs | 2 +- embedded-hal/src/spi.rs | 2 +- 6 files changed, 13 insertions(+), 28 deletions(-) diff --git a/embedded-hal-async/README.md b/embedded-hal-async/README.md index 0541aa808..a0527605a 100644 --- a/embedded-hal-async/README.md +++ b/embedded-hal-async/README.md @@ -7,16 +7,9 @@ An asynchronous Hardware Abstraction Layer (HAL) for embedded systems. This crate contains asynchronous versions of the [`embedded-hal`](https://crates.io/crates/embedded-hal) traits and shares its scope and [design goals](https://docs.rs/embedded-hal/latest/embedded_hal/#design-goals). -The purpose of this crate is to iterate over these trait versions before integrating them into [`embedded-hal`](https://crates.io/crates/embedded-hal). - -**NOTE** These traits are still experimental. At least one breaking change to this crate is expected in the future (changing from GATs to `async fn`), but there might be more. This project is developed and maintained by the [HAL team](https://github.com/rust-embedded/wg#the-hal-team). -## [API reference] - -[API reference]: https://docs.rs/embedded-hal-async - ## Minimum Supported Rust Version (MSRV) This crate requires Rust nightly newer than `nightly-2022-11-22`, due to requiring support for @@ -30,8 +23,8 @@ at any time. Licensed under either of - Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or - http://www.apache.org/licenses/LICENSE-2.0) -- MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) + ) +- MIT license ([LICENSE-MIT](LICENSE-MIT) or ) at your option. diff --git a/embedded-hal-async/src/lib.rs b/embedded-hal-async/src/lib.rs index cae21624a..83410cdb9 100644 --- a/embedded-hal-async/src/lib.rs +++ b/embedded-hal-async/src/lib.rs @@ -1,12 +1,4 @@ -//! An asynchronous Hardware Abstraction Layer (HAL) for embedded systems -//! -//! **NOTE** These traits are still experimental. At least one breaking -//! change to this crate is expected in the future (changing from GATs to -//! `async fn`), but there might be more. -//! -//! **NOTE** The traits and modules in this crate should follow the same structure as in -//! `embedded-hal` to ease merging and migration. - +#![doc = include_str!("../README.md")] #![warn(missing_docs)] #![no_std] #![allow(incomplete_features)] diff --git a/embedded-hal-async/src/spi.rs b/embedded-hal-async/src/spi.rs index 2f563820c..a03f2f674 100644 --- a/embedded-hal-async/src/spi.rs +++ b/embedded-hal-async/src/spi.rs @@ -114,7 +114,7 @@ pub use spi_transaction as transaction; /// `SpiDevice` represents ownership over a single SPI device on a (possibly shared) bus, selected /// with a CS (Chip Select) pin. /// -/// See (the docs on embedded-hal)[embedded_hal::spi::blocking] for important information on SPI Bus vs Device traits. +/// See (the docs on embedded-hal)[embedded_hal::spi] for important information on SPI Bus vs Device traits. /// /// # Safety /// @@ -239,7 +239,7 @@ unsafe impl SpiDevice for &mut T { pub trait SpiBusFlush: ErrorType { /// Wait until all operations have completed and the bus is idle. /// - /// See (the docs on embedded-hal)[embedded_hal::spi::blocking] for information on flushing. + /// See (the docs on embedded-hal)[embedded_hal::spi] for information on flushing. async fn flush(&mut self) -> Result<(), Self::Error>; } @@ -257,7 +257,7 @@ pub trait SpiBusRead: SpiBusFlush { /// typically `0x00`, `0xFF`, or configurable. /// /// Implementations are allowed to return before the operation is - /// complete. See (the docs on embedded-hal)[embedded_hal::spi::blocking] for details on flushing. + /// complete. See (the docs on embedded-hal)[embedded_hal::spi] for details on flushing. async fn read(&mut self, words: &mut [Word]) -> Result<(), Self::Error>; } @@ -272,7 +272,7 @@ pub trait SpiBusWrite: SpiBusFlush { /// Write `words` to the slave, ignoring all the incoming words /// /// Implementations are allowed to return before the operation is - /// complete. See (the docs on embedded-hal)[embedded_hal::spi::blocking] for details on flushing. + /// complete. See (the docs on embedded-hal)[embedded_hal::spi] for details on flushing. async fn write(&mut self, words: &[Word]) -> Result<(), Self::Error>; } @@ -286,7 +286,7 @@ impl, Word: 'static + Copy> SpiBusWrite for &mut T { /// /// `SpiBus` represents **exclusive ownership** over the whole SPI bus, with SCK, MOSI and MISO pins. /// -/// See (the docs on embedded-hal)[embedded_hal::spi::blocking] for important information on SPI Bus vs Device traits. +/// See (the docs on embedded-hal)[embedded_hal::spi] for important information on SPI Bus vs Device traits. pub trait SpiBus: SpiBusRead + SpiBusWrite { /// Write and read simultaneously. `write` is written to the slave on MOSI and /// words received on MISO are stored in `read`. @@ -298,7 +298,7 @@ pub trait SpiBus: SpiBusRead + SpiBusWrite( &'a mut self, read: &'a mut [Word], @@ -310,7 +310,7 @@ pub trait SpiBus: SpiBusRead + SpiBusWrite(&'a mut self, words: &'a mut [Word]) -> Result<(), Self::Error>; } diff --git a/embedded-hal-bus/src/spi.rs b/embedded-hal-bus/src/spi.rs index 22ef300be..7266f414b 100644 --- a/embedded-hal-bus/src/spi.rs +++ b/embedded-hal-bus/src/spi.rs @@ -28,7 +28,7 @@ where /// [`SpiDevice`] implementation with exclusive access to the bus (not shared). /// -/// This is the most straightforward way of obtaining an [`SpiDevice`] from an [`SpiBus`](embedded_hal::spi::blocking::SpiBus), +/// This is the most straightforward way of obtaining an [`SpiDevice`] from an [`SpiBus`](embedded_hal::spi::SpiBus), /// ideal for when no sharing is required (only one SPI device is present on the bus). pub struct ExclusiveDevice { bus: BUS, diff --git a/embedded-hal/src/lib.rs b/embedded-hal/src/lib.rs index 410305ac4..def14dbc0 100644 --- a/embedded-hal/src/lib.rs +++ b/embedded-hal/src/lib.rs @@ -35,7 +35,7 @@ //! //! - Where possible must *not* be tied to a specific asynchronous model. The API should be usable //! in blocking mode, with the `futures` model, with an async/await model or with a callback model. -//! (cf. the [`nb`] crate) +//! (cf. the [`nb`](https://docs.rs/nb) crate) //! //! - Must be minimal, and thus easy to implement and zero cost, yet highly composable. People that //! want higher level abstraction should *prefer to use this HAL* rather than *re-implement* diff --git a/embedded-hal/src/spi.rs b/embedded-hal/src/spi.rs index e65022fe1..4ebc68db0 100644 --- a/embedded-hal/src/spi.rs +++ b/embedded-hal/src/spi.rs @@ -132,7 +132,7 @@ //! # For HAL authors //! //! HALs **must** implement [`SpiBus`], [`SpiBusRead`] and [`SpiBusWrite`]. Users can combine the bus together with the CS pin (which should -//! implement [`OutputPin`](crate::digital::blocking::OutputPin)) using HAL-independent [`SpiDevice`] implementations such as the ones in [`embedded-hal-bus`](https://crates.io/crates/embedded-hal-bus). +//! implement [`OutputPin`](crate::digital::OutputPin)) using HAL-independent [`SpiDevice`] implementations such as the ones in [`embedded-hal-bus`](https://crates.io/crates/embedded-hal-bus). //! //! HALs may additionally implement [`SpiDevice`] to **take advantage of hardware CS management**, which may provide some performance //! benefits. (There's no point in a HAL implementing [`SpiDevice`] if the CS management is software-only, this task is better left to From b6764ecd574189b53dfa32a2efcb6051d6aed89a Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 14 Mar 2023 21:49:14 +0100 Subject: [PATCH 026/199] spi: SpiDevice transactiontake an operation slice instead of a closure. --- embedded-hal-async/src/spi.rs | 477 ++++++++++++++++++++-------------- embedded-hal-bus/src/spi.rs | 107 +++++++- embedded-hal/src/spi.rs | 207 ++++++++++----- 3 files changed, 513 insertions(+), 278 deletions(-) diff --git a/embedded-hal-async/src/spi.rs b/embedded-hal-async/src/spi.rs index a03f2f674..4d215c16a 100644 --- a/embedded-hal-async/src/spi.rs +++ b/embedded-hal-async/src/spi.rs @@ -1,205 +1,125 @@ //! SPI master mode traits. -use core::{fmt::Debug, future::Future}; +use core::fmt::Debug; use embedded_hal::digital::OutputPin; use embedded_hal::spi as blocking; pub use embedded_hal::spi::{ - Error, ErrorKind, ErrorType, Mode, Phase, Polarity, MODE_0, MODE_1, MODE_2, MODE_3, + Error, ErrorKind, ErrorType, Mode, Operation, Phase, Polarity, MODE_0, MODE_1, MODE_2, MODE_3, }; -#[macro_export] -/// Do an SPI transaction on a bus. -/// This is a safe wrapper for [SpiDevice::transaction], which handles dereferencing the raw pointer for you. +/// SPI read-only device trait /// -/// # Examples -/// -/// ``` -/// use embedded_hal_async::spi::{transaction, SpiBus, SpiBusRead, SpiBusWrite, SpiDevice}; +/// `SpiDeviceRead` represents ownership over a single SPI device on a (possibly shared) bus, selected +/// with a CS (Chip Select) pin. /// -/// pub async fn transaction_example(mut device: SPI) -> Result -/// where -/// SPI: SpiDevice, -/// SPI::Bus: SpiBus, -/// { -/// transaction!(&mut device, move |bus| async move { -/// // Unlike `SpiDevice::transaction`, we don't need to -/// // manually dereference a pointer in order to use the bus. -/// bus.write(&[42]).await?; -/// let mut data = [0; 4]; -/// bus.read(&mut data).await?; -/// Ok(u32::from_be_bytes(data)) -/// }) -/// .await -/// } -/// ``` +/// See (the docs on embedded-hal)[embedded_hal::spi] for important information on SPI Bus vs Device traits. /// -/// Note that the compiler will prevent you from moving the bus reference outside of the closure -/// ```compile_fail -/// # use embedded_hal_async::spi::{transaction, SpiBus, SpiBusRead, SpiBusWrite, SpiDevice}; -/// # -/// # pub async fn smuggle_test(mut device: SPI) -> Result<(), SPI::Error> -/// # where -/// # SPI: SpiDevice, -/// # SPI::Bus: SpiBus, -/// # { -/// let mut bus_smuggler: Option<&mut SPI::Bus> = None; -/// transaction!(&mut device, move |bus| async move { -/// bus_smuggler = Some(bus); -/// Ok(()) -/// }) -/// .await -/// # } -/// ``` -macro_rules! spi_transaction { - ($device:expr, move |$bus:ident| async move $closure_body:expr) => { - $crate::spi::SpiDevice::transaction($device, move |$bus| { - // Safety: Implementers of the `SpiDevice` trait guarantee that the pointer is - // valid and dereferencable for the entire duration of the closure. - let $bus = unsafe { &mut *$bus }; - async move { - let result = $closure_body; - let $bus = $bus; // Ensure that the bus reference was not moved out of the closure - let _ = $bus; // Silence the "unused variable" warning from prevous line - result - } - }) - }; - ($device:expr, move |$bus:ident| async $closure_body:expr) => { - $crate::spi::SpiDevice::transaction($device, move |$bus| { - // Safety: Implementers of the `SpiDevice` trait guarantee that the pointer is - // valid and dereferencable for the entire duration of the closure. - let $bus = unsafe { &mut *$bus }; - async { - let result = $closure_body; - let $bus = $bus; // Ensure that the bus reference was not moved out of the closure - let _ = $bus; // Silence the "unused variable" warning from prevous line - result - } - }) - }; - ($device:expr, |$bus:ident| async move $closure_body:expr) => { - $crate::spi::SpiDevice::transaction($device, |$bus| { - // Safety: Implementers of the `SpiDevice` trait guarantee that the pointer is - // valid and dereferencable for the entire duration of the closure. - let $bus = unsafe { &mut *$bus }; - async move { - let result = $closure_body; - let $bus = $bus; // Ensure that the bus reference was not moved out of the closure - let _ = $bus; // Silence the "unused variable" warning from prevous line - result - } - }) - }; - ($device:expr, |$bus:ident| async $closure_body:expr) => { - $crate::spi::SpiDevice::transaction($device, |$bus| { - // Safety: Implementers of the `SpiDevice` trait guarantee that the pointer is - // valid and dereferencable for the entire duration of the closure. - let $bus = unsafe { &mut *$bus }; - async { - let result = $closure_body; - let $bus = $bus; // Ensure that the bus reference was not moved out of the closure - let _ = $bus; // Silence the "unused variable" warning from prevous line - result - } - }) - }; -} +/// See the [module-level documentation](self) for important usage information. +pub trait SpiDeviceRead: ErrorType { + /// Perform a read transaction against the device. + /// + /// - Locks the bus + /// - Asserts the CS (Chip Select) pin. + /// - Performs all the operations. + /// - [Flushes](SpiBusFlush::flush) the bus. + /// - Deasserts the CS pin. + /// - Unlocks the bus. + /// + /// The locking mechanism is implementation-defined. The only requirement is it must prevent two + /// transactions from executing concurrently against the same bus. Examples of implementations are: + /// critical sections, blocking mutexes, returning an error or panicking if the bus is already busy. + /// + /// On bus errors the implementation should try to deassert CS. + /// If an error occurs while deasserting CS the bus error should take priority as the return value. + async fn read_transaction(&mut self, operations: &mut [&mut [Word]]) + -> Result<(), Self::Error>; -#[doc(inline)] -pub use spi_transaction as transaction; + /// Do a read within a transaction. + /// + /// This is a convenience method equivalent to `device.read_transaction(&mut [buf])`. + /// + /// See also: [`SpiDeviceRead::read_transaction`], [`SpiBusRead::read`] + async fn read(&mut self, buf: &mut [Word]) -> Result<(), Self::Error> { + self.read_transaction(&mut [buf]).await + } +} -/// SPI device trait +/// SPI write-only device trait /// -/// `SpiDevice` represents ownership over a single SPI device on a (possibly shared) bus, selected +/// `SpiDeviceWrite` represents ownership over a single SPI device on a (possibly shared) bus, selected /// with a CS (Chip Select) pin. /// /// See (the docs on embedded-hal)[embedded_hal::spi] for important information on SPI Bus vs Device traits. /// -/// # Safety -/// -/// See [`SpiDevice::transaction`] for details. -pub unsafe trait SpiDevice: ErrorType { - /// SPI Bus type for this device. - type Bus: ErrorType; - - /// Perform a transaction against the device. - /// - /// **NOTE:** - /// It is not recommended to use this method directly, because it requires `unsafe` code to dereference the raw pointer. - /// Instead, the [`transaction!`] macro should be used, which handles this safely inside the macro. +/// See the [module-level documentation](self) for important usage information. +pub trait SpiDeviceWrite: ErrorType { + /// Perform a write transaction against the device. /// /// - Locks the bus /// - Asserts the CS (Chip Select) pin. - /// - Calls `f` with an exclusive reference to the bus, which can then be used to do transfers against the device. + /// - Performs all the operations. /// - [Flushes](SpiBusFlush::flush) the bus. /// - Deasserts the CS pin. /// - Unlocks the bus. /// /// The locking mechanism is implementation-defined. The only requirement is it must prevent two /// transactions from executing concurrently against the same bus. Examples of implementations are: - /// critical sections, blocking mutexes, async mutexes, returning an error or panicking if the bus is already busy. + /// critical sections, blocking mutexes, returning an error or panicking if the bus is already busy. /// /// On bus errors the implementation should try to deassert CS. /// If an error occurs while deasserting CS the bus error should take priority as the return value. - /// - /// # Safety - /// - /// The current state of the Rust typechecker doesn't allow expressing the necessary lifetime constraints, so - /// the `f` closure receives a lifetime-less `*mut Bus` raw pointer instead. - /// - /// Implementers of the `SpiDevice` trait must guarantee that the pointer is valid and dereferencable - /// for the entire duration of the closure. - async fn transaction(&mut self, f: F) -> Result - where - F: FnOnce(*mut Self::Bus) -> Fut, - Fut: Future::Error>>; + async fn write_transaction(&mut self, operations: &[&[Word]]) -> Result<(), Self::Error>; - /// Do a read within a transaction. + /// Do a write within a transaction. /// - /// This is a convenience method equivalent to `device.transaction(|bus| bus.read(buf))`. + /// This is a convenience method equivalent to `device.write_transaction(&mut [buf])`. /// - /// See also: [`SpiDevice::transaction`], [`SpiBusRead::read`] - async fn read<'a, Word>(&'a mut self, buf: &'a mut [Word]) -> Result<(), Self::Error> - where - Self::Bus: SpiBusRead, - Word: Copy + 'static, - { - transaction!(self, move |bus| async move { bus.read(buf).await }).await + /// See also: [`SpiDeviceWrite::write_transaction`], [`SpiBusWrite::write`] + async fn write(&mut self, buf: &[Word]) -> Result<(), Self::Error> { + self.write_transaction(&[buf]).await } +} - /// Do a write within a transaction. +/// SPI device trait +/// +/// `SpiDevice` represents ownership over a single SPI device on a (possibly shared) bus, selected +/// with a CS (Chip Select) pin. +/// +/// See (the docs on embedded-hal)[embedded_hal::spi] for important information on SPI Bus vs Device traits. +/// +/// See the [module-level documentation](self) for important usage information. +pub trait SpiDevice: + SpiDeviceRead + SpiDeviceWrite + ErrorType +{ + /// Perform a transaction against the device. /// - /// This is a convenience method equivalent to `device.transaction(|bus| bus.write(buf))`. + /// - Locks the bus + /// - Asserts the CS (Chip Select) pin. + /// - Performs all the operations. + /// - [Flushes](SpiBusFlush::flush) the bus. + /// - Deasserts the CS pin. + /// - Unlocks the bus. /// - /// See also: [`SpiDevice::transaction`], [`SpiBusWrite::write`] - async fn write<'a, Word>(&'a mut self, buf: &'a [Word]) -> Result<(), Self::Error> - where - Self::Bus: SpiBusWrite, - Word: Copy + 'static, - { - transaction!(self, move |bus| async move { bus.write(buf).await }).await - } + /// The locking mechanism is implementation-defined. The only requirement is it must prevent two + /// transactions from executing concurrently against the same bus. Examples of implementations are: + /// critical sections, blocking mutexes, returning an error or panicking if the bus is already busy. + /// + /// On bus errors the implementation should try to deassert CS. + /// If an error occurs while deasserting CS the bus error should take priority as the return value. + async fn transaction( + &mut self, + operations: &mut [Operation<'_, Word>], + ) -> Result<(), Self::Error>; /// Do a transfer within a transaction. /// /// This is a convenience method equivalent to `device.transaction(|bus| bus.transfer(read, write))`. /// /// See also: [`SpiDevice::transaction`], [`SpiBus::transfer`] - async fn transfer<'a, Word>( - &'a mut self, - read: &'a mut [Word], - write: &'a [Word], - ) -> Result<(), Self::Error> - where - Self::Bus: SpiBus, - Word: Copy + 'static, - { - transaction!( - self, - move |bus| async move { bus.transfer(read, write).await } - ) - .await + async fn transfer(&mut self, read: &mut [Word], write: &[Word]) -> Result<(), Self::Error> { + self.transaction(&mut [Operation::Transfer(read, write)]) + .await } /// Do an in-place transfer within a transaction. @@ -207,31 +127,49 @@ pub unsafe trait SpiDevice: ErrorType { /// This is a convenience method equivalent to `device.transaction(|bus| bus.transfer_in_place(buf))`. /// /// See also: [`SpiDevice::transaction`], [`SpiBus::transfer_in_place`] - async fn transfer_in_place<'a, Word>( - &'a mut self, - buf: &'a mut [Word], - ) -> Result<(), Self::Error> - where - Self::Bus: SpiBus, - Word: Copy + 'static, - { - transaction!( - self, - move |bus| async move { bus.transfer_in_place(buf).await } - ) - .await + async fn transfer_in_place(&mut self, buf: &mut [Word]) -> Result<(), Self::Error> { + self.transaction(&mut [Operation::TransferInPlace(buf)]) + .await + } +} + +impl> SpiDeviceRead for &mut T { + async fn read_transaction( + &mut self, + operations: &mut [&mut [Word]], + ) -> Result<(), Self::Error> { + T::read_transaction(self, operations).await + } + + async fn read(&mut self, buf: &mut [Word]) -> Result<(), Self::Error> { + T::read(self, buf).await + } +} + +impl> SpiDeviceWrite for &mut T { + async fn write_transaction(&mut self, operations: &[&[Word]]) -> Result<(), Self::Error> { + T::write_transaction(self, operations).await + } + + async fn write(&mut self, buf: &[Word]) -> Result<(), Self::Error> { + T::write(self, buf).await } } -unsafe impl SpiDevice for &mut T { - type Bus = T::Bus; +impl> SpiDevice for &mut T { + async fn transaction( + &mut self, + operations: &mut [Operation<'_, Word>], + ) -> Result<(), Self::Error> { + T::transaction(self, operations).await + } + + async fn transfer(&mut self, read: &mut [Word], write: &[Word]) -> Result<(), Self::Error> { + T::transfer(self, read, write).await + } - async fn transaction(&mut self, f: F) -> Result - where - F: FnOnce(*mut Self::Bus) -> Fut, - Fut: Future::Error>>, - { - T::transaction(self, f).await + async fn transfer_in_place(&mut self, buf: &mut [Word]) -> Result<(), Self::Error> { + T::transfer_in_place(self, buf).await } } @@ -374,57 +312,194 @@ where type Error = ExclusiveDeviceError; } -impl blocking::SpiDevice for ExclusiveDevice +impl blocking::SpiDeviceRead for ExclusiveDevice where - BUS: blocking::SpiBusFlush, + BUS: blocking::SpiBusRead, CS: OutputPin, { - type Bus = BUS; + fn read_transaction(&mut self, operations: &mut [&mut [Word]]) -> Result<(), Self::Error> { + self.cs.set_low().map_err(ExclusiveDeviceError::Cs)?; - fn transaction( - &mut self, - f: impl FnOnce(&mut Self::Bus) -> Result::Error>, - ) -> Result { + let mut op_res = Ok(()); + + for buf in operations { + if let Err(e) = self.bus.read(buf) { + op_res = Err(e); + break; + } + } + + // On failure, it's important to still flush and deassert CS. + let flush_res = self.bus.flush(); + let cs_res = self.cs.set_high(); + + op_res.map_err(ExclusiveDeviceError::Spi)?; + flush_res.map_err(ExclusiveDeviceError::Spi)?; + cs_res.map_err(ExclusiveDeviceError::Cs)?; + + Ok(()) + } +} + +impl blocking::SpiDeviceWrite for ExclusiveDevice +where + BUS: blocking::SpiBusWrite, + CS: OutputPin, +{ + fn write_transaction(&mut self, operations: &[&[Word]]) -> Result<(), Self::Error> { self.cs.set_low().map_err(ExclusiveDeviceError::Cs)?; - let f_res = f(&mut self.bus); + let mut op_res = Ok(()); + + for buf in operations { + if let Err(e) = self.bus.write(buf) { + op_res = Err(e); + break; + } + } // On failure, it's important to still flush and deassert CS. let flush_res = self.bus.flush(); let cs_res = self.cs.set_high(); - let f_res = f_res.map_err(ExclusiveDeviceError::Spi)?; + op_res.map_err(ExclusiveDeviceError::Spi)?; flush_res.map_err(ExclusiveDeviceError::Spi)?; cs_res.map_err(ExclusiveDeviceError::Cs)?; - Ok(f_res) + Ok(()) } } -unsafe impl SpiDevice for ExclusiveDevice +impl blocking::SpiDevice for ExclusiveDevice where - BUS: SpiBusFlush, + BUS: blocking::SpiBus, CS: OutputPin, { - type Bus = BUS; + fn transaction(&mut self, operations: &mut [Operation<'_, Word>]) -> Result<(), Self::Error> { + self.cs.set_low().map_err(ExclusiveDeviceError::Cs)?; + + let op_res = 'ops: { + for op in operations { + let res = match op { + Operation::Read(buf) => self.bus.read(buf), + Operation::Write(buf) => self.bus.write(buf), + Operation::Transfer(read, write) => self.bus.transfer(read, write), + Operation::TransferInPlace(buf) => self.bus.transfer_in_place(buf), + }; + if let Err(e) = res { + break 'ops Err(e); + } + } + Ok(()) + }; + + // On failure, it's important to still flush and deassert CS. + let flush_res = self.bus.flush(); + let cs_res = self.cs.set_high(); + + op_res.map_err(ExclusiveDeviceError::Spi)?; + flush_res.map_err(ExclusiveDeviceError::Spi)?; + cs_res.map_err(ExclusiveDeviceError::Cs)?; - async fn transaction(&mut self, f: F) -> Result - where - F: FnOnce(*mut Self::Bus) -> Fut, - Fut: Future::Error>>, - { + Ok(()) + } +} + +impl SpiDeviceRead for ExclusiveDevice +where + BUS: SpiBusRead, + CS: OutputPin, +{ + async fn read_transaction( + &mut self, + operations: &mut [&mut [Word]], + ) -> Result<(), Self::Error> { self.cs.set_low().map_err(ExclusiveDeviceError::Cs)?; - let f_res = f(&mut self.bus).await; + let mut op_res = Ok(()); + + for buf in operations { + if let Err(e) = self.bus.read(buf).await { + op_res = Err(e); + break; + } + } + + // On failure, it's important to still flush and deassert CS. + let flush_res = self.bus.flush().await; + let cs_res = self.cs.set_high(); + + op_res.map_err(ExclusiveDeviceError::Spi)?; + flush_res.map_err(ExclusiveDeviceError::Spi)?; + cs_res.map_err(ExclusiveDeviceError::Cs)?; + + Ok(()) + } +} + +impl SpiDeviceWrite for ExclusiveDevice +where + BUS: SpiBusWrite, + CS: OutputPin, +{ + async fn write_transaction(&mut self, operations: &[&[Word]]) -> Result<(), Self::Error> { + self.cs.set_low().map_err(ExclusiveDeviceError::Cs)?; + + let mut op_res = Ok(()); + + for buf in operations { + if let Err(e) = self.bus.write(buf).await { + op_res = Err(e); + break; + } + } + + // On failure, it's important to still flush and deassert CS. + let flush_res = self.bus.flush().await; + let cs_res = self.cs.set_high(); + + op_res.map_err(ExclusiveDeviceError::Spi)?; + flush_res.map_err(ExclusiveDeviceError::Spi)?; + cs_res.map_err(ExclusiveDeviceError::Cs)?; + + Ok(()) + } +} + +impl SpiDevice for ExclusiveDevice +where + BUS: SpiBus, + CS: OutputPin, +{ + async fn transaction( + &mut self, + operations: &mut [Operation<'_, Word>], + ) -> Result<(), Self::Error> { + self.cs.set_low().map_err(ExclusiveDeviceError::Cs)?; + + let op_res = 'ops: { + for op in operations { + let res = match op { + Operation::Read(buf) => self.bus.read(buf).await, + Operation::Write(buf) => self.bus.write(buf).await, + Operation::Transfer(read, write) => self.bus.transfer(read, write).await, + Operation::TransferInPlace(buf) => self.bus.transfer_in_place(buf).await, + }; + if let Err(e) = res { + break 'ops Err(e); + } + } + Ok(()) + }; // On failure, it's important to still flush and deassert CS. let flush_res = self.bus.flush().await; let cs_res = self.cs.set_high(); - let f_res = f_res.map_err(ExclusiveDeviceError::Spi)?; + op_res.map_err(ExclusiveDeviceError::Spi)?; flush_res.map_err(ExclusiveDeviceError::Spi)?; cs_res.map_err(ExclusiveDeviceError::Cs)?; - Ok(f_res) + Ok(()) } } diff --git a/embedded-hal-bus/src/spi.rs b/embedded-hal-bus/src/spi.rs index 7266f414b..c0de23fcb 100644 --- a/embedded-hal-bus/src/spi.rs +++ b/embedded-hal-bus/src/spi.rs @@ -2,7 +2,10 @@ use core::fmt::Debug; use embedded_hal::digital::OutputPin; -use embedded_hal::spi::{Error, ErrorKind, ErrorType, SpiBusFlush, SpiDevice}; +use embedded_hal::spi::{ + Error, ErrorKind, ErrorType, Operation, SpiBus, SpiBusRead, SpiBusWrite, SpiDevice, + SpiDeviceRead, SpiDeviceWrite, +}; /// Error type for [`ExclusiveDevice`] operations. #[derive(Copy, Clone, Eq, PartialEq, Debug)] @@ -50,29 +53,111 @@ where type Error = ExclusiveDeviceError; } -impl SpiDevice for ExclusiveDevice +impl SpiDeviceRead for ExclusiveDevice where - BUS: SpiBusFlush, + BUS: SpiBusRead, CS: OutputPin, { - type Bus = BUS; + fn read_transaction(&mut self, operations: &mut [&mut [Word]]) -> Result<(), Self::Error> { + self.cs.set_low().map_err(ExclusiveDeviceError::Cs)?; + + let mut op_res = Ok(()); + + for buf in operations { + if let Err(e) = self.bus.read(buf) { + op_res = Err(e); + break; + } + } + + // On failure, it's important to still flush and deassert CS. + let flush_res = self.bus.flush(); + let cs_res = self.cs.set_high(); - fn transaction( - &mut self, - f: impl FnOnce(&mut Self::Bus) -> Result::Error>, - ) -> Result { + op_res.map_err(ExclusiveDeviceError::Spi)?; + flush_res.map_err(ExclusiveDeviceError::Spi)?; + cs_res.map_err(ExclusiveDeviceError::Cs)?; + + Ok(()) + } +} + +impl SpiDeviceWrite for ExclusiveDevice +where + BUS: SpiBusWrite, + CS: OutputPin, +{ + fn write_transaction(&mut self, operations: &[&[Word]]) -> Result<(), Self::Error> { self.cs.set_low().map_err(ExclusiveDeviceError::Cs)?; - let f_res = f(&mut self.bus); + let mut op_res = Ok(()); + + for buf in operations { + if let Err(e) = self.bus.write(buf) { + op_res = Err(e); + break; + } + } + + // On failure, it's important to still flush and deassert CS. + let flush_res = self.bus.flush(); + let cs_res = self.cs.set_high(); + + op_res.map_err(ExclusiveDeviceError::Spi)?; + flush_res.map_err(ExclusiveDeviceError::Spi)?; + cs_res.map_err(ExclusiveDeviceError::Cs)?; + + Ok(()) + } +} + +impl SpiDevice for ExclusiveDevice +where + BUS: SpiBus, + CS: OutputPin, +{ + fn transaction(&mut self, operations: &mut [Operation<'_, Word>]) -> Result<(), Self::Error> { + self.cs.set_low().map_err(ExclusiveDeviceError::Cs)?; + + let mut op_res = Ok(()); + + for op in operations { + match op { + Operation::Read(buf) => { + if let Err(e) = self.bus.read(buf) { + op_res = Err(e); + break; + } + } + Operation::Write(buf) => { + if let Err(e) = self.bus.write(buf) { + op_res = Err(e); + break; + } + } + Operation::Transfer(read, write) => { + if let Err(e) = self.bus.transfer(read, write) { + op_res = Err(e); + break; + } + } + Operation::TransferInPlace(buf) => { + if let Err(e) = self.bus.transfer_in_place(buf) { + op_res = Err(e); + break; + } + } + } + } // On failure, it's important to still flush and deassert CS. let flush_res = self.bus.flush(); let cs_res = self.cs.set_high(); - let f_res = f_res.map_err(ExclusiveDeviceError::Spi)?; + op_res.map_err(ExclusiveDeviceError::Spi)?; flush_res.map_err(ExclusiveDeviceError::Spi)?; cs_res.map_err(ExclusiveDeviceError::Cs)?; - Ok(f_res) + Ok(()) } } diff --git a/embedded-hal/src/spi.rs b/embedded-hal/src/spi.rs index 4ebc68db0..f8f14efa4 100644 --- a/embedded-hal/src/spi.rs +++ b/embedded-hal/src/spi.rs @@ -46,20 +46,19 @@ //! consists of asserting CS, then doing one or more transfers, then deasserting CS. For the entire duration of the transaction, the [`SpiDevice`] //! implementation will ensure no other transaction can be opened on the same bus. This is the key that allows correct sharing of the bus. //! -//! The capabilities of the bus (read-write, read-only or write-only) are determined by which of the [`SpiBus`], [`SpiBusRead`] [`SpiBusWrite`] traits -//! are implemented for the [`Bus`](SpiDevice::Bus) associated type. +//! For read-only or write-only SPI devices, the [`SpiDeviceRead`] and [`SpiDeviceWrite`] are available. //! //! # For driver authors //! //! When implementing a driver, it's crucial to pick the right trait, to ensure correct operation //! with maximum interoperability. Here are some guidelines depending on the device you're implementing a driver for: //! -//! If your device **has a CS pin**, use [`SpiDevice`]. Do not manually manage the CS pin, the [`SpiDevice`] implementation will do it for you. -//! Add bounds like `where T::Bus: SpiBus`, `where T::Bus: SpiBusRead`, `where T::Bus: SpiBusWrite` to specify the kind of access you need. +//! If your device **has a CS pin**, use [`SpiDevice`] (or [`SpiDeviceRead`]/[`SpiDeviceWrite`]). Do not manually +//! manage the CS pin, the [`SpiDevice`] implementation will do it for you. //! By using [`SpiDevice`], your driver will cooperate nicely with other drivers for other devices in the same shared SPI bus. //! //! ``` -//! # use embedded_hal::spi::{SpiBus, SpiBusRead, SpiBusWrite, SpiDevice}; +//! # use embedded_hal::spi::{SpiBus, SpiBusRead, SpiBusWrite, SpiDevice, Operation}; //! pub struct MyDriver { //! spi: SPI, //! } @@ -67,7 +66,6 @@ //! impl MyDriver //! where //! SPI: SpiDevice, -//! SPI::Bus: SpiBus, // or SpiBusRead/SpiBusWrite if you only need to read or only write. //! { //! pub fn new(spi: SPI) -> Self { //! Self { spi } @@ -77,10 +75,10 @@ //! let mut buf = [0; 2]; //! //! // `transaction` asserts and deasserts CS for us. No need to do it manually! -//! self.spi.transaction(|bus| { -//! bus.write(&[0x90])?; -//! bus.read(&mut buf) -//! }).map_err(MyError::Spi)?; +//! self.spi.transaction(&mut [ +//! Operation::Write(&[0x90]), +//! Operation::Read(&mut buf), +//! ]).map_err(MyError::Spi)?; //! //! Ok(buf) //! } @@ -298,21 +296,75 @@ impl ErrorType for &mut T { type Error = T::Error; } -/// SPI device trait +/// SPI transaction operation. /// -/// `SpiDevice` represents ownership over a single SPI device on a (possibly shared) bus, selected +/// This allows composition of SPI operations into a single bus transaction +#[derive(Debug, PartialEq)] +pub enum Operation<'a, Word: 'static> { + /// Read data into the provided buffer. + /// + /// Equivalent to [`SpiBusRead::read`]. + Read(&'a mut [Word]), + /// Write data from the provided buffer, discarding read data + /// + /// Equivalent to [`SpiBusWrite::write`]. + Write(&'a [Word]), + /// Read data into the first buffer, while writing data from the second buffer. + /// + /// Equivalent to [`SpiBus::transfer`]. + Transfer(&'a mut [Word], &'a [Word]), + /// Write data out while reading data into the provided buffer + /// + /// Equivalent to [`SpiBus::transfer_in_place`]. + TransferInPlace(&'a mut [Word]), +} + +/// SPI read-only device trait +/// +/// `SpiDeviceRead` represents ownership over a single SPI device on a (possibly shared) bus, selected /// with a CS (Chip Select) pin. /// /// See the [module-level documentation](self) for important usage information. -pub trait SpiDevice: ErrorType { - /// SPI Bus type for this device. - type Bus: ErrorType; +pub trait SpiDeviceRead: ErrorType { + /// Perform a read transaction against the device. + /// + /// - Locks the bus + /// - Asserts the CS (Chip Select) pin. + /// - Performs all the operations. + /// - [Flushes](SpiBusFlush::flush) the bus. + /// - Deasserts the CS pin. + /// - Unlocks the bus. + /// + /// The locking mechanism is implementation-defined. The only requirement is it must prevent two + /// transactions from executing concurrently against the same bus. Examples of implementations are: + /// critical sections, blocking mutexes, returning an error or panicking if the bus is already busy. + /// + /// On bus errors the implementation should try to deassert CS. + /// If an error occurs while deasserting CS the bus error should take priority as the return value. + fn read_transaction(&mut self, operations: &mut [&mut [Word]]) -> Result<(), Self::Error>; - /// Perform a transaction against the device. + /// Do a read within a transaction. + /// + /// This is a convenience method equivalent to `device.read_transaction(&mut [buf])`. + /// + /// See also: [`SpiDeviceRead::read_transaction`], [`SpiBusRead::read`] + fn read(&mut self, buf: &mut [Word]) -> Result<(), Self::Error> { + self.read_transaction(&mut [buf]) + } +} + +/// SPI write-only device trait +/// +/// `SpiDeviceWrite` represents ownership over a single SPI device on a (possibly shared) bus, selected +/// with a CS (Chip Select) pin. +/// +/// See the [module-level documentation](self) for important usage information. +pub trait SpiDeviceWrite: ErrorType { + /// Perform a write transaction against the device. /// /// - Locks the bus /// - Asserts the CS (Chip Select) pin. - /// - Calls `f` with an exclusive reference to the bus, which can then be used to do transfers against the device. + /// - Performs all the operations. /// - [Flushes](SpiBusFlush::flush) the bus. /// - Deasserts the CS pin. /// - Unlocks the bus. @@ -323,71 +375,94 @@ pub trait SpiDevice: ErrorType { /// /// On bus errors the implementation should try to deassert CS. /// If an error occurs while deasserting CS the bus error should take priority as the return value. - fn transaction( - &mut self, - f: impl FnOnce(&mut Self::Bus) -> Result::Error>, - ) -> Result; + fn write_transaction(&mut self, operations: &[&[Word]]) -> Result<(), Self::Error>; /// Do a write within a transaction. /// - /// This is a convenience method equivalent to `device.transaction(|bus| bus.write(buf))`. + /// This is a convenience method equivalent to `device.write_transaction(&mut [buf])`. /// - /// See also: [`SpiDevice::transaction`], [`SpiBusWrite::write`] - fn write(&mut self, buf: &[Word]) -> Result<(), Self::Error> - where - Self::Bus: SpiBusWrite, - Word: Copy, - { - self.transaction(|bus| bus.write(buf)) + /// See also: [`SpiDeviceWrite::write_transaction`], [`SpiBusWrite::write`] + fn write(&mut self, buf: &[Word]) -> Result<(), Self::Error> { + self.write_transaction(&[buf]) } +} - /// Do a read within a transaction. +/// SPI device trait +/// +/// `SpiDevice` represents ownership over a single SPI device on a (possibly shared) bus, selected +/// with a CS (Chip Select) pin. +/// +/// See the [module-level documentation](self) for important usage information. +pub trait SpiDevice: + SpiDeviceRead + SpiDeviceWrite + ErrorType +{ + /// Perform a transaction against the device. + /// + /// - Locks the bus + /// - Asserts the CS (Chip Select) pin. + /// - Performs all the operations. + /// - [Flushes](SpiBusFlush::flush) the bus. + /// - Deasserts the CS pin. + /// - Unlocks the bus. /// - /// This is a convenience method equivalent to `device.transaction(|bus| bus.read(buf))`. + /// The locking mechanism is implementation-defined. The only requirement is it must prevent two + /// transactions from executing concurrently against the same bus. Examples of implementations are: + /// critical sections, blocking mutexes, returning an error or panicking if the bus is already busy. /// - /// See also: [`SpiDevice::transaction`], [`SpiBusRead::read`] - fn read(&mut self, buf: &mut [Word]) -> Result<(), Self::Error> - where - Self::Bus: SpiBusRead, - Word: Copy, - { - self.transaction(|bus| bus.read(buf)) - } + /// On bus errors the implementation should try to deassert CS. + /// If an error occurs while deasserting CS the bus error should take priority as the return value. + fn transaction(&mut self, operations: &mut [Operation<'_, Word>]) -> Result<(), Self::Error>; /// Do a transfer within a transaction. /// - /// This is a convenience method equivalent to `device.transaction(|bus| bus.transfer(read, write))`. + /// This is a convenience method equivalent to `device.transaction(&mut [Operation::Transfer(read, write)]`. /// /// See also: [`SpiDevice::transaction`], [`SpiBus::transfer`] - fn transfer(&mut self, read: &mut [Word], write: &[Word]) -> Result<(), Self::Error> - where - Self::Bus: SpiBus, - Word: Copy, - { - self.transaction(|bus| bus.transfer(read, write)) + fn transfer(&mut self, read: &mut [Word], write: &[Word]) -> Result<(), Self::Error> { + self.transaction(&mut [Operation::Transfer(read, write)]) } /// Do an in-place transfer within a transaction. /// - /// This is a convenience method equivalent to `device.transaction(|bus| bus.transfer_in_place(buf))`. + /// This is a convenience method equivalent to `device.transaction([Operation::TransferInPlace(buf)]`. /// /// See also: [`SpiDevice::transaction`], [`SpiBus::transfer_in_place`] - fn transfer_in_place(&mut self, buf: &mut [Word]) -> Result<(), Self::Error> - where - Self::Bus: SpiBus, - Word: Copy, - { - self.transaction(|bus| bus.transfer_in_place(buf)) + fn transfer_in_place(&mut self, buf: &mut [Word]) -> Result<(), Self::Error> { + self.transaction(&mut [Operation::TransferInPlace(buf)]) } } -impl SpiDevice for &mut T { - type Bus = T::Bus; - fn transaction( - &mut self, - f: impl FnOnce(&mut Self::Bus) -> Result::Error>, - ) -> Result { - T::transaction(self, f) +impl> SpiDeviceRead for &mut T { + fn read_transaction(&mut self, operations: &mut [&mut [Word]]) -> Result<(), Self::Error> { + T::read_transaction(self, operations) + } + + fn read(&mut self, buf: &mut [Word]) -> Result<(), Self::Error> { + T::read(self, buf) + } +} + +impl> SpiDeviceWrite for &mut T { + fn write_transaction(&mut self, operations: &[&[Word]]) -> Result<(), Self::Error> { + T::write_transaction(self, operations) + } + + fn write(&mut self, buf: &[Word]) -> Result<(), Self::Error> { + T::write(self, buf) + } +} + +impl> SpiDevice for &mut T { + fn transaction(&mut self, operations: &mut [Operation<'_, Word>]) -> Result<(), Self::Error> { + T::transaction(self, operations) + } + + fn transfer(&mut self, read: &mut [Word], write: &[Word]) -> Result<(), Self::Error> { + T::transfer(self, read, write) + } + + fn transfer_in_place(&mut self, buf: &mut [Word]) -> Result<(), Self::Error> { + T::transfer_in_place(self, buf) } } @@ -406,7 +481,7 @@ impl SpiBusFlush for &mut T { } /// Read-only SPI bus -pub trait SpiBusRead: SpiBusFlush { +pub trait SpiBusRead: SpiBusFlush { /// Read `words` from the slave. /// /// The word value sent on MOSI during reading is implementation-defined, @@ -417,14 +492,14 @@ pub trait SpiBusRead: SpiBusFlush { fn read(&mut self, words: &mut [Word]) -> Result<(), Self::Error>; } -impl, Word: Copy> SpiBusRead for &mut T { +impl, Word: Copy + 'static> SpiBusRead for &mut T { fn read(&mut self, words: &mut [Word]) -> Result<(), Self::Error> { T::read(self, words) } } /// Write-only SPI bus -pub trait SpiBusWrite: SpiBusFlush { +pub trait SpiBusWrite: SpiBusFlush { /// Write `words` to the slave, ignoring all the incoming words /// /// Implementations are allowed to return before the operation is @@ -432,7 +507,7 @@ pub trait SpiBusWrite: SpiBusFlush { fn write(&mut self, words: &[Word]) -> Result<(), Self::Error>; } -impl, Word: Copy> SpiBusWrite for &mut T { +impl, Word: Copy + 'static> SpiBusWrite for &mut T { fn write(&mut self, words: &[Word]) -> Result<(), Self::Error> { T::write(self, words) } @@ -443,7 +518,7 @@ impl, Word: Copy> SpiBusWrite for &mut T { /// `SpiBus` represents **exclusive ownership** over the whole SPI bus, with SCK, MOSI and MISO pins. /// /// See the [module-level documentation](self) for important information on SPI Bus vs Device traits. -pub trait SpiBus: SpiBusRead + SpiBusWrite { +pub trait SpiBus: SpiBusRead + SpiBusWrite { /// Write and read simultaneously. `write` is written to the slave on MOSI and /// words received on MISO are stored in `read`. /// @@ -466,7 +541,7 @@ pub trait SpiBus: SpiBusRead + SpiBusWrite { fn transfer_in_place(&mut self, words: &mut [Word]) -> Result<(), Self::Error>; } -impl, Word: Copy> SpiBus for &mut T { +impl, Word: Copy + 'static> SpiBus for &mut T { fn transfer(&mut self, read: &mut [Word], write: &[Word]) -> Result<(), Self::Error> { T::transfer(self, read, write) } From 76541e0766257a89d9f300f9b5ee61e4605b883a Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 28 Mar 2023 14:43:15 +0200 Subject: [PATCH 027/199] bus/spi: add RefCell, CriticalSection and Mutex shared bus implementations. --- embedded-hal-bus/CHANGELOG.md | 1 + embedded-hal-bus/src/spi.rs | 163 ------------------- embedded-hal-bus/src/spi/critical_section.rs | 133 +++++++++++++++ embedded-hal-bus/src/spi/exclusive.rs | 117 +++++++++++++ embedded-hal-bus/src/spi/mod.rs | 37 +++++ embedded-hal-bus/src/spi/mutex.rs | 124 ++++++++++++++ embedded-hal-bus/src/spi/refcell.rs | 124 ++++++++++++++ 7 files changed, 536 insertions(+), 163 deletions(-) delete mode 100644 embedded-hal-bus/src/spi.rs create mode 100644 embedded-hal-bus/src/spi/critical_section.rs create mode 100644 embedded-hal-bus/src/spi/exclusive.rs create mode 100644 embedded-hal-bus/src/spi/mod.rs create mode 100644 embedded-hal-bus/src/spi/mutex.rs create mode 100644 embedded-hal-bus/src/spi/refcell.rs diff --git a/embedded-hal-bus/CHANGELOG.md b/embedded-hal-bus/CHANGELOG.md index a173d3769..97ee0d8d1 100644 --- a/embedded-hal-bus/CHANGELOG.md +++ b/embedded-hal-bus/CHANGELOG.md @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ### Added - i2c: add bus sharing implementations. +- spi: add bus sharing implementations. ## [v0.1.0-alpha.1] - 2022-09-28 diff --git a/embedded-hal-bus/src/spi.rs b/embedded-hal-bus/src/spi.rs deleted file mode 100644 index c0de23fcb..000000000 --- a/embedded-hal-bus/src/spi.rs +++ /dev/null @@ -1,163 +0,0 @@ -//! SPI bus sharing mechanisms. - -use core::fmt::Debug; -use embedded_hal::digital::OutputPin; -use embedded_hal::spi::{ - Error, ErrorKind, ErrorType, Operation, SpiBus, SpiBusRead, SpiBusWrite, SpiDevice, - SpiDeviceRead, SpiDeviceWrite, -}; - -/// Error type for [`ExclusiveDevice`] operations. -#[derive(Copy, Clone, Eq, PartialEq, Debug)] -pub enum ExclusiveDeviceError { - /// An inner SPI bus operation failed - Spi(BUS), - /// Asserting or deasserting CS failed - Cs(CS), -} - -impl Error for ExclusiveDeviceError -where - BUS: Error + Debug, - CS: Debug, -{ - fn kind(&self) -> ErrorKind { - match self { - Self::Spi(e) => e.kind(), - Self::Cs(_) => ErrorKind::ChipSelectFault, - } - } -} - -/// [`SpiDevice`] implementation with exclusive access to the bus (not shared). -/// -/// This is the most straightforward way of obtaining an [`SpiDevice`] from an [`SpiBus`](embedded_hal::spi::SpiBus), -/// ideal for when no sharing is required (only one SPI device is present on the bus). -pub struct ExclusiveDevice { - bus: BUS, - cs: CS, -} - -impl ExclusiveDevice { - /// Create a new ExclusiveDevice - pub fn new(bus: BUS, cs: CS) -> Self { - Self { bus, cs } - } -} - -impl ErrorType for ExclusiveDevice -where - BUS: ErrorType, - CS: OutputPin, -{ - type Error = ExclusiveDeviceError; -} - -impl SpiDeviceRead for ExclusiveDevice -where - BUS: SpiBusRead, - CS: OutputPin, -{ - fn read_transaction(&mut self, operations: &mut [&mut [Word]]) -> Result<(), Self::Error> { - self.cs.set_low().map_err(ExclusiveDeviceError::Cs)?; - - let mut op_res = Ok(()); - - for buf in operations { - if let Err(e) = self.bus.read(buf) { - op_res = Err(e); - break; - } - } - - // On failure, it's important to still flush and deassert CS. - let flush_res = self.bus.flush(); - let cs_res = self.cs.set_high(); - - op_res.map_err(ExclusiveDeviceError::Spi)?; - flush_res.map_err(ExclusiveDeviceError::Spi)?; - cs_res.map_err(ExclusiveDeviceError::Cs)?; - - Ok(()) - } -} - -impl SpiDeviceWrite for ExclusiveDevice -where - BUS: SpiBusWrite, - CS: OutputPin, -{ - fn write_transaction(&mut self, operations: &[&[Word]]) -> Result<(), Self::Error> { - self.cs.set_low().map_err(ExclusiveDeviceError::Cs)?; - - let mut op_res = Ok(()); - - for buf in operations { - if let Err(e) = self.bus.write(buf) { - op_res = Err(e); - break; - } - } - - // On failure, it's important to still flush and deassert CS. - let flush_res = self.bus.flush(); - let cs_res = self.cs.set_high(); - - op_res.map_err(ExclusiveDeviceError::Spi)?; - flush_res.map_err(ExclusiveDeviceError::Spi)?; - cs_res.map_err(ExclusiveDeviceError::Cs)?; - - Ok(()) - } -} - -impl SpiDevice for ExclusiveDevice -where - BUS: SpiBus, - CS: OutputPin, -{ - fn transaction(&mut self, operations: &mut [Operation<'_, Word>]) -> Result<(), Self::Error> { - self.cs.set_low().map_err(ExclusiveDeviceError::Cs)?; - - let mut op_res = Ok(()); - - for op in operations { - match op { - Operation::Read(buf) => { - if let Err(e) = self.bus.read(buf) { - op_res = Err(e); - break; - } - } - Operation::Write(buf) => { - if let Err(e) = self.bus.write(buf) { - op_res = Err(e); - break; - } - } - Operation::Transfer(read, write) => { - if let Err(e) = self.bus.transfer(read, write) { - op_res = Err(e); - break; - } - } - Operation::TransferInPlace(buf) => { - if let Err(e) = self.bus.transfer_in_place(buf) { - op_res = Err(e); - break; - } - } - } - } - - // On failure, it's important to still flush and deassert CS. - let flush_res = self.bus.flush(); - let cs_res = self.cs.set_high(); - - op_res.map_err(ExclusiveDeviceError::Spi)?; - flush_res.map_err(ExclusiveDeviceError::Spi)?; - cs_res.map_err(ExclusiveDeviceError::Cs)?; - - Ok(()) - } -} diff --git a/embedded-hal-bus/src/spi/critical_section.rs b/embedded-hal-bus/src/spi/critical_section.rs new file mode 100644 index 000000000..a327609fd --- /dev/null +++ b/embedded-hal-bus/src/spi/critical_section.rs @@ -0,0 +1,133 @@ +use core::cell::RefCell; +use critical_section::Mutex; +use embedded_hal::digital::OutputPin; +use embedded_hal::spi::{ + ErrorType, Operation, SpiBus, SpiBusRead, SpiBusWrite, SpiDevice, SpiDeviceRead, SpiDeviceWrite, +}; + +use super::DeviceError; + +/// `critical-section`-based shared bus [`SpiDevice`] implementation. +/// +/// This allows for sharing an [`SpiBus`](embedded_hal::spi::SpiBus), obtaining multiple [`SpiDevice`] instances, +/// each with its own `CS` pin. +/// +/// Sharing is implemented with a `critical-section` [`Mutex`](critical_section::Mutex). A critical section is taken for +/// the entire duration of a transaction. This allows sharing a single bus across multiple threads (interrupt priority levels). +/// The downside is critical sections typically require globally disabling interrupts, so `CriticalSectionDevice` will likely +/// negatively impact real-time properties, such as interrupt latency. If you can, prefer using +/// [`RefCellDevice`](super::RefCellDevice) instead, which does not require taking critical sections. +pub struct CriticalSectionDevice<'a, BUS, CS> { + bus: &'a Mutex>, + cs: CS, +} + +impl<'a, BUS, CS> CriticalSectionDevice<'a, BUS, CS> { + /// Create a new ExclusiveDevice + pub fn new(bus: &'a Mutex>, cs: CS) -> Self { + Self { bus, cs } + } +} + +impl<'a, BUS, CS> ErrorType for CriticalSectionDevice<'a, BUS, CS> +where + BUS: ErrorType, + CS: OutputPin, +{ + type Error = DeviceError; +} + +impl<'a, Word: Copy + 'static, BUS, CS> SpiDeviceRead for CriticalSectionDevice<'a, BUS, CS> +where + BUS: SpiBusRead, + CS: OutputPin, +{ + fn read_transaction(&mut self, operations: &mut [&mut [Word]]) -> Result<(), Self::Error> { + critical_section::with(|cs| { + let bus = &mut *self.bus.borrow_ref_mut(cs); + + self.cs.set_low().map_err(DeviceError::Cs)?; + + let mut op_res = Ok(()); + for buf in operations { + if let Err(e) = bus.read(buf) { + op_res = Err(e); + break; + } + } + + // On failure, it's important to still flush and deassert CS. + let flush_res = bus.flush(); + let cs_res = self.cs.set_high(); + + op_res.map_err(DeviceError::Spi)?; + flush_res.map_err(DeviceError::Spi)?; + cs_res.map_err(DeviceError::Cs)?; + + Ok(()) + }) + } +} + +impl<'a, Word: Copy + 'static, BUS, CS> SpiDeviceWrite for CriticalSectionDevice<'a, BUS, CS> +where + BUS: SpiBusWrite, + CS: OutputPin, +{ + fn write_transaction(&mut self, operations: &[&[Word]]) -> Result<(), Self::Error> { + critical_section::with(|cs| { + let bus = &mut *self.bus.borrow_ref_mut(cs); + + self.cs.set_low().map_err(DeviceError::Cs)?; + + let mut op_res = Ok(()); + for buf in operations { + if let Err(e) = bus.write(buf) { + op_res = Err(e); + break; + } + } + + // On failure, it's important to still flush and deassert CS. + let flush_res = bus.flush(); + let cs_res = self.cs.set_high(); + + op_res.map_err(DeviceError::Spi)?; + flush_res.map_err(DeviceError::Spi)?; + cs_res.map_err(DeviceError::Cs)?; + + Ok(()) + }) + } +} + +impl<'a, Word: Copy + 'static, BUS, CS> SpiDevice for CriticalSectionDevice<'a, BUS, CS> +where + BUS: SpiBus, + CS: OutputPin, +{ + fn transaction(&mut self, operations: &mut [Operation<'_, Word>]) -> Result<(), Self::Error> { + critical_section::with(|cs| { + let bus = &mut *self.bus.borrow_ref_mut(cs); + + self.cs.set_low().map_err(DeviceError::Cs)?; + + let op_res = operations.iter_mut().try_for_each(|op| match op { + Operation::Read(buf) => bus.read(buf), + Operation::Write(buf) => bus.write(buf), + Operation::Transfer(read, write) => bus.transfer(read, write), + Operation::TransferInPlace(buf) => bus.transfer_in_place(buf), + }); + + // On failure, it's important to still flush and deassert CS. + let flush_res = bus.flush(); + let cs_res = self.cs.set_high(); + + op_res.map_err(DeviceError::Spi)?; + flush_res.map_err(DeviceError::Spi)?; + cs_res.map_err(DeviceError::Cs)?; + + Ok(()) + }) + } +} diff --git a/embedded-hal-bus/src/spi/exclusive.rs b/embedded-hal-bus/src/spi/exclusive.rs new file mode 100644 index 000000000..fc7926eeb --- /dev/null +++ b/embedded-hal-bus/src/spi/exclusive.rs @@ -0,0 +1,117 @@ +//! SPI bus sharing mechanisms. + +use embedded_hal::digital::OutputPin; +use embedded_hal::spi::{ + ErrorType, Operation, SpiBus, SpiBusRead, SpiBusWrite, SpiDevice, SpiDeviceRead, SpiDeviceWrite, +}; + +use super::DeviceError; + +/// [`SpiDevice`] implementation with exclusive access to the bus (not shared). +/// +/// This is the most straightforward way of obtaining an [`SpiDevice`] from an [`SpiBus`](embedded_hal::spi::SpiBus), +/// ideal for when no sharing is required (only one SPI device is present on the bus). +pub struct ExclusiveDevice { + bus: BUS, + cs: CS, +} + +impl ExclusiveDevice { + /// Create a new ExclusiveDevice + pub fn new(bus: BUS, cs: CS) -> Self { + Self { bus, cs } + } +} + +impl ErrorType for ExclusiveDevice +where + BUS: ErrorType, + CS: OutputPin, +{ + type Error = DeviceError; +} + +impl SpiDeviceRead for ExclusiveDevice +where + BUS: SpiBusRead, + CS: OutputPin, +{ + fn read_transaction(&mut self, operations: &mut [&mut [Word]]) -> Result<(), Self::Error> { + self.cs.set_low().map_err(DeviceError::Cs)?; + + let mut op_res = Ok(()); + + for buf in operations { + if let Err(e) = self.bus.read(buf) { + op_res = Err(e); + break; + } + } + + // On failure, it's important to still flush and deassert CS. + let flush_res = self.bus.flush(); + let cs_res = self.cs.set_high(); + + op_res.map_err(DeviceError::Spi)?; + flush_res.map_err(DeviceError::Spi)?; + cs_res.map_err(DeviceError::Cs)?; + + Ok(()) + } +} + +impl SpiDeviceWrite for ExclusiveDevice +where + BUS: SpiBusWrite, + CS: OutputPin, +{ + fn write_transaction(&mut self, operations: &[&[Word]]) -> Result<(), Self::Error> { + self.cs.set_low().map_err(DeviceError::Cs)?; + + let mut op_res = Ok(()); + + for buf in operations { + if let Err(e) = self.bus.write(buf) { + op_res = Err(e); + break; + } + } + + // On failure, it's important to still flush and deassert CS. + let flush_res = self.bus.flush(); + let cs_res = self.cs.set_high(); + + op_res.map_err(DeviceError::Spi)?; + flush_res.map_err(DeviceError::Spi)?; + cs_res.map_err(DeviceError::Cs)?; + + Ok(()) + } +} + +impl SpiDevice for ExclusiveDevice +where + BUS: SpiBus, + CS: OutputPin, +{ + fn transaction(&mut self, operations: &mut [Operation<'_, Word>]) -> Result<(), Self::Error> { + self.cs.set_low().map_err(DeviceError::Cs)?; + + let op_res = operations.iter_mut().try_for_each(|op| match op { + Operation::Read(buf) => self.bus.read(buf), + Operation::Write(buf) => self.bus.write(buf), + Operation::Transfer(read, write) => self.bus.transfer(read, write), + Operation::TransferInPlace(buf) => self.bus.transfer_in_place(buf), + }); + + // On failure, it's important to still flush and deassert CS. + let flush_res = self.bus.flush(); + let cs_res = self.cs.set_high(); + + op_res.map_err(DeviceError::Spi)?; + flush_res.map_err(DeviceError::Spi)?; + cs_res.map_err(DeviceError::Cs)?; + + Ok(()) + } +} diff --git a/embedded-hal-bus/src/spi/mod.rs b/embedded-hal-bus/src/spi/mod.rs new file mode 100644 index 000000000..6ae803865 --- /dev/null +++ b/embedded-hal-bus/src/spi/mod.rs @@ -0,0 +1,37 @@ +//! `SpiDevice` implementations. + +use core::fmt::Debug; +use embedded_hal::spi::{Error, ErrorKind}; + +mod exclusive; +pub use exclusive::*; +mod refcell; +pub use refcell::*; +#[cfg(feature = "std")] +mod mutex; +#[cfg(feature = "std")] +pub use mutex::*; +mod critical_section; +pub use self::critical_section::*; + +/// Error type for [`ExclusiveDevice`] operations. +#[derive(Copy, Clone, Eq, PartialEq, Debug)] +pub enum DeviceError { + /// An inner SPI bus operation failed + Spi(BUS), + /// Asserting or deasserting CS failed + Cs(CS), +} + +impl Error for DeviceError +where + BUS: Error + Debug, + CS: Debug, +{ + fn kind(&self) -> ErrorKind { + match self { + Self::Spi(e) => e.kind(), + Self::Cs(_) => ErrorKind::ChipSelectFault, + } + } +} diff --git a/embedded-hal-bus/src/spi/mutex.rs b/embedded-hal-bus/src/spi/mutex.rs new file mode 100644 index 000000000..1bb693ded --- /dev/null +++ b/embedded-hal-bus/src/spi/mutex.rs @@ -0,0 +1,124 @@ +use embedded_hal::digital::OutputPin; +use embedded_hal::spi::{ + ErrorType, Operation, SpiBus, SpiBusRead, SpiBusWrite, SpiDevice, SpiDeviceRead, SpiDeviceWrite, +}; +use std::sync::Mutex; + +use super::DeviceError; + +/// `std` `Mutex`-based shared bus [`SpiDevice`] implementation. +/// +/// This allows for sharing an [`SpiBus`](embedded_hal::spi::SpiBus), obtaining multiple [`SpiDevice`] instances, +/// each with its own `CS` pin. +/// +/// Sharing is implemented with a `std` [`Mutex`](std::sync::Mutex). It allows a single bus across multiple threads, +/// with finer-grained locking than [`CriticalSectionDevice`](super::CriticalSectionDevice). The downside is +/// it is only available in `std` targets. +pub struct MutexDevice<'a, BUS, CS> { + bus: &'a Mutex, + cs: CS, +} + +impl<'a, BUS, CS> MutexDevice<'a, BUS, CS> { + /// Create a new ExclusiveDevice + pub fn new(bus: &'a Mutex, cs: CS) -> Self { + Self { bus, cs } + } +} + +impl<'a, BUS, CS> ErrorType for MutexDevice<'a, BUS, CS> +where + BUS: ErrorType, + CS: OutputPin, +{ + type Error = DeviceError; +} + +impl<'a, Word: Copy + 'static, BUS, CS> SpiDeviceRead for MutexDevice<'a, BUS, CS> +where + BUS: SpiBusRead, + CS: OutputPin, +{ + fn read_transaction(&mut self, operations: &mut [&mut [Word]]) -> Result<(), Self::Error> { + let bus = &mut *self.bus.lock().unwrap(); + + self.cs.set_low().map_err(DeviceError::Cs)?; + + let mut op_res = Ok(()); + for buf in operations { + if let Err(e) = bus.read(buf) { + op_res = Err(e); + break; + } + } + + // On failure, it's important to still flush and deassert CS. + let flush_res = bus.flush(); + let cs_res = self.cs.set_high(); + + op_res.map_err(DeviceError::Spi)?; + flush_res.map_err(DeviceError::Spi)?; + cs_res.map_err(DeviceError::Cs)?; + + Ok(()) + } +} + +impl<'a, Word: Copy + 'static, BUS, CS> SpiDeviceWrite for MutexDevice<'a, BUS, CS> +where + BUS: SpiBusWrite, + CS: OutputPin, +{ + fn write_transaction(&mut self, operations: &[&[Word]]) -> Result<(), Self::Error> { + let bus = &mut *self.bus.lock().unwrap(); + + self.cs.set_low().map_err(DeviceError::Cs)?; + + let mut op_res = Ok(()); + for buf in operations { + if let Err(e) = bus.write(buf) { + op_res = Err(e); + break; + } + } + + // On failure, it's important to still flush and deassert CS. + let flush_res = bus.flush(); + let cs_res = self.cs.set_high(); + + op_res.map_err(DeviceError::Spi)?; + flush_res.map_err(DeviceError::Spi)?; + cs_res.map_err(DeviceError::Cs)?; + + Ok(()) + } +} + +impl<'a, Word: Copy + 'static, BUS, CS> SpiDevice for MutexDevice<'a, BUS, CS> +where + BUS: SpiBus, + CS: OutputPin, +{ + fn transaction(&mut self, operations: &mut [Operation<'_, Word>]) -> Result<(), Self::Error> { + let bus = &mut *self.bus.lock().unwrap(); + + self.cs.set_low().map_err(DeviceError::Cs)?; + + let op_res = operations.iter_mut().try_for_each(|op| match op { + Operation::Read(buf) => bus.read(buf), + Operation::Write(buf) => bus.write(buf), + Operation::Transfer(read, write) => bus.transfer(read, write), + Operation::TransferInPlace(buf) => bus.transfer_in_place(buf), + }); + + // On failure, it's important to still flush and deassert CS. + let flush_res = bus.flush(); + let cs_res = self.cs.set_high(); + + op_res.map_err(DeviceError::Spi)?; + flush_res.map_err(DeviceError::Spi)?; + cs_res.map_err(DeviceError::Cs)?; + + Ok(()) + } +} diff --git a/embedded-hal-bus/src/spi/refcell.rs b/embedded-hal-bus/src/spi/refcell.rs new file mode 100644 index 000000000..47e29a879 --- /dev/null +++ b/embedded-hal-bus/src/spi/refcell.rs @@ -0,0 +1,124 @@ +use core::cell::RefCell; +use embedded_hal::digital::OutputPin; +use embedded_hal::spi::{ + ErrorType, Operation, SpiBus, SpiBusRead, SpiBusWrite, SpiDevice, SpiDeviceRead, SpiDeviceWrite, +}; + +use super::DeviceError; + +/// `RefCell`-based shared bus [`SpiDevice`] implementation. +/// +/// This allows for sharing an [`SpiBus`](embedded_hal::spi::SpiBus), obtaining multiple [`SpiDevice`] instances, +/// each with its own `CS` pin. +/// +/// Sharing is implemented with a `RefCell`. This means it has low overhead, but `RefCellDevice` instances are not `Send`, +/// so it only allows sharing within a single thread (interrupt priority level). If you need to share a bus across several +/// threads, use [`CriticalSectionDevice`](super::CriticalSectionDevice) instead. +pub struct RefCellDevice<'a, BUS, CS> { + bus: &'a RefCell, + cs: CS, +} + +impl<'a, BUS, CS> RefCellDevice<'a, BUS, CS> { + /// Create a new ExclusiveDevice + pub fn new(bus: &'a RefCell, cs: CS) -> Self { + Self { bus, cs } + } +} + +impl<'a, BUS, CS> ErrorType for RefCellDevice<'a, BUS, CS> +where + BUS: ErrorType, + CS: OutputPin, +{ + type Error = DeviceError; +} + +impl<'a, Word: Copy + 'static, BUS, CS> SpiDeviceRead for RefCellDevice<'a, BUS, CS> +where + BUS: SpiBusRead, + CS: OutputPin, +{ + fn read_transaction(&mut self, operations: &mut [&mut [Word]]) -> Result<(), Self::Error> { + let bus = &mut *self.bus.borrow_mut(); + + self.cs.set_low().map_err(DeviceError::Cs)?; + + let mut op_res = Ok(()); + for buf in operations { + if let Err(e) = bus.read(buf) { + op_res = Err(e); + break; + } + } + + // On failure, it's important to still flush and deassert CS. + let flush_res = bus.flush(); + let cs_res = self.cs.set_high(); + + op_res.map_err(DeviceError::Spi)?; + flush_res.map_err(DeviceError::Spi)?; + cs_res.map_err(DeviceError::Cs)?; + + Ok(()) + } +} + +impl<'a, Word: Copy + 'static, BUS, CS> SpiDeviceWrite for RefCellDevice<'a, BUS, CS> +where + BUS: SpiBusWrite, + CS: OutputPin, +{ + fn write_transaction(&mut self, operations: &[&[Word]]) -> Result<(), Self::Error> { + let bus = &mut *self.bus.borrow_mut(); + + self.cs.set_low().map_err(DeviceError::Cs)?; + + let mut op_res = Ok(()); + for buf in operations { + if let Err(e) = bus.write(buf) { + op_res = Err(e); + break; + } + } + + // On failure, it's important to still flush and deassert CS. + let flush_res = bus.flush(); + let cs_res = self.cs.set_high(); + + op_res.map_err(DeviceError::Spi)?; + flush_res.map_err(DeviceError::Spi)?; + cs_res.map_err(DeviceError::Cs)?; + + Ok(()) + } +} + +impl<'a, Word: Copy + 'static, BUS, CS> SpiDevice for RefCellDevice<'a, BUS, CS> +where + BUS: SpiBus, + CS: OutputPin, +{ + fn transaction(&mut self, operations: &mut [Operation<'_, Word>]) -> Result<(), Self::Error> { + let bus = &mut *self.bus.borrow_mut(); + + self.cs.set_low().map_err(DeviceError::Cs)?; + + let op_res = operations.iter_mut().try_for_each(|op| match op { + Operation::Read(buf) => bus.read(buf), + Operation::Write(buf) => bus.write(buf), + Operation::Transfer(read, write) => bus.transfer(read, write), + Operation::TransferInPlace(buf) => bus.transfer_in_place(buf), + }); + + // On failure, it's important to still flush and deassert CS. + let flush_res = bus.flush(); + let cs_res = self.cs.set_high(); + + op_res.map_err(DeviceError::Spi)?; + flush_res.map_err(DeviceError::Spi)?; + cs_res.map_err(DeviceError::Cs)?; + + Ok(()) + } +} From 756b05519e88a0219bc731573b86c175eda6dc63 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sat, 1 Apr 2023 10:52:48 +0200 Subject: [PATCH 028/199] spi: Add changelog for transaction changes. --- embedded-hal-async/CHANGELOG.md | 1 + embedded-hal/CHANGELOG.md | 1 + 2 files changed, 2 insertions(+) diff --git a/embedded-hal-async/CHANGELOG.md b/embedded-hal-async/CHANGELOG.md index 771ce2a24..8ee28411f 100644 --- a/embedded-hal-async/CHANGELOG.md +++ b/embedded-hal-async/CHANGELOG.md @@ -14,6 +14,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - delay: make infallible. - i2c: remove `_iter()` methods. - i2c: add default implementations for all methods based on `transaction()`. +- spi: SpiDevice transaction now takes an operation slice instead of a closure ## [v0.2.0-alpha.0] - 2022-11-23 diff --git a/embedded-hal/CHANGELOG.md b/embedded-hal/CHANGELOG.md index b1e3e53d8..ab6f2c575 100644 --- a/embedded-hal/CHANGELOG.md +++ b/embedded-hal/CHANGELOG.md @@ -16,6 +16,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - i2c: remove `_iter()` methods. - i2c: add default implementations for all methods based on `transaction()`. - i2c: document guidelines for shared bus usage. +- spi: SpiDevice transaction now takes an operation slice instead of a closure ## [v1.0.0-alpha.9] - 2022-09-28 From 76c66f3799ac201448206b3f15c3d704896ac843 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 4 Apr 2023 22:09:17 +0200 Subject: [PATCH 029/199] Release e-h v1.0.0-alpha.10, e-h-async v0.2.0-alpha.1, e-h-bus v0.1.0-alpha.2, e-h-nb v1.0.0-alpha.2. --- embedded-hal-async/CHANGELOG.md | 6 +++++- embedded-hal-async/Cargo.toml | 4 ++-- embedded-hal-bus/CHANGELOG.md | 8 +++++++- embedded-hal-bus/Cargo.toml | 4 ++-- embedded-hal-bus/README.md | 2 +- embedded-hal-nb/CHANGELOG.md | 8 +++++++- embedded-hal-nb/Cargo.toml | 4 ++-- embedded-hal/CHANGELOG.md | 4 ++++ embedded-hal/Cargo.toml | 4 ++-- 9 files changed, 32 insertions(+), 12 deletions(-) diff --git a/embedded-hal-async/CHANGELOG.md b/embedded-hal-async/CHANGELOG.md index 8ee28411f..b87c600c2 100644 --- a/embedded-hal-async/CHANGELOG.md +++ b/embedded-hal-async/CHANGELOG.md @@ -7,10 +7,13 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] +## [v0.2.0-alpha.1] - 2023-04-04 + ### Added - Added a `serial::Write` trait. ### Changed +- Updated `embedded-hal` to version `1.0.0-alpha.10`. - delay: make infallible. - i2c: remove `_iter()` methods. - i2c: add default implementations for all methods based on `transaction()`. @@ -46,7 +49,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/). First release to crates.io -[Unreleased]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-async-v0.2.0-alpha.0...HEAD +[Unreleased]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-async-v0.2.0-alpha.1...HEAD +[v0.2.0-alpha.1]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-async-v0.2.0-alpha.0...embedded-hal-async-v0.2.0-alpha.1 [v0.2.0-alpha.0]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-async-v0.1.0-alpha.3...embedded-hal-async-v0.2.0-alpha.0 [v0.1.0-alpha.3]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-async-v0.1.0-alpha.2...embedded-hal-async-v0.1.0-alpha.3 [v0.1.0-alpha.2]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-async-v0.1.0-alpha.1...embedded-hal-async-v0.1.0-alpha.2 diff --git a/embedded-hal-async/Cargo.toml b/embedded-hal-async/Cargo.toml index 31b746e5b..a230a0f68 100644 --- a/embedded-hal-async/Cargo.toml +++ b/embedded-hal-async/Cargo.toml @@ -11,8 +11,8 @@ license = "MIT OR Apache-2.0" name = "embedded-hal-async" readme = "README.md" repository = "https://github.com/rust-embedded/embedded-hal" -version = "0.2.0-alpha.0" +version = "0.2.0-alpha.1" rust-version = "1.65.0" [dependencies] -embedded-hal = { version = "=1.0.0-alpha.9", path = "../embedded-hal" } +embedded-hal = { version = "=1.0.0-alpha.10", path = "../embedded-hal" } diff --git a/embedded-hal-bus/CHANGELOG.md b/embedded-hal-bus/CHANGELOG.md index 97ee0d8d1..45abc9b4d 100644 --- a/embedded-hal-bus/CHANGELOG.md +++ b/embedded-hal-bus/CHANGELOG.md @@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] +## [v0.1.0-alpha.2] - 2023-04-04 + +### Changed +- Updated `embedded-hal` to version `1.0.0-alpha.10`. + ### Added - i2c: add bus sharing implementations. - spi: add bus sharing implementations. @@ -20,6 +25,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). First release to crates.io -[Unreleased]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-bus-v0.1.0-alpha.1...HEAD +[Unreleased]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-bus-v0.1.0-alpha.2...HEAD +[v0.1.0-alpha.2]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-bus-v0.1.0-alpha.1...embedded-hal-bus-v0.1.0-alpha.2 [v0.1.0-alpha.1]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-bus-v0.1.0-alpha.0...embedded-hal-bus-v0.1.0-alpha.1 [v0.1.0-alpha.0]: https://github.com/rust-embedded/embedded-hal/tree/embedded-hal-bus-v0.1.0-alpha.0 diff --git a/embedded-hal-bus/Cargo.toml b/embedded-hal-bus/Cargo.toml index 94d471848..2ad3815da 100644 --- a/embedded-hal-bus/Cargo.toml +++ b/embedded-hal-bus/Cargo.toml @@ -11,11 +11,11 @@ license = "MIT OR Apache-2.0" name = "embedded-hal-bus" readme = "README.md" repository = "https://github.com/rust-embedded/embedded-hal" -version = "0.1.0-alpha.1" +version = "0.1.0-alpha.2" [features] std = [] [dependencies] -embedded-hal = { version = "=1.0.0-alpha.9", path = "../embedded-hal" } +embedded-hal = { version = "=1.0.0-alpha.10", path = "../embedded-hal" } critical-section = { version = "1.0" } diff --git a/embedded-hal-bus/README.md b/embedded-hal-bus/README.md index 16ee78842..81bf52c2c 100644 --- a/embedded-hal-bus/README.md +++ b/embedded-hal-bus/README.md @@ -15,7 +15,7 @@ This project is developed and maintained by the [HAL team](https://github.com/ru To support bus sharing, `embedded-hal` provides the `SpiBus` and `SpiDevice` traits. `SpiBus` represents an entire bus, while `SpiDevice` represents a device on that bus. For further details on these traits, please consult the -[`embedded-hal` documentation](https://docs.rs/embedded-hal/1.0.0-alpha.9/embedded_hal/spi/index.html). +[`embedded-hal` documentation](https://docs.rs/embedded-hal/1.0.0-alpha.10/embedded_hal/spi/index.html). `embedded-hal` trait implementations for microcontrollers should implement the `SpiBus` trait. However, device drivers should use the `SpiDevice` traits, _not the `SpiBus` traits_ if at all possible diff --git a/embedded-hal-nb/CHANGELOG.md b/embedded-hal-nb/CHANGELOG.md index 5634771f3..0bc3a63f9 100644 --- a/embedded-hal-nb/CHANGELOG.md +++ b/embedded-hal-nb/CHANGELOG.md @@ -9,6 +9,11 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ... +## [v1.0.0-alpha.2] - 2023-04-04 + +### Changed +- Updated `embedded-hal` to version `1.0.0-alpha.10`. + ## [v1.0.0-alpha.1] - 2022-09-28 ### Changed @@ -18,6 +23,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). First release to crates.io -[Unreleased]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-nb-v1.0.0-alpha.1...HEAD +[Unreleased]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-nb-v1.0.0-alpha.2...HEAD +[v1.0.0-alpha.2]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-nb-v1.0.0-alpha.1...embedded-hal-nb-v1.0.0-alpha.2which [v1.0.0-alpha.1]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-nb-v1.0.0-alpha.0...embedded-hal-nb-v1.0.0-alpha.1 [v1.0.0-alpha.0]: https://github.com/rust-embedded/embedded-hal/tree/embedded-hal-nb-v1.0.0-alpha.0 diff --git a/embedded-hal-nb/Cargo.toml b/embedded-hal-nb/Cargo.toml index 2c54b129d..eefaa19a2 100644 --- a/embedded-hal-nb/Cargo.toml +++ b/embedded-hal-nb/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "embedded-hal-nb" -version = "1.0.0-alpha.1" +version = "1.0.0-alpha.2" edition = "2021" categories = ["embedded", "hardware-support", "no-std"] @@ -12,7 +12,7 @@ readme = "README.md" repository = "https://github.com/rust-embedded/embedded-hal" [dependencies] -embedded-hal = { version = "=1.0.0-alpha.9", path = "../embedded-hal" } +embedded-hal = { version = "=1.0.0-alpha.10", path = "../embedded-hal" } nb = "1" [dev-dependencies] diff --git a/embedded-hal/CHANGELOG.md b/embedded-hal/CHANGELOG.md index ab6f2c575..aab1418b8 100644 --- a/embedded-hal/CHANGELOG.md +++ b/embedded-hal/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] +## [v1.0.0-alpha.10] - 2023-04-04 + +*** This is (also) an alpha release with breaking changes (sorry) *** + ### Added - Added `pwm::SetDutyCycle` trait. diff --git a/embedded-hal/Cargo.toml b/embedded-hal/Cargo.toml index ef7be8315..5e8307061 100644 --- a/embedded-hal/Cargo.toml +++ b/embedded-hal/Cargo.toml @@ -2,7 +2,7 @@ authors = [ "The Embedded HAL Team ", "Jorge Aparicio ", - "Jonathan 'theJPster' Pallant " + "Jonathan 'theJPster' Pallant ", ] categories = ["asynchronous", "embedded", "hardware-support", "no-std"] description = " A Hardware Abstraction Layer (HAL) for embedded systems " @@ -13,4 +13,4 @@ license = "MIT OR Apache-2.0" name = "embedded-hal" readme = "README.md" repository = "https://github.com/rust-embedded/embedded-hal" -version = "1.0.0-alpha.9" +version = "1.0.0-alpha.10" From 28df44c1f7e93d83a4ffd2b7405a5b414ddd0e0d Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 2 May 2023 20:08:00 +0200 Subject: [PATCH 030/199] spi: clarify flushing/blocking behavior. Fixes #455. --- embedded-hal/src/spi.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/embedded-hal/src/spi.rs b/embedded-hal/src/spi.rs index f8f14efa4..e8310d5ce 100644 --- a/embedded-hal/src/spi.rs +++ b/embedded-hal/src/spi.rs @@ -142,7 +142,11 @@ //! # Flushing //! //! To improve performance, [`SpiBus`] implementations are allowed to return before the operation is finished, i.e. when the bus is still not -//! idle. +//! idle. This allows pipelining SPI transfers with CPU work. +//! +//! When calling another method when a previous operation is still in progress, implementations can either wait for the previous operation +//! to finish, or enqueue the new one, but they must not return a "busy" error. Users must be able to do multiple method calls in a row +//! and have them executed "as if" they were done sequentially, without having to check for "busy" errors. //! //! When using a [`SpiBus`], call [`flush`](SpiBusFlush::flush) to wait for operations to actually finish. Examples of situations //! where this is needed are: From 215d7b4fa04be4f4c6f62b34c855ac53f372f194 Mon Sep 17 00:00:00 2001 From: dimpolo <33688001+dimpolo@users.noreply.github.com> Date: Tue, 2 May 2023 22:20:10 +0200 Subject: [PATCH 031/199] embedded-can: make `StandardId::as_raw` and `ExtendedId::as_raw` const --- embedded-can/src/id.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/embedded-can/src/id.rs b/embedded-can/src/id.rs index 88ddaa0a2..6fb377190 100644 --- a/embedded-can/src/id.rs +++ b/embedded-can/src/id.rs @@ -34,7 +34,7 @@ impl StandardId { /// Returns this CAN Identifier as a raw 16-bit integer. #[inline] - pub fn as_raw(&self) -> u16 { + pub const fn as_raw(&self) -> u16 { self.0 } } @@ -73,7 +73,7 @@ impl ExtendedId { /// Returns this CAN Identifier as a raw 32-bit integer. #[inline] - pub fn as_raw(&self) -> u32 { + pub const fn as_raw(&self) -> u32 { self.0 } From d187dc0fcb3c6f35f8a3e2eb613e60110334810a Mon Sep 17 00:00:00 2001 From: Thomas Gilligan Date: Wed, 3 May 2023 19:31:29 +1000 Subject: [PATCH 032/199] Add a doc example for i2c::RefCellDevice --- embedded-hal-bus/src/i2c/refcell.rs | 57 +++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/embedded-hal-bus/src/i2c/refcell.rs b/embedded-hal-bus/src/i2c/refcell.rs index cc72df692..ffb1c3f59 100644 --- a/embedded-hal-bus/src/i2c/refcell.rs +++ b/embedded-hal-bus/src/i2c/refcell.rs @@ -12,6 +12,63 @@ pub struct RefCellDevice<'a, T> { impl<'a, T> RefCellDevice<'a, T> { /// Create a new `RefCellDevice` + /// + /// # Examples + /// + /// Assuming there is a pressure sensor with address `0x42` on the same bus as a temperature sensor + /// with address `0x20`; [`RefCellDevice`] can be used to give access to both of these sensors + /// from a single `i2c` instance. + /// + /// ``` + /// use embedded_hal_bus::i2c; + /// use core::cell::RefCell; + /// # use embedded_hal::i2c::{self as hali2c, SevenBitAddress, TenBitAddress, I2c, Operation, ErrorKind}; + /// # pub struct Sensor { + /// # i2c: I2C, + /// # address: u8, + /// # } + /// # impl Sensor { + /// # pub const fn new(i2c: I2C, address: u8) -> Self { + /// # Self { i2c, address } + /// # } + /// # } + /// # type PressureSensor = Sensor; + /// # type TemperatureSensor = Sensor; + /// # pub struct I2c0; + /// # #[derive(Debug, Copy, Clone, Eq, PartialEq)] + /// # pub enum Error { } + /// # impl hali2c::Error for Error { + /// # fn kind(&self) -> hali2c::ErrorKind { + /// # ErrorKind::Other + /// # } + /// # } + /// # impl hali2c::ErrorType for I2c0 { + /// # type Error = Error; + /// # } + /// # impl I2c for I2c0 { + /// # fn transaction(&mut self, address: u8, operations: &mut [Operation<'_>]) -> Result<(), Self::Error> { + /// # Ok(()) + /// # } + /// # } + /// # struct Hal; + /// # impl Hal { + /// # fn i2c(&self) -> I2c0 { + /// # I2c0 + /// # } + /// # } + /// # let hal = Hal; + /// + /// let i2c = hal.i2c(); + /// let i2c_ref_cell = RefCell::new(i2c); + /// let mut temperature_sensor = TemperatureSensor::new( + /// i2c::RefCellDevice::new(&i2c_ref_cell), + /// 0x20, + /// ); + /// let mut pressure_sensor = PressureSensor::new( + /// i2c::RefCellDevice::new(&i2c_ref_cell), + /// 0x42, + /// ); + /// ``` pub fn new(bus: &'a RefCell) -> Self { Self { bus } } From baefcf241baf2b366a9a2f11fe382428a988ba64 Mon Sep 17 00:00:00 2001 From: Thomas Gilligan Date: Wed, 3 May 2023 20:52:23 +1000 Subject: [PATCH 033/199] Put i2c bus example on struct, fix doc test --- embedded-hal-bus/src/i2c/refcell.rs | 114 ++++++++++++++-------------- 1 file changed, 57 insertions(+), 57 deletions(-) diff --git a/embedded-hal-bus/src/i2c/refcell.rs b/embedded-hal-bus/src/i2c/refcell.rs index ffb1c3f59..d9a72aadd 100644 --- a/embedded-hal-bus/src/i2c/refcell.rs +++ b/embedded-hal-bus/src/i2c/refcell.rs @@ -6,69 +6,69 @@ use embedded_hal::i2c::{ErrorType, I2c}; /// Sharing is implemented with a `RefCell`. This means it has low overhead, but `RefCellDevice` instances are not `Send`, /// so it only allows sharing within a single thread (interrupt priority level). If you need to share a bus across several /// threads, use [`CriticalSectionDevice`](super::CriticalSectionDevice) instead. +/// +/// # Examples +/// +/// Assuming there is a pressure sensor with address `0x42` on the same bus as a temperature sensor +/// with address `0x20`; [`RefCellDevice`] can be used to give access to both of these sensors +/// from a single `i2c` instance. +/// +/// ``` +/// use embedded_hal_bus::i2c; +/// use core::cell::RefCell; +/// # use embedded_hal::i2c::{self as hali2c, SevenBitAddress, TenBitAddress, I2c, Operation, ErrorKind}; +/// # pub struct Sensor { +/// # i2c: I2C, +/// # address: u8, +/// # } +/// # impl Sensor { +/// # pub const fn new(i2c: I2C, address: u8) -> Self { +/// # Self { i2c, address } +/// # } +/// # } +/// # type PressureSensor = Sensor; +/// # type TemperatureSensor = Sensor; +/// # pub struct I2c0; +/// # #[derive(Debug, Copy, Clone, Eq, PartialEq)] +/// # pub enum Error { } +/// # impl hali2c::Error for Error { +/// # fn kind(&self) -> hali2c::ErrorKind { +/// # ErrorKind::Other +/// # } +/// # } +/// # impl hali2c::ErrorType for I2c0 { +/// # type Error = Error; +/// # } +/// # impl I2c for I2c0 { +/// # fn transaction(&mut self, address: u8, operations: &mut [Operation<'_>]) -> Result<(), Self::Error> { +/// # Ok(()) +/// # } +/// # } +/// # struct Hal; +/// # impl Hal { +/// # fn i2c(&self) -> I2c0 { +/// # I2c0 +/// # } +/// # } +/// # let hal = Hal; +/// +/// let i2c = hal.i2c(); +/// let i2c_ref_cell = RefCell::new(i2c); +/// let mut temperature_sensor = TemperatureSensor::new( +/// i2c::RefCellDevice::new(&i2c_ref_cell), +/// 0x20, +/// ); +/// let mut pressure_sensor = PressureSensor::new( +/// i2c::RefCellDevice::new(&i2c_ref_cell), +/// 0x42, +/// ); +/// ``` pub struct RefCellDevice<'a, T> { bus: &'a RefCell, } impl<'a, T> RefCellDevice<'a, T> { /// Create a new `RefCellDevice` - /// - /// # Examples - /// - /// Assuming there is a pressure sensor with address `0x42` on the same bus as a temperature sensor - /// with address `0x20`; [`RefCellDevice`] can be used to give access to both of these sensors - /// from a single `i2c` instance. - /// - /// ``` - /// use embedded_hal_bus::i2c; - /// use core::cell::RefCell; - /// # use embedded_hal::i2c::{self as hali2c, SevenBitAddress, TenBitAddress, I2c, Operation, ErrorKind}; - /// # pub struct Sensor { - /// # i2c: I2C, - /// # address: u8, - /// # } - /// # impl Sensor { - /// # pub const fn new(i2c: I2C, address: u8) -> Self { - /// # Self { i2c, address } - /// # } - /// # } - /// # type PressureSensor = Sensor; - /// # type TemperatureSensor = Sensor; - /// # pub struct I2c0; - /// # #[derive(Debug, Copy, Clone, Eq, PartialEq)] - /// # pub enum Error { } - /// # impl hali2c::Error for Error { - /// # fn kind(&self) -> hali2c::ErrorKind { - /// # ErrorKind::Other - /// # } - /// # } - /// # impl hali2c::ErrorType for I2c0 { - /// # type Error = Error; - /// # } - /// # impl I2c for I2c0 { - /// # fn transaction(&mut self, address: u8, operations: &mut [Operation<'_>]) -> Result<(), Self::Error> { - /// # Ok(()) - /// # } - /// # } - /// # struct Hal; - /// # impl Hal { - /// # fn i2c(&self) -> I2c0 { - /// # I2c0 - /// # } - /// # } - /// # let hal = Hal; - /// - /// let i2c = hal.i2c(); - /// let i2c_ref_cell = RefCell::new(i2c); - /// let mut temperature_sensor = TemperatureSensor::new( - /// i2c::RefCellDevice::new(&i2c_ref_cell), - /// 0x20, - /// ); - /// let mut pressure_sensor = PressureSensor::new( - /// i2c::RefCellDevice::new(&i2c_ref_cell), - /// 0x42, - /// ); - /// ``` pub fn new(bus: &'a RefCell) -> Self { Self { bus } } From 6a0c442fc93ccb2440da2067245653d342d97c46 Mon Sep 17 00:00:00 2001 From: Thomas Gilligan Date: Thu, 4 May 2023 01:44:40 +1000 Subject: [PATCH 034/199] =?UTF-8?q?Actually=20commit=20doc=20test=20fix=20?= =?UTF-8?q?=F0=9F=A5=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- embedded-hal-bus/src/i2c/refcell.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/embedded-hal-bus/src/i2c/refcell.rs b/embedded-hal-bus/src/i2c/refcell.rs index d9a72aadd..3d6564434 100644 --- a/embedded-hal-bus/src/i2c/refcell.rs +++ b/embedded-hal-bus/src/i2c/refcell.rs @@ -22,12 +22,12 @@ use embedded_hal::i2c::{ErrorType, I2c}; /// # address: u8, /// # } /// # impl Sensor { -/// # pub const fn new(i2c: I2C, address: u8) -> Self { +/// # pub fn new(i2c: I2C, address: u8) -> Self { /// # Self { i2c, address } /// # } /// # } -/// # type PressureSensor = Sensor; -/// # type TemperatureSensor = Sensor; +/// # type PressureSensor = Sensor; +/// # type TemperatureSensor = Sensor; /// # pub struct I2c0; /// # #[derive(Debug, Copy, Clone, Eq, PartialEq)] /// # pub enum Error { } From 1567f258bfa72146cb0f0d94de4f1d63574ff42c Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 10 May 2023 22:22:57 +0200 Subject: [PATCH 035/199] spi: remove write-only and read-only traits. --- embedded-hal-async/CHANGELOG.md | 3 + embedded-hal-async/src/spi.rs | 304 +++---------------- embedded-hal-bus/src/spi/critical_section.rs | 68 +---- embedded-hal-bus/src/spi/exclusive.rs | 62 +--- embedded-hal-bus/src/spi/mutex.rs | 64 +--- embedded-hal-bus/src/spi/refcell.rs | 64 +--- embedded-hal/CHANGELOG.md | 3 + embedded-hal/src/spi.rs | 197 ++++-------- 8 files changed, 109 insertions(+), 656 deletions(-) diff --git a/embedded-hal-async/CHANGELOG.md b/embedded-hal-async/CHANGELOG.md index b87c600c2..5da34a3ca 100644 --- a/embedded-hal-async/CHANGELOG.md +++ b/embedded-hal-async/CHANGELOG.md @@ -7,6 +7,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] +### Removed +- spi: removed read-only and write-only traits. + ## [v0.2.0-alpha.1] - 2023-04-04 ### Added diff --git a/embedded-hal-async/src/spi.rs b/embedded-hal-async/src/spi.rs index 4d215c16a..0c99d79bf 100644 --- a/embedded-hal-async/src/spi.rs +++ b/embedded-hal-async/src/spi.rs @@ -8,21 +8,19 @@ pub use embedded_hal::spi::{ Error, ErrorKind, ErrorType, Mode, Operation, Phase, Polarity, MODE_0, MODE_1, MODE_2, MODE_3, }; -/// SPI read-only device trait +/// SPI device trait /// -/// `SpiDeviceRead` represents ownership over a single SPI device on a (possibly shared) bus, selected +/// `SpiDevice` represents ownership over a single SPI device on a (possibly shared) bus, selected /// with a CS (Chip Select) pin. /// /// See (the docs on embedded-hal)[embedded_hal::spi] for important information on SPI Bus vs Device traits. -/// -/// See the [module-level documentation](self) for important usage information. -pub trait SpiDeviceRead: ErrorType { - /// Perform a read transaction against the device. +pub trait SpiDevice: ErrorType { + /// Perform a transaction against the device. /// /// - Locks the bus /// - Asserts the CS (Chip Select) pin. /// - Performs all the operations. - /// - [Flushes](SpiBusFlush::flush) the bus. + /// - [Flushes](SpiBus::flush) the bus. /// - Deasserts the CS pin. /// - Unlocks the bus. /// @@ -32,85 +30,28 @@ pub trait SpiDeviceRead: ErrorType { /// /// On bus errors the implementation should try to deassert CS. /// If an error occurs while deasserting CS the bus error should take priority as the return value. - async fn read_transaction(&mut self, operations: &mut [&mut [Word]]) - -> Result<(), Self::Error>; + async fn transaction( + &mut self, + operations: &mut [Operation<'_, Word>], + ) -> Result<(), Self::Error>; /// Do a read within a transaction. /// /// This is a convenience method equivalent to `device.read_transaction(&mut [buf])`. /// - /// See also: [`SpiDeviceRead::read_transaction`], [`SpiBusRead::read`] + /// See also: [`SpiDevice::transaction`], [`SpiDevice::read`] async fn read(&mut self, buf: &mut [Word]) -> Result<(), Self::Error> { - self.read_transaction(&mut [buf]).await + self.transaction(&mut [Operation::Read(buf)]).await } -} - -/// SPI write-only device trait -/// -/// `SpiDeviceWrite` represents ownership over a single SPI device on a (possibly shared) bus, selected -/// with a CS (Chip Select) pin. -/// -/// See (the docs on embedded-hal)[embedded_hal::spi] for important information on SPI Bus vs Device traits. -/// -/// See the [module-level documentation](self) for important usage information. -pub trait SpiDeviceWrite: ErrorType { - /// Perform a write transaction against the device. - /// - /// - Locks the bus - /// - Asserts the CS (Chip Select) pin. - /// - Performs all the operations. - /// - [Flushes](SpiBusFlush::flush) the bus. - /// - Deasserts the CS pin. - /// - Unlocks the bus. - /// - /// The locking mechanism is implementation-defined. The only requirement is it must prevent two - /// transactions from executing concurrently against the same bus. Examples of implementations are: - /// critical sections, blocking mutexes, returning an error or panicking if the bus is already busy. - /// - /// On bus errors the implementation should try to deassert CS. - /// If an error occurs while deasserting CS the bus error should take priority as the return value. - async fn write_transaction(&mut self, operations: &[&[Word]]) -> Result<(), Self::Error>; /// Do a write within a transaction. /// /// This is a convenience method equivalent to `device.write_transaction(&mut [buf])`. /// - /// See also: [`SpiDeviceWrite::write_transaction`], [`SpiBusWrite::write`] + /// See also: [`SpiDevice::transaction`], [`SpiDevice::write`] async fn write(&mut self, buf: &[Word]) -> Result<(), Self::Error> { - self.write_transaction(&[buf]).await + self.transaction(&mut [Operation::Write(buf)]).await } -} - -/// SPI device trait -/// -/// `SpiDevice` represents ownership over a single SPI device on a (possibly shared) bus, selected -/// with a CS (Chip Select) pin. -/// -/// See (the docs on embedded-hal)[embedded_hal::spi] for important information on SPI Bus vs Device traits. -/// -/// See the [module-level documentation](self) for important usage information. -pub trait SpiDevice: - SpiDeviceRead + SpiDeviceWrite + ErrorType -{ - /// Perform a transaction against the device. - /// - /// - Locks the bus - /// - Asserts the CS (Chip Select) pin. - /// - Performs all the operations. - /// - [Flushes](SpiBusFlush::flush) the bus. - /// - Deasserts the CS pin. - /// - Unlocks the bus. - /// - /// The locking mechanism is implementation-defined. The only requirement is it must prevent two - /// transactions from executing concurrently against the same bus. Examples of implementations are: - /// critical sections, blocking mutexes, returning an error or panicking if the bus is already busy. - /// - /// On bus errors the implementation should try to deassert CS. - /// If an error occurs while deasserting CS the bus error should take priority as the return value. - async fn transaction( - &mut self, - operations: &mut [Operation<'_, Word>], - ) -> Result<(), Self::Error>; /// Do a transfer within a transaction. /// @@ -133,36 +74,21 @@ pub trait SpiDevice: } } -impl> SpiDeviceRead for &mut T { - async fn read_transaction( +impl> SpiDevice for &mut T { + async fn transaction( &mut self, - operations: &mut [&mut [Word]], + operations: &mut [Operation<'_, Word>], ) -> Result<(), Self::Error> { - T::read_transaction(self, operations).await + T::transaction(self, operations).await } async fn read(&mut self, buf: &mut [Word]) -> Result<(), Self::Error> { T::read(self, buf).await } -} - -impl> SpiDeviceWrite for &mut T { - async fn write_transaction(&mut self, operations: &[&[Word]]) -> Result<(), Self::Error> { - T::write_transaction(self, operations).await - } async fn write(&mut self, buf: &[Word]) -> Result<(), Self::Error> { T::write(self, buf).await } -} - -impl> SpiDevice for &mut T { - async fn transaction( - &mut self, - operations: &mut [Operation<'_, Word>], - ) -> Result<(), Self::Error> { - T::transaction(self, operations).await - } async fn transfer(&mut self, read: &mut [Word], write: &[Word]) -> Result<(), Self::Error> { T::transfer(self, read, write).await @@ -173,22 +99,12 @@ impl> SpiDevice for &mut T { } } -/// Flush support for SPI bus -pub trait SpiBusFlush: ErrorType { - /// Wait until all operations have completed and the bus is idle. - /// - /// See (the docs on embedded-hal)[embedded_hal::spi] for information on flushing. - async fn flush(&mut self) -> Result<(), Self::Error>; -} - -impl SpiBusFlush for &mut T { - async fn flush(&mut self) -> Result<(), Self::Error> { - T::flush(self).await - } -} - -/// Read-only SPI bus -pub trait SpiBusRead: SpiBusFlush { +/// SPI bus +/// +/// `SpiBus` represents **exclusive ownership** over the whole SPI bus, with SCK, MOSI and MISO pins. +/// +/// See (the docs on embedded-hal)[embedded_hal::spi] for important information on SPI Bus vs Device traits. +pub trait SpiBus: ErrorType { /// Read `words` from the slave. /// /// The word value sent on MOSI during reading is implementation-defined, @@ -197,35 +113,13 @@ pub trait SpiBusRead: SpiBusFlush { /// Implementations are allowed to return before the operation is /// complete. See (the docs on embedded-hal)[embedded_hal::spi] for details on flushing. async fn read(&mut self, words: &mut [Word]) -> Result<(), Self::Error>; -} - -impl, Word: 'static + Copy> SpiBusRead for &mut T { - async fn read(&mut self, words: &mut [Word]) -> Result<(), Self::Error> { - T::read(self, words).await - } -} -/// Write-only SPI -pub trait SpiBusWrite: SpiBusFlush { /// Write `words` to the slave, ignoring all the incoming words /// /// Implementations are allowed to return before the operation is /// complete. See (the docs on embedded-hal)[embedded_hal::spi] for details on flushing. async fn write(&mut self, words: &[Word]) -> Result<(), Self::Error>; -} - -impl, Word: 'static + Copy> SpiBusWrite for &mut T { - async fn write(&mut self, words: &[Word]) -> Result<(), Self::Error> { - T::write(self, words).await - } -} -/// Read-write SPI bus -/// -/// `SpiBus` represents **exclusive ownership** over the whole SPI bus, with SCK, MOSI and MISO pins. -/// -/// See (the docs on embedded-hal)[embedded_hal::spi] for important information on SPI Bus vs Device traits. -pub trait SpiBus: SpiBusRead + SpiBusWrite { /// Write and read simultaneously. `write` is written to the slave on MOSI and /// words received on MISO are stored in `read`. /// @@ -237,11 +131,7 @@ pub trait SpiBus: SpiBusRead + SpiBusWrite( - &'a mut self, - read: &'a mut [Word], - write: &'a [Word], - ) -> Result<(), Self::Error>; + async fn transfer(&mut self, read: &mut [Word], write: &[Word]) -> Result<(), Self::Error>; /// Write and read simultaneously. The contents of `words` are /// written to the slave, and the received words are stored into the same @@ -249,21 +139,34 @@ pub trait SpiBus: SpiBusRead + SpiBusWrite(&'a mut self, words: &'a mut [Word]) -> Result<(), Self::Error>; + async fn transfer_in_place(&mut self, words: &mut [Word]) -> Result<(), Self::Error>; + + /// Wait until all operations have completed and the bus is idle. + /// + /// See (the docs on embedded-hal)[embedded_hal::spi] for information on flushing. + async fn flush(&mut self) -> Result<(), Self::Error>; } impl, Word: 'static + Copy> SpiBus for &mut T { - async fn transfer<'a>( - &'a mut self, - read: &'a mut [Word], - write: &'a [Word], - ) -> Result<(), Self::Error> { + async fn read(&mut self, words: &mut [Word]) -> Result<(), Self::Error> { + T::read(self, words).await + } + + async fn write(&mut self, words: &[Word]) -> Result<(), Self::Error> { + T::write(self, words).await + } + + async fn transfer(&mut self, read: &mut [Word], write: &[Word]) -> Result<(), Self::Error> { T::transfer(self, read, write).await } - async fn transfer_in_place<'a>(&'a mut self, words: &'a mut [Word]) -> Result<(), Self::Error> { + async fn transfer_in_place(&mut self, words: &mut [Word]) -> Result<(), Self::Error> { T::transfer_in_place(self, words).await } + + async fn flush(&mut self) -> Result<(), Self::Error> { + T::flush(self).await + } } /// Error type for [`ExclusiveDevice`] operations. @@ -312,64 +215,6 @@ where type Error = ExclusiveDeviceError; } -impl blocking::SpiDeviceRead for ExclusiveDevice -where - BUS: blocking::SpiBusRead, - CS: OutputPin, -{ - fn read_transaction(&mut self, operations: &mut [&mut [Word]]) -> Result<(), Self::Error> { - self.cs.set_low().map_err(ExclusiveDeviceError::Cs)?; - - let mut op_res = Ok(()); - - for buf in operations { - if let Err(e) = self.bus.read(buf) { - op_res = Err(e); - break; - } - } - - // On failure, it's important to still flush and deassert CS. - let flush_res = self.bus.flush(); - let cs_res = self.cs.set_high(); - - op_res.map_err(ExclusiveDeviceError::Spi)?; - flush_res.map_err(ExclusiveDeviceError::Spi)?; - cs_res.map_err(ExclusiveDeviceError::Cs)?; - - Ok(()) - } -} - -impl blocking::SpiDeviceWrite for ExclusiveDevice -where - BUS: blocking::SpiBusWrite, - CS: OutputPin, -{ - fn write_transaction(&mut self, operations: &[&[Word]]) -> Result<(), Self::Error> { - self.cs.set_low().map_err(ExclusiveDeviceError::Cs)?; - - let mut op_res = Ok(()); - - for buf in operations { - if let Err(e) = self.bus.write(buf) { - op_res = Err(e); - break; - } - } - - // On failure, it's important to still flush and deassert CS. - let flush_res = self.bus.flush(); - let cs_res = self.cs.set_high(); - - op_res.map_err(ExclusiveDeviceError::Spi)?; - flush_res.map_err(ExclusiveDeviceError::Spi)?; - cs_res.map_err(ExclusiveDeviceError::Cs)?; - - Ok(()) - } -} - impl blocking::SpiDevice for ExclusiveDevice where BUS: blocking::SpiBus, @@ -405,67 +250,6 @@ where } } -impl SpiDeviceRead for ExclusiveDevice -where - BUS: SpiBusRead, - CS: OutputPin, -{ - async fn read_transaction( - &mut self, - operations: &mut [&mut [Word]], - ) -> Result<(), Self::Error> { - self.cs.set_low().map_err(ExclusiveDeviceError::Cs)?; - - let mut op_res = Ok(()); - - for buf in operations { - if let Err(e) = self.bus.read(buf).await { - op_res = Err(e); - break; - } - } - - // On failure, it's important to still flush and deassert CS. - let flush_res = self.bus.flush().await; - let cs_res = self.cs.set_high(); - - op_res.map_err(ExclusiveDeviceError::Spi)?; - flush_res.map_err(ExclusiveDeviceError::Spi)?; - cs_res.map_err(ExclusiveDeviceError::Cs)?; - - Ok(()) - } -} - -impl SpiDeviceWrite for ExclusiveDevice -where - BUS: SpiBusWrite, - CS: OutputPin, -{ - async fn write_transaction(&mut self, operations: &[&[Word]]) -> Result<(), Self::Error> { - self.cs.set_low().map_err(ExclusiveDeviceError::Cs)?; - - let mut op_res = Ok(()); - - for buf in operations { - if let Err(e) = self.bus.write(buf).await { - op_res = Err(e); - break; - } - } - - // On failure, it's important to still flush and deassert CS. - let flush_res = self.bus.flush().await; - let cs_res = self.cs.set_high(); - - op_res.map_err(ExclusiveDeviceError::Spi)?; - flush_res.map_err(ExclusiveDeviceError::Spi)?; - cs_res.map_err(ExclusiveDeviceError::Cs)?; - - Ok(()) - } -} - impl SpiDevice for ExclusiveDevice where BUS: SpiBus, diff --git a/embedded-hal-bus/src/spi/critical_section.rs b/embedded-hal-bus/src/spi/critical_section.rs index a327609fd..c3e8cf699 100644 --- a/embedded-hal-bus/src/spi/critical_section.rs +++ b/embedded-hal-bus/src/spi/critical_section.rs @@ -1,9 +1,7 @@ use core::cell::RefCell; use critical_section::Mutex; use embedded_hal::digital::OutputPin; -use embedded_hal::spi::{ - ErrorType, Operation, SpiBus, SpiBusRead, SpiBusWrite, SpiDevice, SpiDeviceRead, SpiDeviceWrite, -}; +use embedded_hal::spi::{ErrorType, Operation, SpiBus, SpiDevice}; use super::DeviceError; @@ -37,70 +35,6 @@ where type Error = DeviceError; } -impl<'a, Word: Copy + 'static, BUS, CS> SpiDeviceRead for CriticalSectionDevice<'a, BUS, CS> -where - BUS: SpiBusRead, - CS: OutputPin, -{ - fn read_transaction(&mut self, operations: &mut [&mut [Word]]) -> Result<(), Self::Error> { - critical_section::with(|cs| { - let bus = &mut *self.bus.borrow_ref_mut(cs); - - self.cs.set_low().map_err(DeviceError::Cs)?; - - let mut op_res = Ok(()); - for buf in operations { - if let Err(e) = bus.read(buf) { - op_res = Err(e); - break; - } - } - - // On failure, it's important to still flush and deassert CS. - let flush_res = bus.flush(); - let cs_res = self.cs.set_high(); - - op_res.map_err(DeviceError::Spi)?; - flush_res.map_err(DeviceError::Spi)?; - cs_res.map_err(DeviceError::Cs)?; - - Ok(()) - }) - } -} - -impl<'a, Word: Copy + 'static, BUS, CS> SpiDeviceWrite for CriticalSectionDevice<'a, BUS, CS> -where - BUS: SpiBusWrite, - CS: OutputPin, -{ - fn write_transaction(&mut self, operations: &[&[Word]]) -> Result<(), Self::Error> { - critical_section::with(|cs| { - let bus = &mut *self.bus.borrow_ref_mut(cs); - - self.cs.set_low().map_err(DeviceError::Cs)?; - - let mut op_res = Ok(()); - for buf in operations { - if let Err(e) = bus.write(buf) { - op_res = Err(e); - break; - } - } - - // On failure, it's important to still flush and deassert CS. - let flush_res = bus.flush(); - let cs_res = self.cs.set_high(); - - op_res.map_err(DeviceError::Spi)?; - flush_res.map_err(DeviceError::Spi)?; - cs_res.map_err(DeviceError::Cs)?; - - Ok(()) - }) - } -} - impl<'a, Word: Copy + 'static, BUS, CS> SpiDevice for CriticalSectionDevice<'a, BUS, CS> where BUS: SpiBus, diff --git a/embedded-hal-bus/src/spi/exclusive.rs b/embedded-hal-bus/src/spi/exclusive.rs index fc7926eeb..9d4d002c9 100644 --- a/embedded-hal-bus/src/spi/exclusive.rs +++ b/embedded-hal-bus/src/spi/exclusive.rs @@ -1,9 +1,7 @@ //! SPI bus sharing mechanisms. use embedded_hal::digital::OutputPin; -use embedded_hal::spi::{ - ErrorType, Operation, SpiBus, SpiBusRead, SpiBusWrite, SpiDevice, SpiDeviceRead, SpiDeviceWrite, -}; +use embedded_hal::spi::{ErrorType, Operation, SpiBus, SpiDevice}; use super::DeviceError; @@ -31,64 +29,6 @@ where type Error = DeviceError; } -impl SpiDeviceRead for ExclusiveDevice -where - BUS: SpiBusRead, - CS: OutputPin, -{ - fn read_transaction(&mut self, operations: &mut [&mut [Word]]) -> Result<(), Self::Error> { - self.cs.set_low().map_err(DeviceError::Cs)?; - - let mut op_res = Ok(()); - - for buf in operations { - if let Err(e) = self.bus.read(buf) { - op_res = Err(e); - break; - } - } - - // On failure, it's important to still flush and deassert CS. - let flush_res = self.bus.flush(); - let cs_res = self.cs.set_high(); - - op_res.map_err(DeviceError::Spi)?; - flush_res.map_err(DeviceError::Spi)?; - cs_res.map_err(DeviceError::Cs)?; - - Ok(()) - } -} - -impl SpiDeviceWrite for ExclusiveDevice -where - BUS: SpiBusWrite, - CS: OutputPin, -{ - fn write_transaction(&mut self, operations: &[&[Word]]) -> Result<(), Self::Error> { - self.cs.set_low().map_err(DeviceError::Cs)?; - - let mut op_res = Ok(()); - - for buf in operations { - if let Err(e) = self.bus.write(buf) { - op_res = Err(e); - break; - } - } - - // On failure, it's important to still flush and deassert CS. - let flush_res = self.bus.flush(); - let cs_res = self.cs.set_high(); - - op_res.map_err(DeviceError::Spi)?; - flush_res.map_err(DeviceError::Spi)?; - cs_res.map_err(DeviceError::Cs)?; - - Ok(()) - } -} - impl SpiDevice for ExclusiveDevice where BUS: SpiBus, diff --git a/embedded-hal-bus/src/spi/mutex.rs b/embedded-hal-bus/src/spi/mutex.rs index 1bb693ded..59f8dfe95 100644 --- a/embedded-hal-bus/src/spi/mutex.rs +++ b/embedded-hal-bus/src/spi/mutex.rs @@ -1,7 +1,5 @@ use embedded_hal::digital::OutputPin; -use embedded_hal::spi::{ - ErrorType, Operation, SpiBus, SpiBusRead, SpiBusWrite, SpiDevice, SpiDeviceRead, SpiDeviceWrite, -}; +use embedded_hal::spi::{ErrorType, Operation, SpiBus, SpiDevice}; use std::sync::Mutex; use super::DeviceError; @@ -34,66 +32,6 @@ where type Error = DeviceError; } -impl<'a, Word: Copy + 'static, BUS, CS> SpiDeviceRead for MutexDevice<'a, BUS, CS> -where - BUS: SpiBusRead, - CS: OutputPin, -{ - fn read_transaction(&mut self, operations: &mut [&mut [Word]]) -> Result<(), Self::Error> { - let bus = &mut *self.bus.lock().unwrap(); - - self.cs.set_low().map_err(DeviceError::Cs)?; - - let mut op_res = Ok(()); - for buf in operations { - if let Err(e) = bus.read(buf) { - op_res = Err(e); - break; - } - } - - // On failure, it's important to still flush and deassert CS. - let flush_res = bus.flush(); - let cs_res = self.cs.set_high(); - - op_res.map_err(DeviceError::Spi)?; - flush_res.map_err(DeviceError::Spi)?; - cs_res.map_err(DeviceError::Cs)?; - - Ok(()) - } -} - -impl<'a, Word: Copy + 'static, BUS, CS> SpiDeviceWrite for MutexDevice<'a, BUS, CS> -where - BUS: SpiBusWrite, - CS: OutputPin, -{ - fn write_transaction(&mut self, operations: &[&[Word]]) -> Result<(), Self::Error> { - let bus = &mut *self.bus.lock().unwrap(); - - self.cs.set_low().map_err(DeviceError::Cs)?; - - let mut op_res = Ok(()); - for buf in operations { - if let Err(e) = bus.write(buf) { - op_res = Err(e); - break; - } - } - - // On failure, it's important to still flush and deassert CS. - let flush_res = bus.flush(); - let cs_res = self.cs.set_high(); - - op_res.map_err(DeviceError::Spi)?; - flush_res.map_err(DeviceError::Spi)?; - cs_res.map_err(DeviceError::Cs)?; - - Ok(()) - } -} - impl<'a, Word: Copy + 'static, BUS, CS> SpiDevice for MutexDevice<'a, BUS, CS> where BUS: SpiBus, diff --git a/embedded-hal-bus/src/spi/refcell.rs b/embedded-hal-bus/src/spi/refcell.rs index 47e29a879..9fd5d3e03 100644 --- a/embedded-hal-bus/src/spi/refcell.rs +++ b/embedded-hal-bus/src/spi/refcell.rs @@ -1,8 +1,6 @@ use core::cell::RefCell; use embedded_hal::digital::OutputPin; -use embedded_hal::spi::{ - ErrorType, Operation, SpiBus, SpiBusRead, SpiBusWrite, SpiDevice, SpiDeviceRead, SpiDeviceWrite, -}; +use embedded_hal::spi::{ErrorType, Operation, SpiBus, SpiDevice}; use super::DeviceError; @@ -34,66 +32,6 @@ where type Error = DeviceError; } -impl<'a, Word: Copy + 'static, BUS, CS> SpiDeviceRead for RefCellDevice<'a, BUS, CS> -where - BUS: SpiBusRead, - CS: OutputPin, -{ - fn read_transaction(&mut self, operations: &mut [&mut [Word]]) -> Result<(), Self::Error> { - let bus = &mut *self.bus.borrow_mut(); - - self.cs.set_low().map_err(DeviceError::Cs)?; - - let mut op_res = Ok(()); - for buf in operations { - if let Err(e) = bus.read(buf) { - op_res = Err(e); - break; - } - } - - // On failure, it's important to still flush and deassert CS. - let flush_res = bus.flush(); - let cs_res = self.cs.set_high(); - - op_res.map_err(DeviceError::Spi)?; - flush_res.map_err(DeviceError::Spi)?; - cs_res.map_err(DeviceError::Cs)?; - - Ok(()) - } -} - -impl<'a, Word: Copy + 'static, BUS, CS> SpiDeviceWrite for RefCellDevice<'a, BUS, CS> -where - BUS: SpiBusWrite, - CS: OutputPin, -{ - fn write_transaction(&mut self, operations: &[&[Word]]) -> Result<(), Self::Error> { - let bus = &mut *self.bus.borrow_mut(); - - self.cs.set_low().map_err(DeviceError::Cs)?; - - let mut op_res = Ok(()); - for buf in operations { - if let Err(e) = bus.write(buf) { - op_res = Err(e); - break; - } - } - - // On failure, it's important to still flush and deassert CS. - let flush_res = bus.flush(); - let cs_res = self.cs.set_high(); - - op_res.map_err(DeviceError::Spi)?; - flush_res.map_err(DeviceError::Spi)?; - cs_res.map_err(DeviceError::Cs)?; - - Ok(()) - } -} - impl<'a, Word: Copy + 'static, BUS, CS> SpiDevice for RefCellDevice<'a, BUS, CS> where BUS: SpiBus, diff --git a/embedded-hal/CHANGELOG.md b/embedded-hal/CHANGELOG.md index aab1418b8..3c25f2dd6 100644 --- a/embedded-hal/CHANGELOG.md +++ b/embedded-hal/CHANGELOG.md @@ -7,6 +7,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] +### Removed +- spi: removed read-only and write-only traits. + ## [v1.0.0-alpha.10] - 2023-04-04 *** This is (also) an alpha release with breaking changes (sorry) *** diff --git a/embedded-hal/src/spi.rs b/embedded-hal/src/spi.rs index e8310d5ce..ea77bf982 100644 --- a/embedded-hal/src/spi.rs +++ b/embedded-hal/src/spi.rs @@ -23,21 +23,15 @@ //! //! ## Bus //! -//! SPI bus traits represent **exclusive ownership** over the whole SPI bus. This is usually the entire +//! The [`SpiBus`] trait represents **exclusive ownership** over the whole SPI bus. This is usually the entire //! SPI MCU peripheral, plus the SCK, MOSI and MISO pins. //! //! Owning an instance of an SPI bus guarantees exclusive access, this is, we have the guarantee no other //! piece of code will try to use the bus while we own it. //! -//! There's 3 bus traits, depending on the bus capabilities. -//! -//! - [`SpiBus`]: Read-write access. This is the most commonly used. -//! - [`SpiBusRead`]: Read-only access, for example a bus with a MISO pin but no MOSI pin. -//! - [`SpiBusWrite`]: Write-only access, for example a bus with a MOSI pin but no MISO pin. -//! //! ## Device //! -//! [`SpiDevice`] represents **ownership over a single SPI device selected by a CS pin** in a (possibly shared) bus. This is typically: +//! The [`SpiDevice`] trait represents **ownership over a single SPI device selected by a CS pin** in a (possibly shared) bus. This is typically: //! //! - Exclusive ownership of the **CS pin**. //! - Access to the **underlying SPI bus**. If shared, it'll be behind some kind of lock/mutex. @@ -46,19 +40,17 @@ //! consists of asserting CS, then doing one or more transfers, then deasserting CS. For the entire duration of the transaction, the [`SpiDevice`] //! implementation will ensure no other transaction can be opened on the same bus. This is the key that allows correct sharing of the bus. //! -//! For read-only or write-only SPI devices, the [`SpiDeviceRead`] and [`SpiDeviceWrite`] are available. -//! //! # For driver authors //! //! When implementing a driver, it's crucial to pick the right trait, to ensure correct operation //! with maximum interoperability. Here are some guidelines depending on the device you're implementing a driver for: //! -//! If your device **has a CS pin**, use [`SpiDevice`] (or [`SpiDeviceRead`]/[`SpiDeviceWrite`]). Do not manually +//! If your device **has a CS pin**, use [`SpiDevice`]. Do not manually //! manage the CS pin, the [`SpiDevice`] implementation will do it for you. //! By using [`SpiDevice`], your driver will cooperate nicely with other drivers for other devices in the same shared SPI bus. //! //! ``` -//! # use embedded_hal::spi::{SpiBus, SpiBusRead, SpiBusWrite, SpiDevice, Operation}; +//! # use embedded_hal::spi::{SpiBus, SpiDevice, Operation}; //! pub struct MyDriver { //! spi: SPI, //! } @@ -91,19 +83,19 @@ //! } //! ``` //! -//! If your device **does not have a CS pin**, use [`SpiBus`] (or [`SpiBusRead`], [`SpiBusWrite`]). This will ensure +//! If your device **does not have a CS pin**, use [`SpiBus`]. This will ensure //! your driver has exclusive access to the bus, so no other drivers can interfere. It's not possible to safely share //! a bus without CS pins. By requiring [`SpiBus`] you disallow sharing, ensuring correct operation. //! //! ``` -//! # use embedded_hal::spi::{SpiBus, SpiBusRead, SpiBusWrite}; +//! # use embedded_hal::spi::SpiBus; //! pub struct MyDriver { //! spi: SPI, //! } //! //! impl MyDriver //! where -//! SPI: SpiBus, // or SpiBusRead/SpiBusWrite if you only need to read or only write. +//! SPI: SpiBus, //! { //! pub fn new(spi: SPI) -> Self { //! Self { spi } @@ -129,7 +121,7 @@ //! //! # For HAL authors //! -//! HALs **must** implement [`SpiBus`], [`SpiBusRead`] and [`SpiBusWrite`]. Users can combine the bus together with the CS pin (which should +//! HALs **must** implement [`SpiBus`]. Users can combine the bus together with the CS pin (which should //! implement [`OutputPin`](crate::digital::OutputPin)) using HAL-independent [`SpiDevice`] implementations such as the ones in [`embedded-hal-bus`](https://crates.io/crates/embedded-hal-bus). //! //! HALs may additionally implement [`SpiDevice`] to **take advantage of hardware CS management**, which may provide some performance @@ -148,23 +140,23 @@ //! to finish, or enqueue the new one, but they must not return a "busy" error. Users must be able to do multiple method calls in a row //! and have them executed "as if" they were done sequentially, without having to check for "busy" errors. //! -//! When using a [`SpiBus`], call [`flush`](SpiBusFlush::flush) to wait for operations to actually finish. Examples of situations +//! When using a [`SpiBus`], call [`flush`](SpiBus::flush) to wait for operations to actually finish. Examples of situations //! where this is needed are: //! - To synchronize SPI activity and GPIO activity, for example before deasserting a CS pin. //! - Before deinitializing the hardware SPI peripheral. //! -//! When using a [`SpiDevice`], you can still call [`flush`](SpiBusFlush::flush) on the bus within a transaction. +//! When using a [`SpiDevice`], you can still call [`flush`](SpiBus::flush) on the bus within a transaction. //! It's very rarely needed, because [`transaction`](SpiDevice::transaction) already flushes for you //! before deasserting CS. For example, you may need it to synchronize with GPIOs other than CS, such as DCX pins //! sometimes found in SPI displays. //! -//! For example, for [`write`](SpiBusWrite::write) operations, it is common for hardware SPI peripherals to have a small +//! For example, for [`write`](SpiBus::write) operations, it is common for hardware SPI peripherals to have a small //! FIFO buffer, usually 1-4 bytes. Software writes data to the FIFO, and the peripheral sends it on MOSI at its own pace, -//! at the specified SPI frequency. It is allowed for an implementation of [`write`](SpiBusWrite::write) to return as soon -//! as all the data has been written to the FIFO, before it is actually sent. Calling [`flush`](SpiBusFlush::flush) would +//! at the specified SPI frequency. It is allowed for an implementation of [`write`](SpiBus::write) to return as soon +//! as all the data has been written to the FIFO, before it is actually sent. Calling [`flush`](SpiBus::flush) would //! wait until all the bits have actually been sent, the FIFO is empty, and the bus is idle. //! -//! This still applies to other operations such as [`read`](SpiBusRead::read) or [`transfer`](SpiBus::transfer). It is less obvious +//! This still applies to other operations such as [`read`](SpiBus::read) or [`transfer`](SpiBus::transfer). It is less obvious //! why, because these methods can't return before receiving all the read data. However it's still technically possible //! for them to return before the bus is idle. For example, assuming SPI mode 0, the last bit is sampled on the first (rising) edge //! of SCK, at which point a method could return, but the second (falling) SCK edge still has to happen before the bus is idle. @@ -307,11 +299,11 @@ impl ErrorType for &mut T { pub enum Operation<'a, Word: 'static> { /// Read data into the provided buffer. /// - /// Equivalent to [`SpiBusRead::read`]. + /// Equivalent to [`SpiBus::read`]. Read(&'a mut [Word]), /// Write data from the provided buffer, discarding read data /// - /// Equivalent to [`SpiBusWrite::write`]. + /// Equivalent to [`SpiBus::write`]. Write(&'a [Word]), /// Read data into the first buffer, while writing data from the second buffer. /// @@ -323,19 +315,19 @@ pub enum Operation<'a, Word: 'static> { TransferInPlace(&'a mut [Word]), } -/// SPI read-only device trait +/// SPI device trait /// -/// `SpiDeviceRead` represents ownership over a single SPI device on a (possibly shared) bus, selected +/// `SpiDevice` represents ownership over a single SPI device on a (possibly shared) bus, selected /// with a CS (Chip Select) pin. /// /// See the [module-level documentation](self) for important usage information. -pub trait SpiDeviceRead: ErrorType { - /// Perform a read transaction against the device. +pub trait SpiDevice: ErrorType { + /// Perform a transaction against the device. /// /// - Locks the bus /// - Asserts the CS (Chip Select) pin. /// - Performs all the operations. - /// - [Flushes](SpiBusFlush::flush) the bus. + /// - [Flushes](SpiBus::flush) the bus. /// - Deasserts the CS pin. /// - Unlocks the bus. /// @@ -345,77 +337,25 @@ pub trait SpiDeviceRead: ErrorType { /// /// On bus errors the implementation should try to deassert CS. /// If an error occurs while deasserting CS the bus error should take priority as the return value. - fn read_transaction(&mut self, operations: &mut [&mut [Word]]) -> Result<(), Self::Error>; + fn transaction(&mut self, operations: &mut [Operation<'_, Word>]) -> Result<(), Self::Error>; /// Do a read within a transaction. /// - /// This is a convenience method equivalent to `device.read_transaction(&mut [buf])`. + /// This is a convenience method equivalent to `device.transaction(&mut [Operation::Read(buf)])`. /// - /// See also: [`SpiDeviceRead::read_transaction`], [`SpiBusRead::read`] + /// See also: [`SpiDevice::transaction`], [`SpiBus::read`] fn read(&mut self, buf: &mut [Word]) -> Result<(), Self::Error> { - self.read_transaction(&mut [buf]) + self.transaction(&mut [Operation::Read(buf)]) } -} - -/// SPI write-only device trait -/// -/// `SpiDeviceWrite` represents ownership over a single SPI device on a (possibly shared) bus, selected -/// with a CS (Chip Select) pin. -/// -/// See the [module-level documentation](self) for important usage information. -pub trait SpiDeviceWrite: ErrorType { - /// Perform a write transaction against the device. - /// - /// - Locks the bus - /// - Asserts the CS (Chip Select) pin. - /// - Performs all the operations. - /// - [Flushes](SpiBusFlush::flush) the bus. - /// - Deasserts the CS pin. - /// - Unlocks the bus. - /// - /// The locking mechanism is implementation-defined. The only requirement is it must prevent two - /// transactions from executing concurrently against the same bus. Examples of implementations are: - /// critical sections, blocking mutexes, returning an error or panicking if the bus is already busy. - /// - /// On bus errors the implementation should try to deassert CS. - /// If an error occurs while deasserting CS the bus error should take priority as the return value. - fn write_transaction(&mut self, operations: &[&[Word]]) -> Result<(), Self::Error>; /// Do a write within a transaction. /// - /// This is a convenience method equivalent to `device.write_transaction(&mut [buf])`. + /// This is a convenience method equivalent to `device.transaction(&mut [Operation::Write(buf)])`. /// - /// See also: [`SpiDeviceWrite::write_transaction`], [`SpiBusWrite::write`] + /// See also: [`SpiDevice::transaction`], [`SpiBus::write`] fn write(&mut self, buf: &[Word]) -> Result<(), Self::Error> { - self.write_transaction(&[buf]) + self.transaction(&mut [Operation::Write(buf)]) } -} - -/// SPI device trait -/// -/// `SpiDevice` represents ownership over a single SPI device on a (possibly shared) bus, selected -/// with a CS (Chip Select) pin. -/// -/// See the [module-level documentation](self) for important usage information. -pub trait SpiDevice: - SpiDeviceRead + SpiDeviceWrite + ErrorType -{ - /// Perform a transaction against the device. - /// - /// - Locks the bus - /// - Asserts the CS (Chip Select) pin. - /// - Performs all the operations. - /// - [Flushes](SpiBusFlush::flush) the bus. - /// - Deasserts the CS pin. - /// - Unlocks the bus. - /// - /// The locking mechanism is implementation-defined. The only requirement is it must prevent two - /// transactions from executing concurrently against the same bus. Examples of implementations are: - /// critical sections, blocking mutexes, returning an error or panicking if the bus is already busy. - /// - /// On bus errors the implementation should try to deassert CS. - /// If an error occurs while deasserting CS the bus error should take priority as the return value. - fn transaction(&mut self, operations: &mut [Operation<'_, Word>]) -> Result<(), Self::Error>; /// Do a transfer within a transaction. /// @@ -428,7 +368,7 @@ pub trait SpiDevice: /// Do an in-place transfer within a transaction. /// - /// This is a convenience method equivalent to `device.transaction([Operation::TransferInPlace(buf)]`. + /// This is a convenience method equivalent to `device.transaction(&mut [Operation::TransferInPlace(buf)]`. /// /// See also: [`SpiDevice::transaction`], [`SpiBus::transfer_in_place`] fn transfer_in_place(&mut self, buf: &mut [Word]) -> Result<(), Self::Error> { @@ -436,30 +376,18 @@ pub trait SpiDevice: } } -impl> SpiDeviceRead for &mut T { - fn read_transaction(&mut self, operations: &mut [&mut [Word]]) -> Result<(), Self::Error> { - T::read_transaction(self, operations) +impl> SpiDevice for &mut T { + fn transaction(&mut self, operations: &mut [Operation<'_, Word>]) -> Result<(), Self::Error> { + T::transaction(self, operations) } fn read(&mut self, buf: &mut [Word]) -> Result<(), Self::Error> { T::read(self, buf) } -} - -impl> SpiDeviceWrite for &mut T { - fn write_transaction(&mut self, operations: &[&[Word]]) -> Result<(), Self::Error> { - T::write_transaction(self, operations) - } fn write(&mut self, buf: &[Word]) -> Result<(), Self::Error> { T::write(self, buf) } -} - -impl> SpiDevice for &mut T { - fn transaction(&mut self, operations: &mut [Operation<'_, Word>]) -> Result<(), Self::Error> { - T::transaction(self, operations) - } fn transfer(&mut self, read: &mut [Word], write: &[Word]) -> Result<(), Self::Error> { T::transfer(self, read, write) @@ -470,22 +398,12 @@ impl> SpiDevice for &mut T { } } -/// Flush support for SPI bus -pub trait SpiBusFlush: ErrorType { - /// Wait until all operations have completed and the bus is idle. - /// - /// See the [module-level documentation](self) for important usage information. - fn flush(&mut self) -> Result<(), Self::Error>; -} - -impl SpiBusFlush for &mut T { - fn flush(&mut self) -> Result<(), Self::Error> { - T::flush(self) - } -} - -/// Read-only SPI bus -pub trait SpiBusRead: SpiBusFlush { +/// SPI bus +/// +/// `SpiBus` represents **exclusive ownership** over the whole SPI bus, with SCK, MOSI and MISO pins. +/// +/// See the [module-level documentation](self) for important information on SPI Bus vs Device traits. +pub trait SpiBus: ErrorType { /// Read `words` from the slave. /// /// The word value sent on MOSI during reading is implementation-defined, @@ -494,35 +412,13 @@ pub trait SpiBusRead: SpiBusFlush { /// Implementations are allowed to return before the operation is /// complete. See the [module-level documentation](self) for details. fn read(&mut self, words: &mut [Word]) -> Result<(), Self::Error>; -} - -impl, Word: Copy + 'static> SpiBusRead for &mut T { - fn read(&mut self, words: &mut [Word]) -> Result<(), Self::Error> { - T::read(self, words) - } -} -/// Write-only SPI bus -pub trait SpiBusWrite: SpiBusFlush { /// Write `words` to the slave, ignoring all the incoming words /// /// Implementations are allowed to return before the operation is /// complete. See the [module-level documentation](self) for details. fn write(&mut self, words: &[Word]) -> Result<(), Self::Error>; -} -impl, Word: Copy + 'static> SpiBusWrite for &mut T { - fn write(&mut self, words: &[Word]) -> Result<(), Self::Error> { - T::write(self, words) - } -} - -/// Read-write SPI bus -/// -/// `SpiBus` represents **exclusive ownership** over the whole SPI bus, with SCK, MOSI and MISO pins. -/// -/// See the [module-level documentation](self) for important information on SPI Bus vs Device traits. -pub trait SpiBus: SpiBusRead + SpiBusWrite { /// Write and read simultaneously. `write` is written to the slave on MOSI and /// words received on MISO are stored in `read`. /// @@ -543,9 +439,22 @@ pub trait SpiBus: SpiBusRead + SpiBusWrite Result<(), Self::Error>; + + /// Wait until all operations have completed and the bus is idle. + /// + /// See the [module-level documentation](self) for important usage information. + fn flush(&mut self) -> Result<(), Self::Error>; } impl, Word: Copy + 'static> SpiBus for &mut T { + fn read(&mut self, words: &mut [Word]) -> Result<(), Self::Error> { + T::read(self, words) + } + + fn write(&mut self, words: &[Word]) -> Result<(), Self::Error> { + T::write(self, words) + } + fn transfer(&mut self, read: &mut [Word], write: &[Word]) -> Result<(), Self::Error> { T::transfer(self, read, write) } @@ -553,4 +462,8 @@ impl, Word: Copy + 'static> SpiBus for &mut T { fn transfer_in_place(&mut self, words: &mut [Word]) -> Result<(), Self::Error> { T::transfer_in_place(self, words) } + + fn flush(&mut self) -> Result<(), Self::Error> { + T::flush(self) + } } From d07d39e359ed2157cbc0bbd88ef950b60c58e804 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 23 May 2023 21:26:15 +0200 Subject: [PATCH 036/199] spi: add Operation::DelayUs(u32). --- embedded-hal-async/src/spi.rs | 33 ++++++++++++++---- embedded-hal-bus/src/spi/critical_section.rs | 36 ++++++++++++++++---- embedded-hal-bus/src/spi/exclusive.rs | 36 ++++++++++++++++---- embedded-hal-bus/src/spi/mod.rs | 9 +++++ embedded-hal-bus/src/spi/mutex.rs | 36 ++++++++++++++++---- embedded-hal-bus/src/spi/refcell.rs | 36 ++++++++++++++++---- embedded-hal/CHANGELOG.md | 3 ++ embedded-hal/src/spi.rs | 2 ++ 8 files changed, 160 insertions(+), 31 deletions(-) diff --git a/embedded-hal-async/src/spi.rs b/embedded-hal-async/src/spi.rs index 0c99d79bf..2c05f56e4 100644 --- a/embedded-hal-async/src/spi.rs +++ b/embedded-hal-async/src/spi.rs @@ -8,6 +8,8 @@ pub use embedded_hal::spi::{ Error, ErrorKind, ErrorType, Mode, Operation, Phase, Polarity, MODE_0, MODE_1, MODE_2, MODE_3, }; +use crate::delay::DelayUs; + /// SPI device trait /// /// `SpiDevice` represents ownership over a single SPI device on a (possibly shared) bus, selected @@ -195,19 +197,20 @@ where /// /// This is the most straightforward way of obtaining an [`SpiDevice`] from an [`SpiBus`], /// ideal for when no sharing is required (only one SPI device is present on the bus). -pub struct ExclusiveDevice { +pub struct ExclusiveDevice { bus: BUS, cs: CS, + delay: D, } -impl ExclusiveDevice { +impl ExclusiveDevice { /// Create a new ExclusiveDevice - pub fn new(bus: BUS, cs: CS) -> Self { - Self { bus, cs } + pub fn new(bus: BUS, cs: CS, delay: D) -> Self { + Self { bus, cs, delay } } } -impl ErrorType for ExclusiveDevice +impl ErrorType for ExclusiveDevice where BUS: ErrorType, CS: OutputPin, @@ -215,10 +218,11 @@ where type Error = ExclusiveDeviceError; } -impl blocking::SpiDevice for ExclusiveDevice +impl blocking::SpiDevice for ExclusiveDevice where BUS: blocking::SpiBus, CS: OutputPin, + D: embedded_hal::delay::DelayUs, { fn transaction(&mut self, operations: &mut [Operation<'_, Word>]) -> Result<(), Self::Error> { self.cs.set_low().map_err(ExclusiveDeviceError::Cs)?; @@ -230,6 +234,13 @@ where Operation::Write(buf) => self.bus.write(buf), Operation::Transfer(read, write) => self.bus.transfer(read, write), Operation::TransferInPlace(buf) => self.bus.transfer_in_place(buf), + Operation::DelayUs(us) => match self.bus.flush() { + Err(e) => Err(e), + Ok(()) => { + self.delay.delay_us(*us); + Ok(()) + } + }, }; if let Err(e) = res { break 'ops Err(e); @@ -250,10 +261,11 @@ where } } -impl SpiDevice for ExclusiveDevice +impl SpiDevice for ExclusiveDevice where BUS: SpiBus, CS: OutputPin, + D: DelayUs, { async fn transaction( &mut self, @@ -268,6 +280,13 @@ where Operation::Write(buf) => self.bus.write(buf).await, Operation::Transfer(read, write) => self.bus.transfer(read, write).await, Operation::TransferInPlace(buf) => self.bus.transfer_in_place(buf).await, + Operation::DelayUs(us) => match self.bus.flush().await { + Err(e) => Err(e), + Ok(()) => { + self.delay.delay_us(*us).await; + Ok(()) + } + }, }; if let Err(e) = res { break 'ops Err(e); diff --git a/embedded-hal-bus/src/spi/critical_section.rs b/embedded-hal-bus/src/spi/critical_section.rs index c3e8cf699..2a4c09788 100644 --- a/embedded-hal-bus/src/spi/critical_section.rs +++ b/embedded-hal-bus/src/spi/critical_section.rs @@ -1,5 +1,6 @@ use core::cell::RefCell; use critical_section::Mutex; +use embedded_hal::delay::DelayUs; use embedded_hal::digital::OutputPin; use embedded_hal::spi::{ErrorType, Operation, SpiBus, SpiDevice}; @@ -15,19 +16,36 @@ use super::DeviceError; /// The downside is critical sections typically require globally disabling interrupts, so `CriticalSectionDevice` will likely /// negatively impact real-time properties, such as interrupt latency. If you can, prefer using /// [`RefCellDevice`](super::RefCellDevice) instead, which does not require taking critical sections. -pub struct CriticalSectionDevice<'a, BUS, CS> { +pub struct CriticalSectionDevice<'a, BUS, CS, D> { bus: &'a Mutex>, cs: CS, + delay: D, } -impl<'a, BUS, CS> CriticalSectionDevice<'a, BUS, CS> { +impl<'a, BUS, CS, D> CriticalSectionDevice<'a, BUS, CS, D> { /// Create a new ExclusiveDevice - pub fn new(bus: &'a Mutex>, cs: CS) -> Self { - Self { bus, cs } + pub fn new(bus: &'a Mutex>, cs: CS, delay: D) -> Self { + Self { bus, cs, delay } } } -impl<'a, BUS, CS> ErrorType for CriticalSectionDevice<'a, BUS, CS> +impl<'a, BUS, CS> CriticalSectionDevice<'a, BUS, CS, super::NoDelay> { + /// Create a new CriticalSectionDevice without support for in-transaction delays. + /// + /// # Panics + /// + /// The returned device will panic if you try to execute a transaction + /// that contains any operations of type `Operation::DelayUs`. + pub fn new_no_delay(bus: &'a Mutex>, cs: CS) -> Self { + Self { + bus, + cs, + delay: super::NoDelay, + } + } +} + +impl<'a, BUS, CS, D> ErrorType for CriticalSectionDevice<'a, BUS, CS, D> where BUS: ErrorType, CS: OutputPin, @@ -35,10 +53,11 @@ where type Error = DeviceError; } -impl<'a, Word: Copy + 'static, BUS, CS> SpiDevice for CriticalSectionDevice<'a, BUS, CS> +impl<'a, Word: Copy + 'static, BUS, CS, D> SpiDevice for CriticalSectionDevice<'a, BUS, CS, D> where BUS: SpiBus, CS: OutputPin, + D: DelayUs, { fn transaction(&mut self, operations: &mut [Operation<'_, Word>]) -> Result<(), Self::Error> { critical_section::with(|cs| { @@ -51,6 +70,11 @@ where Operation::Write(buf) => bus.write(buf), Operation::Transfer(read, write) => bus.transfer(read, write), Operation::TransferInPlace(buf) => bus.transfer_in_place(buf), + Operation::DelayUs(us) => { + bus.flush()?; + self.delay.delay_us(*us); + Ok(()) + } }); // On failure, it's important to still flush and deassert CS. diff --git a/embedded-hal-bus/src/spi/exclusive.rs b/embedded-hal-bus/src/spi/exclusive.rs index 9d4d002c9..cbec194dc 100644 --- a/embedded-hal-bus/src/spi/exclusive.rs +++ b/embedded-hal-bus/src/spi/exclusive.rs @@ -1,5 +1,6 @@ //! SPI bus sharing mechanisms. +use embedded_hal::delay::DelayUs; use embedded_hal::digital::OutputPin; use embedded_hal::spi::{ErrorType, Operation, SpiBus, SpiDevice}; @@ -9,19 +10,36 @@ use super::DeviceError; /// /// This is the most straightforward way of obtaining an [`SpiDevice`] from an [`SpiBus`](embedded_hal::spi::SpiBus), /// ideal for when no sharing is required (only one SPI device is present on the bus). -pub struct ExclusiveDevice { +pub struct ExclusiveDevice { bus: BUS, cs: CS, + delay: D, } -impl ExclusiveDevice { +impl ExclusiveDevice { /// Create a new ExclusiveDevice - pub fn new(bus: BUS, cs: CS) -> Self { - Self { bus, cs } + pub fn new(bus: BUS, cs: CS, delay: D) -> Self { + Self { bus, cs, delay } } } -impl ErrorType for ExclusiveDevice +impl ExclusiveDevice { + /// Create a new ExclusiveDevice without support for in-transaction delays. + /// + /// # Panics + /// + /// The returned device will panic if you try to execute a transaction + /// that contains any operations of type `Operation::DelayUs`. + pub fn new_no_delay(bus: BUS, cs: CS) -> Self { + Self { + bus, + cs, + delay: super::NoDelay, + } + } +} + +impl ErrorType for ExclusiveDevice where BUS: ErrorType, CS: OutputPin, @@ -29,10 +47,11 @@ where type Error = DeviceError; } -impl SpiDevice for ExclusiveDevice +impl SpiDevice for ExclusiveDevice where BUS: SpiBus, CS: OutputPin, + D: DelayUs, { fn transaction(&mut self, operations: &mut [Operation<'_, Word>]) -> Result<(), Self::Error> { self.cs.set_low().map_err(DeviceError::Cs)?; @@ -42,6 +61,11 @@ where Operation::Write(buf) => self.bus.write(buf), Operation::Transfer(read, write) => self.bus.transfer(read, write), Operation::TransferInPlace(buf) => self.bus.transfer_in_place(buf), + Operation::DelayUs(us) => { + self.bus.flush()?; + self.delay.delay_us(*us); + Ok(()) + } }); // On failure, it's important to still flush and deassert CS. diff --git a/embedded-hal-bus/src/spi/mod.rs b/embedded-hal-bus/src/spi/mod.rs index 6ae803865..10f74e8d6 100644 --- a/embedded-hal-bus/src/spi/mod.rs +++ b/embedded-hal-bus/src/spi/mod.rs @@ -35,3 +35,12 @@ where } } } + +/// Dummy `DelayUs` implementation that panics on use. +pub struct NoDelay; + +impl embedded_hal::delay::DelayUs for NoDelay { + fn delay_us(&mut self, _us: u32) { + panic!("You've tried to execute a SPI transaction containing a `Operation::Delay` in a `SpiDevice` created with `new_no_delay()`. Create it with `new()` instead, passing a `DelayUs` implementation.") + } +} diff --git a/embedded-hal-bus/src/spi/mutex.rs b/embedded-hal-bus/src/spi/mutex.rs index 59f8dfe95..bdf329217 100644 --- a/embedded-hal-bus/src/spi/mutex.rs +++ b/embedded-hal-bus/src/spi/mutex.rs @@ -1,3 +1,4 @@ +use embedded_hal::delay::DelayUs; use embedded_hal::digital::OutputPin; use embedded_hal::spi::{ErrorType, Operation, SpiBus, SpiDevice}; use std::sync::Mutex; @@ -12,19 +13,36 @@ use super::DeviceError; /// Sharing is implemented with a `std` [`Mutex`](std::sync::Mutex). It allows a single bus across multiple threads, /// with finer-grained locking than [`CriticalSectionDevice`](super::CriticalSectionDevice). The downside is /// it is only available in `std` targets. -pub struct MutexDevice<'a, BUS, CS> { +pub struct MutexDevice<'a, BUS, CS, D> { bus: &'a Mutex, cs: CS, + delay: D, } -impl<'a, BUS, CS> MutexDevice<'a, BUS, CS> { +impl<'a, BUS, CS, D> MutexDevice<'a, BUS, CS, D> { /// Create a new ExclusiveDevice - pub fn new(bus: &'a Mutex, cs: CS) -> Self { - Self { bus, cs } + pub fn new(bus: &'a Mutex, cs: CS, delay: D) -> Self { + Self { bus, cs, delay } } } -impl<'a, BUS, CS> ErrorType for MutexDevice<'a, BUS, CS> +impl<'a, BUS, CS> MutexDevice<'a, BUS, CS, super::NoDelay> { + /// Create a new MutexDevice without support for in-transaction delays. + /// + /// # Panics + /// + /// The returned device will panic if you try to execute a transaction + /// that contains any operations of type `Operation::DelayUs`. + pub fn new_no_delay(bus: &'a Mutex, cs: CS) -> Self { + Self { + bus, + cs, + delay: super::NoDelay, + } + } +} + +impl<'a, BUS, CS, D> ErrorType for MutexDevice<'a, BUS, CS, D> where BUS: ErrorType, CS: OutputPin, @@ -32,10 +50,11 @@ where type Error = DeviceError; } -impl<'a, Word: Copy + 'static, BUS, CS> SpiDevice for MutexDevice<'a, BUS, CS> +impl<'a, Word: Copy + 'static, BUS, CS, D> SpiDevice for MutexDevice<'a, BUS, CS, D> where BUS: SpiBus, CS: OutputPin, + D: DelayUs, { fn transaction(&mut self, operations: &mut [Operation<'_, Word>]) -> Result<(), Self::Error> { let bus = &mut *self.bus.lock().unwrap(); @@ -47,6 +66,11 @@ where Operation::Write(buf) => bus.write(buf), Operation::Transfer(read, write) => bus.transfer(read, write), Operation::TransferInPlace(buf) => bus.transfer_in_place(buf), + Operation::DelayUs(us) => { + bus.flush()?; + self.delay.delay_us(*us); + Ok(()) + } }); // On failure, it's important to still flush and deassert CS. diff --git a/embedded-hal-bus/src/spi/refcell.rs b/embedded-hal-bus/src/spi/refcell.rs index 9fd5d3e03..a08e8a262 100644 --- a/embedded-hal-bus/src/spi/refcell.rs +++ b/embedded-hal-bus/src/spi/refcell.rs @@ -1,4 +1,5 @@ use core::cell::RefCell; +use embedded_hal::delay::DelayUs; use embedded_hal::digital::OutputPin; use embedded_hal::spi::{ErrorType, Operation, SpiBus, SpiDevice}; @@ -12,19 +13,36 @@ use super::DeviceError; /// Sharing is implemented with a `RefCell`. This means it has low overhead, but `RefCellDevice` instances are not `Send`, /// so it only allows sharing within a single thread (interrupt priority level). If you need to share a bus across several /// threads, use [`CriticalSectionDevice`](super::CriticalSectionDevice) instead. -pub struct RefCellDevice<'a, BUS, CS> { +pub struct RefCellDevice<'a, BUS, CS, D> { bus: &'a RefCell, cs: CS, + delay: D, } -impl<'a, BUS, CS> RefCellDevice<'a, BUS, CS> { +impl<'a, BUS, CS, D> RefCellDevice<'a, BUS, CS, D> { /// Create a new ExclusiveDevice - pub fn new(bus: &'a RefCell, cs: CS) -> Self { - Self { bus, cs } + pub fn new(bus: &'a RefCell, cs: CS, delay: D) -> Self { + Self { bus, cs, delay } } } -impl<'a, BUS, CS> ErrorType for RefCellDevice<'a, BUS, CS> +impl<'a, BUS, CS> RefCellDevice<'a, BUS, CS, super::NoDelay> { + /// Create a new RefCellDevice without support for in-transaction delays. + /// + /// # Panics + /// + /// The returned device will panic if you try to execute a transaction + /// that contains any operations of type `Operation::DelayUs`. + pub fn new_no_delay(bus: &'a RefCell, cs: CS) -> Self { + Self { + bus, + cs, + delay: super::NoDelay, + } + } +} + +impl<'a, BUS, CS, D> ErrorType for RefCellDevice<'a, BUS, CS, D> where BUS: ErrorType, CS: OutputPin, @@ -32,10 +50,11 @@ where type Error = DeviceError; } -impl<'a, Word: Copy + 'static, BUS, CS> SpiDevice for RefCellDevice<'a, BUS, CS> +impl<'a, Word: Copy + 'static, BUS, CS, D> SpiDevice for RefCellDevice<'a, BUS, CS, D> where BUS: SpiBus, CS: OutputPin, + D: DelayUs, { fn transaction(&mut self, operations: &mut [Operation<'_, Word>]) -> Result<(), Self::Error> { let bus = &mut *self.bus.borrow_mut(); @@ -47,6 +66,11 @@ where Operation::Write(buf) => bus.write(buf), Operation::Transfer(read, write) => bus.transfer(read, write), Operation::TransferInPlace(buf) => bus.transfer_in_place(buf), + Operation::DelayUs(us) => { + bus.flush()?; + self.delay.delay_us(*us); + Ok(()) + } }); // On failure, it's important to still flush and deassert CS. diff --git a/embedded-hal/CHANGELOG.md b/embedded-hal/CHANGELOG.md index 3c25f2dd6..afd99a7a7 100644 --- a/embedded-hal/CHANGELOG.md +++ b/embedded-hal/CHANGELOG.md @@ -7,6 +7,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] +### Added +- spi: added `Operation::DelayUs(u32)`. + ### Removed - spi: removed read-only and write-only traits. diff --git a/embedded-hal/src/spi.rs b/embedded-hal/src/spi.rs index ea77bf982..3bcdda795 100644 --- a/embedded-hal/src/spi.rs +++ b/embedded-hal/src/spi.rs @@ -313,6 +313,8 @@ pub enum Operation<'a, Word: 'static> { /// /// Equivalent to [`SpiBus::transfer_in_place`]. TransferInPlace(&'a mut [Word]), + /// Delay for at least the specified number of microseconds + DelayUs(u32), } /// SPI device trait From d500bfa66158d9ee692dfa7fff5ac585b74b7a4f Mon Sep 17 00:00:00 2001 From: Luo Jia / Zhouqi Jiang Date: Wed, 31 May 2023 20:17:50 +0800 Subject: [PATCH 037/199] embedded-hal: digital: add #[inline] hints for PinState functions Add inline hints for: - ::not - >::from - >::from Those hints would allow further optimizations if PinState structure functions is used on embedded-hal projects. Signed-off-by: Zhouqi Jiang --- embedded-hal/src/digital.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/embedded-hal/src/digital.rs b/embedded-hal/src/digital.rs index 629228442..c476cb769 100644 --- a/embedded-hal/src/digital.rs +++ b/embedded-hal/src/digital.rs @@ -82,6 +82,7 @@ pub enum PinState { } impl From for PinState { + #[inline] fn from(value: bool) -> Self { match value { false => PinState::Low, @@ -93,6 +94,7 @@ impl From for PinState { impl Not for PinState { type Output = PinState; + #[inline] fn not(self) -> Self::Output { match self { PinState::High => PinState::Low, @@ -102,6 +104,7 @@ impl Not for PinState { } impl From for bool { + #[inline] fn from(value: PinState) -> bool { match value { PinState::Low => false, From 50cceacf4667998ee0617603bf00e5cb08300f2a Mon Sep 17 00:00:00 2001 From: Diego Barrios Romero Date: Tue, 20 Jun 2023 10:20:00 +0200 Subject: [PATCH 038/199] Switch to GHMQ --- .github/bors.toml | 16 ---------------- .github/workflows/clippy.yml | 8 +++++--- .github/workflows/rustfmt.yml | 8 +++++--- .github/workflows/test.yml | 10 ++++++---- 4 files changed, 16 insertions(+), 26 deletions(-) delete mode 100644 .github/bors.toml diff --git a/.github/bors.toml b/.github/bors.toml deleted file mode 100644 index 0f213ce07..000000000 --- a/.github/bors.toml +++ /dev/null @@ -1,16 +0,0 @@ -block_labels = ["needs-decision"] -delete_merged_branches = true -required_approvals = 1 -status = [ - "test (stable, x86_64-unknown-linux-gnu)", - "test (stable, thumbv6m-none-eabi)", - "test (stable, thumbv7m-none-eabi)", - "test (1.59.0, x86_64-unknown-linux-gnu)", - "test (1.59.0, thumbv6m-none-eabi)", - "test (1.59.0, thumbv7m-none-eabi)", - "test (nightly, x86_64-unknown-linux-gnu)", - "test (nightly, thumbv6m-none-eabi)", - "test (nightly, thumbv7m-none-eabi)", - "clippy", - "fmt", -] diff --git a/.github/workflows/clippy.yml b/.github/workflows/clippy.yml index 9fa0c3bed..20cd030f6 100644 --- a/.github/workflows/clippy.yml +++ b/.github/workflows/clippy.yml @@ -1,7 +1,9 @@ on: - push: - branches: [ staging, trying, master ] - pull_request: + push: # Run CI for all branches except GitHub merge queue tmp branches + branches-ignore: + - "gh-readonly-queue/**" + pull_request: # Run CI for PRs on any branch + merge_group: # Run CI for the GitHub merge queue name: Clippy check jobs: diff --git a/.github/workflows/rustfmt.yml b/.github/workflows/rustfmt.yml index 29049c24d..3d9036e69 100644 --- a/.github/workflows/rustfmt.yml +++ b/.github/workflows/rustfmt.yml @@ -1,7 +1,9 @@ on: - push: - branches: [ staging, trying, master ] - pull_request: + push: # Run CI for all branches except GitHub merge queue tmp branches + branches-ignore: + - "gh-readonly-queue/**" + pull_request: # Run CI for PRs on any branch + merge_group: # Run CI for the GitHub merge queue name: Code formatting check diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index ff0013f53..1677ea895 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -1,7 +1,9 @@ on: - push: - branches: [ staging, trying, master ] - pull_request: + push: # Run CI for all branches except GitHub merge queue tmp branches + branches-ignore: + - "gh-readonly-queue/**" + pull_request: # Run CI for PRs on any branch + merge_group: # Run CI for the GitHub merge queue name: Continuous integration @@ -32,7 +34,7 @@ jobs: toolchain: ${{ matrix.rust }} target: ${{ matrix.target }} - - run: sed -i '/nightly-only/d' Cargo.toml + - run: sed -i '/nightly-only/d' Cargo.toml if: matrix.rust != 'nightly' - run: cargo check --target=${{ matrix.target }} --features=${{ matrix.features }} From 851dae5ec7f6eec2c2a03df242302d2db8eb50c0 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 4 Jul 2023 17:46:22 +0200 Subject: [PATCH 039/199] Release e-h 1.0-alpha.11, e-h-async 0.2-alpha.2, e-h-bus 0.1-alpha.3, e-h-nb v1.0-alpha.3 --- embedded-hal-async/CHANGELOG.md | 12 +++++++++++- embedded-hal-async/Cargo.toml | 4 ++-- embedded-hal-bus/CHANGELOG.md | 9 ++++++++- embedded-hal-bus/Cargo.toml | 4 ++-- embedded-hal-nb/CHANGELOG.md | 11 +++++++++-- embedded-hal-nb/Cargo.toml | 4 ++-- embedded-hal/CHANGELOG.md | 9 ++++++++- embedded-hal/Cargo.toml | 2 +- 8 files changed, 43 insertions(+), 12 deletions(-) diff --git a/embedded-hal-async/CHANGELOG.md b/embedded-hal-async/CHANGELOG.md index 5da34a3ca..c1b4de3c3 100644 --- a/embedded-hal-async/CHANGELOG.md +++ b/embedded-hal-async/CHANGELOG.md @@ -7,6 +7,15 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] +## [v0.2.0-alpha.2] - 2023-07-04 + +### Added +- spi: added `Operation::DelayUs(u32)`. + +### Changed +- Updated `embedded-hal` to version `1.0.0-alpha.11`. +- spi: removed redundant lifetime annotations. Note that recent nightlies care about them and require impls to match, so you might have to adjust them. + ### Removed - spi: removed read-only and write-only traits. @@ -52,7 +61,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/). First release to crates.io -[Unreleased]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-async-v0.2.0-alpha.1...HEAD +[Unreleased]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-async-v0.2.0-alpha.2...HEAD +[v0.2.0-alpha.2]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-async-v0.2.0-alpha.1...embedded-hal-async-v0.2.0-alpha.2 [v0.2.0-alpha.1]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-async-v0.2.0-alpha.0...embedded-hal-async-v0.2.0-alpha.1 [v0.2.0-alpha.0]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-async-v0.1.0-alpha.3...embedded-hal-async-v0.2.0-alpha.0 [v0.1.0-alpha.3]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-async-v0.1.0-alpha.2...embedded-hal-async-v0.1.0-alpha.3 diff --git a/embedded-hal-async/Cargo.toml b/embedded-hal-async/Cargo.toml index a230a0f68..dbae01c7d 100644 --- a/embedded-hal-async/Cargo.toml +++ b/embedded-hal-async/Cargo.toml @@ -11,8 +11,8 @@ license = "MIT OR Apache-2.0" name = "embedded-hal-async" readme = "README.md" repository = "https://github.com/rust-embedded/embedded-hal" -version = "0.2.0-alpha.1" +version = "0.2.0-alpha.2" rust-version = "1.65.0" [dependencies] -embedded-hal = { version = "=1.0.0-alpha.10", path = "../embedded-hal" } +embedded-hal = { version = "=1.0.0-alpha.11", path = "../embedded-hal" } diff --git a/embedded-hal-bus/CHANGELOG.md b/embedded-hal-bus/CHANGELOG.md index 45abc9b4d..c97121910 100644 --- a/embedded-hal-bus/CHANGELOG.md +++ b/embedded-hal-bus/CHANGELOG.md @@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] +## [v0.1.0-alpha.3] - 2023-07-04 + +### Changed +- Updated `embedded-hal` to version `1.0.0-alpha.11`. + + ## [v0.1.0-alpha.2] - 2023-04-04 ### Changed @@ -25,7 +31,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/). First release to crates.io -[Unreleased]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-bus-v0.1.0-alpha.2...HEAD +[Unreleased]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-bus-v0.1.0-alpha.3...HEAD +[v0.1.0-alpha.3]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-bus-v0.1.0-alpha.2...embedded-hal-bus-v0.1.0-alpha.3 [v0.1.0-alpha.2]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-bus-v0.1.0-alpha.1...embedded-hal-bus-v0.1.0-alpha.2 [v0.1.0-alpha.1]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-bus-v0.1.0-alpha.0...embedded-hal-bus-v0.1.0-alpha.1 [v0.1.0-alpha.0]: https://github.com/rust-embedded/embedded-hal/tree/embedded-hal-bus-v0.1.0-alpha.0 diff --git a/embedded-hal-bus/Cargo.toml b/embedded-hal-bus/Cargo.toml index 2ad3815da..5e6f40995 100644 --- a/embedded-hal-bus/Cargo.toml +++ b/embedded-hal-bus/Cargo.toml @@ -11,11 +11,11 @@ license = "MIT OR Apache-2.0" name = "embedded-hal-bus" readme = "README.md" repository = "https://github.com/rust-embedded/embedded-hal" -version = "0.1.0-alpha.2" +version = "0.1.0-alpha.3" [features] std = [] [dependencies] -embedded-hal = { version = "=1.0.0-alpha.10", path = "../embedded-hal" } +embedded-hal = { version = "=1.0.0-alpha.11", path = "../embedded-hal" } critical-section = { version = "1.0" } diff --git a/embedded-hal-nb/CHANGELOG.md b/embedded-hal-nb/CHANGELOG.md index 0bc3a63f9..11ebab3b0 100644 --- a/embedded-hal-nb/CHANGELOG.md +++ b/embedded-hal-nb/CHANGELOG.md @@ -9,6 +9,12 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ... +## [v1.0.0-alpha.3] - 2023-07-04 + +### Changed +- Updated `embedded-hal` to version `1.0.0-alpha.11`. + + ## [v1.0.0-alpha.2] - 2023-04-04 ### Changed @@ -23,7 +29,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/). First release to crates.io -[Unreleased]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-nb-v1.0.0-alpha.2...HEAD -[v1.0.0-alpha.2]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-nb-v1.0.0-alpha.1...embedded-hal-nb-v1.0.0-alpha.2which +[Unreleased]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-nb-v1.0.0-alpha.3...HEAD +[v1.0.0-alpha.3]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-nb-v1.0.0-alpha.2...embedded-hal-nb-v1.0.0-alpha.3 +[v1.0.0-alpha.2]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-nb-v1.0.0-alpha.1...embedded-hal-nb-v1.0.0-alpha.2 [v1.0.0-alpha.1]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-nb-v1.0.0-alpha.0...embedded-hal-nb-v1.0.0-alpha.1 [v1.0.0-alpha.0]: https://github.com/rust-embedded/embedded-hal/tree/embedded-hal-nb-v1.0.0-alpha.0 diff --git a/embedded-hal-nb/Cargo.toml b/embedded-hal-nb/Cargo.toml index eefaa19a2..0bf3d7346 100644 --- a/embedded-hal-nb/Cargo.toml +++ b/embedded-hal-nb/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "embedded-hal-nb" -version = "1.0.0-alpha.2" +version = "1.0.0-alpha.3" edition = "2021" categories = ["embedded", "hardware-support", "no-std"] @@ -12,7 +12,7 @@ readme = "README.md" repository = "https://github.com/rust-embedded/embedded-hal" [dependencies] -embedded-hal = { version = "=1.0.0-alpha.10", path = "../embedded-hal" } +embedded-hal = { version = "=1.0.0-alpha.11", path = "../embedded-hal" } nb = "1" [dev-dependencies] diff --git a/embedded-hal/CHANGELOG.md b/embedded-hal/CHANGELOG.md index afd99a7a7..0803cb8f7 100644 --- a/embedded-hal/CHANGELOG.md +++ b/embedded-hal/CHANGELOG.md @@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] + +## [v1.0.0-alpha.11] - 2023-07-04 + +*** This is (also) an alpha release with breaking changes (sorry) *** + ### Added - spi: added `Operation::DelayUs(u32)`. @@ -286,7 +291,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/). Initial release -[Unreleased]: https://github.com/rust-embedded/embedded-hal/compare/v1.0.0-alpha.9...HEAD +[Unreleased]: https://github.com/rust-embedded/embedded-hal/compare/v1.0.0-alpha.11...HEAD +[v1.0.0-alpha.11]: https://github.com/rust-embedded/embedded-hal/compare/v1.0.0-alpha.10...v1.0.0-alpha.11 +[v1.0.0-alpha.10]: https://github.com/rust-embedded/embedded-hal/compare/v1.0.0-alpha.9...v1.0.0-alpha.10 [v1.0.0-alpha.9]: https://github.com/rust-embedded/embedded-hal/compare/v1.0.0-alpha.8...v1.0.0-alpha.9 [v1.0.0-alpha.8]: https://github.com/rust-embedded/embedded-hal/compare/v1.0.0-alpha.7...v1.0.0-alpha.8 [v1.0.0-alpha.7]: https://github.com/rust-embedded/embedded-hal/compare/v1.0.0-alpha.6...v1.0.0-alpha.7 diff --git a/embedded-hal/Cargo.toml b/embedded-hal/Cargo.toml index 5e8307061..c70b3106a 100644 --- a/embedded-hal/Cargo.toml +++ b/embedded-hal/Cargo.toml @@ -13,4 +13,4 @@ license = "MIT OR Apache-2.0" name = "embedded-hal" readme = "README.md" repository = "https://github.com/rust-embedded/embedded-hal" -version = "1.0.0-alpha.10" +version = "1.0.0-alpha.11" From 6e27094671a116b3e1301a6fcb5c7bb0b9c8dae8 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 4 Jul 2023 22:25:50 +0200 Subject: [PATCH 040/199] Add embedded-io. --- Cargo.toml | 1 + embedded-io/CHANGELOG.md | 34 +++++ embedded-io/Cargo.toml | 12 ++ embedded-io/LICENSE-APACHE | 201 ++++++++++++++++++++++++++ embedded-io/LICENSE-MIT | 25 ++++ embedded-io/README.md | 39 +++++ embedded-io/src/lib.rs | 281 +++++++++++++++++++++++++++++++++++++ 7 files changed, 593 insertions(+) create mode 100644 embedded-io/CHANGELOG.md create mode 100644 embedded-io/Cargo.toml create mode 100644 embedded-io/LICENSE-APACHE create mode 100644 embedded-io/LICENSE-MIT create mode 100644 embedded-io/README.md create mode 100644 embedded-io/src/lib.rs diff --git a/Cargo.toml b/Cargo.toml index 4322dde4e..2a8ca15a9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,4 +7,5 @@ members = [ "embedded-hal-nb", "embedded-hal-bus", "embedded-can", + "embedded-io", ] diff --git a/embedded-io/CHANGELOG.md b/embedded-io/CHANGELOG.md new file mode 100644 index 000000000..43db9e22f --- /dev/null +++ b/embedded-io/CHANGELOG.md @@ -0,0 +1,34 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## Unreleased + +- Moved `embedded_io::blocking` to the crate root. +- Split async traits to the `embedded-io-async` crate. +- Split async trait adapters to separate crates. + +## 0.4.0 - 2022-11-25 + +- Switch all traits to use [`async_fn_in_trait`](https://blog.rust-lang.org/inside-rust/2022/11/17/async-fn-in-trait-nightly.html) (AFIT). Requires `nightly-2022-11-22` or newer. + +## 0.3.1 - 2022-10-26 + +- Fix compilation on recent nightlies (#5) + +## 0.3.0 - 2022-05-19 + +- `FromFutures` adapter now requires `futures` Cargo feature. (breaking change) +- Add `FromTokio` adapter. +- Add blanket impls for `&mut T`, `Box`. +- Add impl `Read`, `BufRead` for `&[u8]` +- Add impl `Write` for `&mut [u8]` +- Add impl `Write` for `Vec` +- impl `std::error::Error` for `ReadExactError`, `WriteFmtError`. + +## 0.2.0 - 2022-05-07 + +- First release \ No newline at end of file diff --git a/embedded-io/Cargo.toml b/embedded-io/Cargo.toml new file mode 100644 index 000000000..71fe31203 --- /dev/null +++ b/embedded-io/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "embedded-io" +version = "0.5.0" +edition = "2021" +description = "Embedded IO traits" +repository = "https://github.com/rust-embedded/embedded-hal" +readme = "README.md" +license = "MIT OR Apache-2.0" +categories = [ + "embedded", + "no-std", +] diff --git a/embedded-io/LICENSE-APACHE b/embedded-io/LICENSE-APACHE new file mode 100644 index 000000000..16fe87b06 --- /dev/null +++ b/embedded-io/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/embedded-io/LICENSE-MIT b/embedded-io/LICENSE-MIT new file mode 100644 index 000000000..e00608fbd --- /dev/null +++ b/embedded-io/LICENSE-MIT @@ -0,0 +1,25 @@ +Copyright (c) 2023 The embedded-io authors + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/embedded-io/README.md b/embedded-io/README.md new file mode 100644 index 000000000..33461b3cf --- /dev/null +++ b/embedded-io/README.md @@ -0,0 +1,39 @@ +[![crates.io](https://img.shields.io/crates/d/embedded-io.svg)](https://crates.io/crates/embedded-io) +[![crates.io](https://img.shields.io/crates/v/embedded-io.svg)](https://crates.io/crates/embedded-io) +[![Documentation](https://docs.rs/embedded-io/badge.svg)](https://docs.rs/embedded-io) + +# `embedded-io` + +This project is developed and maintained by the [HAL team](https://github.com/rust-embedded/wg#the-hal-team). + +IO traits for embedded systems. + +Rust's `std::io` traits are not available in `no_std` targets, mainly because `std::io::Error` +requires allocation. This crate contains replacement equivalent traits, usable in `no_std` +targets. + +The only difference with `std::io` is `Error` is an associated type. This allows each implementor +to return its own error type, while avoiding `dyn` or `Box`. This is how errors are handled in [`embedded-hal`](https://github.com/rust-embedded/embedded-hal/). + +## Minimum Supported Rust Version (MSRV) + +This crate is guaranteed to compile on stable Rust 1.59 and up. It *might* +compile with older versions but that may change in any new patch release. + +See [here](../docs/msrv.md) for details on how the MSRV may be upgraded. + +## License + +Licensed under either of + +- Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or + ) +- MIT license ([LICENSE-MIT](LICENSE-MIT) or ) + +at your option. + +### Contribution + +Unless you explicitly state otherwise, any contribution intentionally submitted +for inclusion in the work by you, as defined in the Apache-2.0 license, shall be +dual licensed as above, without any additional terms or conditions. diff --git a/embedded-io/src/lib.rs b/embedded-io/src/lib.rs new file mode 100644 index 000000000..4d90b5ea5 --- /dev/null +++ b/embedded-io/src/lib.rs @@ -0,0 +1,281 @@ +#![no_std] +#![warn(missing_docs)] +#![doc = include_str!("../README.md")] + +use core::fmt; + +/// Enumeration of possible methods to seek within an I/O object. +/// +/// Semantics are the same as [`std::io::SeekFrom`], check its documentation for details. +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +pub enum SeekFrom { + /// Sets the offset to the provided number of bytes. + Start(u64), + /// Sets the offset to the size of this object plus the specified number of bytes. + End(i64), + /// Sets the offset to the current position plus the specified number of bytes. + Current(i64), +} + +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +#[non_exhaustive] +/// Possible kinds of errors. +pub enum ErrorKind { + /// Unspecified error kind. + Other, +} + +/// Error trait. +/// +/// This trait allows generic code to do limited inspecting of errors, +/// to react differently to different kinds. +pub trait Error: core::fmt::Debug { + /// Get the kind of this error. + fn kind(&self) -> ErrorKind; +} + +impl Error for core::convert::Infallible { + fn kind(&self) -> ErrorKind { + match *self {} + } +} + +impl Error for ErrorKind { + fn kind(&self) -> ErrorKind { + *self + } +} + +/// Base trait for all IO traits. +/// +/// All IO operations of all traits return the error defined in this trait. +/// +/// Having a shared trait instead of having every trait define its own +/// `Error` associated type enforces all impls on the same type use the same error. +/// This is very convenient when writing generic code, it means you have to +/// handle a single error type `T::Error`, instead of `::Error` and `::Error` +/// which might be different types. +pub trait Io { + /// Error type of all the IO operations on this type. + type Error: Error; +} + +impl crate::Io for &mut T { + type Error = T::Error; +} + +/// Error returned by [`Read::read_exact`] +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +pub enum ReadExactError { + /// An EOF error was encountered before reading the exact amount of requested bytes. + UnexpectedEof, + /// Error returned by the inner Read. + Other(E), +} + +impl From for ReadExactError { + fn from(err: E) -> Self { + Self::Other(err) + } +} + +impl fmt::Display for ReadExactError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{:?}", self) + } +} + +/// Error returned by [`Write::write_fmt`] +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +pub enum WriteFmtError { + /// [`Write::write`] wrote zero bytes + WriteZero, + /// An error was encountered while formatting. + FmtError, + /// Error returned by the inner Write. + Other(E), +} + +impl From for WriteFmtError { + fn from(err: E) -> Self { + Self::Other(err) + } +} + +impl fmt::Display for WriteFmtError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{:?}", self) + } +} + +/// Error returned by [`Write::write_all`] +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +pub enum WriteAllError { + /// [`Write::write`] wrote zero bytes + WriteZero, + /// Error returned by the inner Write. + Other(E), +} + +impl From for WriteAllError { + fn from(err: E) -> Self { + Self::Other(err) + } +} + +impl fmt::Display for WriteAllError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{:?}", self) + } +} + +/// Blocking reader. +/// +/// Semantics are the same as [`std::io::Read`], check its documentation for details. +pub trait Read: crate::Io { + /// Pull some bytes from this source into the specified buffer, returning how many bytes were read. + fn read(&mut self, buf: &mut [u8]) -> Result; + + /// Read the exact number of bytes required to fill `buf`. + fn read_exact(&mut self, mut buf: &mut [u8]) -> Result<(), ReadExactError> { + while !buf.is_empty() { + match self.read(buf) { + Ok(0) => break, + Ok(n) => buf = &mut buf[n..], + Err(e) => return Err(ReadExactError::Other(e)), + } + } + if !buf.is_empty() { + Err(ReadExactError::UnexpectedEof) + } else { + Ok(()) + } + } +} + +/// Blocking buffered reader. +/// +/// Semantics are the same as [`std::io::BufRead`], check its documentation for details. +pub trait BufRead: crate::Io { + /// Return the contents of the internal buffer, filling it with more data from the inner reader if it is empty. + fn fill_buf(&mut self) -> Result<&[u8], Self::Error>; + + /// Tell this buffer that `amt` bytes have been consumed from the buffer, so they should no longer be returned in calls to `fill_buf`. + fn consume(&mut self, amt: usize); +} + +/// Blocking writer. +/// +/// Semantics are the same as [`std::io::Write`], check its documentation for details. +pub trait Write: crate::Io { + /// Write a buffer into this writer, returning how many bytes were written. + fn write(&mut self, buf: &[u8]) -> Result; + + /// Flush this output stream, ensuring that all intermediately buffered contents reach their destination. + fn flush(&mut self) -> Result<(), Self::Error>; + + /// Write an entire buffer into this writer. + fn write_all(&mut self, mut buf: &[u8]) -> Result<(), WriteAllError> { + while !buf.is_empty() { + match self.write(buf) { + Ok(0) => return Err(WriteAllError::WriteZero), + Ok(n) => buf = &buf[n..], + Err(e) => return Err(WriteAllError::Other(e)), + } + } + Ok(()) + } + + /// Write a formatted string into this writer, returning any error encountered. + fn write_fmt(&mut self, fmt: fmt::Arguments<'_>) -> Result<(), WriteFmtError> { + // Create a shim which translates a Write to a fmt::Write and saves + // off I/O errors. instead of discarding them + struct Adapter<'a, T: Write + ?Sized + 'a> { + inner: &'a mut T, + error: Result<(), WriteAllError>, + } + + impl fmt::Write for Adapter<'_, T> { + fn write_str(&mut self, s: &str) -> fmt::Result { + match self.inner.write_all(s.as_bytes()) { + Ok(()) => Ok(()), + Err(e) => { + self.error = Err(e); + Err(fmt::Error) + } + } + } + } + + let mut output = Adapter { + inner: self, + error: Ok(()), + }; + match fmt::write(&mut output, fmt) { + Ok(()) => Ok(()), + Err(..) => match output.error { + // check if the error came from the underlying `Write` or not + Err(e) => match e { + WriteAllError::WriteZero => Err(WriteFmtError::WriteZero), + WriteAllError::Other(e) => Err(WriteFmtError::Other(e)), + }, + Ok(()) => Err(WriteFmtError::FmtError), + }, + } + } +} + +/// Blocking seek within streams. +/// +/// Semantics are the same as [`std::io::Seek`], check its documentation for details. +pub trait Seek: crate::Io { + /// Seek to an offset, in bytes, in a stream. + fn seek(&mut self, pos: crate::SeekFrom) -> Result; + + /// Rewind to the beginning of a stream. + fn rewind(&mut self) -> Result<(), Self::Error> { + self.seek(crate::SeekFrom::Start(0))?; + Ok(()) + } + + /// Returns the current seek position from the start of the stream. + fn stream_position(&mut self) -> Result { + self.seek(crate::SeekFrom::Current(0)) + } +} + +impl Read for &mut T { + #[inline] + fn read(&mut self, buf: &mut [u8]) -> Result { + T::read(self, buf) + } +} + +impl BufRead for &mut T { + fn fill_buf(&mut self) -> Result<&[u8], Self::Error> { + T::fill_buf(self) + } + + fn consume(&mut self, amt: usize) { + T::consume(self, amt) + } +} + +impl Write for &mut T { + #[inline] + fn write(&mut self, buf: &[u8]) -> Result { + T::write(self, buf) + } + + #[inline] + fn flush(&mut self) -> Result<(), Self::Error> { + T::flush(self) + } +} + +impl Seek for &mut T { + #[inline] + fn seek(&mut self, pos: crate::SeekFrom) -> Result { + T::seek(self, pos) + } +} From 6eb30c50d1bf49dde55fe25d46d695c06ba50b5f Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 4 Jul 2023 22:26:21 +0200 Subject: [PATCH 041/199] io: add impls for slices. --- embedded-io/src/impls/mod.rs | 2 ++ embedded-io/src/impls/slice_mut.rs | 31 ++++++++++++++++++++++ embedded-io/src/impls/slice_ref.rs | 41 ++++++++++++++++++++++++++++++ embedded-io/src/lib.rs | 2 ++ 4 files changed, 76 insertions(+) create mode 100644 embedded-io/src/impls/mod.rs create mode 100644 embedded-io/src/impls/slice_mut.rs create mode 100644 embedded-io/src/impls/slice_ref.rs diff --git a/embedded-io/src/impls/mod.rs b/embedded-io/src/impls/mod.rs new file mode 100644 index 000000000..7f4a6f802 --- /dev/null +++ b/embedded-io/src/impls/mod.rs @@ -0,0 +1,2 @@ +mod slice_mut; +mod slice_ref; diff --git a/embedded-io/src/impls/slice_mut.rs b/embedded-io/src/impls/slice_mut.rs new file mode 100644 index 000000000..cefaa296c --- /dev/null +++ b/embedded-io/src/impls/slice_mut.rs @@ -0,0 +1,31 @@ +use crate::{Io, Write}; +use core::mem; + +impl Io for &mut [u8] { + type Error = core::convert::Infallible; +} + +/// Write is implemented for `&mut [u8]` by copying into the slice, overwriting +/// its data. +/// +/// Note that writing updates the slice to point to the yet unwritten part. +/// The slice will be empty when it has been completely overwritten. +/// +/// If the number of bytes to be written exceeds the size of the slice, write operations will +/// return short writes: ultimately, `Ok(0)`; in this situation, `write_all` returns an error of +/// kind `ErrorKind::WriteZero`. +impl Write for &mut [u8] { + #[inline] + fn write(&mut self, buf: &[u8]) -> Result { + let amt = core::cmp::min(buf.len(), self.len()); + let (a, b) = mem::take(self).split_at_mut(amt); + a.copy_from_slice(&buf[..amt]); + *self = b; + Ok(amt) + } + + #[inline] + fn flush(&mut self) -> Result<(), Self::Error> { + Ok(()) + } +} diff --git a/embedded-io/src/impls/slice_ref.rs b/embedded-io/src/impls/slice_ref.rs new file mode 100644 index 000000000..73c98ab55 --- /dev/null +++ b/embedded-io/src/impls/slice_ref.rs @@ -0,0 +1,41 @@ +use crate::{BufRead, Io, Read}; + +impl Io for &[u8] { + type Error = core::convert::Infallible; +} + +/// Read is implemented for `&[u8]` by copying from the slice. +/// +/// Note that reading updates the slice to point to the yet unread part. +/// The slice will be empty when EOF is reached. +impl Read for &[u8] { + #[inline] + fn read(&mut self, buf: &mut [u8]) -> Result { + let amt = core::cmp::min(buf.len(), self.len()); + let (a, b) = self.split_at(amt); + + // First check if the amount of bytes we want to read is small: + // `copy_from_slice` will generally expand to a call to `memcpy`, and + // for a single byte the overhead is significant. + if amt == 1 { + buf[0] = a[0]; + } else { + buf[..amt].copy_from_slice(a); + } + + *self = b; + Ok(amt) + } +} + +impl BufRead for &[u8] { + #[inline] + fn fill_buf(&mut self) -> Result<&[u8], Self::Error> { + Ok(*self) + } + + #[inline] + fn consume(&mut self, amt: usize) { + *self = &self[amt..]; + } +} diff --git a/embedded-io/src/lib.rs b/embedded-io/src/lib.rs index 4d90b5ea5..6bafbbe53 100644 --- a/embedded-io/src/lib.rs +++ b/embedded-io/src/lib.rs @@ -4,6 +4,8 @@ use core::fmt; +mod impls; + /// Enumeration of possible methods to seek within an I/O object. /// /// Semantics are the same as [`std::io::SeekFrom`], check its documentation for details. From eb6e078b2688d59c7a5d2553a61bd1f70650f2af Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 4 Jul 2023 22:31:01 +0200 Subject: [PATCH 042/199] io: add std/alloc features, add Box/Vec impls. --- embedded-io/Cargo.toml | 8 ++++++ embedded-io/src/impls/boxx.rs | 47 +++++++++++++++++++++++++++++++++++ embedded-io/src/impls/mod.rs | 5 ++++ embedded-io/src/impls/vec.rs | 21 ++++++++++++++++ embedded-io/src/lib.rs | 15 ++++++++++- 5 files changed, 95 insertions(+), 1 deletion(-) create mode 100644 embedded-io/src/impls/boxx.rs create mode 100644 embedded-io/src/impls/vec.rs diff --git a/embedded-io/Cargo.toml b/embedded-io/Cargo.toml index 71fe31203..d1f82254f 100644 --- a/embedded-io/Cargo.toml +++ b/embedded-io/Cargo.toml @@ -10,3 +10,11 @@ categories = [ "embedded", "no-std", ] + +[features] +std = ["alloc"] +alloc = [] + +[package.metadata.docs.rs] +features = ["std"] +rustdoc-args = ["--cfg", "docsrs"] diff --git a/embedded-io/src/impls/boxx.rs b/embedded-io/src/impls/boxx.rs new file mode 100644 index 000000000..27bc1931c --- /dev/null +++ b/embedded-io/src/impls/boxx.rs @@ -0,0 +1,47 @@ +use crate::{BufRead, Io, Read, Seek, Write}; +use alloc::boxed::Box; + +#[cfg_attr(docsrs, doc(cfg(any(feature = "std", feature = "alloc"))))] +impl Io for Box { + type Error = T::Error; +} + +#[cfg_attr(docsrs, doc(cfg(any(feature = "std", feature = "alloc"))))] +impl Read for Box { + #[inline] + fn read(&mut self, buf: &mut [u8]) -> Result { + T::read(self, buf) + } +} + +#[cfg_attr(docsrs, doc(cfg(any(feature = "std", feature = "alloc"))))] +impl BufRead for Box { + fn fill_buf(&mut self) -> Result<&[u8], Self::Error> { + T::fill_buf(self) + } + + fn consume(&mut self, amt: usize) { + T::consume(self, amt) + } +} + +#[cfg_attr(docsrs, doc(cfg(any(feature = "std", feature = "alloc"))))] +impl Write for Box { + #[inline] + fn write(&mut self, buf: &[u8]) -> Result { + T::write(self, buf) + } + + #[inline] + fn flush(&mut self) -> Result<(), Self::Error> { + T::flush(self) + } +} + +#[cfg_attr(docsrs, doc(cfg(any(feature = "std", feature = "alloc"))))] +impl Seek for Box { + #[inline] + fn seek(&mut self, pos: crate::SeekFrom) -> Result { + T::seek(self, pos) + } +} diff --git a/embedded-io/src/impls/mod.rs b/embedded-io/src/impls/mod.rs index 7f4a6f802..e79b9b8bf 100644 --- a/embedded-io/src/impls/mod.rs +++ b/embedded-io/src/impls/mod.rs @@ -1,2 +1,7 @@ mod slice_mut; mod slice_ref; + +#[cfg(feature = "alloc")] +mod boxx; +#[cfg(feature = "alloc")] +mod vec; diff --git a/embedded-io/src/impls/vec.rs b/embedded-io/src/impls/vec.rs new file mode 100644 index 000000000..8aa1b62d6 --- /dev/null +++ b/embedded-io/src/impls/vec.rs @@ -0,0 +1,21 @@ +use crate::{Io, Write}; +use alloc::vec::Vec; + +#[cfg_attr(docsrs, doc(cfg(any(feature = "std", feature = "alloc"))))] +impl Io for Vec { + type Error = core::convert::Infallible; +} + +#[cfg_attr(docsrs, doc(cfg(any(feature = "std", feature = "alloc"))))] +impl Write for Vec { + #[inline] + fn write(&mut self, buf: &[u8]) -> Result { + self.extend_from_slice(buf); + Ok(buf.len()) + } + + #[inline] + fn flush(&mut self) -> Result<(), Self::Error> { + Ok(()) + } +} diff --git a/embedded-io/src/lib.rs b/embedded-io/src/lib.rs index 6bafbbe53..61f112832 100644 --- a/embedded-io/src/lib.rs +++ b/embedded-io/src/lib.rs @@ -1,9 +1,13 @@ -#![no_std] +#![cfg_attr(not(feature = "std"), no_std)] +#![cfg_attr(docsrs, feature(doc_cfg))] #![warn(missing_docs)] #![doc = include_str!("../README.md")] use core::fmt; +#[cfg(feature = "alloc")] +extern crate alloc; + mod impls; /// Enumeration of possible methods to seek within an I/O object. @@ -87,6 +91,9 @@ impl fmt::Display for ReadExactError { } } +#[cfg(feature = "std")] +impl std::error::Error for ReadExactError {} + /// Error returned by [`Write::write_fmt`] #[derive(Debug, Copy, Clone, Eq, PartialEq)] pub enum WriteFmtError { @@ -110,6 +117,9 @@ impl fmt::Display for WriteFmtError { } } +#[cfg(feature = "std")] +impl std::error::Error for WriteFmtError {} + /// Error returned by [`Write::write_all`] #[derive(Debug, Copy, Clone, Eq, PartialEq)] pub enum WriteAllError { @@ -131,6 +141,9 @@ impl fmt::Display for WriteAllError { } } +#[cfg(feature = "std")] +impl std::error::Error for WriteAllError {} + /// Blocking reader. /// /// Semantics are the same as [`std::io::Read`], check its documentation for details. From 1b5a858b1231185b60a6835c91581860c9ce6194 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 4 Jul 2023 22:32:39 +0200 Subject: [PATCH 043/199] io: add adapters to/from std::io. --- embedded-io/src/adapters.rs | 161 ++++++++++++++++++++++++++++++++++++ embedded-io/src/lib.rs | 3 + 2 files changed, 164 insertions(+) create mode 100644 embedded-io/src/adapters.rs diff --git a/embedded-io/src/adapters.rs b/embedded-io/src/adapters.rs new file mode 100644 index 000000000..942d52455 --- /dev/null +++ b/embedded-io/src/adapters.rs @@ -0,0 +1,161 @@ +//! Adapters to/from `std::io` traits. +//! +//! To interoperate with `std::io`, wrap a type in one of these +//! adapters. +//! +//! There's no separate adapters for Read/ReadBuf/Write traits. Instead, a single +//! adapter implements the right traits based on what the inner type implements. +//! This allows adapting a `Read+Write`, for example. + +use crate::SeekFrom; + +/// Adapter from `std::io` traits. +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] +#[derive(Clone)] +pub struct FromStd { + inner: T, +} + +impl FromStd { + /// Create a new adapter. + pub fn new(inner: T) -> Self { + Self { inner } + } + + /// Consume the adapter, returning the inner object. + pub fn into_inner(self) -> T { + self.inner + } +} + +impl FromStd { + /// Borrow the inner object. + pub fn inner(&self) -> &T { + &self.inner + } + + /// Mutably borrow the inner object. + pub fn inner_mut(&mut self) -> &mut T { + &mut self.inner + } +} + +impl crate::Io for FromStd { + type Error = std::io::Error; +} + +impl crate::Read for FromStd { + fn read(&mut self, buf: &mut [u8]) -> Result { + self.inner.read(buf) + } +} + +impl crate::BufRead for FromStd { + fn fill_buf(&mut self) -> Result<&[u8], Self::Error> { + self.inner.fill_buf() + } + + fn consume(&mut self, amt: usize) { + self.inner.consume(amt) + } +} + +impl crate::Write for FromStd { + fn write(&mut self, buf: &[u8]) -> Result { + self.inner.write(buf) + } + fn flush(&mut self) -> Result<(), Self::Error> { + self.inner.flush() + } +} + +impl crate::Seek for FromStd { + fn seek(&mut self, pos: crate::SeekFrom) -> Result { + self.inner.seek(pos.into()) + } +} + +/// Adapter to `std::io` traits. +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] +pub struct ToStd { + inner: T, +} + +impl ToStd { + /// Create a new adapter. + pub fn new(inner: T) -> Self { + Self { inner } + } + + /// Consume the adapter, returning the inner object. + pub fn into_inner(self) -> T { + self.inner + } +} + +impl ToStd { + /// Borrow the inner object. + pub fn inner(&self) -> &T { + &self.inner + } + + /// Mutably borrow the inner object. + pub fn inner_mut(&mut self) -> &mut T { + &mut self.inner + } +} + +impl std::io::Read for ToStd { + fn read(&mut self, buf: &mut [u8]) -> Result { + self.inner.read(buf).map_err(to_io_error) + } +} + +impl std::io::Write for ToStd { + fn write(&mut self, buf: &[u8]) -> Result { + self.inner.write(buf).map_err(to_io_error) + } + fn flush(&mut self) -> Result<(), std::io::Error> { + self.inner.flush().map_err(to_io_error) + } +} + +impl std::io::Seek for ToStd { + fn seek(&mut self, pos: std::io::SeekFrom) -> Result { + self.inner.seek(pos.into()).map_err(to_io_error) + } +} + +fn to_io_error(err: T) -> std::io::Error { + let kind = std::io::ErrorKind::Other; + std::io::Error::new(kind, format!("{:?}", err)) +} + +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] +impl crate::Error for std::io::Error { + fn kind(&self) -> crate::ErrorKind { + crate::ErrorKind::Other + } +} + +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] +impl From for std::io::SeekFrom { + fn from(pos: SeekFrom) -> Self { + match pos { + SeekFrom::Start(n) => std::io::SeekFrom::Start(n), + SeekFrom::End(n) => std::io::SeekFrom::End(n), + SeekFrom::Current(n) => std::io::SeekFrom::Current(n), + } + } +} + +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] +impl From for SeekFrom { + fn from(pos: std::io::SeekFrom) -> SeekFrom { + match pos { + std::io::SeekFrom::Start(n) => SeekFrom::Start(n), + std::io::SeekFrom::End(n) => SeekFrom::End(n), + std::io::SeekFrom::Current(n) => SeekFrom::Current(n), + } + } +} diff --git a/embedded-io/src/lib.rs b/embedded-io/src/lib.rs index 61f112832..07bf60517 100644 --- a/embedded-io/src/lib.rs +++ b/embedded-io/src/lib.rs @@ -8,6 +8,9 @@ use core::fmt; #[cfg(feature = "alloc")] extern crate alloc; +#[cfg(feature = "std")] +pub mod adapters; + mod impls; /// Enumeration of possible methods to seek within an I/O object. From d6f641995fc1597920b7d86bf58d0332b7ebbaeb Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 4 Jul 2023 22:39:53 +0200 Subject: [PATCH 044/199] Add embedded-io-async. --- Cargo.toml | 1 + embedded-io-async/Cargo.toml | 15 +++++ embedded-io-async/README.md | 35 ++++++++++ embedded-io-async/src/lib.rs | 123 +++++++++++++++++++++++++++++++++++ 4 files changed, 174 insertions(+) create mode 100644 embedded-io-async/Cargo.toml create mode 100644 embedded-io-async/README.md create mode 100644 embedded-io-async/src/lib.rs diff --git a/Cargo.toml b/Cargo.toml index 2a8ca15a9..ab7723059 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,4 +8,5 @@ members = [ "embedded-hal-bus", "embedded-can", "embedded-io", + "embedded-io-async", # nightly-only ] diff --git a/embedded-io-async/Cargo.toml b/embedded-io-async/Cargo.toml new file mode 100644 index 000000000..e900278f9 --- /dev/null +++ b/embedded-io-async/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "embedded-io-async" +version = "0.5.0" +edition = "2021" +description = "Async embedded IO traits" +repository = "https://github.com/rust-embedded/embedded-hal" +readme = "README.md" +license = "MIT OR Apache-2.0" +categories = [ + "embedded", + "no-std", +] + +[dependencies] +embedded-io = { version = "0.5", path = "../embedded-io" } diff --git a/embedded-io-async/README.md b/embedded-io-async/README.md new file mode 100644 index 000000000..557a0866a --- /dev/null +++ b/embedded-io-async/README.md @@ -0,0 +1,35 @@ +[![crates.io](https://img.shields.io/crates/d/embedded-io-async.svg)](https://crates.io/crates/embedded-io-async) +[![crates.io](https://img.shields.io/crates/v/embedded-io-async.svg)](https://crates.io/crates/embedded-io-async) +[![Documentation](https://docs.rs/embedded-io-async/badge.svg)](https://docs.rs/embedded-io-async) + +# `embedded-io-async` + +Async IO traits for embedded systems. + +This crate contains asynchronous versions of the [`embedded-io`](https://crates.io/crates/embedded-io) traits and shares its scope and design goals. + +This project is developed and maintained by the [HAL team](https://github.com/rust-embedded/wg#the-hal-team). + +## Minimum Supported Rust Version (MSRV) + +This crate requires Rust nightly newer than `nightly-2022-11-22`, due to requiring support for +`async fn` in traits (AFIT), which is not stable yet. + +Keep in mind Rust nightlies can make backwards-incompatible changes to unstable features +at any time. + +## License + +Licensed under either of + +- Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or + ) +- MIT license ([LICENSE-MIT](LICENSE-MIT) or ) + +at your option. + +### Contribution + +Unless you explicitly state otherwise, any contribution intentionally submitted +for inclusion in the work by you, as defined in the Apache-2.0 license, shall be +dual licensed as above, without any additional terms or conditions. diff --git a/embedded-io-async/src/lib.rs b/embedded-io-async/src/lib.rs new file mode 100644 index 000000000..8bf4fd26f --- /dev/null +++ b/embedded-io-async/src/lib.rs @@ -0,0 +1,123 @@ +#![no_std] +#![feature(async_fn_in_trait, impl_trait_projections)] +#![cfg_attr(docsrs, feature(doc_cfg))] +#![warn(missing_docs)] +#![doc = include_str!("../README.md")] + +pub use embedded_io::{Error, ErrorKind, Io, ReadExactError, SeekFrom, WriteAllError}; + +/// Async reader. +/// +/// Semantics are the same as [`std::io::Read`], check its documentation for details. +pub trait Read: Io { + /// Pull some bytes from this source into the specified buffer, returning how many bytes were read. + async fn read(&mut self, buf: &mut [u8]) -> Result; + + /// Read the exact number of bytes required to fill `buf`. + async fn read_exact(&mut self, mut buf: &mut [u8]) -> Result<(), ReadExactError> { + while !buf.is_empty() { + match self.read(buf).await { + Ok(0) => break, + Ok(n) => buf = &mut buf[n..], + Err(e) => return Err(ReadExactError::Other(e)), + } + } + if !buf.is_empty() { + Err(ReadExactError::UnexpectedEof) + } else { + Ok(()) + } + } +} + +/// Async buffered reader. +/// +/// Semantics are the same as [`std::io::BufRead`], check its documentation for details. +pub trait BufRead: Io { + /// Return the contents of the internal buffer, filling it with more data from the inner reader if it is empty. + async fn fill_buf(&mut self) -> Result<&[u8], Self::Error>; + + /// Tell this buffer that `amt` bytes have been consumed from the buffer, so they should no longer be returned in calls to `fill_buf`. + fn consume(&mut self, amt: usize); +} + +/// Async writer. +/// +/// Semantics are the same as [`std::io::Write`], check its documentation for details. +pub trait Write: Io { + /// Write a buffer into this writer, returning how many bytes were written. + async fn write(&mut self, buf: &[u8]) -> Result; + + /// Flush this output stream, ensuring that all intermediately buffered contents reach their destination. + async fn flush(&mut self) -> Result<(), Self::Error> { + Ok(()) + } + + /// Write an entire buffer into this writer. + async fn write_all(&mut self, buf: &[u8]) -> Result<(), WriteAllError> { + let mut buf = buf; + while !buf.is_empty() { + match self.write(buf).await { + Ok(0) => return Err(WriteAllError::WriteZero), + Ok(n) => buf = &buf[n..], + Err(e) => return Err(WriteAllError::Other(e)), + } + } + Ok(()) + } +} + +/// Async seek within streams. +/// +/// Semantics are the same as [`std::io::Seek`], check its documentation for details. +pub trait Seek: Io { + /// Seek to an offset, in bytes, in a stream. + async fn seek(&mut self, pos: SeekFrom) -> Result; + + /// Rewind to the beginning of a stream. + async fn rewind(&mut self) -> Result<(), Self::Error> { + self.seek(SeekFrom::Start(0)).await?; + Ok(()) + } + + /// Returns the current seek position from the start of the stream. + async fn stream_position(&mut self) -> Result { + self.seek(SeekFrom::Current(0)).await + } +} + +impl Read for &mut T { + #[inline] + async fn read(&mut self, buf: &mut [u8]) -> Result { + T::read(self, buf).await + } +} + +impl BufRead for &mut T { + async fn fill_buf(&mut self) -> Result<&[u8], Self::Error> { + T::fill_buf(self).await + } + + fn consume(&mut self, amt: usize) { + T::consume(self, amt) + } +} + +impl Write for &mut T { + #[inline] + async fn write(&mut self, buf: &[u8]) -> Result { + T::write(self, buf).await + } + + #[inline] + async fn flush(&mut self) -> Result<(), Self::Error> { + T::flush(self).await + } +} + +impl Seek for &mut T { + #[inline] + async fn seek(&mut self, pos: SeekFrom) -> Result { + T::seek(self, pos).await + } +} From a105876f16045ae37b67e19f67c672112164c516 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 4 Jul 2023 22:58:10 +0200 Subject: [PATCH 045/199] io-async: add impls for slices. --- embedded-io-async/src/impls/mod.rs | 2 ++ embedded-io-async/src/impls/slice_mut.rs | 22 ++++++++++++++ embedded-io-async/src/impls/slice_ref.rs | 37 ++++++++++++++++++++++++ embedded-io-async/src/lib.rs | 2 ++ 4 files changed, 63 insertions(+) create mode 100644 embedded-io-async/src/impls/mod.rs create mode 100644 embedded-io-async/src/impls/slice_mut.rs create mode 100644 embedded-io-async/src/impls/slice_ref.rs diff --git a/embedded-io-async/src/impls/mod.rs b/embedded-io-async/src/impls/mod.rs new file mode 100644 index 000000000..7f4a6f802 --- /dev/null +++ b/embedded-io-async/src/impls/mod.rs @@ -0,0 +1,2 @@ +mod slice_mut; +mod slice_ref; diff --git a/embedded-io-async/src/impls/slice_mut.rs b/embedded-io-async/src/impls/slice_mut.rs new file mode 100644 index 000000000..4a195e3f4 --- /dev/null +++ b/embedded-io-async/src/impls/slice_mut.rs @@ -0,0 +1,22 @@ +use crate::Write; +use core::mem; + +/// Write is implemented for `&mut [u8]` by copying into the slice, overwriting +/// its data. +/// +/// Note that writing updates the slice to point to the yet unwritten part. +/// The slice will be empty when it has been completely overwritten. +/// +/// If the number of bytes to be written exceeds the size of the slice, write operations will +/// return short writes: ultimately, `Ok(0)`; in this situation, `write_all` returns an error of +/// kind `ErrorKind::WriteZero`. +impl Write for &mut [u8] { + #[inline] + async fn write(&mut self, buf: &[u8]) -> Result { + let amt = core::cmp::min(buf.len(), self.len()); + let (a, b) = mem::take(self).split_at_mut(amt); + a.copy_from_slice(&buf[..amt]); + *self = b; + Ok(amt) + } +} diff --git a/embedded-io-async/src/impls/slice_ref.rs b/embedded-io-async/src/impls/slice_ref.rs new file mode 100644 index 000000000..a6a4ba807 --- /dev/null +++ b/embedded-io-async/src/impls/slice_ref.rs @@ -0,0 +1,37 @@ +use crate::{BufRead, Read}; + +/// Read is implemented for `&[u8]` by copying from the slice. +/// +/// Note that reading updates the slice to point to the yet unread part. +/// The slice will be empty when EOF is reached. +impl Read for &[u8] { + #[inline] + async fn read(&mut self, buf: &mut [u8]) -> Result { + let amt = core::cmp::min(buf.len(), self.len()); + let (a, b) = self.split_at(amt); + + // First check if the amount of bytes we want to read is small: + // `copy_from_slice` will generally expand to a call to `memcpy`, and + // for a single byte the overhead is significant. + if amt == 1 { + buf[0] = a[0]; + } else { + buf[..amt].copy_from_slice(a); + } + + *self = b; + Ok(amt) + } +} + +impl BufRead for &[u8] { + #[inline] + async fn fill_buf(&mut self) -> Result<&[u8], Self::Error> { + Ok(*self) + } + + #[inline] + fn consume(&mut self, amt: usize) { + *self = &self[amt..]; + } +} diff --git a/embedded-io-async/src/lib.rs b/embedded-io-async/src/lib.rs index 8bf4fd26f..0e120c40c 100644 --- a/embedded-io-async/src/lib.rs +++ b/embedded-io-async/src/lib.rs @@ -4,6 +4,8 @@ #![warn(missing_docs)] #![doc = include_str!("../README.md")] +mod impls; + pub use embedded_io::{Error, ErrorKind, Io, ReadExactError, SeekFrom, WriteAllError}; /// Async reader. From 2c818b8b09aee0014a2b88e4997bd3a01e5f5a07 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 4 Jul 2023 23:00:22 +0200 Subject: [PATCH 046/199] io-async: add std/alloc features, add Box/Vec impls. --- embedded-io-async/Cargo.toml | 8 ++++++ embedded-io-async/src/impls/boxx.rs | 44 +++++++++++++++++++++++++++++ embedded-io-async/src/impls/mod.rs | 5 ++++ embedded-io-async/src/impls/vec.rs | 12 ++++++++ embedded-io-async/src/lib.rs | 5 +++- 5 files changed, 73 insertions(+), 1 deletion(-) create mode 100644 embedded-io-async/src/impls/boxx.rs create mode 100644 embedded-io-async/src/impls/vec.rs diff --git a/embedded-io-async/Cargo.toml b/embedded-io-async/Cargo.toml index e900278f9..e8f19af39 100644 --- a/embedded-io-async/Cargo.toml +++ b/embedded-io-async/Cargo.toml @@ -11,5 +11,13 @@ categories = [ "no-std", ] +[features] +std = ["alloc", "embedded-io/std"] +alloc = ["embedded-io/alloc"] + [dependencies] embedded-io = { version = "0.5", path = "../embedded-io" } + +[package.metadata.docs.rs] +features = ["std"] +rustdoc-args = ["--cfg", "docsrs"] diff --git a/embedded-io-async/src/impls/boxx.rs b/embedded-io-async/src/impls/boxx.rs new file mode 100644 index 000000000..677bf328d --- /dev/null +++ b/embedded-io-async/src/impls/boxx.rs @@ -0,0 +1,44 @@ +use crate::{BufRead, Read, Seek, SeekFrom, Write}; +use alloc::boxed::Box; + +#[cfg_attr(docsrs, doc(cfg(any(feature = "std", feature = "alloc"))))] +impl Read for Box { + #[inline] + async fn read(&mut self, buf: &mut [u8]) -> Result { + T::read(self, buf).await + } +} + +#[cfg_attr(docsrs, doc(cfg(any(feature = "std", feature = "alloc"))))] +impl BufRead for Box { + #[inline] + async fn fill_buf(&mut self) -> Result<&[u8], Self::Error> { + T::fill_buf(self).await + } + + #[inline] + fn consume(&mut self, amt: usize) { + T::consume(self, amt) + } +} + +#[cfg_attr(docsrs, doc(cfg(any(feature = "std", feature = "alloc"))))] +impl Write for Box { + #[inline] + async fn write(&mut self, buf: &[u8]) -> Result { + T::write(self, buf).await + } + + #[inline] + async fn flush(&mut self) -> Result<(), Self::Error> { + T::flush(self).await + } +} + +#[cfg_attr(docsrs, doc(cfg(any(feature = "std", feature = "alloc"))))] +impl Seek for Box { + #[inline] + async fn seek(&mut self, pos: SeekFrom) -> Result { + T::seek(self, pos).await + } +} diff --git a/embedded-io-async/src/impls/mod.rs b/embedded-io-async/src/impls/mod.rs index 7f4a6f802..e79b9b8bf 100644 --- a/embedded-io-async/src/impls/mod.rs +++ b/embedded-io-async/src/impls/mod.rs @@ -1,2 +1,7 @@ mod slice_mut; mod slice_ref; + +#[cfg(feature = "alloc")] +mod boxx; +#[cfg(feature = "alloc")] +mod vec; diff --git a/embedded-io-async/src/impls/vec.rs b/embedded-io-async/src/impls/vec.rs new file mode 100644 index 000000000..c24405e39 --- /dev/null +++ b/embedded-io-async/src/impls/vec.rs @@ -0,0 +1,12 @@ +use alloc::vec::Vec; + +use crate::Write; + +#[cfg_attr(docsrs, doc(cfg(any(feature = "std", feature = "alloc"))))] +impl Write for Vec { + #[inline] + async fn write(&mut self, buf: &[u8]) -> Result { + self.extend_from_slice(buf); + Ok(buf.len()) + } +} diff --git a/embedded-io-async/src/lib.rs b/embedded-io-async/src/lib.rs index 0e120c40c..8ef951450 100644 --- a/embedded-io-async/src/lib.rs +++ b/embedded-io-async/src/lib.rs @@ -1,9 +1,12 @@ -#![no_std] #![feature(async_fn_in_trait, impl_trait_projections)] +#![cfg_attr(not(feature = "std"), no_std)] #![cfg_attr(docsrs, feature(doc_cfg))] #![warn(missing_docs)] #![doc = include_str!("../README.md")] +#[cfg(feature = "alloc")] +extern crate alloc; + mod impls; pub use embedded_io::{Error, ErrorKind, Io, ReadExactError, SeekFrom, WriteAllError}; From 931dd37ad66ad6588dcbbec9481e5f9323719e0a Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 4 Jul 2023 23:05:19 +0200 Subject: [PATCH 047/199] Remove serial traits (blocking and async). --- embedded-hal-async/src/lib.rs | 1 - embedded-hal-async/src/serial.rs | 27 --------- embedded-hal-nb/src/serial.rs | 76 ++++++++++++++++++++++++- embedded-hal/src/lib.rs | 1 - embedded-hal/src/serial.rs | 98 -------------------------------- 5 files changed, 73 insertions(+), 130 deletions(-) delete mode 100644 embedded-hal-async/src/serial.rs delete mode 100644 embedded-hal/src/serial.rs diff --git a/embedded-hal-async/src/lib.rs b/embedded-hal-async/src/lib.rs index 83410cdb9..37a81c0ff 100644 --- a/embedded-hal-async/src/lib.rs +++ b/embedded-hal-async/src/lib.rs @@ -7,5 +7,4 @@ pub mod delay; pub mod digital; pub mod i2c; -pub mod serial; pub mod spi; diff --git a/embedded-hal-async/src/serial.rs b/embedded-hal-async/src/serial.rs deleted file mode 100644 index 60ee1bc05..000000000 --- a/embedded-hal-async/src/serial.rs +++ /dev/null @@ -1,27 +0,0 @@ -//! Serial interface - -pub use embedded_hal::serial::{Error, ErrorKind, ErrorType}; - -/// Write half of a serial interface -pub trait Write: ErrorType { - /// Writes a slice, blocking until everything has been written. - /// - /// An implementation can choose to buffer the write, returning `Ok(())` - /// after the complete slice has been written to a buffer, but before all - /// words have been sent via the serial interface. To make sure that - /// everything has been sent, call [`flush`](Write::flush) after this function returns. - async fn write(&mut self, words: &[Word]) -> Result<(), Self::Error>; - - /// Ensures that none of the previously written data is still buffered - async fn flush(&mut self) -> Result<(), Self::Error>; -} - -impl, Word: 'static + Copy> Write for &mut T { - async fn write(&mut self, words: &[Word]) -> Result<(), Self::Error> { - T::write(self, words).await - } - - async fn flush(&mut self) -> Result<(), Self::Error> { - T::flush(self).await - } -} diff --git a/embedded-hal-nb/src/serial.rs b/embedded-hal-nb/src/serial.rs index e01803ab1..c069c1441 100644 --- a/embedded-hal-nb/src/serial.rs +++ b/embedded-hal-nb/src/serial.rs @@ -1,6 +1,77 @@ //! Serial interface -pub use embedded_hal::serial::{Error, ErrorKind, ErrorType}; +/// Serial error +pub trait Error: core::fmt::Debug { + /// Convert error to a generic serial error kind + /// + /// By using this method, serial errors freely defined by HAL implementations + /// can be converted to a set of generic serial errors upon which generic + /// code can act. + fn kind(&self) -> ErrorKind; +} + +impl Error for core::convert::Infallible { + fn kind(&self) -> ErrorKind { + match *self {} + } +} + +/// Serial error kind +/// +/// This represents a common set of serial operation errors. HAL implementations are +/// free to define more specific or additional error types. However, by providing +/// a mapping to these common serial errors, generic code can still react to them. +#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] +#[non_exhaustive] +pub enum ErrorKind { + /// The peripheral receive buffer was overrun. + Overrun, + /// Received data does not conform to the peripheral configuration. + /// Can be caused by a misconfigured device on either end of the serial line. + FrameFormat, + /// Parity check failed. + Parity, + /// Serial line is too noisy to read valid data. + Noise, + /// A different error occurred. The original error may contain more information. + Other, +} + +impl Error for ErrorKind { + fn kind(&self) -> ErrorKind { + *self + } +} + +impl core::fmt::Display for ErrorKind { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + match self { + Self::Overrun => write!(f, "The peripheral receive buffer was overrun"), + Self::Parity => write!(f, "Parity check failed"), + Self::Noise => write!(f, "Serial line is too noisy to read valid data"), + Self::FrameFormat => write!( + f, + "Received data does not conform to the peripheral configuration" + ), + Self::Other => write!( + f, + "A different error occurred. The original error may contain more information" + ), + } + } +} + +/// Serial error type trait +/// +/// This just defines the error type, to be used by the other traits. +pub trait ErrorType { + /// Error type + type Error: Error; +} + +impl ErrorType for &mut T { + type Error = T::Error; +} /// Read half of a serial interface /// @@ -40,8 +111,7 @@ impl, Word: Copy> Write for &mut T { /// /// TODO write example of usage -impl core::fmt::Write - for dyn Write + '_ +impl core::fmt::Write for dyn Write + '_ where Word: Copy + From, { diff --git a/embedded-hal/src/lib.rs b/embedded-hal/src/lib.rs index def14dbc0..ac09042b3 100644 --- a/embedded-hal/src/lib.rs +++ b/embedded-hal/src/lib.rs @@ -81,7 +81,6 @@ pub mod delay; pub mod digital; pub mod i2c; pub mod pwm; -pub mod serial; pub mod spi; mod private { diff --git a/embedded-hal/src/serial.rs b/embedded-hal/src/serial.rs deleted file mode 100644 index ab9be722b..000000000 --- a/embedded-hal/src/serial.rs +++ /dev/null @@ -1,98 +0,0 @@ -//! Serial traits - -/// Serial error -pub trait Error: core::fmt::Debug { - /// Convert error to a generic serial error kind - /// - /// By using this method, serial errors freely defined by HAL implementations - /// can be converted to a set of generic serial errors upon which generic - /// code can act. - fn kind(&self) -> ErrorKind; -} - -impl Error for core::convert::Infallible { - fn kind(&self) -> ErrorKind { - match *self {} - } -} - -/// Serial error kind -/// -/// This represents a common set of serial operation errors. HAL implementations are -/// free to define more specific or additional error types. However, by providing -/// a mapping to these common serial errors, generic code can still react to them. -#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] -#[non_exhaustive] -pub enum ErrorKind { - /// The peripheral receive buffer was overrun. - Overrun, - /// Received data does not conform to the peripheral configuration. - /// Can be caused by a misconfigured device on either end of the serial line. - FrameFormat, - /// Parity check failed. - Parity, - /// Serial line is too noisy to read valid data. - Noise, - /// A different error occurred. The original error may contain more information. - Other, -} - -impl Error for ErrorKind { - fn kind(&self) -> ErrorKind { - *self - } -} - -impl core::fmt::Display for ErrorKind { - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - match self { - Self::Overrun => write!(f, "The peripheral receive buffer was overrun"), - Self::Parity => write!(f, "Parity check failed"), - Self::Noise => write!(f, "Serial line is too noisy to read valid data"), - Self::FrameFormat => write!( - f, - "Received data does not conform to the peripheral configuration" - ), - Self::Other => write!( - f, - "A different error occurred. The original error may contain more information" - ), - } - } -} - -/// Serial error type trait -/// -/// This just defines the error type, to be used by the other traits. -pub trait ErrorType { - /// Error type - type Error: Error; -} - -impl ErrorType for &mut T { - type Error = T::Error; -} - -/// Write half of a serial interface (blocking variant) -pub trait Write: ErrorType { - /// Writes a slice, blocking until everything has been written - /// - /// An implementation can choose to buffer the write, returning `Ok(())` - /// after the complete slice has been written to a buffer, but before all - /// words have been sent via the serial interface. To make sure that - /// everything has been sent, call [`flush`](Write::flush) after this function returns. - fn write(&mut self, buffer: &[Word]) -> Result<(), Self::Error>; - - /// Block until the serial interface has sent all buffered words - fn flush(&mut self) -> Result<(), Self::Error>; -} - -impl, Word: Copy> Write for &mut T { - fn write(&mut self, buffer: &[Word]) -> Result<(), Self::Error> { - T::write(self, buffer) - } - - fn flush(&mut self) -> Result<(), Self::Error> { - T::flush(self) - } -} From 31b62d3a0775b2d4b5ea45e9500259f02af26daa Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 5 Jul 2023 00:09:19 +0200 Subject: [PATCH 048/199] io: add ReadReady, WriteReady. --- embedded-io-async/src/lib.rs | 4 +++- embedded-io/CHANGELOG.md | 1 + embedded-io/src/impls/boxx.rs | 18 ++++++++++++++- embedded-io/src/lib.rs | 41 +++++++++++++++++++++++++++++++++++ 4 files changed, 62 insertions(+), 2 deletions(-) diff --git a/embedded-io-async/src/lib.rs b/embedded-io-async/src/lib.rs index 8ef951450..f204eada5 100644 --- a/embedded-io-async/src/lib.rs +++ b/embedded-io-async/src/lib.rs @@ -9,7 +9,9 @@ extern crate alloc; mod impls; -pub use embedded_io::{Error, ErrorKind, Io, ReadExactError, SeekFrom, WriteAllError}; +pub use embedded_io::{ + Error, ErrorKind, Io, ReadExactError, ReadReady, SeekFrom, WriteAllError, WriteReady, +}; /// Async reader. /// diff --git a/embedded-io/CHANGELOG.md b/embedded-io/CHANGELOG.md index 43db9e22f..6af13a646 100644 --- a/embedded-io/CHANGELOG.md +++ b/embedded-io/CHANGELOG.md @@ -7,6 +7,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased +- Added `ReadReady`, `WriteReady` traits. They allow peeking whether the i/o handle is ready to read/write, so they allow using the traits in a non-blocking way. - Moved `embedded_io::blocking` to the crate root. - Split async traits to the `embedded-io-async` crate. - Split async trait adapters to separate crates. diff --git a/embedded-io/src/impls/boxx.rs b/embedded-io/src/impls/boxx.rs index 27bc1931c..727d1f21c 100644 --- a/embedded-io/src/impls/boxx.rs +++ b/embedded-io/src/impls/boxx.rs @@ -1,4 +1,4 @@ -use crate::{BufRead, Io, Read, Seek, Write}; +use crate::{BufRead, Io, Read, ReadReady, Seek, Write, WriteReady}; use alloc::boxed::Box; #[cfg_attr(docsrs, doc(cfg(any(feature = "std", feature = "alloc"))))] @@ -45,3 +45,19 @@ impl Seek for Box { T::seek(self, pos) } } + +#[cfg_attr(docsrs, doc(cfg(any(feature = "std", feature = "alloc"))))] +impl ReadReady for Box { + #[inline] + fn read_ready(&mut self) -> Result { + T::read_ready(self) + } +} + +#[cfg_attr(docsrs, doc(cfg(any(feature = "std", feature = "alloc"))))] +impl WriteReady for Box { + #[inline] + fn write_ready(&mut self) -> Result { + T::write_ready(self) + } +} diff --git a/embedded-io/src/lib.rs b/embedded-io/src/lib.rs index 07bf60517..7f7aa2eb9 100644 --- a/embedded-io/src/lib.rs +++ b/embedded-io/src/lib.rs @@ -262,6 +262,33 @@ pub trait Seek: crate::Io { } } +/// Get whether a reader is ready. +/// +/// This allows using a [`Read`] or [`BufRead`] in a nonblocking fashion, i.e. trying to read +/// only when it is ready. +pub trait ReadReady: crate::Io { + /// Get whether the reader is ready for immediately reading. + /// + /// This usually means that there is either some bytes have been received and are buffered and ready to be read, + /// or that the reader is at EOF. + /// + /// If this returns `true`, it's guaranteed that the next call to [`Read::read`] or [`BufRead::fill_buf`] will not block. + fn read_ready(&mut self) -> Result; +} + +/// Get whether a writer is ready. +/// +/// This allows using a [`Write`] in a nonblocking fashion, i.e. trying to write +/// only when it is ready. +pub trait WriteReady: crate::Io { + /// Get whether the writer is ready for immediately writeing. + /// + /// This usually means that there is free space in the internal transmit buffer. + /// + /// If this returns `true`, it's guaranteed that the next call to [`Write::write`] will not block. + fn write_ready(&mut self) -> Result; +} + impl Read for &mut T { #[inline] fn read(&mut self, buf: &mut [u8]) -> Result { @@ -297,3 +324,17 @@ impl Seek for &mut T { T::seek(self, pos) } } + +impl ReadReady for &mut T { + #[inline] + fn read_ready(&mut self) -> Result { + T::read_ready(self) + } +} + +impl WriteReady for &mut T { + #[inline] + fn write_ready(&mut self) -> Result { + T::write_ready(self) + } +} From 5df51e56dae11fc4fbe1bb9fc6ed08ceb1958e2c Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 4 Jul 2023 23:02:02 +0200 Subject: [PATCH 049/199] ci: test io with std and alloc features, update nightly. --- .github/workflows/clippy.yml | 4 ++-- .github/workflows/test.yml | 4 +++- embedded-hal-async/src/lib.rs | 1 - 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/.github/workflows/clippy.yml b/.github/workflows/clippy.yml index 20cd030f6..bdec38346 100644 --- a/.github/workflows/clippy.yml +++ b/.github/workflows/clippy.yml @@ -15,6 +15,6 @@ jobs: with: # embedded-hal-async needs nightly. # Use a pinned version to avoid spontaneous breakages (new clippy lints are added often) - toolchain: nightly-2022-11-22 + toolchain: nightly-2023-07-03 components: clippy - - run: cargo clippy --features=embedded-hal-bus/std -- --deny=warnings \ No newline at end of file + - run: cargo clippy --features=std -- --deny=warnings \ No newline at end of file diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 1677ea895..e50f0c6a4 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -25,7 +25,9 @@ jobs: - thumbv7m-none-eabi include: - target: x86_64-unknown-linux-gnu - features: embedded-hal-bus/std + features: std + - target: x86_64-unknown-linux-gnu + features: alloc steps: - uses: actions/checkout@v3 diff --git a/embedded-hal-async/src/lib.rs b/embedded-hal-async/src/lib.rs index 37a81c0ff..5fe9168e7 100644 --- a/embedded-hal-async/src/lib.rs +++ b/embedded-hal-async/src/lib.rs @@ -1,7 +1,6 @@ #![doc = include_str!("../README.md")] #![warn(missing_docs)] #![no_std] -#![allow(incomplete_features)] #![feature(async_fn_in_trait, impl_trait_projections)] pub mod delay; From e36386845ce7fc6969e0ea5b3ae5042113e92df6 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 12 Jul 2023 12:48:27 +0200 Subject: [PATCH 050/199] Apply suggestions from code review Co-authored-by: Diego Barrios Romero --- embedded-io/CHANGELOG.md | 2 +- embedded-io/README.md | 2 +- embedded-io/src/adapters.rs | 14 +++++++------- embedded-io/src/lib.rs | 2 +- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/embedded-io/CHANGELOG.md b/embedded-io/CHANGELOG.md index 6af13a646..f7043a7de 100644 --- a/embedded-io/CHANGELOG.md +++ b/embedded-io/CHANGELOG.md @@ -7,7 +7,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased -- Added `ReadReady`, `WriteReady` traits. They allow peeking whether the i/o handle is ready to read/write, so they allow using the traits in a non-blocking way. +- Added `ReadReady`, `WriteReady` traits. They allow peeking whether the I/O handle is ready to read/write, so they allow using the traits in a non-blocking way. - Moved `embedded_io::blocking` to the crate root. - Split async traits to the `embedded-io-async` crate. - Split async trait adapters to separate crates. diff --git a/embedded-io/README.md b/embedded-io/README.md index 33461b3cf..2563bdbf7 100644 --- a/embedded-io/README.md +++ b/embedded-io/README.md @@ -6,7 +6,7 @@ This project is developed and maintained by the [HAL team](https://github.com/rust-embedded/wg#the-hal-team). -IO traits for embedded systems. +Input/Output traits for embedded systems. Rust's `std::io` traits are not available in `no_std` targets, mainly because `std::io::Error` requires allocation. This crate contains replacement equivalent traits, usable in `no_std` diff --git a/embedded-io/src/adapters.rs b/embedded-io/src/adapters.rs index 942d52455..741e243d0 100644 --- a/embedded-io/src/adapters.rs +++ b/embedded-io/src/adapters.rs @@ -3,9 +3,9 @@ //! To interoperate with `std::io`, wrap a type in one of these //! adapters. //! -//! There's no separate adapters for Read/ReadBuf/Write traits. Instead, a single +//! There are no separate adapters for `Read`/`ReadBuf`/`Write` traits. Instead, a single //! adapter implements the right traits based on what the inner type implements. -//! This allows adapting a `Read+Write`, for example. +//! This allows using these adapters when using `Read+Write`, for example. use crate::SeekFrom; @@ -107,26 +107,26 @@ impl ToStd { impl std::io::Read for ToStd { fn read(&mut self, buf: &mut [u8]) -> Result { - self.inner.read(buf).map_err(to_io_error) + self.inner.read(buf).map_err(to_std_error) } } impl std::io::Write for ToStd { fn write(&mut self, buf: &[u8]) -> Result { - self.inner.write(buf).map_err(to_io_error) + self.inner.write(buf).map_err(to_std_error) } fn flush(&mut self) -> Result<(), std::io::Error> { - self.inner.flush().map_err(to_io_error) + self.inner.flush().map_err(to_std_error) } } impl std::io::Seek for ToStd { fn seek(&mut self, pos: std::io::SeekFrom) -> Result { - self.inner.seek(pos.into()).map_err(to_io_error) + self.inner.seek(pos.into()).map_err(to_std_error) } } -fn to_io_error(err: T) -> std::io::Error { +fn to_std_error(err: T) -> std::io::Error { let kind = std::io::ErrorKind::Other; std::io::Error::new(kind, format!("{:?}", err)) } diff --git a/embedded-io/src/lib.rs b/embedded-io/src/lib.rs index 7f7aa2eb9..40c98d0b2 100644 --- a/embedded-io/src/lib.rs +++ b/embedded-io/src/lib.rs @@ -281,7 +281,7 @@ pub trait ReadReady: crate::Io { /// This allows using a [`Write`] in a nonblocking fashion, i.e. trying to write /// only when it is ready. pub trait WriteReady: crate::Io { - /// Get whether the writer is ready for immediately writeing. + /// Get whether the writer is ready for immediately writing. /// /// This usually means that there is free space in the internal transmit buffer. /// From 7aaa585c634fb04ef5c12a8e190c1b728ef34db8 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 12 Jul 2023 13:26:23 +0200 Subject: [PATCH 051/199] Rename trait `Io` to `ErrorType`. --- embedded-io-async/src/lib.rs | 10 +++++----- embedded-io/CHANGELOG.md | 1 + embedded-io/src/adapters.rs | 2 +- embedded-io/src/impls/boxx.rs | 4 ++-- embedded-io/src/impls/slice_mut.rs | 4 ++-- embedded-io/src/impls/slice_ref.rs | 4 ++-- embedded-io/src/impls/vec.rs | 4 ++-- embedded-io/src/lib.rs | 18 +++++++++--------- 8 files changed, 24 insertions(+), 23 deletions(-) diff --git a/embedded-io-async/src/lib.rs b/embedded-io-async/src/lib.rs index f204eada5..d5a934ae2 100644 --- a/embedded-io-async/src/lib.rs +++ b/embedded-io-async/src/lib.rs @@ -10,13 +10,13 @@ extern crate alloc; mod impls; pub use embedded_io::{ - Error, ErrorKind, Io, ReadExactError, ReadReady, SeekFrom, WriteAllError, WriteReady, + Error, ErrorKind, ErrorType, ReadExactError, ReadReady, SeekFrom, WriteAllError, WriteReady, }; /// Async reader. /// /// Semantics are the same as [`std::io::Read`], check its documentation for details. -pub trait Read: Io { +pub trait Read: ErrorType { /// Pull some bytes from this source into the specified buffer, returning how many bytes were read. async fn read(&mut self, buf: &mut [u8]) -> Result; @@ -40,7 +40,7 @@ pub trait Read: Io { /// Async buffered reader. /// /// Semantics are the same as [`std::io::BufRead`], check its documentation for details. -pub trait BufRead: Io { +pub trait BufRead: ErrorType { /// Return the contents of the internal buffer, filling it with more data from the inner reader if it is empty. async fn fill_buf(&mut self) -> Result<&[u8], Self::Error>; @@ -51,7 +51,7 @@ pub trait BufRead: Io { /// Async writer. /// /// Semantics are the same as [`std::io::Write`], check its documentation for details. -pub trait Write: Io { +pub trait Write: ErrorType { /// Write a buffer into this writer, returning how many bytes were written. async fn write(&mut self, buf: &[u8]) -> Result; @@ -77,7 +77,7 @@ pub trait Write: Io { /// Async seek within streams. /// /// Semantics are the same as [`std::io::Seek`], check its documentation for details. -pub trait Seek: Io { +pub trait Seek: ErrorType { /// Seek to an offset, in bytes, in a stream. async fn seek(&mut self, pos: SeekFrom) -> Result; diff --git a/embedded-io/CHANGELOG.md b/embedded-io/CHANGELOG.md index f7043a7de..a91aabe37 100644 --- a/embedded-io/CHANGELOG.md +++ b/embedded-io/CHANGELOG.md @@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Moved `embedded_io::blocking` to the crate root. - Split async traits to the `embedded-io-async` crate. - Split async trait adapters to separate crates. +- Rename trait `Io` to `ErrorKind`, for consistency with `embedded-hal`. ## 0.4.0 - 2022-11-25 diff --git a/embedded-io/src/adapters.rs b/embedded-io/src/adapters.rs index 741e243d0..729f8dc0d 100644 --- a/embedded-io/src/adapters.rs +++ b/embedded-io/src/adapters.rs @@ -40,7 +40,7 @@ impl FromStd { } } -impl crate::Io for FromStd { +impl crate::ErrorType for FromStd { type Error = std::io::Error; } diff --git a/embedded-io/src/impls/boxx.rs b/embedded-io/src/impls/boxx.rs index 727d1f21c..6e61ebf8d 100644 --- a/embedded-io/src/impls/boxx.rs +++ b/embedded-io/src/impls/boxx.rs @@ -1,8 +1,8 @@ -use crate::{BufRead, Io, Read, ReadReady, Seek, Write, WriteReady}; +use crate::{BufRead, ErrorType, Read, ReadReady, Seek, Write, WriteReady}; use alloc::boxed::Box; #[cfg_attr(docsrs, doc(cfg(any(feature = "std", feature = "alloc"))))] -impl Io for Box { +impl ErrorType for Box { type Error = T::Error; } diff --git a/embedded-io/src/impls/slice_mut.rs b/embedded-io/src/impls/slice_mut.rs index cefaa296c..b89b72182 100644 --- a/embedded-io/src/impls/slice_mut.rs +++ b/embedded-io/src/impls/slice_mut.rs @@ -1,7 +1,7 @@ -use crate::{Io, Write}; +use crate::{ErrorType, Write}; use core::mem; -impl Io for &mut [u8] { +impl ErrorType for &mut [u8] { type Error = core::convert::Infallible; } diff --git a/embedded-io/src/impls/slice_ref.rs b/embedded-io/src/impls/slice_ref.rs index 73c98ab55..6332d70dd 100644 --- a/embedded-io/src/impls/slice_ref.rs +++ b/embedded-io/src/impls/slice_ref.rs @@ -1,6 +1,6 @@ -use crate::{BufRead, Io, Read}; +use crate::{BufRead, ErrorType, Read}; -impl Io for &[u8] { +impl ErrorType for &[u8] { type Error = core::convert::Infallible; } diff --git a/embedded-io/src/impls/vec.rs b/embedded-io/src/impls/vec.rs index 8aa1b62d6..3b279c564 100644 --- a/embedded-io/src/impls/vec.rs +++ b/embedded-io/src/impls/vec.rs @@ -1,8 +1,8 @@ -use crate::{Io, Write}; +use crate::{ErrorType, Write}; use alloc::vec::Vec; #[cfg_attr(docsrs, doc(cfg(any(feature = "std", feature = "alloc"))))] -impl Io for Vec { +impl ErrorType for Vec { type Error = core::convert::Infallible; } diff --git a/embedded-io/src/lib.rs b/embedded-io/src/lib.rs index 40c98d0b2..b71c43f5a 100644 --- a/embedded-io/src/lib.rs +++ b/embedded-io/src/lib.rs @@ -55,7 +55,7 @@ impl Error for ErrorKind { } } -/// Base trait for all IO traits. +/// Base trait for all IO traits, defining the error type. /// /// All IO operations of all traits return the error defined in this trait. /// @@ -64,12 +64,12 @@ impl Error for ErrorKind { /// This is very convenient when writing generic code, it means you have to /// handle a single error type `T::Error`, instead of `::Error` and `::Error` /// which might be different types. -pub trait Io { +pub trait ErrorType { /// Error type of all the IO operations on this type. type Error: Error; } -impl crate::Io for &mut T { +impl crate::ErrorType for &mut T { type Error = T::Error; } @@ -150,7 +150,7 @@ impl std::error::Error for WriteAllError {} /// Blocking reader. /// /// Semantics are the same as [`std::io::Read`], check its documentation for details. -pub trait Read: crate::Io { +pub trait Read: crate::ErrorType { /// Pull some bytes from this source into the specified buffer, returning how many bytes were read. fn read(&mut self, buf: &mut [u8]) -> Result; @@ -174,7 +174,7 @@ pub trait Read: crate::Io { /// Blocking buffered reader. /// /// Semantics are the same as [`std::io::BufRead`], check its documentation for details. -pub trait BufRead: crate::Io { +pub trait BufRead: crate::ErrorType { /// Return the contents of the internal buffer, filling it with more data from the inner reader if it is empty. fn fill_buf(&mut self) -> Result<&[u8], Self::Error>; @@ -185,7 +185,7 @@ pub trait BufRead: crate::Io { /// Blocking writer. /// /// Semantics are the same as [`std::io::Write`], check its documentation for details. -pub trait Write: crate::Io { +pub trait Write: crate::ErrorType { /// Write a buffer into this writer, returning how many bytes were written. fn write(&mut self, buf: &[u8]) -> Result; @@ -246,7 +246,7 @@ pub trait Write: crate::Io { /// Blocking seek within streams. /// /// Semantics are the same as [`std::io::Seek`], check its documentation for details. -pub trait Seek: crate::Io { +pub trait Seek: crate::ErrorType { /// Seek to an offset, in bytes, in a stream. fn seek(&mut self, pos: crate::SeekFrom) -> Result; @@ -266,7 +266,7 @@ pub trait Seek: crate::Io { /// /// This allows using a [`Read`] or [`BufRead`] in a nonblocking fashion, i.e. trying to read /// only when it is ready. -pub trait ReadReady: crate::Io { +pub trait ReadReady: crate::ErrorType { /// Get whether the reader is ready for immediately reading. /// /// This usually means that there is either some bytes have been received and are buffered and ready to be read, @@ -280,7 +280,7 @@ pub trait ReadReady: crate::Io { /// /// This allows using a [`Write`] in a nonblocking fashion, i.e. trying to write /// only when it is ready. -pub trait WriteReady: crate::Io { +pub trait WriteReady: crate::ErrorType { /// Get whether the writer is ready for immediately writing. /// /// This usually means that there is free space in the internal transmit buffer. From e37cb225b59f2348ae3941ff4fd87616d4ab90f9 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 12 Jul 2023 15:53:44 +0200 Subject: [PATCH 052/199] io: add error kinds from std. --- embedded-io/src/adapters.rs | 42 +++++++++++++++++++++++++-- embedded-io/src/lib.rs | 58 +++++++++++++++++++++++++++++++++++++ 2 files changed, 97 insertions(+), 3 deletions(-) diff --git a/embedded-io/src/adapters.rs b/embedded-io/src/adapters.rs index 729f8dc0d..d64c92e4b 100644 --- a/embedded-io/src/adapters.rs +++ b/embedded-io/src/adapters.rs @@ -126,15 +126,51 @@ impl std::io::Seek for ToStd { } } -fn to_std_error(err: T) -> std::io::Error { - let kind = std::io::ErrorKind::Other; +fn to_std_error(err: T) -> std::io::Error { + let kind = match err.kind() { + crate::ErrorKind::NotFound => std::io::ErrorKind::NotFound, + crate::ErrorKind::PermissionDenied => std::io::ErrorKind::PermissionDenied, + crate::ErrorKind::ConnectionRefused => std::io::ErrorKind::ConnectionRefused, + crate::ErrorKind::ConnectionReset => std::io::ErrorKind::ConnectionReset, + crate::ErrorKind::ConnectionAborted => std::io::ErrorKind::ConnectionAborted, + crate::ErrorKind::NotConnected => std::io::ErrorKind::NotConnected, + crate::ErrorKind::AddrInUse => std::io::ErrorKind::AddrInUse, + crate::ErrorKind::AddrNotAvailable => std::io::ErrorKind::AddrNotAvailable, + crate::ErrorKind::BrokenPipe => std::io::ErrorKind::BrokenPipe, + crate::ErrorKind::AlreadyExists => std::io::ErrorKind::AlreadyExists, + crate::ErrorKind::InvalidInput => std::io::ErrorKind::InvalidInput, + crate::ErrorKind::InvalidData => std::io::ErrorKind::InvalidData, + crate::ErrorKind::TimedOut => std::io::ErrorKind::TimedOut, + crate::ErrorKind::Interrupted => std::io::ErrorKind::Interrupted, + crate::ErrorKind::Unsupported => std::io::ErrorKind::Unsupported, + crate::ErrorKind::OutOfMemory => std::io::ErrorKind::OutOfMemory, + _ => std::io::ErrorKind::Other, + }; std::io::Error::new(kind, format!("{:?}", err)) } #[cfg_attr(docsrs, doc(cfg(feature = "std")))] impl crate::Error for std::io::Error { fn kind(&self) -> crate::ErrorKind { - crate::ErrorKind::Other + match self.kind() { + std::io::ErrorKind::NotFound => crate::ErrorKind::NotFound, + std::io::ErrorKind::PermissionDenied => crate::ErrorKind::PermissionDenied, + std::io::ErrorKind::ConnectionRefused => crate::ErrorKind::ConnectionRefused, + std::io::ErrorKind::ConnectionReset => crate::ErrorKind::ConnectionReset, + std::io::ErrorKind::ConnectionAborted => crate::ErrorKind::ConnectionAborted, + std::io::ErrorKind::NotConnected => crate::ErrorKind::NotConnected, + std::io::ErrorKind::AddrInUse => crate::ErrorKind::AddrInUse, + std::io::ErrorKind::AddrNotAvailable => crate::ErrorKind::AddrNotAvailable, + std::io::ErrorKind::BrokenPipe => crate::ErrorKind::BrokenPipe, + std::io::ErrorKind::AlreadyExists => crate::ErrorKind::AlreadyExists, + std::io::ErrorKind::InvalidInput => crate::ErrorKind::InvalidInput, + std::io::ErrorKind::InvalidData => crate::ErrorKind::InvalidData, + std::io::ErrorKind::TimedOut => crate::ErrorKind::TimedOut, + std::io::ErrorKind::Interrupted => crate::ErrorKind::Interrupted, + std::io::ErrorKind::Unsupported => crate::ErrorKind::Unsupported, + std::io::ErrorKind::OutOfMemory => crate::ErrorKind::OutOfMemory, + _ => crate::ErrorKind::Other, + } } } diff --git a/embedded-io/src/lib.rs b/embedded-io/src/lib.rs index b71c43f5a..1a8487889 100644 --- a/embedded-io/src/lib.rs +++ b/embedded-io/src/lib.rs @@ -29,9 +29,67 @@ pub enum SeekFrom { #[derive(Debug, Copy, Clone, Eq, PartialEq)] #[non_exhaustive] /// Possible kinds of errors. +/// +/// This list is intended to grow over time and it is not recommended to +/// exhaustively match against it. In application code, use `match` for the `ErrorKind` +/// values you are expecting; use `_` to match "all other errors". +/// +/// This is the `embedded-io` equivalent of [`std::io::ErrorKind`], except with the following changes: +/// +/// - `WouldBlock` is removed, since `embedded-io` traits are always blocking. See the [crate-level documentation](crate) for details. +/// - `WriteZero` is removed, since it is a separate variant in [`WriteAllError`] and [`WriteFmtError`]. pub enum ErrorKind { /// Unspecified error kind. Other, + + /// An entity was not found, often a file. + NotFound, + /// The operation lacked the necessary privileges to complete. + PermissionDenied, + /// The connection was refused by the remote server. + ConnectionRefused, + /// The connection was reset by the remote server. + ConnectionReset, + /// The connection was aborted (terminated) by the remote server. + ConnectionAborted, + /// The network operation failed because it was not connected yet. + NotConnected, + /// A socket address could not be bound because the address is already in + /// use elsewhere. + AddrInUse, + /// A nonexistent interface was requested or the requested address was not + /// local. + AddrNotAvailable, + /// The operation failed because a pipe was closed. + BrokenPipe, + /// An entity already exists, often a file. + AlreadyExists, + /// A parameter was incorrect. + InvalidInput, + /// Data not valid for the operation were encountered. + /// + /// Unlike [`InvalidInput`], this typically means that the operation + /// parameters were valid, however the error was caused by malformed + /// input data. + /// + /// For example, a function that reads a file into a string will error with + /// `InvalidData` if the file's contents are not valid UTF-8. + /// + /// [`InvalidInput`]: ErrorKind::InvalidInput + InvalidData, + /// The I/O operation's timeout expired, causing it to be canceled. + TimedOut, + /// This operation was interrupted. + /// + /// Interrupted operations can typically be retried. + Interrupted, + /// This operation is unsupported on this platform. + /// + /// This means that the operation can never succeed. + Unsupported, + /// An operation could not be completed, because it failed + /// to allocate enough memory. + OutOfMemory, } /// Error trait. From b55faf99066a9486b7fc525e3944faee988be9c8 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 12 Jul 2023 15:00:53 +0200 Subject: [PATCH 053/199] io: expand docs. --- embedded-io-async/src/lib.rs | 74 +++++++++++++++++++++++++++++++++--- embedded-io/README.md | 8 +++- embedded-io/src/lib.rs | 69 +++++++++++++++++++++++++++++---- 3 files changed, 137 insertions(+), 14 deletions(-) diff --git a/embedded-io-async/src/lib.rs b/embedded-io-async/src/lib.rs index d5a934ae2..c94e349a4 100644 --- a/embedded-io-async/src/lib.rs +++ b/embedded-io-async/src/lib.rs @@ -15,12 +15,41 @@ pub use embedded_io::{ /// Async reader. /// -/// Semantics are the same as [`std::io::Read`], check its documentation for details. +/// This trait is the `embedded-io-async` equivalent of [`std::io::Read`]. pub trait Read: ErrorType { - /// Pull some bytes from this source into the specified buffer, returning how many bytes were read. + /// Read some bytes from this source into the specified buffer, returning how many bytes were read. + /// + /// If no bytes are currently available to read, this function waits until at least one byte is available. + /// + /// If bytes are available, a non-zero amount of bytes is read to the beginning of `buf`, and the amount + /// is returned. It is not guaranteed that *all* available bytes are returned, it is possible for the + /// implementation to read an amount of bytes less than `buf.len()` while there are more bytes immediately + /// available. + /// + /// If the reader is at end-of-file (EOF), `Ok(0)` is returned. There is no guarantee that a reader at EOF + /// will always be so in the future, for example a reader can stop being at EOF if another process appends + /// more bytes to the underlying file. + /// + /// If `buf.len() == 0`, `read` returns without waiting, with either `Ok(0)` or an error. + /// The `Ok(0)` doesn't indicate EOF, unlike when called with a non-empty buffer. + /// + /// Implementations are encouraged to make this function side-effect-free on cancel (AKA "cancel-safe"), i.e. + /// guarantee that if you cancel (drop) a `read()` future that hasn't completed yet, the stream's + /// state hasn't changed (no bytes have been read). + /// + /// This is not a requirement to allow implementations that read into the user's buffer straight from + /// the hardware with e.g. DMA. + /// + /// Implementations should document whether they're actually side-effect-free on cancel or not. async fn read(&mut self, buf: &mut [u8]) -> Result; /// Read the exact number of bytes required to fill `buf`. + /// + /// This function calls `read()` in a loop until exactly `buf.len()` bytes have + /// been read, waiting if needed. + /// + /// This function is not side-effect-free on cancel (AKA "cancel-safe"), i.e. if you cancel (drop) a returned + /// future that hasn't completed yet, some bytes might have already been read, which will get lost. async fn read_exact(&mut self, mut buf: &mut [u8]) -> Result<(), ReadExactError> { while !buf.is_empty() { match self.read(buf).await { @@ -39,9 +68,15 @@ pub trait Read: ErrorType { /// Async buffered reader. /// -/// Semantics are the same as [`std::io::BufRead`], check its documentation for details. +/// This trait is the `embedded-io-async` equivalent of [`std::io::BufRead`]. pub trait BufRead: ErrorType { /// Return the contents of the internal buffer, filling it with more data from the inner reader if it is empty. + /// + /// If no bytes are currently available to read, this function waits until at least one byte is available. + /// + /// If the reader is at end-of-file (EOF), an empty slice is returned. There is no guarantee that a reader at EOF + /// will always be so in the future, for example a reader can stop being at EOF if another process appends + /// more bytes to the underlying file. async fn fill_buf(&mut self) -> Result<&[u8], Self::Error>; /// Tell this buffer that `amt` bytes have been consumed from the buffer, so they should no longer be returned in calls to `fill_buf`. @@ -50,9 +85,32 @@ pub trait BufRead: ErrorType { /// Async writer. /// -/// Semantics are the same as [`std::io::Write`], check its documentation for details. +/// This trait is the `embedded-io-async` equivalent of [`std::io::Write`]. pub trait Write: ErrorType { /// Write a buffer into this writer, returning how many bytes were written. + /// + /// If the writer is not currently ready to accept more bytes (for example, its buffer is full), + /// this function waits until it is ready to accept least one byte. + /// + /// If it's ready to accept bytes, a non-zero amount of bytes is written from the beginning of `buf`, and the amount + /// is returned. It is not guaranteed that *all* available buffer space is filled, i.e. it is possible for the + /// implementation to write an amount of bytes less than `buf.len()` while the writer continues to be + /// ready to accept more bytes immediately. + /// + /// Implementations should never return `Ok(0)` when `buf.len() != 0`. Situations where the writer is not + /// able to accept more bytes and likely never will are better indicated with errors. + /// + /// If `buf.len() == 0`, `write` returns without waiting, with either `Ok(0)` or an error. + /// The `Ok(0)` doesn't indicate an error. + /// + /// Implementations are encouraged to make this function side-effect-free on cancel (AKA "cancel-safe"), i.e. + /// guarantee that if you cancel (drop) a `write()` future that hasn't completed yet, the stream's + /// state hasn't changed (no bytes have been written). + /// + /// This is not a requirement to allow implementations that write from the user's buffer straight to + /// the hardware with e.g. DMA. + /// + /// Implementations should document whether they're actually side-effect-free on cancel or not. async fn write(&mut self, buf: &[u8]) -> Result; /// Flush this output stream, ensuring that all intermediately buffered contents reach their destination. @@ -61,6 +119,12 @@ pub trait Write: ErrorType { } /// Write an entire buffer into this writer. + /// + /// This function calls `write()` in a loop until exactly `buf.len()` bytes have + /// been written, waiting if needed. + /// + /// This function is not side-effect-free on cancel (AKA "cancel-safe"), i.e. if you cancel (drop) a returned + /// future that hasn't completed yet, some bytes might have already been written. async fn write_all(&mut self, buf: &[u8]) -> Result<(), WriteAllError> { let mut buf = buf; while !buf.is_empty() { @@ -76,7 +140,7 @@ pub trait Write: ErrorType { /// Async seek within streams. /// -/// Semantics are the same as [`std::io::Seek`], check its documentation for details. +/// This trait is the `embedded-io-async` equivalent of [`std::io::Seek`]. pub trait Seek: ErrorType { /// Seek to an offset, in bytes, in a stream. async fn seek(&mut self, pos: SeekFrom) -> Result; diff --git a/embedded-io/README.md b/embedded-io/README.md index 2563bdbf7..d579b09f6 100644 --- a/embedded-io/README.md +++ b/embedded-io/README.md @@ -12,8 +12,12 @@ Rust's `std::io` traits are not available in `no_std` targets, mainly because `s requires allocation. This crate contains replacement equivalent traits, usable in `no_std` targets. -The only difference with `std::io` is `Error` is an associated type. This allows each implementor -to return its own error type, while avoiding `dyn` or `Box`. This is how errors are handled in [`embedded-hal`](https://github.com/rust-embedded/embedded-hal/). +## Differences with `std::io` + +- `Error` is an associated type. This allows each implementor to return its own error type, +while avoiding `dyn` or `Box`. This is consistent with how errors are handled in [`embedded-hal`](https://github.com/rust-embedded/embedded-hal/). +- In `std::io`, the `Read`/`Write` traits might be blocking or non-blocking (i.e. returning `WouldBlock` errors) depending on the file descriptor's mode, which is only known at run-time. This allows passing a non-blocking stream to code that expects a blocking +stream, causing unexpected errors. To solve this, `embedded-io` specifies `Read`/`Write` are always blocking, and adds new `ReadReady`/`WriteReady` traits to allow using streams in a non-blocking way. ## Minimum Supported Rust Version (MSRV) diff --git a/embedded-io/src/lib.rs b/embedded-io/src/lib.rs index b71c43f5a..c514e055e 100644 --- a/embedded-io/src/lib.rs +++ b/embedded-io/src/lib.rs @@ -15,7 +15,7 @@ mod impls; /// Enumeration of possible methods to seek within an I/O object. /// -/// Semantics are the same as [`std::io::SeekFrom`], check its documentation for details. +/// This is the `embedded-io` equivalent of [`std::io::SeekFrom`]. #[derive(Debug, Copy, Clone, Eq, PartialEq)] pub enum SeekFrom { /// Sets the offset to the provided number of bytes. @@ -149,12 +149,33 @@ impl std::error::Error for WriteAllError {} /// Blocking reader. /// -/// Semantics are the same as [`std::io::Read`], check its documentation for details. +/// This trait is the `embedded-io` equivalent of [`std::io::Read`]. pub trait Read: crate::ErrorType { - /// Pull some bytes from this source into the specified buffer, returning how many bytes were read. + /// Read some bytes from this source into the specified buffer, returning how many bytes were read. + /// + /// If no bytes are currently available to read, this function blocks until at least one byte is available. + /// + /// If bytes are available, a non-zero amount of bytes is read to the beginning of `buf`, and the amount + /// is returned. It is not guaranteed that *all* available bytes are returned, it is possible for the + /// implementation to read an amount of bytes less than `buf.len()` while there are more bytes immediately + /// available. + /// + /// If the reader is at end-of-file (EOF), `Ok(0)` is returned. There is no guarantee that a reader at EOF + /// will always be so in the future, for example a reader can stop being at EOF if another process appends + /// more bytes to the underlying file. + /// + /// If `buf.len() == 0`, `read` returns without blocking, with either `Ok(0)` or an error. + /// The `Ok(0)` doesn't indicate EOF, unlike when called with a non-empty buffer. fn read(&mut self, buf: &mut [u8]) -> Result; /// Read the exact number of bytes required to fill `buf`. + /// + /// This function calls `read()` in a loop until exactly `buf.len()` bytes have + /// been read, blocking if needed. + /// + /// If you are using [`ReadReady`] to avoid blocking, you should not use this function. + /// `ReadReady::read_ready()` returning true only guarantees the first call to `read()` will + /// not block, so this function may still block in subsequent calls. fn read_exact(&mut self, mut buf: &mut [u8]) -> Result<(), ReadExactError> { while !buf.is_empty() { match self.read(buf) { @@ -173,9 +194,15 @@ pub trait Read: crate::ErrorType { /// Blocking buffered reader. /// -/// Semantics are the same as [`std::io::BufRead`], check its documentation for details. +/// This trait is the `embedded-io` equivalent of [`std::io::BufRead`]. pub trait BufRead: crate::ErrorType { /// Return the contents of the internal buffer, filling it with more data from the inner reader if it is empty. + /// + /// If no bytes are currently available to read, this function blocks until at least one byte is available. + /// + /// If the reader is at end-of-file (EOF), an empty slice is returned. There is no guarantee that a reader at EOF + /// will always be so in the future, for example a reader can stop being at EOF if another process appends + /// more bytes to the underlying file. fn fill_buf(&mut self) -> Result<&[u8], Self::Error>; /// Tell this buffer that `amt` bytes have been consumed from the buffer, so they should no longer be returned in calls to `fill_buf`. @@ -184,15 +211,36 @@ pub trait BufRead: crate::ErrorType { /// Blocking writer. /// -/// Semantics are the same as [`std::io::Write`], check its documentation for details. +/// This trait is the `embedded-io` equivalent of [`std::io::Write`]. pub trait Write: crate::ErrorType { /// Write a buffer into this writer, returning how many bytes were written. + /// + /// If the writer is not currently ready to accept more bytes (for example, its buffer is full), + /// this function blocks until it is ready to accept least one byte. + /// + /// If it's ready to accept bytes, a non-zero amount of bytes is written from the beginning of `buf`, and the amount + /// is returned. It is not guaranteed that *all* available buffer space is filled, i.e. it is possible for the + /// implementation to write an amount of bytes less than `buf.len()` while the writer continues to be + /// ready to accept more bytes immediately. + /// + /// Implementations should never return `Ok(0)` when `buf.len() != 0`. Situations where the writer is not + /// able to accept more bytes and likely never will are better indicated with errors. + /// + /// If `buf.len() == 0`, `write` returns without blocking, with either `Ok(0)` or an error. + /// The `Ok(0)` doesn't indicate an error. fn write(&mut self, buf: &[u8]) -> Result; - /// Flush this output stream, ensuring that all intermediately buffered contents reach their destination. + /// Flush this output stream, blocking until all intermediately buffered contents reach their destination. fn flush(&mut self) -> Result<(), Self::Error>; /// Write an entire buffer into this writer. + /// + /// This function calls `write()` in a loop until exactly `buf.len()` bytes have + /// been written, blocking if needed. + /// + /// If you are using [`WriteReady`] to avoid blocking, you should not use this function. + /// `WriteReady::write_ready()` returning true only guarantees the first call to `write()` will + /// not block, so this function may still block in subsequent calls. fn write_all(&mut self, mut buf: &[u8]) -> Result<(), WriteAllError> { while !buf.is_empty() { match self.write(buf) { @@ -205,6 +253,13 @@ pub trait Write: crate::ErrorType { } /// Write a formatted string into this writer, returning any error encountered. + /// + /// This function calls `write()` in a loop until the entire formatted string has + /// been written, blocking if needed. + /// + /// If you are using [`WriteReady`] to avoid blocking, you should not use this function. + /// `WriteReady::write_ready()` returning true only guarantees the first call to `write()` will + /// not block, so this function may still block in subsequent calls. fn write_fmt(&mut self, fmt: fmt::Arguments<'_>) -> Result<(), WriteFmtError> { // Create a shim which translates a Write to a fmt::Write and saves // off I/O errors. instead of discarding them @@ -245,7 +300,7 @@ pub trait Write: crate::ErrorType { /// Blocking seek within streams. /// -/// Semantics are the same as [`std::io::Seek`], check its documentation for details. +/// This trait is the `embedded-io` equivalent of [`std::io::Seek`]. pub trait Seek: crate::ErrorType { /// Seek to an offset, in bytes, in a stream. fn seek(&mut self, pos: crate::SeekFrom) -> Result; From 9ac61a73d78f071ff50198cdb8d22a992030f908 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 12 Jul 2023 15:17:34 +0200 Subject: [PATCH 054/199] Add `+ ?Sized` to all blanket impls. --- Cargo.toml | 1 + embedded-hal-async/src/delay.rs | 2 +- embedded-hal-async/src/digital.rs | 2 +- embedded-hal-async/src/i2c.rs | 2 +- embedded-hal-async/src/spi.rs | 4 ++-- embedded-hal-nb/src/serial.rs | 6 +++--- embedded-hal-nb/src/spi.rs | 2 +- embedded-hal/src/delay.rs | 2 +- embedded-hal/src/digital.rs | 12 ++++++------ embedded-hal/src/i2c.rs | 4 ++-- embedded-hal/src/pwm.rs | 4 ++-- embedded-hal/src/spi.rs | 6 +++--- 12 files changed, 24 insertions(+), 23 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index ab7723059..ca4e0c584 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,4 +1,5 @@ [workspace] +resolver = "2" # CI removes lines containing 'nightly-only' when not building with nightly. members = [ diff --git a/embedded-hal-async/src/delay.rs b/embedded-hal-async/src/delay.rs index 4b0223930..acf18f2af 100644 --- a/embedded-hal-async/src/delay.rs +++ b/embedded-hal-async/src/delay.rs @@ -13,7 +13,7 @@ pub trait DelayUs { impl DelayUs for &mut T where - T: DelayUs, + T: DelayUs + ?Sized, { async fn delay_us(&mut self, us: u32) { T::delay_us(self, us).await diff --git a/embedded-hal-async/src/digital.rs b/embedded-hal-async/src/digital.rs index 600876975..8f09eb78d 100644 --- a/embedded-hal-async/src/digital.rs +++ b/embedded-hal-async/src/digital.rs @@ -48,7 +48,7 @@ pub trait Wait: embedded_hal::digital::ErrorType { async fn wait_for_any_edge(&mut self) -> Result<(), Self::Error>; } -impl Wait for &mut T { +impl Wait for &mut T { async fn wait_for_high(&mut self) -> Result<(), Self::Error> { T::wait_for_high(self).await } diff --git a/embedded-hal-async/src/i2c.rs b/embedded-hal-async/src/i2c.rs index 67712dd46..0837da8dd 100644 --- a/embedded-hal-async/src/i2c.rs +++ b/embedded-hal-async/src/i2c.rs @@ -122,7 +122,7 @@ pub trait I2c: ErrorType { ) -> Result<(), Self::Error>; } -impl> I2c
for &mut T { +impl + ?Sized> I2c for &mut T { async fn read(&mut self, address: A, read: &mut [u8]) -> Result<(), Self::Error> { T::read(self, address, read).await } diff --git a/embedded-hal-async/src/spi.rs b/embedded-hal-async/src/spi.rs index 2c05f56e4..834a37fd2 100644 --- a/embedded-hal-async/src/spi.rs +++ b/embedded-hal-async/src/spi.rs @@ -76,7 +76,7 @@ pub trait SpiDevice: ErrorType { } } -impl> SpiDevice for &mut T { +impl + ?Sized> SpiDevice for &mut T { async fn transaction( &mut self, operations: &mut [Operation<'_, Word>], @@ -149,7 +149,7 @@ pub trait SpiBus: ErrorType { async fn flush(&mut self) -> Result<(), Self::Error>; } -impl, Word: 'static + Copy> SpiBus for &mut T { +impl + ?Sized, Word: 'static + Copy> SpiBus for &mut T { async fn read(&mut self, words: &mut [Word]) -> Result<(), Self::Error> { T::read(self, words).await } diff --git a/embedded-hal-nb/src/serial.rs b/embedded-hal-nb/src/serial.rs index c069c1441..624aadee3 100644 --- a/embedded-hal-nb/src/serial.rs +++ b/embedded-hal-nb/src/serial.rs @@ -69,7 +69,7 @@ pub trait ErrorType { type Error: Error; } -impl ErrorType for &mut T { +impl ErrorType for &mut T { type Error = T::Error; } @@ -82,7 +82,7 @@ pub trait Read: ErrorType { fn read(&mut self) -> nb::Result; } -impl, Word: Copy> Read for &mut T { +impl + ?Sized, Word: Copy> Read for &mut T { fn read(&mut self) -> nb::Result { T::read(self) } @@ -97,7 +97,7 @@ pub trait Write: ErrorType { fn flush(&mut self) -> nb::Result<(), Self::Error>; } -impl, Word: Copy> Write for &mut T { +impl + ?Sized, Word: Copy> Write for &mut T { fn write(&mut self, word: Word) -> nb::Result<(), Self::Error> { T::write(self, word) } diff --git a/embedded-hal-nb/src/spi.rs b/embedded-hal-nb/src/spi.rs index a9e7c94f0..ecdb88ffb 100644 --- a/embedded-hal-nb/src/spi.rs +++ b/embedded-hal-nb/src/spi.rs @@ -31,7 +31,7 @@ pub trait FullDuplex: ErrorType { fn write(&mut self, word: Word) -> nb::Result<(), Self::Error>; } -impl, Word: Copy> FullDuplex for &mut T { +impl + ?Sized, Word: Copy> FullDuplex for &mut T { fn read(&mut self) -> nb::Result { T::read(self) } diff --git a/embedded-hal/src/delay.rs b/embedded-hal/src/delay.rs index 7486f412f..da4cd7e2d 100644 --- a/embedded-hal/src/delay.rs +++ b/embedded-hal/src/delay.rs @@ -18,7 +18,7 @@ pub trait DelayUs { impl DelayUs for &mut T where - T: DelayUs, + T: DelayUs + ?Sized, { fn delay_us(&mut self, us: u32) { T::delay_us(self, us) diff --git a/embedded-hal/src/digital.rs b/embedded-hal/src/digital.rs index c476cb769..677bb64d1 100644 --- a/embedded-hal/src/digital.rs +++ b/embedded-hal/src/digital.rs @@ -55,11 +55,11 @@ pub trait ErrorType { type Error: Error; } -impl ErrorType for &T { +impl ErrorType for &T { type Error = T::Error; } -impl ErrorType for &mut T { +impl ErrorType for &mut T { type Error = T::Error; } @@ -139,7 +139,7 @@ pub trait OutputPin: ErrorType { } } -impl OutputPin for &mut T { +impl OutputPin for &mut T { fn set_low(&mut self) -> Result<(), Self::Error> { T::set_low(self) } @@ -166,7 +166,7 @@ pub trait StatefulOutputPin: OutputPin { fn is_set_low(&self) -> Result; } -impl StatefulOutputPin for &mut T { +impl StatefulOutputPin for &mut T { fn is_set_high(&self) -> Result { T::is_set_high(self) } @@ -182,7 +182,7 @@ pub trait ToggleableOutputPin: ErrorType { fn toggle(&mut self) -> Result<(), Self::Error>; } -impl ToggleableOutputPin for &mut T { +impl ToggleableOutputPin for &mut T { fn toggle(&mut self) -> Result<(), Self::Error> { T::toggle(self) } @@ -197,7 +197,7 @@ pub trait InputPin: ErrorType { fn is_low(&self) -> Result; } -impl InputPin for &T { +impl InputPin for &T { fn is_high(&self) -> Result { T::is_high(self) } diff --git a/embedded-hal/src/i2c.rs b/embedded-hal/src/i2c.rs index fdaf9e15c..735408758 100644 --- a/embedded-hal/src/i2c.rs +++ b/embedded-hal/src/i2c.rs @@ -249,7 +249,7 @@ pub trait ErrorType { type Error: Error; } -impl ErrorType for &mut T { +impl ErrorType for &mut T { type Error = T::Error; } @@ -372,7 +372,7 @@ pub trait I2c: ErrorType { ) -> Result<(), Self::Error>; } -impl> I2c for &mut T { +impl + ?Sized> I2c for &mut T { fn read(&mut self, address: A, read: &mut [u8]) -> Result<(), Self::Error> { T::read(self, address, read) } diff --git a/embedded-hal/src/pwm.rs b/embedded-hal/src/pwm.rs index 1ba9c4df0..4c0f6ac60 100644 --- a/embedded-hal/src/pwm.rs +++ b/embedded-hal/src/pwm.rs @@ -53,7 +53,7 @@ pub trait ErrorType { type Error: Error; } -impl ErrorType for &mut T { +impl ErrorType for &mut T { type Error = T::Error; } @@ -101,7 +101,7 @@ pub trait SetDutyCycle: ErrorType { } } -impl SetDutyCycle for &mut T { +impl SetDutyCycle for &mut T { fn get_max_duty_cycle(&self) -> u16 { T::get_max_duty_cycle(self) } diff --git a/embedded-hal/src/spi.rs b/embedded-hal/src/spi.rs index 3bcdda795..54bf8bcea 100644 --- a/embedded-hal/src/spi.rs +++ b/embedded-hal/src/spi.rs @@ -288,7 +288,7 @@ pub trait ErrorType { type Error: Error; } -impl ErrorType for &mut T { +impl ErrorType for &mut T { type Error = T::Error; } @@ -378,7 +378,7 @@ pub trait SpiDevice: ErrorType { } } -impl> SpiDevice for &mut T { +impl + ?Sized> SpiDevice for &mut T { fn transaction(&mut self, operations: &mut [Operation<'_, Word>]) -> Result<(), Self::Error> { T::transaction(self, operations) } @@ -448,7 +448,7 @@ pub trait SpiBus: ErrorType { fn flush(&mut self) -> Result<(), Self::Error>; } -impl, Word: Copy + 'static> SpiBus for &mut T { +impl + ?Sized, Word: Copy + 'static> SpiBus for &mut T { fn read(&mut self, words: &mut [Word]) -> Result<(), Self::Error> { T::read(self, words) } From 93a50114311ad6cd4bade5d1dd41134fbf14f704 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1niel=20Buga?= Date: Wed, 12 Jul 2023 22:00:03 +0200 Subject: [PATCH 055/199] Add bus access methods to ExclusiveDevice --- embedded-hal-async/CHANGELOG.md | 3 +++ embedded-hal-async/src/spi.rs | 10 ++++++++++ 2 files changed, 13 insertions(+) diff --git a/embedded-hal-async/CHANGELOG.md b/embedded-hal-async/CHANGELOG.md index c1b4de3c3..5ea703cdd 100644 --- a/embedded-hal-async/CHANGELOG.md +++ b/embedded-hal-async/CHANGELOG.md @@ -7,6 +7,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] +### Added +- spi: added `ExclusiveDevice::{bus, bus_mut}`. + ## [v0.2.0-alpha.2] - 2023-07-04 ### Added diff --git a/embedded-hal-async/src/spi.rs b/embedded-hal-async/src/spi.rs index 834a37fd2..1e852e34b 100644 --- a/embedded-hal-async/src/spi.rs +++ b/embedded-hal-async/src/spi.rs @@ -208,6 +208,16 @@ impl ExclusiveDevice { pub fn new(bus: BUS, cs: CS, delay: D) -> Self { Self { bus, cs, delay } } + + /// Returns a reference to the underlying bus object. + pub fn bus(&self) -> &BUS { + &self.bus + } + + /// Returns a mutable reference to the underlying bus object. + pub fn bus_mut(&mut self) -> &mut BUS { + &mut self.bus + } } impl ErrorType for ExclusiveDevice From 3f4c8d95758c1eef5733f082aca2d6c2a25e59c7 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 12 Jul 2023 18:41:05 +0200 Subject: [PATCH 056/199] io: remove useless `crate::`. --- embedded-io/src/lib.rs | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/embedded-io/src/lib.rs b/embedded-io/src/lib.rs index 81851ae50..2c50deca9 100644 --- a/embedded-io/src/lib.rs +++ b/embedded-io/src/lib.rs @@ -127,7 +127,7 @@ pub trait ErrorType { type Error: Error; } -impl crate::ErrorType for &mut T { +impl ErrorType for &mut T { type Error = T::Error; } @@ -208,7 +208,7 @@ impl std::error::Error for WriteAllError {} /// Blocking reader. /// /// This trait is the `embedded-io` equivalent of [`std::io::Read`]. -pub trait Read: crate::ErrorType { +pub trait Read: ErrorType { /// Read some bytes from this source into the specified buffer, returning how many bytes were read. /// /// If no bytes are currently available to read, this function blocks until at least one byte is available. @@ -253,7 +253,7 @@ pub trait Read: crate::ErrorType { /// Blocking buffered reader. /// /// This trait is the `embedded-io` equivalent of [`std::io::BufRead`]. -pub trait BufRead: crate::ErrorType { +pub trait BufRead: ErrorType { /// Return the contents of the internal buffer, filling it with more data from the inner reader if it is empty. /// /// If no bytes are currently available to read, this function blocks until at least one byte is available. @@ -270,7 +270,7 @@ pub trait BufRead: crate::ErrorType { /// Blocking writer. /// /// This trait is the `embedded-io` equivalent of [`std::io::Write`]. -pub trait Write: crate::ErrorType { +pub trait Write: ErrorType { /// Write a buffer into this writer, returning how many bytes were written. /// /// If the writer is not currently ready to accept more bytes (for example, its buffer is full), @@ -359,19 +359,19 @@ pub trait Write: crate::ErrorType { /// Blocking seek within streams. /// /// This trait is the `embedded-io` equivalent of [`std::io::Seek`]. -pub trait Seek: crate::ErrorType { +pub trait Seek: ErrorType { /// Seek to an offset, in bytes, in a stream. - fn seek(&mut self, pos: crate::SeekFrom) -> Result; + fn seek(&mut self, pos: SeekFrom) -> Result; /// Rewind to the beginning of a stream. fn rewind(&mut self) -> Result<(), Self::Error> { - self.seek(crate::SeekFrom::Start(0))?; + self.seek(SeekFrom::Start(0))?; Ok(()) } /// Returns the current seek position from the start of the stream. fn stream_position(&mut self) -> Result { - self.seek(crate::SeekFrom::Current(0)) + self.seek(SeekFrom::Current(0)) } } @@ -379,7 +379,7 @@ pub trait Seek: crate::ErrorType { /// /// This allows using a [`Read`] or [`BufRead`] in a nonblocking fashion, i.e. trying to read /// only when it is ready. -pub trait ReadReady: crate::ErrorType { +pub trait ReadReady: ErrorType { /// Get whether the reader is ready for immediately reading. /// /// This usually means that there is either some bytes have been received and are buffered and ready to be read, @@ -393,7 +393,7 @@ pub trait ReadReady: crate::ErrorType { /// /// This allows using a [`Write`] in a nonblocking fashion, i.e. trying to write /// only when it is ready. -pub trait WriteReady: crate::ErrorType { +pub trait WriteReady: ErrorType { /// Get whether the writer is ready for immediately writing. /// /// This usually means that there is free space in the internal transmit buffer. @@ -433,7 +433,7 @@ impl Write for &mut T { impl Seek for &mut T { #[inline] - fn seek(&mut self, pos: crate::SeekFrom) -> Result { + fn seek(&mut self, pos: SeekFrom) -> Result { T::seek(self, pos) } } From 3a4814f33a64e8ac416faa0f7440041079558b89 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 12 Jul 2023 19:02:39 +0200 Subject: [PATCH 057/199] io: Add `From` impls to convert between `ErrorKind` and `std::io::ErrorKind`. --- embedded-io/CHANGELOG.md | 4 +++- embedded-io/src/lib.rs | 52 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+), 1 deletion(-) diff --git a/embedded-io/CHANGELOG.md b/embedded-io/CHANGELOG.md index a91aabe37..45a1445e3 100644 --- a/embedded-io/CHANGELOG.md +++ b/embedded-io/CHANGELOG.md @@ -7,7 +7,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased -- Added `ReadReady`, `WriteReady` traits. They allow peeking whether the I/O handle is ready to read/write, so they allow using the traits in a non-blocking way. +- Add `ReadReady`, `WriteReady` traits. They allow peeking whether the I/O handle is ready to read/write, so they allow using the traits in a non-blocking way. +- Add variants to `ErrorKind` mirroring `std::io::ErrorKind`. +- Add `From` impls to convert between `ErrorKind` and `std::io::ErrorKind`. - Moved `embedded_io::blocking` to the crate root. - Split async traits to the `embedded-io-async` crate. - Split async trait adapters to separate crates. diff --git a/embedded-io/src/lib.rs b/embedded-io/src/lib.rs index 2c50deca9..ceb176aeb 100644 --- a/embedded-io/src/lib.rs +++ b/embedded-io/src/lib.rs @@ -92,6 +92,58 @@ pub enum ErrorKind { OutOfMemory, } +#[cfg(feature = "std")] +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] +impl From for std::io::ErrorKind { + fn from(value: ErrorKind) -> Self { + match value { + ErrorKind::NotFound => std::io::ErrorKind::NotFound, + ErrorKind::PermissionDenied => std::io::ErrorKind::PermissionDenied, + ErrorKind::ConnectionRefused => std::io::ErrorKind::ConnectionRefused, + ErrorKind::ConnectionReset => std::io::ErrorKind::ConnectionReset, + ErrorKind::ConnectionAborted => std::io::ErrorKind::ConnectionAborted, + ErrorKind::NotConnected => std::io::ErrorKind::NotConnected, + ErrorKind::AddrInUse => std::io::ErrorKind::AddrInUse, + ErrorKind::AddrNotAvailable => std::io::ErrorKind::AddrNotAvailable, + ErrorKind::BrokenPipe => std::io::ErrorKind::BrokenPipe, + ErrorKind::AlreadyExists => std::io::ErrorKind::AlreadyExists, + ErrorKind::InvalidInput => std::io::ErrorKind::InvalidInput, + ErrorKind::InvalidData => std::io::ErrorKind::InvalidData, + ErrorKind::TimedOut => std::io::ErrorKind::TimedOut, + ErrorKind::Interrupted => std::io::ErrorKind::Interrupted, + ErrorKind::Unsupported => std::io::ErrorKind::Unsupported, + ErrorKind::OutOfMemory => std::io::ErrorKind::OutOfMemory, + _ => std::io::ErrorKind::Other, + } + } +} + +#[cfg(feature = "std")] +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] +impl From for ErrorKind { + fn from(value: std::io::ErrorKind) -> Self { + match value { + std::io::ErrorKind::NotFound => ErrorKind::NotFound, + std::io::ErrorKind::PermissionDenied => ErrorKind::PermissionDenied, + std::io::ErrorKind::ConnectionRefused => ErrorKind::ConnectionRefused, + std::io::ErrorKind::ConnectionReset => ErrorKind::ConnectionReset, + std::io::ErrorKind::ConnectionAborted => ErrorKind::ConnectionAborted, + std::io::ErrorKind::NotConnected => ErrorKind::NotConnected, + std::io::ErrorKind::AddrInUse => ErrorKind::AddrInUse, + std::io::ErrorKind::AddrNotAvailable => ErrorKind::AddrNotAvailable, + std::io::ErrorKind::BrokenPipe => ErrorKind::BrokenPipe, + std::io::ErrorKind::AlreadyExists => ErrorKind::AlreadyExists, + std::io::ErrorKind::InvalidInput => ErrorKind::InvalidInput, + std::io::ErrorKind::InvalidData => ErrorKind::InvalidData, + std::io::ErrorKind::TimedOut => ErrorKind::TimedOut, + std::io::ErrorKind::Interrupted => ErrorKind::Interrupted, + std::io::ErrorKind::Unsupported => ErrorKind::Unsupported, + std::io::ErrorKind::OutOfMemory => ErrorKind::OutOfMemory, + _ => ErrorKind::Other, + } + } +} + /// Error trait. /// /// This trait allows generic code to do limited inspecting of errors, From f04beb5e02915a2aa51d55cb2349f0031b9f23a3 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 12 Jul 2023 19:12:04 +0200 Subject: [PATCH 058/199] io: remove adapters from main crate. --- embedded-io/CHANGELOG.md | 2 +- embedded-io/src/adapters.rs | 197 ------------------------------------ embedded-io/src/lib.rs | 35 ++++++- 3 files changed, 33 insertions(+), 201 deletions(-) delete mode 100644 embedded-io/src/adapters.rs diff --git a/embedded-io/CHANGELOG.md b/embedded-io/CHANGELOG.md index 45a1445e3..d5038933e 100644 --- a/embedded-io/CHANGELOG.md +++ b/embedded-io/CHANGELOG.md @@ -12,7 +12,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Add `From` impls to convert between `ErrorKind` and `std::io::ErrorKind`. - Moved `embedded_io::blocking` to the crate root. - Split async traits to the `embedded-io-async` crate. -- Split async trait adapters to separate crates. +- Split trait adapters to the `embedded-io-adapters` crate. - Rename trait `Io` to `ErrorKind`, for consistency with `embedded-hal`. ## 0.4.0 - 2022-11-25 diff --git a/embedded-io/src/adapters.rs b/embedded-io/src/adapters.rs deleted file mode 100644 index d64c92e4b..000000000 --- a/embedded-io/src/adapters.rs +++ /dev/null @@ -1,197 +0,0 @@ -//! Adapters to/from `std::io` traits. -//! -//! To interoperate with `std::io`, wrap a type in one of these -//! adapters. -//! -//! There are no separate adapters for `Read`/`ReadBuf`/`Write` traits. Instead, a single -//! adapter implements the right traits based on what the inner type implements. -//! This allows using these adapters when using `Read+Write`, for example. - -use crate::SeekFrom; - -/// Adapter from `std::io` traits. -#[cfg_attr(docsrs, doc(cfg(feature = "std")))] -#[derive(Clone)] -pub struct FromStd { - inner: T, -} - -impl FromStd { - /// Create a new adapter. - pub fn new(inner: T) -> Self { - Self { inner } - } - - /// Consume the adapter, returning the inner object. - pub fn into_inner(self) -> T { - self.inner - } -} - -impl FromStd { - /// Borrow the inner object. - pub fn inner(&self) -> &T { - &self.inner - } - - /// Mutably borrow the inner object. - pub fn inner_mut(&mut self) -> &mut T { - &mut self.inner - } -} - -impl crate::ErrorType for FromStd { - type Error = std::io::Error; -} - -impl crate::Read for FromStd { - fn read(&mut self, buf: &mut [u8]) -> Result { - self.inner.read(buf) - } -} - -impl crate::BufRead for FromStd { - fn fill_buf(&mut self) -> Result<&[u8], Self::Error> { - self.inner.fill_buf() - } - - fn consume(&mut self, amt: usize) { - self.inner.consume(amt) - } -} - -impl crate::Write for FromStd { - fn write(&mut self, buf: &[u8]) -> Result { - self.inner.write(buf) - } - fn flush(&mut self) -> Result<(), Self::Error> { - self.inner.flush() - } -} - -impl crate::Seek for FromStd { - fn seek(&mut self, pos: crate::SeekFrom) -> Result { - self.inner.seek(pos.into()) - } -} - -/// Adapter to `std::io` traits. -#[cfg_attr(docsrs, doc(cfg(feature = "std")))] -pub struct ToStd { - inner: T, -} - -impl ToStd { - /// Create a new adapter. - pub fn new(inner: T) -> Self { - Self { inner } - } - - /// Consume the adapter, returning the inner object. - pub fn into_inner(self) -> T { - self.inner - } -} - -impl ToStd { - /// Borrow the inner object. - pub fn inner(&self) -> &T { - &self.inner - } - - /// Mutably borrow the inner object. - pub fn inner_mut(&mut self) -> &mut T { - &mut self.inner - } -} - -impl std::io::Read for ToStd { - fn read(&mut self, buf: &mut [u8]) -> Result { - self.inner.read(buf).map_err(to_std_error) - } -} - -impl std::io::Write for ToStd { - fn write(&mut self, buf: &[u8]) -> Result { - self.inner.write(buf).map_err(to_std_error) - } - fn flush(&mut self) -> Result<(), std::io::Error> { - self.inner.flush().map_err(to_std_error) - } -} - -impl std::io::Seek for ToStd { - fn seek(&mut self, pos: std::io::SeekFrom) -> Result { - self.inner.seek(pos.into()).map_err(to_std_error) - } -} - -fn to_std_error(err: T) -> std::io::Error { - let kind = match err.kind() { - crate::ErrorKind::NotFound => std::io::ErrorKind::NotFound, - crate::ErrorKind::PermissionDenied => std::io::ErrorKind::PermissionDenied, - crate::ErrorKind::ConnectionRefused => std::io::ErrorKind::ConnectionRefused, - crate::ErrorKind::ConnectionReset => std::io::ErrorKind::ConnectionReset, - crate::ErrorKind::ConnectionAborted => std::io::ErrorKind::ConnectionAborted, - crate::ErrorKind::NotConnected => std::io::ErrorKind::NotConnected, - crate::ErrorKind::AddrInUse => std::io::ErrorKind::AddrInUse, - crate::ErrorKind::AddrNotAvailable => std::io::ErrorKind::AddrNotAvailable, - crate::ErrorKind::BrokenPipe => std::io::ErrorKind::BrokenPipe, - crate::ErrorKind::AlreadyExists => std::io::ErrorKind::AlreadyExists, - crate::ErrorKind::InvalidInput => std::io::ErrorKind::InvalidInput, - crate::ErrorKind::InvalidData => std::io::ErrorKind::InvalidData, - crate::ErrorKind::TimedOut => std::io::ErrorKind::TimedOut, - crate::ErrorKind::Interrupted => std::io::ErrorKind::Interrupted, - crate::ErrorKind::Unsupported => std::io::ErrorKind::Unsupported, - crate::ErrorKind::OutOfMemory => std::io::ErrorKind::OutOfMemory, - _ => std::io::ErrorKind::Other, - }; - std::io::Error::new(kind, format!("{:?}", err)) -} - -#[cfg_attr(docsrs, doc(cfg(feature = "std")))] -impl crate::Error for std::io::Error { - fn kind(&self) -> crate::ErrorKind { - match self.kind() { - std::io::ErrorKind::NotFound => crate::ErrorKind::NotFound, - std::io::ErrorKind::PermissionDenied => crate::ErrorKind::PermissionDenied, - std::io::ErrorKind::ConnectionRefused => crate::ErrorKind::ConnectionRefused, - std::io::ErrorKind::ConnectionReset => crate::ErrorKind::ConnectionReset, - std::io::ErrorKind::ConnectionAborted => crate::ErrorKind::ConnectionAborted, - std::io::ErrorKind::NotConnected => crate::ErrorKind::NotConnected, - std::io::ErrorKind::AddrInUse => crate::ErrorKind::AddrInUse, - std::io::ErrorKind::AddrNotAvailable => crate::ErrorKind::AddrNotAvailable, - std::io::ErrorKind::BrokenPipe => crate::ErrorKind::BrokenPipe, - std::io::ErrorKind::AlreadyExists => crate::ErrorKind::AlreadyExists, - std::io::ErrorKind::InvalidInput => crate::ErrorKind::InvalidInput, - std::io::ErrorKind::InvalidData => crate::ErrorKind::InvalidData, - std::io::ErrorKind::TimedOut => crate::ErrorKind::TimedOut, - std::io::ErrorKind::Interrupted => crate::ErrorKind::Interrupted, - std::io::ErrorKind::Unsupported => crate::ErrorKind::Unsupported, - std::io::ErrorKind::OutOfMemory => crate::ErrorKind::OutOfMemory, - _ => crate::ErrorKind::Other, - } - } -} - -#[cfg_attr(docsrs, doc(cfg(feature = "std")))] -impl From for std::io::SeekFrom { - fn from(pos: SeekFrom) -> Self { - match pos { - SeekFrom::Start(n) => std::io::SeekFrom::Start(n), - SeekFrom::End(n) => std::io::SeekFrom::End(n), - SeekFrom::Current(n) => std::io::SeekFrom::Current(n), - } - } -} - -#[cfg_attr(docsrs, doc(cfg(feature = "std")))] -impl From for SeekFrom { - fn from(pos: std::io::SeekFrom) -> SeekFrom { - match pos { - std::io::SeekFrom::Start(n) => SeekFrom::Start(n), - std::io::SeekFrom::End(n) => SeekFrom::End(n), - std::io::SeekFrom::Current(n) => SeekFrom::Current(n), - } - } -} diff --git a/embedded-io/src/lib.rs b/embedded-io/src/lib.rs index ceb176aeb..454ac8642 100644 --- a/embedded-io/src/lib.rs +++ b/embedded-io/src/lib.rs @@ -8,9 +8,6 @@ use core::fmt; #[cfg(feature = "alloc")] extern crate alloc; -#[cfg(feature = "std")] -pub mod adapters; - mod impls; /// Enumeration of possible methods to seek within an I/O object. @@ -26,6 +23,30 @@ pub enum SeekFrom { Current(i64), } +#[cfg(feature = "std")] +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] +impl From for std::io::SeekFrom { + fn from(pos: SeekFrom) -> Self { + match pos { + SeekFrom::Start(n) => std::io::SeekFrom::Start(n), + SeekFrom::End(n) => std::io::SeekFrom::End(n), + SeekFrom::Current(n) => std::io::SeekFrom::Current(n), + } + } +} + +#[cfg(feature = "std")] +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] +impl From for SeekFrom { + fn from(pos: std::io::SeekFrom) -> SeekFrom { + match pos { + std::io::SeekFrom::Start(n) => SeekFrom::Start(n), + std::io::SeekFrom::End(n) => SeekFrom::End(n), + std::io::SeekFrom::Current(n) => SeekFrom::Current(n), + } + } +} + #[derive(Debug, Copy, Clone, Eq, PartialEq)] #[non_exhaustive] /// Possible kinds of errors. @@ -165,6 +186,14 @@ impl Error for ErrorKind { } } +#[cfg(feature = "std")] +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] +impl crate::Error for std::io::Error { + fn kind(&self) -> crate::ErrorKind { + self.kind().into() + } +} + /// Base trait for all IO traits, defining the error type. /// /// All IO operations of all traits return the error defined in this trait. From 8db8b0bb211e9002d8d55cb7a5e3b14f50268327 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 12 Jul 2023 19:25:57 +0200 Subject: [PATCH 059/199] io: add separate embedded-io-adapters crate, add futures/tokio support. If we want to release `embedded-io` 1.0 and the adapters live there, we can't make any breaking changes to them after release. Putting them in a separate crate allows doing breaking changes without breaking everyone using the IO traits. --- .github/workflows/clippy.yml | 2 +- .github/workflows/test.yml | 4 +- Cargo.toml | 1 + embedded-io-adapters/Cargo.toml | 28 ++++ embedded-io-adapters/LICENSE-APACHE | 201 +++++++++++++++++++++++++ embedded-io-adapters/LICENSE-MIT | 25 +++ embedded-io-adapters/README.md | 56 +++++++ embedded-io-adapters/src/futures_03.rs | 66 ++++++++ embedded-io-adapters/src/lib.rs | 20 +++ embedded-io-adapters/src/std.rs | 139 +++++++++++++++++ embedded-io-adapters/src/tokio_1.rs | 81 ++++++++++ 11 files changed, 621 insertions(+), 2 deletions(-) create mode 100644 embedded-io-adapters/Cargo.toml create mode 100644 embedded-io-adapters/LICENSE-APACHE create mode 100644 embedded-io-adapters/LICENSE-MIT create mode 100644 embedded-io-adapters/README.md create mode 100644 embedded-io-adapters/src/futures_03.rs create mode 100644 embedded-io-adapters/src/lib.rs create mode 100644 embedded-io-adapters/src/std.rs create mode 100644 embedded-io-adapters/src/tokio_1.rs diff --git a/.github/workflows/clippy.yml b/.github/workflows/clippy.yml index bdec38346..1479e4a56 100644 --- a/.github/workflows/clippy.yml +++ b/.github/workflows/clippy.yml @@ -17,4 +17,4 @@ jobs: # Use a pinned version to avoid spontaneous breakages (new clippy lints are added often) toolchain: nightly-2023-07-03 components: clippy - - run: cargo clippy --features=std -- --deny=warnings \ No newline at end of file + - run: cargo clippy --all-features -- --deny=warnings \ No newline at end of file diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index e50f0c6a4..44a82a39b 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -28,7 +28,9 @@ jobs: features: std - target: x86_64-unknown-linux-gnu features: alloc - + - target: x86_64-unknown-linux-gnu + features: std,tokio-1,futures-03 + rust: nightly steps: - uses: actions/checkout@v3 - uses: dtolnay/rust-toolchain@master diff --git a/Cargo.toml b/Cargo.toml index ca4e0c584..ccfa1e0f6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,4 +10,5 @@ members = [ "embedded-can", "embedded-io", "embedded-io-async", # nightly-only + "embedded-io-adapters", ] diff --git a/embedded-io-adapters/Cargo.toml b/embedded-io-adapters/Cargo.toml new file mode 100644 index 000000000..05905ad60 --- /dev/null +++ b/embedded-io-adapters/Cargo.toml @@ -0,0 +1,28 @@ +[package] +name = "embedded-io-adapters" +version = "0.1.0" +edition = "2021" +description = "Adapters between the `embedded-io` traits and other I/O traits" +repository = "https://github.com/rust-embedded/embedded-hal" +readme = "README.md" +license = "MIT OR Apache-2.0" +categories = [ + "embedded", + "no-std", +] + +[features] +std = ["embedded-io/std"] +tokio-1 = ["std", "dep:tokio", "dep:embedded-io-async", "embedded-io-async?/std"] +futures-03 = ["std", "dep:futures", "dep:embedded-io-async", "embedded-io-async?/std"] + +[dependencies] +embedded-io = { version = "0.5", path = "../embedded-io" } +embedded-io-async = { version = "0.5", path = "../embedded-io-async", optional = true } + +futures = { version = "0.3.21", features = ["std"], default-features = false, optional = true } +tokio = { version = "1", default-features = false, optional = true } + +[package.metadata.docs.rs] +features = ["std"] +rustdoc-args = ["--cfg", "docsrs"] diff --git a/embedded-io-adapters/LICENSE-APACHE b/embedded-io-adapters/LICENSE-APACHE new file mode 100644 index 000000000..16fe87b06 --- /dev/null +++ b/embedded-io-adapters/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/embedded-io-adapters/LICENSE-MIT b/embedded-io-adapters/LICENSE-MIT new file mode 100644 index 000000000..e00608fbd --- /dev/null +++ b/embedded-io-adapters/LICENSE-MIT @@ -0,0 +1,25 @@ +Copyright (c) 2023 The embedded-io authors + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/embedded-io-adapters/README.md b/embedded-io-adapters/README.md new file mode 100644 index 000000000..e4ad0278e --- /dev/null +++ b/embedded-io-adapters/README.md @@ -0,0 +1,56 @@ +[![crates.io](https://img.shields.io/crates/d/embedded-io.svg)](https://crates.io/crates/embedded-io) +[![crates.io](https://img.shields.io/crates/v/embedded-io.svg)](https://crates.io/crates/embedded-io) +[![Documentation](https://docs.rs/embedded-io/badge.svg)](https://docs.rs/embedded-io) + +# `embedded-io-adapters` + +This project is developed and maintained by the [HAL team](https://github.com/rust-embedded/wg#the-hal-team). + +Adapters between the [`embedded-io`](https://crates.io/crates/embedded-io) and [`embedded-io-async`](https://crates.io/crates/embedded-io-async) traits and other IO traits. + +The adapters are structs that wrap an I/O stream and implement another family of I/O traits +based on the wrapped streams. This allows "converting" from an `embedded_io::Read` +to a `std::io::Read` or vice versa, for example. + +There are no separate adapters for `Read`/`ReadBuf`/`Write` traits. Instead, a single +adapter implements the right traits based on what the inner type implements. +This allows using these adapters when using combinations of traits, like `Read+Write`. + +## Supported traits + +For `embedded-io`: + +- [`std::io`](https://doc.rust-lang.org/stable/std/io/index.html) traits. Needs the `std` feature. + +For `embedded-io-async`: + +- [`futures` 0.3](https://crates.io/crates/futures) traits. Needs the `futures-03` feature. +- [`tokio` 1.x](https://crates.io/crates/tokio) traits. Needs the `tokio-1` feature. + +## Minimum Supported Rust Version (MSRV) + +This crate is guaranteed to compile on stable Rust 1.59 and up. It *might* +compile with older versions but that may change in any new patch release. + +See [here](../docs/msrv.md) for details on how the MSRV may be upgraded. + +Enabling any of the `tokio-*` or `futures-*` Cargo features requires Rust nightly newer than +`nightly-2022-11-22`, due to requiring support for `async fn` in traits (AFIT), +which is not stable yet. Keep in mind Rust nightlies can make backwards-incompatible +changes to unstable features at any time. + +## License + +Licensed under either of + +- Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or + ) +- MIT license ([LICENSE-MIT](LICENSE-MIT) or ) + +at your option. + +### Contribution + +Unless you explicitly state otherwise, any contribution intentionally submitted +for inclusion in the work by you, as defined in the Apache-2.0 license, shall be +dual licensed as above, without any additional terms or conditions. diff --git a/embedded-io-adapters/src/futures_03.rs b/embedded-io-adapters/src/futures_03.rs new file mode 100644 index 000000000..b672215ed --- /dev/null +++ b/embedded-io-adapters/src/futures_03.rs @@ -0,0 +1,66 @@ +//! Adapters to/from `futures::io` traits. + +use core::future::poll_fn; +use core::pin::Pin; + +/// Adapter from `futures::io` traits. +#[derive(Clone)] +pub struct FromFutures { + inner: T, +} + +impl FromFutures { + /// Create a new adapter. + pub fn new(inner: T) -> Self { + Self { inner } + } + + /// Consume the adapter, returning the inner object. + pub fn into_inner(self) -> T { + self.inner + } +} + +impl FromFutures { + /// Borrow the inner object. + pub fn inner(&self) -> &T { + &self.inner + } + + /// Mutably borrow the inner object. + pub fn inner_mut(&mut self) -> &mut T { + &mut self.inner + } +} + +impl embedded_io::ErrorType for FromFutures { + type Error = std::io::Error; +} + +impl embedded_io_async::Read for FromFutures { + async fn read(&mut self, buf: &mut [u8]) -> Result { + poll_fn(|cx| Pin::new(&mut self.inner).poll_read(cx, buf)).await + } +} + +impl embedded_io_async::Write for FromFutures { + async fn write(&mut self, buf: &[u8]) -> Result { + poll_fn(|cx| Pin::new(&mut self.inner).poll_write(cx, buf)).await + } + + async fn flush(&mut self) -> Result<(), Self::Error> { + poll_fn(|cx| Pin::new(&mut self.inner).poll_flush(cx)).await + } +} + +impl embedded_io_async::Seek for FromFutures { + async fn seek(&mut self, pos: embedded_io::SeekFrom) -> Result { + poll_fn(move |cx| Pin::new(&mut self.inner).poll_seek(cx, pos.into())).await + } +} + +// TODO: ToFutures. +// It's a bit tricky because futures::io is "stateless", while we're "stateful" (we +// return futures that borrow Self and get polled for the duration of the operation.) +// It can probably done by storing the futures in Self, with unsafe Pin hacks because +// we're a self-referential struct diff --git a/embedded-io-adapters/src/lib.rs b/embedded-io-adapters/src/lib.rs new file mode 100644 index 000000000..d11316405 --- /dev/null +++ b/embedded-io-adapters/src/lib.rs @@ -0,0 +1,20 @@ +#![cfg_attr(not(feature = "std"), no_std)] +#![cfg_attr(docsrs, feature(doc_cfg))] +#![cfg_attr( + any(feature = "tokio-1", feature = "futures-03"), + feature(async_fn_in_trait, impl_trait_projections) +)] +#![warn(missing_docs)] +#![doc = include_str!("../README.md")] + +#[cfg(feature = "std")] +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] +pub mod std; + +#[cfg(feature = "futures-03")] +#[cfg_attr(docsrs, doc(cfg(feature = "futures-03")))] +pub mod futures_03; + +#[cfg(feature = "tokio-1")] +#[cfg_attr(docsrs, doc(cfg(feature = "tokio-1")))] +pub mod tokio_1; diff --git a/embedded-io-adapters/src/std.rs b/embedded-io-adapters/src/std.rs new file mode 100644 index 000000000..7449c805b --- /dev/null +++ b/embedded-io-adapters/src/std.rs @@ -0,0 +1,139 @@ +//! Adapters to/from `std::io` traits. + +/// Adapter from `std::io` traits. +#[derive(Clone)] +pub struct FromStd { + inner: T, +} + +impl FromStd { + /// Create a new adapter. + pub fn new(inner: T) -> Self { + Self { inner } + } + + /// Consume the adapter, returning the inner object. + pub fn into_inner(self) -> T { + self.inner + } +} + +impl FromStd { + /// Borrow the inner object. + pub fn inner(&self) -> &T { + &self.inner + } + + /// Mutably borrow the inner object. + pub fn inner_mut(&mut self) -> &mut T { + &mut self.inner + } +} + +impl embedded_io::ErrorType for FromStd { + type Error = std::io::Error; +} + +impl embedded_io::Read for FromStd { + fn read(&mut self, buf: &mut [u8]) -> Result { + self.inner.read(buf) + } +} + +impl embedded_io::BufRead for FromStd { + fn fill_buf(&mut self) -> Result<&[u8], Self::Error> { + self.inner.fill_buf() + } + + fn consume(&mut self, amt: usize) { + self.inner.consume(amt) + } +} + +impl embedded_io::Write for FromStd { + fn write(&mut self, buf: &[u8]) -> Result { + self.inner.write(buf) + } + fn flush(&mut self) -> Result<(), Self::Error> { + self.inner.flush() + } +} + +impl embedded_io::Seek for FromStd { + fn seek(&mut self, pos: embedded_io::SeekFrom) -> Result { + self.inner.seek(pos.into()) + } +} + +/// Adapter to `std::io` traits. +pub struct ToStd { + inner: T, +} + +impl ToStd { + /// Create a new adapter. + pub fn new(inner: T) -> Self { + Self { inner } + } + + /// Consume the adapter, returning the inner object. + pub fn into_inner(self) -> T { + self.inner + } +} + +impl ToStd { + /// Borrow the inner object. + pub fn inner(&self) -> &T { + &self.inner + } + + /// Mutably borrow the inner object. + pub fn inner_mut(&mut self) -> &mut T { + &mut self.inner + } +} + +impl std::io::Read for ToStd { + fn read(&mut self, buf: &mut [u8]) -> Result { + self.inner.read(buf).map_err(to_std_error) + } +} + +impl std::io::Write for ToStd { + fn write(&mut self, buf: &[u8]) -> Result { + self.inner.write(buf).map_err(to_std_error) + } + fn flush(&mut self) -> Result<(), std::io::Error> { + self.inner.flush().map_err(to_std_error) + } +} + +impl std::io::Seek for ToStd { + fn seek(&mut self, pos: std::io::SeekFrom) -> Result { + self.inner.seek(pos.into()).map_err(to_std_error) + } +} + +fn to_std_error(err: T) -> std::io::Error { + let kind = match err.kind() { + embedded_io::ErrorKind::NotFound => std::io::ErrorKind::NotFound, + embedded_io::ErrorKind::PermissionDenied => std::io::ErrorKind::PermissionDenied, + embedded_io::ErrorKind::ConnectionRefused => std::io::ErrorKind::ConnectionRefused, + embedded_io::ErrorKind::ConnectionReset => std::io::ErrorKind::ConnectionReset, + embedded_io::ErrorKind::ConnectionAborted => std::io::ErrorKind::ConnectionAborted, + embedded_io::ErrorKind::NotConnected => std::io::ErrorKind::NotConnected, + embedded_io::ErrorKind::AddrInUse => std::io::ErrorKind::AddrInUse, + embedded_io::ErrorKind::AddrNotAvailable => std::io::ErrorKind::AddrNotAvailable, + embedded_io::ErrorKind::BrokenPipe => std::io::ErrorKind::BrokenPipe, + embedded_io::ErrorKind::AlreadyExists => std::io::ErrorKind::AlreadyExists, + embedded_io::ErrorKind::InvalidInput => std::io::ErrorKind::InvalidInput, + embedded_io::ErrorKind::InvalidData => std::io::ErrorKind::InvalidData, + embedded_io::ErrorKind::TimedOut => std::io::ErrorKind::TimedOut, + embedded_io::ErrorKind::Interrupted => std::io::ErrorKind::Interrupted, + embedded_io::ErrorKind::Unsupported => std::io::ErrorKind::Unsupported, + embedded_io::ErrorKind::OutOfMemory => std::io::ErrorKind::OutOfMemory, + _ => std::io::ErrorKind::Other, + }; + std::io::Error::new(kind, format!("{:?}", err)) +} diff --git a/embedded-io-adapters/src/tokio_1.rs b/embedded-io-adapters/src/tokio_1.rs new file mode 100644 index 000000000..04e1cf1aa --- /dev/null +++ b/embedded-io-adapters/src/tokio_1.rs @@ -0,0 +1,81 @@ +//! Adapters to/from `tokio::io` traits. + +use core::future::poll_fn; +use core::pin::Pin; +use core::task::Poll; + +/// Adapter from `tokio::io` traits. +#[derive(Clone)] +pub struct FromTokio { + inner: T, +} + +impl FromTokio { + /// Create a new adapter. + pub fn new(inner: T) -> Self { + Self { inner } + } + + /// Consume the adapter, returning the inner object. + pub fn into_inner(self) -> T { + self.inner + } +} + +impl FromTokio { + /// Borrow the inner object. + pub fn inner(&self) -> &T { + &self.inner + } + + /// Mutably borrow the inner object. + pub fn inner_mut(&mut self) -> &mut T { + &mut self.inner + } +} + +impl embedded_io::ErrorType for FromTokio { + type Error = std::io::Error; +} + +impl embedded_io_async::Read for FromTokio { + async fn read(&mut self, buf: &mut [u8]) -> Result { + poll_fn(|cx| { + let mut buf = tokio::io::ReadBuf::new(buf); + match Pin::new(&mut self.inner).poll_read(cx, &mut buf) { + Poll::Ready(r) => match r { + Ok(()) => Poll::Ready(Ok(buf.filled().len())), + Err(e) => Poll::Ready(Err(e)), + }, + Poll::Pending => Poll::Pending, + } + }) + .await + } +} + +impl embedded_io_async::Write for FromTokio { + async fn write(&mut self, buf: &[u8]) -> Result { + poll_fn(|cx| Pin::new(&mut self.inner).poll_write(cx, buf)).await + } + + async fn flush(&mut self) -> Result<(), Self::Error> { + poll_fn(|cx| Pin::new(&mut self.inner).poll_flush(cx)).await + } +} + +impl embedded_io_async::Seek for FromTokio { + async fn seek(&mut self, pos: embedded_io::SeekFrom) -> Result { + // Note: `start_seek` can return an error if there is another seek in progress. + // Therefor it is recommended to call `poll_complete` before any call to `start_seek`. + poll_fn(|cx| Pin::new(&mut self.inner).poll_complete(cx)).await?; + Pin::new(&mut self.inner).start_seek(pos.into())?; + poll_fn(|cx| Pin::new(&mut self.inner).poll_complete(cx)).await + } +} + +// TODO: ToTokio. +// It's a bit tricky because tokio::io is "stateless", while we're "stateful" (we +// return futures that borrow Self and get polled for the duration of the operation.) +// It can probably done by storing the futures in Self, with unsafe Pin hacks because +// we're a self-referential struct From 7d975f025d1eefaf0a4a0b4f69053f3b9ee889f4 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Fri, 14 Jul 2023 21:01:43 +0200 Subject: [PATCH 060/199] Bump MSRV to 1.60 Needed for namespaced features (`dep:` in Cargo.toml) --- .github/workflows/test.yml | 2 +- README.md | 2 +- embedded-can/README.md | 4 ++-- embedded-hal-bus/README.md | 4 ++-- embedded-hal-nb/README.md | 4 ++-- embedded-hal/CHANGELOG.md | 2 +- embedded-hal/README.md | 4 ++-- embedded-io-adapters/README.md | 2 +- embedded-io/README.md | 2 +- 9 files changed, 13 insertions(+), 13 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 44a82a39b..9314fc3d9 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -17,7 +17,7 @@ jobs: matrix: rust: - stable - - 1.59.0 # MSRV + - 1.60.0 # MSRV - nightly target: - x86_64-unknown-linux-gnu diff --git a/README.md b/README.md index 6067b23b8..3b8692783 100644 --- a/README.md +++ b/README.md @@ -72,7 +72,7 @@ on crates.io. ## Minimum Supported Rust Version (MSRV) -This crate is guaranteed to compile on stable Rust 1.59 and up. It *might* +This crate is guaranteed to compile on stable Rust 1.60 and up. It *might* compile with older versions but that may change in any new patch release. See [here](docs/msrv.md) for details on how the MSRV may be upgraded. diff --git a/embedded-can/README.md b/embedded-can/README.md index 3380c43f3..d0b7d05d9 100644 --- a/embedded-can/README.md +++ b/embedded-can/README.md @@ -1,7 +1,7 @@ [![crates.io](https://img.shields.io/crates/d/embedded-can.svg)](https://crates.io/crates/embedded-can) [![crates.io](https://img.shields.io/crates/v/embedded-can.svg)](https://crates.io/crates/embedded-can) [![Documentation](https://docs.rs/embedded-can/badge.svg)](https://docs.rs/embedded-can) -![Minimum Supported Rust Version](https://img.shields.io/badge/rustc-1.59+-blue.svg) +![Minimum Supported Rust Version](https://img.shields.io/badge/rustc-1.60+-blue.svg) # `embedded-can` @@ -15,7 +15,7 @@ This project is developed and maintained by the [HAL team](https://github.com/ru ## Minimum Supported Rust Version (MSRV) -This crate is guaranteed to compile on stable Rust 1.59 and up. It *might* +This crate is guaranteed to compile on stable Rust 1.60 and up. It *might* compile with older versions but that may change in any new patch release. See [here](../docs/msrv.md) for details on how the MSRV may be upgraded. diff --git a/embedded-hal-bus/README.md b/embedded-hal-bus/README.md index 81bf52c2c..394ffb88b 100644 --- a/embedded-hal-bus/README.md +++ b/embedded-hal-bus/README.md @@ -1,7 +1,7 @@ [![crates.io](https://img.shields.io/crates/d/embedded-hal-bus.svg)](https://crates.io/crates/embedded-hal-bus) [![crates.io](https://img.shields.io/crates/v/embedded-hal-bus.svg)](https://crates.io/crates/embedded-hal-bus) [![Documentation](https://docs.rs/embedded-hal-bus/badge.svg)](https://docs.rs/embedded-hal-bus) -![Minimum Supported Rust Version](https://img.shields.io/badge/rustc-1.59+-blue.svg) +![Minimum Supported Rust Version](https://img.shields.io/badge/rustc-1.60+-blue.svg) # `embedded-hal-bus` @@ -34,7 +34,7 @@ provides mechanisms to obtain multiple `I2c` instances out of a single `I2c` ins ## Minimum Supported Rust Version (MSRV) -This crate is guaranteed to compile on stable Rust 1.59 and up. It *might* +This crate is guaranteed to compile on stable Rust 1.60 and up. It *might* compile with older versions but that may change in any new patch release. See [here](../docs/msrv.md) for details on how the MSRV may be upgraded. diff --git a/embedded-hal-nb/README.md b/embedded-hal-nb/README.md index f04b79475..aa72e7b1e 100644 --- a/embedded-hal-nb/README.md +++ b/embedded-hal-nb/README.md @@ -1,7 +1,7 @@ [![crates.io](https://img.shields.io/crates/d/embedded-hal-nb.svg)](https://crates.io/crates/embedded-hal-nb) [![crates.io](https://img.shields.io/crates/v/embedded-hal-nb.svg)](https://crates.io/crates/embedded-hal-nb) [![Documentation](https://docs.rs/embedded-hal-nb/badge.svg)](https://docs.rs/embedded-hal-nb) -![Minimum Supported Rust Version](https://img.shields.io/badge/rustc-1.59+-blue.svg) +![Minimum Supported Rust Version](https://img.shields.io/badge/rustc-1.60+-blue.svg) # `embedded-hal-nb` @@ -17,7 +17,7 @@ This project is developed and maintained by the [HAL team](https://github.com/ru ## Minimum Supported Rust Version (MSRV) -This crate is guaranteed to compile on stable Rust 1.59 and up. It *might* +This crate is guaranteed to compile on stable Rust 1.60 and up. It *might* compile with older versions but that may change in any new patch release. See [here](../docs/msrv.md) for details on how the MSRV may be upgraded. diff --git a/embedded-hal/CHANGELOG.md b/embedded-hal/CHANGELOG.md index 0803cb8f7..ccb13c888 100644 --- a/embedded-hal/CHANGELOG.md +++ b/embedded-hal/CHANGELOG.md @@ -65,7 +65,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). *** This is (also) an alpha release with breaking changes (sorry) *** ### Changed -- The Minimum Supported Rust Version (MSRV) is now 1.59.0 +- The Minimum Supported Rust Version (MSRV) is now 1.60.0 - `spi`: unify all traits into `SpiReadBus`, `SpiWriteBus` and `SpiBus` (read-write). - `spi`: Add `SpiDevice` trait to represent a single device in a (possibly shared) bus, with managed chip-select (CS) pin. - `spi`: Clarify that implementations are allowed to return before operations are finished, add `flush` to wait until finished. diff --git a/embedded-hal/README.md b/embedded-hal/README.md index 902e64883..98871ebb9 100644 --- a/embedded-hal/README.md +++ b/embedded-hal/README.md @@ -1,7 +1,7 @@ [![crates.io](https://img.shields.io/crates/d/embedded-hal.svg)](https://crates.io/crates/embedded-hal) [![crates.io](https://img.shields.io/crates/v/embedded-hal.svg)](https://crates.io/crates/embedded-hal) [![Documentation](https://docs.rs/embedded-hal/badge.svg)](https://docs.rs/embedded-hal) -![Minimum Supported Rust Version](https://img.shields.io/badge/rustc-1.59+-blue.svg) +![Minimum Supported Rust Version](https://img.shields.io/badge/rustc-1.60+-blue.svg) # `embedded-hal` @@ -15,7 +15,7 @@ This project is developed and maintained by the [HAL team](https://github.com/ru ## Minimum Supported Rust Version (MSRV) -This crate is guaranteed to compile on stable Rust 1.59 and up. It *might* +This crate is guaranteed to compile on stable Rust 1.60 and up. It *might* compile with older versions but that may change in any new patch release. See [here](../docs/msrv.md) for details on how the MSRV may be upgraded. diff --git a/embedded-io-adapters/README.md b/embedded-io-adapters/README.md index e4ad0278e..0cdb8c558 100644 --- a/embedded-io-adapters/README.md +++ b/embedded-io-adapters/README.md @@ -29,7 +29,7 @@ For `embedded-io-async`: ## Minimum Supported Rust Version (MSRV) -This crate is guaranteed to compile on stable Rust 1.59 and up. It *might* +This crate is guaranteed to compile on stable Rust 1.60 and up. It *might* compile with older versions but that may change in any new patch release. See [here](../docs/msrv.md) for details on how the MSRV may be upgraded. diff --git a/embedded-io/README.md b/embedded-io/README.md index d579b09f6..3318daa2b 100644 --- a/embedded-io/README.md +++ b/embedded-io/README.md @@ -21,7 +21,7 @@ stream, causing unexpected errors. To solve this, `embedded-io` specifies `Read` ## Minimum Supported Rust Version (MSRV) -This crate is guaranteed to compile on stable Rust 1.59 and up. It *might* +This crate is guaranteed to compile on stable Rust 1.60 and up. It *might* compile with older versions but that may change in any new patch release. See [here](../docs/msrv.md) for details on how the MSRV may be upgraded. From 981caa649b280fe2df1a23b2c611486dcdd16032 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sat, 15 Jul 2023 13:05:54 +0200 Subject: [PATCH 061/199] Fix workspace issues when building for stable. --- .github/workflows/test.yml | 2 +- Cargo.stable.toml | 20 ++++++++++++++++++++ Cargo.toml | 5 ++--- 3 files changed, 23 insertions(+), 4 deletions(-) create mode 100644 Cargo.stable.toml diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 9314fc3d9..a2fa0d9d7 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -38,7 +38,7 @@ jobs: toolchain: ${{ matrix.rust }} target: ${{ matrix.target }} - - run: sed -i '/nightly-only/d' Cargo.toml + - run: mv Cargo.stable.toml Cargo.toml if: matrix.rust != 'nightly' - run: cargo check --target=${{ matrix.target }} --features=${{ matrix.features }} diff --git a/Cargo.stable.toml b/Cargo.stable.toml new file mode 100644 index 000000000..78f6fd3cd --- /dev/null +++ b/Cargo.stable.toml @@ -0,0 +1,20 @@ +# CI moves this file to `Cargo.toml` when building for stable. + +[workspace] +resolver = "2" + +members = [ + "embedded-hal", + "embedded-hal-nb", + "embedded-hal-bus", + "embedded-can", + "embedded-io", + "embedded-io-adapters", +] + +# Cargo implicitly adds path dependencies to the workspace. +# Even if they're optional and not enabled. This prevents that. +exclude = [ + "embedded-hal-async", + "embedded-io-async", +] \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml index ccfa1e0f6..eb2832174 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,14 +1,13 @@ [workspace] resolver = "2" -# CI removes lines containing 'nightly-only' when not building with nightly. members = [ "embedded-hal", - "embedded-hal-async", # nightly-only + "embedded-hal-async", "embedded-hal-nb", "embedded-hal-bus", "embedded-can", "embedded-io", - "embedded-io-async", # nightly-only + "embedded-io-async", "embedded-io-adapters", ] From 332d0cea4d8c22f03f97543e00c0acc7d6220268 Mon Sep 17 00:00:00 2001 From: Scott Mabin Date: Fri, 14 Jul 2023 12:09:51 +0100 Subject: [PATCH 062/199] Missing `ReadExact` & WriteAll error impls for std - Add `ReadExactError` for `std::io::Error` - Add `WriteAllError` for `std::io::Error` --- embedded-io-adapters/src/std.rs | 24 +++--------------------- embedded-io/CHANGELOG.md | 1 + embedded-io/src/lib.rs | 30 ++++++++++++++++++++++++++++++ 3 files changed, 34 insertions(+), 21 deletions(-) diff --git a/embedded-io-adapters/src/std.rs b/embedded-io-adapters/src/std.rs index 7449c805b..63978583d 100644 --- a/embedded-io-adapters/src/std.rs +++ b/embedded-io-adapters/src/std.rs @@ -115,25 +115,7 @@ impl std::io::Seek for ToStd { } } -fn to_std_error(err: T) -> std::io::Error { - let kind = match err.kind() { - embedded_io::ErrorKind::NotFound => std::io::ErrorKind::NotFound, - embedded_io::ErrorKind::PermissionDenied => std::io::ErrorKind::PermissionDenied, - embedded_io::ErrorKind::ConnectionRefused => std::io::ErrorKind::ConnectionRefused, - embedded_io::ErrorKind::ConnectionReset => std::io::ErrorKind::ConnectionReset, - embedded_io::ErrorKind::ConnectionAborted => std::io::ErrorKind::ConnectionAborted, - embedded_io::ErrorKind::NotConnected => std::io::ErrorKind::NotConnected, - embedded_io::ErrorKind::AddrInUse => std::io::ErrorKind::AddrInUse, - embedded_io::ErrorKind::AddrNotAvailable => std::io::ErrorKind::AddrNotAvailable, - embedded_io::ErrorKind::BrokenPipe => std::io::ErrorKind::BrokenPipe, - embedded_io::ErrorKind::AlreadyExists => std::io::ErrorKind::AlreadyExists, - embedded_io::ErrorKind::InvalidInput => std::io::ErrorKind::InvalidInput, - embedded_io::ErrorKind::InvalidData => std::io::ErrorKind::InvalidData, - embedded_io::ErrorKind::TimedOut => std::io::ErrorKind::TimedOut, - embedded_io::ErrorKind::Interrupted => std::io::ErrorKind::Interrupted, - embedded_io::ErrorKind::Unsupported => std::io::ErrorKind::Unsupported, - embedded_io::ErrorKind::OutOfMemory => std::io::ErrorKind::OutOfMemory, - _ => std::io::ErrorKind::Other, - }; - std::io::Error::new(kind, format!("{:?}", err)) +/// Convert a embedded-io error to a std::io::Error +pub fn to_std_error(err: T) -> std::io::Error { + std::io::Error::new(err.kind().into(), format!("{:?}", err)) } diff --git a/embedded-io/CHANGELOG.md b/embedded-io/CHANGELOG.md index d5038933e..520e4b4dc 100644 --- a/embedded-io/CHANGELOG.md +++ b/embedded-io/CHANGELOG.md @@ -13,6 +13,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Moved `embedded_io::blocking` to the crate root. - Split async traits to the `embedded-io-async` crate. - Split trait adapters to the `embedded-io-adapters` crate. +- Add `std::io` impls for `ReadExactError` & `WriteAllError`. - Rename trait `Io` to `ErrorKind`, for consistency with `embedded-hal`. ## 0.4.0 - 2022-11-25 diff --git a/embedded-io/src/lib.rs b/embedded-io/src/lib.rs index 454ac8642..f6a8aa288 100644 --- a/embedded-io/src/lib.rs +++ b/embedded-io/src/lib.rs @@ -227,6 +227,33 @@ impl From for ReadExactError { } } +#[cfg(feature = "std")] +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] +impl From> for std::io::Error { + fn from(err: ReadExactError) -> Self { + match err { + ReadExactError::UnexpectedEof => std::io::Error::new( + std::io::ErrorKind::UnexpectedEof, + "UnexpectedEof".to_owned(), + ), + ReadExactError::Other(e) => std::io::Error::new(e.kind(), format!("{:?}", e)), + } + } +} + +#[cfg(feature = "std")] +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] +impl From> for std::io::Error { + fn from(err: WriteAllError) -> Self { + match err { + WriteAllError::WriteZero => { + std::io::Error::new(std::io::ErrorKind::WriteZero, "WriteZero".to_owned()) + } + WriteAllError::Other(e) => std::io::Error::new(e.kind(), format!("{:?}", e)), + } + } +} + impl fmt::Display for ReadExactError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{:?}", self) @@ -234,6 +261,7 @@ impl fmt::Display for ReadExactError { } #[cfg(feature = "std")] +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] impl std::error::Error for ReadExactError {} /// Error returned by [`Write::write_fmt`] @@ -260,6 +288,7 @@ impl fmt::Display for WriteFmtError { } #[cfg(feature = "std")] +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] impl std::error::Error for WriteFmtError {} /// Error returned by [`Write::write_all`] @@ -284,6 +313,7 @@ impl fmt::Display for WriteAllError { } #[cfg(feature = "std")] +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] impl std::error::Error for WriteAllError {} /// Blocking reader. From fa91e1facc77716d33d8c2e41d635ffcfb24410d Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sat, 15 Jul 2023 16:27:26 +0200 Subject: [PATCH 063/199] doc: use include_str!(README.md) for embedded-hal crate docs. --- embedded-hal/README.md | 79 ++++++++++++++++++++++++++++++++++++++--- embedded-hal/src/lib.rs | 77 +-------------------------------------- 2 files changed, 76 insertions(+), 80 deletions(-) diff --git a/embedded-hal/README.md b/embedded-hal/README.md index 98871ebb9..97aff726e 100644 --- a/embedded-hal/README.md +++ b/embedded-hal/README.md @@ -9,9 +9,80 @@ This project is developed and maintained by the [HAL team](https://github.com/rust-embedded/wg#the-hal-team). -## [API reference] +**NOTE** This HAL is still is active development. Expect the traits presented here to be +tweaked, split or be replaced wholesale before being stabilized, i.e. before hitting the 1.0.0 +release. -[API reference]: https://docs.rs/embedded-hal +**NOTE** If you want to use an alpha release of the 1.0.0 version, use an exact version +specifier in your `Cargo.toml` like: `embedded-hal = "=1.0.0-alpha.2"`. + +## Companion crates + +The main `embedded-hal` crate contains only blocking traits, where the operation is done +synchronously before returning. Check out the following crates, which contain versions +of the traits for other execution models: + +- [`embedded-hal-async`](https://docs.rs/embedded-hal-async): async/await-based. +- [`embedded-hal-nb`](https://docs.rs/embedded-hal-nb): polling-based, using the `nb` crate. + +The [`embedded-hal-bus`](https://docs.rs/embedded-hal-bus) crate provides utilities for sharing +SPI and I2C buses. + +Additionally, more domain-specific traits are available in separate crates: +- [`embedded-can`](https://docs.rs/embedded-can): Controller Area Network (CAN) + +## Design goals + +The HAL + +- Must *erase* device specific details. Neither register, register blocks or magic values should +appear in the API. + +- Must be generic *within* a device and *across* devices. The API to use a serial interface must +be the same regardless of whether the implementation uses the USART1 or UART4 peripheral of a +device or the UART0 peripheral of another device. + +- Where possible must *not* be tied to a specific asynchronous model. The API should be usable +in blocking mode, with the `futures` model, with an async/await model or with a callback model. +(cf. the [`nb`](https://docs.rs/nb) crate) + +- Must be minimal, and thus easy to implement and zero cost, yet highly composable. People that +want higher level abstraction should *prefer to use this HAL* rather than *re-implement* +register manipulation code. + +- Serve as a foundation for building an ecosystem of platform agnostic drivers. Here driver +means a library crate that lets a target platform interface an external device like a digital +sensor or a wireless transceiver. The advantage of this system is that by writing the driver as +a generic library on top of `embedded-hal` driver authors can support any number of target +platforms (e.g. Cortex-M microcontrollers, AVR microcontrollers, embedded Linux, etc.). The +advantage for application developers is that by adopting `embedded-hal` they can unlock all +these drivers for their platform. + +- Trait methods must be fallible so that they can be used in any possible situation. +Nevertheless, HAL implementations can additionally provide infallible versions of the same methods +if they can never fail in their platform. This way, generic code can use the fallible abstractions +provided here but platform-specific code can avoid fallibility-related boilerplate if possible. + +## Out of scope + +- Initialization and configuration stuff like "ensure this serial interface and that SPI +interface are not using the same pins". The HAL will focus on *doing I/O*. + +## Reference implementation + +The [`stm32f1xx-hal`] crate contains a reference implementation of this HAL. + +[`stm32f1xx-hal`]: https://crates.io/crates/stm32f1xx-hal + +## Platform agnostic drivers + +You can find platform agnostic drivers built on top of `embedded-hal` on crates.io by [searching +for the *embedded-hal* keyword](https://crates.io/keywords/embedded-hal). + +If you are writing a platform agnostic driver yourself you are highly encouraged to [add the +embedded-hal keyword](https://doc.rust-lang.org/cargo/reference/manifest.html#package-metadata) +to your crate before publishing it! +// ## Minimum Supported Rust Version (MSRV) @@ -25,8 +96,8 @@ See [here](../docs/msrv.md) for details on how the MSRV may be upgraded. Licensed under either of - Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or - http://www.apache.org/licenses/LICENSE-2.0) -- MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) + ) +- MIT license ([LICENSE-MIT](LICENSE-MIT) or ) at your option. diff --git a/embedded-hal/src/lib.rs b/embedded-hal/src/lib.rs index ac09042b3..c195bb2a1 100644 --- a/embedded-hal/src/lib.rs +++ b/embedded-hal/src/lib.rs @@ -1,79 +1,4 @@ -//! A Hardware Abstraction Layer (HAL) for embedded systems -//! -//! **NOTE** This HAL is still is active development. Expect the traits presented here to be -//! tweaked, split or be replaced wholesale before being stabilized, i.e. before hitting the 1.0.0 -//! release. -//! -//! **NOTE** If you want to use an alpha release of the 1.0.0 version, use an exact version -//! specifier in your `Cargo.toml` like: `embedded-hal = "=1.0.0-alpha.2"`. -//! -//! # Companion crates -//! -//! The main `embedded-hal` crate contains only blocking traits, where the operation is done -//! synchronously before returning. Check out the following crates, which contain versions -//! of the traits for other execution models: -//! -//! - [`embedded-hal-async`](https://docs.rs/embedded-hal-async): async/await-based. -//! - [`embedded-hal-nb`](https://docs.rs/embedded-hal-nb): polling-based, using the `nb` crate. -//! -//! The [`embedded-hal-bus`](https://docs.rs/embedded-hal-bus) crate provides utilities for sharing -//! SPI and I2C buses. -//! -//! Additionally, more domain-specific traits are available in separate crates: -//! - [`embedded-can`](https://docs.rs/embedded-can): Controller Area Network (CAN) -//! -//! # Design goals -//! -//! The HAL -//! -//! - Must *erase* device specific details. Neither register, register blocks or magic values should -//! appear in the API. -//! -//! - Must be generic *within* a device and *across* devices. The API to use a serial interface must -//! be the same regardless of whether the implementation uses the USART1 or UART4 peripheral of a -//! device or the UART0 peripheral of another device. -//! -//! - Where possible must *not* be tied to a specific asynchronous model. The API should be usable -//! in blocking mode, with the `futures` model, with an async/await model or with a callback model. -//! (cf. the [`nb`](https://docs.rs/nb) crate) -//! -//! - Must be minimal, and thus easy to implement and zero cost, yet highly composable. People that -//! want higher level abstraction should *prefer to use this HAL* rather than *re-implement* -//! register manipulation code. -//! -//! - Serve as a foundation for building an ecosystem of platform agnostic drivers. Here driver -//! means a library crate that lets a target platform interface an external device like a digital -//! sensor or a wireless transceiver. The advantage of this system is that by writing the driver as -//! a generic library on top of `embedded-hal` driver authors can support any number of target -//! platforms (e.g. Cortex-M microcontrollers, AVR microcontrollers, embedded Linux, etc.). The -//! advantage for application developers is that by adopting `embedded-hal` they can unlock all -//! these drivers for their platform. -//! -//! - Trait methods must be fallible so that they can be used in any possible situation. -//! Nevertheless, HAL implementations can additionally provide infallible versions of the same methods -//! if they can never fail in their platform. This way, generic code can use the fallible abstractions -//! provided here but platform-specific code can avoid fallibility-related boilerplate if possible. -//! -//! # Out of scope -//! -//! - Initialization and configuration stuff like "ensure this serial interface and that SPI -//! interface are not using the same pins". The HAL will focus on *doing I/O*. -//! -//! # Reference implementation -//! -//! The [`stm32f1xx-hal`] crate contains a reference implementation of this HAL. -//! -//! [`stm32f1xx-hal`]: https://crates.io/crates/stm32f1xx-hal -//! -//! # Platform agnostic drivers -//! -//! You can find platform agnostic drivers built on top of `embedded-hal` on crates.io by [searching -//! for the *embedded-hal* keyword](https://crates.io/keywords/embedded-hal). -//! -//! If you are writing a platform agnostic driver yourself you are highly encouraged to [add the -//! embedded-hal keyword](https://doc.rust-lang.org/cargo/reference/manifest.html#package-metadata) -//! to your crate before publishing it! - +#![doc = include_str!("../README.md")] #![warn(missing_docs)] #![no_std] From ebde20615071da4c3830e086853bf1ea11ee15f4 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sat, 15 Jul 2023 18:07:13 +0200 Subject: [PATCH 064/199] readme: add io crates. --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 3b8692783..02cee6050 100644 --- a/README.md +++ b/README.md @@ -34,6 +34,9 @@ The main `embedded-hal` project is not tied to a specific execution model like | [embedded-hal-nb](./embedded-hal-nb) | [![crates.io](https://img.shields.io/crates/v/embedded-hal-nb.svg)](https://crates.io/crates/embedded-hal-nb) | [![Documentation](https://docs.rs/embedded-hal-nb/badge.svg)](https://docs.rs/embedded-hal-nb) | Core traits, polling version using the `nb` crate | | [embedded-hal-bus](./embedded-hal-bus) | [![crates.io](https://img.shields.io/crates/v/embedded-hal-bus.svg)](https://crates.io/crates/embedded-hal-bus) | [![Documentation](https://docs.rs/embedded-hal-bus/badge.svg)](https://docs.rs/embedded-hal-bus) | Utilities for sharing SPI and I2C buses | | [embedded-can](./embedded-can) | [![crates.io](https://img.shields.io/crates/v/embedded-can.svg)](https://crates.io/crates/embedded-can) | [![Documentation](https://docs.rs/embedded-can/badge.svg)](https://docs.rs/embedded-can) | Controller Area Network (CAN) traits | +| [embedded-io](./embedded-io) | [![crates.io](https://img.shields.io/crates/v/embedded-io.svg)](https://crates.io/crates/embedded-io) | [![Documentation](https://docs.rs/embedded-io/badge.svg)](https://docs.rs/embedded-io) | I/O traits (read, write, seek, etc.), blocking and nonblocking version. | +| [embedded-io-async](./embedded-io-async) | [![crates.io](https://img.shields.io/crates/v/embedded-io-async.svg)](https://crates.io/crates/embedded-io-async) | [![Documentation](https://docs.rs/embedded-io-async/badge.svg)](https://docs.rs/embedded-io-async) | I/O traits, async version | +| [embedded-io-adapters](./embedded-io-adapters) | [![crates.io](https://img.shields.io/crates/v/embedded-io-adapters.svg)](https://crates.io/crates/embedded-io-adapters) | [![Documentation](https://docs.rs/embedded-io-adapters/badge.svg)](https://docs.rs/embedded-io-adapters) | Adapters between the [`embedded-io`](https://crates.io/crates/embedded-io) and [`embedded-io-async`](https://crates.io/crates/embedded-io-async) traits and other IO traits (`std`, `tokio`, `futures`...) | ## Releases From baccfdbc5aae5ed414060e77d28d2229f9b5c19d Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sat, 15 Jul 2023 18:07:26 +0200 Subject: [PATCH 065/199] readme: remove stm32f1xx-hal as reference impl. It doesn't implement embedded-hal 1.0. --- embedded-hal/README.md | 6 ------ 1 file changed, 6 deletions(-) diff --git a/embedded-hal/README.md b/embedded-hal/README.md index 97aff726e..b496b9ae9 100644 --- a/embedded-hal/README.md +++ b/embedded-hal/README.md @@ -68,12 +68,6 @@ provided here but platform-specific code can avoid fallibility-related boilerpla - Initialization and configuration stuff like "ensure this serial interface and that SPI interface are not using the same pins". The HAL will focus on *doing I/O*. -## Reference implementation - -The [`stm32f1xx-hal`] crate contains a reference implementation of this HAL. - -[`stm32f1xx-hal`]: https://crates.io/crates/stm32f1xx-hal - ## Platform agnostic drivers You can find platform agnostic drivers built on top of `embedded-hal` on crates.io by [searching From 994c6c92be9f05c63cafd730bf4341b871ebbcf1 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sat, 15 Jul 2023 18:08:04 +0200 Subject: [PATCH 066/199] readme: document that serial should use embedded-io. --- embedded-hal-async/README.md | 7 +++++++ embedded-hal/README.md | 7 +++++++ 2 files changed, 14 insertions(+) diff --git a/embedded-hal-async/README.md b/embedded-hal-async/README.md index a0527605a..d22d658d8 100644 --- a/embedded-hal-async/README.md +++ b/embedded-hal-async/README.md @@ -10,6 +10,13 @@ This crate contains asynchronous versions of the [`embedded-hal`](https://crates This project is developed and maintained by the [HAL team](https://github.com/rust-embedded/wg#the-hal-team). +## Serial/UART traits + +There is no serial traits in `embedded-hal-async`. Instead, use [`embedded-io-async`](https://crates.io/crates/embedded-io). +A serial port is essentially a byte-oriented stream, and that's what `embedded-io-async` models. Sharing the traits +with all byte streams has some advantages. For example, it allows generic code providing a command-line interface +or a console to operate either on hardware serial ports or on virtual ones like Telnet or USB CDC-ACM. + ## Minimum Supported Rust Version (MSRV) This crate requires Rust nightly newer than `nightly-2022-11-22`, due to requiring support for diff --git a/embedded-hal/README.md b/embedded-hal/README.md index b496b9ae9..3f3918bb0 100644 --- a/embedded-hal/README.md +++ b/embedded-hal/README.md @@ -78,6 +78,13 @@ embedded-hal keyword](https://doc.rust-lang.org/cargo/reference/manifest.html#pa to your crate before publishing it! // +## Serial/UART traits + +There is no serial traits in `embedded-hal`. Instead, use [`embedded-io`](https://crates.io/crates/embedded-io). +A serial port is essentially a byte-oriented stream, and that's what `embedded-io` models. Sharing the traits +with all byte streams has some advantages. For example, it allows generic code providing a command-line interface +or a console to operate either on hardware serial ports or on virtual ones like Telnet or USB CDC-ACM. + ## Minimum Supported Rust Version (MSRV) This crate is guaranteed to compile on stable Rust 1.60 and up. It *might* From 1567dee785ddc3710369dd70e30c28762b1bf496 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sat, 15 Jul 2023 13:44:43 +0200 Subject: [PATCH 067/199] bus: add doc(cfg). --- embedded-hal-bus/Cargo.toml | 4 ++++ embedded-hal-bus/src/i2c/mutex.rs | 1 + embedded-hal-bus/src/lib.rs | 1 + embedded-hal-bus/src/spi/mutex.rs | 1 + 4 files changed, 7 insertions(+) diff --git a/embedded-hal-bus/Cargo.toml b/embedded-hal-bus/Cargo.toml index 5e6f40995..9ffa63d32 100644 --- a/embedded-hal-bus/Cargo.toml +++ b/embedded-hal-bus/Cargo.toml @@ -19,3 +19,7 @@ std = [] [dependencies] embedded-hal = { version = "=1.0.0-alpha.11", path = "../embedded-hal" } critical-section = { version = "1.0" } + +[package.metadata.docs.rs] +features = ["std"] +rustdoc-args = ["--cfg", "docsrs"] diff --git a/embedded-hal-bus/src/i2c/mutex.rs b/embedded-hal-bus/src/i2c/mutex.rs index 56f922c8c..ff9da7b2a 100644 --- a/embedded-hal-bus/src/i2c/mutex.rs +++ b/embedded-hal-bus/src/i2c/mutex.rs @@ -6,6 +6,7 @@ use std::sync::Mutex; /// Sharing is implemented with an `std` [`Mutex`](std::sync::Mutex). It allows a single bus across multiple threads, /// with finer-grained locking than [`CriticalSectionDevice`](super::CriticalSectionDevice). The downside is that /// it is only available in `std` targets. +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] pub struct MutexDevice<'a, T> { bus: &'a Mutex, } diff --git a/embedded-hal-bus/src/lib.rs b/embedded-hal-bus/src/lib.rs index ca76c09c8..dabff2b25 100644 --- a/embedded-hal-bus/src/lib.rs +++ b/embedded-hal-bus/src/lib.rs @@ -1,6 +1,7 @@ #![doc = include_str!("../README.md")] #![warn(missing_docs)] #![cfg_attr(not(feature = "std"), no_std)] +#![cfg_attr(docsrs, feature(doc_cfg))] pub mod i2c; pub mod spi; diff --git a/embedded-hal-bus/src/spi/mutex.rs b/embedded-hal-bus/src/spi/mutex.rs index bdf329217..7030ca02c 100644 --- a/embedded-hal-bus/src/spi/mutex.rs +++ b/embedded-hal-bus/src/spi/mutex.rs @@ -13,6 +13,7 @@ use super::DeviceError; /// Sharing is implemented with a `std` [`Mutex`](std::sync::Mutex). It allows a single bus across multiple threads, /// with finer-grained locking than [`CriticalSectionDevice`](super::CriticalSectionDevice). The downside is /// it is only available in `std` targets. +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] pub struct MutexDevice<'a, BUS, CS, D> { bus: &'a Mutex, cs: CS, From b2c2971592ec93d8e5bd72f7c566496ff7f63250 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sat, 15 Jul 2023 15:57:06 +0200 Subject: [PATCH 068/199] spi: move async ExclusiveDevice to embedded-hal-bus. --- embedded-hal-async/src/spi.rs | 152 ------------------- embedded-hal-bus/Cargo.toml | 4 +- embedded-hal-bus/src/asynch/mod.rs | 3 + embedded-hal-bus/src/asynch/spi/exclusive.rs | 147 ++++++++++++++++++ embedded-hal-bus/src/asynch/spi/mod.rs | 16 ++ embedded-hal-bus/src/lib.rs | 5 + 6 files changed, 174 insertions(+), 153 deletions(-) create mode 100644 embedded-hal-bus/src/asynch/mod.rs create mode 100644 embedded-hal-bus/src/asynch/spi/exclusive.rs create mode 100644 embedded-hal-bus/src/asynch/spi/mod.rs diff --git a/embedded-hal-async/src/spi.rs b/embedded-hal-async/src/spi.rs index 1e852e34b..e9548359f 100644 --- a/embedded-hal-async/src/spi.rs +++ b/embedded-hal-async/src/spi.rs @@ -1,15 +1,9 @@ //! SPI master mode traits. -use core::fmt::Debug; - -use embedded_hal::digital::OutputPin; -use embedded_hal::spi as blocking; pub use embedded_hal::spi::{ Error, ErrorKind, ErrorType, Mode, Operation, Phase, Polarity, MODE_0, MODE_1, MODE_2, MODE_3, }; -use crate::delay::DelayUs; - /// SPI device trait /// /// `SpiDevice` represents ownership over a single SPI device on a (possibly shared) bus, selected @@ -170,149 +164,3 @@ impl + ?Sized, Word: 'static + Copy> SpiBus for &mut T { T::flush(self).await } } - -/// Error type for [`ExclusiveDevice`] operations. -#[derive(Copy, Clone, Eq, PartialEq, Debug)] -pub enum ExclusiveDeviceError { - /// An inner SPI bus operation failed - Spi(BUS), - /// Asserting or deasserting CS failed - Cs(CS), -} - -impl Error for ExclusiveDeviceError -where - BUS: Error + Debug, - CS: Debug, -{ - fn kind(&self) -> ErrorKind { - match self { - Self::Spi(e) => e.kind(), - Self::Cs(_) => ErrorKind::ChipSelectFault, - } - } -} - -/// [`SpiDevice`] implementation with exclusive access to the bus (not shared). -/// -/// This is the most straightforward way of obtaining an [`SpiDevice`] from an [`SpiBus`], -/// ideal for when no sharing is required (only one SPI device is present on the bus). -pub struct ExclusiveDevice { - bus: BUS, - cs: CS, - delay: D, -} - -impl ExclusiveDevice { - /// Create a new ExclusiveDevice - pub fn new(bus: BUS, cs: CS, delay: D) -> Self { - Self { bus, cs, delay } - } - - /// Returns a reference to the underlying bus object. - pub fn bus(&self) -> &BUS { - &self.bus - } - - /// Returns a mutable reference to the underlying bus object. - pub fn bus_mut(&mut self) -> &mut BUS { - &mut self.bus - } -} - -impl ErrorType for ExclusiveDevice -where - BUS: ErrorType, - CS: OutputPin, -{ - type Error = ExclusiveDeviceError; -} - -impl blocking::SpiDevice for ExclusiveDevice -where - BUS: blocking::SpiBus, - CS: OutputPin, - D: embedded_hal::delay::DelayUs, -{ - fn transaction(&mut self, operations: &mut [Operation<'_, Word>]) -> Result<(), Self::Error> { - self.cs.set_low().map_err(ExclusiveDeviceError::Cs)?; - - let op_res = 'ops: { - for op in operations { - let res = match op { - Operation::Read(buf) => self.bus.read(buf), - Operation::Write(buf) => self.bus.write(buf), - Operation::Transfer(read, write) => self.bus.transfer(read, write), - Operation::TransferInPlace(buf) => self.bus.transfer_in_place(buf), - Operation::DelayUs(us) => match self.bus.flush() { - Err(e) => Err(e), - Ok(()) => { - self.delay.delay_us(*us); - Ok(()) - } - }, - }; - if let Err(e) = res { - break 'ops Err(e); - } - } - Ok(()) - }; - - // On failure, it's important to still flush and deassert CS. - let flush_res = self.bus.flush(); - let cs_res = self.cs.set_high(); - - op_res.map_err(ExclusiveDeviceError::Spi)?; - flush_res.map_err(ExclusiveDeviceError::Spi)?; - cs_res.map_err(ExclusiveDeviceError::Cs)?; - - Ok(()) - } -} - -impl SpiDevice for ExclusiveDevice -where - BUS: SpiBus, - CS: OutputPin, - D: DelayUs, -{ - async fn transaction( - &mut self, - operations: &mut [Operation<'_, Word>], - ) -> Result<(), Self::Error> { - self.cs.set_low().map_err(ExclusiveDeviceError::Cs)?; - - let op_res = 'ops: { - for op in operations { - let res = match op { - Operation::Read(buf) => self.bus.read(buf).await, - Operation::Write(buf) => self.bus.write(buf).await, - Operation::Transfer(read, write) => self.bus.transfer(read, write).await, - Operation::TransferInPlace(buf) => self.bus.transfer_in_place(buf).await, - Operation::DelayUs(us) => match self.bus.flush().await { - Err(e) => Err(e), - Ok(()) => { - self.delay.delay_us(*us).await; - Ok(()) - } - }, - }; - if let Err(e) = res { - break 'ops Err(e); - } - } - Ok(()) - }; - - // On failure, it's important to still flush and deassert CS. - let flush_res = self.bus.flush().await; - let cs_res = self.cs.set_high(); - - op_res.map_err(ExclusiveDeviceError::Spi)?; - flush_res.map_err(ExclusiveDeviceError::Spi)?; - cs_res.map_err(ExclusiveDeviceError::Cs)?; - - Ok(()) - } -} diff --git a/embedded-hal-bus/Cargo.toml b/embedded-hal-bus/Cargo.toml index 9ffa63d32..66477fdfa 100644 --- a/embedded-hal-bus/Cargo.toml +++ b/embedded-hal-bus/Cargo.toml @@ -15,11 +15,13 @@ version = "0.1.0-alpha.3" [features] std = [] +async = ["dep:embedded-hal-async"] [dependencies] embedded-hal = { version = "=1.0.0-alpha.11", path = "../embedded-hal" } +embedded-hal-async = { version = "=0.2.0-alpha.2", path = "../embedded-hal-async", optional = true } critical-section = { version = "1.0" } [package.metadata.docs.rs] -features = ["std"] +features = ["std", "async"] rustdoc-args = ["--cfg", "docsrs"] diff --git a/embedded-hal-bus/src/asynch/mod.rs b/embedded-hal-bus/src/asynch/mod.rs new file mode 100644 index 000000000..a9957d654 --- /dev/null +++ b/embedded-hal-bus/src/asynch/mod.rs @@ -0,0 +1,3 @@ +//! Bus sharing utilities for [`embedded-hal-async`] + +pub mod spi; diff --git a/embedded-hal-bus/src/asynch/spi/exclusive.rs b/embedded-hal-bus/src/asynch/spi/exclusive.rs new file mode 100644 index 000000000..4c80a698e --- /dev/null +++ b/embedded-hal-bus/src/asynch/spi/exclusive.rs @@ -0,0 +1,147 @@ +use embedded_hal::{digital::OutputPin, spi as blocking}; +use embedded_hal_async::{ + delay::DelayUs, + spi::{ErrorType, Operation, SpiBus, SpiDevice}, +}; + +pub use crate::spi::DeviceError; + +/// [`SpiDevice`] implementation with exclusive access to the bus (not shared). +/// +/// This is the most straightforward way of obtaining an [`SpiDevice`] from an [`SpiBus`], +/// ideal for when no sharing is required (only one SPI device is present on the bus). +pub struct ExclusiveDevice { + bus: BUS, + cs: CS, + delay: D, +} + +impl ExclusiveDevice { + /// Create a new ExclusiveDevice + pub fn new(bus: BUS, cs: CS, delay: D) -> Self { + Self { bus, cs, delay } + } + + /// Returns a reference to the underlying bus object. + pub fn bus(&self) -> &BUS { + &self.bus + } + + /// Returns a mutable reference to the underlying bus object. + pub fn bus_mut(&mut self) -> &mut BUS { + &mut self.bus + } +} + +impl ExclusiveDevice { + /// Create a new ExclusiveDevice without support for in-transaction delays. + /// + /// # Panics + /// + /// The returned device will panic if you try to execute a transaction + /// that contains any operations of type `Operation::DelayUs`. + pub fn new_no_delay(bus: BUS, cs: CS) -> Self { + Self { + bus, + cs, + delay: super::NoDelay, + } + } +} + +impl ErrorType for ExclusiveDevice +where + BUS: ErrorType, + CS: OutputPin, +{ + type Error = DeviceError; +} + +impl blocking::SpiDevice for ExclusiveDevice +where + BUS: blocking::SpiBus, + CS: OutputPin, + D: embedded_hal::delay::DelayUs, +{ + fn transaction(&mut self, operations: &mut [Operation<'_, Word>]) -> Result<(), Self::Error> { + self.cs.set_low().map_err(DeviceError::Cs)?; + + let op_res = 'ops: { + for op in operations { + let res = match op { + Operation::Read(buf) => self.bus.read(buf), + Operation::Write(buf) => self.bus.write(buf), + Operation::Transfer(read, write) => self.bus.transfer(read, write), + Operation::TransferInPlace(buf) => self.bus.transfer_in_place(buf), + Operation::DelayUs(us) => match self.bus.flush() { + Err(e) => Err(e), + Ok(()) => { + self.delay.delay_us(*us); + Ok(()) + } + }, + }; + if let Err(e) = res { + break 'ops Err(e); + } + } + Ok(()) + }; + + // On failure, it's important to still flush and deassert CS. + let flush_res = self.bus.flush(); + let cs_res = self.cs.set_high(); + + op_res.map_err(DeviceError::Spi)?; + flush_res.map_err(DeviceError::Spi)?; + cs_res.map_err(DeviceError::Cs)?; + + Ok(()) + } +} + +impl SpiDevice for ExclusiveDevice +where + BUS: SpiBus, + CS: OutputPin, + D: DelayUs, +{ + async fn transaction( + &mut self, + operations: &mut [Operation<'_, Word>], + ) -> Result<(), Self::Error> { + self.cs.set_low().map_err(DeviceError::Cs)?; + + let op_res = 'ops: { + for op in operations { + let res = match op { + Operation::Read(buf) => self.bus.read(buf).await, + Operation::Write(buf) => self.bus.write(buf).await, + Operation::Transfer(read, write) => self.bus.transfer(read, write).await, + Operation::TransferInPlace(buf) => self.bus.transfer_in_place(buf).await, + Operation::DelayUs(us) => match self.bus.flush().await { + Err(e) => Err(e), + Ok(()) => { + self.delay.delay_us(*us).await; + Ok(()) + } + }, + }; + if let Err(e) = res { + break 'ops Err(e); + } + } + Ok(()) + }; + + // On failure, it's important to still flush and deassert CS. + let flush_res = self.bus.flush().await; + let cs_res = self.cs.set_high(); + + op_res.map_err(DeviceError::Spi)?; + flush_res.map_err(DeviceError::Spi)?; + cs_res.map_err(DeviceError::Cs)?; + + Ok(()) + } +} diff --git a/embedded-hal-bus/src/asynch/spi/mod.rs b/embedded-hal-bus/src/asynch/spi/mod.rs new file mode 100644 index 000000000..77731bf04 --- /dev/null +++ b/embedded-hal-bus/src/asynch/spi/mod.rs @@ -0,0 +1,16 @@ +//! `SpiDevice` implementations. + +mod exclusive; +pub use exclusive::*; + +pub use crate::spi::NoDelay; + +impl embedded_hal_async::delay::DelayUs for NoDelay { + async fn delay_ms(&mut self, _ms: u32) { + panic!("You've tried to execute a SPI transaction containing a `Operation::Delay` in a `SpiDevice` created with `new_no_delay()`. Create it with `new()` instead, passing a `DelayUs` implementation.") + } + + async fn delay_us(&mut self, _us: u32) { + panic!("You've tried to execute a SPI transaction containing a `Operation::Delay` in a `SpiDevice` created with `new_no_delay()`. Create it with `new()` instead, passing a `DelayUs` implementation.") + } +} diff --git a/embedded-hal-bus/src/lib.rs b/embedded-hal-bus/src/lib.rs index dabff2b25..812ce1183 100644 --- a/embedded-hal-bus/src/lib.rs +++ b/embedded-hal-bus/src/lib.rs @@ -2,6 +2,11 @@ #![warn(missing_docs)] #![cfg_attr(not(feature = "std"), no_std)] #![cfg_attr(docsrs, feature(doc_cfg))] +#![cfg_attr(feature = "async", feature(async_fn_in_trait, impl_trait_projections))] pub mod i2c; pub mod spi; + +#[cfg(feature = "async")] +#[cfg_attr(docsrs, doc(cfg(feature = "async")))] +pub mod asynch; From 5f8f7f932ea077ad082617815093f7ebd536a53c Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sat, 15 Jul 2023 16:00:24 +0200 Subject: [PATCH 069/199] ci: add rustdoc check with deny warnings. Catches things like broken links. --- .github/workflows/rustdoc.yml | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 .github/workflows/rustdoc.yml diff --git a/.github/workflows/rustdoc.yml b/.github/workflows/rustdoc.yml new file mode 100644 index 000000000..e0202bac8 --- /dev/null +++ b/.github/workflows/rustdoc.yml @@ -0,0 +1,17 @@ +on: + push: # Run CI for all branches except GitHub merge queue tmp branches + branches-ignore: + - "gh-readonly-queue/**" + pull_request: # Run CI for PRs on any branch + merge_group: # Run CI for the GitHub merge queue + +name: Rustdoc check +jobs: + clippy: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: dtolnay/rust-toolchain@master + with: + toolchain: nightly-2023-07-03 + - run: RUSTDOCFLAGS="--deny=warnings --cfg=docsrs" cargo doc --all-features \ No newline at end of file From 7921e820fbb18e5eda725405eb9b75a41c8fb00b Mon Sep 17 00:00:00 2001 From: Diego Barrios Romero Date: Mon, 17 Jul 2023 12:50:26 +0200 Subject: [PATCH 070/199] Fix typo --- embedded-hal/README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/embedded-hal/README.md b/embedded-hal/README.md index 3f3918bb0..f29a8c599 100644 --- a/embedded-hal/README.md +++ b/embedded-hal/README.md @@ -76,7 +76,6 @@ for the *embedded-hal* keyword](https://crates.io/keywords/embedded-hal). If you are writing a platform agnostic driver yourself you are highly encouraged to [add the embedded-hal keyword](https://doc.rust-lang.org/cargo/reference/manifest.html#package-metadata) to your crate before publishing it! -// ## Serial/UART traits From 8d03c73d8e9b88ad55564f02efc9df7c41030b06 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1niel=20Buga?= Date: Tue, 18 Jul 2023 22:48:44 +0200 Subject: [PATCH 071/199] Merge spi ExclusiveDevice implementations --- embedded-hal-bus/src/asynch/mod.rs | 3 - embedded-hal-bus/src/asynch/spi/exclusive.rs | 147 ------------------- embedded-hal-bus/src/asynch/spi/mod.rs | 16 -- embedded-hal-bus/src/lib.rs | 4 - embedded-hal-bus/src/spi/exclusive.rs | 63 ++++++++ 5 files changed, 63 insertions(+), 170 deletions(-) delete mode 100644 embedded-hal-bus/src/asynch/mod.rs delete mode 100644 embedded-hal-bus/src/asynch/spi/exclusive.rs delete mode 100644 embedded-hal-bus/src/asynch/spi/mod.rs diff --git a/embedded-hal-bus/src/asynch/mod.rs b/embedded-hal-bus/src/asynch/mod.rs deleted file mode 100644 index a9957d654..000000000 --- a/embedded-hal-bus/src/asynch/mod.rs +++ /dev/null @@ -1,3 +0,0 @@ -//! Bus sharing utilities for [`embedded-hal-async`] - -pub mod spi; diff --git a/embedded-hal-bus/src/asynch/spi/exclusive.rs b/embedded-hal-bus/src/asynch/spi/exclusive.rs deleted file mode 100644 index 4c80a698e..000000000 --- a/embedded-hal-bus/src/asynch/spi/exclusive.rs +++ /dev/null @@ -1,147 +0,0 @@ -use embedded_hal::{digital::OutputPin, spi as blocking}; -use embedded_hal_async::{ - delay::DelayUs, - spi::{ErrorType, Operation, SpiBus, SpiDevice}, -}; - -pub use crate::spi::DeviceError; - -/// [`SpiDevice`] implementation with exclusive access to the bus (not shared). -/// -/// This is the most straightforward way of obtaining an [`SpiDevice`] from an [`SpiBus`], -/// ideal for when no sharing is required (only one SPI device is present on the bus). -pub struct ExclusiveDevice { - bus: BUS, - cs: CS, - delay: D, -} - -impl ExclusiveDevice { - /// Create a new ExclusiveDevice - pub fn new(bus: BUS, cs: CS, delay: D) -> Self { - Self { bus, cs, delay } - } - - /// Returns a reference to the underlying bus object. - pub fn bus(&self) -> &BUS { - &self.bus - } - - /// Returns a mutable reference to the underlying bus object. - pub fn bus_mut(&mut self) -> &mut BUS { - &mut self.bus - } -} - -impl ExclusiveDevice { - /// Create a new ExclusiveDevice without support for in-transaction delays. - /// - /// # Panics - /// - /// The returned device will panic if you try to execute a transaction - /// that contains any operations of type `Operation::DelayUs`. - pub fn new_no_delay(bus: BUS, cs: CS) -> Self { - Self { - bus, - cs, - delay: super::NoDelay, - } - } -} - -impl ErrorType for ExclusiveDevice -where - BUS: ErrorType, - CS: OutputPin, -{ - type Error = DeviceError; -} - -impl blocking::SpiDevice for ExclusiveDevice -where - BUS: blocking::SpiBus, - CS: OutputPin, - D: embedded_hal::delay::DelayUs, -{ - fn transaction(&mut self, operations: &mut [Operation<'_, Word>]) -> Result<(), Self::Error> { - self.cs.set_low().map_err(DeviceError::Cs)?; - - let op_res = 'ops: { - for op in operations { - let res = match op { - Operation::Read(buf) => self.bus.read(buf), - Operation::Write(buf) => self.bus.write(buf), - Operation::Transfer(read, write) => self.bus.transfer(read, write), - Operation::TransferInPlace(buf) => self.bus.transfer_in_place(buf), - Operation::DelayUs(us) => match self.bus.flush() { - Err(e) => Err(e), - Ok(()) => { - self.delay.delay_us(*us); - Ok(()) - } - }, - }; - if let Err(e) = res { - break 'ops Err(e); - } - } - Ok(()) - }; - - // On failure, it's important to still flush and deassert CS. - let flush_res = self.bus.flush(); - let cs_res = self.cs.set_high(); - - op_res.map_err(DeviceError::Spi)?; - flush_res.map_err(DeviceError::Spi)?; - cs_res.map_err(DeviceError::Cs)?; - - Ok(()) - } -} - -impl SpiDevice for ExclusiveDevice -where - BUS: SpiBus, - CS: OutputPin, - D: DelayUs, -{ - async fn transaction( - &mut self, - operations: &mut [Operation<'_, Word>], - ) -> Result<(), Self::Error> { - self.cs.set_low().map_err(DeviceError::Cs)?; - - let op_res = 'ops: { - for op in operations { - let res = match op { - Operation::Read(buf) => self.bus.read(buf).await, - Operation::Write(buf) => self.bus.write(buf).await, - Operation::Transfer(read, write) => self.bus.transfer(read, write).await, - Operation::TransferInPlace(buf) => self.bus.transfer_in_place(buf).await, - Operation::DelayUs(us) => match self.bus.flush().await { - Err(e) => Err(e), - Ok(()) => { - self.delay.delay_us(*us).await; - Ok(()) - } - }, - }; - if let Err(e) = res { - break 'ops Err(e); - } - } - Ok(()) - }; - - // On failure, it's important to still flush and deassert CS. - let flush_res = self.bus.flush().await; - let cs_res = self.cs.set_high(); - - op_res.map_err(DeviceError::Spi)?; - flush_res.map_err(DeviceError::Spi)?; - cs_res.map_err(DeviceError::Cs)?; - - Ok(()) - } -} diff --git a/embedded-hal-bus/src/asynch/spi/mod.rs b/embedded-hal-bus/src/asynch/spi/mod.rs deleted file mode 100644 index 77731bf04..000000000 --- a/embedded-hal-bus/src/asynch/spi/mod.rs +++ /dev/null @@ -1,16 +0,0 @@ -//! `SpiDevice` implementations. - -mod exclusive; -pub use exclusive::*; - -pub use crate::spi::NoDelay; - -impl embedded_hal_async::delay::DelayUs for NoDelay { - async fn delay_ms(&mut self, _ms: u32) { - panic!("You've tried to execute a SPI transaction containing a `Operation::Delay` in a `SpiDevice` created with `new_no_delay()`. Create it with `new()` instead, passing a `DelayUs` implementation.") - } - - async fn delay_us(&mut self, _us: u32) { - panic!("You've tried to execute a SPI transaction containing a `Operation::Delay` in a `SpiDevice` created with `new_no_delay()`. Create it with `new()` instead, passing a `DelayUs` implementation.") - } -} diff --git a/embedded-hal-bus/src/lib.rs b/embedded-hal-bus/src/lib.rs index 812ce1183..cdb668ae3 100644 --- a/embedded-hal-bus/src/lib.rs +++ b/embedded-hal-bus/src/lib.rs @@ -6,7 +6,3 @@ pub mod i2c; pub mod spi; - -#[cfg(feature = "async")] -#[cfg_attr(docsrs, doc(cfg(feature = "async")))] -pub mod asynch; diff --git a/embedded-hal-bus/src/spi/exclusive.rs b/embedded-hal-bus/src/spi/exclusive.rs index cbec194dc..da2381945 100644 --- a/embedded-hal-bus/src/spi/exclusive.rs +++ b/embedded-hal-bus/src/spi/exclusive.rs @@ -3,6 +3,11 @@ use embedded_hal::delay::DelayUs; use embedded_hal::digital::OutputPin; use embedded_hal::spi::{ErrorType, Operation, SpiBus, SpiDevice}; +#[cfg(feature = "async")] +use embedded_hal_async::{ + delay::DelayUs as AsyncDelayUs, + spi::{SpiBus as AsyncSpiBus, SpiDevice as AsyncSpiDevice}, +}; use super::DeviceError; @@ -21,6 +26,16 @@ impl ExclusiveDevice { pub fn new(bus: BUS, cs: CS, delay: D) -> Self { Self { bus, cs, delay } } + + /// Returns a reference to the underlying bus object. + pub fn bus(&self) -> &BUS { + &self.bus + } + + /// Returns a mutable reference to the underlying bus object. + pub fn bus_mut(&mut self) -> &mut BUS { + &mut self.bus + } } impl ExclusiveDevice { @@ -79,3 +94,51 @@ where Ok(()) } } + +#[cfg(feature = "async")] +#[cfg_attr(docsrs, doc(cfg(feature = "async")))] +impl AsyncSpiDevice for ExclusiveDevice +where + BUS: AsyncSpiBus, + CS: OutputPin, + D: AsyncDelayUs, +{ + async fn transaction( + &mut self, + operations: &mut [Operation<'_, Word>], + ) -> Result<(), Self::Error> { + self.cs.set_low().map_err(DeviceError::Cs)?; + + let op_res = 'ops: { + for op in operations { + let res = match op { + Operation::Read(buf) => self.bus.read(buf).await, + Operation::Write(buf) => self.bus.write(buf).await, + Operation::Transfer(read, write) => self.bus.transfer(read, write).await, + Operation::TransferInPlace(buf) => self.bus.transfer_in_place(buf).await, + Operation::DelayUs(us) => match self.bus.flush().await { + Err(e) => Err(e), + Ok(()) => { + self.delay.delay_us(*us).await; + Ok(()) + } + }, + }; + if let Err(e) = res { + break 'ops Err(e); + } + } + Ok(()) + }; + + // On failure, it's important to still flush and deassert CS. + let flush_res = self.bus.flush().await; + let cs_res = self.cs.set_high(); + + op_res.map_err(DeviceError::Spi)?; + flush_res.map_err(DeviceError::Spi)?; + cs_res.map_err(DeviceError::Cs)?; + + Ok(()) + } +} From ff94c7d109446c1dd17f77ce28f729a06126934b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1niel=20Buga?= Date: Tue, 18 Jul 2023 22:53:43 +0200 Subject: [PATCH 072/199] Implement async DelayUs for NoDelay --- embedded-hal-bus/src/spi/mod.rs | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/embedded-hal-bus/src/spi/mod.rs b/embedded-hal-bus/src/spi/mod.rs index 10f74e8d6..498dd1414 100644 --- a/embedded-hal-bus/src/spi/mod.rs +++ b/embedded-hal-bus/src/spi/mod.rs @@ -39,8 +39,25 @@ where /// Dummy `DelayUs` implementation that panics on use. pub struct NoDelay; +#[cold] +fn no_delay_panic() { + panic!("You've tried to execute a SPI transaction containing a `Operation::Delay` in a `SpiDevice` created with `new_no_delay()`. Create it with `new()` instead, passing a `DelayUs` implementation."); +} + impl embedded_hal::delay::DelayUs for NoDelay { fn delay_us(&mut self, _us: u32) { - panic!("You've tried to execute a SPI transaction containing a `Operation::Delay` in a `SpiDevice` created with `new_no_delay()`. Create it with `new()` instead, passing a `DelayUs` implementation.") + no_delay_panic(); + } +} + +#[cfg(feature = "async")] +#[cfg_attr(docsrs, doc(cfg(feature = "async")))] +impl embedded_hal_async::delay::DelayUs for NoDelay { + async fn delay_us(&mut self, _us: u32) { + no_delay_panic(); + } + + async fn delay_ms(&mut self, _us: u32) { + no_delay_panic(); } } From 428c6a14d14e5328a5af5d265b5bc1d4178b998b Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sun, 6 Aug 2023 15:30:57 +0200 Subject: [PATCH 073/199] io/adapters: derive Clone for ToStd. --- embedded-io-adapters/src/std.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/embedded-io-adapters/src/std.rs b/embedded-io-adapters/src/std.rs index 63978583d..ac6a606d3 100644 --- a/embedded-io-adapters/src/std.rs +++ b/embedded-io-adapters/src/std.rs @@ -66,6 +66,7 @@ impl embedded_io::Seek for FromStd { } /// Adapter to `std::io` traits. +#[derive(Clone)] pub struct ToStd { inner: T, } From 35b0dc33989f8edcf6208ee394562b05a85d69b0 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sun, 6 Aug 2023 15:32:54 +0200 Subject: [PATCH 074/199] io: add defmt support. --- embedded-io-async/Cargo.toml | 2 ++ embedded-io/Cargo.toml | 3 +++ embedded-io/src/lib.rs | 13 +++++++++++-- 3 files changed, 16 insertions(+), 2 deletions(-) diff --git a/embedded-io-async/Cargo.toml b/embedded-io-async/Cargo.toml index e8f19af39..011a65589 100644 --- a/embedded-io-async/Cargo.toml +++ b/embedded-io-async/Cargo.toml @@ -14,9 +14,11 @@ categories = [ [features] std = ["alloc", "embedded-io/std"] alloc = ["embedded-io/alloc"] +defmt-03 = ["dep:defmt-03", "embedded-io/defmt-03"] [dependencies] embedded-io = { version = "0.5", path = "../embedded-io" } +defmt-03 = { package = "defmt", version = "0.3", optional = true } [package.metadata.docs.rs] features = ["std"] diff --git a/embedded-io/Cargo.toml b/embedded-io/Cargo.toml index d1f82254f..a7ca732f3 100644 --- a/embedded-io/Cargo.toml +++ b/embedded-io/Cargo.toml @@ -15,6 +15,9 @@ categories = [ std = ["alloc"] alloc = [] +[dependencies] +defmt-03 = { package = "defmt", version = "0.3", optional = true } + [package.metadata.docs.rs] features = ["std"] rustdoc-args = ["--cfg", "docsrs"] diff --git a/embedded-io/src/lib.rs b/embedded-io/src/lib.rs index f6a8aa288..f798b5b6e 100644 --- a/embedded-io/src/lib.rs +++ b/embedded-io/src/lib.rs @@ -5,6 +5,10 @@ use core::fmt; +// needed to prevent defmt macros from breaking, since they emit code that does `defmt::blahblah`. +#[cfg(feature = "defmt-03")] +use defmt_03 as defmt; + #[cfg(feature = "alloc")] extern crate alloc; @@ -14,6 +18,7 @@ mod impls; /// /// This is the `embedded-io` equivalent of [`std::io::SeekFrom`]. #[derive(Debug, Copy, Clone, Eq, PartialEq)] +#[cfg_attr(feature = "defmt-03", derive(defmt::Format))] pub enum SeekFrom { /// Sets the offset to the provided number of bytes. Start(u64), @@ -47,8 +52,6 @@ impl From for SeekFrom { } } -#[derive(Debug, Copy, Clone, Eq, PartialEq)] -#[non_exhaustive] /// Possible kinds of errors. /// /// This list is intended to grow over time and it is not recommended to @@ -59,6 +62,9 @@ impl From for SeekFrom { /// /// - `WouldBlock` is removed, since `embedded-io` traits are always blocking. See the [crate-level documentation](crate) for details. /// - `WriteZero` is removed, since it is a separate variant in [`WriteAllError`] and [`WriteFmtError`]. +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +#[cfg_attr(feature = "defmt-03", derive(defmt::Format))] +#[non_exhaustive] pub enum ErrorKind { /// Unspecified error kind. Other, @@ -214,6 +220,7 @@ impl ErrorType for &mut T { /// Error returned by [`Read::read_exact`] #[derive(Debug, Copy, Clone, Eq, PartialEq)] +#[cfg_attr(feature = "defmt-03", derive(defmt::Format))] pub enum ReadExactError { /// An EOF error was encountered before reading the exact amount of requested bytes. UnexpectedEof, @@ -266,6 +273,7 @@ impl std::error::Error for ReadExactError {} /// Error returned by [`Write::write_fmt`] #[derive(Debug, Copy, Clone, Eq, PartialEq)] +#[cfg_attr(feature = "defmt-03", derive(defmt::Format))] pub enum WriteFmtError { /// [`Write::write`] wrote zero bytes WriteZero, @@ -293,6 +301,7 @@ impl std::error::Error for WriteFmtError {} /// Error returned by [`Write::write_all`] #[derive(Debug, Copy, Clone, Eq, PartialEq)] +#[cfg_attr(feature = "defmt-03", derive(defmt::Format))] pub enum WriteAllError { /// [`Write::write`] wrote zero bytes WriteZero, From 6ea5659babe340ed9330bd060158013c8d24920f Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sun, 6 Aug 2023 12:43:48 +0200 Subject: [PATCH 075/199] io: Release embedded-io 0.5 --- embedded-io-adapters/CHANGELOG.md | 10 ++++++++++ embedded-io-adapters/Cargo.toml | 2 +- embedded-io-async/CHANGELOG.md | 10 ++++++++++ embedded-io/CHANGELOG.md | 4 ++-- 4 files changed, 23 insertions(+), 3 deletions(-) create mode 100644 embedded-io-adapters/CHANGELOG.md create mode 100644 embedded-io-async/CHANGELOG.md diff --git a/embedded-io-adapters/CHANGELOG.md b/embedded-io-adapters/CHANGELOG.md new file mode 100644 index 000000000..978a92d8b --- /dev/null +++ b/embedded-io-adapters/CHANGELOG.md @@ -0,0 +1,10 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## 0.5.0 - 2023-08-06 + +- First release \ No newline at end of file diff --git a/embedded-io-adapters/Cargo.toml b/embedded-io-adapters/Cargo.toml index 05905ad60..23814b62f 100644 --- a/embedded-io-adapters/Cargo.toml +++ b/embedded-io-adapters/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "embedded-io-adapters" -version = "0.1.0" +version = "0.5.0" edition = "2021" description = "Adapters between the `embedded-io` traits and other I/O traits" repository = "https://github.com/rust-embedded/embedded-hal" diff --git a/embedded-io-async/CHANGELOG.md b/embedded-io-async/CHANGELOG.md new file mode 100644 index 000000000..978a92d8b --- /dev/null +++ b/embedded-io-async/CHANGELOG.md @@ -0,0 +1,10 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## 0.5.0 - 2023-08-06 + +- First release \ No newline at end of file diff --git a/embedded-io/CHANGELOG.md b/embedded-io/CHANGELOG.md index 520e4b4dc..e042a7239 100644 --- a/embedded-io/CHANGELOG.md +++ b/embedded-io/CHANGELOG.md @@ -5,7 +5,7 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## Unreleased +## 0.5.0 - 2023-08-06 - Add `ReadReady`, `WriteReady` traits. They allow peeking whether the I/O handle is ready to read/write, so they allow using the traits in a non-blocking way. - Add variants to `ErrorKind` mirroring `std::io::ErrorKind`. @@ -13,7 +13,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Moved `embedded_io::blocking` to the crate root. - Split async traits to the `embedded-io-async` crate. - Split trait adapters to the `embedded-io-adapters` crate. -- Add `std::io` impls for `ReadExactError` & `WriteAllError`. +- Add `std::error` impls for `ReadExactError` & `WriteAllError`. - Rename trait `Io` to `ErrorKind`, for consistency with `embedded-hal`. ## 0.4.0 - 2022-11-25 From 9fd6ebcfb06dc8ffb39b8f697fa3e6315dd97a04 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sun, 6 Aug 2023 18:56:21 +0200 Subject: [PATCH 076/199] hal: add optional defmt support. --- embedded-hal-async/Cargo.toml | 4 ++++ embedded-hal-bus/Cargo.toml | 2 ++ embedded-hal-bus/src/lib.rs | 4 ++++ embedded-hal-bus/src/spi/mod.rs | 6 ++++++ embedded-hal/Cargo.toml | 3 +++ embedded-hal/src/digital.rs | 5 +++++ embedded-hal/src/i2c.rs | 6 ++++++ embedded-hal/src/lib.rs | 4 ++++ embedded-hal/src/pwm.rs | 4 ++++ embedded-hal/src/spi.rs | 10 +++++++++- 10 files changed, 47 insertions(+), 1 deletion(-) diff --git a/embedded-hal-async/Cargo.toml b/embedded-hal-async/Cargo.toml index dbae01c7d..918514052 100644 --- a/embedded-hal-async/Cargo.toml +++ b/embedded-hal-async/Cargo.toml @@ -14,5 +14,9 @@ repository = "https://github.com/rust-embedded/embedded-hal" version = "0.2.0-alpha.2" rust-version = "1.65.0" +[features] +defmt-03 = ["dep:defmt-03", "embedded-hal/defmt-03"] + [dependencies] embedded-hal = { version = "=1.0.0-alpha.11", path = "../embedded-hal" } +defmt-03 = { package = "defmt", version = "0.3", optional = true } \ No newline at end of file diff --git a/embedded-hal-bus/Cargo.toml b/embedded-hal-bus/Cargo.toml index 66477fdfa..6edcdc75c 100644 --- a/embedded-hal-bus/Cargo.toml +++ b/embedded-hal-bus/Cargo.toml @@ -16,11 +16,13 @@ version = "0.1.0-alpha.3" [features] std = [] async = ["dep:embedded-hal-async"] +defmt-03 = ["dep:defmt-03", "embedded-hal/defmt-03", "embedded-hal-async?/defmt-03"] [dependencies] embedded-hal = { version = "=1.0.0-alpha.11", path = "../embedded-hal" } embedded-hal-async = { version = "=0.2.0-alpha.2", path = "../embedded-hal-async", optional = true } critical-section = { version = "1.0" } +defmt-03 = { package = "defmt", version = "0.3", optional = true } [package.metadata.docs.rs] features = ["std", "async"] diff --git a/embedded-hal-bus/src/lib.rs b/embedded-hal-bus/src/lib.rs index cdb668ae3..6feef548b 100644 --- a/embedded-hal-bus/src/lib.rs +++ b/embedded-hal-bus/src/lib.rs @@ -4,5 +4,9 @@ #![cfg_attr(docsrs, feature(doc_cfg))] #![cfg_attr(feature = "async", feature(async_fn_in_trait, impl_trait_projections))] +// needed to prevent defmt macros from breaking, since they emit code that does `defmt::blahblah`. +#[cfg(feature = "defmt-03")] +use defmt_03 as defmt; + pub mod i2c; pub mod spi; diff --git a/embedded-hal-bus/src/spi/mod.rs b/embedded-hal-bus/src/spi/mod.rs index 498dd1414..e3e888c8e 100644 --- a/embedded-hal-bus/src/spi/mod.rs +++ b/embedded-hal-bus/src/spi/mod.rs @@ -14,8 +14,12 @@ pub use mutex::*; mod critical_section; pub use self::critical_section::*; +#[cfg(feature = "defmt-03")] +use crate::defmt; + /// Error type for [`ExclusiveDevice`] operations. #[derive(Copy, Clone, Eq, PartialEq, Debug)] +#[cfg_attr(feature = "defmt-03", derive(defmt::Format))] pub enum DeviceError { /// An inner SPI bus operation failed Spi(BUS), @@ -37,6 +41,8 @@ where } /// Dummy `DelayUs` implementation that panics on use. +#[derive(Copy, Clone, Eq, PartialEq, Debug)] +#[cfg_attr(feature = "defmt-03", derive(defmt::Format))] pub struct NoDelay; #[cold] diff --git a/embedded-hal/Cargo.toml b/embedded-hal/Cargo.toml index c70b3106a..ad9a0fdc3 100644 --- a/embedded-hal/Cargo.toml +++ b/embedded-hal/Cargo.toml @@ -14,3 +14,6 @@ name = "embedded-hal" readme = "README.md" repository = "https://github.com/rust-embedded/embedded-hal" version = "1.0.0-alpha.11" + +[dependencies] +defmt-03 = { package = "defmt", version = "0.3", optional = true } \ No newline at end of file diff --git a/embedded-hal/src/digital.rs b/embedded-hal/src/digital.rs index 677bb64d1..3f024ca2c 100644 --- a/embedded-hal/src/digital.rs +++ b/embedded-hal/src/digital.rs @@ -2,6 +2,9 @@ use core::{convert::From, ops::Not}; +#[cfg(feature = "defmt-03")] +use crate::defmt; + /// Error pub trait Error: core::fmt::Debug { /// Convert error to a generic error kind @@ -24,6 +27,7 @@ impl Error for core::convert::Infallible { /// free to define more specific or additional error types. However, by providing /// a mapping to these common errors, generic code can still react to them. #[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] +#[cfg_attr(feature = "defmt-03", derive(defmt::Format))] #[non_exhaustive] pub enum ErrorKind { /// A different error occurred. The original error may contain more information. @@ -74,6 +78,7 @@ impl ErrorType for &mut T { /// assert_eq!(!state, PinState::High); /// ``` #[derive(Debug, PartialEq, Eq, Clone, Copy)] +#[cfg_attr(feature = "defmt-03", derive(defmt::Format))] pub enum PinState { /// Low pin state Low, diff --git a/embedded-hal/src/i2c.rs b/embedded-hal/src/i2c.rs index 735408758..9195308b8 100644 --- a/embedded-hal/src/i2c.rs +++ b/embedded-hal/src/i2c.rs @@ -154,6 +154,9 @@ use crate::private; +#[cfg(feature = "defmt-03")] +use crate::defmt; + /// I2C error pub trait Error: core::fmt::Debug { /// Convert error to a generic I2C error kind @@ -176,6 +179,7 @@ impl Error for core::convert::Infallible { /// free to define more specific or additional error types. However, by providing /// a mapping to these common I2C errors, generic code can still react to them. #[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] +#[cfg_attr(feature = "defmt-03", derive(defmt::Format))] #[non_exhaustive] pub enum ErrorKind { /// Bus error occurred. e.g. A START or a STOP condition is detected and is not @@ -199,6 +203,7 @@ pub enum ErrorKind { /// response was received to an address versus a no acknowledge to a data byte. /// Where it is not possible to differentiate, `Unknown` should be indicated. #[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] +#[cfg_attr(feature = "defmt-03", derive(defmt::Format))] pub enum NoAcknowledgeSource { /// The device did not acknowledge its address. The device may be missing. Address, @@ -272,6 +277,7 @@ impl AddressMode for TenBitAddress {} /// /// Several operations can be combined as part of a transaction. #[derive(Debug, PartialEq, Eq)] +#[cfg_attr(feature = "defmt-03", derive(defmt::Format))] pub enum Operation<'a> { /// Read data into the provided buffer Read(&'a mut [u8]), diff --git a/embedded-hal/src/lib.rs b/embedded-hal/src/lib.rs index c195bb2a1..f5eb76c32 100644 --- a/embedded-hal/src/lib.rs +++ b/embedded-hal/src/lib.rs @@ -15,3 +15,7 @@ mod private { impl Sealed for SevenBitAddress {} impl Sealed for TenBitAddress {} } + +// needed to prevent defmt macros from breaking, since they emit code that does `defmt::blahblah`. +#[cfg(feature = "defmt-03")] +use defmt_03 as defmt; diff --git a/embedded-hal/src/pwm.rs b/embedded-hal/src/pwm.rs index 4c0f6ac60..3893eaade 100644 --- a/embedded-hal/src/pwm.rs +++ b/embedded-hal/src/pwm.rs @@ -1,5 +1,8 @@ //! Pulse Width Modulation (PWM) traits +#[cfg(feature = "defmt-03")] +use crate::defmt; + /// Error pub trait Error: core::fmt::Debug { /// Convert error to a generic error kind @@ -22,6 +25,7 @@ impl Error for core::convert::Infallible { /// free to define more specific or additional error types. However, by providing /// a mapping to these common errors, generic code can still react to them. #[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] +#[cfg_attr(feature = "defmt-03", derive(defmt::Format))] #[non_exhaustive] pub enum ErrorKind { /// A different error occurred. The original error may contain more information. diff --git a/embedded-hal/src/spi.rs b/embedded-hal/src/spi.rs index 54bf8bcea..3813b9a6c 100644 --- a/embedded-hal/src/spi.rs +++ b/embedded-hal/src/spi.rs @@ -163,8 +163,12 @@ use core::fmt::Debug; +#[cfg(feature = "defmt-03")] +use crate::defmt; + /// Clock polarity #[derive(Clone, Copy, Debug, PartialEq, Eq)] +#[cfg_attr(feature = "defmt-03", derive(defmt::Format))] pub enum Polarity { /// Clock signal low when idle IdleLow, @@ -174,6 +178,7 @@ pub enum Polarity { /// Clock phase #[derive(Clone, Copy, Debug, PartialEq, Eq)] +#[cfg_attr(feature = "defmt-03", derive(defmt::Format))] pub enum Phase { /// Data in "captured" on the first clock transition CaptureOnFirstTransition, @@ -183,6 +188,7 @@ pub enum Phase { /// SPI mode #[derive(Clone, Copy, Debug, PartialEq, Eq)] +#[cfg_attr(feature = "defmt-03", derive(defmt::Format))] pub struct Mode { /// Clock polarity pub polarity: Polarity, @@ -236,6 +242,7 @@ impl Error for core::convert::Infallible { /// free to define more specific or additional error types. However, by providing /// a mapping to these common SPI errors, generic code can still react to them. #[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] +#[cfg_attr(feature = "defmt-03", derive(defmt::Format))] #[non_exhaustive] pub enum ErrorKind { /// The peripheral receive buffer was overrun @@ -295,7 +302,8 @@ impl ErrorType for &mut T { /// SPI transaction operation. /// /// This allows composition of SPI operations into a single bus transaction -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Eq)] +#[cfg_attr(feature = "defmt-03", derive(defmt::Format))] pub enum Operation<'a, Word: 'static> { /// Read data into the provided buffer. /// From 680a2b381c3c2131b7e860d7ca858f716d7a80aa Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sun, 6 Aug 2023 20:35:45 +0200 Subject: [PATCH 077/199] ci: actually test all feature combinations, add defmt. the GHA `include` field is nonsense. --- .github/workflows/test.yml | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index a2fa0d9d7..bb7bf5e56 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -23,14 +23,18 @@ jobs: - x86_64-unknown-linux-gnu - thumbv6m-none-eabi - thumbv7m-none-eabi + features: + - '' include: - - target: x86_64-unknown-linux-gnu + - rust: stable + target: x86_64-unknown-linux-gnu features: std - - target: x86_64-unknown-linux-gnu + - rust: stable + target: x86_64-unknown-linux-gnu features: alloc - - target: x86_64-unknown-linux-gnu - features: std,tokio-1,futures-03 - rust: nightly + - rust: nightly + target: x86_64-unknown-linux-gnu + features: std,tokio-1,futures-03,defmt-03 steps: - uses: actions/checkout@v3 - uses: dtolnay/rust-toolchain@master From 3505e76a0c30b4d1f2ce2fceff8bbdb9111d6106 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sun, 6 Aug 2023 20:46:23 +0200 Subject: [PATCH 078/199] ci: don't build for both thumbv6 and thumbv7. We have 16 jobs now, starts to hit the GHA free limit of 20. There's no advantage in testing thumbv6 and thumbv7 because they're almost identical and nothing in e-h depends on them, so remove thumbv6. --- .github/workflows/test.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index bb7bf5e56..19ff36502 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -21,7 +21,6 @@ jobs: - nightly target: - x86_64-unknown-linux-gnu - - thumbv6m-none-eabi - thumbv7m-none-eabi features: - '' From cb1fdf2ec479b570b73a79c42710282305abe885 Mon Sep 17 00:00:00 2001 From: Matt Johnston Date: Tue, 15 Aug 2023 22:20:24 +0800 Subject: [PATCH 079/199] Fix doc links for adapters, add features The doc links in the README needed updating Include tokio and futures features in docs --- embedded-io-adapters/Cargo.toml | 2 +- embedded-io-adapters/README.md | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/embedded-io-adapters/Cargo.toml b/embedded-io-adapters/Cargo.toml index 23814b62f..5dbcf4354 100644 --- a/embedded-io-adapters/Cargo.toml +++ b/embedded-io-adapters/Cargo.toml @@ -24,5 +24,5 @@ futures = { version = "0.3.21", features = ["std"], default-features = false, op tokio = { version = "1", default-features = false, optional = true } [package.metadata.docs.rs] -features = ["std"] +features = ["std", "tokio-1", "futures-03"] rustdoc-args = ["--cfg", "docsrs"] diff --git a/embedded-io-adapters/README.md b/embedded-io-adapters/README.md index 0cdb8c558..6edd3c47b 100644 --- a/embedded-io-adapters/README.md +++ b/embedded-io-adapters/README.md @@ -1,6 +1,6 @@ -[![crates.io](https://img.shields.io/crates/d/embedded-io.svg)](https://crates.io/crates/embedded-io) -[![crates.io](https://img.shields.io/crates/v/embedded-io.svg)](https://crates.io/crates/embedded-io) -[![Documentation](https://docs.rs/embedded-io/badge.svg)](https://docs.rs/embedded-io) +[![crates.io](https://img.shields.io/crates/d/embedded-io-adapters.svg)](https://crates.io/crates/embedded-io-adapters) +[![crates.io](https://img.shields.io/crates/v/embedded-io-adapters.svg)](https://crates.io/crates/embedded-io-adapters) +[![Documentation](https://docs.rs/embedded-io-adapters/badge.svg)](https://docs.rs/embedded-io-adapters) # `embedded-io-adapters` From 575cee33438b64aec5de49cb505643437f47aadc Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 15 Aug 2023 19:18:16 +0200 Subject: [PATCH 080/199] Release embedded-hal{,-async,-nb} v1.0.0-rc.1, embedded-hal-bus v0.1.0-rc.1 --- embedded-hal-async/CHANGELOG.md | 12 +++++++++--- embedded-hal-async/Cargo.toml | 4 ++-- embedded-hal-async/README.md | 12 ++++++++++-- embedded-hal-bus/CHANGELOG.md | 11 ++++++++++- embedded-hal-bus/Cargo.toml | 6 +++--- embedded-hal-bus/README.md | 6 ++++-- embedded-hal-nb/CHANGELOG.md | 5 +++++ embedded-hal-nb/Cargo.toml | 4 ++-- embedded-hal/CHANGELOG.md | 11 +++++++++-- embedded-hal/Cargo.toml | 4 ++-- embedded-hal/README.md | 4 ++++ embedded-io-async/README.md | 6 ++++++ embedded-io/CHANGELOG.md | 1 + embedded-io/README.md | 6 ++++++ 14 files changed, 73 insertions(+), 19 deletions(-) diff --git a/embedded-hal-async/CHANGELOG.md b/embedded-hal-async/CHANGELOG.md index 5ea703cdd..478e994f7 100644 --- a/embedded-hal-async/CHANGELOG.md +++ b/embedded-hal-async/CHANGELOG.md @@ -7,8 +7,13 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] -### Added -- spi: added `ExclusiveDevice::{bus, bus_mut}`. +## [v1.0.0-rc.1] - 2023-08-15 + +- Updated `embedded-hal` to version `1.0.0-rc.1`. +- Add optional `defmt` 0.3 support. +- Remove serial traits, the replacement is the `embedded-io` crate. +- Added `+ ?Sized` to all blanket impls. +- Moved `ExclusiveDevice` to `embedded-hal-bus`. ## [v0.2.0-alpha.2] - 2023-07-04 @@ -64,7 +69,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/). First release to crates.io -[Unreleased]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-async-v0.2.0-alpha.2...HEAD +[Unreleased]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-async-v1.0.0-rc.1...HEAD +[v1.0.0-rc.1]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-async-v0.2.0-alpha.2...embedded-hal-async-v1.0.0-rc.1 [v0.2.0-alpha.2]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-async-v0.2.0-alpha.1...embedded-hal-async-v0.2.0-alpha.2 [v0.2.0-alpha.1]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-async-v0.2.0-alpha.0...embedded-hal-async-v0.2.0-alpha.1 [v0.2.0-alpha.0]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-async-v0.1.0-alpha.3...embedded-hal-async-v0.2.0-alpha.0 diff --git a/embedded-hal-async/Cargo.toml b/embedded-hal-async/Cargo.toml index 918514052..b10cfc734 100644 --- a/embedded-hal-async/Cargo.toml +++ b/embedded-hal-async/Cargo.toml @@ -11,12 +11,12 @@ license = "MIT OR Apache-2.0" name = "embedded-hal-async" readme = "README.md" repository = "https://github.com/rust-embedded/embedded-hal" -version = "0.2.0-alpha.2" +version = "1.0.0-rc.1" rust-version = "1.65.0" [features] defmt-03 = ["dep:defmt-03", "embedded-hal/defmt-03"] [dependencies] -embedded-hal = { version = "=1.0.0-alpha.11", path = "../embedded-hal" } +embedded-hal = { version = "=1.0.0-rc.1", path = "../embedded-hal" } defmt-03 = { package = "defmt", version = "0.3", optional = true } \ No newline at end of file diff --git a/embedded-hal-async/README.md b/embedded-hal-async/README.md index d22d658d8..c53790751 100644 --- a/embedded-hal-async/README.md +++ b/embedded-hal-async/README.md @@ -17,13 +17,21 @@ A serial port is essentially a byte-oriented stream, and that's what `embedded-i with all byte streams has some advantages. For example, it allows generic code providing a command-line interface or a console to operate either on hardware serial ports or on virtual ones like Telnet or USB CDC-ACM. +## Optional Cargo features + +- **`defmt-03`**: Derive `defmt::Format` from `defmt` 0.3 for enums and structs. + ## Minimum Supported Rust Version (MSRV) This crate requires Rust nightly newer than `nightly-2022-11-22`, due to requiring support for -`async fn` in traits (AFIT), which is not stable yet. +`async fn` in traits (AFIT), which is not stable yet. Keep in mind Rust nightlies can make backwards-incompatible changes to unstable features -at any time. +at any time. If this happens, we might do changes that increase the minimum required nightly +version in any patch release. + +When AFIT becomes stable, MSRV will be bumped to the Rust version that stabilizes it, after which +point the [standard MSRV bump policy](../docs/msrv.md) will apply. ## License diff --git a/embedded-hal-bus/CHANGELOG.md b/embedded-hal-bus/CHANGELOG.md index c97121910..ead1de58f 100644 --- a/embedded-hal-bus/CHANGELOG.md +++ b/embedded-hal-bus/CHANGELOG.md @@ -7,6 +7,14 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] +## [v0.1.0-rc.1] - 2023-08-15 + +- Updated `embedded-hal`, `embedded-hal-async` to version `1.0.0-rc.1`. +- The Minimum Supported Rust Version (MSRV) is now 1.60.0 +- Added `embedded-hal-async` support to SPI `ExclusiveDevice`. +- Added methods to access the inner bus to SPI `ExclusiveDevice`. +- Add optional `defmt` 0.3 support. + ## [v0.1.0-alpha.3] - 2023-07-04 ### Changed @@ -31,7 +39,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/). First release to crates.io -[Unreleased]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-bus-v0.1.0-alpha.3...HEAD +[Unreleased]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-bus-v0.1.0-rc.1...HEAD +[v0.1.0-rc.1]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-bus-v0.1.0-alpha.3...embedded-hal-bus-v0.1.0-rc.1 [v0.1.0-alpha.3]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-bus-v0.1.0-alpha.2...embedded-hal-bus-v0.1.0-alpha.3 [v0.1.0-alpha.2]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-bus-v0.1.0-alpha.1...embedded-hal-bus-v0.1.0-alpha.2 [v0.1.0-alpha.1]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-bus-v0.1.0-alpha.0...embedded-hal-bus-v0.1.0-alpha.1 diff --git a/embedded-hal-bus/Cargo.toml b/embedded-hal-bus/Cargo.toml index 6edcdc75c..bfd2a754d 100644 --- a/embedded-hal-bus/Cargo.toml +++ b/embedded-hal-bus/Cargo.toml @@ -11,7 +11,7 @@ license = "MIT OR Apache-2.0" name = "embedded-hal-bus" readme = "README.md" repository = "https://github.com/rust-embedded/embedded-hal" -version = "0.1.0-alpha.3" +version = "0.1.0-rc.1" [features] std = [] @@ -19,8 +19,8 @@ async = ["dep:embedded-hal-async"] defmt-03 = ["dep:defmt-03", "embedded-hal/defmt-03", "embedded-hal-async?/defmt-03"] [dependencies] -embedded-hal = { version = "=1.0.0-alpha.11", path = "../embedded-hal" } -embedded-hal-async = { version = "=0.2.0-alpha.2", path = "../embedded-hal-async", optional = true } +embedded-hal = { version = "=1.0.0-rc.1", path = "../embedded-hal" } +embedded-hal-async = { version = "=1.0.0-rc.1", path = "../embedded-hal-async", optional = true } critical-section = { version = "1.0" } defmt-03 = { package = "defmt", version = "0.3", optional = true } diff --git a/embedded-hal-bus/README.md b/embedded-hal-bus/README.md index 394ffb88b..d0b9bbdb6 100644 --- a/embedded-hal-bus/README.md +++ b/embedded-hal-bus/README.md @@ -28,9 +28,11 @@ This crate provides mechanisms to connect a `SpiBus` and a `SpiDevice`. In the case of I2C, the same `I2c` `embedded-hal` trait represents either an entire bus, or a device on a bus. This crate provides mechanisms to obtain multiple `I2c` instances out of a single `I2c` instance, sharing the bus. -## Features +## Optional Cargo features -- `std`: enable shared bus implementations using `std::sync::Mutex`. +- **`std`**: enable shared bus implementations using `std::sync::Mutex`. +- **`async`**: enable `embedded-hal-async` support. +- **`defmt-03`**: Derive `defmt::Format` from `defmt` 0.3 for enums and structs. ## Minimum Supported Rust Version (MSRV) diff --git a/embedded-hal-nb/CHANGELOG.md b/embedded-hal-nb/CHANGELOG.md index 11ebab3b0..83b7e1510 100644 --- a/embedded-hal-nb/CHANGELOG.md +++ b/embedded-hal-nb/CHANGELOG.md @@ -9,6 +9,11 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ... +## [v1.0.0-rc.1] - 2023-08-15 + +- Updated `embedded-hal` to version `1.0.0-rc.1`. +- Added `+ ?Sized` to all blanket impls. + ## [v1.0.0-alpha.3] - 2023-07-04 ### Changed diff --git a/embedded-hal-nb/Cargo.toml b/embedded-hal-nb/Cargo.toml index 0bf3d7346..66e941923 100644 --- a/embedded-hal-nb/Cargo.toml +++ b/embedded-hal-nb/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "embedded-hal-nb" -version = "1.0.0-alpha.3" +version = "1.0.0-rc.1" edition = "2021" categories = ["embedded", "hardware-support", "no-std"] @@ -12,7 +12,7 @@ readme = "README.md" repository = "https://github.com/rust-embedded/embedded-hal" [dependencies] -embedded-hal = { version = "=1.0.0-alpha.11", path = "../embedded-hal" } +embedded-hal = { version = "=1.0.0-rc.1", path = "../embedded-hal" } nb = "1" [dev-dependencies] diff --git a/embedded-hal/CHANGELOG.md b/embedded-hal/CHANGELOG.md index ccb13c888..62f78ff36 100644 --- a/embedded-hal/CHANGELOG.md +++ b/embedded-hal/CHANGELOG.md @@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] +## [v1.0.0-rc.1] - 2023-08-15 + +- The Minimum Supported Rust Version (MSRV) is now 1.60.0 +- Add optional `defmt` 0.3 support. +- Remove serial traits, the replacement is the `embedded-io` crate. +- Added `+ ?Sized` to all blanket impls. ## [v1.0.0-alpha.11] - 2023-07-04 @@ -65,7 +71,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). *** This is (also) an alpha release with breaking changes (sorry) *** ### Changed -- The Minimum Supported Rust Version (MSRV) is now 1.60.0 +- The Minimum Supported Rust Version (MSRV) is now 1.59.0 - `spi`: unify all traits into `SpiReadBus`, `SpiWriteBus` and `SpiBus` (read-write). - `spi`: Add `SpiDevice` trait to represent a single device in a (possibly shared) bus, with managed chip-select (CS) pin. - `spi`: Clarify that implementations are allowed to return before operations are finished, add `flush` to wait until finished. @@ -291,7 +297,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/). Initial release -[Unreleased]: https://github.com/rust-embedded/embedded-hal/compare/v1.0.0-alpha.11...HEAD +[Unreleased]: https://github.com/rust-embedded/embedded-hal/compare/v1.0.0-rc.1...HEAD +[v1.0.0-rc.1]: https://github.com/rust-embedded/embedded-hal/compare/v1.0.0-alpha.11...v1.0.0-rc.1 [v1.0.0-alpha.11]: https://github.com/rust-embedded/embedded-hal/compare/v1.0.0-alpha.10...v1.0.0-alpha.11 [v1.0.0-alpha.10]: https://github.com/rust-embedded/embedded-hal/compare/v1.0.0-alpha.9...v1.0.0-alpha.10 [v1.0.0-alpha.9]: https://github.com/rust-embedded/embedded-hal/compare/v1.0.0-alpha.8...v1.0.0-alpha.9 diff --git a/embedded-hal/Cargo.toml b/embedded-hal/Cargo.toml index ad9a0fdc3..396cf972b 100644 --- a/embedded-hal/Cargo.toml +++ b/embedded-hal/Cargo.toml @@ -13,7 +13,7 @@ license = "MIT OR Apache-2.0" name = "embedded-hal" readme = "README.md" repository = "https://github.com/rust-embedded/embedded-hal" -version = "1.0.0-alpha.11" +version = "1.0.0-rc.1" [dependencies] -defmt-03 = { package = "defmt", version = "0.3", optional = true } \ No newline at end of file +defmt-03 = { package = "defmt", version = "0.3", optional = true } diff --git a/embedded-hal/README.md b/embedded-hal/README.md index f29a8c599..bed3c40c5 100644 --- a/embedded-hal/README.md +++ b/embedded-hal/README.md @@ -84,6 +84,10 @@ A serial port is essentially a byte-oriented stream, and that's what `embedded-i with all byte streams has some advantages. For example, it allows generic code providing a command-line interface or a console to operate either on hardware serial ports or on virtual ones like Telnet or USB CDC-ACM. +## Optional Cargo features + +- **`defmt-03`**: Derive `defmt::Format` from `defmt` 0.3 for enums and structs. + ## Minimum Supported Rust Version (MSRV) This crate is guaranteed to compile on stable Rust 1.60 and up. It *might* diff --git a/embedded-io-async/README.md b/embedded-io-async/README.md index 557a0866a..6e6ddf18a 100644 --- a/embedded-io-async/README.md +++ b/embedded-io-async/README.md @@ -10,6 +10,12 @@ This crate contains asynchronous versions of the [`embedded-io`](https://crates. This project is developed and maintained by the [HAL team](https://github.com/rust-embedded/wg#the-hal-team). +## Optional Cargo features + +- **`std`**: Adds `From` impls to convert to/from `std::io` structs, adds `std::error::Error` impls. +- **`alloc`**: Adds blanket impls for `Box`, adds `Write` impl to `Vec`. +- **`defmt-03`**: Derive `defmt::Format` from `defmt` 0.3 for enums and structs. + ## Minimum Supported Rust Version (MSRV) This crate requires Rust nightly newer than `nightly-2022-11-22`, due to requiring support for diff --git a/embedded-io/CHANGELOG.md b/embedded-io/CHANGELOG.md index e042a7239..f836114ae 100644 --- a/embedded-io/CHANGELOG.md +++ b/embedded-io/CHANGELOG.md @@ -15,6 +15,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Split trait adapters to the `embedded-io-adapters` crate. - Add `std::error` impls for `ReadExactError` & `WriteAllError`. - Rename trait `Io` to `ErrorKind`, for consistency with `embedded-hal`. +- Added optional `defmt` 0.3 support. ## 0.4.0 - 2022-11-25 diff --git a/embedded-io/README.md b/embedded-io/README.md index 3318daa2b..d647ffe9d 100644 --- a/embedded-io/README.md +++ b/embedded-io/README.md @@ -19,6 +19,12 @@ while avoiding `dyn` or `Box`. This is consistent with how errors are handled in - In `std::io`, the `Read`/`Write` traits might be blocking or non-blocking (i.e. returning `WouldBlock` errors) depending on the file descriptor's mode, which is only known at run-time. This allows passing a non-blocking stream to code that expects a blocking stream, causing unexpected errors. To solve this, `embedded-io` specifies `Read`/`Write` are always blocking, and adds new `ReadReady`/`WriteReady` traits to allow using streams in a non-blocking way. +## Optional Cargo features + +- **`std`**: Adds `From` impls to convert to/from `std::io` structs, adds `std::error::Error` impls. +- **`alloc`**: Adds blanket impls for `Box`, adds `Write` impl to `Vec`. +- **`defmt-03`**: Derive `defmt::Format` from `defmt` 0.3 for enums and structs. + ## Minimum Supported Rust Version (MSRV) This crate is guaranteed to compile on stable Rust 1.60 and up. It *might* From aa5e4754ea269b03929b2657775fef07f5f92e67 Mon Sep 17 00:00:00 2001 From: Daniel Larsen <44644910+daniel-larsen@users.noreply.github.com> Date: Thu, 17 Aug 2023 17:53:52 -0300 Subject: [PATCH 081/199] Fix typo in Changelog --- embedded-io/CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embedded-io/CHANGELOG.md b/embedded-io/CHANGELOG.md index f836114ae..937a43140 100644 --- a/embedded-io/CHANGELOG.md +++ b/embedded-io/CHANGELOG.md @@ -14,7 +14,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Split async traits to the `embedded-io-async` crate. - Split trait adapters to the `embedded-io-adapters` crate. - Add `std::error` impls for `ReadExactError` & `WriteAllError`. -- Rename trait `Io` to `ErrorKind`, for consistency with `embedded-hal`. +- Rename trait `Io` to `ErrorType`, for consistency with `embedded-hal`. - Added optional `defmt` 0.3 support. ## 0.4.0 - 2022-11-25 From ed96f03b3ad425e79fc6cb97b60d0d650a28e557 Mon Sep 17 00:00:00 2001 From: Anatol Ulrich <45840+spookyvision@users.noreply.github.com> Date: Mon, 21 Aug 2023 17:30:55 +0200 Subject: [PATCH 082/199] Update README.md typo: "is still is" --- embedded-hal/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embedded-hal/README.md b/embedded-hal/README.md index bed3c40c5..193c63b3a 100644 --- a/embedded-hal/README.md +++ b/embedded-hal/README.md @@ -9,7 +9,7 @@ This project is developed and maintained by the [HAL team](https://github.com/rust-embedded/wg#the-hal-team). -**NOTE** This HAL is still is active development. Expect the traits presented here to be +**NOTE** This HAL is still in active development. Expect the traits presented here to be tweaked, split or be replaced wholesale before being stabilized, i.e. before hitting the 1.0.0 release. From a0e1f49b1f34fc99524474faf6e0d483ec7c9da8 Mon Sep 17 00:00:00 2001 From: Zhouqi Jiang Date: Wed, 23 Aug 2023 10:07:01 +0800 Subject: [PATCH 083/199] e-h: amend documents to add periods (.), add inline hints when necessary In documentation style of the Rust standard library, first sentence of all modules, types, and functions documentation has a period. We follow Rust standard library style to make it easier for users to read. All functions in embedded-hal (especially type conversations, function fowarding calls) are now marked #[inline] to allow further optimizations. Signed-off-by: Zhouqi Jiang --- embedded-hal/src/delay.rs | 8 ++-- embedded-hal/src/digital.rs | 49 +++++++++++++++---------- embedded-hal/src/i2c.rs | 47 +++++++++++++++--------- embedded-hal/src/pwm.rs | 19 +++++++--- embedded-hal/src/spi.rs | 73 +++++++++++++++++++++++-------------- 5 files changed, 123 insertions(+), 73 deletions(-) diff --git a/embedded-hal/src/delay.rs b/embedded-hal/src/delay.rs index da4cd7e2d..32f085729 100644 --- a/embedded-hal/src/delay.rs +++ b/embedded-hal/src/delay.rs @@ -1,7 +1,6 @@ -//! Delays +//! Delays. -/// Microsecond delay -/// +/// Microsecond delay. pub trait DelayUs { /// Pauses execution for at minimum `us` microseconds. Pause can be longer /// if the implementation requires it due to precision/timing issues. @@ -9,6 +8,7 @@ pub trait DelayUs { /// Pauses execution for at minimum `ms` milliseconds. Pause can be longer /// if the implementation requires it due to precision/timing issues. + #[inline] fn delay_ms(&mut self, ms: u32) { for _ in 0..ms { self.delay_us(1000); @@ -20,10 +20,12 @@ impl DelayUs for &mut T where T: DelayUs + ?Sized, { + #[inline] fn delay_us(&mut self, us: u32) { T::delay_us(self, us) } + #[inline] fn delay_ms(&mut self, ms: u32) { T::delay_ms(self, ms) } diff --git a/embedded-hal/src/digital.rs b/embedded-hal/src/digital.rs index 3f024ca2c..f54c72153 100644 --- a/embedded-hal/src/digital.rs +++ b/embedded-hal/src/digital.rs @@ -1,11 +1,11 @@ -//! Digital I/O +//! Digital I/O. use core::{convert::From, ops::Not}; #[cfg(feature = "defmt-03")] use crate::defmt; -/// Error +/// Error. pub trait Error: core::fmt::Debug { /// Convert error to a generic error kind /// @@ -21,7 +21,7 @@ impl Error for core::convert::Infallible { } } -/// Error kind +/// Error kind. /// /// This represents a common set of operation errors. HAL implementations are /// free to define more specific or additional error types. However, by providing @@ -35,12 +35,14 @@ pub enum ErrorKind { } impl Error for ErrorKind { + #[inline] fn kind(&self) -> ErrorKind { *self } } impl core::fmt::Display for ErrorKind { + #[inline] fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { match self { Self::Other => write!( @@ -51,7 +53,7 @@ impl core::fmt::Display for ErrorKind { } } -/// Error type trait +/// Error type trait. /// /// This just defines the error type, to be used by the other traits. pub trait ErrorType { @@ -67,7 +69,7 @@ impl ErrorType for &mut T { type Error = T::Error; } -/// Digital output pin state +/// Digital output pin state. /// /// Conversion from `bool` and logical negation are also implemented /// for this type. @@ -80,9 +82,9 @@ impl ErrorType for &mut T { #[derive(Debug, PartialEq, Eq, Clone, Copy)] #[cfg_attr(feature = "defmt-03", derive(defmt::Format))] pub enum PinState { - /// Low pin state + /// Low pin state. Low, - /// High pin state + /// High pin state. High, } @@ -118,24 +120,25 @@ impl From for bool { } } -/// Single digital push-pull output pin +/// Single digital push-pull output pin. pub trait OutputPin: ErrorType { - /// Drives the pin low + /// Drives the pin low. /// /// *NOTE* the actual electrical state of the pin may not actually be low, e.g. due to external - /// electrical sources + /// electrical sources. fn set_low(&mut self) -> Result<(), Self::Error>; - /// Drives the pin high + /// Drives the pin high. /// /// *NOTE* the actual electrical state of the pin may not actually be high, e.g. due to external - /// electrical sources + /// electrical sources. fn set_high(&mut self) -> Result<(), Self::Error>; - /// Drives the pin high or low depending on the provided value + /// Drives the pin high or low depending on the provided value. /// /// *NOTE* the actual electrical state of the pin may not actually be high or low, e.g. due to external - /// electrical sources + /// electrical sources. + #[inline] fn set_state(&mut self, state: PinState) -> Result<(), Self::Error> { match state { PinState::Low => self.set_low(), @@ -145,55 +148,61 @@ pub trait OutputPin: ErrorType { } impl OutputPin for &mut T { + #[inline] fn set_low(&mut self) -> Result<(), Self::Error> { T::set_low(self) } + #[inline] fn set_high(&mut self) -> Result<(), Self::Error> { T::set_high(self) } + #[inline] fn set_state(&mut self, state: PinState) -> Result<(), Self::Error> { T::set_state(self, state) } } -/// Push-pull output pin that can read its output state +/// Push-pull output pin that can read its output state. pub trait StatefulOutputPin: OutputPin { /// Is the pin in drive high mode? /// - /// *NOTE* this does *not* read the electrical state of the pin + /// *NOTE* this does *not* read the electrical state of the pin. fn is_set_high(&self) -> Result; /// Is the pin in drive low mode? /// - /// *NOTE* this does *not* read the electrical state of the pin + /// *NOTE* this does *not* read the electrical state of the pin. fn is_set_low(&self) -> Result; } impl StatefulOutputPin for &mut T { + #[inline] fn is_set_high(&self) -> Result { T::is_set_high(self) } + #[inline] fn is_set_low(&self) -> Result { T::is_set_low(self) } } -/// Output pin that can be toggled +/// Output pin that can be toggled. pub trait ToggleableOutputPin: ErrorType { /// Toggle pin output. fn toggle(&mut self) -> Result<(), Self::Error>; } impl ToggleableOutputPin for &mut T { + #[inline] fn toggle(&mut self) -> Result<(), Self::Error> { T::toggle(self) } } -/// Single digital input pin +/// Single digital input pin. pub trait InputPin: ErrorType { /// Is the input pin high? fn is_high(&self) -> Result; @@ -203,10 +212,12 @@ pub trait InputPin: ErrorType { } impl InputPin for &T { + #[inline] fn is_high(&self) -> Result { T::is_high(self) } + #[inline] fn is_low(&self) -> Result { T::is_low(self) } diff --git a/embedded-hal/src/i2c.rs b/embedded-hal/src/i2c.rs index 9195308b8..e42b16ebc 100644 --- a/embedded-hal/src/i2c.rs +++ b/embedded-hal/src/i2c.rs @@ -1,4 +1,4 @@ -//! Blocking I2C API +//! Blocking I2C API. //! //! This API supports 7-bit and 10-bit addresses. Traits feature an [`AddressMode`] //! marker type parameter. Two implementation of the [`AddressMode`] exist: @@ -157,9 +157,9 @@ use crate::private; #[cfg(feature = "defmt-03")] use crate::defmt; -/// I2C error +/// I2C error. pub trait Error: core::fmt::Debug { - /// Convert error to a generic I2C error kind + /// Convert error to a generic I2C error kind. /// /// By using this method, I2C errors freely defined by HAL implementations /// can be converted to a set of generic I2C errors upon which generic @@ -168,12 +168,13 @@ pub trait Error: core::fmt::Debug { } impl Error for core::convert::Infallible { + #[inline] fn kind(&self) -> ErrorKind { match *self {} } } -/// I2C error kind +/// I2C error kind. /// /// This represents a common set of I2C operation errors. HAL implementations are /// free to define more specific or additional error types. However, by providing @@ -185,19 +186,19 @@ pub enum ErrorKind { /// Bus error occurred. e.g. A START or a STOP condition is detected and is not /// located after a multiple of 9 SCL clock pulses. Bus, - /// The arbitration was lost, e.g. electrical problems with the clock signal + /// The arbitration was lost, e.g. electrical problems with the clock signal. ArbitrationLoss, /// A bus operation was not acknowledged, e.g. due to the addressed device not /// being available on the bus or the device not being ready to process requests - /// at the moment + /// at the moment. NoAcknowledge(NoAcknowledgeSource), - /// The peripheral receive buffer was overrun + /// The peripheral receive buffer was overrun. Overrun, /// A different error occurred. The original error may contain more information. Other, } -/// I2C no acknowledge error source +/// I2C no acknowledge error source. /// /// In cases where it is possible, a device should indicate if a no acknowledge /// response was received to an address versus a no acknowledge to a data byte. @@ -216,12 +217,14 @@ pub enum NoAcknowledgeSource { } impl Error for ErrorKind { + #[inline] fn kind(&self) -> ErrorKind { *self } } impl core::fmt::Display for ErrorKind { + #[inline] fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { match self { Self::Bus => write!(f, "Bus error occurred"), @@ -237,6 +240,7 @@ impl core::fmt::Display for ErrorKind { } impl core::fmt::Display for NoAcknowledgeSource { + #[inline] fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { match self { Self::Address => write!(f, "The device did not acknowledge its address"), @@ -246,7 +250,7 @@ impl core::fmt::Display for NoAcknowledgeSource { } } -/// I2C error type trait +/// I2C error type trait. /// /// This just defines the error type, to be used by the other traits. pub trait ErrorType { @@ -258,15 +262,15 @@ impl ErrorType for &mut T { type Error = T::Error; } -/// Address mode (7-bit / 10-bit) +/// Address mode (7-bit / 10-bit). /// /// Note: This trait is sealed and should not be implemented outside of this crate. pub trait AddressMode: private::Sealed + 'static {} -/// 7-bit address mode type +/// 7-bit address mode type. pub type SevenBitAddress = u8; -/// 10-bit address mode type +/// 10-bit address mode type. pub type TenBitAddress = u16; impl AddressMode for SevenBitAddress {} @@ -279,15 +283,15 @@ impl AddressMode for TenBitAddress {} #[derive(Debug, PartialEq, Eq)] #[cfg_attr(feature = "defmt-03", derive(defmt::Format))] pub enum Operation<'a> { - /// Read data into the provided buffer + /// Read data into the provided buffer. Read(&'a mut [u8]), - /// Write data from the provided buffer + /// Write data from the provided buffer. Write(&'a [u8]), } -/// Blocking I2C +/// Blocking I2C. pub trait I2c: ErrorType { - /// Reads enough bytes from slave with `address` to fill `read` + /// Reads enough bytes from slave with `address` to fill `read`. /// /// # I2C Events (contract) /// @@ -305,11 +309,12 @@ pub trait I2c: ErrorType { /// - `MAK` = master acknowledge /// - `NMAK` = master no acknowledge /// - `SP` = stop condition + #[inline] fn read(&mut self, address: A, read: &mut [u8]) -> Result<(), Self::Error> { self.transaction(address, &mut [Operation::Read(read)]) } - /// Writes bytes to slave with address `address` + /// Writes bytes to slave with address `address`. /// /// # I2C Events (contract) /// @@ -325,12 +330,13 @@ pub trait I2c: ErrorType { /// - `SAK` = slave acknowledge /// - `Bi` = ith byte of data /// - `SP` = stop condition + #[inline] fn write(&mut self, address: A, write: &[u8]) -> Result<(), Self::Error> { self.transaction(address, &mut [Operation::Write(write)]) } /// Writes bytes to slave with address `address` and then reads enough bytes to fill `read` *in a - /// single transaction* + /// single transaction*. /// /// # I2C Events (contract) /// @@ -351,6 +357,7 @@ pub trait I2c: ErrorType { /// - `MAK` = master acknowledge /// - `NMAK` = master no acknowledge /// - `SP` = stop condition + #[inline] fn write_read(&mut self, address: A, write: &[u8], read: &mut [u8]) -> Result<(), Self::Error> { self.transaction( address, @@ -379,18 +386,22 @@ pub trait I2c: ErrorType { } impl + ?Sized> I2c for &mut T { + #[inline] fn read(&mut self, address: A, read: &mut [u8]) -> Result<(), Self::Error> { T::read(self, address, read) } + #[inline] fn write(&mut self, address: A, write: &[u8]) -> Result<(), Self::Error> { T::write(self, address, write) } + #[inline] fn write_read(&mut self, address: A, write: &[u8], read: &mut [u8]) -> Result<(), Self::Error> { T::write_read(self, address, write, read) } + #[inline] fn transaction( &mut self, address: A, diff --git a/embedded-hal/src/pwm.rs b/embedded-hal/src/pwm.rs index 3893eaade..7f3b0828c 100644 --- a/embedded-hal/src/pwm.rs +++ b/embedded-hal/src/pwm.rs @@ -1,11 +1,11 @@ -//! Pulse Width Modulation (PWM) traits +//! Pulse Width Modulation (PWM) traits. #[cfg(feature = "defmt-03")] use crate::defmt; /// Error pub trait Error: core::fmt::Debug { - /// Convert error to a generic error kind + /// Convert error to a generic error kind. /// /// By using this method, errors freely defined by HAL implementations /// can be converted to a set of generic errors upon which generic @@ -14,12 +14,13 @@ pub trait Error: core::fmt::Debug { } impl Error for core::convert::Infallible { + #[inline] fn kind(&self) -> ErrorKind { match *self {} } } -/// Error kind +/// Error kind. /// /// This represents a common set of operation errors. HAL implementations are /// free to define more specific or additional error types. However, by providing @@ -33,12 +34,14 @@ pub enum ErrorKind { } impl Error for ErrorKind { + #[inline] fn kind(&self) -> ErrorKind { *self } } impl core::fmt::Display for ErrorKind { + #[inline] fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { match self { Self::Other => write!( @@ -49,7 +52,7 @@ impl core::fmt::Display for ErrorKind { } } -/// Error type trait +/// Error type trait. /// /// This just defines the error type, to be used by the other traits. pub trait ErrorType { @@ -61,7 +64,7 @@ impl ErrorType for &mut T { type Error = T::Error; } -/// Single PWM channel / pin +/// Single PWM channel / pin. pub trait SetDutyCycle: ErrorType { /// Get the maximum duty cycle value. /// @@ -106,26 +109,32 @@ pub trait SetDutyCycle: ErrorType { } impl SetDutyCycle for &mut T { + #[inline] fn get_max_duty_cycle(&self) -> u16 { T::get_max_duty_cycle(self) } + #[inline] fn set_duty_cycle(&mut self, duty: u16) -> Result<(), Self::Error> { T::set_duty_cycle(self, duty) } + #[inline] fn set_duty_cycle_fully_off(&mut self) -> Result<(), Self::Error> { T::set_duty_cycle_fully_off(self) } + #[inline] fn set_duty_cycle_fully_on(&mut self) -> Result<(), Self::Error> { T::set_duty_cycle_fully_on(self) } + #[inline] fn set_duty_cycle_fraction(&mut self, num: u16, denom: u16) -> Result<(), Self::Error> { T::set_duty_cycle_fraction(self, num, denom) } + #[inline] fn set_duty_cycle_percent(&mut self, percent: u8) -> Result<(), Self::Error> { T::set_duty_cycle_percent(self, percent) } diff --git a/embedded-hal/src/spi.rs b/embedded-hal/src/spi.rs index 3813b9a6c..e855aa47a 100644 --- a/embedded-hal/src/spi.rs +++ b/embedded-hal/src/spi.rs @@ -166,63 +166,63 @@ use core::fmt::Debug; #[cfg(feature = "defmt-03")] use crate::defmt; -/// Clock polarity +/// Clock polarity. #[derive(Clone, Copy, Debug, PartialEq, Eq)] #[cfg_attr(feature = "defmt-03", derive(defmt::Format))] pub enum Polarity { - /// Clock signal low when idle + /// Clock signal low when idle. IdleLow, - /// Clock signal high when idle + /// Clock signal high when idle. IdleHigh, } -/// Clock phase +/// Clock phase. #[derive(Clone, Copy, Debug, PartialEq, Eq)] #[cfg_attr(feature = "defmt-03", derive(defmt::Format))] pub enum Phase { - /// Data in "captured" on the first clock transition + /// Data in "captured" on the first clock transition. CaptureOnFirstTransition, - /// Data in "captured" on the second clock transition + /// Data in "captured" on the second clock transition. CaptureOnSecondTransition, } -/// SPI mode +/// SPI mode. #[derive(Clone, Copy, Debug, PartialEq, Eq)] #[cfg_attr(feature = "defmt-03", derive(defmt::Format))] pub struct Mode { - /// Clock polarity + /// Clock polarity. pub polarity: Polarity, - /// Clock phase + /// Clock phase. pub phase: Phase, } -/// Helper for CPOL = 0, CPHA = 0 +/// Helper for CPOL = 0, CPHA = 0. pub const MODE_0: Mode = Mode { polarity: Polarity::IdleLow, phase: Phase::CaptureOnFirstTransition, }; -/// Helper for CPOL = 0, CPHA = 1 +/// Helper for CPOL = 0, CPHA = 1. pub const MODE_1: Mode = Mode { polarity: Polarity::IdleLow, phase: Phase::CaptureOnSecondTransition, }; -/// Helper for CPOL = 1, CPHA = 0 +/// Helper for CPOL = 1, CPHA = 0. pub const MODE_2: Mode = Mode { polarity: Polarity::IdleHigh, phase: Phase::CaptureOnFirstTransition, }; -/// Helper for CPOL = 1, CPHA = 1 +/// Helper for CPOL = 1, CPHA = 1. pub const MODE_3: Mode = Mode { polarity: Polarity::IdleHigh, phase: Phase::CaptureOnSecondTransition, }; -/// SPI error +/// SPI error. pub trait Error: core::fmt::Debug { - /// Convert error to a generic SPI error kind + /// Convert error to a generic SPI error kind. /// /// By using this method, SPI errors freely defined by HAL implementations /// can be converted to a set of generic SPI errors upon which generic @@ -231,12 +231,13 @@ pub trait Error: core::fmt::Debug { } impl Error for core::convert::Infallible { + #[inline] fn kind(&self) -> ErrorKind { match *self {} } } -/// SPI error kind +/// SPI error kind. /// /// This represents a common set of SPI operation errors. HAL implementations are /// free to define more specific or additional error types. However, by providing @@ -245,11 +246,11 @@ impl Error for core::convert::Infallible { #[cfg_attr(feature = "defmt-03", derive(defmt::Format))] #[non_exhaustive] pub enum ErrorKind { - /// The peripheral receive buffer was overrun + /// The peripheral receive buffer was overrun. Overrun, - /// Multiple devices on the SPI bus are trying to drive the slave select pin, e.g. in a multi-master setup + /// Multiple devices on the SPI bus are trying to drive the slave select pin, e.g. in a multi-master setup. ModeFault, - /// Received data does not conform to the peripheral configuration + /// Received data does not conform to the peripheral configuration. FrameFormat, /// An error occurred while asserting or deasserting the Chip Select pin. ChipSelectFault, @@ -258,12 +259,14 @@ pub enum ErrorKind { } impl Error for ErrorKind { + #[inline] fn kind(&self) -> ErrorKind { *self } } impl core::fmt::Display for ErrorKind { + #[inline] fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { match self { Self::Overrun => write!(f, "The peripheral receive buffer was overrun"), @@ -287,11 +290,11 @@ impl core::fmt::Display for ErrorKind { } } -/// SPI error type trait +/// SPI error type trait. /// /// This just defines the error type, to be used by the other SPI traits. pub trait ErrorType { - /// Error type + /// Error type. type Error: Error; } @@ -301,7 +304,7 @@ impl ErrorType for &mut T { /// SPI transaction operation. /// -/// This allows composition of SPI operations into a single bus transaction +/// This allows composition of SPI operations into a single bus transaction. #[derive(Debug, PartialEq, Eq)] #[cfg_attr(feature = "defmt-03", derive(defmt::Format))] pub enum Operation<'a, Word: 'static> { @@ -309,7 +312,7 @@ pub enum Operation<'a, Word: 'static> { /// /// Equivalent to [`SpiBus::read`]. Read(&'a mut [Word]), - /// Write data from the provided buffer, discarding read data + /// Write data from the provided buffer, discarding read data. /// /// Equivalent to [`SpiBus::write`]. Write(&'a [Word]), @@ -317,15 +320,15 @@ pub enum Operation<'a, Word: 'static> { /// /// Equivalent to [`SpiBus::transfer`]. Transfer(&'a mut [Word], &'a [Word]), - /// Write data out while reading data into the provided buffer + /// Write data out while reading data into the provided buffer. /// /// Equivalent to [`SpiBus::transfer_in_place`]. TransferInPlace(&'a mut [Word]), - /// Delay for at least the specified number of microseconds + /// Delay for at least the specified number of microseconds. DelayUs(u32), } -/// SPI device trait +/// SPI device trait. /// /// `SpiDevice` represents ownership over a single SPI device on a (possibly shared) bus, selected /// with a CS (Chip Select) pin. @@ -354,6 +357,7 @@ pub trait SpiDevice: ErrorType { /// This is a convenience method equivalent to `device.transaction(&mut [Operation::Read(buf)])`. /// /// See also: [`SpiDevice::transaction`], [`SpiBus::read`] + #[inline] fn read(&mut self, buf: &mut [Word]) -> Result<(), Self::Error> { self.transaction(&mut [Operation::Read(buf)]) } @@ -363,6 +367,7 @@ pub trait SpiDevice: ErrorType { /// This is a convenience method equivalent to `device.transaction(&mut [Operation::Write(buf)])`. /// /// See also: [`SpiDevice::transaction`], [`SpiBus::write`] + #[inline] fn write(&mut self, buf: &[Word]) -> Result<(), Self::Error> { self.transaction(&mut [Operation::Write(buf)]) } @@ -372,6 +377,7 @@ pub trait SpiDevice: ErrorType { /// This is a convenience method equivalent to `device.transaction(&mut [Operation::Transfer(read, write)]`. /// /// See also: [`SpiDevice::transaction`], [`SpiBus::transfer`] + #[inline] fn transfer(&mut self, read: &mut [Word], write: &[Word]) -> Result<(), Self::Error> { self.transaction(&mut [Operation::Transfer(read, write)]) } @@ -381,34 +387,40 @@ pub trait SpiDevice: ErrorType { /// This is a convenience method equivalent to `device.transaction(&mut [Operation::TransferInPlace(buf)]`. /// /// See also: [`SpiDevice::transaction`], [`SpiBus::transfer_in_place`] + #[inline] fn transfer_in_place(&mut self, buf: &mut [Word]) -> Result<(), Self::Error> { self.transaction(&mut [Operation::TransferInPlace(buf)]) } } impl + ?Sized> SpiDevice for &mut T { + #[inline] fn transaction(&mut self, operations: &mut [Operation<'_, Word>]) -> Result<(), Self::Error> { T::transaction(self, operations) } + #[inline] fn read(&mut self, buf: &mut [Word]) -> Result<(), Self::Error> { T::read(self, buf) } + #[inline] fn write(&mut self, buf: &[Word]) -> Result<(), Self::Error> { T::write(self, buf) } + #[inline] fn transfer(&mut self, read: &mut [Word], write: &[Word]) -> Result<(), Self::Error> { T::transfer(self, read, write) } + #[inline] fn transfer_in_place(&mut self, buf: &mut [Word]) -> Result<(), Self::Error> { T::transfer_in_place(self, buf) } } -/// SPI bus +/// SPI bus. /// /// `SpiBus` represents **exclusive ownership** over the whole SPI bus, with SCK, MOSI and MISO pins. /// @@ -423,7 +435,7 @@ pub trait SpiBus: ErrorType { /// complete. See the [module-level documentation](self) for details. fn read(&mut self, words: &mut [Word]) -> Result<(), Self::Error>; - /// Write `words` to the slave, ignoring all the incoming words + /// Write `words` to the slave, ignoring all the incoming words. /// /// Implementations are allowed to return before the operation is /// complete. See the [module-level documentation](self) for details. @@ -457,22 +469,27 @@ pub trait SpiBus: ErrorType { } impl + ?Sized, Word: Copy + 'static> SpiBus for &mut T { + #[inline] fn read(&mut self, words: &mut [Word]) -> Result<(), Self::Error> { T::read(self, words) } + #[inline] fn write(&mut self, words: &[Word]) -> Result<(), Self::Error> { T::write(self, words) } + #[inline] fn transfer(&mut self, read: &mut [Word], write: &[Word]) -> Result<(), Self::Error> { T::transfer(self, read, write) } + #[inline] fn transfer_in_place(&mut self, words: &mut [Word]) -> Result<(), Self::Error> { T::transfer_in_place(self, words) } + #[inline] fn flush(&mut self) -> Result<(), Self::Error> { T::flush(self) } From 41a61cb00127709bf3333829388d2d650b7dcbd3 Mon Sep 17 00:00:00 2001 From: Zhouqi Jiang Date: Wed, 23 Aug 2023 10:16:09 +0800 Subject: [PATCH 084/199] e-h-async: document fixes and inline hints. Signed-off-by: Zhouqi Jiang --- embedded-hal-async/src/delay.rs | 6 ++++-- embedded-hal-async/src/digital.rs | 7 ++++++- embedded-hal-async/src/i2c.rs | 15 +++++++++++---- embedded-hal-async/src/spi.rs | 20 +++++++++++++++++--- 4 files changed, 38 insertions(+), 10 deletions(-) diff --git a/embedded-hal-async/src/delay.rs b/embedded-hal-async/src/delay.rs index acf18f2af..c1d57c8e6 100644 --- a/embedded-hal-async/src/delay.rs +++ b/embedded-hal-async/src/delay.rs @@ -1,6 +1,6 @@ -//! Delays +//! Delays. -/// Microsecond delay +/// Microsecond delay. pub trait DelayUs { /// Pauses execution for at minimum `us` microseconds. Pause can be longer /// if the implementation requires it due to precision/timing issues. @@ -15,10 +15,12 @@ impl DelayUs for &mut T where T: DelayUs + ?Sized, { + #[inline] async fn delay_us(&mut self, us: u32) { T::delay_us(self, us).await } + #[inline] async fn delay_ms(&mut self, ms: u32) { T::delay_ms(self, ms).await } diff --git a/embedded-hal-async/src/digital.rs b/embedded-hal-async/src/digital.rs index 8f09eb78d..4fb3843cb 100644 --- a/embedded-hal-async/src/digital.rs +++ b/embedded-hal-async/src/digital.rs @@ -1,4 +1,4 @@ -//! Asynchronous digital I/O +//! Asynchronous digital I/O. //! //! # Example //! @@ -49,22 +49,27 @@ pub trait Wait: embedded_hal::digital::ErrorType { } impl Wait for &mut T { + #[inline] async fn wait_for_high(&mut self) -> Result<(), Self::Error> { T::wait_for_high(self).await } + #[inline] async fn wait_for_low(&mut self) -> Result<(), Self::Error> { T::wait_for_low(self).await } + #[inline] async fn wait_for_rising_edge(&mut self) -> Result<(), Self::Error> { T::wait_for_rising_edge(self).await } + #[inline] async fn wait_for_falling_edge(&mut self) -> Result<(), Self::Error> { T::wait_for_falling_edge(self).await } + #[inline] async fn wait_for_any_edge(&mut self) -> Result<(), Self::Error> { T::wait_for_any_edge(self).await } diff --git a/embedded-hal-async/src/i2c.rs b/embedded-hal-async/src/i2c.rs index 0837da8dd..1a8f07a48 100644 --- a/embedded-hal-async/src/i2c.rs +++ b/embedded-hal-async/src/i2c.rs @@ -1,4 +1,4 @@ -//! Async I2C API +//! 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: @@ -21,9 +21,9 @@ pub use embedded_hal::i2c::{ AddressMode, Error, ErrorKind, ErrorType, NoAcknowledgeSource, SevenBitAddress, TenBitAddress, }; -/// Async i2c +/// Async I2c. pub trait I2c: ErrorType { - /// Reads enough bytes from slave with `address` to fill `buffer` + /// Reads enough bytes from slave with `address` to fill `buffer`. /// /// # I2C Events (contract) /// @@ -41,12 +41,13 @@ pub trait I2c: ErrorType { /// - `MAK` = master acknowledge /// - `NMAK` = master no acknowledge /// - `SP` = stop condition + #[inline] async fn read(&mut self, address: A, read: &mut [u8]) -> Result<(), Self::Error> { self.transaction(address, &mut [Operation::Read(read)]) .await } - /// Writes bytes to slave with address `address` + /// Writes bytes to slave with address `address`. /// /// # I2C Events (contract) /// @@ -62,6 +63,7 @@ pub trait I2c: ErrorType { /// - `SAK` = slave acknowledge /// - `Bi` = ith byte of data /// - `SP` = stop condition + #[inline] async fn write(&mut self, address: A, write: &[u8]) -> Result<(), Self::Error> { self.transaction(address, &mut [Operation::Write(write)]) .await @@ -89,6 +91,7 @@ pub trait I2c: ErrorType { /// - `MAK` = master acknowledge /// - `NMAK` = master no acknowledge /// - `SP` = stop condition + #[inline] async fn write_read( &mut self, address: A, @@ -123,14 +126,17 @@ pub trait I2c: ErrorType { } impl + ?Sized> I2c for &mut T { + #[inline] async fn read(&mut self, address: A, read: &mut [u8]) -> Result<(), Self::Error> { T::read(self, address, read).await } + #[inline] async fn write(&mut self, address: A, write: &[u8]) -> Result<(), Self::Error> { T::write(self, address, write).await } + #[inline] async fn write_read( &mut self, address: A, @@ -140,6 +146,7 @@ impl + ?Sized> I2c for &mut T { T::write_read(self, address, write, read).await } + #[inline] async fn transaction( &mut self, address: A, diff --git a/embedded-hal-async/src/spi.rs b/embedded-hal-async/src/spi.rs index e9548359f..9c71f7e0d 100644 --- a/embedded-hal-async/src/spi.rs +++ b/embedded-hal-async/src/spi.rs @@ -4,7 +4,7 @@ pub use embedded_hal::spi::{ Error, ErrorKind, ErrorType, Mode, Operation, Phase, Polarity, MODE_0, MODE_1, MODE_2, MODE_3, }; -/// SPI device trait +/// SPI device trait. /// /// `SpiDevice` represents ownership over a single SPI device on a (possibly shared) bus, selected /// with a CS (Chip Select) pin. @@ -36,6 +36,7 @@ pub trait SpiDevice: ErrorType { /// This is a convenience method equivalent to `device.read_transaction(&mut [buf])`. /// /// See also: [`SpiDevice::transaction`], [`SpiDevice::read`] + #[inline] async fn read(&mut self, buf: &mut [Word]) -> Result<(), Self::Error> { self.transaction(&mut [Operation::Read(buf)]).await } @@ -45,6 +46,7 @@ pub trait SpiDevice: ErrorType { /// This is a convenience method equivalent to `device.write_transaction(&mut [buf])`. /// /// See also: [`SpiDevice::transaction`], [`SpiDevice::write`] + #[inline] async fn write(&mut self, buf: &[Word]) -> Result<(), Self::Error> { self.transaction(&mut [Operation::Write(buf)]).await } @@ -54,6 +56,7 @@ pub trait SpiDevice: ErrorType { /// This is a convenience method equivalent to `device.transaction(|bus| bus.transfer(read, write))`. /// /// See also: [`SpiDevice::transaction`], [`SpiBus::transfer`] + #[inline] async fn transfer(&mut self, read: &mut [Word], write: &[Word]) -> Result<(), Self::Error> { self.transaction(&mut [Operation::Transfer(read, write)]) .await @@ -64,6 +67,7 @@ pub trait SpiDevice: ErrorType { /// This is a convenience method equivalent to `device.transaction(|bus| bus.transfer_in_place(buf))`. /// /// See also: [`SpiDevice::transaction`], [`SpiBus::transfer_in_place`] + #[inline] async fn transfer_in_place(&mut self, buf: &mut [Word]) -> Result<(), Self::Error> { self.transaction(&mut [Operation::TransferInPlace(buf)]) .await @@ -71,6 +75,7 @@ pub trait SpiDevice: ErrorType { } impl + ?Sized> SpiDevice for &mut T { + #[inline] async fn transaction( &mut self, operations: &mut [Operation<'_, Word>], @@ -78,24 +83,28 @@ impl + ?Sized> SpiDevice for &mut T::transaction(self, operations).await } + #[inline] async fn read(&mut self, buf: &mut [Word]) -> Result<(), Self::Error> { T::read(self, buf).await } + #[inline] async fn write(&mut self, buf: &[Word]) -> Result<(), Self::Error> { T::write(self, buf).await } + #[inline] async fn transfer(&mut self, read: &mut [Word], write: &[Word]) -> Result<(), Self::Error> { T::transfer(self, read, write).await } + #[inline] async fn transfer_in_place(&mut self, buf: &mut [Word]) -> Result<(), Self::Error> { T::transfer_in_place(self, buf).await } } -/// SPI bus +/// SPI bus. /// /// `SpiBus` represents **exclusive ownership** over the whole SPI bus, with SCK, MOSI and MISO pins. /// @@ -110,7 +119,7 @@ pub trait SpiBus: ErrorType { /// complete. See (the docs on embedded-hal)[embedded_hal::spi] for details on flushing. async fn read(&mut self, words: &mut [Word]) -> Result<(), Self::Error>; - /// Write `words` to the slave, ignoring all the incoming words + /// Write `words` to the slave, ignoring all the incoming words. /// /// Implementations are allowed to return before the operation is /// complete. See (the docs on embedded-hal)[embedded_hal::spi] for details on flushing. @@ -144,22 +153,27 @@ pub trait SpiBus: ErrorType { } impl + ?Sized, Word: 'static + Copy> SpiBus for &mut T { + #[inline] async fn read(&mut self, words: &mut [Word]) -> Result<(), Self::Error> { T::read(self, words).await } + #[inline] async fn write(&mut self, words: &[Word]) -> Result<(), Self::Error> { T::write(self, words).await } + #[inline] async fn transfer(&mut self, read: &mut [Word], write: &[Word]) -> Result<(), Self::Error> { T::transfer(self, read, write).await } + #[inline] async fn transfer_in_place(&mut self, words: &mut [Word]) -> Result<(), Self::Error> { T::transfer_in_place(self, words).await } + #[inline] async fn flush(&mut self) -> Result<(), Self::Error> { T::flush(self).await } From d81f7b8142966a8c65ce20b60fbef24fd787d05c Mon Sep 17 00:00:00 2001 From: Zhouqi Jiang Date: Wed, 23 Aug 2023 10:20:19 +0800 Subject: [PATCH 085/199] e-h-bus: document fixes and inline hints. Signed-off-by: Zhouqi Jiang --- embedded-hal-bus/src/i2c/critical_section.rs | 7 ++++++- embedded-hal-bus/src/i2c/mutex.rs | 7 ++++++- embedded-hal-bus/src/i2c/refcell.rs | 7 ++++++- embedded-hal-bus/src/spi/critical_section.rs | 5 ++++- embedded-hal-bus/src/spi/exclusive.rs | 8 +++++++- embedded-hal-bus/src/spi/mod.rs | 10 +++++++--- embedded-hal-bus/src/spi/mutex.rs | 5 ++++- embedded-hal-bus/src/spi/refcell.rs | 5 ++++- 8 files changed, 44 insertions(+), 10 deletions(-) diff --git a/embedded-hal-bus/src/i2c/critical_section.rs b/embedded-hal-bus/src/i2c/critical_section.rs index 32334cbcc..19ca73ee2 100644 --- a/embedded-hal-bus/src/i2c/critical_section.rs +++ b/embedded-hal-bus/src/i2c/critical_section.rs @@ -14,7 +14,8 @@ pub struct CriticalSectionDevice<'a, T> { } impl<'a, T> CriticalSectionDevice<'a, T> { - /// Create a new `CriticalSectionDevice` + /// Create a new `CriticalSectionDevice`. + #[inline] pub fn new(bus: &'a Mutex>) -> Self { Self { bus } } @@ -31,6 +32,7 @@ impl<'a, T> I2c for CriticalSectionDevice<'a, T> where T: I2c, { + #[inline] fn read(&mut self, address: u8, read: &mut [u8]) -> Result<(), Self::Error> { critical_section::with(|cs| { let bus = &mut *self.bus.borrow_ref_mut(cs); @@ -38,6 +40,7 @@ where }) } + #[inline] fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Self::Error> { critical_section::with(|cs| { let bus = &mut *self.bus.borrow_ref_mut(cs); @@ -45,6 +48,7 @@ where }) } + #[inline] fn write_read( &mut self, address: u8, @@ -57,6 +61,7 @@ where }) } + #[inline] fn transaction( &mut self, address: u8, diff --git a/embedded-hal-bus/src/i2c/mutex.rs b/embedded-hal-bus/src/i2c/mutex.rs index ff9da7b2a..3b95eec98 100644 --- a/embedded-hal-bus/src/i2c/mutex.rs +++ b/embedded-hal-bus/src/i2c/mutex.rs @@ -12,7 +12,8 @@ pub struct MutexDevice<'a, T> { } impl<'a, T> MutexDevice<'a, T> { - /// Create a new `MutexDevice` + /// Create a new `MutexDevice`. + #[inline] pub fn new(bus: &'a Mutex) -> Self { Self { bus } } @@ -29,16 +30,19 @@ impl<'a, T> I2c for MutexDevice<'a, T> where T: I2c, { + #[inline] fn read(&mut self, address: u8, read: &mut [u8]) -> Result<(), Self::Error> { let bus = &mut *self.bus.lock().unwrap(); bus.read(address, read) } + #[inline] fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Self::Error> { let bus = &mut *self.bus.lock().unwrap(); bus.write(address, write) } + #[inline] fn write_read( &mut self, address: u8, @@ -49,6 +53,7 @@ where bus.write_read(address, write, read) } + #[inline] fn transaction( &mut self, address: u8, diff --git a/embedded-hal-bus/src/i2c/refcell.rs b/embedded-hal-bus/src/i2c/refcell.rs index 3d6564434..1519a751b 100644 --- a/embedded-hal-bus/src/i2c/refcell.rs +++ b/embedded-hal-bus/src/i2c/refcell.rs @@ -68,7 +68,8 @@ pub struct RefCellDevice<'a, T> { } impl<'a, T> RefCellDevice<'a, T> { - /// Create a new `RefCellDevice` + /// Create a new `RefCellDevice`. + #[inline] pub fn new(bus: &'a RefCell) -> Self { Self { bus } } @@ -85,16 +86,19 @@ impl<'a, T> I2c for RefCellDevice<'a, T> where T: I2c, { + #[inline] fn read(&mut self, address: u8, read: &mut [u8]) -> Result<(), Self::Error> { let bus = &mut *self.bus.borrow_mut(); bus.read(address, read) } + #[inline] fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Self::Error> { let bus = &mut *self.bus.borrow_mut(); bus.write(address, write) } + #[inline] fn write_read( &mut self, address: u8, @@ -105,6 +109,7 @@ where bus.write_read(address, write, read) } + #[inline] fn transaction( &mut self, address: u8, diff --git a/embedded-hal-bus/src/spi/critical_section.rs b/embedded-hal-bus/src/spi/critical_section.rs index 2a4c09788..2a93323a6 100644 --- a/embedded-hal-bus/src/spi/critical_section.rs +++ b/embedded-hal-bus/src/spi/critical_section.rs @@ -23,7 +23,8 @@ pub struct CriticalSectionDevice<'a, BUS, CS, D> { } impl<'a, BUS, CS, D> CriticalSectionDevice<'a, BUS, CS, D> { - /// Create a new ExclusiveDevice + /// Create a new ExclusiveDevice. + #[inline] pub fn new(bus: &'a Mutex>, cs: CS, delay: D) -> Self { Self { bus, cs, delay } } @@ -36,6 +37,7 @@ impl<'a, BUS, CS> CriticalSectionDevice<'a, BUS, CS, super::NoDelay> { /// /// The returned device will panic if you try to execute a transaction /// that contains any operations of type `Operation::DelayUs`. + #[inline] pub fn new_no_delay(bus: &'a Mutex>, cs: CS) -> Self { Self { bus, @@ -59,6 +61,7 @@ where CS: OutputPin, D: DelayUs, { + #[inline] fn transaction(&mut self, operations: &mut [Operation<'_, Word>]) -> Result<(), Self::Error> { critical_section::with(|cs| { let bus = &mut *self.bus.borrow_ref_mut(cs); diff --git a/embedded-hal-bus/src/spi/exclusive.rs b/embedded-hal-bus/src/spi/exclusive.rs index da2381945..5d5c035d9 100644 --- a/embedded-hal-bus/src/spi/exclusive.rs +++ b/embedded-hal-bus/src/spi/exclusive.rs @@ -22,17 +22,20 @@ pub struct ExclusiveDevice { } impl ExclusiveDevice { - /// Create a new ExclusiveDevice + /// Create a new ExclusiveDevice. + #[inline] pub fn new(bus: BUS, cs: CS, delay: D) -> Self { Self { bus, cs, delay } } /// Returns a reference to the underlying bus object. + #[inline] pub fn bus(&self) -> &BUS { &self.bus } /// Returns a mutable reference to the underlying bus object. + #[inline] pub fn bus_mut(&mut self) -> &mut BUS { &mut self.bus } @@ -45,6 +48,7 @@ impl ExclusiveDevice { /// /// The returned device will panic if you try to execute a transaction /// that contains any operations of type `Operation::DelayUs`. + #[inline] pub fn new_no_delay(bus: BUS, cs: CS) -> Self { Self { bus, @@ -68,6 +72,7 @@ where CS: OutputPin, D: DelayUs, { + #[inline] fn transaction(&mut self, operations: &mut [Operation<'_, Word>]) -> Result<(), Self::Error> { self.cs.set_low().map_err(DeviceError::Cs)?; @@ -103,6 +108,7 @@ where CS: OutputPin, D: AsyncDelayUs, { + #[inline] async fn transaction( &mut self, operations: &mut [Operation<'_, Word>], diff --git a/embedded-hal-bus/src/spi/mod.rs b/embedded-hal-bus/src/spi/mod.rs index e3e888c8e..090f44649 100644 --- a/embedded-hal-bus/src/spi/mod.rs +++ b/embedded-hal-bus/src/spi/mod.rs @@ -21,9 +21,9 @@ use crate::defmt; #[derive(Copy, Clone, Eq, PartialEq, Debug)] #[cfg_attr(feature = "defmt-03", derive(defmt::Format))] pub enum DeviceError { - /// An inner SPI bus operation failed + /// An inner SPI bus operation failed. Spi(BUS), - /// Asserting or deasserting CS failed + /// Asserting or deasserting CS failed. Cs(CS), } @@ -32,6 +32,7 @@ where BUS: Error + Debug, CS: Debug, { + #[inline] fn kind(&self) -> ErrorKind { match self { Self::Spi(e) => e.kind(), @@ -51,6 +52,7 @@ fn no_delay_panic() { } impl embedded_hal::delay::DelayUs for NoDelay { + #[inline] fn delay_us(&mut self, _us: u32) { no_delay_panic(); } @@ -59,11 +61,13 @@ impl embedded_hal::delay::DelayUs for NoDelay { #[cfg(feature = "async")] #[cfg_attr(docsrs, doc(cfg(feature = "async")))] impl embedded_hal_async::delay::DelayUs for NoDelay { + #[inline] async fn delay_us(&mut self, _us: u32) { no_delay_panic(); } - async fn delay_ms(&mut self, _us: u32) { + #[inline] + async fn delay_ms(&mut self, _ms: u32) { no_delay_panic(); } } diff --git a/embedded-hal-bus/src/spi/mutex.rs b/embedded-hal-bus/src/spi/mutex.rs index 7030ca02c..b37c975ac 100644 --- a/embedded-hal-bus/src/spi/mutex.rs +++ b/embedded-hal-bus/src/spi/mutex.rs @@ -21,7 +21,8 @@ pub struct MutexDevice<'a, BUS, CS, D> { } impl<'a, BUS, CS, D> MutexDevice<'a, BUS, CS, D> { - /// Create a new ExclusiveDevice + /// Create a new MutexDevice. + #[inline] pub fn new(bus: &'a Mutex, cs: CS, delay: D) -> Self { Self { bus, cs, delay } } @@ -34,6 +35,7 @@ impl<'a, BUS, CS> MutexDevice<'a, BUS, CS, super::NoDelay> { /// /// The returned device will panic if you try to execute a transaction /// that contains any operations of type `Operation::DelayUs`. + #[inline] pub fn new_no_delay(bus: &'a Mutex, cs: CS) -> Self { Self { bus, @@ -57,6 +59,7 @@ where CS: OutputPin, D: DelayUs, { + #[inline] fn transaction(&mut self, operations: &mut [Operation<'_, Word>]) -> Result<(), Self::Error> { let bus = &mut *self.bus.lock().unwrap(); diff --git a/embedded-hal-bus/src/spi/refcell.rs b/embedded-hal-bus/src/spi/refcell.rs index a08e8a262..6524b8a93 100644 --- a/embedded-hal-bus/src/spi/refcell.rs +++ b/embedded-hal-bus/src/spi/refcell.rs @@ -20,7 +20,8 @@ pub struct RefCellDevice<'a, BUS, CS, D> { } impl<'a, BUS, CS, D> RefCellDevice<'a, BUS, CS, D> { - /// Create a new ExclusiveDevice + /// Create a new RefCellDevice. + #[inline] pub fn new(bus: &'a RefCell, cs: CS, delay: D) -> Self { Self { bus, cs, delay } } @@ -33,6 +34,7 @@ impl<'a, BUS, CS> RefCellDevice<'a, BUS, CS, super::NoDelay> { /// /// The returned device will panic if you try to execute a transaction /// that contains any operations of type `Operation::DelayUs`. + #[inline] pub fn new_no_delay(bus: &'a RefCell, cs: CS) -> Self { Self { bus, @@ -56,6 +58,7 @@ where CS: OutputPin, D: DelayUs, { + #[inline] fn transaction(&mut self, operations: &mut [Operation<'_, Word>]) -> Result<(), Self::Error> { let bus = &mut *self.bus.borrow_mut(); From 8296af2e29b320aa8c041618ca078342e71b3b6c Mon Sep 17 00:00:00 2001 From: Zhouqi Jiang Date: Wed, 23 Aug 2023 10:22:20 +0800 Subject: [PATCH 086/199] e-h-nb: inline hints, document fixes. Signed-off-by: Zhouqi Jiang --- embedded-hal-nb/src/serial.rs | 23 +++++++++++++++-------- embedded-hal-nb/src/spi.rs | 6 ++++-- 2 files changed, 19 insertions(+), 10 deletions(-) diff --git a/embedded-hal-nb/src/serial.rs b/embedded-hal-nb/src/serial.rs index 624aadee3..18d1265c2 100644 --- a/embedded-hal-nb/src/serial.rs +++ b/embedded-hal-nb/src/serial.rs @@ -1,6 +1,6 @@ -//! Serial interface +//! Serial interface. -/// Serial error +/// Serial error. pub trait Error: core::fmt::Debug { /// Convert error to a generic serial error kind /// @@ -11,12 +11,13 @@ pub trait Error: core::fmt::Debug { } impl Error for core::convert::Infallible { + #[inline] fn kind(&self) -> ErrorKind { match *self {} } } -/// Serial error kind +/// Serial error kind. /// /// This represents a common set of serial operation errors. HAL implementations are /// free to define more specific or additional error types. However, by providing @@ -38,12 +39,14 @@ pub enum ErrorKind { } impl Error for ErrorKind { + #[inline] fn kind(&self) -> ErrorKind { *self } } impl core::fmt::Display for ErrorKind { + #[inline] fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { match self { Self::Overrun => write!(f, "The peripheral receive buffer was overrun"), @@ -61,7 +64,7 @@ impl core::fmt::Display for ErrorKind { } } -/// Serial error type trait +/// Serial error type trait. /// /// This just defines the error type, to be used by the other traits. pub trait ErrorType { @@ -73,7 +76,7 @@ impl ErrorType for &mut T { type Error = T::Error; } -/// Read half of a serial interface +/// 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. @@ -83,25 +86,28 @@ pub trait Read: ErrorType { } impl + ?Sized, Word: Copy> Read for &mut T { + #[inline] fn read(&mut self) -> nb::Result { T::read(self) } } -/// Write half of a serial interface +/// Write half of a serial interface. pub trait Write: ErrorType { - /// Writes a single word to the serial interface + /// Writes a single word to the serial interface. fn write(&mut self, word: Word) -> nb::Result<(), Self::Error>; - /// Ensures that none of the previously written words are still buffered + /// Ensures that none of the previously written words are still buffered. fn flush(&mut self) -> nb::Result<(), Self::Error>; } impl + ?Sized, Word: Copy> Write for &mut T { + #[inline] fn write(&mut self, word: Word) -> nb::Result<(), Self::Error> { T::write(self, word) } + #[inline] fn flush(&mut self) -> nb::Result<(), Self::Error> { T::flush(self) } @@ -115,6 +121,7 @@ impl core::fmt::Write for dyn Write, { + #[inline] fn write_str(&mut self, s: &str) -> core::fmt::Result { let _ = s .bytes() diff --git a/embedded-hal-nb/src/spi.rs b/embedded-hal-nb/src/spi.rs index ecdb88ffb..06689db4c 100644 --- a/embedded-hal-nb/src/spi.rs +++ b/embedded-hal-nb/src/spi.rs @@ -4,11 +4,11 @@ pub use embedded_hal::spi::{ Error, ErrorKind, ErrorType, Mode, Phase, Polarity, MODE_0, MODE_1, MODE_2, MODE_3, }; -/// Full duplex SPI (master mode) +/// Full duplex SPI (master mode). /// /// # Notes /// -/// - It's the task of the user of this interface to manage the slave select lines +/// - 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. /// @@ -32,10 +32,12 @@ pub trait FullDuplex: ErrorType { } impl + ?Sized, Word: Copy> FullDuplex for &mut T { + #[inline] fn read(&mut self) -> nb::Result { T::read(self) } + #[inline] fn write(&mut self, word: Word) -> nb::Result<(), Self::Error> { T::write(self, word) } From d2f099e54fba531987207f39482137709a0953ba Mon Sep 17 00:00:00 2001 From: Zhouqi Jiang Date: Wed, 23 Aug 2023 10:26:47 +0800 Subject: [PATCH 087/199] e-h, e-h-async, e-h-bus, e-h-nb: add changelog entries Signed-off-by: Zhouqi Jiang --- embedded-hal-async/CHANGELOG.md | 3 +++ embedded-hal-bus/CHANGELOG.md | 3 +++ embedded-hal-nb/CHANGELOG.md | 3 ++- embedded-hal/CHANGELOG.md | 3 +++ 4 files changed, 11 insertions(+), 1 deletion(-) diff --git a/embedded-hal-async/CHANGELOG.md b/embedded-hal-async/CHANGELOG.md index 478e994f7..c94153a3e 100644 --- a/embedded-hal-async/CHANGELOG.md +++ b/embedded-hal-async/CHANGELOG.md @@ -7,6 +7,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] +- Minor document fixes. +- Add #[inline] hints to most of `embedded-hal-async` functions. + ## [v1.0.0-rc.1] - 2023-08-15 - Updated `embedded-hal` to version `1.0.0-rc.1`. diff --git a/embedded-hal-bus/CHANGELOG.md b/embedded-hal-bus/CHANGELOG.md index ead1de58f..59b398d1a 100644 --- a/embedded-hal-bus/CHANGELOG.md +++ b/embedded-hal-bus/CHANGELOG.md @@ -7,6 +7,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] +- Minor document fixes. +- Add #[inline] hints to most of `embedded-hal-bus` functions. + ## [v0.1.0-rc.1] - 2023-08-15 - Updated `embedded-hal`, `embedded-hal-async` to version `1.0.0-rc.1`. diff --git a/embedded-hal-nb/CHANGELOG.md b/embedded-hal-nb/CHANGELOG.md index 83b7e1510..f4a1e7944 100644 --- a/embedded-hal-nb/CHANGELOG.md +++ b/embedded-hal-nb/CHANGELOG.md @@ -7,7 +7,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] -... +- Minor document fixes. +- Add #[inline] hints to most of `embedded-hal-nb` functions. ## [v1.0.0-rc.1] - 2023-08-15 diff --git a/embedded-hal/CHANGELOG.md b/embedded-hal/CHANGELOG.md index 62f78ff36..8c6109d6c 100644 --- a/embedded-hal/CHANGELOG.md +++ b/embedded-hal/CHANGELOG.md @@ -7,6 +7,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] +- Minor document fixes. +- Add #[inline] hints to most of `embedded-hal` functions. + ## [v1.0.0-rc.1] - 2023-08-15 - The Minimum Supported Rust Version (MSRV) is now 1.60.0 From 2c9db0d682b37c931ffdd011d1c81cf2ebcc1976 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Fri, 25 Aug 2023 01:11:15 +0200 Subject: [PATCH 088/199] io-adapters: add support for BufRead for `futures` and `tokio`. --- embedded-io-adapters/CHANGELOG.md | 4 ++++ embedded-io-adapters/Cargo.toml | 2 +- embedded-io-adapters/src/futures_03.rs | 12 ++++++++++++ embedded-io-adapters/src/tokio_1.rs | 12 ++++++++++++ 4 files changed, 29 insertions(+), 1 deletion(-) diff --git a/embedded-io-adapters/CHANGELOG.md b/embedded-io-adapters/CHANGELOG.md index 978a92d8b..bf9f2d391 100644 --- a/embedded-io-adapters/CHANGELOG.md +++ b/embedded-io-adapters/CHANGELOG.md @@ -5,6 +5,10 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## Unreleased + +- Add support for adapting `BufRead` from `futures` and `tokio`. + ## 0.5.0 - 2023-08-06 - First release \ No newline at end of file diff --git a/embedded-io-adapters/Cargo.toml b/embedded-io-adapters/Cargo.toml index 5dbcf4354..a849b1f12 100644 --- a/embedded-io-adapters/Cargo.toml +++ b/embedded-io-adapters/Cargo.toml @@ -21,7 +21,7 @@ embedded-io = { version = "0.5", path = "../embedded-io" } embedded-io-async = { version = "0.5", path = "../embedded-io-async", optional = true } futures = { version = "0.3.21", features = ["std"], default-features = false, optional = true } -tokio = { version = "1", default-features = false, optional = true } +tokio = { version = "1", features = ["io-util"], default-features = false, optional = true } [package.metadata.docs.rs] features = ["std", "tokio-1", "futures-03"] diff --git a/embedded-io-adapters/src/futures_03.rs b/embedded-io-adapters/src/futures_03.rs index b672215ed..a3402cbb9 100644 --- a/embedded-io-adapters/src/futures_03.rs +++ b/embedded-io-adapters/src/futures_03.rs @@ -3,6 +3,8 @@ use core::future::poll_fn; use core::pin::Pin; +use futures::AsyncBufReadExt; + /// Adapter from `futures::io` traits. #[derive(Clone)] pub struct FromFutures { @@ -43,6 +45,16 @@ impl embedded_io_async::Read for Fro } } +impl embedded_io_async::BufRead for FromFutures { + async fn fill_buf(&mut self) -> Result<&[u8], Self::Error> { + self.inner.fill_buf().await + } + + fn consume(&mut self, amt: usize) { + Pin::new(&mut self.inner).consume(amt) + } +} + impl embedded_io_async::Write for FromFutures { async fn write(&mut self, buf: &[u8]) -> Result { poll_fn(|cx| Pin::new(&mut self.inner).poll_write(cx, buf)).await diff --git a/embedded-io-adapters/src/tokio_1.rs b/embedded-io-adapters/src/tokio_1.rs index 04e1cf1aa..92060a4fe 100644 --- a/embedded-io-adapters/src/tokio_1.rs +++ b/embedded-io-adapters/src/tokio_1.rs @@ -4,6 +4,8 @@ use core::future::poll_fn; use core::pin::Pin; use core::task::Poll; +use tokio::io::AsyncBufReadExt; + /// Adapter from `tokio::io` traits. #[derive(Clone)] pub struct FromTokio { @@ -54,6 +56,16 @@ impl embedded_io_async::Read for FromT } } +impl embedded_io_async::BufRead for FromTokio { + async fn fill_buf(&mut self) -> Result<&[u8], Self::Error> { + self.inner.fill_buf().await + } + + fn consume(&mut self, amt: usize) { + Pin::new(&mut self.inner).consume(amt) + } +} + impl embedded_io_async::Write for FromTokio { async fn write(&mut self, buf: &[u8]) -> Result { poll_fn(|cx| Pin::new(&mut self.inner).poll_write(cx, buf)).await From 10a7effe167f4d0134de3410aefb293b14b63801 Mon Sep 17 00:00:00 2001 From: Yuri Astrakhan Date: Thu, 31 Aug 2023 23:10:52 -0400 Subject: [PATCH 089/199] Inline a few format args for readability This existed since 1.58, so safe for MSRV --- embedded-io-adapters/src/std.rs | 2 +- embedded-io/src/lib.rs | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/embedded-io-adapters/src/std.rs b/embedded-io-adapters/src/std.rs index ac6a606d3..cd3df2d75 100644 --- a/embedded-io-adapters/src/std.rs +++ b/embedded-io-adapters/src/std.rs @@ -118,5 +118,5 @@ impl std::io::Seek for ToStd { /// Convert a embedded-io error to a std::io::Error pub fn to_std_error(err: T) -> std::io::Error { - std::io::Error::new(err.kind().into(), format!("{:?}", err)) + std::io::Error::new(err.kind().into(), format!("{err:?}")) } diff --git a/embedded-io/src/lib.rs b/embedded-io/src/lib.rs index f798b5b6e..b0867ed62 100644 --- a/embedded-io/src/lib.rs +++ b/embedded-io/src/lib.rs @@ -243,7 +243,7 @@ impl From> for std::io::Error { std::io::ErrorKind::UnexpectedEof, "UnexpectedEof".to_owned(), ), - ReadExactError::Other(e) => std::io::Error::new(e.kind(), format!("{:?}", e)), + ReadExactError::Other(e) => std::io::Error::new(e.kind(), format!("{e:?}")), } } } @@ -256,14 +256,14 @@ impl From> for std::io::Error { WriteAllError::WriteZero => { std::io::Error::new(std::io::ErrorKind::WriteZero, "WriteZero".to_owned()) } - WriteAllError::Other(e) => std::io::Error::new(e.kind(), format!("{:?}", e)), + WriteAllError::Other(e) => std::io::Error::new(e.kind(), format!("{e:?}")), } } } impl fmt::Display for ReadExactError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{:?}", self) + write!(f, "{self:?}") } } @@ -291,7 +291,7 @@ impl From for WriteFmtError { impl fmt::Display for WriteFmtError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{:?}", self) + write!(f, "{self:?}") } } @@ -317,7 +317,7 @@ impl From for WriteAllError { impl fmt::Display for WriteAllError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{:?}", self) + write!(f, "{self:?}") } } From 7bcaa871d63f4c38e7815248361d8e8eee14f8c0 Mon Sep 17 00:00:00 2001 From: Yuri Astrakhan Date: Thu, 31 Aug 2023 23:59:18 -0400 Subject: [PATCH 090/199] Fix doc links and one incorrect type name --- embedded-hal-async/src/spi.rs | 14 +++++++------- embedded-hal-bus/src/spi/critical_section.rs | 6 +++--- embedded-hal-bus/src/spi/exclusive.rs | 4 ++-- embedded-hal-bus/src/spi/refcell.rs | 4 ++-- embedded-hal-nb/src/lib.rs | 2 +- 5 files changed, 15 insertions(+), 15 deletions(-) diff --git a/embedded-hal-async/src/spi.rs b/embedded-hal-async/src/spi.rs index 9c71f7e0d..38d471d9a 100644 --- a/embedded-hal-async/src/spi.rs +++ b/embedded-hal-async/src/spi.rs @@ -9,7 +9,7 @@ pub use embedded_hal::spi::{ /// `SpiDevice` represents ownership over a single SPI device on a (possibly shared) bus, selected /// with a CS (Chip Select) pin. /// -/// See (the docs on embedded-hal)[embedded_hal::spi] for important information on SPI Bus vs Device traits. +/// See [the docs on embedded-hal](embedded_hal::spi) for important information on SPI Bus vs Device traits. pub trait SpiDevice: ErrorType { /// Perform a transaction against the device. /// @@ -108,7 +108,7 @@ impl + ?Sized> SpiDevice for &mut /// /// `SpiBus` represents **exclusive ownership** over the whole SPI bus, with SCK, MOSI and MISO pins. /// -/// See (the docs on embedded-hal)[embedded_hal::spi] for important information on SPI Bus vs Device traits. +/// See [the docs on embedded-hal][embedded_hal::spi] for important information on SPI Bus vs Device traits. pub trait SpiBus: ErrorType { /// Read `words` from the slave. /// @@ -116,13 +116,13 @@ pub trait SpiBus: ErrorType { /// typically `0x00`, `0xFF`, or configurable. /// /// Implementations are allowed to return before the operation is - /// complete. See (the docs on embedded-hal)[embedded_hal::spi] for details on flushing. + /// complete. See [the docs on embedded-hal][embedded_hal::spi] for details on flushing. async fn read(&mut self, words: &mut [Word]) -> Result<(), Self::Error>; /// Write `words` to the slave, ignoring all the incoming words. /// /// Implementations are allowed to return before the operation is - /// complete. See (the docs on embedded-hal)[embedded_hal::spi] for details on flushing. + /// complete. See [the docs on embedded-hal][embedded_hal::spi] for details on flushing. async fn write(&mut self, words: &[Word]) -> Result<(), Self::Error>; /// Write and read simultaneously. `write` is written to the slave on MOSI and @@ -135,7 +135,7 @@ pub trait SpiBus: ErrorType { /// typically `0x00`, `0xFF`, or configurable. /// /// Implementations are allowed to return before the operation is - /// complete. See (the docs on embedded-hal)[embedded_hal::spi] for details on flushing. + /// complete. See [the docs on embedded-hal][embedded_hal::spi] for details on flushing. async fn transfer(&mut self, read: &mut [Word], write: &[Word]) -> Result<(), Self::Error>; /// Write and read simultaneously. The contents of `words` are @@ -143,12 +143,12 @@ pub trait SpiBus: ErrorType { /// `words` buffer, overwriting it. /// /// Implementations are allowed to return before the operation is - /// complete. See (the docs on embedded-hal)[embedded_hal::spi] for details on flushing. + /// complete. See [the docs on embedded-hal][embedded_hal::spi] for details on flushing. async fn transfer_in_place(&mut self, words: &mut [Word]) -> Result<(), Self::Error>; /// Wait until all operations have completed and the bus is idle. /// - /// See (the docs on embedded-hal)[embedded_hal::spi] for information on flushing. + /// See [the docs on embedded-hal][embedded_hal::spi] for information on flushing. async fn flush(&mut self) -> Result<(), Self::Error>; } diff --git a/embedded-hal-bus/src/spi/critical_section.rs b/embedded-hal-bus/src/spi/critical_section.rs index 2a93323a6..baf080167 100644 --- a/embedded-hal-bus/src/spi/critical_section.rs +++ b/embedded-hal-bus/src/spi/critical_section.rs @@ -23,7 +23,7 @@ pub struct CriticalSectionDevice<'a, BUS, CS, D> { } impl<'a, BUS, CS, D> CriticalSectionDevice<'a, BUS, CS, D> { - /// Create a new ExclusiveDevice. + /// Create a new [`CriticalSectionDevice`]. #[inline] pub fn new(bus: &'a Mutex>, cs: CS, delay: D) -> Self { Self { bus, cs, delay } @@ -31,12 +31,12 @@ impl<'a, BUS, CS, D> CriticalSectionDevice<'a, BUS, CS, D> { } impl<'a, BUS, CS> CriticalSectionDevice<'a, BUS, CS, super::NoDelay> { - /// Create a new CriticalSectionDevice without support for in-transaction delays. + /// Create a new [`CriticalSectionDevice`] without support for in-transaction delays. /// /// # Panics /// /// The returned device will panic if you try to execute a transaction - /// that contains any operations of type `Operation::DelayUs`. + /// that contains any operations of type [`Operation::DelayUs`]. #[inline] pub fn new_no_delay(bus: &'a Mutex>, cs: CS) -> Self { Self { diff --git a/embedded-hal-bus/src/spi/exclusive.rs b/embedded-hal-bus/src/spi/exclusive.rs index 5d5c035d9..a8266e296 100644 --- a/embedded-hal-bus/src/spi/exclusive.rs +++ b/embedded-hal-bus/src/spi/exclusive.rs @@ -22,7 +22,7 @@ pub struct ExclusiveDevice { } impl ExclusiveDevice { - /// Create a new ExclusiveDevice. + /// Create a new [`ExclusiveDevice`]. #[inline] pub fn new(bus: BUS, cs: CS, delay: D) -> Self { Self { bus, cs, delay } @@ -42,7 +42,7 @@ impl ExclusiveDevice { } impl ExclusiveDevice { - /// Create a new ExclusiveDevice without support for in-transaction delays. + /// Create a new [`ExclusiveDevice`] without support for in-transaction delays. /// /// # Panics /// diff --git a/embedded-hal-bus/src/spi/refcell.rs b/embedded-hal-bus/src/spi/refcell.rs index 6524b8a93..021852080 100644 --- a/embedded-hal-bus/src/spi/refcell.rs +++ b/embedded-hal-bus/src/spi/refcell.rs @@ -20,7 +20,7 @@ pub struct RefCellDevice<'a, BUS, CS, D> { } impl<'a, BUS, CS, D> RefCellDevice<'a, BUS, CS, D> { - /// Create a new RefCellDevice. + /// Create a new [`RefCellDevice`]. #[inline] pub fn new(bus: &'a RefCell, cs: CS, delay: D) -> Self { Self { bus, cs, delay } @@ -28,7 +28,7 @@ impl<'a, BUS, CS, D> RefCellDevice<'a, BUS, CS, D> { } impl<'a, BUS, CS> RefCellDevice<'a, BUS, CS, super::NoDelay> { - /// Create a new RefCellDevice without support for in-transaction delays. + /// Create a new [`RefCellDevice`] without support for in-transaction delays. /// /// # Panics /// diff --git a/embedded-hal-nb/src/lib.rs b/embedded-hal-nb/src/lib.rs index d2089c5af..4cdf90dc1 100644 --- a/embedded-hal-nb/src/lib.rs +++ b/embedded-hal-nb/src/lib.rs @@ -57,7 +57,7 @@ //! [`svd2rust`]: https://crates.io/crates/svd2rust //! //! Shown below is an implementation of some of the HAL traits for the [`stm32f1xx-hal`] crate. This -//! single implementation will work for *any* microcontroller in the STM32F1xx family. +//! single implementation will work for *any* microcontroller in the `STM32F1xx` family. //! //! [`stm32f1`]: https://crates.io/crates/stm32f1 //! From 3005247c9bbe3da70554f12a388adeca13e1c593 Mon Sep 17 00:00:00 2001 From: Yuri Astrakhan Date: Fri, 1 Sep 2023 00:03:36 -0400 Subject: [PATCH 091/199] A few clippy lint fixes * added `[must_use]` * added a few semicolons --- embedded-can/src/id.rs | 7 +++++++ embedded-hal-async/src/delay.rs | 4 ++-- embedded-hal/src/delay.rs | 4 ++-- embedded-io-async/src/lib.rs | 8 ++++---- embedded-io/src/lib.rs | 8 ++++---- 5 files changed, 19 insertions(+), 12 deletions(-) diff --git a/embedded-can/src/id.rs b/embedded-can/src/id.rs index 6fb377190..2774b6c3d 100644 --- a/embedded-can/src/id.rs +++ b/embedded-can/src/id.rs @@ -15,6 +15,7 @@ impl StandardId { /// /// This will return `None` if `raw` is out of range of an 11-bit integer (`> 0x7FF`). #[inline] + #[must_use] pub const fn new(raw: u16) -> Option { if raw <= 0x7FF { Some(Self(raw)) @@ -28,12 +29,14 @@ impl StandardId { /// # Safety /// Using this method can create an invalid ID and is thus marked as unsafe. #[inline] + #[must_use] pub const unsafe fn new_unchecked(raw: u16) -> Self { Self(raw) } /// Returns this CAN Identifier as a raw 16-bit integer. #[inline] + #[must_use] pub const fn as_raw(&self) -> u16 { self.0 } @@ -54,6 +57,7 @@ impl ExtendedId { /// /// This will return `None` if `raw` is out of range of an 29-bit integer (`> 0x1FFF_FFFF`). #[inline] + #[must_use] pub const fn new(raw: u32) -> Option { if raw <= 0x1FFF_FFFF { Some(Self(raw)) @@ -67,17 +71,20 @@ impl ExtendedId { /// # Safety /// Using this method can create an invalid ID and is thus marked as unsafe. #[inline] + #[must_use] pub const unsafe fn new_unchecked(raw: u32) -> Self { Self(raw) } /// Returns this CAN Identifier as a raw 32-bit integer. #[inline] + #[must_use] pub const fn as_raw(&self) -> u32 { self.0 } /// Returns the Base ID part of this extended identifier. + #[must_use] pub fn standard_id(&self) -> StandardId { // ID-28 to ID-18 StandardId((self.0 >> 18) as u16) diff --git a/embedded-hal-async/src/delay.rs b/embedded-hal-async/src/delay.rs index c1d57c8e6..62bd742f2 100644 --- a/embedded-hal-async/src/delay.rs +++ b/embedded-hal-async/src/delay.rs @@ -17,11 +17,11 @@ where { #[inline] async fn delay_us(&mut self, us: u32) { - T::delay_us(self, us).await + T::delay_us(self, us).await; } #[inline] async fn delay_ms(&mut self, ms: u32) { - T::delay_ms(self, ms).await + T::delay_ms(self, ms).await; } } diff --git a/embedded-hal/src/delay.rs b/embedded-hal/src/delay.rs index 32f085729..77b5633d7 100644 --- a/embedded-hal/src/delay.rs +++ b/embedded-hal/src/delay.rs @@ -22,11 +22,11 @@ where { #[inline] fn delay_us(&mut self, us: u32) { - T::delay_us(self, us) + T::delay_us(self, us); } #[inline] fn delay_ms(&mut self, ms: u32) { - T::delay_ms(self, ms) + T::delay_ms(self, ms); } } diff --git a/embedded-io-async/src/lib.rs b/embedded-io-async/src/lib.rs index c94e349a4..98a656be8 100644 --- a/embedded-io-async/src/lib.rs +++ b/embedded-io-async/src/lib.rs @@ -58,10 +58,10 @@ pub trait Read: ErrorType { Err(e) => return Err(ReadExactError::Other(e)), } } - if !buf.is_empty() { - Err(ReadExactError::UnexpectedEof) - } else { + if buf.is_empty() { Ok(()) + } else { + Err(ReadExactError::UnexpectedEof) } } } @@ -170,7 +170,7 @@ impl BufRead for &mut T { } fn consume(&mut self, amt: usize) { - T::consume(self, amt) + T::consume(self, amt); } } diff --git a/embedded-io/src/lib.rs b/embedded-io/src/lib.rs index f798b5b6e..28a496743 100644 --- a/embedded-io/src/lib.rs +++ b/embedded-io/src/lib.rs @@ -362,10 +362,10 @@ pub trait Read: ErrorType { Err(e) => return Err(ReadExactError::Other(e)), } } - if !buf.is_empty() { - Err(ReadExactError::UnexpectedEof) - } else { + if buf.is_empty() { Ok(()) + } else { + Err(ReadExactError::UnexpectedEof) } } } @@ -535,7 +535,7 @@ impl BufRead for &mut T { } fn consume(&mut self, amt: usize) { - T::consume(self, amt) + T::consume(self, amt); } } From 4e18fc0d6f74c2d641c9f73436c619dd8ce2fcd5 Mon Sep 17 00:00:00 2001 From: Yuri Astrakhan Date: Fri, 1 Sep 2023 00:22:41 -0400 Subject: [PATCH 092/199] Use explicit lossless conversion functions Per Clippy, [cast_lossless](https://rust-lang.github.io/rust-clippy/master/index.html#/cast_lossless) is preferred --- embedded-hal/src/pwm.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/embedded-hal/src/pwm.rs b/embedded-hal/src/pwm.rs index 7f3b0828c..e97a93548 100644 --- a/embedded-hal/src/pwm.rs +++ b/embedded-hal/src/pwm.rs @@ -95,7 +95,7 @@ pub trait SetDutyCycle: ErrorType { /// and that `denom` is not zero. #[inline] fn set_duty_cycle_fraction(&mut self, num: u16, denom: u16) -> Result<(), Self::Error> { - let duty = num as u32 * self.get_max_duty_cycle() as u32 / denom as u32; + let duty = u32::from(num) * u32::from(self.get_max_duty_cycle()) / u32::from(denom); self.set_duty_cycle(duty as u16) } @@ -104,7 +104,7 @@ pub trait SetDutyCycle: ErrorType { /// The caller is responsible for ensuring that `percent` is less than or equal to 100. #[inline] fn set_duty_cycle_percent(&mut self, percent: u8) -> Result<(), Self::Error> { - self.set_duty_cycle_fraction(percent as u16, 100) + self.set_duty_cycle_fraction(u16::from(percent), 100) } } From b0b4f4668db1e8ab5fc0db728ffed8d66697107d Mon Sep 17 00:00:00 2001 From: Yuri Astrakhan Date: Fri, 1 Sep 2023 01:25:34 -0400 Subject: [PATCH 093/199] A few more doc cleanups --- embedded-hal-bus/src/i2c/critical_section.rs | 2 +- embedded-hal-bus/src/i2c/mutex.rs | 2 +- embedded-hal-bus/src/spi/critical_section.rs | 4 ++-- embedded-hal-bus/src/spi/exclusive.rs | 2 +- embedded-hal-bus/src/spi/mutex.rs | 4 ++-- embedded-hal-bus/src/spi/refcell.rs | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/embedded-hal-bus/src/i2c/critical_section.rs b/embedded-hal-bus/src/i2c/critical_section.rs index 19ca73ee2..6830f284f 100644 --- a/embedded-hal-bus/src/i2c/critical_section.rs +++ b/embedded-hal-bus/src/i2c/critical_section.rs @@ -4,7 +4,7 @@ use embedded_hal::i2c::{ErrorType, I2c}; /// `critical-section`-based shared bus [`I2c`] implementation. /// -/// Sharing is implemented with a `critical-section` [`Mutex`](critical_section::Mutex). A critical section is taken for +/// Sharing is implemented with a `critical-section` [`Mutex`]. A critical section is taken for /// the entire duration of a transaction. This allows sharing a single bus across multiple threads (interrupt priority levels). /// The downside is critical sections typically require globally disabling interrupts, so `CriticalSectionDevice` will likely /// negatively impact real-time properties, such as interrupt latency. If you can, prefer using diff --git a/embedded-hal-bus/src/i2c/mutex.rs b/embedded-hal-bus/src/i2c/mutex.rs index 3b95eec98..ef4a9f510 100644 --- a/embedded-hal-bus/src/i2c/mutex.rs +++ b/embedded-hal-bus/src/i2c/mutex.rs @@ -3,7 +3,7 @@ use std::sync::Mutex; /// `std` `Mutex`-based shared bus [`I2c`] implementation. /// -/// Sharing is implemented with an `std` [`Mutex`](std::sync::Mutex). It allows a single bus across multiple threads, +/// Sharing is implemented with an `std` [`Mutex`]. It allows a single bus across multiple threads, /// with finer-grained locking than [`CriticalSectionDevice`](super::CriticalSectionDevice). The downside is that /// it is only available in `std` targets. #[cfg_attr(docsrs, doc(cfg(feature = "std")))] diff --git a/embedded-hal-bus/src/spi/critical_section.rs b/embedded-hal-bus/src/spi/critical_section.rs index baf080167..5bad960af 100644 --- a/embedded-hal-bus/src/spi/critical_section.rs +++ b/embedded-hal-bus/src/spi/critical_section.rs @@ -8,10 +8,10 @@ use super::DeviceError; /// `critical-section`-based shared bus [`SpiDevice`] implementation. /// -/// This allows for sharing an [`SpiBus`](embedded_hal::spi::SpiBus), obtaining multiple [`SpiDevice`] instances, +/// This allows for sharing an [`SpiBus`], obtaining multiple [`SpiDevice`] instances, /// each with its own `CS` pin. /// -/// Sharing is implemented with a `critical-section` [`Mutex`](critical_section::Mutex). A critical section is taken for +/// Sharing is implemented with a `critical-section` [`Mutex`]. A critical section is taken for /// the entire duration of a transaction. This allows sharing a single bus across multiple threads (interrupt priority levels). /// The downside is critical sections typically require globally disabling interrupts, so `CriticalSectionDevice` will likely /// negatively impact real-time properties, such as interrupt latency. If you can, prefer using diff --git a/embedded-hal-bus/src/spi/exclusive.rs b/embedded-hal-bus/src/spi/exclusive.rs index a8266e296..1642269e7 100644 --- a/embedded-hal-bus/src/spi/exclusive.rs +++ b/embedded-hal-bus/src/spi/exclusive.rs @@ -13,7 +13,7 @@ use super::DeviceError; /// [`SpiDevice`] implementation with exclusive access to the bus (not shared). /// -/// This is the most straightforward way of obtaining an [`SpiDevice`] from an [`SpiBus`](embedded_hal::spi::SpiBus), +/// This is the most straightforward way of obtaining an [`SpiDevice`] from an [`SpiBus`], /// ideal for when no sharing is required (only one SPI device is present on the bus). pub struct ExclusiveDevice { bus: BUS, diff --git a/embedded-hal-bus/src/spi/mutex.rs b/embedded-hal-bus/src/spi/mutex.rs index b37c975ac..d1288b995 100644 --- a/embedded-hal-bus/src/spi/mutex.rs +++ b/embedded-hal-bus/src/spi/mutex.rs @@ -7,10 +7,10 @@ use super::DeviceError; /// `std` `Mutex`-based shared bus [`SpiDevice`] implementation. /// -/// This allows for sharing an [`SpiBus`](embedded_hal::spi::SpiBus), obtaining multiple [`SpiDevice`] instances, +/// This allows for sharing an [`SpiBus`], obtaining multiple [`SpiDevice`] instances, /// each with its own `CS` pin. /// -/// Sharing is implemented with a `std` [`Mutex`](std::sync::Mutex). It allows a single bus across multiple threads, +/// Sharing is implemented with a `std` [`Mutex`]. It allows a single bus across multiple threads, /// with finer-grained locking than [`CriticalSectionDevice`](super::CriticalSectionDevice). The downside is /// it is only available in `std` targets. #[cfg_attr(docsrs, doc(cfg(feature = "std")))] diff --git a/embedded-hal-bus/src/spi/refcell.rs b/embedded-hal-bus/src/spi/refcell.rs index 021852080..d407620be 100644 --- a/embedded-hal-bus/src/spi/refcell.rs +++ b/embedded-hal-bus/src/spi/refcell.rs @@ -7,7 +7,7 @@ use super::DeviceError; /// `RefCell`-based shared bus [`SpiDevice`] implementation. /// -/// This allows for sharing an [`SpiBus`](embedded_hal::spi::SpiBus), obtaining multiple [`SpiDevice`] instances, +/// This allows for sharing an [`SpiBus`], obtaining multiple [`SpiDevice`] instances, /// each with its own `CS` pin. /// /// Sharing is implemented with a `RefCell`. This means it has low overhead, but `RefCellDevice` instances are not `Send`, From f3095b3d01bb813128af1054488b67447bf43085 Mon Sep 17 00:00:00 2001 From: Yuri Astrakhan Date: Fri, 1 Sep 2023 01:30:51 -0400 Subject: [PATCH 094/199] A few minor spelling/grammar fixes --- README.md | 2 +- embedded-can/src/lib.rs | 2 +- embedded-hal/CHANGELOG.md | 6 +++--- embedded-hal/README.md | 8 ++++---- embedded-hal/src/i2c.rs | 2 +- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 02cee6050..3d86509eb 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ This project is developed and maintained by the [HAL team](https://github.com/ru ## Scope -`embedded-hal` serves as a foundation for building an ecosystem of platform agnostic drivers. +`embedded-hal` serves as a foundation for building an ecosystem of platform-agnostic drivers. (driver meaning library crates that let a target platform interface an external device like a digital sensor or a wireless transceiver). diff --git a/embedded-can/src/lib.rs b/embedded-can/src/lib.rs index db80c455b..2de2b6ebc 100644 --- a/embedded-can/src/lib.rs +++ b/embedded-can/src/lib.rs @@ -22,7 +22,7 @@ pub trait Frame: Sized { /// This will return `None` if the data length code (DLC) is not valid. fn new_remote(id: impl Into, dlc: usize) -> Option; - /// Returns true if this frame is a extended frame. + /// Returns true if this frame is an extended frame. fn is_extended(&self) -> bool; /// Returns true if this frame is a standard frame. diff --git a/embedded-hal/CHANGELOG.md b/embedded-hal/CHANGELOG.md index 8c6109d6c..019080d5c 100644 --- a/embedded-hal/CHANGELOG.md +++ b/embedded-hal/CHANGELOG.md @@ -93,7 +93,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - Fixed blanket impl of `DelayUs` not covering the `delay_ms` method. ### Changed -- `spi`: traits now enforce all impls on the same struct (eg `Transfer` and `Write`) have the same `Error` type. +- `spi`: traits now enforce all impls on the same struct (e.g. `Transfer` and `Write`) have the same `Error` type. - `digital`: traits now enforce all impls on the same struct have the same `Error` type. - `serial`: traits now enforce all impls on the same struct have the same `Error` type. - `i2c`: traits now enforce all impls on the same struct have the same `Error` type. @@ -150,7 +150,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ### Changed - Swap PWM channel arguments to references - All trait methods have been renamed to remove the `try_` prefix (i.e. `try_send` -> `send`) for consistency. -- Moved all traits into two sub modules for each feature depending on the execution model: `blocking` and `nb` (non-blocking). For example, the spi traits can now be found under `embedded_hal::spi::blocking` or `embedded_hal::spi::nb`. +- Moved all traits into two submodules for each feature depending on the execution model: `blocking` and `nb` (non-blocking). For example, the spi traits can now be found under `embedded_hal::spi::blocking` or `embedded_hal::spi::nb`. - Execution-model-independent definitions have been moved into the feature module. For example, SPI `Phase` is now defined in `embedded_hal::spi::Phase`. For convenience, these definitions are reexported in both of its blocking and non-blocking submodules. - Re-export `nb::{block!, Error, Result}` to avoid version mismatches. These should be used instead of importing the `nb` crate directly in dependent crates. @@ -281,7 +281,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ### Changed -- Re-export most / unchanged traits from embedded-hal v0.2.x to allow inter-operation between HAL +- Re-export most / unchanged traits from embedded-hal v0.2.x to allow interoperation between HAL implementations and drivers that are using different minor versions. ## [v0.1.2] - 2018-02-14 diff --git a/embedded-hal/README.md b/embedded-hal/README.md index 193c63b3a..391599ec5 100644 --- a/embedded-hal/README.md +++ b/embedded-hal/README.md @@ -35,7 +35,7 @@ Additionally, more domain-specific traits are available in separate crates: The HAL -- Must *erase* device specific details. Neither register, register blocks or magic values should +- Must *erase* device specific details. Neither register, register blocks, nor magic values should appear in the API. - Must be generic *within* a device and *across* devices. The API to use a serial interface must @@ -50,7 +50,7 @@ in blocking mode, with the `futures` model, with an async/await model or with a want higher level abstraction should *prefer to use this HAL* rather than *re-implement* register manipulation code. -- Serve as a foundation for building an ecosystem of platform agnostic drivers. Here driver +- Serve as a foundation for building an ecosystem of platform-agnostic drivers. Here driver means a library crate that lets a target platform interface an external device like a digital sensor or a wireless transceiver. The advantage of this system is that by writing the driver as a generic library on top of `embedded-hal` driver authors can support any number of target @@ -70,10 +70,10 @@ interface are not using the same pins". The HAL will focus on *doing I/O*. ## Platform agnostic drivers -You can find platform agnostic drivers built on top of `embedded-hal` on crates.io by [searching +You can find platform-agnostic drivers built on top of `embedded-hal` on crates.io by [searching for the *embedded-hal* keyword](https://crates.io/keywords/embedded-hal). -If you are writing a platform agnostic driver yourself you are highly encouraged to [add the +If you are writing a platform-agnostic driver yourself you are highly encouraged to [add the embedded-hal keyword](https://doc.rust-lang.org/cargo/reference/manifest.html#package-metadata) to your crate before publishing it! diff --git a/embedded-hal/src/i2c.rs b/embedded-hal/src/i2c.rs index e42b16ebc..8f4848a28 100644 --- a/embedded-hal/src/i2c.rs +++ b/embedded-hal/src/i2c.rs @@ -35,7 +35,7 @@ //! //! The [`embedded-hal-bus`](https://docs.rs/embedded-hal-bus) crate provides several //! implementations for sharing I2C buses. You can use them to take an exclusive instance -//! you've received from a HAL and "split" it into mulitple shared ones, to instantiate +//! you've received from a HAL and "split" it into multiple shared ones, to instantiate //! several drivers on the same bus. //! //! # For driver authors From 6e461a0c4303077b0d3b5e91d747199d675d98ed Mon Sep 17 00:00:00 2001 From: Yuri Astrakhan Date: Fri, 1 Sep 2023 01:33:45 -0400 Subject: [PATCH 095/199] Add missing LICENSE-* files to embedded-io-async --- embedded-io-async/LICENSE-APACHE | 201 +++++++++++++++++++++++++++++++ embedded-io-async/LICENSE-MIT | 25 ++++ 2 files changed, 226 insertions(+) create mode 100644 embedded-io-async/LICENSE-APACHE create mode 100644 embedded-io-async/LICENSE-MIT diff --git a/embedded-io-async/LICENSE-APACHE b/embedded-io-async/LICENSE-APACHE new file mode 100644 index 000000000..16fe87b06 --- /dev/null +++ b/embedded-io-async/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/embedded-io-async/LICENSE-MIT b/embedded-io-async/LICENSE-MIT new file mode 100644 index 000000000..3f5b8019c --- /dev/null +++ b/embedded-io-async/LICENSE-MIT @@ -0,0 +1,25 @@ +Copyright (c) 2023 The embedded-io-async authors + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. From 0df21a83a249a28cf9f04fa9dfa57a0df552cb82 Mon Sep 17 00:00:00 2001 From: Yuri Astrakhan Date: Fri, 1 Sep 2023 01:36:01 -0400 Subject: [PATCH 096/199] Remove a few un-needed qualifiers for consistency Some types are referenced inconsistently, so removing qualifiers to keep them uniform --- embedded-hal/src/spi.rs | 2 +- embedded-io/src/lib.rs | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/embedded-hal/src/spi.rs b/embedded-hal/src/spi.rs index e855aa47a..c8c8224fd 100644 --- a/embedded-hal/src/spi.rs +++ b/embedded-hal/src/spi.rs @@ -221,7 +221,7 @@ pub const MODE_3: Mode = Mode { }; /// SPI error. -pub trait Error: core::fmt::Debug { +pub trait Error: Debug { /// Convert error to a generic SPI error kind. /// /// By using this method, SPI errors freely defined by HAL implementations diff --git a/embedded-io/src/lib.rs b/embedded-io/src/lib.rs index f798b5b6e..2daca4448 100644 --- a/embedded-io/src/lib.rs +++ b/embedded-io/src/lib.rs @@ -175,7 +175,7 @@ impl From for ErrorKind { /// /// This trait allows generic code to do limited inspecting of errors, /// to react differently to different kinds. -pub trait Error: core::fmt::Debug { +pub trait Error: fmt::Debug { /// Get the kind of this error. fn kind(&self) -> ErrorKind; } @@ -194,8 +194,8 @@ impl Error for ErrorKind { #[cfg(feature = "std")] #[cfg_attr(docsrs, doc(cfg(feature = "std")))] -impl crate::Error for std::io::Error { - fn kind(&self) -> crate::ErrorKind { +impl Error for std::io::Error { + fn kind(&self) -> ErrorKind { self.kind().into() } } From 6db17645557f260fbceebbbdb601e93c71a696ad Mon Sep 17 00:00:00 2001 From: Yuri Astrakhan Date: Fri, 1 Sep 2023 00:02:03 -0400 Subject: [PATCH 097/199] Cleanup Cargo features and optionals * Make sure all optional dependencies are not exposed automatically, but rather explicit --- embedded-io/Cargo.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/embedded-io/Cargo.toml b/embedded-io/Cargo.toml index a7ca732f3..696823e7b 100644 --- a/embedded-io/Cargo.toml +++ b/embedded-io/Cargo.toml @@ -14,6 +14,7 @@ categories = [ [features] std = ["alloc"] alloc = [] +defmt-03 = ["dep:defmt-03"] [dependencies] defmt-03 = { package = "defmt", version = "0.3", optional = true } From 90e2c5e877d968cc8ce9a759884cfa07b48c4bb1 Mon Sep 17 00:00:00 2001 From: Adam Greig Date: Wed, 13 Sep 2023 02:08:26 +0100 Subject: [PATCH 098/199] Clarify write() may only return Ok(0) if data is empty. In other situations an error should be returned instead; update ErrorKind to add this and update out slice_mut implementation to use it. --- embedded-io-async/src/lib.rs | 8 ++-- embedded-io/CHANGELOG.md | 12 +++++- embedded-io/src/impls/slice_mut.rs | 41 ++++++++++++++++-- embedded-io/src/lib.rs | 68 ++++++------------------------ 4 files changed, 66 insertions(+), 63 deletions(-) diff --git a/embedded-io-async/src/lib.rs b/embedded-io-async/src/lib.rs index 98a656be8..b38d13abf 100644 --- a/embedded-io-async/src/lib.rs +++ b/embedded-io-async/src/lib.rs @@ -10,7 +10,7 @@ extern crate alloc; mod impls; pub use embedded_io::{ - Error, ErrorKind, ErrorType, ReadExactError, ReadReady, SeekFrom, WriteAllError, WriteReady, + Error, ErrorKind, ErrorType, ReadExactError, ReadReady, SeekFrom, WriteReady, }; /// Async reader. @@ -125,13 +125,13 @@ pub trait Write: ErrorType { /// /// This function is not side-effect-free on cancel (AKA "cancel-safe"), i.e. if you cancel (drop) a returned /// future that hasn't completed yet, some bytes might have already been written. - async fn write_all(&mut self, buf: &[u8]) -> Result<(), WriteAllError> { + async fn write_all(&mut self, buf: &[u8]) -> Result<(), Self::Error> { let mut buf = buf; while !buf.is_empty() { match self.write(buf).await { - Ok(0) => return Err(WriteAllError::WriteZero), + Ok(0) => panic!("write() returned Ok(0)"), Ok(n) => buf = &buf[n..], - Err(e) => return Err(WriteAllError::Other(e)), + Err(e) => return Err(e), } } Ok(()) diff --git a/embedded-io/CHANGELOG.md b/embedded-io/CHANGELOG.md index 937a43140..f5a60a3df 100644 --- a/embedded-io/CHANGELOG.md +++ b/embedded-io/CHANGELOG.md @@ -5,6 +5,16 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## Unreleased + +- Prohibit `Write::write` implementations returning `Ok(0)` unless there is no + data to write; consequently remove `WriteAllError` and the `WriteAllError` + variant of `WriteFmtError`. Update the `&mut [u8]` impl to possibly return + a new `SliceWriteError` if the slice is full instead of `Ok(0)`. +- Add `WriteZero` variant to `ErrorKind` for implementations that previously + may have returned `Ok(0)` to indicate no further data could be written. +- `Write::write_all` now panics if the `write()` implementation returns `Ok(0)`. + ## 0.5.0 - 2023-08-06 - Add `ReadReady`, `WriteReady` traits. They allow peeking whether the I/O handle is ready to read/write, so they allow using the traits in a non-blocking way. @@ -37,4 +47,4 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## 0.2.0 - 2022-05-07 -- First release \ No newline at end of file +- First release diff --git a/embedded-io/src/impls/slice_mut.rs b/embedded-io/src/impls/slice_mut.rs index b89b72182..30bb80877 100644 --- a/embedded-io/src/impls/slice_mut.rs +++ b/embedded-io/src/impls/slice_mut.rs @@ -1,10 +1,41 @@ -use crate::{ErrorType, Write}; +use crate::{Error, ErrorKind, ErrorType, Write}; use core::mem; +// needed to prevent defmt macros from breaking, since they emit code that does `defmt::blahblah`. +#[cfg(feature = "defmt-03")] +use defmt_03 as defmt; + +/// Errors that could be returned by `Write` on `&mut [u8]`. +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +#[cfg_attr(feature = "defmt-03", derive(defmt::Format))] +#[non_exhaustive] +pub enum SliceWriteError { + /// The target slice was full and so could not receive any new data. + Full, +} + +impl Error for SliceWriteError { + fn kind(&self) -> ErrorKind { + match self { + SliceWriteError::Full => ErrorKind::WriteZero, + } + } +} + impl ErrorType for &mut [u8] { - type Error = core::convert::Infallible; + type Error = SliceWriteError; } +impl core::fmt::Display for SliceWriteError { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "{self:?}") + } +} + +#[cfg(feature = "std")] +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] +impl std::error::Error for SliceWriteError {} + /// Write is implemented for `&mut [u8]` by copying into the slice, overwriting /// its data. /// @@ -12,12 +43,14 @@ impl ErrorType for &mut [u8] { /// The slice will be empty when it has been completely overwritten. /// /// If the number of bytes to be written exceeds the size of the slice, write operations will -/// return short writes: ultimately, `Ok(0)`; in this situation, `write_all` returns an error of -/// kind `ErrorKind::WriteZero`. +/// return short writes: ultimately, a `SliceWriteError::Full`. impl Write for &mut [u8] { #[inline] fn write(&mut self, buf: &[u8]) -> Result { let amt = core::cmp::min(buf.len(), self.len()); + if !buf.is_empty() && amt == 0 { + return Err(SliceWriteError::Full); + } let (a, b) = mem::take(self).split_at_mut(amt); a.copy_from_slice(&buf[..amt]); *self = b; diff --git a/embedded-io/src/lib.rs b/embedded-io/src/lib.rs index 1422a7b4b..1b91f7a7c 100644 --- a/embedded-io/src/lib.rs +++ b/embedded-io/src/lib.rs @@ -61,7 +61,6 @@ impl From for SeekFrom { /// This is the `embedded-io` equivalent of [`std::io::ErrorKind`], except with the following changes: /// /// - `WouldBlock` is removed, since `embedded-io` traits are always blocking. See the [crate-level documentation](crate) for details. -/// - `WriteZero` is removed, since it is a separate variant in [`WriteAllError`] and [`WriteFmtError`]. #[derive(Debug, Copy, Clone, Eq, PartialEq)] #[cfg_attr(feature = "defmt-03", derive(defmt::Format))] #[non_exhaustive] @@ -117,6 +116,8 @@ pub enum ErrorKind { /// An operation could not be completed, because it failed /// to allocate enough memory. OutOfMemory, + /// An attempted write could not write any data. + WriteZero, } #[cfg(feature = "std")] @@ -248,19 +249,6 @@ impl From> for std::io::Error { } } -#[cfg(feature = "std")] -#[cfg_attr(docsrs, doc(cfg(feature = "std")))] -impl From> for std::io::Error { - fn from(err: WriteAllError) -> Self { - match err { - WriteAllError::WriteZero => { - std::io::Error::new(std::io::ErrorKind::WriteZero, "WriteZero".to_owned()) - } - WriteAllError::Other(e) => std::io::Error::new(e.kind(), format!("{e:?}")), - } - } -} - impl fmt::Display for ReadExactError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{self:?}") @@ -275,8 +263,6 @@ impl std::error::Error for ReadExactError {} #[derive(Debug, Copy, Clone, Eq, PartialEq)] #[cfg_attr(feature = "defmt-03", derive(defmt::Format))] pub enum WriteFmtError { - /// [`Write::write`] wrote zero bytes - WriteZero, /// An error was encountered while formatting. FmtError, /// Error returned by the inner Write. @@ -299,32 +285,6 @@ impl fmt::Display for WriteFmtError { #[cfg_attr(docsrs, doc(cfg(feature = "std")))] impl std::error::Error for WriteFmtError {} -/// Error returned by [`Write::write_all`] -#[derive(Debug, Copy, Clone, Eq, PartialEq)] -#[cfg_attr(feature = "defmt-03", derive(defmt::Format))] -pub enum WriteAllError { - /// [`Write::write`] wrote zero bytes - WriteZero, - /// Error returned by the inner Write. - Other(E), -} - -impl From for WriteAllError { - fn from(err: E) -> Self { - Self::Other(err) - } -} - -impl fmt::Display for WriteAllError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{self:?}") - } -} - -#[cfg(feature = "std")] -#[cfg_attr(docsrs, doc(cfg(feature = "std")))] -impl std::error::Error for WriteAllError {} - /// Blocking reader. /// /// This trait is the `embedded-io` equivalent of [`std::io::Read`]. @@ -401,11 +361,12 @@ pub trait Write: ErrorType { /// implementation to write an amount of bytes less than `buf.len()` while the writer continues to be /// ready to accept more bytes immediately. /// - /// Implementations should never return `Ok(0)` when `buf.len() != 0`. Situations where the writer is not - /// able to accept more bytes and likely never will are better indicated with errors. + /// Implementations must not return `Ok(0)` unless `buf` is empty. Situations where the + /// writer is not able to accept more bytes must instead be indicated with an error, + /// where the `ErrorKind` is `WriteZero`. /// - /// If `buf.len() == 0`, `write` returns without blocking, with either `Ok(0)` or an error. - /// The `Ok(0)` doesn't indicate an error. + /// If `buf` is empty, `write` returns without blocking, with either `Ok(0)` or an error. + /// `Ok(0)` doesn't indicate an error. fn write(&mut self, buf: &[u8]) -> Result; /// Flush this output stream, blocking until all intermediately buffered contents reach their destination. @@ -419,12 +380,14 @@ pub trait Write: ErrorType { /// If you are using [`WriteReady`] to avoid blocking, you should not use this function. /// `WriteReady::write_ready()` returning true only guarantees the first call to `write()` will /// not block, so this function may still block in subsequent calls. - fn write_all(&mut self, mut buf: &[u8]) -> Result<(), WriteAllError> { + /// + /// This function will panic if `write()` returns `Ok(0)`. + fn write_all(&mut self, mut buf: &[u8]) -> Result<(), Self::Error> { while !buf.is_empty() { match self.write(buf) { - Ok(0) => return Err(WriteAllError::WriteZero), + Ok(0) => panic!("write() returned Ok(0)"), Ok(n) => buf = &buf[n..], - Err(e) => return Err(WriteAllError::Other(e)), + Err(e) => return Err(e), } } Ok(()) @@ -443,7 +406,7 @@ pub trait Write: ErrorType { // off I/O errors. instead of discarding them struct Adapter<'a, T: Write + ?Sized + 'a> { inner: &'a mut T, - error: Result<(), WriteAllError>, + error: Result<(), T::Error>, } impl fmt::Write for Adapter<'_, T> { @@ -466,10 +429,7 @@ pub trait Write: ErrorType { Ok(()) => Ok(()), Err(..) => match output.error { // check if the error came from the underlying `Write` or not - Err(e) => match e { - WriteAllError::WriteZero => Err(WriteFmtError::WriteZero), - WriteAllError::Other(e) => Err(WriteFmtError::Other(e)), - }, + Err(e) => Err(WriteFmtError::Other(e)), Ok(()) => Err(WriteFmtError::FmtError), }, } From 9d40f6b1423078d776a84928cc2dbc7521fbb947 Mon Sep 17 00:00:00 2001 From: Adam Greig Date: Thu, 14 Sep 2023 00:41:02 +0100 Subject: [PATCH 099/199] Update embedded-io-adapters to swap Ok(0) for Err when required --- embedded-io-adapters/CHANGELOG.md | 4 +++- embedded-io-adapters/src/futures_03.rs | 6 +++++- embedded-io-adapters/src/std.rs | 14 ++++++++++++-- embedded-io-adapters/src/tokio_1.rs | 6 +++++- 4 files changed, 25 insertions(+), 5 deletions(-) diff --git a/embedded-io-adapters/CHANGELOG.md b/embedded-io-adapters/CHANGELOG.md index bf9f2d391..107b9f98c 100644 --- a/embedded-io-adapters/CHANGELOG.md +++ b/embedded-io-adapters/CHANGELOG.md @@ -8,7 +8,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased - Add support for adapting `BufRead` from `futures` and `tokio`. +- Return an error when a wrapped `std`/`futures`/`tokio` `write()` call returns + `Ok(0)` to comply with `embedded_io::Write` requirements. ## 0.5.0 - 2023-08-06 -- First release \ No newline at end of file +- First release diff --git a/embedded-io-adapters/src/futures_03.rs b/embedded-io-adapters/src/futures_03.rs index a3402cbb9..7fc95aa48 100644 --- a/embedded-io-adapters/src/futures_03.rs +++ b/embedded-io-adapters/src/futures_03.rs @@ -57,7 +57,11 @@ impl embedded_io_async::BufRead f impl embedded_io_async::Write for FromFutures { async fn write(&mut self, buf: &[u8]) -> Result { - poll_fn(|cx| Pin::new(&mut self.inner).poll_write(cx, buf)).await + match poll_fn(|cx| Pin::new(&mut self.inner).poll_write(cx, buf)).await { + Ok(0) if !buf.is_empty() => Err(std::io::ErrorKind::WriteZero.into()), + Ok(n) => Ok(n), + Err(e) => Err(e), + } } async fn flush(&mut self) -> Result<(), Self::Error> { diff --git a/embedded-io-adapters/src/std.rs b/embedded-io-adapters/src/std.rs index cd3df2d75..fe2248bcb 100644 --- a/embedded-io-adapters/src/std.rs +++ b/embedded-io-adapters/src/std.rs @@ -1,5 +1,7 @@ //! Adapters to/from `std::io` traits. +use embedded_io::Error; + /// Adapter from `std::io` traits. #[derive(Clone)] pub struct FromStd { @@ -52,7 +54,11 @@ impl embedded_io::BufRead for FromStd { impl embedded_io::Write for FromStd { fn write(&mut self, buf: &[u8]) -> Result { - self.inner.write(buf) + match self.inner.write(buf) { + Ok(0) if !buf.is_empty() => Err(std::io::ErrorKind::WriteZero.into()), + Ok(n) => Ok(n), + Err(e) => Err(e), + } } fn flush(&mut self) -> Result<(), Self::Error> { self.inner.flush() @@ -103,7 +109,11 @@ impl std::io::Read for ToStd { impl std::io::Write for ToStd { fn write(&mut self, buf: &[u8]) -> Result { - self.inner.write(buf).map_err(to_std_error) + match self.inner.write(buf) { + Ok(n) => Ok(n), + Err(e) if e.kind() == embedded_io::ErrorKind::WriteZero => Ok(0), + Err(e) => Err(to_std_error(e)), + } } fn flush(&mut self) -> Result<(), std::io::Error> { self.inner.flush().map_err(to_std_error) diff --git a/embedded-io-adapters/src/tokio_1.rs b/embedded-io-adapters/src/tokio_1.rs index 92060a4fe..9369efe07 100644 --- a/embedded-io-adapters/src/tokio_1.rs +++ b/embedded-io-adapters/src/tokio_1.rs @@ -68,7 +68,11 @@ impl embedded_io_async::BufRead for impl embedded_io_async::Write for FromTokio { async fn write(&mut self, buf: &[u8]) -> Result { - poll_fn(|cx| Pin::new(&mut self.inner).poll_write(cx, buf)).await + match poll_fn(|cx| Pin::new(&mut self.inner).poll_write(cx, buf)).await { + Ok(0) if !buf.is_empty() => Err(std::io::ErrorKind::WriteZero.into()), + Ok(n) => Ok(n), + Err(e) => Err(e), + } } async fn flush(&mut self) -> Result<(), Self::Error> { From 21abf1d9a26546c220e67c65f438170748ae0cee Mon Sep 17 00:00:00 2001 From: Scott Mabin Date: Mon, 18 Sep 2023 16:02:17 +0100 Subject: [PATCH 100/199] document write_fmt length trick for slice --- embedded-io/src/lib.rs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/embedded-io/src/lib.rs b/embedded-io/src/lib.rs index 1b91f7a7c..6358d5286 100644 --- a/embedded-io/src/lib.rs +++ b/embedded-io/src/lib.rs @@ -401,6 +401,17 @@ pub trait Write: ErrorType { /// If you are using [`WriteReady`] to avoid blocking, you should not use this function. /// `WriteReady::write_ready()` returning true only guarantees the first call to `write()` will /// not block, so this function may still block in subsequent calls. + /// + /// Unlike [`Write::write`], the number of bytes written is not returned. However, in the case of + /// writing to an `&mut [u8]` its possible to calculate the number of bytes written by subtracting + /// the length of the slice after the write, from the initial length of the slice. + /// + /// ```rust + /// # use embedded_io::Write; + /// let mut buf: &mut [u8] = &mut [0u8; 256]; + /// let start = buf.len(); + /// let len = write!(buf, "{}", "Test").and_then(|_| Ok(start - buf.len())); + /// ``` fn write_fmt(&mut self, fmt: fmt::Arguments<'_>) -> Result<(), WriteFmtError> { // Create a shim which translates a Write to a fmt::Write and saves // off I/O errors. instead of discarding them From e632f161698a6f5dea3082669d882f6b70c45dcf Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 2 Oct 2023 15:29:20 +0200 Subject: [PATCH 101/199] Release embedded-io{,-async,-adapters} v0.6.0 --- embedded-io-adapters/CHANGELOG.md | 4 ++++ embedded-io-adapters/Cargo.toml | 6 +++--- embedded-io-async/CHANGELOG.md | 16 +++++++++++++++- embedded-io-async/Cargo.toml | 4 ++-- embedded-io/CHANGELOG.md | 2 +- embedded-io/Cargo.toml | 2 +- 6 files changed, 26 insertions(+), 8 deletions(-) diff --git a/embedded-io-adapters/CHANGELOG.md b/embedded-io-adapters/CHANGELOG.md index 107b9f98c..a81afe1a9 100644 --- a/embedded-io-adapters/CHANGELOG.md +++ b/embedded-io-adapters/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased +Add unreleased changes here + +## 0.6.0 - 2023-10-02 + - Add support for adapting `BufRead` from `futures` and `tokio`. - Return an error when a wrapped `std`/`futures`/`tokio` `write()` call returns `Ok(0)` to comply with `embedded_io::Write` requirements. diff --git a/embedded-io-adapters/Cargo.toml b/embedded-io-adapters/Cargo.toml index a849b1f12..4734b840f 100644 --- a/embedded-io-adapters/Cargo.toml +++ b/embedded-io-adapters/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "embedded-io-adapters" -version = "0.5.0" +version = "0.6.0" edition = "2021" description = "Adapters between the `embedded-io` traits and other I/O traits" repository = "https://github.com/rust-embedded/embedded-hal" @@ -17,8 +17,8 @@ tokio-1 = ["std", "dep:tokio", "dep:embedded-io-async", "embedded-io-async?/std" futures-03 = ["std", "dep:futures", "dep:embedded-io-async", "embedded-io-async?/std"] [dependencies] -embedded-io = { version = "0.5", path = "../embedded-io" } -embedded-io-async = { version = "0.5", path = "../embedded-io-async", optional = true } +embedded-io = { version = "0.6", path = "../embedded-io" } +embedded-io-async = { version = "0.6", path = "../embedded-io-async", optional = true } futures = { version = "0.3.21", features = ["std"], default-features = false, optional = true } tokio = { version = "1", features = ["io-util"], default-features = false, optional = true } diff --git a/embedded-io-async/CHANGELOG.md b/embedded-io-async/CHANGELOG.md index 978a92d8b..94648ee7e 100644 --- a/embedded-io-async/CHANGELOG.md +++ b/embedded-io-async/CHANGELOG.md @@ -5,6 +5,20 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## Unreleased + +Add unreleased changes here + +## 0.6.0 - 2023-10-02 + +- Prohibit `Write::write` implementations returning `Ok(0)` unless there is no + data to write; consequently remove `WriteAllError`. + Update the `&mut [u8]` impl to possibly return + a new `SliceWriteError` if the slice is full instead of `Ok(0)`. +- Add `WriteZero` variant to `ErrorKind` for implementations that previously + may have returned `Ok(0)` to indicate no further data could be written. +- `Write::write_all` now panics if the `write()` implementation returns `Ok(0)`. + ## 0.5.0 - 2023-08-06 -- First release \ No newline at end of file +- First release diff --git a/embedded-io-async/Cargo.toml b/embedded-io-async/Cargo.toml index 011a65589..fc704646b 100644 --- a/embedded-io-async/Cargo.toml +++ b/embedded-io-async/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "embedded-io-async" -version = "0.5.0" +version = "0.6.0" edition = "2021" description = "Async embedded IO traits" repository = "https://github.com/rust-embedded/embedded-hal" @@ -17,7 +17,7 @@ alloc = ["embedded-io/alloc"] defmt-03 = ["dep:defmt-03", "embedded-io/defmt-03"] [dependencies] -embedded-io = { version = "0.5", path = "../embedded-io" } +embedded-io = { version = "0.6", path = "../embedded-io" } defmt-03 = { package = "defmt", version = "0.3", optional = true } [package.metadata.docs.rs] diff --git a/embedded-io/CHANGELOG.md b/embedded-io/CHANGELOG.md index f5a60a3df..bed5fc16f 100644 --- a/embedded-io/CHANGELOG.md +++ b/embedded-io/CHANGELOG.md @@ -5,7 +5,7 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## Unreleased +## 0.6.0 - 2023-10-02 - Prohibit `Write::write` implementations returning `Ok(0)` unless there is no data to write; consequently remove `WriteAllError` and the `WriteAllError` diff --git a/embedded-io/Cargo.toml b/embedded-io/Cargo.toml index 696823e7b..d96ff0b47 100644 --- a/embedded-io/Cargo.toml +++ b/embedded-io/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "embedded-io" -version = "0.5.0" +version = "0.6.0" edition = "2021" description = "Embedded IO traits" repository = "https://github.com/rust-embedded/embedded-hal" From a4dcba4cde473c681c7756748a999d04632eba33 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 2 Oct 2023 15:31:54 +0200 Subject: [PATCH 102/199] Add allow(stable_features). Needed to pass deny-warnings on CI without breaking older nightlies. --- embedded-hal-async/src/lib.rs | 5 +++++ embedded-hal-bus/src/lib.rs | 5 +++++ embedded-io-adapters/src/lib.rs | 12 ++++++++++-- embedded-io-async/src/lib.rs | 7 ++++++- 4 files changed, 26 insertions(+), 3 deletions(-) diff --git a/embedded-hal-async/src/lib.rs b/embedded-hal-async/src/lib.rs index 5fe9168e7..e724207b7 100644 --- a/embedded-hal-async/src/lib.rs +++ b/embedded-hal-async/src/lib.rs @@ -1,6 +1,11 @@ #![doc = include_str!("../README.md")] #![warn(missing_docs)] #![no_std] +// disable warning for already-stabilized features. +// Needed to pass CI, because we deny warnings. +// We don't immediately remove them to not immediately break older nightlies. +// When all features are stable, we'll remove them. +#![allow(stable_features)] #![feature(async_fn_in_trait, impl_trait_projections)] pub mod delay; diff --git a/embedded-hal-bus/src/lib.rs b/embedded-hal-bus/src/lib.rs index 6feef548b..501534947 100644 --- a/embedded-hal-bus/src/lib.rs +++ b/embedded-hal-bus/src/lib.rs @@ -2,6 +2,11 @@ #![warn(missing_docs)] #![cfg_attr(not(feature = "std"), no_std)] #![cfg_attr(docsrs, feature(doc_cfg))] +// disable warning for already-stabilized features. +// Needed to pass CI, because we deny warnings. +// We don't immediately remove them to not immediately break older nightlies. +// When all features are stable, we'll remove them. +#![cfg_attr(feature = "async", allow(stable_features))] #![cfg_attr(feature = "async", feature(async_fn_in_trait, impl_trait_projections))] // needed to prevent defmt macros from breaking, since they emit code that does `defmt::blahblah`. diff --git a/embedded-io-adapters/src/lib.rs b/embedded-io-adapters/src/lib.rs index d11316405..533acc1a7 100644 --- a/embedded-io-adapters/src/lib.rs +++ b/embedded-io-adapters/src/lib.rs @@ -1,11 +1,19 @@ #![cfg_attr(not(feature = "std"), no_std)] #![cfg_attr(docsrs, feature(doc_cfg))] +#![warn(missing_docs)] +#![doc = include_str!("../README.md")] +// disable warning for already-stabilized features. +// Needed to pass CI, because we deny warnings. +// We don't immediately remove them to not immediately break older nightlies. +// When all features are stable, we'll remove them. +#![cfg_attr( + any(feature = "tokio-1", feature = "futures-03"), + allow(stable_features) +)] #![cfg_attr( any(feature = "tokio-1", feature = "futures-03"), feature(async_fn_in_trait, impl_trait_projections) )] -#![warn(missing_docs)] -#![doc = include_str!("../README.md")] #[cfg(feature = "std")] #[cfg_attr(docsrs, doc(cfg(feature = "std")))] diff --git a/embedded-io-async/src/lib.rs b/embedded-io-async/src/lib.rs index b38d13abf..a726147d9 100644 --- a/embedded-io-async/src/lib.rs +++ b/embedded-io-async/src/lib.rs @@ -1,8 +1,13 @@ -#![feature(async_fn_in_trait, impl_trait_projections)] #![cfg_attr(not(feature = "std"), no_std)] #![cfg_attr(docsrs, feature(doc_cfg))] #![warn(missing_docs)] #![doc = include_str!("../README.md")] +// disable warning for already-stabilized features. +// Needed to pass CI, because we deny warnings. +// We don't immediately remove them to not immediately break older nightlies. +// When all features are stable, we'll remove them. +#![allow(stable_features)] +#![feature(async_fn_in_trait, impl_trait_projections)] #[cfg(feature = "alloc")] extern crate alloc; From 739fa02ebe38e9cb29f0c3c9c05ad93dde49c57d Mon Sep 17 00:00:00 2001 From: rmsyn Date: Sun, 15 Oct 2023 23:11:22 +0000 Subject: [PATCH 103/199] async: disable `Send` lint in async traits Adds `#[allow(async_fn_in_trait)]` to disable the lint in `async` trait functions recommending `Send` in the return type. Adding the type annotation is currently unstable, has little-to-no utility in current crates using `embedded-hal`, and will break those users. See for details and discussion. --- embedded-hal-async/src/lib.rs | 2 ++ embedded-io-async/src/lib.rs | 2 ++ 2 files changed, 4 insertions(+) diff --git a/embedded-hal-async/src/lib.rs b/embedded-hal-async/src/lib.rs index e724207b7..cc3060708 100644 --- a/embedded-hal-async/src/lib.rs +++ b/embedded-hal-async/src/lib.rs @@ -6,6 +6,8 @@ // We don't immediately remove them to not immediately break older nightlies. // When all features are stable, we'll remove them. #![allow(stable_features)] +#![allow(unknown_lints)] +#![allow(async_fn_in_trait)] #![feature(async_fn_in_trait, impl_trait_projections)] pub mod delay; diff --git a/embedded-io-async/src/lib.rs b/embedded-io-async/src/lib.rs index a726147d9..2970aa82f 100644 --- a/embedded-io-async/src/lib.rs +++ b/embedded-io-async/src/lib.rs @@ -7,6 +7,8 @@ // We don't immediately remove them to not immediately break older nightlies. // When all features are stable, we'll remove them. #![allow(stable_features)] +#![allow(unknown_lints)] +#![allow(async_fn_in_trait)] #![feature(async_fn_in_trait, impl_trait_projections)] #[cfg(feature = "alloc")] From fb30c9b7475610379c807b3d5e6083ced652c35e Mon Sep 17 00:00:00 2001 From: rmsyn Date: Sun, 15 Oct 2023 23:42:37 +0000 Subject: [PATCH 104/199] ci: update nightly versions for clippy and rustdoc --- .github/workflows/clippy.yml | 4 ++-- .github/workflows/rustdoc.yml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/clippy.yml b/.github/workflows/clippy.yml index 1479e4a56..dcea25c5e 100644 --- a/.github/workflows/clippy.yml +++ b/.github/workflows/clippy.yml @@ -15,6 +15,6 @@ jobs: with: # embedded-hal-async needs nightly. # Use a pinned version to avoid spontaneous breakages (new clippy lints are added often) - toolchain: nightly-2023-07-03 + toolchain: nightly-2023-10-14 components: clippy - - run: cargo clippy --all-features -- --deny=warnings \ No newline at end of file + - run: cargo clippy --all-features -- --deny=warnings diff --git a/.github/workflows/rustdoc.yml b/.github/workflows/rustdoc.yml index e0202bac8..cef117665 100644 --- a/.github/workflows/rustdoc.yml +++ b/.github/workflows/rustdoc.yml @@ -13,5 +13,5 @@ jobs: - uses: actions/checkout@v3 - uses: dtolnay/rust-toolchain@master with: - toolchain: nightly-2023-07-03 - - run: RUSTDOCFLAGS="--deny=warnings --cfg=docsrs" cargo doc --all-features \ No newline at end of file + toolchain: nightly-2023-10-14 + - run: RUSTDOCFLAGS="--deny=warnings --cfg=docsrs" cargo doc --all-features From b2c1c0de1d97ce6f2a756c892b75c5d7e56c01e7 Mon Sep 17 00:00:00 2001 From: Scott Mabin Date: Thu, 12 Oct 2023 22:03:14 +0100 Subject: [PATCH 105/199] Make SliceWriteError publicly accessible --- embedded-io/src/impls/slice_mut.rs | 15 +-------------- embedded-io/src/lib.rs | 9 +++++++++ 2 files changed, 10 insertions(+), 14 deletions(-) diff --git a/embedded-io/src/impls/slice_mut.rs b/embedded-io/src/impls/slice_mut.rs index 30bb80877..4608ee45b 100644 --- a/embedded-io/src/impls/slice_mut.rs +++ b/embedded-io/src/impls/slice_mut.rs @@ -1,19 +1,6 @@ -use crate::{Error, ErrorKind, ErrorType, Write}; +use crate::{Error, ErrorKind, ErrorType, SliceWriteError, Write}; use core::mem; -// needed to prevent defmt macros from breaking, since they emit code that does `defmt::blahblah`. -#[cfg(feature = "defmt-03")] -use defmt_03 as defmt; - -/// Errors that could be returned by `Write` on `&mut [u8]`. -#[derive(Debug, Copy, Clone, Eq, PartialEq)] -#[cfg_attr(feature = "defmt-03", derive(defmt::Format))] -#[non_exhaustive] -pub enum SliceWriteError { - /// The target slice was full and so could not receive any new data. - Full, -} - impl Error for SliceWriteError { fn kind(&self) -> ErrorKind { match self { diff --git a/embedded-io/src/lib.rs b/embedded-io/src/lib.rs index 6358d5286..9f95e636a 100644 --- a/embedded-io/src/lib.rs +++ b/embedded-io/src/lib.rs @@ -259,6 +259,15 @@ impl fmt::Display for ReadExactError { #[cfg_attr(docsrs, doc(cfg(feature = "std")))] impl std::error::Error for ReadExactError {} +/// Errors that could be returned by `Write` on `&mut [u8]`. +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +#[cfg_attr(feature = "defmt-03", derive(defmt::Format))] +#[non_exhaustive] +pub enum SliceWriteError { + /// The target slice was full and so could not receive any new data. + Full, +} + /// Error returned by [`Write::write_fmt`] #[derive(Debug, Copy, Clone, Eq, PartialEq)] #[cfg_attr(feature = "defmt-03", derive(defmt::Format))] From 7414e2129b3d753cdd5b7a664c81bef353f8430e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fabian=20Vi=C3=B6l?= Date: Sat, 26 Aug 2023 09:20:24 +0200 Subject: [PATCH 106/199] Use rust-version field --- embedded-can/Cargo.toml | 1 + embedded-hal-bus/Cargo.toml | 1 + embedded-hal-nb/Cargo.toml | 1 + embedded-hal/Cargo.toml | 1 + embedded-io-adapters/Cargo.toml | 1 + embedded-io/Cargo.toml | 1 + 6 files changed, 6 insertions(+) diff --git a/embedded-can/Cargo.toml b/embedded-can/Cargo.toml index 22620d786..7f8903204 100644 --- a/embedded-can/Cargo.toml +++ b/embedded-can/Cargo.toml @@ -2,6 +2,7 @@ name = "embedded-can" version = "0.4.1" edition = "2021" +rust-version = "1.60" description = "HAL traits for Controller Area Network (CAN) devices." categories = ["embedded", "hardware-support", "no-std"] diff --git a/embedded-hal-bus/Cargo.toml b/embedded-hal-bus/Cargo.toml index bfd2a754d..ab69c254f 100644 --- a/embedded-hal-bus/Cargo.toml +++ b/embedded-hal-bus/Cargo.toml @@ -6,6 +6,7 @@ categories = ["embedded", "hardware-support", "no-std"] description = "Bus/Device connection mechanisms for embedded-hal, a Hardware Abstraction Layer (HAL) for embedded systems" documentation = "https://docs.rs/embedded-hal-bus" edition = "2021" +rust-version = "1.60" keywords = ["hal", "IO"] license = "MIT OR Apache-2.0" name = "embedded-hal-bus" diff --git a/embedded-hal-nb/Cargo.toml b/embedded-hal-nb/Cargo.toml index 66e941923..d7319d558 100644 --- a/embedded-hal-nb/Cargo.toml +++ b/embedded-hal-nb/Cargo.toml @@ -2,6 +2,7 @@ name = "embedded-hal-nb" version = "1.0.0-rc.1" edition = "2021" +rust-version = "1.60" categories = ["embedded", "hardware-support", "no-std"] description = "Non-blocking Hardware Abstraction Layer (HAL) for embedded systems using the `nb` crate." diff --git a/embedded-hal/Cargo.toml b/embedded-hal/Cargo.toml index 396cf972b..6fa3df6cb 100644 --- a/embedded-hal/Cargo.toml +++ b/embedded-hal/Cargo.toml @@ -8,6 +8,7 @@ categories = ["asynchronous", "embedded", "hardware-support", "no-std"] description = " A Hardware Abstraction Layer (HAL) for embedded systems " documentation = "https://docs.rs/embedded-hal" edition = "2021" +rust-version = "1.60" keywords = ["hal", "IO"] license = "MIT OR Apache-2.0" name = "embedded-hal" diff --git a/embedded-io-adapters/Cargo.toml b/embedded-io-adapters/Cargo.toml index 4734b840f..8cf97eceb 100644 --- a/embedded-io-adapters/Cargo.toml +++ b/embedded-io-adapters/Cargo.toml @@ -2,6 +2,7 @@ name = "embedded-io-adapters" version = "0.6.0" edition = "2021" +rust-version = "1.60" description = "Adapters between the `embedded-io` traits and other I/O traits" repository = "https://github.com/rust-embedded/embedded-hal" readme = "README.md" diff --git a/embedded-io/Cargo.toml b/embedded-io/Cargo.toml index d96ff0b47..8db36ea55 100644 --- a/embedded-io/Cargo.toml +++ b/embedded-io/Cargo.toml @@ -2,6 +2,7 @@ name = "embedded-io" version = "0.6.0" edition = "2021" +rust-version = "1.60" description = "Embedded IO traits" repository = "https://github.com/rust-embedded/embedded-hal" readme = "README.md" From 689408b255b9ad6710c255fa6a2c340541545937 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fabian=20Vi=C3=B6l?= Date: Sat, 21 Oct 2023 10:13:08 +0200 Subject: [PATCH 107/199] Reduce MSRV --- embedded-can/Cargo.toml | 2 +- embedded-hal-nb/Cargo.toml | 2 +- embedded-hal/Cargo.toml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/embedded-can/Cargo.toml b/embedded-can/Cargo.toml index 7f8903204..6279fa80d 100644 --- a/embedded-can/Cargo.toml +++ b/embedded-can/Cargo.toml @@ -2,7 +2,7 @@ name = "embedded-can" version = "0.4.1" edition = "2021" -rust-version = "1.60" +rust-version = "1.56" description = "HAL traits for Controller Area Network (CAN) devices." categories = ["embedded", "hardware-support", "no-std"] diff --git a/embedded-hal-nb/Cargo.toml b/embedded-hal-nb/Cargo.toml index d7319d558..7e5e3c5a8 100644 --- a/embedded-hal-nb/Cargo.toml +++ b/embedded-hal-nb/Cargo.toml @@ -2,7 +2,7 @@ name = "embedded-hal-nb" version = "1.0.0-rc.1" edition = "2021" -rust-version = "1.60" +rust-version = "1.56" categories = ["embedded", "hardware-support", "no-std"] description = "Non-blocking Hardware Abstraction Layer (HAL) for embedded systems using the `nb` crate." diff --git a/embedded-hal/Cargo.toml b/embedded-hal/Cargo.toml index 6fa3df6cb..58d70fc10 100644 --- a/embedded-hal/Cargo.toml +++ b/embedded-hal/Cargo.toml @@ -8,7 +8,7 @@ categories = ["asynchronous", "embedded", "hardware-support", "no-std"] description = " A Hardware Abstraction Layer (HAL) for embedded systems " documentation = "https://docs.rs/embedded-hal" edition = "2021" -rust-version = "1.60" +rust-version = "1.56" keywords = ["hal", "IO"] license = "MIT OR Apache-2.0" name = "embedded-hal" From 6990b070ca5584e08b2651fa6d548e0efb95838c Mon Sep 17 00:00:00 2001 From: Scott Mabin Date: Sun, 22 Oct 2023 17:26:13 +0100 Subject: [PATCH 108/199] prepare v0.6.1 --- embedded-io/CHANGELOG.md | 4 ++++ embedded-io/Cargo.toml | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/embedded-io/CHANGELOG.md b/embedded-io/CHANGELOG.md index bed5fc16f..286d1b707 100644 --- a/embedded-io/CHANGELOG.md +++ b/embedded-io/CHANGELOG.md @@ -5,6 +5,10 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## 0.6.1 - 2023-10-22 + +- Make `SliceWriteError` publicly available. + ## 0.6.0 - 2023-10-02 - Prohibit `Write::write` implementations returning `Ok(0)` unless there is no diff --git a/embedded-io/Cargo.toml b/embedded-io/Cargo.toml index d96ff0b47..448b752f2 100644 --- a/embedded-io/Cargo.toml +++ b/embedded-io/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "embedded-io" -version = "0.6.0" +version = "0.6.1" edition = "2021" description = "Embedded IO traits" repository = "https://github.com/rust-embedded/embedded-hal" From ff1eb9cb756bc7bd910505b3501ebbc991d3847b Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sun, 22 Oct 2023 21:47:48 +0200 Subject: [PATCH 109/199] io-async: make slice write impl consistent with blocking impl. --- embedded-io-async/src/impls/slice_mut.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/embedded-io-async/src/impls/slice_mut.rs b/embedded-io-async/src/impls/slice_mut.rs index 4a195e3f4..bd64d1320 100644 --- a/embedded-io-async/src/impls/slice_mut.rs +++ b/embedded-io-async/src/impls/slice_mut.rs @@ -1,5 +1,7 @@ -use crate::Write; use core::mem; +use embedded_io::SliceWriteError; + +use crate::Write; /// Write is implemented for `&mut [u8]` by copying into the slice, overwriting /// its data. @@ -14,6 +16,9 @@ impl Write for &mut [u8] { #[inline] async fn write(&mut self, buf: &[u8]) -> Result { let amt = core::cmp::min(buf.len(), self.len()); + if !buf.is_empty() && amt == 0 { + return Err(SliceWriteError::Full); + } let (a, b) = mem::take(self).split_at_mut(amt); a.copy_from_slice(&buf[..amt]); *self = b; From 1f9da539b462c5b50c79967fd6abb80b9a20c533 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Mon, 6 Nov 2023 08:57:39 +0100 Subject: [PATCH 110/199] Handle reading from Tokio with empty buf as a special case --- embedded-io-adapters/src/tokio_1.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/embedded-io-adapters/src/tokio_1.rs b/embedded-io-adapters/src/tokio_1.rs index 9369efe07..263229c5f 100644 --- a/embedded-io-adapters/src/tokio_1.rs +++ b/embedded-io-adapters/src/tokio_1.rs @@ -42,6 +42,14 @@ impl embedded_io::ErrorType for FromTokio { impl embedded_io_async::Read for FromTokio { async fn read(&mut self, buf: &mut [u8]) -> Result { + // The current tokio implementation (https://github.com/tokio-rs/tokio/blob/tokio-1.33.0/tokio/src/io/poll_evented.rs#L165) + // does not consider the case of buf.is_empty() as a special case, + // which can cause Poll::Pending to be returned at the end of the stream when called with an empty buffer. + // This poll will, however, never become ready, as no more bytes will be received. + if buf.is_empty() { + return Ok(0); + } + poll_fn(|cx| { let mut buf = tokio::io::ReadBuf::new(buf); match Pin::new(&mut self.inner).poll_read(cx, &mut buf) { From ab46bcece0ce01b243ccc4d7b84ea4f0a7a11f5a Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Mon, 6 Nov 2023 13:52:45 +0100 Subject: [PATCH 111/199] Update CHANGELOG.md --- embedded-io-adapters/CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/embedded-io-adapters/CHANGELOG.md b/embedded-io-adapters/CHANGELOG.md index a81afe1a9..e9205d681 100644 --- a/embedded-io-adapters/CHANGELOG.md +++ b/embedded-io-adapters/CHANGELOG.md @@ -9,6 +9,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 Add unreleased changes here +- Handle reading from `FromTokio` with empty buffer, ensuring `Ok(0)` is always returned. + ## 0.6.0 - 2023-10-02 - Add support for adapting `BufRead` from `futures` and `tokio`. From b1d84ed3d79e9ddbdd826d64436ed54544f4fac2 Mon Sep 17 00:00:00 2001 From: Yuri Astrakhan Date: Wed, 22 Nov 2023 17:07:22 -0500 Subject: [PATCH 112/199] Make Cargo feature explicit To keep consistent with all other crates, and for clarity, it is best to explicitly declare `defmt-03` as a feature of the `embedded-hal` crate. This way people can look at the `[features]` section rather than search through any optional dependencies. In this case it's not that big of a deal, but it keeps the pattern. --- embedded-hal/Cargo.toml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/embedded-hal/Cargo.toml b/embedded-hal/Cargo.toml index 396cf972b..b67a46680 100644 --- a/embedded-hal/Cargo.toml +++ b/embedded-hal/Cargo.toml @@ -15,5 +15,8 @@ readme = "README.md" repository = "https://github.com/rust-embedded/embedded-hal" version = "1.0.0-rc.1" +[features] +defmt-03 = ["dep:defmt-03"] + [dependencies] defmt-03 = { package = "defmt", version = "0.3", optional = true } From 7fa580567a6b4f5ba907c0ea20bc7d36db0134d2 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sat, 25 Nov 2023 00:26:06 +0100 Subject: [PATCH 113/199] Workaround https://github.com/tokio-rs/tokio/issues/6165 --- .github/workflows/rustdoc.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/rustdoc.yml b/.github/workflows/rustdoc.yml index cef117665..c4b46425e 100644 --- a/.github/workflows/rustdoc.yml +++ b/.github/workflows/rustdoc.yml @@ -14,4 +14,5 @@ jobs: - uses: dtolnay/rust-toolchain@master with: toolchain: nightly-2023-10-14 - - run: RUSTDOCFLAGS="--deny=warnings --cfg=docsrs" cargo doc --all-features + # tokio/net required to workaround https://github.com/tokio-rs/tokio/issues/6165 + - run: RUSTDOCFLAGS="--deny=warnings --cfg=docsrs" cargo doc --all-features --features tokio/net From a7eb603bc4c2c4bfab975a5b46312c97745afb0c Mon Sep 17 00:00:00 2001 From: Yuri Astrakhan Date: Wed, 22 Nov 2023 18:09:00 -0500 Subject: [PATCH 114/199] A few minor clippy nits * semicolons * doc links * unused Error namespace --- embedded-hal-bus/src/spi/mutex.rs | 4 ++-- embedded-io-adapters/src/futures_03.rs | 2 +- embedded-io-adapters/src/std.rs | 6 +++--- embedded-io-adapters/src/tokio_1.rs | 2 +- embedded-io-async/src/impls/boxx.rs | 2 +- embedded-io/src/impls/boxx.rs | 2 +- 6 files changed, 9 insertions(+), 9 deletions(-) diff --git a/embedded-hal-bus/src/spi/mutex.rs b/embedded-hal-bus/src/spi/mutex.rs index d1288b995..a5f67f9e0 100644 --- a/embedded-hal-bus/src/spi/mutex.rs +++ b/embedded-hal-bus/src/spi/mutex.rs @@ -21,7 +21,7 @@ pub struct MutexDevice<'a, BUS, CS, D> { } impl<'a, BUS, CS, D> MutexDevice<'a, BUS, CS, D> { - /// Create a new MutexDevice. + /// Create a new [`MutexDevice`]. #[inline] pub fn new(bus: &'a Mutex, cs: CS, delay: D) -> Self { Self { bus, cs, delay } @@ -29,7 +29,7 @@ impl<'a, BUS, CS, D> MutexDevice<'a, BUS, CS, D> { } impl<'a, BUS, CS> MutexDevice<'a, BUS, CS, super::NoDelay> { - /// Create a new MutexDevice without support for in-transaction delays. + /// Create a new [`MutexDevice`] without support for in-transaction delays. /// /// # Panics /// diff --git a/embedded-io-adapters/src/futures_03.rs b/embedded-io-adapters/src/futures_03.rs index 7fc95aa48..a40370a24 100644 --- a/embedded-io-adapters/src/futures_03.rs +++ b/embedded-io-adapters/src/futures_03.rs @@ -51,7 +51,7 @@ impl embedded_io_async::BufRead f } fn consume(&mut self, amt: usize) { - Pin::new(&mut self.inner).consume(amt) + Pin::new(&mut self.inner).consume(amt); } } diff --git a/embedded-io-adapters/src/std.rs b/embedded-io-adapters/src/std.rs index fe2248bcb..90a2f8525 100644 --- a/embedded-io-adapters/src/std.rs +++ b/embedded-io-adapters/src/std.rs @@ -1,6 +1,6 @@ //! Adapters to/from `std::io` traits. -use embedded_io::Error; +use embedded_io::Error as _; /// Adapter from `std::io` traits. #[derive(Clone)] @@ -48,7 +48,7 @@ impl embedded_io::BufRead for FromStd { } fn consume(&mut self, amt: usize) { - self.inner.consume(amt) + self.inner.consume(amt); } } @@ -126,7 +126,7 @@ impl std::io::Seek for ToStd { } } -/// Convert a embedded-io error to a std::io::Error +/// Convert a embedded-io error to a [`std::io::Error`] pub fn to_std_error(err: T) -> std::io::Error { std::io::Error::new(err.kind().into(), format!("{err:?}")) } diff --git a/embedded-io-adapters/src/tokio_1.rs b/embedded-io-adapters/src/tokio_1.rs index 263229c5f..418284b93 100644 --- a/embedded-io-adapters/src/tokio_1.rs +++ b/embedded-io-adapters/src/tokio_1.rs @@ -70,7 +70,7 @@ impl embedded_io_async::BufRead for } fn consume(&mut self, amt: usize) { - Pin::new(&mut self.inner).consume(amt) + Pin::new(&mut self.inner).consume(amt); } } diff --git a/embedded-io-async/src/impls/boxx.rs b/embedded-io-async/src/impls/boxx.rs index 677bf328d..d6186b0c7 100644 --- a/embedded-io-async/src/impls/boxx.rs +++ b/embedded-io-async/src/impls/boxx.rs @@ -18,7 +18,7 @@ impl BufRead for Box { #[inline] fn consume(&mut self, amt: usize) { - T::consume(self, amt) + T::consume(self, amt); } } diff --git a/embedded-io/src/impls/boxx.rs b/embedded-io/src/impls/boxx.rs index 6e61ebf8d..037a6be0c 100644 --- a/embedded-io/src/impls/boxx.rs +++ b/embedded-io/src/impls/boxx.rs @@ -21,7 +21,7 @@ impl BufRead for Box { } fn consume(&mut self, amt: usize) { - T::consume(self, amt) + T::consume(self, amt); } } From b39aaed103f3829acee598b875569f622805b770 Mon Sep 17 00:00:00 2001 From: Yuri Astrakhan Date: Wed, 22 Nov 2023 17:55:06 -0500 Subject: [PATCH 115/199] Consolidate identical SPI transaction impl Consolidate three identical implementations of the `SpiDevice::transaction`. Fewer lines of code, faster compilation, fewer bugs... --- embedded-hal-bus/src/spi/critical_section.rs | 25 +---------- embedded-hal-bus/src/spi/mod.rs | 2 + embedded-hal-bus/src/spi/mutex.rs | 25 +---------- embedded-hal-bus/src/spi/refcell.rs | 25 +---------- embedded-hal-bus/src/spi/shared.rs | 44 ++++++++++++++++++++ 5 files changed, 52 insertions(+), 69 deletions(-) create mode 100644 embedded-hal-bus/src/spi/shared.rs diff --git a/embedded-hal-bus/src/spi/critical_section.rs b/embedded-hal-bus/src/spi/critical_section.rs index 5bad960af..4334cf1fe 100644 --- a/embedded-hal-bus/src/spi/critical_section.rs +++ b/embedded-hal-bus/src/spi/critical_section.rs @@ -5,6 +5,7 @@ use embedded_hal::digital::OutputPin; use embedded_hal::spi::{ErrorType, Operation, SpiBus, SpiDevice}; use super::DeviceError; +use crate::spi::shared::transaction; /// `critical-section`-based shared bus [`SpiDevice`] implementation. /// @@ -66,29 +67,7 @@ where critical_section::with(|cs| { let bus = &mut *self.bus.borrow_ref_mut(cs); - self.cs.set_low().map_err(DeviceError::Cs)?; - - let op_res = operations.iter_mut().try_for_each(|op| match op { - Operation::Read(buf) => bus.read(buf), - Operation::Write(buf) => bus.write(buf), - Operation::Transfer(read, write) => bus.transfer(read, write), - Operation::TransferInPlace(buf) => bus.transfer_in_place(buf), - Operation::DelayUs(us) => { - bus.flush()?; - self.delay.delay_us(*us); - Ok(()) - } - }); - - // On failure, it's important to still flush and deassert CS. - let flush_res = bus.flush(); - let cs_res = self.cs.set_high(); - - op_res.map_err(DeviceError::Spi)?; - flush_res.map_err(DeviceError::Spi)?; - cs_res.map_err(DeviceError::Cs)?; - - Ok(()) + transaction(operations, bus, &mut self.delay, &mut self.cs) }) } } diff --git a/embedded-hal-bus/src/spi/mod.rs b/embedded-hal-bus/src/spi/mod.rs index 090f44649..8a5f2b83f 100644 --- a/embedded-hal-bus/src/spi/mod.rs +++ b/embedded-hal-bus/src/spi/mod.rs @@ -12,6 +12,8 @@ mod mutex; #[cfg(feature = "std")] pub use mutex::*; mod critical_section; +mod shared; + pub use self::critical_section::*; #[cfg(feature = "defmt-03")] diff --git a/embedded-hal-bus/src/spi/mutex.rs b/embedded-hal-bus/src/spi/mutex.rs index d1288b995..059fae26e 100644 --- a/embedded-hal-bus/src/spi/mutex.rs +++ b/embedded-hal-bus/src/spi/mutex.rs @@ -4,6 +4,7 @@ use embedded_hal::spi::{ErrorType, Operation, SpiBus, SpiDevice}; use std::sync::Mutex; use super::DeviceError; +use crate::spi::shared::transaction; /// `std` `Mutex`-based shared bus [`SpiDevice`] implementation. /// @@ -63,28 +64,6 @@ where fn transaction(&mut self, operations: &mut [Operation<'_, Word>]) -> Result<(), Self::Error> { let bus = &mut *self.bus.lock().unwrap(); - self.cs.set_low().map_err(DeviceError::Cs)?; - - let op_res = operations.iter_mut().try_for_each(|op| match op { - Operation::Read(buf) => bus.read(buf), - Operation::Write(buf) => bus.write(buf), - Operation::Transfer(read, write) => bus.transfer(read, write), - Operation::TransferInPlace(buf) => bus.transfer_in_place(buf), - Operation::DelayUs(us) => { - bus.flush()?; - self.delay.delay_us(*us); - Ok(()) - } - }); - - // On failure, it's important to still flush and deassert CS. - let flush_res = bus.flush(); - let cs_res = self.cs.set_high(); - - op_res.map_err(DeviceError::Spi)?; - flush_res.map_err(DeviceError::Spi)?; - cs_res.map_err(DeviceError::Cs)?; - - Ok(()) + transaction(operations, bus, &mut self.delay, &mut self.cs) } } diff --git a/embedded-hal-bus/src/spi/refcell.rs b/embedded-hal-bus/src/spi/refcell.rs index d407620be..414f4d12e 100644 --- a/embedded-hal-bus/src/spi/refcell.rs +++ b/embedded-hal-bus/src/spi/refcell.rs @@ -4,6 +4,7 @@ use embedded_hal::digital::OutputPin; use embedded_hal::spi::{ErrorType, Operation, SpiBus, SpiDevice}; use super::DeviceError; +use crate::spi::shared::transaction; /// `RefCell`-based shared bus [`SpiDevice`] implementation. /// @@ -62,28 +63,6 @@ where fn transaction(&mut self, operations: &mut [Operation<'_, Word>]) -> Result<(), Self::Error> { let bus = &mut *self.bus.borrow_mut(); - self.cs.set_low().map_err(DeviceError::Cs)?; - - let op_res = operations.iter_mut().try_for_each(|op| match op { - Operation::Read(buf) => bus.read(buf), - Operation::Write(buf) => bus.write(buf), - Operation::Transfer(read, write) => bus.transfer(read, write), - Operation::TransferInPlace(buf) => bus.transfer_in_place(buf), - Operation::DelayUs(us) => { - bus.flush()?; - self.delay.delay_us(*us); - Ok(()) - } - }); - - // On failure, it's important to still flush and deassert CS. - let flush_res = bus.flush(); - let cs_res = self.cs.set_high(); - - op_res.map_err(DeviceError::Spi)?; - flush_res.map_err(DeviceError::Spi)?; - cs_res.map_err(DeviceError::Cs)?; - - Ok(()) + transaction(operations, bus, &mut self.delay, &mut self.cs) } } diff --git a/embedded-hal-bus/src/spi/shared.rs b/embedded-hal-bus/src/spi/shared.rs new file mode 100644 index 000000000..233169ced --- /dev/null +++ b/embedded-hal-bus/src/spi/shared.rs @@ -0,0 +1,44 @@ +use embedded_hal::delay::DelayUs; +use embedded_hal::digital::OutputPin; +use embedded_hal::spi::{ErrorType, Operation, SpiBus}; + +use crate::spi::DeviceError; + +/// Common implementation to perform a transaction against the device. +#[inline] +pub fn transaction( + operations: &mut [Operation], + bus: &mut BUS, + delay: &mut D, + cs: &mut CS, +) -> Result<(), DeviceError> +where + BUS: SpiBus + ErrorType, + CS: OutputPin, + D: DelayUs, + Word: Copy, +{ + cs.set_low().map_err(DeviceError::Cs)?; + + let op_res = operations.iter_mut().try_for_each(|op| match op { + Operation::Read(buf) => bus.read(buf), + Operation::Write(buf) => bus.write(buf), + Operation::Transfer(read, write) => bus.transfer(read, write), + Operation::TransferInPlace(buf) => bus.transfer_in_place(buf), + Operation::DelayUs(us) => { + bus.flush()?; + delay.delay_us(*us); + Ok(()) + } + }); + + // On failure, it's important to still flush and deassert CS. + let flush_res = bus.flush(); + let cs_res = cs.set_high(); + + op_res.map_err(DeviceError::Spi)?; + flush_res.map_err(DeviceError::Spi)?; + cs_res.map_err(DeviceError::Cs)?; + + Ok(()) +} From 3471d21ca74b046843031dbed9297d62b42e9e5a Mon Sep 17 00:00:00 2001 From: Anton Patrushev Date: Sun, 26 Nov 2023 23:55:15 +0400 Subject: [PATCH 116/199] fix embedded-io-async link in readme --- embedded-hal-async/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embedded-hal-async/README.md b/embedded-hal-async/README.md index c53790751..2f739993a 100644 --- a/embedded-hal-async/README.md +++ b/embedded-hal-async/README.md @@ -12,7 +12,7 @@ This project is developed and maintained by the [HAL team](https://github.com/ru ## Serial/UART traits -There is no serial traits in `embedded-hal-async`. Instead, use [`embedded-io-async`](https://crates.io/crates/embedded-io). +There is no serial traits in `embedded-hal-async`. Instead, use [`embedded-io-async`](https://crates.io/crates/embedded-io-async). A serial port is essentially a byte-oriented stream, and that's what `embedded-io-async` models. Sharing the traits with all byte streams has some advantages. For example, it allows generic code providing a command-line interface or a console to operate either on hardware serial ports or on virtual ones like Telnet or USB CDC-ACM. From 8ce43e5a3abcc3b649cba2f2625322b7ba80ba2b Mon Sep 17 00:00:00 2001 From: Yuri Astrakhan Date: Thu, 23 Nov 2023 11:14:18 -0500 Subject: [PATCH 117/199] Make downcasting safer --- embedded-hal/src/pwm.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/embedded-hal/src/pwm.rs b/embedded-hal/src/pwm.rs index e97a93548..d84abbcb4 100644 --- a/embedded-hal/src/pwm.rs +++ b/embedded-hal/src/pwm.rs @@ -95,8 +95,14 @@ pub trait SetDutyCycle: ErrorType { /// and that `denom` is not zero. #[inline] fn set_duty_cycle_fraction(&mut self, num: u16, denom: u16) -> Result<(), Self::Error> { + debug_assert!(num <= denom); let duty = u32::from(num) * u32::from(self.get_max_duty_cycle()) / u32::from(denom); - self.set_duty_cycle(duty as u16) + + // This is safe because we know that `num <= denom`, so `duty <= self.get_max_duty_cycle()` (u16) + #[allow(clippy::cast_possible_truncation)] + { + self.set_duty_cycle(duty as u16) + } } /// Set the duty cycle to `percent / 100` From 6eeb8c4c6b768280cafe7c050f867b305cce8ee6 Mon Sep 17 00:00:00 2001 From: Yuri Astrakhan Date: Mon, 27 Nov 2023 13:24:43 -0500 Subject: [PATCH 118/199] Ensure denum is not zero in debug build --- embedded-hal/src/pwm.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/embedded-hal/src/pwm.rs b/embedded-hal/src/pwm.rs index d84abbcb4..bf06e52a5 100644 --- a/embedded-hal/src/pwm.rs +++ b/embedded-hal/src/pwm.rs @@ -95,6 +95,7 @@ pub trait SetDutyCycle: ErrorType { /// and that `denom` is not zero. #[inline] fn set_duty_cycle_fraction(&mut self, num: u16, denom: u16) -> Result<(), Self::Error> { + debug_assert!(denom != 0); debug_assert!(num <= denom); let duty = u32::from(num) * u32::from(self.get_max_duty_cycle()) / u32::from(denom); From c9fbac0d80a9aa69ceb4c927fb498682b6120cbc Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 28 Nov 2023 19:35:14 +0100 Subject: [PATCH 119/199] Use `feature()` on nightly toolchains only. `feature()` is only allowed on Nightly, it's completely disallowed on stable and beta even for already-stabilized features. So, we autodetect whether the user is using nightly and conditionally use `feature()`. This allows the crates to Just Work on current 1.75 beta and will also Just Work when 1.75 stable is out. Keeping `feature()` is desirable to keep support for: - Espressif's xtensa rustc fork. (they build from the stable branch but enable use of `feature()`, so latest xtensa rustc still requires `feature()`) - Users of older nightlies Once xtensa rust 1.75 is out, we can remove this (upstream nightlies that require `feature()` will be quite old by then, so dropping support for them should be OK). I decided to not use already-made crates like `rustversion` to do this because they're quite big and do way more than what we need, so I felt badd adding another dep. The code is inspired from `rustversion`'s build.rs. --- embedded-hal-async/build.rs | 18 ++++++++++++++++++ embedded-hal-async/src/lib.rs | 5 ++--- embedded-hal-bus/build.rs | 18 ++++++++++++++++++ embedded-hal-bus/src/lib.rs | 7 +++++-- embedded-io-adapters/build.rs | 18 ++++++++++++++++++ embedded-io-adapters/src/lib.rs | 4 ++-- embedded-io-async/build.rs | 18 ++++++++++++++++++ embedded-io-async/src/lib.rs | 5 ++--- 8 files changed, 83 insertions(+), 10 deletions(-) create mode 100644 embedded-hal-async/build.rs create mode 100644 embedded-hal-bus/build.rs create mode 100644 embedded-io-adapters/build.rs create mode 100644 embedded-io-async/build.rs diff --git a/embedded-hal-async/build.rs b/embedded-hal-async/build.rs new file mode 100644 index 000000000..78bd27ec7 --- /dev/null +++ b/embedded-hal-async/build.rs @@ -0,0 +1,18 @@ +use std::env; +use std::ffi::OsString; +use std::process::Command; + +fn main() { + println!("cargo:rerun-if-changed=build.rs"); + + let rustc = env::var_os("RUSTC").unwrap_or_else(|| OsString::from("rustc")); + + let output = Command::new(rustc) + .arg("--version") + .output() + .expect("failed to run `rustc --version`"); + + if String::from_utf8_lossy(&output.stdout).contains("nightly") { + println!("cargo:rustc-cfg=nightly"); + } +} diff --git a/embedded-hal-async/src/lib.rs b/embedded-hal-async/src/lib.rs index cc3060708..44901deca 100644 --- a/embedded-hal-async/src/lib.rs +++ b/embedded-hal-async/src/lib.rs @@ -5,10 +5,9 @@ // Needed to pass CI, because we deny warnings. // We don't immediately remove them to not immediately break older nightlies. // When all features are stable, we'll remove them. -#![allow(stable_features)] -#![allow(unknown_lints)] +#![cfg_attr(nightly, allow(stable_features, unknown_lints))] +#![cfg_attr(nightly, feature(async_fn_in_trait, impl_trait_projections))] #![allow(async_fn_in_trait)] -#![feature(async_fn_in_trait, impl_trait_projections)] pub mod delay; pub mod digital; diff --git a/embedded-hal-bus/build.rs b/embedded-hal-bus/build.rs new file mode 100644 index 000000000..78bd27ec7 --- /dev/null +++ b/embedded-hal-bus/build.rs @@ -0,0 +1,18 @@ +use std::env; +use std::ffi::OsString; +use std::process::Command; + +fn main() { + println!("cargo:rerun-if-changed=build.rs"); + + let rustc = env::var_os("RUSTC").unwrap_or_else(|| OsString::from("rustc")); + + let output = Command::new(rustc) + .arg("--version") + .output() + .expect("failed to run `rustc --version`"); + + if String::from_utf8_lossy(&output.stdout).contains("nightly") { + println!("cargo:rustc-cfg=nightly"); + } +} diff --git a/embedded-hal-bus/src/lib.rs b/embedded-hal-bus/src/lib.rs index 501534947..e7e75ec76 100644 --- a/embedded-hal-bus/src/lib.rs +++ b/embedded-hal-bus/src/lib.rs @@ -6,8 +6,11 @@ // Needed to pass CI, because we deny warnings. // We don't immediately remove them to not immediately break older nightlies. // When all features are stable, we'll remove them. -#![cfg_attr(feature = "async", allow(stable_features))] -#![cfg_attr(feature = "async", feature(async_fn_in_trait, impl_trait_projections))] +#![cfg_attr(all(feature = "async", nightly), allow(stable_features))] +#![cfg_attr( + all(feature = "async", nightly), + feature(async_fn_in_trait, impl_trait_projections) +)] // needed to prevent defmt macros from breaking, since they emit code that does `defmt::blahblah`. #[cfg(feature = "defmt-03")] diff --git a/embedded-io-adapters/build.rs b/embedded-io-adapters/build.rs new file mode 100644 index 000000000..78bd27ec7 --- /dev/null +++ b/embedded-io-adapters/build.rs @@ -0,0 +1,18 @@ +use std::env; +use std::ffi::OsString; +use std::process::Command; + +fn main() { + println!("cargo:rerun-if-changed=build.rs"); + + let rustc = env::var_os("RUSTC").unwrap_or_else(|| OsString::from("rustc")); + + let output = Command::new(rustc) + .arg("--version") + .output() + .expect("failed to run `rustc --version`"); + + if String::from_utf8_lossy(&output.stdout).contains("nightly") { + println!("cargo:rustc-cfg=nightly"); + } +} diff --git a/embedded-io-adapters/src/lib.rs b/embedded-io-adapters/src/lib.rs index 533acc1a7..f9d0f1a5c 100644 --- a/embedded-io-adapters/src/lib.rs +++ b/embedded-io-adapters/src/lib.rs @@ -7,11 +7,11 @@ // We don't immediately remove them to not immediately break older nightlies. // When all features are stable, we'll remove them. #![cfg_attr( - any(feature = "tokio-1", feature = "futures-03"), + all(any(feature = "tokio-1", feature = "futures-03"), nightly), allow(stable_features) )] #![cfg_attr( - any(feature = "tokio-1", feature = "futures-03"), + all(any(feature = "tokio-1", feature = "futures-03"), nightly), feature(async_fn_in_trait, impl_trait_projections) )] diff --git a/embedded-io-async/build.rs b/embedded-io-async/build.rs new file mode 100644 index 000000000..78bd27ec7 --- /dev/null +++ b/embedded-io-async/build.rs @@ -0,0 +1,18 @@ +use std::env; +use std::ffi::OsString; +use std::process::Command; + +fn main() { + println!("cargo:rerun-if-changed=build.rs"); + + let rustc = env::var_os("RUSTC").unwrap_or_else(|| OsString::from("rustc")); + + let output = Command::new(rustc) + .arg("--version") + .output() + .expect("failed to run `rustc --version`"); + + if String::from_utf8_lossy(&output.stdout).contains("nightly") { + println!("cargo:rustc-cfg=nightly"); + } +} diff --git a/embedded-io-async/src/lib.rs b/embedded-io-async/src/lib.rs index 2970aa82f..1171d42a0 100644 --- a/embedded-io-async/src/lib.rs +++ b/embedded-io-async/src/lib.rs @@ -6,10 +6,9 @@ // Needed to pass CI, because we deny warnings. // We don't immediately remove them to not immediately break older nightlies. // When all features are stable, we'll remove them. -#![allow(stable_features)] -#![allow(unknown_lints)] +#![cfg_attr(nightly, allow(stable_features, unknown_lints))] +#![cfg_attr(nightly, feature(async_fn_in_trait, impl_trait_projections))] #![allow(async_fn_in_trait)] -#![feature(async_fn_in_trait, impl_trait_projections)] #[cfg(feature = "alloc")] extern crate alloc; From 2af28b46fe5e9bd6cac8167013a0b520b82cbec1 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 28 Nov 2023 21:07:02 +0100 Subject: [PATCH 120/199] pwm: rename `get_max_duty_cycle` to `max_duty_cycle`. --- embedded-hal/CHANGELOG.md | 1 + embedded-hal/src/pwm.rs | 12 ++++++------ 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/embedded-hal/CHANGELOG.md b/embedded-hal/CHANGELOG.md index 019080d5c..0b126ff95 100644 --- a/embedded-hal/CHANGELOG.md +++ b/embedded-hal/CHANGELOG.md @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - Minor document fixes. - Add #[inline] hints to most of `embedded-hal` functions. +- pwm: rename `get_max_duty_cycle` to `max_duty_cycle`. ## [v1.0.0-rc.1] - 2023-08-15 diff --git a/embedded-hal/src/pwm.rs b/embedded-hal/src/pwm.rs index bf06e52a5..782c45fff 100644 --- a/embedded-hal/src/pwm.rs +++ b/embedded-hal/src/pwm.rs @@ -69,7 +69,7 @@ pub trait SetDutyCycle: ErrorType { /// Get the maximum duty cycle value. /// /// This value corresponds to a 100% duty cycle. - fn get_max_duty_cycle(&self) -> u16; + fn max_duty_cycle(&self) -> u16; /// Set the duty cycle to `duty / max_duty`. /// @@ -86,7 +86,7 @@ pub trait SetDutyCycle: ErrorType { /// Set the duty cycle to 100%, or always active. #[inline] fn set_duty_cycle_fully_on(&mut self) -> Result<(), Self::Error> { - self.set_duty_cycle(self.get_max_duty_cycle()) + self.set_duty_cycle(self.max_duty_cycle()) } /// Set the duty cycle to `num / denom`. @@ -97,9 +97,9 @@ pub trait SetDutyCycle: ErrorType { fn set_duty_cycle_fraction(&mut self, num: u16, denom: u16) -> Result<(), Self::Error> { debug_assert!(denom != 0); debug_assert!(num <= denom); - let duty = u32::from(num) * u32::from(self.get_max_duty_cycle()) / u32::from(denom); + let duty = u32::from(num) * u32::from(self.max_duty_cycle()) / u32::from(denom); - // This is safe because we know that `num <= denom`, so `duty <= self.get_max_duty_cycle()` (u16) + // This is safe because we know that `num <= denom`, so `duty <= self.max_duty_cycle()` (u16) #[allow(clippy::cast_possible_truncation)] { self.set_duty_cycle(duty as u16) @@ -117,8 +117,8 @@ pub trait SetDutyCycle: ErrorType { impl SetDutyCycle for &mut T { #[inline] - fn get_max_duty_cycle(&self) -> u16 { - T::get_max_duty_cycle(self) + fn max_duty_cycle(&self) -> u16 { + T::max_duty_cycle(self) } #[inline] From 4ec58c85d81a191a0efddcf7269d0a4c753b2b45 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 28 Nov 2023 21:31:22 +0100 Subject: [PATCH 121/199] Rename `DelayUs` to `DelayNs`, add `delay_ns`. --- embedded-hal-async/CHANGELOG.md | 3 ++ embedded-hal-async/src/delay.rs | 34 ++++++++++++++++---- embedded-hal-bus/src/spi/critical_section.rs | 4 +-- embedded-hal-bus/src/spi/exclusive.rs | 6 ++-- embedded-hal-bus/src/spi/mod.rs | 13 +++----- embedded-hal-bus/src/spi/mutex.rs | 4 +-- embedded-hal-bus/src/spi/refcell.rs | 4 +-- embedded-hal-bus/src/spi/shared.rs | 4 +-- embedded-hal/CHANGELOG.md | 4 +++ embedded-hal/src/delay.rs | 33 ++++++++++++++----- 10 files changed, 75 insertions(+), 34 deletions(-) diff --git a/embedded-hal-async/CHANGELOG.md b/embedded-hal-async/CHANGELOG.md index c94153a3e..66e660182 100644 --- a/embedded-hal-async/CHANGELOG.md +++ b/embedded-hal-async/CHANGELOG.md @@ -9,6 +9,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - Minor document fixes. - Add #[inline] hints to most of `embedded-hal-async` functions. +- delay: Rename `DelayUs` to `DelayNs` +- delay: Add `DelayNs::delay_ns()` +- delay: Add default impls of `delay_ms` and `delay_us` based on `delay_ns`. ## [v1.0.0-rc.1] - 2023-08-15 diff --git a/embedded-hal-async/src/delay.rs b/embedded-hal-async/src/delay.rs index 62bd742f2..87c540d2d 100644 --- a/embedded-hal-async/src/delay.rs +++ b/embedded-hal-async/src/delay.rs @@ -1,20 +1,42 @@ //! Delays. -/// Microsecond delay. -pub trait DelayUs { +/// Delay with up to nanosecond precision. +pub trait DelayNs { + /// Pauses execution for at minimum `ns` nanoseconds. Pause can be longer + /// if the implementation requires it due to precision/timing issues. + async fn delay_ns(&mut self, ns: u32); + /// Pauses execution for at minimum `us` microseconds. Pause can be longer /// if the implementation requires it due to precision/timing issues. - async fn delay_us(&mut self, us: u32); + async fn delay_us(&mut self, mut us: u32) { + while us > 4_294_967 { + us -= 4_294_967; + self.delay_ns(4_294_967_000).await; + } + self.delay_ns(us * 1_000).await; + } /// Pauses execution for at minimum `ms` milliseconds. Pause can be longer /// if the implementation requires it due to precision/timing issues. - async fn delay_ms(&mut self, ms: u32); + #[inline] + async fn delay_ms(&mut self, mut ms: u32) { + while ms > 4294 { + ms -= 4294; + self.delay_ns(4_294_000_000).await; + } + self.delay_ns(ms * 1_000_000).await; + } } -impl DelayUs for &mut T +impl DelayNs for &mut T where - T: DelayUs + ?Sized, + T: DelayNs + ?Sized, { + #[inline] + async fn delay_ns(&mut self, ns: u32) { + T::delay_ns(self, ns).await; + } + #[inline] async fn delay_us(&mut self, us: u32) { T::delay_us(self, us).await; diff --git a/embedded-hal-bus/src/spi/critical_section.rs b/embedded-hal-bus/src/spi/critical_section.rs index 4334cf1fe..0b37c3ed9 100644 --- a/embedded-hal-bus/src/spi/critical_section.rs +++ b/embedded-hal-bus/src/spi/critical_section.rs @@ -1,6 +1,6 @@ use core::cell::RefCell; use critical_section::Mutex; -use embedded_hal::delay::DelayUs; +use embedded_hal::delay::DelayNs; use embedded_hal::digital::OutputPin; use embedded_hal::spi::{ErrorType, Operation, SpiBus, SpiDevice}; @@ -60,7 +60,7 @@ impl<'a, Word: Copy + 'static, BUS, CS, D> SpiDevice for CriticalSectionDe where BUS: SpiBus, CS: OutputPin, - D: DelayUs, + D: DelayNs, { #[inline] fn transaction(&mut self, operations: &mut [Operation<'_, Word>]) -> Result<(), Self::Error> { diff --git a/embedded-hal-bus/src/spi/exclusive.rs b/embedded-hal-bus/src/spi/exclusive.rs index 1642269e7..fcc8c6114 100644 --- a/embedded-hal-bus/src/spi/exclusive.rs +++ b/embedded-hal-bus/src/spi/exclusive.rs @@ -1,11 +1,11 @@ //! SPI bus sharing mechanisms. -use embedded_hal::delay::DelayUs; +use embedded_hal::delay::DelayNs; use embedded_hal::digital::OutputPin; use embedded_hal::spi::{ErrorType, Operation, SpiBus, SpiDevice}; #[cfg(feature = "async")] use embedded_hal_async::{ - delay::DelayUs as AsyncDelayUs, + delay::DelayNs as AsyncDelayUs, spi::{SpiBus as AsyncSpiBus, SpiDevice as AsyncSpiDevice}, }; @@ -70,7 +70,7 @@ impl SpiDevice for ExclusiveDevice, CS: OutputPin, - D: DelayUs, + D: DelayNs, { #[inline] fn transaction(&mut self, operations: &mut [Operation<'_, Word>]) -> Result<(), Self::Error> { diff --git a/embedded-hal-bus/src/spi/mod.rs b/embedded-hal-bus/src/spi/mod.rs index 8a5f2b83f..15c9e7046 100644 --- a/embedded-hal-bus/src/spi/mod.rs +++ b/embedded-hal-bus/src/spi/mod.rs @@ -53,23 +53,18 @@ fn no_delay_panic() { panic!("You've tried to execute a SPI transaction containing a `Operation::Delay` in a `SpiDevice` created with `new_no_delay()`. Create it with `new()` instead, passing a `DelayUs` implementation."); } -impl embedded_hal::delay::DelayUs for NoDelay { +impl embedded_hal::delay::DelayNs for NoDelay { #[inline] - fn delay_us(&mut self, _us: u32) { + fn delay_ns(&mut self, _ns: u32) { no_delay_panic(); } } #[cfg(feature = "async")] #[cfg_attr(docsrs, doc(cfg(feature = "async")))] -impl embedded_hal_async::delay::DelayUs for NoDelay { +impl embedded_hal_async::delay::DelayNs for NoDelay { #[inline] - async fn delay_us(&mut self, _us: u32) { - no_delay_panic(); - } - - #[inline] - async fn delay_ms(&mut self, _ms: u32) { + async fn delay_ns(&mut self, _ns: u32) { no_delay_panic(); } } diff --git a/embedded-hal-bus/src/spi/mutex.rs b/embedded-hal-bus/src/spi/mutex.rs index 69a0822e7..449a449b3 100644 --- a/embedded-hal-bus/src/spi/mutex.rs +++ b/embedded-hal-bus/src/spi/mutex.rs @@ -1,4 +1,4 @@ -use embedded_hal::delay::DelayUs; +use embedded_hal::delay::DelayNs; use embedded_hal::digital::OutputPin; use embedded_hal::spi::{ErrorType, Operation, SpiBus, SpiDevice}; use std::sync::Mutex; @@ -58,7 +58,7 @@ impl<'a, Word: Copy + 'static, BUS, CS, D> SpiDevice for MutexDevice<'a, B where BUS: SpiBus, CS: OutputPin, - D: DelayUs, + D: DelayNs, { #[inline] fn transaction(&mut self, operations: &mut [Operation<'_, Word>]) -> Result<(), Self::Error> { diff --git a/embedded-hal-bus/src/spi/refcell.rs b/embedded-hal-bus/src/spi/refcell.rs index 414f4d12e..3c8b465fe 100644 --- a/embedded-hal-bus/src/spi/refcell.rs +++ b/embedded-hal-bus/src/spi/refcell.rs @@ -1,5 +1,5 @@ use core::cell::RefCell; -use embedded_hal::delay::DelayUs; +use embedded_hal::delay::DelayNs; use embedded_hal::digital::OutputPin; use embedded_hal::spi::{ErrorType, Operation, SpiBus, SpiDevice}; @@ -57,7 +57,7 @@ impl<'a, Word: Copy + 'static, BUS, CS, D> SpiDevice for RefCellDevice<'a, where BUS: SpiBus, CS: OutputPin, - D: DelayUs, + D: DelayNs, { #[inline] fn transaction(&mut self, operations: &mut [Operation<'_, Word>]) -> Result<(), Self::Error> { diff --git a/embedded-hal-bus/src/spi/shared.rs b/embedded-hal-bus/src/spi/shared.rs index 233169ced..bc0a70d56 100644 --- a/embedded-hal-bus/src/spi/shared.rs +++ b/embedded-hal-bus/src/spi/shared.rs @@ -1,4 +1,4 @@ -use embedded_hal::delay::DelayUs; +use embedded_hal::delay::DelayNs; use embedded_hal::digital::OutputPin; use embedded_hal::spi::{ErrorType, Operation, SpiBus}; @@ -15,7 +15,7 @@ pub fn transaction( where BUS: SpiBus + ErrorType, CS: OutputPin, - D: DelayUs, + D: DelayNs, Word: Copy, { cs.set_low().map_err(DeviceError::Cs)?; diff --git a/embedded-hal/CHANGELOG.md b/embedded-hal/CHANGELOG.md index 0b126ff95..0f171b32c 100644 --- a/embedded-hal/CHANGELOG.md +++ b/embedded-hal/CHANGELOG.md @@ -10,6 +10,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - Minor document fixes. - Add #[inline] hints to most of `embedded-hal` functions. - pwm: rename `get_max_duty_cycle` to `max_duty_cycle`. +- delay: Rename `DelayUs` to `DelayNs` +- delay: Add `DelayNs::delay_ns()` +- delay: Add default impls of `delay_ms` and `delay_us` based on `delay_ns`. +- delay: Make the default impl of `delay_ms` more efficient, it now does less calls to the underlying `delay_ns` (previously `delay_us`). ## [v1.0.0-rc.1] - 2023-08-15 diff --git a/embedded-hal/src/delay.rs b/embedded-hal/src/delay.rs index 77b5633d7..8a1fc3c9d 100644 --- a/embedded-hal/src/delay.rs +++ b/embedded-hal/src/delay.rs @@ -1,25 +1,42 @@ //! Delays. -/// Microsecond delay. -pub trait DelayUs { +/// Delay with up to nanosecond precision. +pub trait DelayNs { + /// Pauses execution for at minimum `ns` nanoseconds. Pause can be longer + /// if the implementation requires it due to precision/timing issues. + fn delay_ns(&mut self, ns: u32); + /// 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); + fn delay_us(&mut self, mut us: u32) { + while us > 4_294_967 { + us -= 4_294_967; + self.delay_ns(4_294_967_000); + } + self.delay_ns(us * 1_000); + } /// Pauses execution for at minimum `ms` milliseconds. Pause can be longer /// if the implementation requires it due to precision/timing issues. #[inline] - fn delay_ms(&mut self, ms: u32) { - for _ in 0..ms { - self.delay_us(1000); + fn delay_ms(&mut self, mut ms: u32) { + while ms > 4294 { + ms -= 4294; + self.delay_ns(4_294_000_000); } + self.delay_ns(ms * 1_000_000); } } -impl DelayUs for &mut T +impl DelayNs for &mut T where - T: DelayUs + ?Sized, + T: DelayNs + ?Sized, { + #[inline] + fn delay_ns(&mut self, ns: u32) { + T::delay_ns(self, ns); + } + #[inline] fn delay_us(&mut self, us: u32) { T::delay_us(self, us); From 493dfd82cb3113d13d994286b833f4dd085558c3 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 28 Nov 2023 21:34:23 +0100 Subject: [PATCH 122/199] bus: use shared `fn transaction` for exclusive bus too. --- embedded-hal-bus/src/spi/exclusive.rs | 24 +----------------------- 1 file changed, 1 insertion(+), 23 deletions(-) diff --git a/embedded-hal-bus/src/spi/exclusive.rs b/embedded-hal-bus/src/spi/exclusive.rs index fcc8c6114..e83551eba 100644 --- a/embedded-hal-bus/src/spi/exclusive.rs +++ b/embedded-hal-bus/src/spi/exclusive.rs @@ -74,29 +74,7 @@ where { #[inline] fn transaction(&mut self, operations: &mut [Operation<'_, Word>]) -> Result<(), Self::Error> { - self.cs.set_low().map_err(DeviceError::Cs)?; - - let op_res = operations.iter_mut().try_for_each(|op| match op { - Operation::Read(buf) => self.bus.read(buf), - Operation::Write(buf) => self.bus.write(buf), - Operation::Transfer(read, write) => self.bus.transfer(read, write), - Operation::TransferInPlace(buf) => self.bus.transfer_in_place(buf), - Operation::DelayUs(us) => { - self.bus.flush()?; - self.delay.delay_us(*us); - Ok(()) - } - }); - - // On failure, it's important to still flush and deassert CS. - let flush_res = self.bus.flush(); - let cs_res = self.cs.set_high(); - - op_res.map_err(DeviceError::Spi)?; - flush_res.map_err(DeviceError::Spi)?; - cs_res.map_err(DeviceError::Cs)?; - - Ok(()) + transaction(operations, &mut self.bus, &mut self.delay, &mut self.cs) } } From c2eab4d4bc76349dca736a5b14bcb8148c732b1d Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 28 Nov 2023 22:47:27 +0100 Subject: [PATCH 123/199] spi: make `Operation::DelayNs` with nanosecond granularity. --- embedded-hal-async/CHANGELOG.md | 1 + embedded-hal-bus/src/spi/critical_section.rs | 2 +- embedded-hal-bus/src/spi/exclusive.rs | 11 ++++++----- embedded-hal-bus/src/spi/mod.rs | 4 ++-- embedded-hal-bus/src/spi/mutex.rs | 2 +- embedded-hal-bus/src/spi/refcell.rs | 2 +- embedded-hal-bus/src/spi/shared.rs | 4 ++-- embedded-hal/CHANGELOG.md | 1 + embedded-hal/src/spi.rs | 4 ++-- 9 files changed, 17 insertions(+), 14 deletions(-) diff --git a/embedded-hal-async/CHANGELOG.md b/embedded-hal-async/CHANGELOG.md index 66e660182..f692512c3 100644 --- a/embedded-hal-async/CHANGELOG.md +++ b/embedded-hal-async/CHANGELOG.md @@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - delay: Rename `DelayUs` to `DelayNs` - delay: Add `DelayNs::delay_ns()` - delay: Add default impls of `delay_ms` and `delay_us` based on `delay_ns`. +- spi: Rename `Operation::DelayUs` to `Operation::DelayNs`, with nanosecond precision. ## [v1.0.0-rc.1] - 2023-08-15 diff --git a/embedded-hal-bus/src/spi/critical_section.rs b/embedded-hal-bus/src/spi/critical_section.rs index 0b37c3ed9..7c87eeaaa 100644 --- a/embedded-hal-bus/src/spi/critical_section.rs +++ b/embedded-hal-bus/src/spi/critical_section.rs @@ -37,7 +37,7 @@ impl<'a, BUS, CS> CriticalSectionDevice<'a, BUS, CS, super::NoDelay> { /// # Panics /// /// The returned device will panic if you try to execute a transaction - /// that contains any operations of type [`Operation::DelayUs`]. + /// that contains any operations of type [`Operation::DelayNs`]. #[inline] pub fn new_no_delay(bus: &'a Mutex>, cs: CS) -> Self { Self { diff --git a/embedded-hal-bus/src/spi/exclusive.rs b/embedded-hal-bus/src/spi/exclusive.rs index e83551eba..3717844c1 100644 --- a/embedded-hal-bus/src/spi/exclusive.rs +++ b/embedded-hal-bus/src/spi/exclusive.rs @@ -5,10 +5,11 @@ use embedded_hal::digital::OutputPin; use embedded_hal::spi::{ErrorType, Operation, SpiBus, SpiDevice}; #[cfg(feature = "async")] use embedded_hal_async::{ - delay::DelayNs as AsyncDelayUs, + delay::DelayNs as AsyncDelayNs, spi::{SpiBus as AsyncSpiBus, SpiDevice as AsyncSpiDevice}, }; +use super::shared::transaction; use super::DeviceError; /// [`SpiDevice`] implementation with exclusive access to the bus (not shared). @@ -47,7 +48,7 @@ impl ExclusiveDevice { /// # Panics /// /// The returned device will panic if you try to execute a transaction - /// that contains any operations of type `Operation::DelayUs`. + /// that contains any operations of type [`Operation::DelayNs`]. #[inline] pub fn new_no_delay(bus: BUS, cs: CS) -> Self { Self { @@ -84,7 +85,7 @@ impl AsyncSpiDevice for ExclusiveDevice< where BUS: AsyncSpiBus, CS: OutputPin, - D: AsyncDelayUs, + D: AsyncDelayNs, { #[inline] async fn transaction( @@ -100,10 +101,10 @@ where Operation::Write(buf) => self.bus.write(buf).await, Operation::Transfer(read, write) => self.bus.transfer(read, write).await, Operation::TransferInPlace(buf) => self.bus.transfer_in_place(buf).await, - Operation::DelayUs(us) => match self.bus.flush().await { + Operation::DelayNs(ns) => match self.bus.flush().await { Err(e) => Err(e), Ok(()) => { - self.delay.delay_us(*us).await; + self.delay.delay_ns(*ns).await; Ok(()) } }, diff --git a/embedded-hal-bus/src/spi/mod.rs b/embedded-hal-bus/src/spi/mod.rs index 15c9e7046..8b51242d8 100644 --- a/embedded-hal-bus/src/spi/mod.rs +++ b/embedded-hal-bus/src/spi/mod.rs @@ -43,14 +43,14 @@ where } } -/// Dummy `DelayUs` implementation that panics on use. +/// Dummy [`DelayNs`](embedded_hal::delay::DelayNs) implementation that panics on use. #[derive(Copy, Clone, Eq, PartialEq, Debug)] #[cfg_attr(feature = "defmt-03", derive(defmt::Format))] pub struct NoDelay; #[cold] fn no_delay_panic() { - panic!("You've tried to execute a SPI transaction containing a `Operation::Delay` in a `SpiDevice` created with `new_no_delay()`. Create it with `new()` instead, passing a `DelayUs` implementation."); + panic!("You've tried to execute a SPI transaction containing a `Operation::DelayNs` in a `SpiDevice` created with `new_no_delay()`. Create it with `new()` instead, passing a `DelayNs` implementation."); } impl embedded_hal::delay::DelayNs for NoDelay { diff --git a/embedded-hal-bus/src/spi/mutex.rs b/embedded-hal-bus/src/spi/mutex.rs index 449a449b3..3167c72d3 100644 --- a/embedded-hal-bus/src/spi/mutex.rs +++ b/embedded-hal-bus/src/spi/mutex.rs @@ -35,7 +35,7 @@ impl<'a, BUS, CS> MutexDevice<'a, BUS, CS, super::NoDelay> { /// # Panics /// /// The returned device will panic if you try to execute a transaction - /// that contains any operations of type `Operation::DelayUs`. + /// that contains any operations of type [`Operation::DelayNs`]. #[inline] pub fn new_no_delay(bus: &'a Mutex, cs: CS) -> Self { Self { diff --git a/embedded-hal-bus/src/spi/refcell.rs b/embedded-hal-bus/src/spi/refcell.rs index 3c8b465fe..da8602624 100644 --- a/embedded-hal-bus/src/spi/refcell.rs +++ b/embedded-hal-bus/src/spi/refcell.rs @@ -34,7 +34,7 @@ impl<'a, BUS, CS> RefCellDevice<'a, BUS, CS, super::NoDelay> { /// # Panics /// /// The returned device will panic if you try to execute a transaction - /// that contains any operations of type `Operation::DelayUs`. + /// that contains any operations of type [`Operation::DelayNs`]. #[inline] pub fn new_no_delay(bus: &'a RefCell, cs: CS) -> Self { Self { diff --git a/embedded-hal-bus/src/spi/shared.rs b/embedded-hal-bus/src/spi/shared.rs index bc0a70d56..95730ba19 100644 --- a/embedded-hal-bus/src/spi/shared.rs +++ b/embedded-hal-bus/src/spi/shared.rs @@ -25,9 +25,9 @@ where Operation::Write(buf) => bus.write(buf), Operation::Transfer(read, write) => bus.transfer(read, write), Operation::TransferInPlace(buf) => bus.transfer_in_place(buf), - Operation::DelayUs(us) => { + Operation::DelayNs(ns) => { bus.flush()?; - delay.delay_us(*us); + delay.delay_ns(*ns); Ok(()) } }); diff --git a/embedded-hal/CHANGELOG.md b/embedded-hal/CHANGELOG.md index 0f171b32c..c207cfa0a 100644 --- a/embedded-hal/CHANGELOG.md +++ b/embedded-hal/CHANGELOG.md @@ -14,6 +14,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - delay: Add `DelayNs::delay_ns()` - delay: Add default impls of `delay_ms` and `delay_us` based on `delay_ns`. - delay: Make the default impl of `delay_ms` more efficient, it now does less calls to the underlying `delay_ns` (previously `delay_us`). +- spi: Rename `Operation::DelayUs` to `Operation::DelayNs`, with nanosecond precision. ## [v1.0.0-rc.1] - 2023-08-15 diff --git a/embedded-hal/src/spi.rs b/embedded-hal/src/spi.rs index c8c8224fd..ca61269e7 100644 --- a/embedded-hal/src/spi.rs +++ b/embedded-hal/src/spi.rs @@ -324,8 +324,8 @@ pub enum Operation<'a, Word: 'static> { /// /// Equivalent to [`SpiBus::transfer_in_place`]. TransferInPlace(&'a mut [Word]), - /// Delay for at least the specified number of microseconds. - DelayUs(u32), + /// Delay for at least the specified number of nanoseconds. + DelayNs(u32), } /// SPI device trait. From 2767cbdce1ab46d11e3d1281446b585aa1517cca Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 29 Nov 2023 14:48:23 +0100 Subject: [PATCH 124/199] release embedded-hal-* v1.0.0-rc.2, embedded-io-{async,adapters} v0.6.1 --- embedded-hal-async/CHANGELOG.md | 9 ++++++++- embedded-hal-async/Cargo.toml | 4 ++-- embedded-hal-bus/CHANGELOG.md | 9 ++++++++- embedded-hal-bus/Cargo.toml | 6 +++--- embedded-hal-nb/CHANGELOG.md | 9 ++++++++- embedded-hal-nb/Cargo.toml | 4 ++-- embedded-hal/CHANGELOG.md | 7 ++++++- embedded-hal/Cargo.toml | 2 +- embedded-io-adapters/CHANGELOG.md | 3 +++ embedded-io-adapters/Cargo.toml | 4 ++-- embedded-io-async/CHANGELOG.md | 4 ++++ embedded-io-async/Cargo.toml | 2 +- 12 files changed, 48 insertions(+), 15 deletions(-) diff --git a/embedded-hal-async/CHANGELOG.md b/embedded-hal-async/CHANGELOG.md index f692512c3..468c3b711 100644 --- a/embedded-hal-async/CHANGELOG.md +++ b/embedded-hal-async/CHANGELOG.md @@ -7,12 +7,18 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] +No unreleased changes + +## [v1.0.0-rc.2] - 2023-11-28 + +- Updated `embedded-hal` to version `1.0.0-rc.2`. - Minor document fixes. - Add #[inline] hints to most of `embedded-hal-async` functions. - delay: Rename `DelayUs` to `DelayNs` - delay: Add `DelayNs::delay_ns()` - delay: Add default impls of `delay_ms` and `delay_us` based on `delay_ns`. - spi: Rename `Operation::DelayUs` to `Operation::DelayNs`, with nanosecond precision. +- Use `feature()` on nightly toolchains only. This adds support for 1.75 beta and stable. ## [v1.0.0-rc.1] - 2023-08-15 @@ -76,7 +82,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/). First release to crates.io -[Unreleased]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-async-v1.0.0-rc.1...HEAD +[Unreleased]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-async-v1.0.0-rc.2...HEAD +[v1.0.0-rc.2]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-async-v1.0.0-rc.1...embedded-hal-async-v1.0.0-rc.2 [v1.0.0-rc.1]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-async-v0.2.0-alpha.2...embedded-hal-async-v1.0.0-rc.1 [v0.2.0-alpha.2]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-async-v0.2.0-alpha.1...embedded-hal-async-v0.2.0-alpha.2 [v0.2.0-alpha.1]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-async-v0.2.0-alpha.0...embedded-hal-async-v0.2.0-alpha.1 diff --git a/embedded-hal-async/Cargo.toml b/embedded-hal-async/Cargo.toml index b10cfc734..231f00e2d 100644 --- a/embedded-hal-async/Cargo.toml +++ b/embedded-hal-async/Cargo.toml @@ -11,12 +11,12 @@ license = "MIT OR Apache-2.0" name = "embedded-hal-async" readme = "README.md" repository = "https://github.com/rust-embedded/embedded-hal" -version = "1.0.0-rc.1" +version = "1.0.0-rc.2" rust-version = "1.65.0" [features] defmt-03 = ["dep:defmt-03", "embedded-hal/defmt-03"] [dependencies] -embedded-hal = { version = "=1.0.0-rc.1", path = "../embedded-hal" } +embedded-hal = { version = "=1.0.0-rc.2", path = "../embedded-hal" } defmt-03 = { package = "defmt", version = "0.3", optional = true } \ No newline at end of file diff --git a/embedded-hal-bus/CHANGELOG.md b/embedded-hal-bus/CHANGELOG.md index 59b398d1a..6858e9c19 100644 --- a/embedded-hal-bus/CHANGELOG.md +++ b/embedded-hal-bus/CHANGELOG.md @@ -7,8 +7,14 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] +No unreleased changes + +## [v1.0.0-rc.2] - 2023-11-28 + +- Updated `embedded-hal(-async)` to version `1.0.0-rc.2`. - Minor document fixes. - Add #[inline] hints to most of `embedded-hal-bus` functions. +- Use `feature()` on nightly toolchains only. This adds async support for 1.75 beta and stable. ## [v0.1.0-rc.1] - 2023-08-15 @@ -42,7 +48,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/). First release to crates.io -[Unreleased]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-bus-v0.1.0-rc.1...HEAD +[Unreleased]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-bus-v0.1.0-rc.2...HEAD +[v0.1.0-rc.2]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-bus-v0.1.0-rc.1...embedded-hal-bus-v0.1.0-rc.2 [v0.1.0-rc.1]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-bus-v0.1.0-alpha.3...embedded-hal-bus-v0.1.0-rc.1 [v0.1.0-alpha.3]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-bus-v0.1.0-alpha.2...embedded-hal-bus-v0.1.0-alpha.3 [v0.1.0-alpha.2]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-bus-v0.1.0-alpha.1...embedded-hal-bus-v0.1.0-alpha.2 diff --git a/embedded-hal-bus/Cargo.toml b/embedded-hal-bus/Cargo.toml index ab69c254f..937a49b2c 100644 --- a/embedded-hal-bus/Cargo.toml +++ b/embedded-hal-bus/Cargo.toml @@ -12,7 +12,7 @@ license = "MIT OR Apache-2.0" name = "embedded-hal-bus" readme = "README.md" repository = "https://github.com/rust-embedded/embedded-hal" -version = "0.1.0-rc.1" +version = "0.1.0-rc.2" [features] std = [] @@ -20,8 +20,8 @@ async = ["dep:embedded-hal-async"] defmt-03 = ["dep:defmt-03", "embedded-hal/defmt-03", "embedded-hal-async?/defmt-03"] [dependencies] -embedded-hal = { version = "=1.0.0-rc.1", path = "../embedded-hal" } -embedded-hal-async = { version = "=1.0.0-rc.1", path = "../embedded-hal-async", optional = true } +embedded-hal = { version = "=1.0.0-rc.2", path = "../embedded-hal" } +embedded-hal-async = { version = "=1.0.0-rc.2", path = "../embedded-hal-async", optional = true } critical-section = { version = "1.0" } defmt-03 = { package = "defmt", version = "0.3", optional = true } diff --git a/embedded-hal-nb/CHANGELOG.md b/embedded-hal-nb/CHANGELOG.md index f4a1e7944..11753a27f 100644 --- a/embedded-hal-nb/CHANGELOG.md +++ b/embedded-hal-nb/CHANGELOG.md @@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] +No unreleased changes + +## [v1.0.0-rc.2] - 2023-11-28 + +- Updated `embedded-hal` to version `1.0.0-rc.2`. - Minor document fixes. - Add #[inline] hints to most of `embedded-hal-nb` functions. @@ -35,7 +40,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/). First release to crates.io -[Unreleased]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-nb-v1.0.0-alpha.3...HEAD +[Unreleased]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-nb-v1.0.0-rc.2...HEAD +[v1.0.0-rc.2]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-nb-v1.0.0-rc.1...embedded-hal-nb-v1.0.0-rc.2 +[v1.0.0-rc.1]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-nb-v1.0.0-alpha.3...embedded-hal-nb-v1.0.0-rc.1 [v1.0.0-alpha.3]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-nb-v1.0.0-alpha.2...embedded-hal-nb-v1.0.0-alpha.3 [v1.0.0-alpha.2]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-nb-v1.0.0-alpha.1...embedded-hal-nb-v1.0.0-alpha.2 [v1.0.0-alpha.1]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-nb-v1.0.0-alpha.0...embedded-hal-nb-v1.0.0-alpha.1 diff --git a/embedded-hal-nb/Cargo.toml b/embedded-hal-nb/Cargo.toml index 7e5e3c5a8..52c6755ea 100644 --- a/embedded-hal-nb/Cargo.toml +++ b/embedded-hal-nb/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "embedded-hal-nb" -version = "1.0.0-rc.1" +version = "1.0.0-rc.2" edition = "2021" rust-version = "1.56" @@ -13,7 +13,7 @@ readme = "README.md" repository = "https://github.com/rust-embedded/embedded-hal" [dependencies] -embedded-hal = { version = "=1.0.0-rc.1", path = "../embedded-hal" } +embedded-hal = { version = "=1.0.0-rc.2", path = "../embedded-hal" } nb = "1" [dev-dependencies] diff --git a/embedded-hal/CHANGELOG.md b/embedded-hal/CHANGELOG.md index c207cfa0a..a177ba7f1 100644 --- a/embedded-hal/CHANGELOG.md +++ b/embedded-hal/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] +No unreleased changes + +## [v1.0.0-rc.2] - 2023-11-28 + - Minor document fixes. - Add #[inline] hints to most of `embedded-hal` functions. - pwm: rename `get_max_duty_cycle` to `max_duty_cycle`. @@ -306,7 +310,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/). Initial release -[Unreleased]: https://github.com/rust-embedded/embedded-hal/compare/v1.0.0-rc.1...HEAD +[Unreleased]: https://github.com/rust-embedded/embedded-hal/compare/v1.0.0-rc.2...HEAD +[v1.0.0-rc.2]: https://github.com/rust-embedded/embedded-hal/compare/v1.0.0-rc.1...v1.0.0-rc.2 [v1.0.0-rc.1]: https://github.com/rust-embedded/embedded-hal/compare/v1.0.0-alpha.11...v1.0.0-rc.1 [v1.0.0-alpha.11]: https://github.com/rust-embedded/embedded-hal/compare/v1.0.0-alpha.10...v1.0.0-alpha.11 [v1.0.0-alpha.10]: https://github.com/rust-embedded/embedded-hal/compare/v1.0.0-alpha.9...v1.0.0-alpha.10 diff --git a/embedded-hal/Cargo.toml b/embedded-hal/Cargo.toml index fb549a7e7..a26ae3372 100644 --- a/embedded-hal/Cargo.toml +++ b/embedded-hal/Cargo.toml @@ -14,7 +14,7 @@ license = "MIT OR Apache-2.0" name = "embedded-hal" readme = "README.md" repository = "https://github.com/rust-embedded/embedded-hal" -version = "1.0.0-rc.1" +version = "1.0.0-rc.2" [features] defmt-03 = ["dep:defmt-03"] diff --git a/embedded-io-adapters/CHANGELOG.md b/embedded-io-adapters/CHANGELOG.md index e9205d681..b540b67b4 100644 --- a/embedded-io-adapters/CHANGELOG.md +++ b/embedded-io-adapters/CHANGELOG.md @@ -9,7 +9,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 Add unreleased changes here +## 0.6.1 - 2023-11-28 + - Handle reading from `FromTokio` with empty buffer, ensuring `Ok(0)` is always returned. +- Use `feature()` on nightly toolchains only. This adds async support for 1.75 beta and stable. ## 0.6.0 - 2023-10-02 diff --git a/embedded-io-adapters/Cargo.toml b/embedded-io-adapters/Cargo.toml index 8cf97eceb..89721f1bb 100644 --- a/embedded-io-adapters/Cargo.toml +++ b/embedded-io-adapters/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "embedded-io-adapters" -version = "0.6.0" +version = "0.6.1" edition = "2021" rust-version = "1.60" description = "Adapters between the `embedded-io` traits and other I/O traits" @@ -19,7 +19,7 @@ futures-03 = ["std", "dep:futures", "dep:embedded-io-async", "embedded-io-async? [dependencies] embedded-io = { version = "0.6", path = "../embedded-io" } -embedded-io-async = { version = "0.6", path = "../embedded-io-async", optional = true } +embedded-io-async = { version = "0.6.1", path = "../embedded-io-async", optional = true } futures = { version = "0.3.21", features = ["std"], default-features = false, optional = true } tokio = { version = "1", features = ["io-util"], default-features = false, optional = true } diff --git a/embedded-io-async/CHANGELOG.md b/embedded-io-async/CHANGELOG.md index 94648ee7e..5f421e351 100644 --- a/embedded-io-async/CHANGELOG.md +++ b/embedded-io-async/CHANGELOG.md @@ -9,6 +9,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 Add unreleased changes here +## 0.6.1 - 2023-11-28 + +- Use `feature()` on nightly toolchains only. This adds support for 1.75 beta and stable. + ## 0.6.0 - 2023-10-02 - Prohibit `Write::write` implementations returning `Ok(0)` unless there is no diff --git a/embedded-io-async/Cargo.toml b/embedded-io-async/Cargo.toml index fc704646b..87891795c 100644 --- a/embedded-io-async/Cargo.toml +++ b/embedded-io-async/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "embedded-io-async" -version = "0.6.0" +version = "0.6.1" edition = "2021" description = "Async embedded IO traits" repository = "https://github.com/rust-embedded/embedded-hal" From ca8ef80477914652c1f442c39dd240400e838f3b Mon Sep 17 00:00:00 2001 From: James Munns Date: Thu, 30 Nov 2023 14:05:05 +0100 Subject: [PATCH 125/199] Use named constants to clarify delay-stepdown behavior --- embedded-hal/src/delay.rs | 29 +++++++++++++++++++++-------- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/embedded-hal/src/delay.rs b/embedded-hal/src/delay.rs index 8a1fc3c9d..213c42491 100644 --- a/embedded-hal/src/delay.rs +++ b/embedded-hal/src/delay.rs @@ -1,5 +1,10 @@ //! Delays. +/// Nanoseconds per microsecond +const NANOS_PER_MICRO: u32 = 1_000; +/// Nanoseconds per millisecond +const NANOS_PER_MILLI: u32 = 1_000_000; + /// Delay with up to nanosecond precision. pub trait DelayNs { /// Pauses execution for at minimum `ns` nanoseconds. Pause can be longer @@ -9,22 +14,30 @@ pub trait DelayNs { /// 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, mut us: u32) { - while us > 4_294_967 { - us -= 4_294_967; - self.delay_ns(4_294_967_000); + const MAX_MICROS: u32 = u32::MAX / NANOS_PER_MICRO; + + // Avoid potential overflow if micro -> micro conversion is too large + while us > MAX_MICROS { + us -= MAX_MICROS; + self.delay_ns(MAX_MICROS * NANOS_PER_MICRO); } - self.delay_ns(us * 1_000); + + self.delay_ns(us * NANOS_PER_MICRO); } /// Pauses execution for at minimum `ms` milliseconds. Pause can be longer /// if the implementation requires it due to precision/timing issues. #[inline] fn delay_ms(&mut self, mut ms: u32) { - while ms > 4294 { - ms -= 4294; - self.delay_ns(4_294_000_000); + const MAX_MILLIS: u32 = u32::MAX / NANOS_PER_MILLI; + + // Avoid potential overflow if milli -> micro conversion is too large + while ms > MAX_MILLIS { + ms -= MAX_MILLIS; + self.delay_ns(MAX_MILLIS * NANOS_PER_MILLI); } - self.delay_ns(ms * 1_000_000); + + self.delay_ns(ms * NANOS_PER_MILLI); } } From 5656d678f09a9045b9be8d26a4fa88af0c0b0ee5 Mon Sep 17 00:00:00 2001 From: James Munns Date: Thu, 30 Nov 2023 14:09:20 +0100 Subject: [PATCH 126/199] Fix comments --- embedded-hal/src/delay.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/embedded-hal/src/delay.rs b/embedded-hal/src/delay.rs index 213c42491..01e563750 100644 --- a/embedded-hal/src/delay.rs +++ b/embedded-hal/src/delay.rs @@ -16,7 +16,7 @@ pub trait DelayNs { fn delay_us(&mut self, mut us: u32) { const MAX_MICROS: u32 = u32::MAX / NANOS_PER_MICRO; - // Avoid potential overflow if micro -> micro conversion is too large + // Avoid potential overflow if micro -> nano conversion is too large while us > MAX_MICROS { us -= MAX_MICROS; self.delay_ns(MAX_MICROS * NANOS_PER_MICRO); @@ -31,7 +31,7 @@ pub trait DelayNs { fn delay_ms(&mut self, mut ms: u32) { const MAX_MILLIS: u32 = u32::MAX / NANOS_PER_MILLI; - // Avoid potential overflow if milli -> micro conversion is too large + // Avoid potential overflow if milli -> nano conversion is too large while ms > MAX_MILLIS { ms -= MAX_MILLIS; self.delay_ns(MAX_MILLIS * NANOS_PER_MILLI); From 5051c5b6a3bfde01eb0719293db395093417b50b Mon Sep 17 00:00:00 2001 From: James Munns Date: Fri, 8 Dec 2023 10:19:47 +0100 Subject: [PATCH 127/199] Add an explanation of how 7-bit addresses should be aligned within a u8 --- embedded-hal/src/i2c.rs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/embedded-hal/src/i2c.rs b/embedded-hal/src/i2c.rs index 8f4848a28..c2a1efb3a 100644 --- a/embedded-hal/src/i2c.rs +++ b/embedded-hal/src/i2c.rs @@ -268,6 +268,17 @@ impl ErrorType for &mut T { pub trait AddressMode: private::Sealed + 'static {} /// 7-bit address mode type. +/// +/// Note that 7-bit addresses defined by drivers should be specified in **right-aligned** form, +/// e.g. in the range `0x00..=0x7F`. +/// +/// For example, a device that has the seven bit address of `0b011_0010`, and therefore is addressed on the wire using: +/// +/// * `0b0110010_0` or `0x64` for *writes* +/// * `0b0110010_1` or `0x65` for *reads* +/// +/// Should be specified as `0b0011_0010` or `0x32`, NOT `0x64` or `0x65`. Care should be taken by both HAL and driver +/// crate writers to use this scheme consistently. pub type SevenBitAddress = u8; /// 10-bit address mode type. From 4857fefc90276c2d6bfe244d994865a2b4e754b8 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 13 Dec 2023 01:06:42 +0100 Subject: [PATCH 128/199] ci: simplify, test MSRV. - Use separate jobs instead of `matrix`. The Github Actions matrix is very cursed, especially if you use `include`. We don't have that many combinations, IMO doing separate jobs is way more straightforward and only slightly more verbose. - `embedded-hal` needs `rust-version = 1.60` because it uses `dep:`. Fixes #538 --- .github/workflows/test.yml | 70 +++++++++++++++++++--------------- Cargo.stable.toml | 20 ---------- embedded-hal-async/Cargo.toml | 2 +- embedded-hal-async/README.md | 11 ++---- embedded-hal-bus/README.md | 2 + embedded-hal/Cargo.toml | 2 +- embedded-io-adapters/README.md | 5 +-- embedded-io-async/Cargo.toml | 1 + embedded-io-async/README.md | 7 ++-- 9 files changed, 52 insertions(+), 68 deletions(-) delete mode 100644 Cargo.stable.toml diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 19ff36502..f7abf9027 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -13,38 +13,48 @@ env: jobs: test: runs-on: ubuntu-latest - strategy: - matrix: - rust: - - stable - - 1.60.0 # MSRV - - nightly - target: - - x86_64-unknown-linux-gnu - - thumbv7m-none-eabi - features: - - '' - include: - - rust: stable - target: x86_64-unknown-linux-gnu - features: std - - rust: stable - target: x86_64-unknown-linux-gnu - features: alloc - - rust: nightly - target: x86_64-unknown-linux-gnu - features: std,tokio-1,futures-03,defmt-03 steps: - uses: actions/checkout@v3 - - uses: dtolnay/rust-toolchain@master - with: - toolchain: ${{ matrix.rust }} - target: ${{ matrix.target }} + - uses: dtolnay/rust-toolchain@beta + - run: cargo test --workspace + + test-all-features: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: dtolnay/rust-toolchain@beta + - run: cargo test --workspace --all-features - - run: mv Cargo.stable.toml Cargo.toml - if: matrix.rust != 'nightly' + build-nostd: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: dtolnay/rust-toolchain@beta + with: + target: thumbv7m-none-eabi + - run: > + cargo build + --workspace + --target thumbv7m-none-eabi + --features async,defmt-03 - - run: cargo check --target=${{ matrix.target }} --features=${{ matrix.features }} + msrv-1-60: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: dtolnay/rust-toolchain@1.60 + - run: > + cargo test + -p embedded-hal:1.0.0-rc.2 + -p embedded-hal-bus + -p embedded-hal-nb + -p embedded-io + -p embedded-io-adapters + -p embedded-can - - run: cargo test --target=${{ matrix.target }} --features=${{ matrix.features }} - if: contains(matrix.target, 'linux') + msrv-1-75: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: dtolnay/rust-toolchain@beta + - run: cargo test --workspace --all-features diff --git a/Cargo.stable.toml b/Cargo.stable.toml deleted file mode 100644 index 78f6fd3cd..000000000 --- a/Cargo.stable.toml +++ /dev/null @@ -1,20 +0,0 @@ -# CI moves this file to `Cargo.toml` when building for stable. - -[workspace] -resolver = "2" - -members = [ - "embedded-hal", - "embedded-hal-nb", - "embedded-hal-bus", - "embedded-can", - "embedded-io", - "embedded-io-adapters", -] - -# Cargo implicitly adds path dependencies to the workspace. -# Even if they're optional and not enabled. This prevents that. -exclude = [ - "embedded-hal-async", - "embedded-io-async", -] \ No newline at end of file diff --git a/embedded-hal-async/Cargo.toml b/embedded-hal-async/Cargo.toml index 231f00e2d..e14722974 100644 --- a/embedded-hal-async/Cargo.toml +++ b/embedded-hal-async/Cargo.toml @@ -12,7 +12,7 @@ name = "embedded-hal-async" readme = "README.md" repository = "https://github.com/rust-embedded/embedded-hal" version = "1.0.0-rc.2" -rust-version = "1.65.0" +rust-version = "1.75" [features] defmt-03 = ["dep:defmt-03", "embedded-hal/defmt-03"] diff --git a/embedded-hal-async/README.md b/embedded-hal-async/README.md index 2f739993a..39440d5c7 100644 --- a/embedded-hal-async/README.md +++ b/embedded-hal-async/README.md @@ -23,15 +23,10 @@ or a console to operate either on hardware serial ports or on virtual ones like ## Minimum Supported Rust Version (MSRV) -This crate requires Rust nightly newer than `nightly-2022-11-22`, due to requiring support for -`async fn` in traits (AFIT), which is not stable yet. +This crate is guaranteed to compile on stable Rust 1.75 and up. It *might* +compile with older versions but that may change in any new patch release. -Keep in mind Rust nightlies can make backwards-incompatible changes to unstable features -at any time. If this happens, we might do changes that increase the minimum required nightly -version in any patch release. - -When AFIT becomes stable, MSRV will be bumped to the Rust version that stabilizes it, after which -point the [standard MSRV bump policy](../docs/msrv.md) will apply. +See [here](../docs/msrv.md) for details on how the MSRV may be upgraded. ## License diff --git a/embedded-hal-bus/README.md b/embedded-hal-bus/README.md index d0b9bbdb6..3cfae9448 100644 --- a/embedded-hal-bus/README.md +++ b/embedded-hal-bus/README.md @@ -41,6 +41,8 @@ compile with older versions but that may change in any new patch release. See [here](../docs/msrv.md) for details on how the MSRV may be upgraded. +Enabling the `async` Cargo features requires Rust 1.75 or higher. + ## License Licensed under either of diff --git a/embedded-hal/Cargo.toml b/embedded-hal/Cargo.toml index a26ae3372..fcc145e74 100644 --- a/embedded-hal/Cargo.toml +++ b/embedded-hal/Cargo.toml @@ -8,7 +8,7 @@ categories = ["asynchronous", "embedded", "hardware-support", "no-std"] description = " A Hardware Abstraction Layer (HAL) for embedded systems " documentation = "https://docs.rs/embedded-hal" edition = "2021" -rust-version = "1.56" +rust-version = "1.60" keywords = ["hal", "IO"] license = "MIT OR Apache-2.0" name = "embedded-hal" diff --git a/embedded-io-adapters/README.md b/embedded-io-adapters/README.md index 6edd3c47b..a069d1fac 100644 --- a/embedded-io-adapters/README.md +++ b/embedded-io-adapters/README.md @@ -34,10 +34,7 @@ compile with older versions but that may change in any new patch release. See [here](../docs/msrv.md) for details on how the MSRV may be upgraded. -Enabling any of the `tokio-*` or `futures-*` Cargo features requires Rust nightly newer than -`nightly-2022-11-22`, due to requiring support for `async fn` in traits (AFIT), -which is not stable yet. Keep in mind Rust nightlies can make backwards-incompatible -changes to unstable features at any time. +Enabling any of the `tokio-*` or `futures-*` Cargo features requires Rust 1.75 or higher. ## License diff --git a/embedded-io-async/Cargo.toml b/embedded-io-async/Cargo.toml index 87891795c..6cd60c9c7 100644 --- a/embedded-io-async/Cargo.toml +++ b/embedded-io-async/Cargo.toml @@ -2,6 +2,7 @@ name = "embedded-io-async" version = "0.6.1" edition = "2021" +rust-version = "1.75" description = "Async embedded IO traits" repository = "https://github.com/rust-embedded/embedded-hal" readme = "README.md" diff --git a/embedded-io-async/README.md b/embedded-io-async/README.md index 6e6ddf18a..744f6e6f2 100644 --- a/embedded-io-async/README.md +++ b/embedded-io-async/README.md @@ -18,11 +18,10 @@ This project is developed and maintained by the [HAL team](https://github.com/ru ## Minimum Supported Rust Version (MSRV) -This crate requires Rust nightly newer than `nightly-2022-11-22`, due to requiring support for -`async fn` in traits (AFIT), which is not stable yet. +This crate is guaranteed to compile on stable Rust 1.75 and up. It *might* +compile with older versions but that may change in any new patch release. -Keep in mind Rust nightlies can make backwards-incompatible changes to unstable features -at any time. +See [here](../docs/msrv.md) for details on how the MSRV may be upgraded. ## License From fc2cc4c3afa028ce51e4f662ac73f3bc659b0176 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 13 Dec 2023 15:00:38 +0100 Subject: [PATCH 129/199] gpio: require `&mut self` in `InputPin` and `StatefulOutputPin`. --- embedded-hal/CHANGELOG.md | 2 +- embedded-hal/src/digital.rs | 18 +++++++++--------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/embedded-hal/CHANGELOG.md b/embedded-hal/CHANGELOG.md index a177ba7f1..c64a5ddbc 100644 --- a/embedded-hal/CHANGELOG.md +++ b/embedded-hal/CHANGELOG.md @@ -7,7 +7,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] -No unreleased changes +- gpio: require `&mut self` in `InputPin` and `StatefulOutputPin`. ## [v1.0.0-rc.2] - 2023-11-28 diff --git a/embedded-hal/src/digital.rs b/embedded-hal/src/digital.rs index f54c72153..f65fc0bfd 100644 --- a/embedded-hal/src/digital.rs +++ b/embedded-hal/src/digital.rs @@ -169,22 +169,22 @@ pub trait StatefulOutputPin: OutputPin { /// Is the pin in drive high mode? /// /// *NOTE* this does *not* read the electrical state of the pin. - fn is_set_high(&self) -> Result; + fn is_set_high(&mut self) -> Result; /// Is the pin in drive low mode? /// /// *NOTE* this does *not* read the electrical state of the pin. - fn is_set_low(&self) -> Result; + fn is_set_low(&mut self) -> Result; } impl StatefulOutputPin for &mut T { #[inline] - fn is_set_high(&self) -> Result { + fn is_set_high(&mut self) -> Result { T::is_set_high(self) } #[inline] - fn is_set_low(&self) -> Result { + fn is_set_low(&mut self) -> Result { T::is_set_low(self) } } @@ -205,20 +205,20 @@ impl ToggleableOutputPin for &mut T { /// Single digital input pin. pub trait InputPin: ErrorType { /// Is the input pin high? - fn is_high(&self) -> Result; + fn is_high(&mut self) -> Result; /// Is the input pin low? - fn is_low(&self) -> Result; + fn is_low(&mut self) -> Result; } -impl InputPin for &T { +impl InputPin for &mut T { #[inline] - fn is_high(&self) -> Result { + fn is_high(&mut self) -> Result { T::is_high(self) } #[inline] - fn is_low(&self) -> Result { + fn is_low(&mut self) -> Result { T::is_low(self) } } From 38f6070118272c0aa6c599578476489dea435f30 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Thu, 14 Dec 2023 14:24:10 +0100 Subject: [PATCH 130/199] Release v1.0.0-rc.3 --- .github/workflows/test.yml | 2 +- embedded-hal-async/CHANGELOG.md | 7 ++++++- embedded-hal-async/Cargo.toml | 6 +++--- embedded-hal-bus/CHANGELOG.md | 9 +++++++-- embedded-hal-bus/Cargo.toml | 6 +++--- embedded-hal-nb/CHANGELOG.md | 7 ++++++- embedded-hal-nb/Cargo.toml | 4 ++-- embedded-hal/CHANGELOG.md | 7 ++++++- embedded-hal/Cargo.toml | 2 +- 9 files changed, 35 insertions(+), 15 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index f7abf9027..e1d7c5664 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -45,7 +45,7 @@ jobs: - uses: dtolnay/rust-toolchain@1.60 - run: > cargo test - -p embedded-hal:1.0.0-rc.2 + -p embedded-hal:1.0.0-rc.3 -p embedded-hal-bus -p embedded-hal-nb -p embedded-io diff --git a/embedded-hal-async/CHANGELOG.md b/embedded-hal-async/CHANGELOG.md index 468c3b711..314e0ebaa 100644 --- a/embedded-hal-async/CHANGELOG.md +++ b/embedded-hal-async/CHANGELOG.md @@ -9,6 +9,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/). No unreleased changes +## [v1.0.0-rc.3] - 2023-12-14 + +- Updated `embedded-hal` to version `1.0.0-rc.3`. + ## [v1.0.0-rc.2] - 2023-11-28 - Updated `embedded-hal` to version `1.0.0-rc.2`. @@ -82,7 +86,8 @@ No unreleased changes First release to crates.io -[Unreleased]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-async-v1.0.0-rc.2...HEAD +[Unreleased]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-async-v1.0.0-rc.3...HEAD +[v1.0.0-rc.3]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-async-v1.0.0-rc.2...embedded-hal-async-v1.0.0-rc.3 [v1.0.0-rc.2]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-async-v1.0.0-rc.1...embedded-hal-async-v1.0.0-rc.2 [v1.0.0-rc.1]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-async-v0.2.0-alpha.2...embedded-hal-async-v1.0.0-rc.1 [v0.2.0-alpha.2]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-async-v0.2.0-alpha.1...embedded-hal-async-v0.2.0-alpha.2 diff --git a/embedded-hal-async/Cargo.toml b/embedded-hal-async/Cargo.toml index e14722974..fe09580fe 100644 --- a/embedded-hal-async/Cargo.toml +++ b/embedded-hal-async/Cargo.toml @@ -11,12 +11,12 @@ license = "MIT OR Apache-2.0" name = "embedded-hal-async" readme = "README.md" repository = "https://github.com/rust-embedded/embedded-hal" -version = "1.0.0-rc.2" +version = "1.0.0-rc.3" rust-version = "1.75" [features] defmt-03 = ["dep:defmt-03", "embedded-hal/defmt-03"] [dependencies] -embedded-hal = { version = "=1.0.0-rc.2", path = "../embedded-hal" } -defmt-03 = { package = "defmt", version = "0.3", optional = true } \ No newline at end of file +embedded-hal = { version = "=1.0.0-rc.3", path = "../embedded-hal" } +defmt-03 = { package = "defmt", version = "0.3", optional = true } diff --git a/embedded-hal-bus/CHANGELOG.md b/embedded-hal-bus/CHANGELOG.md index 6858e9c19..8155db960 100644 --- a/embedded-hal-bus/CHANGELOG.md +++ b/embedded-hal-bus/CHANGELOG.md @@ -9,7 +9,11 @@ and this project adheres to [Semantic Versioning](http://semver.org/). No unreleased changes -## [v1.0.0-rc.2] - 2023-11-28 +## [v0.1.0-rc.3] - 2023-12-14 + +- Updated `embedded-hal` to version `1.0.0-rc.3`. + +## [v0.1.0-rc.2] - 2023-11-28 - Updated `embedded-hal(-async)` to version `1.0.0-rc.2`. - Minor document fixes. @@ -48,7 +52,8 @@ No unreleased changes First release to crates.io -[Unreleased]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-bus-v0.1.0-rc.2...HEAD +[Unreleased]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-bus-v0.1.0-rc.3...HEAD +[v0.1.0-rc.3]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-bus-v0.1.0-rc.2...embedded-hal-bus-v0.1.0-rc.3 [v0.1.0-rc.2]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-bus-v0.1.0-rc.1...embedded-hal-bus-v0.1.0-rc.2 [v0.1.0-rc.1]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-bus-v0.1.0-alpha.3...embedded-hal-bus-v0.1.0-rc.1 [v0.1.0-alpha.3]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-bus-v0.1.0-alpha.2...embedded-hal-bus-v0.1.0-alpha.3 diff --git a/embedded-hal-bus/Cargo.toml b/embedded-hal-bus/Cargo.toml index 937a49b2c..7f04bbded 100644 --- a/embedded-hal-bus/Cargo.toml +++ b/embedded-hal-bus/Cargo.toml @@ -12,7 +12,7 @@ license = "MIT OR Apache-2.0" name = "embedded-hal-bus" readme = "README.md" repository = "https://github.com/rust-embedded/embedded-hal" -version = "0.1.0-rc.2" +version = "0.1.0-rc.3" [features] std = [] @@ -20,8 +20,8 @@ async = ["dep:embedded-hal-async"] defmt-03 = ["dep:defmt-03", "embedded-hal/defmt-03", "embedded-hal-async?/defmt-03"] [dependencies] -embedded-hal = { version = "=1.0.0-rc.2", path = "../embedded-hal" } -embedded-hal-async = { version = "=1.0.0-rc.2", path = "../embedded-hal-async", optional = true } +embedded-hal = { version = "=1.0.0-rc.3", path = "../embedded-hal" } +embedded-hal-async = { version = "=1.0.0-rc.3", path = "../embedded-hal-async", optional = true } critical-section = { version = "1.0" } defmt-03 = { package = "defmt", version = "0.3", optional = true } diff --git a/embedded-hal-nb/CHANGELOG.md b/embedded-hal-nb/CHANGELOG.md index 11753a27f..6ac3be4ed 100644 --- a/embedded-hal-nb/CHANGELOG.md +++ b/embedded-hal-nb/CHANGELOG.md @@ -9,6 +9,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/). No unreleased changes +## [v1.0.0-rc.3] - 2023-12-14 + +- Updated `embedded-hal` to version `1.0.0-rc.3`. + ## [v1.0.0-rc.2] - 2023-11-28 - Updated `embedded-hal` to version `1.0.0-rc.2`. @@ -40,7 +44,8 @@ No unreleased changes First release to crates.io -[Unreleased]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-nb-v1.0.0-rc.2...HEAD +[Unreleased]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-nb-v1.0.0-rc.3...HEAD +[v1.0.0-rc.3]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-nb-v1.0.0-rc.2...embedded-hal-nb-v1.0.0-rc.3 [v1.0.0-rc.2]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-nb-v1.0.0-rc.1...embedded-hal-nb-v1.0.0-rc.2 [v1.0.0-rc.1]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-nb-v1.0.0-alpha.3...embedded-hal-nb-v1.0.0-rc.1 [v1.0.0-alpha.3]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-nb-v1.0.0-alpha.2...embedded-hal-nb-v1.0.0-alpha.3 diff --git a/embedded-hal-nb/Cargo.toml b/embedded-hal-nb/Cargo.toml index 52c6755ea..d0c006e76 100644 --- a/embedded-hal-nb/Cargo.toml +++ b/embedded-hal-nb/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "embedded-hal-nb" -version = "1.0.0-rc.2" +version = "1.0.0-rc.3" edition = "2021" rust-version = "1.56" @@ -13,7 +13,7 @@ readme = "README.md" repository = "https://github.com/rust-embedded/embedded-hal" [dependencies] -embedded-hal = { version = "=1.0.0-rc.2", path = "../embedded-hal" } +embedded-hal = { version = "=1.0.0-rc.3", path = "../embedded-hal" } nb = "1" [dev-dependencies] diff --git a/embedded-hal/CHANGELOG.md b/embedded-hal/CHANGELOG.md index c64a5ddbc..7dfdd866c 100644 --- a/embedded-hal/CHANGELOG.md +++ b/embedded-hal/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] +No unreleased changes yet + +## [v1.0.0-rc.3] - 2023-12-14 + - gpio: require `&mut self` in `InputPin` and `StatefulOutputPin`. ## [v1.0.0-rc.2] - 2023-11-28 @@ -310,7 +314,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/). Initial release -[Unreleased]: https://github.com/rust-embedded/embedded-hal/compare/v1.0.0-rc.2...HEAD +[Unreleased]: https://github.com/rust-embedded/embedded-hal/compare/v1.0.0-rc.3...HEAD +[v1.0.0-rc.3]: https://github.com/rust-embedded/embedded-hal/compare/v1.0.0-rc.2...v1.0.0-rc.3 [v1.0.0-rc.2]: https://github.com/rust-embedded/embedded-hal/compare/v1.0.0-rc.1...v1.0.0-rc.2 [v1.0.0-rc.1]: https://github.com/rust-embedded/embedded-hal/compare/v1.0.0-alpha.11...v1.0.0-rc.1 [v1.0.0-alpha.11]: https://github.com/rust-embedded/embedded-hal/compare/v1.0.0-alpha.10...v1.0.0-alpha.11 diff --git a/embedded-hal/Cargo.toml b/embedded-hal/Cargo.toml index fcc145e74..b91f45f44 100644 --- a/embedded-hal/Cargo.toml +++ b/embedded-hal/Cargo.toml @@ -14,7 +14,7 @@ license = "MIT OR Apache-2.0" name = "embedded-hal" readme = "README.md" repository = "https://github.com/rust-embedded/embedded-hal" -version = "1.0.0-rc.2" +version = "1.0.0-rc.3" [features] defmt-03 = ["dep:defmt-03"] From c5c076a9a0f66118768b28c5786b12dd87d909e8 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 19 Dec 2023 20:55:16 +0100 Subject: [PATCH 131/199] gpio: remove `ToggleableOutputPin`, move `toggle()` to `StatefulOutputPin`. --- embedded-hal/CHANGELOG.md | 2 +- embedded-hal/src/digital.rs | 14 ++++++-------- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/embedded-hal/CHANGELOG.md b/embedded-hal/CHANGELOG.md index 7dfdd866c..9d3ac625b 100644 --- a/embedded-hal/CHANGELOG.md +++ b/embedded-hal/CHANGELOG.md @@ -7,7 +7,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] -No unreleased changes yet +- gpio: remove `ToggleableOutputPin`, move `toggle()` to `StatefulOutputPin`. ## [v1.0.0-rc.3] - 2023-12-14 diff --git a/embedded-hal/src/digital.rs b/embedded-hal/src/digital.rs index f65fc0bfd..afcf03963 100644 --- a/embedded-hal/src/digital.rs +++ b/embedded-hal/src/digital.rs @@ -175,6 +175,12 @@ pub trait StatefulOutputPin: OutputPin { /// /// *NOTE* this does *not* read the electrical state of the pin. fn is_set_low(&mut self) -> Result; + + /// Toggle pin output. + fn toggle(&mut self) -> Result<(), Self::Error> { + let was_low: bool = self.is_set_low()?; + self.set_state(PinState::from(was_low)) + } } impl StatefulOutputPin for &mut T { @@ -187,15 +193,7 @@ impl StatefulOutputPin for &mut T { fn is_set_low(&mut self) -> Result { T::is_set_low(self) } -} - -/// Output pin that can be toggled. -pub trait ToggleableOutputPin: ErrorType { - /// Toggle pin output. - fn toggle(&mut self) -> Result<(), Self::Error>; -} -impl ToggleableOutputPin for &mut T { #[inline] fn toggle(&mut self) -> Result<(), Self::Error> { T::toggle(self) From d065eb572fd344197b07a8be18c656aa95a1e831 Mon Sep 17 00:00:00 2001 From: Diego Barrios Romero Date: Wed, 16 Jun 2021 19:23:50 +0200 Subject: [PATCH 132/199] Add initial version of migration guide --- MIGRATING-0.2-1.0.md | 96 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 96 insertions(+) create mode 100644 MIGRATING-0.2-1.0.md diff --git a/MIGRATING-0.2-1.0.md b/MIGRATING-0.2-1.0.md new file mode 100644 index 000000000..70c83bb08 --- /dev/null +++ b/MIGRATING-0.2-1.0.md @@ -0,0 +1,96 @@ +# Migrating from embedded-hal 0.2.x to 1.0.0 + +## Table of contents + +- [Migrating from embedded-hal 0.2.x to 1.0.0](#migrating-from-embedded-hal-02x-to-100) + - [Table of contents](#table-of-contents) + - [Trait organization](#trait-organization) + - [Fallibility](#fallibility) + - [Method renaming](#method-renaming) + - [`nb` dependency](#nb-dependency) + - [Prelude](#prelude) + - [Features](#features) + - [Use-case-specific help](#use-case-specific-help) + - [For driver authors](#for-driver-authors) + - [I2C traits](#i2c-traits) + - [SPI traits](#spi-traits) + - [For HAL authors](#for-hal-authors) + +## Trait organization + +All traits have been organized in modules depending on their execution model. That includes `blocking` and `nb` for +non-blocking. In the future when we add asynchronous traits, we envision adding a `futures` (or similarly-named) module. + +## Fallibility + +All trait methods are now fallible so that they can be used in any possible situation. +However, HAL implementations can also provide infallible versions of the methods. + +## Method renaming + +The methods in `SPI`, `I2C` and `Serial` traits for both `blocking` and `nb` execution models have been renamed +to `write()`, `read()` and `flush()`. + +## `nb` dependency + +The `Result` type and `block!` macro from the [`nb`] crate are now reexported in `embeddeh_hal::nb`. +This ensures there are no version mismatches. +You should remove the `nb` crate dependency in your `Cargo.toml` in any version and use the reexported items. + +In your `Cargo.toml`: +```diff +- nb = "1" +``` + +In your code: +```diff +- use nb; ++ use embedded_hal::nb; +``` +You can also drop `#[macro_use]` if you are using Rust edition 2018. + +Alternatively (needs Rust edition 2018): +```diff +- use nb::{Result, block}; ++ use embedded_hal::nb::{Result, block}; +``` + +## Prelude + +The prelude has been removed because it could make method calls ambiguous, since the method names are now +the same across execution models. +To overcome this, simply import the traits you wish to use individually. +If you run into ambiguous method calls, you can disambiguate using fully-qualified syntax (the error message +from the compiler should already tell you how it should look like in your case) or tweak your trait imports or code +to limit the scope of the trait imports and thus avoid ambiguity. +Please note that it is also possible to import traits *inside a function*. + +## Features + +The `unproven` feature has been removed and the traits have been marked as proven. +In the past, managing unproven features, and having "sort of breaking" changes have been a struggling point. +Also, people tended to adopt `unproven` features quickly, but the features would take a very +long time to stabilize. + +Instead, we would like to push experimentation OUT of the `embedded-hal` crate, allowing people to +experiment externally, and merge when some kind of feasability had been proven. + +## Use-case-specific help + +### For driver authors + +### I2C traits + +Nothing changed. + +#### SPI traits + +For the blocking traits nothing changed. +For the non-blocking traits, TODO + +### For HAL authors + +TODO + + +[MeetingSummary]: https://hackmd.io/ck-xRXtMTmKYXdK5bEh82A From 533af846aa4aa1031c95f7515b96b3d17b3a5256 Mon Sep 17 00:00:00 2001 From: Diego Barrios Romero Date: Thu, 17 Jun 2021 09:26:33 +0200 Subject: [PATCH 133/199] Update MIGRATING-0.2-1.0.md Co-authored-by: Ryan --- MIGRATING-0.2-1.0.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MIGRATING-0.2-1.0.md b/MIGRATING-0.2-1.0.md index 70c83bb08..b2cb9746a 100644 --- a/MIGRATING-0.2-1.0.md +++ b/MIGRATING-0.2-1.0.md @@ -59,7 +59,7 @@ Alternatively (needs Rust edition 2018): The prelude has been removed because it could make method calls ambiguous, since the method names are now the same across execution models. -To overcome this, simply import the traits you wish to use individually. +To overcome this, please import the traits you wish to use individually. If you run into ambiguous method calls, you can disambiguate using fully-qualified syntax (the error message from the compiler should already tell you how it should look like in your case) or tweak your trait imports or code to limit the scope of the trait imports and thus avoid ambiguity. From a255e9097531e097d09aae462060042460627949 Mon Sep 17 00:00:00 2001 From: Diego Barrios Romero Date: Wed, 30 Jun 2021 09:51:58 +0200 Subject: [PATCH 134/199] Add more content --- MIGRATING-0.2-1.0.md | 57 +++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 54 insertions(+), 3 deletions(-) diff --git a/MIGRATING-0.2-1.0.md b/MIGRATING-0.2-1.0.md index b2cb9746a..5ee5c4652 100644 --- a/MIGRATING-0.2-1.0.md +++ b/MIGRATING-0.2-1.0.md @@ -9,6 +9,7 @@ - [Method renaming](#method-renaming) - [`nb` dependency](#nb-dependency) - [Prelude](#prelude) + - [`rng` module](#rng-module) - [Features](#features) - [Use-case-specific help](#use-case-specific-help) - [For driver authors](#for-driver-authors) @@ -26,10 +27,54 @@ non-blocking. In the future when we add asynchronous traits, we envision adding All trait methods are now fallible so that they can be used in any possible situation. However, HAL implementations can also provide infallible versions of the methods. +For example, an implementation similar to the one below would allow to use the GPIO pins as `OutputPin`s +in any generic driver or implementation-agnostic code (by importing the `OutputPin` trait), +as well as using the infallible methods in non-generic code, thus avoiding the need to use `unwrap()` +the results in many cases and resulting in more succinct code. + +It should be noted that given this implementation, importing the `OutputPin` trait can result in +ambiguous calls, so please remove the trait imports if you do not need them. + +```rust +use core::convert::Infallible; +use embedded_hal::blocking::digital::OutputPin; + +struct GpioPin; + +impl OutputPin for GpioPin { + type Error = Infallible; + + fn set_high(&mut self) -> Result<(), Self::Error> { + // ... + Ok(()) + } + + fn set_low(&mut self) -> Result<(), Self::Error> { + // ... + Ok(()) + } +} + +impl GpioPin { + fn set_high(&mut self) { + // ... + } + + fn set_low(&mut self) { + // ... + } +} +``` + ## Method renaming The methods in `SPI`, `I2C` and `Serial` traits for both `blocking` and `nb` execution models have been renamed -to `write()`, `read()` and `flush()`. +to `write()`, `read()` and `flush()` for consistency. + +In order to avoid method call ambiguity, only the traits from the corresponding execution model should be imported +into the relevant scope. This is the reason why we have removed the prelude. + +For more on this, see [Prelude](#prelude). ## `nb` dependency @@ -65,15 +110,21 @@ from the compiler should already tell you how it should look like in your case) to limit the scope of the trait imports and thus avoid ambiguity. Please note that it is also possible to import traits *inside a function*. +## `rng` module + +The `rng` module and its traits have been removed in favor of the [`rand_core`] traits. + +[`rand_core`]: https://crates.io/crates/rand_core + ## Features The `unproven` feature has been removed and the traits have been marked as proven. -In the past, managing unproven features, and having "sort of breaking" changes have been a struggling point. +In the past, managing unproven features, and having "sort of breaking" changes has been a struggling point. Also, people tended to adopt `unproven` features quickly, but the features would take a very long time to stabilize. Instead, we would like to push experimentation OUT of the `embedded-hal` crate, allowing people to -experiment externally, and merge when some kind of feasability had been proven. +experiment externally, and merge when some kind of feasibility had been proven. ## Use-case-specific help From da438d492083e493464c89b9f8ad727f0d872011 Mon Sep 17 00:00:00 2001 From: Diego Barrios Romero Date: Sat, 11 Sep 2021 19:23:16 +0200 Subject: [PATCH 135/199] Update guide to current state --- MIGRATING-0.2-1.0.md | 46 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 45 insertions(+), 1 deletion(-) diff --git a/MIGRATING-0.2-1.0.md b/MIGRATING-0.2-1.0.md index 5ee5c4652..dce695fc0 100644 --- a/MIGRATING-0.2-1.0.md +++ b/MIGRATING-0.2-1.0.md @@ -7,9 +7,12 @@ - [Trait organization](#trait-organization) - [Fallibility](#fallibility) - [Method renaming](#method-renaming) + - [SPI transfer return type](#spi-transfer-return-type) + - [Error type bounds](#error-type-bounds) - [`nb` dependency](#nb-dependency) - [Prelude](#prelude) - [`rng` module](#rng-module) + - [Removed blanket implementations](#removed-blanket-implementations) - [Features](#features) - [Use-case-specific help](#use-case-specific-help) - [For driver authors](#for-driver-authors) @@ -19,9 +22,11 @@ ## Trait organization -All traits have been organized in modules depending on their execution model. That includes `blocking` and `nb` for +All traits have been organized in modules for each feature, each of these containing sub-modules depending on their execution model. That includes `blocking` and `nb` for non-blocking. In the future when we add asynchronous traits, we envision adding a `futures` (or similarly-named) module. +Execution-model-independent definitions have been moved into the feature module. For example, SPI `Phase` is now defined in `embedded_hal::spi::Phase`. For convenience, these definitions are reexported in both of its blocking and non-blocking submodules. + ## Fallibility All trait methods are now fallible so that they can be used in any possible situation. @@ -76,6 +81,38 @@ into the relevant scope. This is the reason why we have removed the prelude. For more on this, see [Prelude](#prelude). +## SPI transfer return type + +The `transfer()` method in the trait `spi::blocking::Transfer` previously returned +a slice of the output data. +This slice is the same as the output buffer which is passed to the method, though, thus redundant and potentially confusing. +The `transfer()` method now returns `Result<(), Self::Error>`. +If you were using this return value, adapting the code should be straight forward by simply using the reception buffer which is passed. +See an example: +```rust +let tx_data = [1, 2, 3, 4]; +let mut rx_data = [0; 4]; +let data = spi.transfer(&tx_data, &mut rx_data)?; +println!("{:?}", data); +// There is no need to do `let data = `, since we already have the data in `rx_data`. +// Do this instead: +spi.transfer(&tx_data, &mut rx_data)?; +println!("{:?}", rx_data); +``` + +## Error type bounds + +All associated error types are now required to implement `core::fmt::Debug`. +Usually it is enough to add a `#[derive(Debug)]` clause to your error types. For example: + +```diff ++ #[derive(Debug)] +pub enum MyError { + InvalidInputData, + // ... +} +``` + ## `nb` dependency The `Result` type and `block!` macro from the [`nb`] crate are now reexported in `embeddeh_hal::nb`. @@ -116,6 +153,13 @@ The `rng` module and its traits have been removed in favor of the [`rand_core`] [`rand_core`]: https://crates.io/crates/rand_core +## Removed blanket implementations + +There were several blanket implementations of blocking traits using the non-blocking +traits as a base. +Due to their relative simplicity and some technical concerns, these have been removed. +Implementing them yourself is now necessary. This should be straight-forward. + ## Features The `unproven` feature has been removed and the traits have been marked as proven. From 1a0d60caad613c2e26eec4d51592973db1dda8f1 Mon Sep 17 00:00:00 2001 From: Diego Barrios Romero Date: Wed, 17 Aug 2022 21:21:36 +0200 Subject: [PATCH 136/199] Add overview and removed traits section + Update to current state --- MIGRATING-0.2-1.0.md | 200 ++++++++++++++++++++++++++++++++----------- 1 file changed, 151 insertions(+), 49 deletions(-) diff --git a/MIGRATING-0.2-1.0.md b/MIGRATING-0.2-1.0.md index dce695fc0..4664a0469 100644 --- a/MIGRATING-0.2-1.0.md +++ b/MIGRATING-0.2-1.0.md @@ -4,28 +4,123 @@ - [Migrating from embedded-hal 0.2.x to 1.0.0](#migrating-from-embedded-hal-02x-to-100) - [Table of contents](#table-of-contents) + - [Overview and reasoning](#overview-and-reasoning) - [Trait organization](#trait-organization) + - [Trait unification](#trait-unification) + - [Removed traits](#removed-traits) + - [Unconstrained associated types](#unconstrained-associated-types) + - [Impractical traits](#impractical-traits) + - [Delay traits](#delay-traits) + - [Bus/device separation](#busdevice-separation) - [Fallibility](#fallibility) - - [Method renaming](#method-renaming) - [SPI transfer return type](#spi-transfer-return-type) - [Error type bounds](#error-type-bounds) - - [`nb` dependency](#nb-dependency) - [Prelude](#prelude) - [`rng` module](#rng-module) - [Removed blanket implementations](#removed-blanket-implementations) - [Features](#features) + - [Companion crates](#companion-crates) - [Use-case-specific help](#use-case-specific-help) - [For driver authors](#for-driver-authors) - [I2C traits](#i2c-traits) - [SPI traits](#spi-traits) - [For HAL authors](#for-hal-authors) +## Overview and reasoning + +There have been _a lot_ of changes in `embedded_hal` between versions 0.2.x and 1.0.0. +We understand the significance of `embedded-hal` in the Rust embedded +ecosystem and thus intend to release a version that stays compatible for a long time. + +In this version, among many other changes, we have addressed several big topics that have emerged over the years: +- [Associated type compatibiilty](#removed-traits) +- [Trait fragmentation](#trait-organization) +- [Bus/device separation](#bus-device-separation) +- [Fallibility](#fallibility) +- [Execution model support](#trait-organization) + ## Trait organization -All traits have been organized in modules for each feature, each of these containing sub-modules depending on their execution model. That includes `blocking` and `nb` for -non-blocking. In the future when we add asynchronous traits, we envision adding a `futures` (or similarly-named) module. +All traits have been organized in modules for each feature. For example `embedded_hal::spi` and `embedded_hal::i2c`. +We only foresee having blocking traits in `embedded-hal`. We have put the traits for different execution models +into separate crates. Notably `embedded-hal-async` and `embedded-hal-nb`. See [companion crates](#companion-crates). +This allows for a separate and more tailored evolution. + + + +Execution-model-independent definitions have been moved into the feature module. For example, SPI `Phase` is now defined in `embedded_hal::spi::Phase`. + +### Trait unification + +Previously, there were multiple traits for the same feature. In order to avoid fragmentation and ensure +interoperability for generic code, these have now been united. + +For example, most generic code should simply use the `SpiDevice` trait instead of +choosing from `Transactional`, `Transfer`, `Write` and `WriteIter`. + +For HAL implementations and some specialized use-cases there are still a few traits to implement for SPI +but the number has been reduced. + +Please see more about this separation [below](#bus-device-separation). + +## Removed traits + +These traits have been removed in the 1.0.0 release: + +- [`adc::OneShot`][adc] +- [`adc::Channel`][adc] +- [`capture::Capture`][capture] +- `delay::DelayMs` (replaced by `DelayUs`) +- [`digital::IoPin`][iopin] +- [`pwm::Pwm`][pwm] +- [`pwm::PwmPin`][pwm] +- [`qei::Qei`][qei] +- [`timer::Cancel`][timer] +- [`timer::CountDown`][timer] +- [`timer::Periodic`][timer] +- [`watchdog::Disable`][watchdog] +- [`watchdog::Enable`][watchdog] +- [`watchdog::Watchdog`][watchdog] + +Please find a general [roadmap with further guidance here][roadmap-rm-traits] about +how to get these traits back in a future release. +If you need them, we would like to hear from you. Please add your use case to the appropriate issue for the trait affected. + +[roadmap-rm-traits]: https://github.com/rust-embedded/embedded-hal/issues/357 +[adc]: https://github.com/rust-embedded/embedded-hal/issues/377 +[iopin]: https://github.com/rust-embedded/embedded-hal/issues/397 +[capture]: https://github.com/rust-embedded/embedded-hal/issues/361 +[pwm]: https://github.com/rust-embedded/embedded-hal/issues/358 +[qei]: https://github.com/rust-embedded/embedded-hal/issues/362 +[timer]: https://github.com/rust-embedded/embedded-hal/issues/359 +[watchdog]: https://github.com/rust-embedded/embedded-hal/issues/360 + +### Unconstrained associated types + +Traits defined in `embedded-hal` pursue creating an interface for interoperability between generic code (be it generic user code, generic application code, generic device drivers, etc.). +When a trait has an unconstrained associated type, it is not possible to write generic code around it. Each side (implementer and user) need to specify which type the associated type will be. If the types match, the both parts can work together, however, this is not truly generic code. + +For example, if somebody creates a device driver that receives a `CountDown` struct, it needs to specify what its `Time` type should be. If they choose a type coming from `fugit`, somebody else cannot use this driver if the HAL implementation for the MCU they are using only provides `CountDown` with `Time` types defined in `embedded-time`. It is also not possible for the user to implement `CountDown` for `Time` types defined by `fugit` in a straight-forward way due to the orphan rule. +In summary, it is not possible for anybody to start a countdown for a certain duration in a generic way, without it being tied to a particular time implementation and thus forcing everybody to use that one. + +At the moment no solution for this has been found so we have decided to remove such traits hoping that a solution may be found +and we can add them back in a future 1.x release. + +### Impractical traits + +The [`digital::IoPin` trait][iopin] and the [`adc` traits][adc] have been deemed impractical for use and have thus been removed. +Please feel free to comment on the appropriate issue if you need any of these crates and propose a solution. + +### Delay traits + +The `DelayMs` trait has been removed. The functionality provided by this trait should now be provided by the `DelayUs` trait, +which also features a convenience `delay_ms()` method, so changes should be minimal. -Execution-model-independent definitions have been moved into the feature module. For example, SPI `Phase` is now defined in `embedded_hal::spi::Phase`. For convenience, these definitions are reexported in both of its blocking and non-blocking submodules. +This allowed us to reduce the API surface while still keeping the main functionality. We intend to add a generic `Delay` trait +in the future, once the time representation issue has been resolved. + +## Bus/device separation + ## Fallibility @@ -34,19 +129,19 @@ However, HAL implementations can also provide infallible versions of the methods For example, an implementation similar to the one below would allow to use the GPIO pins as `OutputPin`s in any generic driver or implementation-agnostic code (by importing the `OutputPin` trait), -as well as using the infallible methods in non-generic code, thus avoiding the need to use `unwrap()` -the results in many cases and resulting in more succinct code. +as well as using the infallible methods in non-generic code. +This avoids the need to use `unwrap()` the results in many cases and results in more succinct code. It should be noted that given this implementation, importing the `OutputPin` trait can result in ambiguous calls, so please remove the trait imports if you do not need them. ```rust use core::convert::Infallible; -use embedded_hal::blocking::digital::OutputPin; +use embedded_hal::digital::blocking::OutputPin; -struct GpioPin; +struct HalImplGpioPin; -impl OutputPin for GpioPin { +impl OutputPin for HalImplGpioPin { type Error = Infallible; fn set_high(&mut self) -> Result<(), Self::Error> { @@ -60,7 +155,7 @@ impl OutputPin for GpioPin { } } -impl GpioPin { +impl HalImplGpioPin { fn set_high(&mut self) { // ... } @@ -71,24 +166,15 @@ impl GpioPin { } ``` -## Method renaming - -The methods in `SPI`, `I2C` and `Serial` traits for both `blocking` and `nb` execution models have been renamed -to `write()`, `read()` and `flush()` for consistency. - -In order to avoid method call ambiguity, only the traits from the corresponding execution model should be imported -into the relevant scope. This is the reason why we have removed the prelude. - -For more on this, see [Prelude](#prelude). - ## SPI transfer return type -The `transfer()` method in the trait `spi::blocking::Transfer` previously returned -a slice of the output data. +Previously the `transfer()` method in SPI returned a slice of the output data. This slice is the same as the output buffer which is passed to the method, though, thus redundant and potentially confusing. The `transfer()` method now returns `Result<(), Self::Error>`. If you were using this return value, adapting the code should be straight forward by simply using the reception buffer which is passed. + See an example: + ```rust let tx_data = [1, 2, 3, 4]; let mut rx_data = [0; 4]; @@ -113,38 +199,52 @@ pub enum MyError { } ``` -## `nb` dependency +Additionally, for the I2C, SPI and Serial communication interfaces we have added a dedicated mechanism +which allows for two crucial requirements: +1. Generic code like drivers can interpret and act on errors if they want to. +2. HAL implementations can have arbitrarily-precise error types. -The `Result` type and `block!` macro from the [`nb`] crate are now reexported in `embeddeh_hal::nb`. -This ensures there are no version mismatches. -You should remove the `nb` crate dependency in your `Cargo.toml` in any version and use the reexported items. +This works in the following way: -In your `Cargo.toml`: -```diff -- nb = "1" -``` +For each interface, `embedded-hal` defines an `ErrorKind` `enum` type with all sensible error variants as well +as an `Error` trait featuring a method that converts the type into that `ErrorKind`. -In your code: -```diff -- use nb; -+ use embedded_hal::nb; -``` -You can also drop `#[macro_use]` if you are using Rust edition 2018. +`embedded-hal` still allows for implementation-defined error types associated to each trait, but requires these to +implement the appropriate `Error` trait, thus providing a mapping to a defined set of error variants. -Alternatively (needs Rust edition 2018): -```diff -- use nb::{Result, block}; -+ use embedded_hal::nb::{Result, block}; +With this mechanism, HAL implementations can continue to define their own error types which can carry as much +information as they want. On the other hand it is now possible for generic code to inspect those errors +and act on common errors like I2Cs NACK. + +Furthermore, implementation-specific code can access the original error type and retrieve any information contained. + +An example of a driver which looks for I2C NACK errors and returns its own `DeviceBusy` or `Comm` error +wrapping the original one could be as follows: + +```rust +const address = 0x1D; + +fn set_some_parameter(&mut self) -> Result<(), Self::Error> { + const data = [0, 1]; + match self.i2c.write(address, &data) { + Err(e) => match e.kind() { + ErrorKind::NoAcknowledge(_) => Err(Self::Error::DeviceBusy(e)), + _ => Err(Self::Error::Comm(e)) // wrap and return any other error + }, + Ok(_) => Ok(()) + } +} ``` ## Prelude The prelude has been removed because it could make method calls ambiguous, since the method names are now -the same across execution models. +the same across traits. To overcome this, please import the traits you wish to use individually. -If you run into ambiguous method calls, you can disambiguate using fully-qualified syntax (the error message + +If you run into ambiguous method calls, you can disambiguate using the fully-qualified syntax (the error message from the compiler should already tell you how it should look like in your case) or tweak your trait imports or code -to limit the scope of the trait imports and thus avoid ambiguity. +to limit the scope of the trait imports and thus avoid the ambiguity. Please note that it is also possible to import traits *inside a function*. ## `rng` module @@ -157,8 +257,11 @@ The `rng` module and its traits have been removed in favor of the [`rand_core`] There were several blanket implementations of blocking traits using the non-blocking traits as a base. -Due to their relative simplicity and some technical concerns, these have been removed. -Implementing them yourself is now necessary. This should be straight-forward. + +Since the non-blocking traits have been extracted into the separate crate `embedded-hal-nb`, +these have been removed. + + ## Features @@ -170,18 +273,17 @@ long time to stabilize. Instead, we would like to push experimentation OUT of the `embedded-hal` crate, allowing people to experiment externally, and merge when some kind of feasibility had been proven. +## Companion crates + ## Use-case-specific help ### For driver authors ### I2C traits -Nothing changed. #### SPI traits -For the blocking traits nothing changed. -For the non-blocking traits, TODO ### For HAL authors From c090f4dfa3fc479b683f871cc8fdd34648e0eea0 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 25 Dec 2023 00:34:23 +0100 Subject: [PATCH 137/199] Expand migration guide, update for the latest changes. --- MIGRATING-0.2-1.0.md | 231 +++++++++++++++++++++++++++++-------------- 1 file changed, 156 insertions(+), 75 deletions(-) diff --git a/MIGRATING-0.2-1.0.md b/MIGRATING-0.2-1.0.md index 4664a0469..e980e046d 100644 --- a/MIGRATING-0.2-1.0.md +++ b/MIGRATING-0.2-1.0.md @@ -2,29 +2,23 @@ ## Table of contents -- [Migrating from embedded-hal 0.2.x to 1.0.0](#migrating-from-embedded-hal-02x-to-100) - - [Table of contents](#table-of-contents) - - [Overview and reasoning](#overview-and-reasoning) - - [Trait organization](#trait-organization) - - [Trait unification](#trait-unification) - - [Removed traits](#removed-traits) - - [Unconstrained associated types](#unconstrained-associated-types) - - [Impractical traits](#impractical-traits) - - [Delay traits](#delay-traits) - - [Bus/device separation](#busdevice-separation) - - [Fallibility](#fallibility) - - [SPI transfer return type](#spi-transfer-return-type) - - [Error type bounds](#error-type-bounds) - - [Prelude](#prelude) - - [`rng` module](#rng-module) - - [Removed blanket implementations](#removed-blanket-implementations) - - [Features](#features) - - [Companion crates](#companion-crates) - - [Use-case-specific help](#use-case-specific-help) - - [For driver authors](#for-driver-authors) - - [I2C traits](#i2c-traits) - - [SPI traits](#spi-traits) - - [For HAL authors](#for-hal-authors) +- [Overview and reasoning](#overview-and-reasoning) +- [Trait organization](#trait-organization) +- [Trait unification](#trait-unification) +- [Removed traits](#removed-traits) + - [Unconstrained associated types](#unconstrained-associated-types) + - [Impractical traits](#impractical-traits) + - [Serial traits](#serial-traits) + - [RNG traits](#rng-traits) + - [CAN traits](#can-traits) +- [SPI Bus/device separation](#spi-busdevice-separation) +- [Fallibility](#fallibility) +- [SPI transfer return type](#spi-transfer-return-type) +- [Error type bounds](#error-type-bounds) +- [Prelude](#prelude) +- [Removed blanket implementations](#removed-blanket-implementations) +- [Cargo Features](#cargo-features) +- [Companion crates](#companion-crates) ## Overview and reasoning @@ -32,7 +26,21 @@ There have been _a lot_ of changes in `embedded_hal` between versions 0.2.x and We understand the significance of `embedded-hal` in the Rust embedded ecosystem and thus intend to release a version that stays compatible for a long time. -In this version, among many other changes, we have addressed several big topics that have emerged over the years: +The main difference betewen `embedded-hal` 0.2 and 1.0 is the project is now focused +on a single goal: traits for writing drivers that work on any HAL. + +In `embedded-hal` 0.2, the traits had dual goals: +- Standardize the API of HAL crates, so HAL crate authors get guidance on how to design their APIs and + end users writing code directly against one HAL get a familiar API. +- Allowing writing generic drivers using the traits, so they work on top of any HAL crate. + +For `embedded-hal` 1.0, we decided to drop the first goal, targeting only the second. The reasons are: + +- Standardizing HAL APIs is difficult, because hardware out there has wildly different sets of capabilities. Modeling all capabilities required many different variants of the traits, and required "customization points" like associated types, significantly increasing complexity. +- There is a tension between both goals. "Customization points" like associated types make the traits hard to use in generic HAL-independent drivers. +- The second goal delivers much more value. Being able to use any driver together with any HAL crate, out of the box, and across the entire Rust Embedded ecosystem, is just plain awesome. + +This refocusing on drivers is the root cause of many of the changes between `embedded-hal` 0.2 and 1.0: - [Associated type compatibiilty](#removed-traits) - [Trait fragmentation](#trait-organization) - [Bus/device separation](#bus-device-separation) @@ -41,39 +49,39 @@ In this version, among many other changes, we have addressed several big topics ## Trait organization -All traits have been organized in modules for each feature. For example `embedded_hal::spi` and `embedded_hal::i2c`. +All traits have been organized in modules for each peripheral. For example `embedded_hal::spi` and `embedded_hal::i2c`. We only foresee having blocking traits in `embedded-hal`. We have put the traits for different execution models into separate crates. Notably `embedded-hal-async` and `embedded-hal-nb`. See [companion crates](#companion-crates). This allows for a separate and more tailored evolution. - - -Execution-model-independent definitions have been moved into the feature module. For example, SPI `Phase` is now defined in `embedded_hal::spi::Phase`. +Execution-model-independent definitions have been moved into the peripheral's module. For example, SPI `Phase` is now defined in `embedded_hal::spi::Phase`. -### Trait unification +## Trait unification -Previously, there were multiple traits for the same feature. In order to avoid fragmentation and ensure -interoperability for generic code, these have now been united. +Previously, there were multiple traits for the same peripheral, for different sets of capabilities. The reasoning +was different hardware supports a different set of features, so by making the traits granular each HAL implementation +can implement only the features supported by the hardware. -For example, most generic code should simply use the `SpiDevice` trait instead of -choosing from `Transactional`, `Transfer`, `Write` and `WriteIter`. +However, this has proven to be troublesome for generic drivers, in cases where a driver expects to use one +trait, but the HAL crate implements only other traits. To avoid this fragmentation and ensure +interoperability for generic code, these have now been unified. -For HAL implementations and some specialized use-cases there are still a few traits to implement for SPI -but the number has been reduced. +- I2C: `Read`, `Write`, `WriteIter`, `WriteIterRead`, `WriteRead`, `Transactional`, `TransactionalIter` have now been unified into a single `I2c` trait. +- SPI: `Write` `WriteIter`, `Transfer`, `Transactional` have been unified into `SpiBus`. +- GPIO: `ToggleableOutputPin` has been merged into `StatefulOutputPin`. +- Delays: `DelayMs`, `DelayUs` has been unified into `DelayNs` (and precision extended to nanoseconds). -Please see more about this separation [below](#bus-device-separation). +HAL implementation crates should implement the full functionality of the traits. If a feature is not supported natively by the hardware, it should be polyfilled/emulated in software. In no case should "not supported" errors be returned. This ensures maximum compatibility. ## Removed traits -These traits have been removed in the 1.0.0 release: +These traits have been removed in the 1.0.0 release, with no replacement for now: - [`adc::OneShot`][adc] - [`adc::Channel`][adc] - [`capture::Capture`][capture] -- `delay::DelayMs` (replaced by `DelayUs`) - [`digital::IoPin`][iopin] - [`pwm::Pwm`][pwm] -- [`pwm::PwmPin`][pwm] - [`qei::Qei`][qei] - [`timer::Cancel`][timer] - [`timer::CountDown`][timer] @@ -83,8 +91,11 @@ These traits have been removed in the 1.0.0 release: - [`watchdog::Watchdog`][watchdog] Please find a general [roadmap with further guidance here][roadmap-rm-traits] about -how to get these traits back in a future release. -If you need them, we would like to hear from you. Please add your use case to the appropriate issue for the trait affected. +whether and how to get these traits back in a future release + +If you are a generic driver author and need one of them, we would like to hear from you. Please add your use case to the appropriate issue for the trait affected. + +HAL implementation crates are encouraged to provide their own APIs for functionality for the removed traits, and not implement any traits. This will allow the APIs to more closely match the hardware capabilities, and allow users to continue to use them. [roadmap-rm-traits]: https://github.com/rust-embedded/embedded-hal/issues/357 [adc]: https://github.com/rust-embedded/embedded-hal/issues/377 @@ -98,10 +109,10 @@ If you need them, we would like to hear from you. Please add your use case to th ### Unconstrained associated types Traits defined in `embedded-hal` pursue creating an interface for interoperability between generic code (be it generic user code, generic application code, generic device drivers, etc.). -When a trait has an unconstrained associated type, it is not possible to write generic code around it. Each side (implementer and user) need to specify which type the associated type will be. If the types match, the both parts can work together, however, this is not truly generic code. +When a trait has an unconstrained associated type (for example `type Time;`), it is not possible to write generic code around it. Each side (implementer and user) need to specify which type the associated type will be. If the types match, the both parts can work together, however, this is not truly generic code. For example, if somebody creates a device driver that receives a `CountDown` struct, it needs to specify what its `Time` type should be. If they choose a type coming from `fugit`, somebody else cannot use this driver if the HAL implementation for the MCU they are using only provides `CountDown` with `Time` types defined in `embedded-time`. It is also not possible for the user to implement `CountDown` for `Time` types defined by `fugit` in a straight-forward way due to the orphan rule. -In summary, it is not possible for anybody to start a countdown for a certain duration in a generic way, without it being tied to a particular time implementation and thus forcing everybody to use that one. +In summary, it is not possible for anybody to start a countdown for a certain duration in a generic way, without it being tied to a particular time implementation and thus forcing everybody to use that one. This means all these traits don't fulfill the "allow writing generic drivers" goal. At the moment no solution for this has been found so we have decided to remove such traits hoping that a solution may be found and we can add them back in a future 1.x release. @@ -109,18 +120,99 @@ and we can add them back in a future 1.x release. ### Impractical traits The [`digital::IoPin` trait][iopin] and the [`adc` traits][adc] have been deemed impractical for use and have thus been removed. -Please feel free to comment on the appropriate issue if you need any of these crates and propose a solution. +Please feel free to comment on the appropriate issue if you need any of these traitsk and propose a solution. + +### Serial traits + +The `blocking::serial::Write` trait has been removed in favor of the [`embedded-io`] traits, also maintained within the `embedded-hal` repository. + +[`embedded-io`]: https://crates.io/crates/embedded-io + +### RNG traits + +The `rng` module and its traits have been removed in favor of the [`rand_core`] traits. -### Delay traits +[`rand_core`]: https://crates.io/crates/rand_core + +### CAN traits + +The `can` module and its traits have been removed in favor of the [`embedded-can`] traits. + +[`embedded-can`]: https://crates.io/crates/embedded-can + +## SPI Bus/device separation + +The SPI traits have been unified into a single `SpiBus` trait. However, to allow sharing an SPI bus, and hardware control of the CS pin, 1.0 adds the `SpiDevice` trait. + +The short summary is: +- `SpiBus` represents an entire SPI bus (with SCK, MOSI, MISO) pins +- `SpiDevice` represents a single device on an SPI bus, selected by a CS pin. -The `DelayMs` trait has been removed. The functionality provided by this trait should now be provided by the `DelayUs` trait, -which also features a convenience `delay_ms()` method, so changes should be minimal. +See the [SPI documentation](https://docs.rs/embedded-hal/1.0.0/embedded_hal/spi/index.html) for more details. -This allowed us to reduce the API surface while still keeping the main functionality. We intend to add a generic `Delay` trait -in the future, once the time representation issue has been resolved. +When upgrading code to `embedded-hal` 1.0, it is critical to implement/use the right trait depending on the underlying situation. -## Bus/device separation - +### For HAL implementation crates + +- If you previously implemented the SPI traits, and did *not* manage a CS pin automatically, you should now implement `SpiBus`, which is the equivalent in 1.0. +- Optionally, if the API *does* manage a CS pin automatically, you may implement `SpiDevice`. + - This is required if the underlying API requires it to manage the CS pin, like `spidev` on Linux. + +Do not implement `SpiBus` and `SpiDevice` on the same struct, since this is never correct. When there's no CS pin being controlled you must implement only `SpiBus`, and when there is, implement only `SpiDevice`. If you want to offer both APIs, implement them on separate structs so the user has to cohose one or the other. + +### For driver crates + +- If your device has SCK, MOSI, MISO, CS pins: use `SpiDevice`. + - Do NOT take the CS pin as a separate `OutputPin`, the `SpiDevice` will manage it for you. Taking the CS pin separately will make your driver not work on shared buses. +- If your device only has SCK, MOSI, MISO: use `SpiBus`. + - This means bus sharing won't be supported, but there's no way to share without a CS pin anyway. +- If you're using SPI to bitbang non-SPI protocols (for example, WS2812 smart LEDs), use `SpiBus`. + +### For end users + +You will most likely find the HAL crate you're using implements `SpiBus`, and the driver you want to use +requires `SpiDevice`. To convert from `SpiBus` to `SpiDevice`, wrap it with a [`embedded_hal_bus::spi::ExclusiveDevice`](https://docs.rs/embedded-hal-bus/0.1.0/embedded_hal_bus/spi/struct.ExclusiveDevice.html), together with the CS pin: + +```rust +use embedded_hal_bus::spi::{ExclusiveDevice, NoDelay}; + +// Create the SPI from the HAL. This implements SpiBus, not SpiDevice! +let spi_bus = my_hal::spi::Spi::new(...); +// Create the CS. This must implement OutputPin. +let cs = my_hal::gpio::Output::new(...); + +// Combine the SPI bus and the CS pin into a SPI device. This now does implement SpiDevice! +let spi_device = ExclusiveDevice::new(spi_bus, cs, NoDelay); + +// Now you can create your driver with it! +let driver = my_driver::Driver::new(spi_device, ...); +``` + +If you want multiple drivers to share the same SPI bus, [`embedded_hal_bus::spi`](https://docs.rs/embedded-hal-bus/0.1.0/embedded_hal_bus/spi/index.html) +has a few options depending on the kind of mutex you want to use. This is now built-in to `embedded-hal`, using external crates like `shared-bus` is discouraged. + +For example, you can use `RefCellDevice` when you don't need drivers to be `Send`. + +```rust +use core::cell::RefCell; +use embedded_hal_bus::spi::{RefCellDevice, NoDelay}; + +// Create the SPI bus and CS pins. +let spi_bus = my_hal::spi::Spi::new(...); +let cs1 = my_hal::gpio::Output::new(...); +let cs2 = my_hal::gpio::Output::new(...); + +// Wrap the bus with a RefCell. +let spi_bus = RefCell::new(spi_bus); + +// Combine references to the SPI bus with a CS pin to get a SpiDevice for one device on the bus. +let device1 = RefCellDevice::new(&spi_bus, cs1, NoDelay); +let device2 = RefCellDevice::new(&spi_bus, cs2, NoDelay); + +// Now you can create drivers. They will transparently talk each to its own device, sharing the same bus. +let driver1 = my_driver::Driver::new(device1, ...); +let driver2 = my_driver::Driver::new(device2, ...); +``` ## Fallibility @@ -199,7 +291,7 @@ pub enum MyError { } ``` -Additionally, for the I2C, SPI and Serial communication interfaces we have added a dedicated mechanism +Additionally, for the I2C and SPI communication interfaces we have added a dedicated mechanism which allows for two crucial requirements: 1. Generic code like drivers can interpret and act on errors if they want to. 2. HAL implementations can have arbitrarily-precise error types. @@ -247,12 +339,6 @@ from the compiler should already tell you how it should look like in your case) to limit the scope of the trait imports and thus avoid the ambiguity. Please note that it is also possible to import traits *inside a function*. -## `rng` module - -The `rng` module and its traits have been removed in favor of the [`rand_core`] traits. - -[`rand_core`]: https://crates.io/crates/rand_core - ## Removed blanket implementations There were several blanket implementations of blocking traits using the non-blocking @@ -261,9 +347,7 @@ traits as a base. Since the non-blocking traits have been extracted into the separate crate `embedded-hal-nb`, these have been removed. - - -## Features +## Cargo features The `unproven` feature has been removed and the traits have been marked as proven. In the past, managing unproven features, and having "sort of breaking" changes has been a struggling point. @@ -275,19 +359,16 @@ experiment externally, and merge when some kind of feasibility had been proven. ## Companion crates -## Use-case-specific help - -### For driver authors - -### I2C traits - - -#### SPI traits - - -### For HAL authors - -TODO +The `embedded-hal` project now spans several crates, where some functionality has been moved out from the main `embedded-hal` crate to separate crates as detailed above. Here is the full listing of crates: +| Crate | crates.io | Docs | | +|-|-|-|-| +| [embedded-hal](./embedded-hal) | [![crates.io](https://img.shields.io/crates/v/embedded-hal.svg)](https://crates.io/crates/embedded-hal) | [![Documentation](https://docs.rs/embedded-hal/badge.svg)](https://docs.rs/embedded-hal) | Core traits, blocking version | +| [embedded-hal-async](./embedded-hal-async) | [![crates.io](https://img.shields.io/crates/v/embedded-hal-async.svg)](https://crates.io/crates/embedded-hal-async) | [![Documentation](https://docs.rs/embedded-hal-async/badge.svg)](https://docs.rs/embedded-hal-async) | Core traits, async version | +| [embedded-hal-nb](./embedded-hal-nb) | [![crates.io](https://img.shields.io/crates/v/embedded-hal-nb.svg)](https://crates.io/crates/embedded-hal-nb) | [![Documentation](https://docs.rs/embedded-hal-nb/badge.svg)](https://docs.rs/embedded-hal-nb) | Core traits, polling version using the `nb` crate | +| [embedded-hal-bus](./embedded-hal-bus) | [![crates.io](https://img.shields.io/crates/v/embedded-hal-bus.svg)](https://crates.io/crates/embedded-hal-bus) | [![Documentation](https://docs.rs/embedded-hal-bus/badge.svg)](https://docs.rs/embedded-hal-bus) | Utilities for sharing SPI and I2C buses | +| [embedded-can](./embedded-can) | [![crates.io](https://img.shields.io/crates/v/embedded-can.svg)](https://crates.io/crates/embedded-can) | [![Documentation](https://docs.rs/embedded-can/badge.svg)](https://docs.rs/embedded-can) | Controller Area Network (CAN) traits | +| [embedded-io](./embedded-io) | [![crates.io](https://img.shields.io/crates/v/embedded-io.svg)](https://crates.io/crates/embedded-io) | [![Documentation](https://docs.rs/embedded-io/badge.svg)](https://docs.rs/embedded-io) | I/O traits (read, write, seek, etc.), blocking and nonblocking version. | +| [embedded-io-async](./embedded-io-async) | [![crates.io](https://img.shields.io/crates/v/embedded-io-async.svg)](https://crates.io/crates/embedded-io-async) | [![Documentation](https://docs.rs/embedded-io-async/badge.svg)](https://docs.rs/embedded-io-async) | I/O traits, async version | +| [embedded-io-adapters](./embedded-io-adapters) | [![crates.io](https://img.shields.io/crates/v/embedded-io-adapters.svg)](https://crates.io/crates/embedded-io-adapters) | [![Documentation](https://docs.rs/embedded-io-adapters/badge.svg)](https://docs.rs/embedded-io-adapters) | Adapters between the [`embedded-io`](https://crates.io/crates/embedded-io) and [`embedded-io-async`](https://crates.io/crates/embedded-io-async) traits and other IO traits (`std`, `tokio`, `futures`...) | -[MeetingSummary]: https://hackmd.io/ck-xRXtMTmKYXdK5bEh82A From c5a1df0f3de205ac02c0ec5b15a9e5a303288f8f Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 25 Dec 2023 00:34:50 +0100 Subject: [PATCH 138/199] README fixes. --- embedded-hal/README.md | 22 ++++++++-------------- 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/embedded-hal/README.md b/embedded-hal/README.md index 391599ec5..79667661e 100644 --- a/embedded-hal/README.md +++ b/embedded-hal/README.md @@ -9,13 +9,6 @@ This project is developed and maintained by the [HAL team](https://github.com/rust-embedded/wg#the-hal-team). -**NOTE** This HAL is still in active development. Expect the traits presented here to be -tweaked, split or be replaced wholesale before being stabilized, i.e. before hitting the 1.0.0 -release. - -**NOTE** If you want to use an alpha release of the 1.0.0 version, use an exact version -specifier in your `Cargo.toml` like: `embedded-hal = "=1.0.0-alpha.2"`. - ## Companion crates The main `embedded-hal` crate contains only blocking traits, where the operation is done @@ -30,6 +23,14 @@ SPI and I2C buses. Additionally, more domain-specific traits are available in separate crates: - [`embedded-can`](https://docs.rs/embedded-can): Controller Area Network (CAN) +- [`embedded-io`](https://docs.rs/embedded-io): I/O byte streams (like `std::io`, but `no-std`-compatible). + +## Serial/UART traits + +There is no serial traits in `embedded-hal`. Instead, use [`embedded-io`](https://crates.io/crates/embedded-io). +A serial port is essentially a byte-oriented stream, and that's what `embedded-io` models. Sharing the traits +with all byte streams has some advantages. For example, it allows generic code providing a command-line interface +or a console to operate either on hardware serial ports or on virtual ones like Telnet or USB CDC-ACM. ## Design goals @@ -77,13 +78,6 @@ If you are writing a platform-agnostic driver yourself you are highly encouraged embedded-hal keyword](https://doc.rust-lang.org/cargo/reference/manifest.html#package-metadata) to your crate before publishing it! -## Serial/UART traits - -There is no serial traits in `embedded-hal`. Instead, use [`embedded-io`](https://crates.io/crates/embedded-io). -A serial port is essentially a byte-oriented stream, and that's what `embedded-io` models. Sharing the traits -with all byte streams has some advantages. For example, it allows generic code providing a command-line interface -or a console to operate either on hardware serial ports or on virtual ones like Telnet or USB CDC-ACM. - ## Optional Cargo features - **`defmt-03`**: Derive `defmt::Format` from `defmt` 0.3 for enums and structs. From 009ccf8cb33041cda99ede640d21994ffdc15634 Mon Sep 17 00:00:00 2001 From: Adam Greig Date: Wed, 27 Dec 2023 04:22:52 +0000 Subject: [PATCH 139/199] Fix a couple of typos in migration guide --- MIGRATING-0.2-1.0.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/MIGRATING-0.2-1.0.md b/MIGRATING-0.2-1.0.md index e980e046d..4401ca90b 100644 --- a/MIGRATING-0.2-1.0.md +++ b/MIGRATING-0.2-1.0.md @@ -120,7 +120,7 @@ and we can add them back in a future 1.x release. ### Impractical traits The [`digital::IoPin` trait][iopin] and the [`adc` traits][adc] have been deemed impractical for use and have thus been removed. -Please feel free to comment on the appropriate issue if you need any of these traitsk and propose a solution. +Please feel free to comment on the appropriate issue if you need any of these traits and propose a solution. ### Serial traits @@ -158,7 +158,7 @@ When upgrading code to `embedded-hal` 1.0, it is critical to implement/use the r - Optionally, if the API *does* manage a CS pin automatically, you may implement `SpiDevice`. - This is required if the underlying API requires it to manage the CS pin, like `spidev` on Linux. -Do not implement `SpiBus` and `SpiDevice` on the same struct, since this is never correct. When there's no CS pin being controlled you must implement only `SpiBus`, and when there is, implement only `SpiDevice`. If you want to offer both APIs, implement them on separate structs so the user has to cohose one or the other. +Do not implement `SpiBus` and `SpiDevice` on the same struct, since this is never correct. When there's no CS pin being controlled you must implement only `SpiBus`, and when there is, implement only `SpiDevice`. If you want to offer both APIs, implement them on separate structs so the user has to choose one or the other. ### For driver crates @@ -335,7 +335,7 @@ the same across traits. To overcome this, please import the traits you wish to use individually. If you run into ambiguous method calls, you can disambiguate using the fully-qualified syntax (the error message -from the compiler should already tell you how it should look like in your case) or tweak your trait imports or code +from the compiler should already tell you how it should look in your case) or tweak your trait imports or code to limit the scope of the trait imports and thus avoid the ambiguity. Please note that it is also possible to import traits *inside a function*. From c9b953a48e4f5e548adc06bcf1aafb34a07893c9 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 27 Dec 2023 14:41:30 +0100 Subject: [PATCH 140/199] i2c: disallow returning before transfer is finished. Fixes #455. --- embedded-hal/src/i2c.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/embedded-hal/src/i2c.rs b/embedded-hal/src/i2c.rs index c2a1efb3a..634d52248 100644 --- a/embedded-hal/src/i2c.rs +++ b/embedded-hal/src/i2c.rs @@ -38,6 +38,15 @@ //! you've received from a HAL and "split" it into multiple shared ones, to instantiate //! several drivers on the same bus. //! +//! # Flushing +//! +//! Implementations must flush the transfer, ensuring the bus has returned to an idle state before returning. +//! No pipelining is allowed. Users must be able to shut down the I2C peripheral immediately after a transfer +//! returns, without any risk of e.g. cutting short a stop condition. +//! +//! (Implementations must wait until the last ACK bit to report it as an error anyway. Therefore pipelining would only +//! yield very small time savings, not worth the complexity) +//! //! # For driver authors //! //! Drivers can select the adequate address length with `I2c` or `I2c` depending From 101c6f9225f80c3eda21a99668e53f90da3a379d Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 27 Dec 2023 14:51:38 +0100 Subject: [PATCH 141/199] bus/spi: add semver risk warning to `new_no_delay`. Fixes #552. --- embedded-hal-bus/src/spi/critical_section.rs | 10 ++++++++++ embedded-hal-bus/src/spi/exclusive.rs | 10 ++++++++++ embedded-hal-bus/src/spi/mutex.rs | 10 ++++++++++ embedded-hal-bus/src/spi/refcell.rs | 10 ++++++++++ 4 files changed, 40 insertions(+) diff --git a/embedded-hal-bus/src/spi/critical_section.rs b/embedded-hal-bus/src/spi/critical_section.rs index 7c87eeaaa..838be4f5a 100644 --- a/embedded-hal-bus/src/spi/critical_section.rs +++ b/embedded-hal-bus/src/spi/critical_section.rs @@ -34,6 +34,16 @@ impl<'a, BUS, CS, D> CriticalSectionDevice<'a, BUS, CS, D> { impl<'a, BUS, CS> CriticalSectionDevice<'a, BUS, CS, super::NoDelay> { /// Create a new [`CriticalSectionDevice`] without support for in-transaction delays. /// + /// **Warning**: The returned instance *technically* doesn't comply with the `SpiDevice` + /// contract, which mandates delay support. It is relatively rare for drivers to use + /// in-transaction delays, so you might still want to use this method because it's more practical. + /// + /// Note that a future version of the driver might start using delays, causing your + /// code to panic. This wouldn't be considered a breaking change from the driver side, because + /// drivers are allowed to assume `SpiDevice` implementations comply with the contract. + /// If you feel this risk outweighs the convenience of having `cargo` automatically upgrade + /// the driver crate, you might want to pin the driver's version. + /// /// # Panics /// /// The returned device will panic if you try to execute a transaction diff --git a/embedded-hal-bus/src/spi/exclusive.rs b/embedded-hal-bus/src/spi/exclusive.rs index 3717844c1..f28a10af4 100644 --- a/embedded-hal-bus/src/spi/exclusive.rs +++ b/embedded-hal-bus/src/spi/exclusive.rs @@ -45,6 +45,16 @@ impl ExclusiveDevice { impl ExclusiveDevice { /// Create a new [`ExclusiveDevice`] without support for in-transaction delays. /// + /// **Warning**: The returned instance *technically* doesn't comply with the `SpiDevice` + /// contract, which mandates delay support. It is relatively rare for drivers to use + /// in-transaction delays, so you might still want to use this method because it's more practical. + /// + /// Note that a future version of the driver might start using delays, causing your + /// code to panic. This wouldn't be considered a breaking change from the driver side, because + /// drivers are allowed to assume `SpiDevice` implementations comply with the contract. + /// If you feel this risk outweighs the convenience of having `cargo` automatically upgrade + /// the driver crate, you might want to pin the driver's version. + /// /// # Panics /// /// The returned device will panic if you try to execute a transaction diff --git a/embedded-hal-bus/src/spi/mutex.rs b/embedded-hal-bus/src/spi/mutex.rs index 3167c72d3..b06f3fd95 100644 --- a/embedded-hal-bus/src/spi/mutex.rs +++ b/embedded-hal-bus/src/spi/mutex.rs @@ -32,6 +32,16 @@ impl<'a, BUS, CS, D> MutexDevice<'a, BUS, CS, D> { impl<'a, BUS, CS> MutexDevice<'a, BUS, CS, super::NoDelay> { /// Create a new [`MutexDevice`] without support for in-transaction delays. /// + /// **Warning**: The returned instance *technically* doesn't comply with the `SpiDevice` + /// contract, which mandates delay support. It is relatively rare for drivers to use + /// in-transaction delays, so you might still want to use this method because it's more practical. + /// + /// Note that a future version of the driver might start using delays, causing your + /// code to panic. This wouldn't be considered a breaking change from the driver side, because + /// drivers are allowed to assume `SpiDevice` implementations comply with the contract. + /// If you feel this risk outweighs the convenience of having `cargo` automatically upgrade + /// the driver crate, you might want to pin the driver's version. + /// /// # Panics /// /// The returned device will panic if you try to execute a transaction diff --git a/embedded-hal-bus/src/spi/refcell.rs b/embedded-hal-bus/src/spi/refcell.rs index da8602624..382591c34 100644 --- a/embedded-hal-bus/src/spi/refcell.rs +++ b/embedded-hal-bus/src/spi/refcell.rs @@ -31,6 +31,16 @@ impl<'a, BUS, CS, D> RefCellDevice<'a, BUS, CS, D> { impl<'a, BUS, CS> RefCellDevice<'a, BUS, CS, super::NoDelay> { /// Create a new [`RefCellDevice`] without support for in-transaction delays. /// + /// **Warning**: The returned instance *technically* doesn't comply with the `SpiDevice` + /// contract, which mandates delay support. It is relatively rare for drivers to use + /// in-transaction delays, so you might still want to use this method because it's more practical. + /// + /// Note that a future version of the driver might start using delays, causing your + /// code to panic. This wouldn't be considered a breaking change from the driver side, because + /// drivers are allowed to assume `SpiDevice` implementations comply with the contract. + /// If you feel this risk outweighs the convenience of having `cargo` automatically upgrade + /// the driver crate, you might want to pin the driver's version. + /// /// # Panics /// /// The returned device will panic if you try to execute a transaction From ff1f4695be29d7b3b80a79b6b5d41d493661299a Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 27 Dec 2023 15:06:17 +0100 Subject: [PATCH 142/199] spi: document `Operation::DelayNs` is not for CS delays. --- embedded-hal/src/spi.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/embedded-hal/src/spi.rs b/embedded-hal/src/spi.rs index ca61269e7..508a4a9b1 100644 --- a/embedded-hal/src/spi.rs +++ b/embedded-hal/src/spi.rs @@ -160,6 +160,16 @@ //! why, because these methods can't return before receiving all the read data. However it's still technically possible //! for them to return before the bus is idle. For example, assuming SPI mode 0, the last bit is sampled on the first (rising) edge //! of SCK, at which point a method could return, but the second (falling) SCK edge still has to happen before the bus is idle. +//! +//! # CS-to-clock delays +//! +//! Many chips require a minimum delay between asserting CS and the first SCK edge, and the last SCK edge and deasserting CS. +//! Drivers should *NOT* use [`Operation::DelayNs`] for this, they should instead document that the user should configure the +//! delays when creating the `SpiDevice` instance, same as they have to configure the SPI frequency and mode. This has a few advantages: +//! +//! - Allows implementations that use hardware-managed CS to program the delay in hardware +//! - Allows the end user more flexibility. For example, they can choose to not configure any delay if their MCU is slow +//! enough to "naturally" do the delay (very common if the delay is in the order of nanoseconds). use core::fmt::Debug; From e1472112b215e88230a0e297e05556840103c38d Mon Sep 17 00:00:00 2001 From: Jan Niehusmann Date: Wed, 27 Dec 2023 19:45:00 +0000 Subject: [PATCH 143/199] Fix rustdoc link in doc comment of set_duty_cycle --- embedded-hal/src/pwm.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/embedded-hal/src/pwm.rs b/embedded-hal/src/pwm.rs index 782c45fff..68a94f54f 100644 --- a/embedded-hal/src/pwm.rs +++ b/embedded-hal/src/pwm.rs @@ -74,7 +74,9 @@ pub trait SetDutyCycle: ErrorType { /// Set the duty cycle to `duty / max_duty`. /// /// The caller is responsible for ensuring that the duty cycle value is less than or equal to the maximum duty cycle value, - /// as reported by `get_max_duty`. + /// as reported by [`max_duty_cycle`]. + /// + /// [`max_duty_cycle`]: SetDutyCycle::max_duty_cycle fn set_duty_cycle(&mut self, duty: u16) -> Result<(), Self::Error>; /// Set the duty cycle to 0%, or always inactive. From 83620fed38de805139e46c3c9d119970619f5eba Mon Sep 17 00:00:00 2001 From: GrantM11235 Date: Thu, 28 Dec 2023 23:09:33 -0600 Subject: [PATCH 144/199] Migration guide typo --- MIGRATING-0.2-1.0.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MIGRATING-0.2-1.0.md b/MIGRATING-0.2-1.0.md index 4401ca90b..a2fa8c6f9 100644 --- a/MIGRATING-0.2-1.0.md +++ b/MIGRATING-0.2-1.0.md @@ -26,7 +26,7 @@ There have been _a lot_ of changes in `embedded_hal` between versions 0.2.x and We understand the significance of `embedded-hal` in the Rust embedded ecosystem and thus intend to release a version that stays compatible for a long time. -The main difference betewen `embedded-hal` 0.2 and 1.0 is the project is now focused +The main difference between `embedded-hal` 0.2 and 1.0 is the project is now focused on a single goal: traits for writing drivers that work on any HAL. In `embedded-hal` 0.2, the traits had dual goals: From 74ca5ae008c87b517f5eea67d4613204e693089d Mon Sep 17 00:00:00 2001 From: Ralf Date: Sat, 30 Dec 2023 19:54:24 +0100 Subject: [PATCH 145/199] Commit b6764ecd replaced closures by operations. Update documentation accordingly. --- embedded-hal-async/src/spi.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/embedded-hal-async/src/spi.rs b/embedded-hal-async/src/spi.rs index 38d471d9a..69a46a468 100644 --- a/embedded-hal-async/src/spi.rs +++ b/embedded-hal-async/src/spi.rs @@ -53,7 +53,7 @@ pub trait SpiDevice: ErrorType { /// Do a transfer within a transaction. /// - /// This is a convenience method equivalent to `device.transaction(|bus| bus.transfer(read, write))`. + /// This is a convenience method equivalent to `device.transaction(&mut [Operation::Transfer(read, write)])`. /// /// See also: [`SpiDevice::transaction`], [`SpiBus::transfer`] #[inline] @@ -64,7 +64,7 @@ pub trait SpiDevice: ErrorType { /// Do an in-place transfer within a transaction. /// - /// This is a convenience method equivalent to `device.transaction(|bus| bus.transfer_in_place(buf))`. + /// This is a convenience method equivalent to `device.transaction(&mut [Operation::TransferInPlace(buf)])`. /// /// See also: [`SpiDevice::transaction`], [`SpiBus::transfer_in_place`] #[inline] From 871f473dcf98657c288120ca093cad8c7a8ac518 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 26 Dec 2023 17:09:20 +0100 Subject: [PATCH 146/199] Move migration guide to docs dir. --- MIGRATING-0.2-1.0.md => docs/migrating-from-0.2-to-1.0.md | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename MIGRATING-0.2-1.0.md => docs/migrating-from-0.2-to-1.0.md (100%) diff --git a/MIGRATING-0.2-1.0.md b/docs/migrating-from-0.2-to-1.0.md similarity index 100% rename from MIGRATING-0.2-1.0.md rename to docs/migrating-from-0.2-to-1.0.md From b7ee113478d8f88dbf9f08ba3b76d9bd7d138f97 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 26 Dec 2023 17:27:23 +0100 Subject: [PATCH 147/199] docs/migration: add advice to impl both versions, add e-h-compat --- docs/migrating-from-0.2-to-1.0.md | 84 +++++++++++++++++++++++++++---- 1 file changed, 73 insertions(+), 11 deletions(-) diff --git a/docs/migrating-from-0.2-to-1.0.md b/docs/migrating-from-0.2-to-1.0.md index a2fa8c6f9..5c9709b48 100644 --- a/docs/migrating-from-0.2-to-1.0.md +++ b/docs/migrating-from-0.2-to-1.0.md @@ -19,6 +19,8 @@ - [Removed blanket implementations](#removed-blanket-implementations) - [Cargo Features](#cargo-features) - [Companion crates](#companion-crates) +- [Supporting both 0.2 and 1.0 in the same HAL](#supporting-both-02-and-10-in-the-same-hal) +- [`embedded-hal-compat`](#embedded-hal-compat) ## Overview and reasoning @@ -41,7 +43,7 @@ For `embedded-hal` 1.0, we decided to drop the first goal, targeting only the se - The second goal delivers much more value. Being able to use any driver together with any HAL crate, out of the box, and across the entire Rust Embedded ecosystem, is just plain awesome. This refocusing on drivers is the root cause of many of the changes between `embedded-hal` 0.2 and 1.0: -- [Associated type compatibiilty](#removed-traits) +- [Associated type compatibility](#removed-traits) - [Trait fragmentation](#trait-organization) - [Bus/device separation](#bus-device-separation) - [Fallibility](#fallibility) @@ -91,7 +93,7 @@ These traits have been removed in the 1.0.0 release, with no replacement for now - [`watchdog::Watchdog`][watchdog] Please find a general [roadmap with further guidance here][roadmap-rm-traits] about -whether and how to get these traits back in a future release +whether and how to get these traits back in a future release. If you are a generic driver author and need one of them, we would like to hear from you. Please add your use case to the appropriate issue for the trait affected. @@ -359,16 +361,76 @@ experiment externally, and merge when some kind of feasibility had been proven. ## Companion crates -The `embedded-hal` project now spans several crates, where some functionality has been moved out from the main `embedded-hal` crate to separate crates as detailed above. Here is the full listing of crates: +The `embedded-hal` project now spans several crates, where some functionality has been moved out from the main `embedded-hal` crate to separate crates as detailed above. + +Different crates are released independently. The main `embedded-hal-*` trait crates have reached 1.0 maturity, others will become 1.0 as time passes. + +Here is the full listing of crates: | Crate | crates.io | Docs | | |-|-|-|-| -| [embedded-hal](./embedded-hal) | [![crates.io](https://img.shields.io/crates/v/embedded-hal.svg)](https://crates.io/crates/embedded-hal) | [![Documentation](https://docs.rs/embedded-hal/badge.svg)](https://docs.rs/embedded-hal) | Core traits, blocking version | -| [embedded-hal-async](./embedded-hal-async) | [![crates.io](https://img.shields.io/crates/v/embedded-hal-async.svg)](https://crates.io/crates/embedded-hal-async) | [![Documentation](https://docs.rs/embedded-hal-async/badge.svg)](https://docs.rs/embedded-hal-async) | Core traits, async version | -| [embedded-hal-nb](./embedded-hal-nb) | [![crates.io](https://img.shields.io/crates/v/embedded-hal-nb.svg)](https://crates.io/crates/embedded-hal-nb) | [![Documentation](https://docs.rs/embedded-hal-nb/badge.svg)](https://docs.rs/embedded-hal-nb) | Core traits, polling version using the `nb` crate | -| [embedded-hal-bus](./embedded-hal-bus) | [![crates.io](https://img.shields.io/crates/v/embedded-hal-bus.svg)](https://crates.io/crates/embedded-hal-bus) | [![Documentation](https://docs.rs/embedded-hal-bus/badge.svg)](https://docs.rs/embedded-hal-bus) | Utilities for sharing SPI and I2C buses | -| [embedded-can](./embedded-can) | [![crates.io](https://img.shields.io/crates/v/embedded-can.svg)](https://crates.io/crates/embedded-can) | [![Documentation](https://docs.rs/embedded-can/badge.svg)](https://docs.rs/embedded-can) | Controller Area Network (CAN) traits | -| [embedded-io](./embedded-io) | [![crates.io](https://img.shields.io/crates/v/embedded-io.svg)](https://crates.io/crates/embedded-io) | [![Documentation](https://docs.rs/embedded-io/badge.svg)](https://docs.rs/embedded-io) | I/O traits (read, write, seek, etc.), blocking and nonblocking version. | -| [embedded-io-async](./embedded-io-async) | [![crates.io](https://img.shields.io/crates/v/embedded-io-async.svg)](https://crates.io/crates/embedded-io-async) | [![Documentation](https://docs.rs/embedded-io-async/badge.svg)](https://docs.rs/embedded-io-async) | I/O traits, async version | -| [embedded-io-adapters](./embedded-io-adapters) | [![crates.io](https://img.shields.io/crates/v/embedded-io-adapters.svg)](https://crates.io/crates/embedded-io-adapters) | [![Documentation](https://docs.rs/embedded-io-adapters/badge.svg)](https://docs.rs/embedded-io-adapters) | Adapters between the [`embedded-io`](https://crates.io/crates/embedded-io) and [`embedded-io-async`](https://crates.io/crates/embedded-io-async) traits and other IO traits (`std`, `tokio`, `futures`...) | +| [embedded-hal](../embedded-hal) | [![crates.io](https://img.shields.io/crates/v/embedded-hal.svg)](https://crates.io/crates/embedded-hal) | [![Documentation](https://docs.rs/embedded-hal/badge.svg)](https://docs.rs/embedded-hal) | Core traits, blocking version | +| [embedded-hal-async](../embedded-hal-async) | [![crates.io](https://img.shields.io/crates/v/embedded-hal-async.svg)](https://crates.io/crates/embedded-hal-async) | [![Documentation](https://docs.rs/embedded-hal-async/badge.svg)](https://docs.rs/embedded-hal-async) | Core traits, async version | +| [embedded-hal-nb](../embedded-hal-nb) | [![crates.io](https://img.shields.io/crates/v/embedded-hal-nb.svg)](https://crates.io/crates/embedded-hal-nb) | [![Documentation](https://docs.rs/embedded-hal-nb/badge.svg)](https://docs.rs/embedded-hal-nb) | Core traits, polling version using the `nb` crate | +| [embedded-hal-bus](../embedded-hal-bus) | [![crates.io](https://img.shields.io/crates/v/embedded-hal-bus.svg)](https://crates.io/crates/embedded-hal-bus) | [![Documentation](https://docs.rs/embedded-hal-bus/badge.svg)](https://docs.rs/embedded-hal-bus) | Utilities for sharing SPI and I2C buses | +| [embedded-can](../embedded-can) | [![crates.io](https://img.shields.io/crates/v/embedded-can.svg)](https://crates.io/crates/embedded-can) | [![Documentation](https://docs.rs/embedded-can/badge.svg)](https://docs.rs/embedded-can) | Controller Area Network (CAN) traits | +| [embedded-io](../embedded-io) | [![crates.io](https://img.shields.io/crates/v/embedded-io.svg)](https://crates.io/crates/embedded-io) | [![Documentation](https://docs.rs/embedded-io/badge.svg)](https://docs.rs/embedded-io) | I/O traits (read, write, seek, etc.), blocking and nonblocking version. | +| [embedded-io-async](../embedded-io-async) | [![crates.io](https://img.shields.io/crates/v/embedded-io-async.svg)](https://crates.io/crates/embedded-io-async) | [![Documentation](https://docs.rs/embedded-io-async/badge.svg)](https://docs.rs/embedded-io-async) | I/O traits, async version | +| [embedded-io-adapters](../embedded-io-adapters) | [![crates.io](https://img.shields.io/crates/v/embedded-io-adapters.svg)](https://crates.io/crates/embedded-io-adapters) | [![Documentation](https://docs.rs/embedded-io-adapters/badge.svg)](https://docs.rs/embedded-io-adapters) | Adapters between the [`embedded-io`](https://crates.io/crates/embedded-io) and [`embedded-io-async`](https://crates.io/crates/embedded-io-async) traits and other IO traits (`std`, `tokio`, `futures`...) | + +## Supporting both 0.2 and 1.0 in the same HAL + +It is strongly recommended that HAL implementation crates provide implementations for both the `embedded-hal` v0.2 and v1.0 traits. +This allows users to use drivers using either version seamlessly. + +The way you do it is adding a dependency on both versions in `Cargo.toml` like this: + +```toml +[dependencies] +embedded-hal-02 = { package = "embedded-hal", version = "0.2.7", features = ["unproven"] } +embedded-hal-1 = { package = "embedded-hal", version = "1.0" } +``` + +This allows you to refer to the v0.2 traits under the `embedded_hal_02` name, and the v1.0 traits under +`embedded_hal_1`. Implement both versions on the same struct. For example, for an input pin: + +```rust +/// The HAL's input pin struct +struct Input {...} + +/// Implement the v0.2 traits on the struct. +impl embedded_hal_02::digital::v2::InputPin for Input { + type Error = Infallible; + + fn is_high(&self) -> Result { + ... + } + + fn is_low(&self) -> Result { + ... + } +} + +/// ... and implement the v1.0 traits on the *same* struct. +impl embedded_hal_1::digital::ErrorType for Input { + type Error = Infallible; +} + +impl embedded_hal_1::digital::InputPin for Input { + fn is_high(&mut self) -> Result { + ... + } + + fn is_low(&mut self) -> Result { + ... + } +} +``` + +## `embedded-hal-compat` + +For HAL implementation crates that haven't been updated yet, [embedded-hal-compat](https://github.com/ryankurte/embedded-hal-compat) +provides shims to support interoperability between `embedded-hal` v0.2 and v1.0. +This allows using a driver requiring v1.0 with a HAL crate implementing only v0.2 or vice-versa, (generally) without alteration. +See the [docs](https://docs.rs/embedded-hal-compat/) for examples. From bf488dfb20809b90d0d743fd57a962a5ee176b66 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 26 Dec 2023 17:28:12 +0100 Subject: [PATCH 148/199] docs: update readmes for v1.0 --- README.md | 19 ++++--------------- docs/version-policy.md | 26 -------------------------- embedded-hal-bus/README.md | 2 +- 3 files changed, 5 insertions(+), 42 deletions(-) delete mode 100644 docs/version-policy.md diff --git a/README.md b/README.md index 3d86509eb..da60c3fed 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,9 @@ This project is developed and maintained by the [HAL team](https://github.com/rust-embedded/wg#the-hal-team). +> [!IMPORTANT] +> 📣 `embedded-hal` v1.0 is now released! Check out the [announcement blog post](https://blog.rust-embedded.org/embedded-hal-v1/), the [API documentation](https://docs.rs/embedded-hal) and the [migration guide](docs/migrating-from-0.2-to-1.0.md). + ## Scope `embedded-hal` serves as a foundation for building an ecosystem of platform-agnostic drivers. @@ -38,24 +41,10 @@ The main `embedded-hal` project is not tied to a specific execution model like | [embedded-io-async](./embedded-io-async) | [![crates.io](https://img.shields.io/crates/v/embedded-io-async.svg)](https://crates.io/crates/embedded-io-async) | [![Documentation](https://docs.rs/embedded-io-async/badge.svg)](https://docs.rs/embedded-io-async) | I/O traits, async version | | [embedded-io-adapters](./embedded-io-adapters) | [![crates.io](https://img.shields.io/crates/v/embedded-io-adapters.svg)](https://crates.io/crates/embedded-io-adapters) | [![Documentation](https://docs.rs/embedded-io-adapters/badge.svg)](https://docs.rs/embedded-io-adapters) | Adapters between the [`embedded-io`](https://crates.io/crates/embedded-io) and [`embedded-io-async`](https://crates.io/crates/embedded-io-async) traits and other IO traits (`std`, `tokio`, `futures`...) | -## Releases - -At the moment we are working towards a `1.0.0` release (see [#177]). During this process we will -release alpha versions like `1.0.0-alpha.1` and `1.0.0-alpha.2`. -Alpha releases are **not guaranteed** to be compatible with each other. -They are provided as early previews for community testing and preparation for the final release. -If you use an alpha release, we recommend you choose an exact version specification in your -`Cargo.toml` like: `embedded-hal = "=1.0.0-alpha.9"` - -See [this guide](docs/version-policy.md) for a way to implement both an `embedded-hal` `0.2.x` -version and an `-alpha` version side by side in a HAL. - -[#177]: https://github.com/rust-embedded/embedded-hal/issues/177 - ## Documents +- [Migrating from v0.2 to v1.0](docs/migrating-from-0.2-to-1.0.md). - [How-to: add a new trait](docs/how-to-add-a-new-trait.md) -- [Version policy](docs/version-policy.md) - [MSRV](docs/msrv.md) ## Implementations and drivers diff --git a/docs/version-policy.md b/docs/version-policy.md deleted file mode 100644 index 2f4f0e75e..000000000 --- a/docs/version-policy.md +++ /dev/null @@ -1,26 +0,0 @@ -# Version policy - -At the moment we are working towards a `1.0.0` release (see [#177]). During this process we will -release alpha versions like `1.0.0-alpha.1` and `1.0.0-alpha.2`. -Alpha releases are **not guaranteed** to be compatible with each other. -They are provided as early previews for community testing and preparation for the final release. -If you use an alpha release, we recommend you choose an exact version specification in your -`Cargo.toml` like: `embedded-hal = "=1.0.0-alpha.2"` - -See below for a way to implement both an `embedded-hal` `0.2.x` version and an `-alpha` version -side by side in a HAL. - -[#177]: https://github.com/rust-embedded/embedded-hal/issues/177 - -## Supporting different (alpha and non-alpha) HALs - -[embedded-hal-compat](https://github.com/ryankurte/embedded-hal-compat) provides shims -to support interoperability between the latest `0.2.x` and `1.0.0-alpha.N` HALs, allowing one to use -incompatible HAL components (generally) without alteration. -See the [docs](https://docs.rs/embedded-hal-compat/) for examples. - -It is also possible for HAL implementations to support both the latest `0.2.x` and `1.0.0-alpha.N` versions -side by side, for an example see [LPC8xx HAL](https://github.com/lpc-rs/lpc8xx-hal). - -Note that `embedded-hal` `-alpha` versions are a moving target and _not guaranteed_ to be compatible. -Because of this we only aim to support the latest `-alpha`. diff --git a/embedded-hal-bus/README.md b/embedded-hal-bus/README.md index 3cfae9448..9fc8aaae0 100644 --- a/embedded-hal-bus/README.md +++ b/embedded-hal-bus/README.md @@ -15,7 +15,7 @@ This project is developed and maintained by the [HAL team](https://github.com/ru To support bus sharing, `embedded-hal` provides the `SpiBus` and `SpiDevice` traits. `SpiBus` represents an entire bus, while `SpiDevice` represents a device on that bus. For further details on these traits, please consult the -[`embedded-hal` documentation](https://docs.rs/embedded-hal/1.0.0-alpha.10/embedded_hal/spi/index.html). +[`embedded-hal` documentation](https://docs.rs/embedded-hal/latest/embedded_hal/spi/index.html). `embedded-hal` trait implementations for microcontrollers should implement the `SpiBus` trait. However, device drivers should use the `SpiDevice` traits, _not the `SpiBus` traits_ if at all possible From 5f89197a3e2152144790d1ebabc83330f3304a79 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 9 Jan 2024 22:11:33 +0100 Subject: [PATCH 149/199] docs/migration: document gpio change to `&mut self`. --- docs/migrating-from-0.2-to-1.0.md | 90 +++++++++++++++++++++++++++++++ 1 file changed, 90 insertions(+) diff --git a/docs/migrating-from-0.2-to-1.0.md b/docs/migrating-from-0.2-to-1.0.md index 5c9709b48..5fa5481b1 100644 --- a/docs/migrating-from-0.2-to-1.0.md +++ b/docs/migrating-from-0.2-to-1.0.md @@ -15,6 +15,7 @@ - [Fallibility](#fallibility) - [SPI transfer return type](#spi-transfer-return-type) - [Error type bounds](#error-type-bounds) +- [GPIO traits now require `&mut self`](#gpio-traits-now-require-mut-self) - [Prelude](#prelude) - [Removed blanket implementations](#removed-blanket-implementations) - [Cargo Features](#cargo-features) @@ -330,6 +331,95 @@ fn set_some_parameter(&mut self) -> Result<(), Self::Error> { } ``` +## GPIO traits now require `&mut self` + +Methods on `InputPin` and `State` now take `&mut self` instead of `&self`, to allow implementations to +have mutable state or access exclusive resources. + +**For HAL implementors**: You should not need to do any changes, since `&mut self` is strictly more permissive +for implementations. + +For ease of use, you might want to provide inherent methods that take `&self` if the hardware permits it. In this case, +you might need to do `&*self` to call them from the trait methods. Otherwise Rust will resolve the +method call to the trait method, causing infinite recursion. + +```rust +struct HalPin; + +impl HalPin { + fn is_high(&self) -> bool { + true + } + + fn is_low(&self) -> bool { + true + } +} + +impl InputPin for HalPin { + fn is_high(&mut self) -> Result { + // Needs `&*self` so that the inherent `is_high` is picked. + Ok((&*self).is_high()) + } + + fn is_low(&mut self) -> Result { + Ok((&*self).is_low()) + } +} +``` + +**For driver authors**: If your driver does not need sharing input pins, you should be able to upgrade without any changes. +If you do need to share input pins, the recommended solution is wrapping them with a `RefCell`. + +Note that if you need to share multiple objects, you should prefer using a single `RefCell` wherever possible to reduce RAM +usage. Make an "inner" struct with all the objects that need sharing, and wrap it in a single `RefCell`. Below is an example +skeleton of a keypad driver using row/column multiplexing, sharing multiple `InputPin`s and `OutputPin`s with a single `RefCell`: + +```rust +use core::cell::RefCell; + +use embedded_hal::digital::{ErrorType, InputPin, OutputPin}; + +pub struct Keypad { + inner: RefCell>, +} + +struct KeypadInner { + cols: [O; NCOLS], + rows: [I; NROWS], +} + +pub struct KeypadInput<'a, O: OutputPin, I: InputPin, const NCOLS: usize, const NROWS: usize> { + inner: &'a RefCell>, + row: usize, + col: usize, +} + +impl<'a, O: OutputPin, I: InputPin, const NCOLS: usize, const NROWS: usize> ErrorType for KeypadInput<'a, O, I, NCOLS, NROWS> { + type Error = core::convert::Infallible; +} + +impl<'a, O: OutputPin, I: InputPin, const NCOLS: usize, const NROWS: usize> InputPin for KeypadInput<'a, O, I, NCOLS, NROWS> { + fn is_high(&mut self) -> Result { + Ok(!self.is_low()?) + } + + fn is_low(&mut self) -> Result { + let inner = &mut *self.inner.borrow_mut(); + let row = &mut inner.rows[self.row]; + let col = &mut inner.cols[self.col]; + + // using unwrap for demo purposes, you should propagate errors up instead. + col.set_low().unwrap(); + let out = row.is_low().unwrap(); + col.set_high().unwrap(); + + Ok(out) + } +} +``` + + ## Prelude The prelude has been removed because it could make method calls ambiguous, since the method names are now From 11c8b9e23f220ca8088673f3c7e3863d3cfa4d02 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 27 Dec 2023 15:16:04 +0100 Subject: [PATCH 150/199] Release embedded-hal{,-async,-nb} v1.0.0, embedded-hal-bus v0.1.0 --- .github/workflows/test.yml | 2 +- embedded-hal-async/CHANGELOG.md | 9 +++++++-- embedded-hal-async/Cargo.toml | 4 ++-- embedded-hal-bus/CHANGELOG.md | 7 ++++++- embedded-hal-bus/Cargo.toml | 6 +++--- embedded-hal-nb/CHANGELOG.md | 7 ++++++- embedded-hal-nb/Cargo.toml | 4 ++-- embedded-hal/CHANGELOG.md | 7 ++++++- embedded-hal/Cargo.toml | 2 +- 9 files changed, 34 insertions(+), 14 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index e1d7c5664..924bbd821 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -45,7 +45,7 @@ jobs: - uses: dtolnay/rust-toolchain@1.60 - run: > cargo test - -p embedded-hal:1.0.0-rc.3 + -p embedded-hal:1.0.0 -p embedded-hal-bus -p embedded-hal-nb -p embedded-io diff --git a/embedded-hal-async/CHANGELOG.md b/embedded-hal-async/CHANGELOG.md index 314e0ebaa..40fc8b68a 100644 --- a/embedded-hal-async/CHANGELOG.md +++ b/embedded-hal-async/CHANGELOG.md @@ -7,7 +7,11 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] -No unreleased changes +No unreleased changes yet. + +## [v1.0.0] - 2023-12-28 + +- Updated `embedded-hal` to version `1.0.0`. ## [v1.0.0-rc.3] - 2023-12-14 @@ -86,7 +90,8 @@ No unreleased changes First release to crates.io -[Unreleased]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-async-v1.0.0-rc.3...HEAD +[Unreleased]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-async-v1.0.0...HEAD +[v1.0.0]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-async-v1.0.0-rc.3...embedded-hal-async-v1.0.0 [v1.0.0-rc.3]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-async-v1.0.0-rc.2...embedded-hal-async-v1.0.0-rc.3 [v1.0.0-rc.2]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-async-v1.0.0-rc.1...embedded-hal-async-v1.0.0-rc.2 [v1.0.0-rc.1]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-async-v0.2.0-alpha.2...embedded-hal-async-v1.0.0-rc.1 diff --git a/embedded-hal-async/Cargo.toml b/embedded-hal-async/Cargo.toml index fe09580fe..4d2679336 100644 --- a/embedded-hal-async/Cargo.toml +++ b/embedded-hal-async/Cargo.toml @@ -11,12 +11,12 @@ license = "MIT OR Apache-2.0" name = "embedded-hal-async" readme = "README.md" repository = "https://github.com/rust-embedded/embedded-hal" -version = "1.0.0-rc.3" +version = "1.0.0" rust-version = "1.75" [features] defmt-03 = ["dep:defmt-03", "embedded-hal/defmt-03"] [dependencies] -embedded-hal = { version = "=1.0.0-rc.3", path = "../embedded-hal" } +embedded-hal = { version = "1.0.0", path = "../embedded-hal" } defmt-03 = { package = "defmt", version = "0.3", optional = true } diff --git a/embedded-hal-bus/CHANGELOG.md b/embedded-hal-bus/CHANGELOG.md index 8155db960..04de7aa69 100644 --- a/embedded-hal-bus/CHANGELOG.md +++ b/embedded-hal-bus/CHANGELOG.md @@ -9,6 +9,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/). No unreleased changes +## [v0.1.0] - 2023-12-28 + +- Updated `embedded-hal` to version `1.0.0`. + ## [v0.1.0-rc.3] - 2023-12-14 - Updated `embedded-hal` to version `1.0.0-rc.3`. @@ -52,7 +56,8 @@ No unreleased changes First release to crates.io -[Unreleased]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-bus-v0.1.0-rc.3...HEAD +[Unreleased]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-bus-v0.1.0...HEAD +[v0.1.0]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-bus-v0.1.0-rc.3...embedded-hal-bus-v0.1.0 [v0.1.0-rc.3]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-bus-v0.1.0-rc.2...embedded-hal-bus-v0.1.0-rc.3 [v0.1.0-rc.2]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-bus-v0.1.0-rc.1...embedded-hal-bus-v0.1.0-rc.2 [v0.1.0-rc.1]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-bus-v0.1.0-alpha.3...embedded-hal-bus-v0.1.0-rc.1 diff --git a/embedded-hal-bus/Cargo.toml b/embedded-hal-bus/Cargo.toml index 7f04bbded..5c89c9704 100644 --- a/embedded-hal-bus/Cargo.toml +++ b/embedded-hal-bus/Cargo.toml @@ -12,7 +12,7 @@ license = "MIT OR Apache-2.0" name = "embedded-hal-bus" readme = "README.md" repository = "https://github.com/rust-embedded/embedded-hal" -version = "0.1.0-rc.3" +version = "0.1.0" [features] std = [] @@ -20,8 +20,8 @@ async = ["dep:embedded-hal-async"] defmt-03 = ["dep:defmt-03", "embedded-hal/defmt-03", "embedded-hal-async?/defmt-03"] [dependencies] -embedded-hal = { version = "=1.0.0-rc.3", path = "../embedded-hal" } -embedded-hal-async = { version = "=1.0.0-rc.3", path = "../embedded-hal-async", optional = true } +embedded-hal = { version = "1.0.0", path = "../embedded-hal" } +embedded-hal-async = { version = "1.0.0", path = "../embedded-hal-async", optional = true } critical-section = { version = "1.0" } defmt-03 = { package = "defmt", version = "0.3", optional = true } diff --git a/embedded-hal-nb/CHANGELOG.md b/embedded-hal-nb/CHANGELOG.md index 6ac3be4ed..53ff11796 100644 --- a/embedded-hal-nb/CHANGELOG.md +++ b/embedded-hal-nb/CHANGELOG.md @@ -9,6 +9,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/). No unreleased changes +## [v1.0.0] - 2023-12-28 + +- Updated `embedded-hal` to version `1.0.0`. + ## [v1.0.0-rc.3] - 2023-12-14 - Updated `embedded-hal` to version `1.0.0-rc.3`. @@ -44,7 +48,8 @@ No unreleased changes First release to crates.io -[Unreleased]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-nb-v1.0.0-rc.3...HEAD +[Unreleased]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-nb-v1.0.0...HEAD +[v1.0.0]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-nb-v1.0.0-rc.3...embedded-hal-nb-v1.0.0 [v1.0.0-rc.3]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-nb-v1.0.0-rc.2...embedded-hal-nb-v1.0.0-rc.3 [v1.0.0-rc.2]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-nb-v1.0.0-rc.1...embedded-hal-nb-v1.0.0-rc.2 [v1.0.0-rc.1]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-nb-v1.0.0-alpha.3...embedded-hal-nb-v1.0.0-rc.1 diff --git a/embedded-hal-nb/Cargo.toml b/embedded-hal-nb/Cargo.toml index d0c006e76..d4575ead0 100644 --- a/embedded-hal-nb/Cargo.toml +++ b/embedded-hal-nb/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "embedded-hal-nb" -version = "1.0.0-rc.3" +version = "1.0.0" edition = "2021" rust-version = "1.56" @@ -13,7 +13,7 @@ readme = "README.md" repository = "https://github.com/rust-embedded/embedded-hal" [dependencies] -embedded-hal = { version = "=1.0.0-rc.3", path = "../embedded-hal" } +embedded-hal = { version = "1.0.0", path = "../embedded-hal" } nb = "1" [dev-dependencies] diff --git a/embedded-hal/CHANGELOG.md b/embedded-hal/CHANGELOG.md index 9d3ac625b..fcbcc41bf 100644 --- a/embedded-hal/CHANGELOG.md +++ b/embedded-hal/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] +No unreleased changes yet. + +## [v1.0.0] - 2023-12-28 + - gpio: remove `ToggleableOutputPin`, move `toggle()` to `StatefulOutputPin`. ## [v1.0.0-rc.3] - 2023-12-14 @@ -314,7 +318,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/). Initial release -[Unreleased]: https://github.com/rust-embedded/embedded-hal/compare/v1.0.0-rc.3...HEAD +[Unreleased]: https://github.com/rust-embedded/embedded-hal/compare/v1.0.0...HEAD +[v1.0.0]: https://github.com/rust-embedded/embedded-hal/compare/v1.0.0-rc.3...v1.0.0 [v1.0.0-rc.3]: https://github.com/rust-embedded/embedded-hal/compare/v1.0.0-rc.2...v1.0.0-rc.3 [v1.0.0-rc.2]: https://github.com/rust-embedded/embedded-hal/compare/v1.0.0-rc.1...v1.0.0-rc.2 [v1.0.0-rc.1]: https://github.com/rust-embedded/embedded-hal/compare/v1.0.0-alpha.11...v1.0.0-rc.1 diff --git a/embedded-hal/Cargo.toml b/embedded-hal/Cargo.toml index b91f45f44..f67b2527b 100644 --- a/embedded-hal/Cargo.toml +++ b/embedded-hal/Cargo.toml @@ -14,7 +14,7 @@ license = "MIT OR Apache-2.0" name = "embedded-hal" readme = "README.md" repository = "https://github.com/rust-embedded/embedded-hal" -version = "1.0.0-rc.3" +version = "1.0.0" [features] defmt-03 = ["dep:defmt-03"] From 676ccd8a5d732fba1b507c303923b7c5e63bbfe7 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 9 Jan 2024 23:49:56 +0100 Subject: [PATCH 151/199] migration: autoderef workaround still works with just `*self`. --- docs/migrating-from-0.2-to-1.0.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/migrating-from-0.2-to-1.0.md b/docs/migrating-from-0.2-to-1.0.md index 5fa5481b1..b35a7a2d4 100644 --- a/docs/migrating-from-0.2-to-1.0.md +++ b/docs/migrating-from-0.2-to-1.0.md @@ -340,7 +340,7 @@ have mutable state or access exclusive resources. for implementations. For ease of use, you might want to provide inherent methods that take `&self` if the hardware permits it. In this case, -you might need to do `&*self` to call them from the trait methods. Otherwise Rust will resolve the +you might need to do `*self` to call them from the trait methods. Otherwise Rust will resolve the method call to the trait method, causing infinite recursion. ```rust @@ -358,12 +358,12 @@ impl HalPin { impl InputPin for HalPin { fn is_high(&mut self) -> Result { - // Needs `&*self` so that the inherent `is_high` is picked. - Ok((&*self).is_high()) + // Needs `*self` so that the inherent `is_high` is picked. + Ok((*self).is_high()) } fn is_low(&mut self) -> Result { - Ok((&*self).is_low()) + Ok((*self).is_low()) } } ``` From 983a60bc8e6d798cb579f8c9b14149fed4608373 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 9 Jan 2024 23:50:13 +0100 Subject: [PATCH 152/199] Link to announcement blog post and migration guide in changelogs. --- embedded-hal-async/CHANGELOG.md | 2 ++ embedded-hal-nb/CHANGELOG.md | 2 ++ embedded-hal/CHANGELOG.md | 2 ++ 3 files changed, 6 insertions(+) diff --git a/embedded-hal-async/CHANGELOG.md b/embedded-hal-async/CHANGELOG.md index 40fc8b68a..cfd5ad051 100644 --- a/embedded-hal-async/CHANGELOG.md +++ b/embedded-hal-async/CHANGELOG.md @@ -11,6 +11,8 @@ No unreleased changes yet. ## [v1.0.0] - 2023-12-28 +Check out the [announcement blog post](https://blog.rust-embedded.org/embedded-hal-v1/) and the [migration guide](../docs/migrating-from-0.2-to-1.0.md) for help with migrating from v0.2 to v1.0. + - Updated `embedded-hal` to version `1.0.0`. ## [v1.0.0-rc.3] - 2023-12-14 diff --git a/embedded-hal-nb/CHANGELOG.md b/embedded-hal-nb/CHANGELOG.md index 53ff11796..7aaf47903 100644 --- a/embedded-hal-nb/CHANGELOG.md +++ b/embedded-hal-nb/CHANGELOG.md @@ -11,6 +11,8 @@ No unreleased changes ## [v1.0.0] - 2023-12-28 +CheckCheck out the [announcement blog post](https://blog.rust-embedded.org/embedded-hal-v1/) and the [migration guide](../docs/migrating-from-0.2-to-1.0.md) for help with migrating from v0.2 to v1.0. + - Updated `embedded-hal` to version `1.0.0`. ## [v1.0.0-rc.3] - 2023-12-14 diff --git a/embedded-hal/CHANGELOG.md b/embedded-hal/CHANGELOG.md index fcbcc41bf..4a1af0e3f 100644 --- a/embedded-hal/CHANGELOG.md +++ b/embedded-hal/CHANGELOG.md @@ -11,6 +11,8 @@ No unreleased changes yet. ## [v1.0.0] - 2023-12-28 +Check out the [announcement blog post](https://blog.rust-embedded.org/embedded-hal-v1/) and the [migration guide](../docs/migrating-from-0.2-to-1.0.md) for help with migrating from v0.2 to v1.0. + - gpio: remove `ToggleableOutputPin`, move `toggle()` to `StatefulOutputPin`. ## [v1.0.0-rc.3] - 2023-12-14 From ebc756e2e4ec0107f2877c1f032de59dd929cb67 Mon Sep 17 00:00:00 2001 From: Diego Barrios Romero Date: Wed, 10 Jan 2024 08:56:14 +0100 Subject: [PATCH 153/199] Fix typo --- embedded-hal-nb/CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embedded-hal-nb/CHANGELOG.md b/embedded-hal-nb/CHANGELOG.md index 7aaf47903..f31190acb 100644 --- a/embedded-hal-nb/CHANGELOG.md +++ b/embedded-hal-nb/CHANGELOG.md @@ -11,7 +11,7 @@ No unreleased changes ## [v1.0.0] - 2023-12-28 -CheckCheck out the [announcement blog post](https://blog.rust-embedded.org/embedded-hal-v1/) and the [migration guide](../docs/migrating-from-0.2-to-1.0.md) for help with migrating from v0.2 to v1.0. +Check out the [announcement blog post](https://blog.rust-embedded.org/embedded-hal-v1/) and the [migration guide](../docs/migrating-from-0.2-to-1.0.md) for help with migrating from v0.2 to v1.0. - Updated `embedded-hal` to version `1.0.0`. From a5ad7bf4f1c048a6750a74abc82395459db04a29 Mon Sep 17 00:00:00 2001 From: Markus Reiter Date: Wed, 17 Jan 2024 01:58:15 +0100 Subject: [PATCH 154/199] Re-export `digital` error types in `embedded-hal-async`. --- embedded-hal-async/src/digital.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/embedded-hal-async/src/digital.rs b/embedded-hal-async/src/digital.rs index 4fb3843cb..5efe3d180 100644 --- a/embedded-hal-async/src/digital.rs +++ b/embedded-hal-async/src/digital.rs @@ -15,9 +15,10 @@ //! .expect("failed to await input pin") //! } //! ``` +pub use embedded_hal::digital::{Error, ErrorKind, ErrorType}; /// Asynchronously wait for GPIO pin state. -pub trait Wait: embedded_hal::digital::ErrorType { +pub trait Wait: ErrorType { /// Wait until the pin is high. If it is already high, return immediately. /// /// # Note for implementers From db179e98faa91e1a4df7c84cf519cd741d4630f7 Mon Sep 17 00:00:00 2001 From: Markus Reiter Date: Wed, 17 Jan 2024 02:02:57 +0100 Subject: [PATCH 155/199] Merge `i2c` re-exports. --- embedded-hal-async/src/i2c.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/embedded-hal-async/src/i2c.rs b/embedded-hal-async/src/i2c.rs index 1a8f07a48..445cd9e6b 100644 --- a/embedded-hal-async/src/i2c.rs +++ b/embedded-hal-async/src/i2c.rs @@ -16,9 +16,9 @@ //! 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. -pub use embedded_hal::i2c::Operation; pub use embedded_hal::i2c::{ - AddressMode, Error, ErrorKind, ErrorType, NoAcknowledgeSource, SevenBitAddress, TenBitAddress, + AddressMode, Error, ErrorKind, ErrorType, NoAcknowledgeSource, Operation, SevenBitAddress, + TenBitAddress, }; /// Async I2c. From aebcf0f785ef7ce667e37768edfe6f06e22d6fab Mon Sep 17 00:00:00 2001 From: Andrew Walbran Date: Sun, 4 Feb 2024 20:43:58 +0000 Subject: [PATCH 156/199] Implement Display and std::error::Error for DeviceError if possible. --- embedded-hal-bus/README.md | 3 ++- embedded-hal-bus/src/spi/mod.rs | 14 +++++++++++++- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/embedded-hal-bus/README.md b/embedded-hal-bus/README.md index 9fc8aaae0..ce71eec92 100644 --- a/embedded-hal-bus/README.md +++ b/embedded-hal-bus/README.md @@ -30,7 +30,8 @@ provides mechanisms to obtain multiple `I2c` instances out of a single `I2c` ins ## Optional Cargo features -- **`std`**: enable shared bus implementations using `std::sync::Mutex`. +- **`std`**: enable shared bus implementations using `std::sync::Mutex`, and implement + `std::error::Error` for `DeviceError`. - **`async`**: enable `embedded-hal-async` support. - **`defmt-03`**: Derive `defmt::Format` from `defmt` 0.3 for enums and structs. diff --git a/embedded-hal-bus/src/spi/mod.rs b/embedded-hal-bus/src/spi/mod.rs index 8b51242d8..d408bd92f 100644 --- a/embedded-hal-bus/src/spi/mod.rs +++ b/embedded-hal-bus/src/spi/mod.rs @@ -1,6 +1,6 @@ //! `SpiDevice` implementations. -use core::fmt::Debug; +use core::fmt::{self, Debug, Display, Formatter}; use embedded_hal::spi::{Error, ErrorKind}; mod exclusive; @@ -29,6 +29,18 @@ pub enum DeviceError { Cs(CS), } +impl Display for DeviceError { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + match self { + Self::Spi(bus) => write!(f, "SPI bus error: {}", bus), + Self::Cs(cs) => write!(f, "SPI CS error: {}", cs), + } + } +} + +#[cfg(feature = "std")] +impl std::error::Error for DeviceError {} + impl Error for DeviceError where BUS: Error + Debug, From 8f0e5493e7bffc116674dc03faf827807dbc2b53 Mon Sep 17 00:00:00 2001 From: Jan Niehusmann Date: Tue, 16 Apr 2024 20:33:32 +0000 Subject: [PATCH 157/199] Use rust 1.75 in msrv-1-75 CI job --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 924bbd821..12f48a133 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -56,5 +56,5 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - - uses: dtolnay/rust-toolchain@beta + - uses: dtolnay/rust-toolchain@1.75 - run: cargo test --workspace --all-features From 22a04ceeb2e16342c4c55c2aca285f70115b1c28 Mon Sep 17 00:00:00 2001 From: Jan Niehusmann Date: Tue, 16 Apr 2024 20:36:38 +0000 Subject: [PATCH 158/199] Use stable toolchain for tests --- .github/workflows/test.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 12f48a133..65a228d62 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -15,21 +15,21 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - - uses: dtolnay/rust-toolchain@beta + - uses: dtolnay/rust-toolchain@stable - run: cargo test --workspace test-all-features: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - - uses: dtolnay/rust-toolchain@beta + - uses: dtolnay/rust-toolchain@stable - run: cargo test --workspace --all-features build-nostd: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - - uses: dtolnay/rust-toolchain@beta + - uses: dtolnay/rust-toolchain@stable with: target: thumbv7m-none-eabi - run: > From fdc6f3e71df697292e2504dc7fa063a694d628d0 Mon Sep 17 00:00:00 2001 From: Jan Niehusmann Date: Tue, 16 Apr 2024 18:46:18 +0000 Subject: [PATCH 159/199] Add license file to the top-level directory. These files are referenced from README.md, so they should be present. The same files are already available in the individual crates, so another possibility would be linking to one of the subdirectories. I think it's preferable to have them at the top-level for better visibility. Fixes #588 --- LICENSE-APACHE | 201 +++++++++++++++++++++++++++++++++++++++++++++++++ LICENSE-MIT | 25 ++++++ 2 files changed, 226 insertions(+) create mode 100644 LICENSE-APACHE create mode 100644 LICENSE-MIT diff --git a/LICENSE-APACHE b/LICENSE-APACHE new file mode 100644 index 000000000..16fe87b06 --- /dev/null +++ b/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/LICENSE-MIT b/LICENSE-MIT new file mode 100644 index 000000000..52cb453f2 --- /dev/null +++ b/LICENSE-MIT @@ -0,0 +1,25 @@ +Copyright (c) 2017-2018 Jorge Aparicio + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. From 67eef7fad8994a3d39ae9519a143c92a5b530f75 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 23 Apr 2024 21:22:20 +0200 Subject: [PATCH 160/199] Fix "imported redundantly" warning. --- embedded-hal/src/digital.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embedded-hal/src/digital.rs b/embedded-hal/src/digital.rs index afcf03963..201cd7637 100644 --- a/embedded-hal/src/digital.rs +++ b/embedded-hal/src/digital.rs @@ -1,6 +1,6 @@ //! Digital I/O. -use core::{convert::From, ops::Not}; +use core::ops::Not; #[cfg(feature = "defmt-03")] use crate::defmt; From e00ffa01c269254006aa0770250c033f2e025d51 Mon Sep 17 00:00:00 2001 From: Ryan Summers Date: Thu, 7 Mar 2024 10:37:30 +0100 Subject: [PATCH 161/199] Adding AtomicDevice for I2C bus sharing --- embedded-hal-bus/CHANGELOG.md | 3 +- embedded-hal-bus/Cargo.toml | 1 + embedded-hal-bus/src/i2c/atomic.rs | 170 +++++++++++++++++++++++++++++ embedded-hal-bus/src/i2c/mod.rs | 2 + embedded-hal-bus/src/spi/atomic.rs | 129 ++++++++++++++++++++++ embedded-hal-bus/src/spi/mod.rs | 2 + 6 files changed, 306 insertions(+), 1 deletion(-) create mode 100644 embedded-hal-bus/src/i2c/atomic.rs create mode 100644 embedded-hal-bus/src/spi/atomic.rs diff --git a/embedded-hal-bus/CHANGELOG.md b/embedded-hal-bus/CHANGELOG.md index 04de7aa69..55013fc08 100644 --- a/embedded-hal-bus/CHANGELOG.md +++ b/embedded-hal-bus/CHANGELOG.md @@ -7,7 +7,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] -No unreleased changes +### Added +- Added a new `AtomicDevice` for I2C and SPI to enable bus sharing across multiple contexts. ## [v0.1.0] - 2023-12-28 diff --git a/embedded-hal-bus/Cargo.toml b/embedded-hal-bus/Cargo.toml index 5c89c9704..cec1945a8 100644 --- a/embedded-hal-bus/Cargo.toml +++ b/embedded-hal-bus/Cargo.toml @@ -24,6 +24,7 @@ embedded-hal = { version = "1.0.0", path = "../embedded-hal" } embedded-hal-async = { version = "1.0.0", path = "../embedded-hal-async", optional = true } critical-section = { version = "1.0" } defmt-03 = { package = "defmt", version = "0.3", optional = true } +portable-atomic = {version = "1", default-features = false} [package.metadata.docs.rs] features = ["std", "async"] diff --git a/embedded-hal-bus/src/i2c/atomic.rs b/embedded-hal-bus/src/i2c/atomic.rs new file mode 100644 index 000000000..c6905c886 --- /dev/null +++ b/embedded-hal-bus/src/i2c/atomic.rs @@ -0,0 +1,170 @@ +use core::cell::UnsafeCell; +use embedded_hal::i2c::{Error, ErrorKind, ErrorType, I2c}; + +/// `UnsafeCell`-based shared bus [`I2c`] implementation. +/// +/// Sharing is implemented with a `UnsafeCell`. This means it has low overhead, similar to [`crate::i2c::RefCellDevice`] instances, but they are `Send`. +/// so it only allows sharing across multiple threads (interrupt priority levels). When attempting +/// to preempt usage of the bus, a `AtomicError::Busy` error is returned. +/// +/// This primitive is particularly well-suited for applications that have external arbitration +/// rules, such as the RTIC framework. +/// +/// # Examples +/// +/// Assuming there is a pressure sensor with address `0x42` on the same bus as a temperature sensor +/// with address `0x20`; [`AtomicDevice`] can be used to give access to both of these sensors +/// from a single `i2c` instance. +/// +/// ``` +/// use embedded_hal_bus::i2c; +/// use core::cell::UnsafeCell; +/// # use embedded_hal::i2c::{self as hali2c, SevenBitAddress, TenBitAddress, I2c, Operation, ErrorKind}; +/// # pub struct Sensor { +/// # i2c: I2C, +/// # address: u8, +/// # } +/// # impl Sensor { +/// # pub fn new(i2c: I2C, address: u8) -> Self { +/// # Self { i2c, address } +/// # } +/// # } +/// # type PressureSensor = Sensor; +/// # type TemperatureSensor = Sensor; +/// # pub struct I2c0; +/// # #[derive(Debug, Copy, Clone, Eq, PartialEq)] +/// # pub enum Error { } +/// # impl hali2c::Error for Error { +/// # fn kind(&self) -> hali2c::ErrorKind { +/// # ErrorKind::Other +/// # } +/// # } +/// # impl hali2c::ErrorType for I2c0 { +/// # type Error = Error; +/// # } +/// # impl I2c for I2c0 { +/// # fn transaction(&mut self, address: u8, operations: &mut [Operation<'_>]) -> Result<(), Self::Error> { +/// # Ok(()) +/// # } +/// # } +/// # struct Hal; +/// # impl Hal { +/// # fn i2c(&self) -> I2c0 { +/// # I2c0 +/// # } +/// # } +/// # let hal = Hal; +/// +/// let i2c = hal.i2c(); +/// let i2c_unsafe_cell = UnsafeCell::new(i2c); +/// let mut temperature_sensor = TemperatureSensor::new( +/// i2c::AtomicDevice::new(&i2c_unsafe_cell), +/// 0x20, +/// ); +/// let mut pressure_sensor = PressureSensor::new( +/// i2c::AtomicDevice::new(&i2c_unsafe_cell), +/// 0x42, +/// ); +/// ``` +pub struct AtomicDevice<'a, T> { + bus: &'a UnsafeCell, + busy: portable_atomic::AtomicBool, +} + +#[derive(Debug, Copy, Clone)] +/// Wrapper type for errors originating from the atomically-checked I2C bus manager. +pub enum AtomicError { + /// This error is returned if the I2C bus was already in use when an operation was attempted, + /// which indicates that the driver requirements are not being met with regard to + /// synchronization. + Busy, + + /// An I2C-related error occurred, and the internal error should be inspected. + Other(T), +} + +impl Error for AtomicError { + fn kind(&self) -> ErrorKind { + match self { + AtomicError::Other(e) => e.kind(), + _ => ErrorKind::Other, + } + } +} + +unsafe impl<'a, T> Send for AtomicDevice<'a, T> {} + +impl<'a, T> AtomicDevice<'a, T> +where + T: I2c, +{ + /// Create a new `AtomicDevice`. + #[inline] + pub fn new(bus: &'a UnsafeCell) -> Self { + Self { + bus, + busy: portable_atomic::AtomicBool::from(false), + } + } + + fn lock(&self, f: F) -> Result> + where + F: FnOnce(&mut T) -> Result::Error>, + { + self.busy + .compare_exchange( + false, + true, + core::sync::atomic::Ordering::SeqCst, + core::sync::atomic::Ordering::SeqCst, + ) + .map_err(|_| AtomicError::::Busy)?; + + let result = f(unsafe { &mut *self.bus.get() }); + + self.busy.store(false, core::sync::atomic::Ordering::SeqCst); + + result.map_err(AtomicError::Other) + } +} + +impl<'a, T> ErrorType for AtomicDevice<'a, T> +where + T: I2c, +{ + type Error = AtomicError; +} + +impl<'a, T> I2c for AtomicDevice<'a, T> +where + T: I2c, +{ + #[inline] + fn read(&mut self, address: u8, read: &mut [u8]) -> Result<(), Self::Error> { + self.lock(|bus| bus.read(address, read)) + } + + #[inline] + fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Self::Error> { + self.lock(|bus| bus.write(address, write)) + } + + #[inline] + fn write_read( + &mut self, + address: u8, + write: &[u8], + read: &mut [u8], + ) -> Result<(), Self::Error> { + self.lock(|bus| bus.write_read(address, write, read)) + } + + #[inline] + fn transaction( + &mut self, + address: u8, + operations: &mut [embedded_hal::i2c::Operation<'_>], + ) -> Result<(), Self::Error> { + self.lock(|bus| bus.transaction(address, operations)) + } +} diff --git a/embedded-hal-bus/src/i2c/mod.rs b/embedded-hal-bus/src/i2c/mod.rs index 8eaf2882f..5295492f1 100644 --- a/embedded-hal-bus/src/i2c/mod.rs +++ b/embedded-hal-bus/src/i2c/mod.rs @@ -8,3 +8,5 @@ mod mutex; pub use mutex::*; mod critical_section; pub use self::critical_section::*; +mod atomic; +pub use atomic::*; diff --git a/embedded-hal-bus/src/spi/atomic.rs b/embedded-hal-bus/src/spi/atomic.rs new file mode 100644 index 000000000..c5db18f09 --- /dev/null +++ b/embedded-hal-bus/src/spi/atomic.rs @@ -0,0 +1,129 @@ +use core::cell::UnsafeCell; +use embedded_hal::delay::DelayNs; +use embedded_hal::digital::OutputPin; +use embedded_hal::spi::{Error, ErrorKind, ErrorType, Operation, SpiBus, SpiDevice}; + +use super::DeviceError; +use crate::spi::shared::transaction; + +/// `UnsafeCell`-based shared bus [`SpiDevice`] implementation. +/// +/// This allows for sharing an [`SpiBus`], obtaining multiple [`SpiDevice`] instances, +/// each with its own `CS` pin. +/// +/// Sharing is implemented with a `UnsafeCell`. This means it has low overhead, and, unlike [`crate::spi::RefCellDevice`], instances are `Send`, +/// so it only allows sharing across multiple threads (interrupt priority level). When attempting +/// to preempt usage of the bus, a `AtomicError::Busy` error is returned. +/// +/// This primitive is particularly well-suited for applications that have external arbitration +/// rules, such as the RTIC framework. +/// +pub struct AtomicDevice<'a, BUS, CS, D> { + bus: &'a UnsafeCell, + cs: CS, + delay: D, + busy: portable_atomic::AtomicBool, +} + +#[derive(Debug, Copy, Clone)] +/// Wrapper type for errors originating from the atomically-checked SPI bus manager. +pub enum AtomicError { + /// This error is returned if the SPI bus was already in use when an operation was attempted, + /// which indicates that the driver requirements are not being met with regard to + /// synchronization. + Busy, + + /// An SPI-related error occurred, and the internal error should be inspected. + Other(T), +} + +impl<'a, BUS, CS, D> AtomicDevice<'a, BUS, CS, D> { + /// Create a new [`AtomicDevice`]. + #[inline] + pub fn new(bus: &'a UnsafeCell, cs: CS, delay: D) -> Self { + Self { + bus, + cs, + delay, + busy: portable_atomic::AtomicBool::from(false), + } + } +} + +impl<'a, BUS, CS> AtomicDevice<'a, BUS, CS, super::NoDelay> +where + BUS: ErrorType, + CS: OutputPin, +{ + /// Create a new [`AtomicDevice`] without support for in-transaction delays. + /// + /// **Warning**: The returned instance *technically* doesn't comply with the `SpiDevice` + /// contract, which mandates delay support. It is relatively rare for drivers to use + /// in-transaction delays, so you might still want to use this method because it's more practical. + /// + /// Note that a future version of the driver might start using delays, causing your + /// code to panic. This wouldn't be considered a breaking change from the driver side, because + /// drivers are allowed to assume `SpiDevice` implementations comply with the contract. + /// If you feel this risk outweighs the convenience of having `cargo` automatically upgrade + /// the driver crate, you might want to pin the driver's version. + /// + /// # Panics + /// + /// The returned device will panic if you try to execute a transaction + /// that contains any operations of type [`Operation::DelayNs`]. + #[inline] + pub fn new_no_delay(bus: &'a UnsafeCell, cs: CS) -> Self { + Self { + bus, + cs, + delay: super::NoDelay, + busy: portable_atomic::AtomicBool::from(false), + } + } +} + +unsafe impl<'a, BUS, CS, D> Send for AtomicDevice<'a, BUS, CS, D> {} + +impl Error for AtomicError { + fn kind(&self) -> ErrorKind { + match self { + AtomicError::Other(e) => e.kind(), + _ => ErrorKind::Other, + } + } +} + +impl<'a, BUS, CS, D> ErrorType for AtomicDevice<'a, BUS, CS, D> +where + BUS: ErrorType, + CS: OutputPin, +{ + type Error = AtomicError>; +} + +impl<'a, Word: Copy + 'static, BUS, CS, D> SpiDevice for AtomicDevice<'a, BUS, CS, D> +where + BUS: SpiBus, + CS: OutputPin, + D: DelayNs, +{ + #[inline] + fn transaction(&mut self, operations: &mut [Operation<'_, Word>]) -> Result<(), Self::Error> { + self.busy + .compare_exchange( + false, + true, + core::sync::atomic::Ordering::SeqCst, + core::sync::atomic::Ordering::SeqCst, + ) + .map_err(|_| AtomicError::Busy)?; + + let bus = unsafe { &mut *self.bus.get() }; + + let result = transaction(operations, bus, &mut self.delay, &mut self.cs); + + self.busy.store(false, core::sync::atomic::Ordering::SeqCst); + + result.map_err(AtomicError::Other) + } +} diff --git a/embedded-hal-bus/src/spi/mod.rs b/embedded-hal-bus/src/spi/mod.rs index d408bd92f..d55fa5eaf 100644 --- a/embedded-hal-bus/src/spi/mod.rs +++ b/embedded-hal-bus/src/spi/mod.rs @@ -11,8 +11,10 @@ pub use refcell::*; mod mutex; #[cfg(feature = "std")] pub use mutex::*; +mod atomic; mod critical_section; mod shared; +pub use atomic::*; pub use self::critical_section::*; From 7dbaf6d4799459bed6fece61ef2b0bd167b7c904 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 23 Apr 2024 21:23:26 +0200 Subject: [PATCH 162/199] bus: make AtomicDevice use a per-bus "busy" flag, not per-device. Needed for soundness. --- embedded-hal-bus/src/i2c/atomic.rs | 30 +++++++++++++++--------------- embedded-hal-bus/src/lib.rs | 1 + embedded-hal-bus/src/spi/atomic.rs | 28 +++++++++++----------------- embedded-hal-bus/src/util.rs | 25 +++++++++++++++++++++++++ 4 files changed, 52 insertions(+), 32 deletions(-) create mode 100644 embedded-hal-bus/src/util.rs diff --git a/embedded-hal-bus/src/i2c/atomic.rs b/embedded-hal-bus/src/i2c/atomic.rs index c6905c886..18264a7dd 100644 --- a/embedded-hal-bus/src/i2c/atomic.rs +++ b/embedded-hal-bus/src/i2c/atomic.rs @@ -1,6 +1,7 @@ -use core::cell::UnsafeCell; use embedded_hal::i2c::{Error, ErrorKind, ErrorType, I2c}; +use crate::util::AtomicCell; + /// `UnsafeCell`-based shared bus [`I2c`] implementation. /// /// Sharing is implemented with a `UnsafeCell`. This means it has low overhead, similar to [`crate::i2c::RefCellDevice`] instances, but they are `Send`. @@ -18,7 +19,7 @@ use embedded_hal::i2c::{Error, ErrorKind, ErrorType, I2c}; /// /// ``` /// use embedded_hal_bus::i2c; -/// use core::cell::UnsafeCell; +/// use embedded_hal_bus::util::AtomicCell; /// # use embedded_hal::i2c::{self as hali2c, SevenBitAddress, TenBitAddress, I2c, Operation, ErrorKind}; /// # pub struct Sensor { /// # i2c: I2C, @@ -56,19 +57,18 @@ use embedded_hal::i2c::{Error, ErrorKind, ErrorType, I2c}; /// # let hal = Hal; /// /// let i2c = hal.i2c(); -/// let i2c_unsafe_cell = UnsafeCell::new(i2c); +/// let i2c_cell = AtomicCell::new(i2c); /// let mut temperature_sensor = TemperatureSensor::new( -/// i2c::AtomicDevice::new(&i2c_unsafe_cell), +/// i2c::AtomicDevice::new(&i2c_cell), /// 0x20, /// ); /// let mut pressure_sensor = PressureSensor::new( -/// i2c::AtomicDevice::new(&i2c_unsafe_cell), +/// i2c::AtomicDevice::new(&i2c_cell), /// 0x42, /// ); /// ``` pub struct AtomicDevice<'a, T> { - bus: &'a UnsafeCell, - busy: portable_atomic::AtomicBool, + bus: &'a AtomicCell, } #[derive(Debug, Copy, Clone)] @@ -100,18 +100,16 @@ where { /// Create a new `AtomicDevice`. #[inline] - pub fn new(bus: &'a UnsafeCell) -> Self { - Self { - bus, - busy: portable_atomic::AtomicBool::from(false), - } + pub fn new(bus: &'a AtomicCell) -> Self { + Self { bus } } fn lock(&self, f: F) -> Result> where F: FnOnce(&mut T) -> Result::Error>, { - self.busy + self.bus + .busy .compare_exchange( false, true, @@ -120,9 +118,11 @@ where ) .map_err(|_| AtomicError::::Busy)?; - let result = f(unsafe { &mut *self.bus.get() }); + let result = f(unsafe { &mut *self.bus.bus.get() }); - self.busy.store(false, core::sync::atomic::Ordering::SeqCst); + self.bus + .busy + .store(false, core::sync::atomic::Ordering::SeqCst); result.map_err(AtomicError::Other) } diff --git a/embedded-hal-bus/src/lib.rs b/embedded-hal-bus/src/lib.rs index e7e75ec76..396043a31 100644 --- a/embedded-hal-bus/src/lib.rs +++ b/embedded-hal-bus/src/lib.rs @@ -18,3 +18,4 @@ use defmt_03 as defmt; pub mod i2c; pub mod spi; +pub mod util; diff --git a/embedded-hal-bus/src/spi/atomic.rs b/embedded-hal-bus/src/spi/atomic.rs index c5db18f09..08666dfed 100644 --- a/embedded-hal-bus/src/spi/atomic.rs +++ b/embedded-hal-bus/src/spi/atomic.rs @@ -1,10 +1,10 @@ -use core::cell::UnsafeCell; use embedded_hal::delay::DelayNs; use embedded_hal::digital::OutputPin; use embedded_hal::spi::{Error, ErrorKind, ErrorType, Operation, SpiBus, SpiDevice}; use super::DeviceError; use crate::spi::shared::transaction; +use crate::util::AtomicCell; /// `UnsafeCell`-based shared bus [`SpiDevice`] implementation. /// @@ -19,10 +19,9 @@ use crate::spi::shared::transaction; /// rules, such as the RTIC framework. /// pub struct AtomicDevice<'a, BUS, CS, D> { - bus: &'a UnsafeCell, + bus: &'a AtomicCell, cs: CS, delay: D, - busy: portable_atomic::AtomicBool, } #[derive(Debug, Copy, Clone)] @@ -40,13 +39,8 @@ pub enum AtomicError { impl<'a, BUS, CS, D> AtomicDevice<'a, BUS, CS, D> { /// Create a new [`AtomicDevice`]. #[inline] - pub fn new(bus: &'a UnsafeCell, cs: CS, delay: D) -> Self { - Self { - bus, - cs, - delay, - busy: portable_atomic::AtomicBool::from(false), - } + pub fn new(bus: &'a AtomicCell, cs: CS, delay: D) -> Self { + Self { bus, cs, delay } } } @@ -72,18 +66,15 @@ where /// The returned device will panic if you try to execute a transaction /// that contains any operations of type [`Operation::DelayNs`]. #[inline] - pub fn new_no_delay(bus: &'a UnsafeCell, cs: CS) -> Self { + pub fn new_no_delay(bus: &'a AtomicCell, cs: CS) -> Self { Self { bus, cs, delay: super::NoDelay, - busy: portable_atomic::AtomicBool::from(false), } } } -unsafe impl<'a, BUS, CS, D> Send for AtomicDevice<'a, BUS, CS, D> {} - impl Error for AtomicError { fn kind(&self) -> ErrorKind { match self { @@ -109,7 +100,8 @@ where { #[inline] fn transaction(&mut self, operations: &mut [Operation<'_, Word>]) -> Result<(), Self::Error> { - self.busy + self.bus + .busy .compare_exchange( false, true, @@ -118,11 +110,13 @@ where ) .map_err(|_| AtomicError::Busy)?; - let bus = unsafe { &mut *self.bus.get() }; + let bus = unsafe { &mut *self.bus.bus.get() }; let result = transaction(operations, bus, &mut self.delay, &mut self.cs); - self.busy.store(false, core::sync::atomic::Ordering::SeqCst); + self.bus + .busy + .store(false, core::sync::atomic::Ordering::SeqCst); result.map_err(AtomicError::Other) } diff --git a/embedded-hal-bus/src/util.rs b/embedded-hal-bus/src/util.rs new file mode 100644 index 000000000..739f4b1b3 --- /dev/null +++ b/embedded-hal-bus/src/util.rs @@ -0,0 +1,25 @@ +//! Utilities shared by all bus types. + +use core::cell::UnsafeCell; + +/// Cell type used by [`spi::AtomicDevice`](crate::spi::AtomicDevice) and [`i2c::AtomicDevice`](crate::i2c::AtomicDevice). +/// +/// To use `AtomicDevice`, you must wrap the bus with this struct, and then +/// construct multiple `AtomicDevice` instances with references to it. +pub struct AtomicCell { + pub(crate) bus: UnsafeCell, + pub(crate) busy: portable_atomic::AtomicBool, +} + +unsafe impl Send for AtomicCell {} +unsafe impl Sync for AtomicCell {} + +impl AtomicCell { + /// Create a new `AtomicCell` + pub fn new(bus: BUS) -> Self { + Self { + bus: UnsafeCell::new(bus), + busy: portable_atomic::AtomicBool::from(false), + } + } +} From e4b8c779425f1008f8dd44c5786aee90c5a9e7fb Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 23 Apr 2024 21:40:25 +0200 Subject: [PATCH 163/199] bus: expand docs for AtomicDevice a bit. --- embedded-hal-bus/src/i2c/atomic.rs | 19 ++++++++++++++----- embedded-hal-bus/src/spi/atomic.rs | 22 +++++++++++++++------- 2 files changed, 29 insertions(+), 12 deletions(-) diff --git a/embedded-hal-bus/src/i2c/atomic.rs b/embedded-hal-bus/src/i2c/atomic.rs index 18264a7dd..793e827b1 100644 --- a/embedded-hal-bus/src/i2c/atomic.rs +++ b/embedded-hal-bus/src/i2c/atomic.rs @@ -2,14 +2,23 @@ use embedded_hal::i2c::{Error, ErrorKind, ErrorType, I2c}; use crate::util::AtomicCell; -/// `UnsafeCell`-based shared bus [`I2c`] implementation. +/// Atomics-based shared bus [`I2c`] implementation. /// -/// Sharing is implemented with a `UnsafeCell`. This means it has low overhead, similar to [`crate::i2c::RefCellDevice`] instances, but they are `Send`. -/// so it only allows sharing across multiple threads (interrupt priority levels). When attempting -/// to preempt usage of the bus, a `AtomicError::Busy` error is returned. +/// Sharing is implemented with a [`AtomicDevice`], which consists of an `UnsafeCell` and an `AtomicBool` "locked" flag. +/// This means it has low overhead, like [`RefCellDevice`](crate::i2c::RefCellDevice). Aditionally, it is `Send`, +/// which allows sharing a single bus across multiple threads (interrupt priority level), like [`CriticalSectionDevice`](crate::i2c::CriticalSectionDevice), +/// while not using critical sections and therefore impacting real-time performance less. +/// +/// The downside is using a simple `AtomicBool` for locking doesn't prevent two threads from trying to lock it at once. +/// For example, the main thread can be doing an I2C transaction, and an interrupt fires and tries to do another. In this +/// case, a `Busy` error is returned that must be handled somehow, usually dropping the data or trying again later. +/// +/// Note that retrying in a loop on `Busy` errors usually leads to deadlocks. In the above example, it'll prevent the +/// interrupt handler from returning, which will starve the main thread and prevent it from releasing the lock. If +/// this is an issue for your use case, you most likely should use [`CriticalSectionDevice`](crate::i2c::CriticalSectionDevice) instead. /// /// This primitive is particularly well-suited for applications that have external arbitration -/// rules, such as the RTIC framework. +/// rules that prevent `Busy` errors in the first place, such as the RTIC framework. /// /// # Examples /// diff --git a/embedded-hal-bus/src/spi/atomic.rs b/embedded-hal-bus/src/spi/atomic.rs index 08666dfed..e97ed8ae0 100644 --- a/embedded-hal-bus/src/spi/atomic.rs +++ b/embedded-hal-bus/src/spi/atomic.rs @@ -6,18 +6,26 @@ use super::DeviceError; use crate::spi::shared::transaction; use crate::util::AtomicCell; -/// `UnsafeCell`-based shared bus [`SpiDevice`] implementation. +/// Atomics-based shared bus [`SpiDevice`] implementation. /// /// This allows for sharing an [`SpiBus`], obtaining multiple [`SpiDevice`] instances, /// each with its own `CS` pin. /// -/// Sharing is implemented with a `UnsafeCell`. This means it has low overhead, and, unlike [`crate::spi::RefCellDevice`], instances are `Send`, -/// so it only allows sharing across multiple threads (interrupt priority level). When attempting -/// to preempt usage of the bus, a `AtomicError::Busy` error is returned. +/// Sharing is implemented with a [`AtomicDevice`], which consists of an `UnsafeCell` and an `AtomicBool` "locked" flag. +/// This means it has low overhead, like [`RefCellDevice`](crate::spi::RefCellDevice). Aditionally, it is `Send`, +/// which allows sharing a single bus across multiple threads (interrupt priority level), like [`CriticalSectionDevice`](crate::spi::CriticalSectionDevice), +/// while not using critical sections and therefore impacting real-time performance less. /// -/// This primitive is particularly well-suited for applications that have external arbitration -/// rules, such as the RTIC framework. +/// The downside is using a simple `AtomicBool` for locking doesn't prevent two threads from trying to lock it at once. +/// For example, the main thread can be doing a SPI transaction, and an interrupt fires and tries to do another. In this +/// case, a `Busy` error is returned that must be handled somehow, usually dropping the data or trying again later. +/// +/// Note that retrying in a loop on `Busy` errors usually leads to deadlocks. In the above example, it'll prevent the +/// interrupt handler from returning, which will starve the main thread and prevent it from releasing the lock. If +/// this is an issue for your use case, you most likely should use [`CriticalSectionDevice`](crate::spi::CriticalSectionDevice) instead. /// +/// This primitive is particularly well-suited for applications that have external arbitration +/// rules that prevent `Busy` errors in the first place, such as the RTIC framework. pub struct AtomicDevice<'a, BUS, CS, D> { bus: &'a AtomicCell, cs: CS, @@ -25,7 +33,7 @@ pub struct AtomicDevice<'a, BUS, CS, D> { } #[derive(Debug, Copy, Clone)] -/// Wrapper type for errors originating from the atomically-checked SPI bus manager. +/// Wrapper type for errors returned by [`AtomicDevice`]. pub enum AtomicError { /// This error is returned if the SPI bus was already in use when an operation was attempted, /// which indicates that the driver requirements are not being met with regard to From bae4c27884e99046082d465cf2911e155efe6027 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 23 Apr 2024 20:55:07 +0200 Subject: [PATCH 164/199] bus: make constructors set CS high. This avoids a footgun where a HAL makes OutputPins low by default (or the user sets them to low and doesn't notice). --- embedded-hal-bus/CHANGELOG.md | 2 +- embedded-hal-bus/src/spi/atomic.rs | 24 ++++++++++++++++---- embedded-hal-bus/src/spi/critical_section.rs | 24 ++++++++++++++++---- embedded-hal-bus/src/spi/exclusive.rs | 24 ++++++++++++++++---- embedded-hal-bus/src/spi/mutex.rs | 24 ++++++++++++++++---- embedded-hal-bus/src/spi/refcell.rs | 24 ++++++++++++++++---- 6 files changed, 96 insertions(+), 26 deletions(-) diff --git a/embedded-hal-bus/CHANGELOG.md b/embedded-hal-bus/CHANGELOG.md index 55013fc08..16408934a 100644 --- a/embedded-hal-bus/CHANGELOG.md +++ b/embedded-hal-bus/CHANGELOG.md @@ -7,8 +7,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] -### Added - Added a new `AtomicDevice` for I2C and SPI to enable bus sharing across multiple contexts. +- SPI shared bus constructors now set `CS` high, to prevent sharing issues if it was low. ## [v0.1.0] - 2023-12-28 diff --git a/embedded-hal-bus/src/spi/atomic.rs b/embedded-hal-bus/src/spi/atomic.rs index e97ed8ae0..7d18e28c5 100644 --- a/embedded-hal-bus/src/spi/atomic.rs +++ b/embedded-hal-bus/src/spi/atomic.rs @@ -46,9 +46,16 @@ pub enum AtomicError { impl<'a, BUS, CS, D> AtomicDevice<'a, BUS, CS, D> { /// Create a new [`AtomicDevice`]. + /// + /// This sets the `cs` pin high, and returns an error if that fails. It is recommended + /// to set the pin high the moment it's configured as an output, to avoid glitches. #[inline] - pub fn new(bus: &'a AtomicCell, cs: CS, delay: D) -> Self { - Self { bus, cs, delay } + pub fn new(bus: &'a AtomicCell, mut cs: CS, delay: D) -> Result + where + CS: OutputPin, + { + cs.set_high()?; + Ok(Self { bus, cs, delay }) } } @@ -59,6 +66,9 @@ where { /// Create a new [`AtomicDevice`] without support for in-transaction delays. /// + /// This sets the `cs` pin high, and returns an error if that fails. It is recommended + /// to set the pin high the moment it's configured as an output, to avoid glitches. + /// /// **Warning**: The returned instance *technically* doesn't comply with the `SpiDevice` /// contract, which mandates delay support. It is relatively rare for drivers to use /// in-transaction delays, so you might still want to use this method because it's more practical. @@ -74,12 +84,16 @@ where /// The returned device will panic if you try to execute a transaction /// that contains any operations of type [`Operation::DelayNs`]. #[inline] - pub fn new_no_delay(bus: &'a AtomicCell, cs: CS) -> Self { - Self { + pub fn new_no_delay(bus: &'a AtomicCell, mut cs: CS) -> Result + where + CS: OutputPin, + { + cs.set_high()?; + Ok(Self { bus, cs, delay: super::NoDelay, - } + }) } } diff --git a/embedded-hal-bus/src/spi/critical_section.rs b/embedded-hal-bus/src/spi/critical_section.rs index 838be4f5a..4c3a46eb2 100644 --- a/embedded-hal-bus/src/spi/critical_section.rs +++ b/embedded-hal-bus/src/spi/critical_section.rs @@ -25,15 +25,25 @@ pub struct CriticalSectionDevice<'a, BUS, CS, D> { impl<'a, BUS, CS, D> CriticalSectionDevice<'a, BUS, CS, D> { /// Create a new [`CriticalSectionDevice`]. + /// + /// This sets the `cs` pin high, and returns an error if that fails. It is recommended + /// to set the pin high the moment it's configured as an output, to avoid glitches. #[inline] - pub fn new(bus: &'a Mutex>, cs: CS, delay: D) -> Self { - Self { bus, cs, delay } + pub fn new(bus: &'a Mutex>, mut cs: CS, delay: D) -> Result + where + CS: OutputPin, + { + cs.set_high()?; + Ok(Self { bus, cs, delay }) } } impl<'a, BUS, CS> CriticalSectionDevice<'a, BUS, CS, super::NoDelay> { /// Create a new [`CriticalSectionDevice`] without support for in-transaction delays. /// + /// This sets the `cs` pin high, and returns an error if that fails. It is recommended + /// to set the pin high the moment it's configured as an output, to avoid glitches. + /// /// **Warning**: The returned instance *technically* doesn't comply with the `SpiDevice` /// contract, which mandates delay support. It is relatively rare for drivers to use /// in-transaction delays, so you might still want to use this method because it's more practical. @@ -49,12 +59,16 @@ impl<'a, BUS, CS> CriticalSectionDevice<'a, BUS, CS, super::NoDelay> { /// The returned device will panic if you try to execute a transaction /// that contains any operations of type [`Operation::DelayNs`]. #[inline] - pub fn new_no_delay(bus: &'a Mutex>, cs: CS) -> Self { - Self { + pub fn new_no_delay(bus: &'a Mutex>, mut cs: CS) -> Result + where + CS: OutputPin, + { + cs.set_high()?; + Ok(Self { bus, cs, delay: super::NoDelay, - } + }) } } diff --git a/embedded-hal-bus/src/spi/exclusive.rs b/embedded-hal-bus/src/spi/exclusive.rs index f28a10af4..1599ae7ae 100644 --- a/embedded-hal-bus/src/spi/exclusive.rs +++ b/embedded-hal-bus/src/spi/exclusive.rs @@ -24,9 +24,16 @@ pub struct ExclusiveDevice { impl ExclusiveDevice { /// Create a new [`ExclusiveDevice`]. + /// + /// This sets the `cs` pin high, and returns an error if that fails. It is recommended + /// to set the pin high the moment it's configured as an output, to avoid glitches. #[inline] - pub fn new(bus: BUS, cs: CS, delay: D) -> Self { - Self { bus, cs, delay } + pub fn new(bus: BUS, mut cs: CS, delay: D) -> Result + where + CS: OutputPin, + { + cs.set_high()?; + Ok(Self { bus, cs, delay }) } /// Returns a reference to the underlying bus object. @@ -45,6 +52,9 @@ impl ExclusiveDevice { impl ExclusiveDevice { /// Create a new [`ExclusiveDevice`] without support for in-transaction delays. /// + /// This sets the `cs` pin high, and returns an error if that fails. It is recommended + /// to set the pin high the moment it's configured as an output, to avoid glitches. + /// /// **Warning**: The returned instance *technically* doesn't comply with the `SpiDevice` /// contract, which mandates delay support. It is relatively rare for drivers to use /// in-transaction delays, so you might still want to use this method because it's more practical. @@ -60,12 +70,16 @@ impl ExclusiveDevice { /// The returned device will panic if you try to execute a transaction /// that contains any operations of type [`Operation::DelayNs`]. #[inline] - pub fn new_no_delay(bus: BUS, cs: CS) -> Self { - Self { + pub fn new_no_delay(bus: BUS, mut cs: CS) -> Result + where + CS: OutputPin, + { + cs.set_high()?; + Ok(Self { bus, cs, delay: super::NoDelay, - } + }) } } diff --git a/embedded-hal-bus/src/spi/mutex.rs b/embedded-hal-bus/src/spi/mutex.rs index b06f3fd95..83fe85d88 100644 --- a/embedded-hal-bus/src/spi/mutex.rs +++ b/embedded-hal-bus/src/spi/mutex.rs @@ -23,15 +23,25 @@ pub struct MutexDevice<'a, BUS, CS, D> { impl<'a, BUS, CS, D> MutexDevice<'a, BUS, CS, D> { /// Create a new [`MutexDevice`]. + /// + /// This sets the `cs` pin high, and returns an error if that fails. It is recommended + /// to set the pin high the moment it's configured as an output, to avoid glitches. #[inline] - pub fn new(bus: &'a Mutex, cs: CS, delay: D) -> Self { - Self { bus, cs, delay } + pub fn new(bus: &'a Mutex, mut cs: CS, delay: D) -> Result + where + CS: OutputPin, + { + cs.set_high()?; + Ok(Self { bus, cs, delay }) } } impl<'a, BUS, CS> MutexDevice<'a, BUS, CS, super::NoDelay> { /// Create a new [`MutexDevice`] without support for in-transaction delays. /// + /// This sets the `cs` pin high, and returns an error if that fails. It is recommended + /// to set the pin high the moment it's configured as an output, to avoid glitches. + /// /// **Warning**: The returned instance *technically* doesn't comply with the `SpiDevice` /// contract, which mandates delay support. It is relatively rare for drivers to use /// in-transaction delays, so you might still want to use this method because it's more practical. @@ -47,12 +57,16 @@ impl<'a, BUS, CS> MutexDevice<'a, BUS, CS, super::NoDelay> { /// The returned device will panic if you try to execute a transaction /// that contains any operations of type [`Operation::DelayNs`]. #[inline] - pub fn new_no_delay(bus: &'a Mutex, cs: CS) -> Self { - Self { + pub fn new_no_delay(bus: &'a Mutex, mut cs: CS) -> Result + where + CS: OutputPin, + { + cs.set_high()?; + Ok(Self { bus, cs, delay: super::NoDelay, - } + }) } } diff --git a/embedded-hal-bus/src/spi/refcell.rs b/embedded-hal-bus/src/spi/refcell.rs index 382591c34..35bea03a2 100644 --- a/embedded-hal-bus/src/spi/refcell.rs +++ b/embedded-hal-bus/src/spi/refcell.rs @@ -22,15 +22,25 @@ pub struct RefCellDevice<'a, BUS, CS, D> { impl<'a, BUS, CS, D> RefCellDevice<'a, BUS, CS, D> { /// Create a new [`RefCellDevice`]. + /// + /// This sets the `cs` pin high, and returns an error if that fails. It is recommended + /// to set the pin high the moment it's configured as an output, to avoid glitches. #[inline] - pub fn new(bus: &'a RefCell, cs: CS, delay: D) -> Self { - Self { bus, cs, delay } + pub fn new(bus: &'a RefCell, mut cs: CS, delay: D) -> Result + where + CS: OutputPin, + { + cs.set_high()?; + Ok(Self { bus, cs, delay }) } } impl<'a, BUS, CS> RefCellDevice<'a, BUS, CS, super::NoDelay> { /// Create a new [`RefCellDevice`] without support for in-transaction delays. /// + /// This sets the `cs` pin high, and returns an error if that fails. It is recommended + /// to set the pin high the moment it's configured as an output, to avoid glitches. + /// /// **Warning**: The returned instance *technically* doesn't comply with the `SpiDevice` /// contract, which mandates delay support. It is relatively rare for drivers to use /// in-transaction delays, so you might still want to use this method because it's more practical. @@ -46,12 +56,16 @@ impl<'a, BUS, CS> RefCellDevice<'a, BUS, CS, super::NoDelay> { /// The returned device will panic if you try to execute a transaction /// that contains any operations of type [`Operation::DelayNs`]. #[inline] - pub fn new_no_delay(bus: &'a RefCell, cs: CS) -> Self { - Self { + pub fn new_no_delay(bus: &'a RefCell, mut cs: CS) -> Result + where + CS: OutputPin, + { + cs.set_high()?; + Ok(Self { bus, cs, delay: super::NoDelay, - } + }) } } From add71a3824e4fe6ab7e00249929b22864201ffc1 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 23 Apr 2024 22:14:23 +0200 Subject: [PATCH 165/199] Release embedded-hal-bus v0.2.0 --- embedded-hal-bus/CHANGELOG.md | 7 ++++++- embedded-hal-bus/Cargo.toml | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/embedded-hal-bus/CHANGELOG.md b/embedded-hal-bus/CHANGELOG.md index 16408934a..d92532d3f 100644 --- a/embedded-hal-bus/CHANGELOG.md +++ b/embedded-hal-bus/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] +(Add unreleased changes here) + +## [v0.2.0] - 2024-04-23 + - Added a new `AtomicDevice` for I2C and SPI to enable bus sharing across multiple contexts. - SPI shared bus constructors now set `CS` high, to prevent sharing issues if it was low. @@ -57,7 +61,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/). First release to crates.io -[Unreleased]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-bus-v0.1.0...HEAD +[Unreleased]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-bus-v0.2.0...HEAD +[v0.2.0]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-bus-v0.1.0...embedded-hal-bus-v0.2.0 [v0.1.0]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-bus-v0.1.0-rc.3...embedded-hal-bus-v0.1.0 [v0.1.0-rc.3]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-bus-v0.1.0-rc.2...embedded-hal-bus-v0.1.0-rc.3 [v0.1.0-rc.2]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-bus-v0.1.0-rc.1...embedded-hal-bus-v0.1.0-rc.2 diff --git a/embedded-hal-bus/Cargo.toml b/embedded-hal-bus/Cargo.toml index cec1945a8..2c54bfbd0 100644 --- a/embedded-hal-bus/Cargo.toml +++ b/embedded-hal-bus/Cargo.toml @@ -12,7 +12,7 @@ license = "MIT OR Apache-2.0" name = "embedded-hal-bus" readme = "README.md" repository = "https://github.com/rust-embedded/embedded-hal" -version = "0.1.0" +version = "0.2.0" [features] std = [] From 1c42f823f47893b5d5e55a8f27f9325f43fe94a0 Mon Sep 17 00:00:00 2001 From: Jan Niehusmann Date: Tue, 4 Jun 2024 20:47:24 +0000 Subject: [PATCH 166/199] Update workflow to actions/checkout@v4 This fixes a warning about Node 16 being deprecated --- .github/workflows/clippy.yml | 2 +- .github/workflows/rustdoc.yml | 2 +- .github/workflows/rustfmt.yml | 2 +- .github/workflows/test.yml | 10 +++++----- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/workflows/clippy.yml b/.github/workflows/clippy.yml index dcea25c5e..58f813bee 100644 --- a/.github/workflows/clippy.yml +++ b/.github/workflows/clippy.yml @@ -10,7 +10,7 @@ jobs: clippy: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@master with: # embedded-hal-async needs nightly. diff --git a/.github/workflows/rustdoc.yml b/.github/workflows/rustdoc.yml index c4b46425e..105d4e58c 100644 --- a/.github/workflows/rustdoc.yml +++ b/.github/workflows/rustdoc.yml @@ -10,7 +10,7 @@ jobs: clippy: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@master with: toolchain: nightly-2023-10-14 diff --git a/.github/workflows/rustfmt.yml b/.github/workflows/rustfmt.yml index 3d9036e69..6a3522c47 100644 --- a/.github/workflows/rustfmt.yml +++ b/.github/workflows/rustfmt.yml @@ -11,7 +11,7 @@ jobs: fmt: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@master with: toolchain: nightly diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 65a228d62..46fb03b26 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -14,21 +14,21 @@ jobs: test: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@stable - run: cargo test --workspace test-all-features: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@stable - run: cargo test --workspace --all-features build-nostd: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@stable with: target: thumbv7m-none-eabi @@ -41,7 +41,7 @@ jobs: msrv-1-60: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@1.60 - run: > cargo test @@ -55,6 +55,6 @@ jobs: msrv-1-75: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@1.75 - run: cargo test --workspace --all-features From e229494bbf36173d0abde91055b435a4f2c80329 Mon Sep 17 00:00:00 2001 From: 9names <60134748+9names@users.noreply.github.com> Date: Sat, 8 Jun 2024 21:37:28 +1000 Subject: [PATCH 167/199] Feature gate embedded-hal-bus atomic impls --- embedded-hal-bus/Cargo.toml | 3 ++- embedded-hal-bus/src/i2c/mod.rs | 2 ++ embedded-hal-bus/src/spi/mod.rs | 2 ++ embedded-hal-bus/src/util.rs | 6 +++++- 4 files changed, 11 insertions(+), 2 deletions(-) diff --git a/embedded-hal-bus/Cargo.toml b/embedded-hal-bus/Cargo.toml index 2c54bfbd0..3460062e9 100644 --- a/embedded-hal-bus/Cargo.toml +++ b/embedded-hal-bus/Cargo.toml @@ -16,6 +16,7 @@ version = "0.2.0" [features] std = [] +atomic-device = [] async = ["dep:embedded-hal-async"] defmt-03 = ["dep:defmt-03", "embedded-hal/defmt-03", "embedded-hal-async?/defmt-03"] @@ -27,5 +28,5 @@ defmt-03 = { package = "defmt", version = "0.3", optional = true } portable-atomic = {version = "1", default-features = false} [package.metadata.docs.rs] -features = ["std", "async"] +features = ["std", "async", "atomic-device"] rustdoc-args = ["--cfg", "docsrs"] diff --git a/embedded-hal-bus/src/i2c/mod.rs b/embedded-hal-bus/src/i2c/mod.rs index 5295492f1..5e2c14020 100644 --- a/embedded-hal-bus/src/i2c/mod.rs +++ b/embedded-hal-bus/src/i2c/mod.rs @@ -8,5 +8,7 @@ mod mutex; pub use mutex::*; mod critical_section; pub use self::critical_section::*; +#[cfg(feature = "atomic-device")] mod atomic; +#[cfg(feature = "atomic-device")] pub use atomic::*; diff --git a/embedded-hal-bus/src/spi/mod.rs b/embedded-hal-bus/src/spi/mod.rs index d55fa5eaf..23cd824d4 100644 --- a/embedded-hal-bus/src/spi/mod.rs +++ b/embedded-hal-bus/src/spi/mod.rs @@ -11,9 +11,11 @@ pub use refcell::*; mod mutex; #[cfg(feature = "std")] pub use mutex::*; +#[cfg(feature = "atomic-device")] mod atomic; mod critical_section; mod shared; +#[cfg(feature = "atomic-device")] pub use atomic::*; pub use self::critical_section::*; diff --git a/embedded-hal-bus/src/util.rs b/embedded-hal-bus/src/util.rs index 739f4b1b3..70dac74e2 100644 --- a/embedded-hal-bus/src/util.rs +++ b/embedded-hal-bus/src/util.rs @@ -1,7 +1,9 @@ //! Utilities shared by all bus types. +#[allow(unused_imports)] use core::cell::UnsafeCell; +#[cfg(feature = "atomic-device")] /// Cell type used by [`spi::AtomicDevice`](crate::spi::AtomicDevice) and [`i2c::AtomicDevice`](crate::i2c::AtomicDevice). /// /// To use `AtomicDevice`, you must wrap the bus with this struct, and then @@ -10,10 +12,12 @@ pub struct AtomicCell { pub(crate) bus: UnsafeCell, pub(crate) busy: portable_atomic::AtomicBool, } - +#[cfg(feature = "atomic-device")] unsafe impl Send for AtomicCell {} +#[cfg(feature = "atomic-device")] unsafe impl Sync for AtomicCell {} +#[cfg(feature = "atomic-device")] impl AtomicCell { /// Create a new `AtomicCell` pub fn new(bus: BUS) -> Self { From f0eec219bb1ff9a5642e12b8e92dcc98c4d1640c Mon Sep 17 00:00:00 2001 From: 9names <60134748+9names@users.noreply.github.com> Date: Sat, 8 Jun 2024 21:38:28 +1000 Subject: [PATCH 168/199] Add features for selecting portable-atomic features --- embedded-hal-bus/Cargo.toml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/embedded-hal-bus/Cargo.toml b/embedded-hal-bus/Cargo.toml index 3460062e9..44b9a0287 100644 --- a/embedded-hal-bus/Cargo.toml +++ b/embedded-hal-bus/Cargo.toml @@ -19,6 +19,8 @@ std = [] atomic-device = [] async = ["dep:embedded-hal-async"] defmt-03 = ["dep:defmt-03", "embedded-hal/defmt-03", "embedded-hal-async?/defmt-03"] +portable-atomic-critical-section = ["atomic-device", "portable-atomic/critical-section"] +portable-atomic-unsafe-assume-single-core = ["atomic-device", "portable-atomic/unsafe-assume-single-core"] [dependencies] embedded-hal = { version = "1.0.0", path = "../embedded-hal" } From 797dc9559040df979aab69dc876b1c22f5499fc5 Mon Sep 17 00:00:00 2001 From: 9names <60134748+9names@users.noreply.github.com> Date: Sat, 8 Jun 2024 21:47:41 +1000 Subject: [PATCH 169/199] Document embedded-hal-bus features in README.md and Cargo.toml --- embedded-hal-bus/Cargo.toml | 13 +++++++++++++ embedded-hal-bus/README.md | 14 ++++++++++++-- 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/embedded-hal-bus/Cargo.toml b/embedded-hal-bus/Cargo.toml index 44b9a0287..07bff536f 100644 --- a/embedded-hal-bus/Cargo.toml +++ b/embedded-hal-bus/Cargo.toml @@ -15,11 +15,24 @@ repository = "https://github.com/rust-embedded/embedded-hal" version = "0.2.0" [features] +# Enable shared bus implementations using `std::sync::Mutex`, and implement `std::error::Error` for `DeviceError` std = [] +# Enable shared bus implementations that require Atomic CAS operations atomic-device = [] +# Enable `embedded-hal-async` support. async = ["dep:embedded-hal-async"] +# Derive `defmt::Format` from `defmt` 0.3 for enums and structs. See https://github.com/knurling-rs/defmt for more info defmt-03 = ["dep:defmt-03", "embedded-hal/defmt-03", "embedded-hal-async?/defmt-03"] +# Enable critical-section feature in portable-atomic. +# +# `portable-atomic` emulates atomic CAS functionality, allowing `embedded-hal-bus` to use `atomic-device` on hardware that does not natively support atomic CAS. +# This feature requires a critical-section implementation, which is most often provided by your arch crate (cortex-m / riscv / msp430 / avr-device / etc) when the `critical-section-single-core` feature is enabled. +# A list of critical-section impls is available [in the critical section docs](https://github.com/rust-embedded/critical-section?tab=readme-ov-file#usage-in-no-std-binaries) portable-atomic-critical-section = ["atomic-device", "portable-atomic/critical-section"] +# Enable unsafe-assume-single-core feature of portable-atomic. +# +# `portable-atomic` emulates atomic CAS functionality, allowing `embedded-hal-bus` to use `atomic-device` on hardware that does not natively support atomic CAS. +# This feature is only safe on single core systems portable-atomic-unsafe-assume-single-core = ["atomic-device", "portable-atomic/unsafe-assume-single-core"] [dependencies] diff --git a/embedded-hal-bus/README.md b/embedded-hal-bus/README.md index ce71eec92..4922c7f5f 100644 --- a/embedded-hal-bus/README.md +++ b/embedded-hal-bus/README.md @@ -30,10 +30,20 @@ provides mechanisms to obtain multiple `I2c` instances out of a single `I2c` ins ## Optional Cargo features -- **`std`**: enable shared bus implementations using `std::sync::Mutex`, and implement - `std::error::Error` for `DeviceError`. - **`async`**: enable `embedded-hal-async` support. +- **`atomic-device`**: enable shared bus implementations that require Atomic CAS operations. - **`defmt-03`**: Derive `defmt::Format` from `defmt` 0.3 for enums and structs. +- **`portable-atomic-critical-section`**: Enable critical-section feature in portable-atomic. + + `portable-atomic` emulates atomic CAS functionality, allowing `embedded-hal-bus` to use `atomic-device` on hardware that does not natively support atomic CAS. + This feature requires a critical-section implementation, which is most often provided by your arch crate (cortex-m / riscv / msp430 / avr-device / etc) when the `critical-section-single-core` feature is enabled. + A list of critical-section impls is available [in the critical section docs](https://github.com/rust-embedded/critical-section?tab=readme-ov-file#usage-in-no-std-binaries) +- **`portable-atomic-unsafe-assume-single-core`**: Enable unsafe-assume-single-core feature of portable-atomic. + + `portable-atomic` emulates atomic CAS functionality, allowing `embedded-hal-bus` to use `atomic-device` on hardware that does not natively support atomic CAS. + This feature is only safe on single core systems +- **`std`**: enable shared bus implementations using `std::sync::Mutex`, and implement + `std::error::Error` for `DeviceError`. ## Minimum Supported Rust Version (MSRV) From d346553c0cc8a0ad6fde4551af8d62a733633770 Mon Sep 17 00:00:00 2001 From: 9names <60134748+9names@users.noreply.github.com> Date: Sat, 8 Jun 2024 22:03:05 +1000 Subject: [PATCH 170/199] Also enable Atomic* when target_has_atomic=8 --- embedded-hal-bus/src/i2c/mod.rs | 4 ++-- embedded-hal-bus/src/spi/mod.rs | 4 ++-- embedded-hal-bus/src/util.rs | 8 ++++---- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/embedded-hal-bus/src/i2c/mod.rs b/embedded-hal-bus/src/i2c/mod.rs index 5e2c14020..ef8f7ae9c 100644 --- a/embedded-hal-bus/src/i2c/mod.rs +++ b/embedded-hal-bus/src/i2c/mod.rs @@ -8,7 +8,7 @@ mod mutex; pub use mutex::*; mod critical_section; pub use self::critical_section::*; -#[cfg(feature = "atomic-device")] +#[cfg(any(feature = "atomic-device", target_has_atomic = "8"))] mod atomic; -#[cfg(feature = "atomic-device")] +#[cfg(any(feature = "atomic-device", target_has_atomic = "8"))] pub use atomic::*; diff --git a/embedded-hal-bus/src/spi/mod.rs b/embedded-hal-bus/src/spi/mod.rs index 23cd824d4..a7ad6b989 100644 --- a/embedded-hal-bus/src/spi/mod.rs +++ b/embedded-hal-bus/src/spi/mod.rs @@ -11,11 +11,11 @@ pub use refcell::*; mod mutex; #[cfg(feature = "std")] pub use mutex::*; -#[cfg(feature = "atomic-device")] +#[cfg(any(feature = "atomic-device", target_has_atomic = "8"))] mod atomic; mod critical_section; mod shared; -#[cfg(feature = "atomic-device")] +#[cfg(any(feature = "atomic-device", target_has_atomic = "8"))] pub use atomic::*; pub use self::critical_section::*; diff --git a/embedded-hal-bus/src/util.rs b/embedded-hal-bus/src/util.rs index 70dac74e2..f0cb8f16d 100644 --- a/embedded-hal-bus/src/util.rs +++ b/embedded-hal-bus/src/util.rs @@ -3,7 +3,7 @@ #[allow(unused_imports)] use core::cell::UnsafeCell; -#[cfg(feature = "atomic-device")] +#[cfg(any(feature = "atomic-device", target_has_atomic = "8"))] /// Cell type used by [`spi::AtomicDevice`](crate::spi::AtomicDevice) and [`i2c::AtomicDevice`](crate::i2c::AtomicDevice). /// /// To use `AtomicDevice`, you must wrap the bus with this struct, and then @@ -12,12 +12,12 @@ pub struct AtomicCell { pub(crate) bus: UnsafeCell, pub(crate) busy: portable_atomic::AtomicBool, } -#[cfg(feature = "atomic-device")] +#[cfg(any(feature = "atomic-device", target_has_atomic = "8"))] unsafe impl Send for AtomicCell {} -#[cfg(feature = "atomic-device")] +#[cfg(any(feature = "atomic-device", target_has_atomic = "8"))] unsafe impl Sync for AtomicCell {} -#[cfg(feature = "atomic-device")] +#[cfg(any(feature = "atomic-device", target_has_atomic = "8"))] impl AtomicCell { /// Create a new `AtomicCell` pub fn new(bus: BUS) -> Self { From 3c41607d948c2b2e6b52ef314f954e945f63d330 Mon Sep 17 00:00:00 2001 From: Liam Kinne Date: Thu, 4 Jul 2024 20:27:05 +1000 Subject: [PATCH 171/199] add feature and Format derive --- embedded-can/Cargo.toml | 4 ++++ embedded-can/README.md | 4 ++++ embedded-can/src/id.rs | 3 +++ embedded-can/src/lib.rs | 1 + 4 files changed, 12 insertions(+) diff --git a/embedded-can/Cargo.toml b/embedded-can/Cargo.toml index 6279fa80d..07ef7cc53 100644 --- a/embedded-can/Cargo.toml +++ b/embedded-can/Cargo.toml @@ -14,3 +14,7 @@ repository = "https://github.com/rust-embedded/embedded-hal" [dependencies] nb = "1" +defmt = { version = "0.3", optional = true } + +[features] +defmt-03 = ["dep:defmt"] diff --git a/embedded-can/README.md b/embedded-can/README.md index d0b7d05d9..1beaf39cd 100644 --- a/embedded-can/README.md +++ b/embedded-can/README.md @@ -13,6 +13,10 @@ This project is developed and maintained by the [HAL team](https://github.com/ru [API reference]: https://docs.rs/embedded-can +## Optional features + +- **`defmt-03`**: Derive `defmt::Format` from `defmt` 0.3 for enums and structs. + ## Minimum Supported Rust Version (MSRV) This crate is guaranteed to compile on stable Rust 1.60 and up. It *might* diff --git a/embedded-can/src/id.rs b/embedded-can/src/id.rs index 2774b6c3d..b0d6d4d5f 100644 --- a/embedded-can/src/id.rs +++ b/embedded-can/src/id.rs @@ -2,6 +2,7 @@ /// Standard 11-bit CAN Identifier (`0..=0x7FF`). #[derive(Debug, Copy, Clone, Eq, PartialEq, PartialOrd, Ord, Hash)] +#[cfg_attr(feature = "defmt-03", derive(defmt::Format))] pub struct StandardId(u16); impl StandardId { @@ -44,6 +45,7 @@ impl StandardId { /// Extended 29-bit CAN Identifier (`0..=1FFF_FFFF`). #[derive(Debug, Copy, Clone, Eq, PartialEq, PartialOrd, Ord, Hash)] +#[cfg_attr(feature = "defmt-03", derive(defmt::Format))] pub struct ExtendedId(u32); impl ExtendedId { @@ -93,6 +95,7 @@ impl ExtendedId { /// A CAN Identifier (standard or extended). #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] +#[cfg_attr(feature = "defmt-03", derive(defmt::Format))] pub enum Id { /// Standard 11-bit Identifier (`0..=0x7FF`). Standard(StandardId), diff --git a/embedded-can/src/lib.rs b/embedded-can/src/lib.rs index 2de2b6ebc..da561f58c 100644 --- a/embedded-can/src/lib.rs +++ b/embedded-can/src/lib.rs @@ -73,6 +73,7 @@ impl Error for core::convert::Infallible { /// free to define more specific or additional error types. However, by providing /// a mapping to these common CAN errors, generic code can still react to them. #[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] +#[cfg_attr(feature = "defmt-03", derive(defmt::Format))] #[non_exhaustive] pub enum ErrorKind { /// The peripheral receive buffer was overrun. From 86c39b3b4663c401a051a115ddeebaa121de0647 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20J=C3=B6rdens?= Date: Fri, 12 Jul 2024 15:06:34 +0200 Subject: [PATCH 172/199] e-io-adapters: add ToFmt: impl fmt::Write --- embedded-io-adapters/CHANGELOG.md | 2 +- embedded-io-adapters/src/fmt.rs | 42 +++++++++++++++++++++++++++++++ embedded-io-adapters/src/lib.rs | 2 ++ 3 files changed, 45 insertions(+), 1 deletion(-) create mode 100644 embedded-io-adapters/src/fmt.rs diff --git a/embedded-io-adapters/CHANGELOG.md b/embedded-io-adapters/CHANGELOG.md index b540b67b4..f427e0aa1 100644 --- a/embedded-io-adapters/CHANGELOG.md +++ b/embedded-io-adapters/CHANGELOG.md @@ -7,7 +7,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased -Add unreleased changes here +- Added `ToFmt` adapter for `core::fmt::Write`. ## 0.6.1 - 2023-11-28 diff --git a/embedded-io-adapters/src/fmt.rs b/embedded-io-adapters/src/fmt.rs new file mode 100644 index 000000000..7a057668d --- /dev/null +++ b/embedded-io-adapters/src/fmt.rs @@ -0,0 +1,42 @@ +//! Adapters to the `core::fmt::Write`. + +/// Adapter to the `core::fmt::Write` trait. +#[derive(Clone, Default, PartialEq, Debug)] +pub struct ToFmt { + inner: T, +} + +impl ToFmt { + /// Create a new adapter. + pub fn new(inner: T) -> Self { + Self { inner } + } + + /// Consume the adapter, returning the inner object. + pub fn into_inner(self) -> T { + self.inner + } +} + +impl ToFmt { + /// Borrow the inner object. + pub fn inner(&self) -> &T { + &self.inner + } + + /// Mutably borrow the inner object. + pub fn inner_mut(&mut self) -> &mut T { + &mut self.inner + } +} + +impl core::fmt::Write for ToFmt { + fn write_str(&mut self, s: &str) -> core::fmt::Result { + self.inner.write_all(s.as_bytes()).or(Err(core::fmt::Error)) + } + + // Use fmt::Write default impls for + // * write_fmt(): better here than e-io::Write::write_fmt + // since we don't need to bother with saving the Error + // * write_char(): would be the same +} diff --git a/embedded-io-adapters/src/lib.rs b/embedded-io-adapters/src/lib.rs index f9d0f1a5c..936546e66 100644 --- a/embedded-io-adapters/src/lib.rs +++ b/embedded-io-adapters/src/lib.rs @@ -15,6 +15,8 @@ feature(async_fn_in_trait, impl_trait_projections) )] +pub mod fmt; + #[cfg(feature = "std")] #[cfg_attr(docsrs, doc(cfg(feature = "std")))] pub mod std; From a9c1ebd2f4f393fca2ebaea356bdae9dcdd8b2ea Mon Sep 17 00:00:00 2001 From: James Munns Date: Sat, 13 Jul 2024 19:21:19 +0200 Subject: [PATCH 173/199] Update minimum `embedded-io` version The 0.6.0 version of `embedded-io` does not have `SliceWriteError` in the public API, leading to this error if the 0.6.0 version is selected: ``` error[E0432]: unresolved import `embedded_io::SliceWriteError` --> /Users/james/.cargo/registry/src/index.crates.io-6f17d22bba15001f/embedded-io-async-0.6.1/src/impls/slice_mut.rs:2:5 | 2 | use embedded_io::SliceWriteError; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ no `SliceWriteError` in the root For more information about this error, try `rustc --explain E0432`. error: could not compile `embedded-io-async` (lib) due to 1 previous error warning: build failed, waiting for other jobs to finish... ``` This means that embedded-io-async requires *at least* embedded-io 0.6. --- embedded-io-async/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embedded-io-async/Cargo.toml b/embedded-io-async/Cargo.toml index 6cd60c9c7..1cf2e0aad 100644 --- a/embedded-io-async/Cargo.toml +++ b/embedded-io-async/Cargo.toml @@ -18,7 +18,7 @@ alloc = ["embedded-io/alloc"] defmt-03 = ["dep:defmt-03", "embedded-io/defmt-03"] [dependencies] -embedded-io = { version = "0.6", path = "../embedded-io" } +embedded-io = { version = "0.6.1", path = "../embedded-io" } defmt-03 = { package = "defmt", version = "0.3", optional = true } [package.metadata.docs.rs] From a5f6aada222531c842d70e6358ee831861d86148 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Fri, 26 Jul 2024 11:57:04 +0200 Subject: [PATCH 174/199] Remove nightly checks. Async fn in traits has been stable for 7 months, it's very unlikely someone is using such old nightlies. Fixes #618 --- embedded-hal-async/build.rs | 18 ------------------ embedded-hal-async/src/lib.rs | 6 ------ embedded-hal-bus/build.rs | 18 ------------------ embedded-hal-bus/src/lib.rs | 9 --------- embedded-io-adapters/build.rs | 18 ------------------ embedded-io-adapters/src/lib.rs | 12 ------------ embedded-io-async/build.rs | 18 ------------------ embedded-io-async/src/lib.rs | 6 ------ 8 files changed, 105 deletions(-) delete mode 100644 embedded-hal-async/build.rs delete mode 100644 embedded-hal-bus/build.rs delete mode 100644 embedded-io-adapters/build.rs delete mode 100644 embedded-io-async/build.rs diff --git a/embedded-hal-async/build.rs b/embedded-hal-async/build.rs deleted file mode 100644 index 78bd27ec7..000000000 --- a/embedded-hal-async/build.rs +++ /dev/null @@ -1,18 +0,0 @@ -use std::env; -use std::ffi::OsString; -use std::process::Command; - -fn main() { - println!("cargo:rerun-if-changed=build.rs"); - - let rustc = env::var_os("RUSTC").unwrap_or_else(|| OsString::from("rustc")); - - let output = Command::new(rustc) - .arg("--version") - .output() - .expect("failed to run `rustc --version`"); - - if String::from_utf8_lossy(&output.stdout).contains("nightly") { - println!("cargo:rustc-cfg=nightly"); - } -} diff --git a/embedded-hal-async/src/lib.rs b/embedded-hal-async/src/lib.rs index 44901deca..cbd74f5d4 100644 --- a/embedded-hal-async/src/lib.rs +++ b/embedded-hal-async/src/lib.rs @@ -1,12 +1,6 @@ #![doc = include_str!("../README.md")] #![warn(missing_docs)] #![no_std] -// disable warning for already-stabilized features. -// Needed to pass CI, because we deny warnings. -// We don't immediately remove them to not immediately break older nightlies. -// When all features are stable, we'll remove them. -#![cfg_attr(nightly, allow(stable_features, unknown_lints))] -#![cfg_attr(nightly, feature(async_fn_in_trait, impl_trait_projections))] #![allow(async_fn_in_trait)] pub mod delay; diff --git a/embedded-hal-bus/build.rs b/embedded-hal-bus/build.rs deleted file mode 100644 index 78bd27ec7..000000000 --- a/embedded-hal-bus/build.rs +++ /dev/null @@ -1,18 +0,0 @@ -use std::env; -use std::ffi::OsString; -use std::process::Command; - -fn main() { - println!("cargo:rerun-if-changed=build.rs"); - - let rustc = env::var_os("RUSTC").unwrap_or_else(|| OsString::from("rustc")); - - let output = Command::new(rustc) - .arg("--version") - .output() - .expect("failed to run `rustc --version`"); - - if String::from_utf8_lossy(&output.stdout).contains("nightly") { - println!("cargo:rustc-cfg=nightly"); - } -} diff --git a/embedded-hal-bus/src/lib.rs b/embedded-hal-bus/src/lib.rs index 396043a31..dfeca16e1 100644 --- a/embedded-hal-bus/src/lib.rs +++ b/embedded-hal-bus/src/lib.rs @@ -2,15 +2,6 @@ #![warn(missing_docs)] #![cfg_attr(not(feature = "std"), no_std)] #![cfg_attr(docsrs, feature(doc_cfg))] -// disable warning for already-stabilized features. -// Needed to pass CI, because we deny warnings. -// We don't immediately remove them to not immediately break older nightlies. -// When all features are stable, we'll remove them. -#![cfg_attr(all(feature = "async", nightly), allow(stable_features))] -#![cfg_attr( - all(feature = "async", nightly), - feature(async_fn_in_trait, impl_trait_projections) -)] // needed to prevent defmt macros from breaking, since they emit code that does `defmt::blahblah`. #[cfg(feature = "defmt-03")] diff --git a/embedded-io-adapters/build.rs b/embedded-io-adapters/build.rs deleted file mode 100644 index 78bd27ec7..000000000 --- a/embedded-io-adapters/build.rs +++ /dev/null @@ -1,18 +0,0 @@ -use std::env; -use std::ffi::OsString; -use std::process::Command; - -fn main() { - println!("cargo:rerun-if-changed=build.rs"); - - let rustc = env::var_os("RUSTC").unwrap_or_else(|| OsString::from("rustc")); - - let output = Command::new(rustc) - .arg("--version") - .output() - .expect("failed to run `rustc --version`"); - - if String::from_utf8_lossy(&output.stdout).contains("nightly") { - println!("cargo:rustc-cfg=nightly"); - } -} diff --git a/embedded-io-adapters/src/lib.rs b/embedded-io-adapters/src/lib.rs index f9d0f1a5c..9d6628836 100644 --- a/embedded-io-adapters/src/lib.rs +++ b/embedded-io-adapters/src/lib.rs @@ -2,18 +2,6 @@ #![cfg_attr(docsrs, feature(doc_cfg))] #![warn(missing_docs)] #![doc = include_str!("../README.md")] -// disable warning for already-stabilized features. -// Needed to pass CI, because we deny warnings. -// We don't immediately remove them to not immediately break older nightlies. -// When all features are stable, we'll remove them. -#![cfg_attr( - all(any(feature = "tokio-1", feature = "futures-03"), nightly), - allow(stable_features) -)] -#![cfg_attr( - all(any(feature = "tokio-1", feature = "futures-03"), nightly), - feature(async_fn_in_trait, impl_trait_projections) -)] #[cfg(feature = "std")] #[cfg_attr(docsrs, doc(cfg(feature = "std")))] diff --git a/embedded-io-async/build.rs b/embedded-io-async/build.rs deleted file mode 100644 index 78bd27ec7..000000000 --- a/embedded-io-async/build.rs +++ /dev/null @@ -1,18 +0,0 @@ -use std::env; -use std::ffi::OsString; -use std::process::Command; - -fn main() { - println!("cargo:rerun-if-changed=build.rs"); - - let rustc = env::var_os("RUSTC").unwrap_or_else(|| OsString::from("rustc")); - - let output = Command::new(rustc) - .arg("--version") - .output() - .expect("failed to run `rustc --version`"); - - if String::from_utf8_lossy(&output.stdout).contains("nightly") { - println!("cargo:rustc-cfg=nightly"); - } -} diff --git a/embedded-io-async/src/lib.rs b/embedded-io-async/src/lib.rs index 1171d42a0..7c0ac2d55 100644 --- a/embedded-io-async/src/lib.rs +++ b/embedded-io-async/src/lib.rs @@ -2,12 +2,6 @@ #![cfg_attr(docsrs, feature(doc_cfg))] #![warn(missing_docs)] #![doc = include_str!("../README.md")] -// disable warning for already-stabilized features. -// Needed to pass CI, because we deny warnings. -// We don't immediately remove them to not immediately break older nightlies. -// When all features are stable, we'll remove them. -#![cfg_attr(nightly, allow(stable_features, unknown_lints))] -#![cfg_attr(nightly, feature(async_fn_in_trait, impl_trait_projections))] #![allow(async_fn_in_trait)] #[cfg(feature = "alloc")] From 91e254d97c7d45b180b52fc57c254b862085fe6e Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Fri, 26 Jul 2024 12:02:24 +0200 Subject: [PATCH 175/199] Use stable in CI for clippy, rustfmt. Update nightly for rustdoc. --- .github/workflows/clippy.yml | 5 +---- .github/workflows/rustdoc.yml | 2 +- .github/workflows/rustfmt.yml | 3 +-- 3 files changed, 3 insertions(+), 7 deletions(-) diff --git a/.github/workflows/clippy.yml b/.github/workflows/clippy.yml index 58f813bee..422eab62f 100644 --- a/.github/workflows/clippy.yml +++ b/.github/workflows/clippy.yml @@ -11,10 +11,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - uses: dtolnay/rust-toolchain@master + - uses: dtolnay/rust-toolchain@stable with: - # embedded-hal-async needs nightly. - # Use a pinned version to avoid spontaneous breakages (new clippy lints are added often) - toolchain: nightly-2023-10-14 components: clippy - run: cargo clippy --all-features -- --deny=warnings diff --git a/.github/workflows/rustdoc.yml b/.github/workflows/rustdoc.yml index 105d4e58c..7829e62b6 100644 --- a/.github/workflows/rustdoc.yml +++ b/.github/workflows/rustdoc.yml @@ -13,6 +13,6 @@ jobs: - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@master with: - toolchain: nightly-2023-10-14 + toolchain: nightly-2024-07-26 # tokio/net required to workaround https://github.com/tokio-rs/tokio/issues/6165 - run: RUSTDOCFLAGS="--deny=warnings --cfg=docsrs" cargo doc --all-features --features tokio/net diff --git a/.github/workflows/rustfmt.yml b/.github/workflows/rustfmt.yml index 6a3522c47..129eb2285 100644 --- a/.github/workflows/rustfmt.yml +++ b/.github/workflows/rustfmt.yml @@ -12,8 +12,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - uses: dtolnay/rust-toolchain@master + - uses: dtolnay/rust-toolchain@stable with: - toolchain: nightly components: rustfmt - run: cargo fmt --check From f593acb2b0d1d57ca983a50053e6d725e8fb7f21 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Fri, 26 Jul 2024 12:07:55 +0200 Subject: [PATCH 176/199] Fixes for 1.80 clippy. --- embedded-hal-nb/src/spi.rs | 6 ++--- embedded-hal/README.md | 34 +++++++++++++------------- embedded-io-adapters/src/futures_03.rs | 5 ++++ embedded-io-adapters/src/tokio_1.rs | 5 ++++ embedded-io/README.md | 4 +-- 5 files changed, 32 insertions(+), 22 deletions(-) diff --git a/embedded-hal-nb/src/spi.rs b/embedded-hal-nb/src/spi.rs index 06689db4c..75553c78b 100644 --- a/embedded-hal-nb/src/spi.rs +++ b/embedded-hal-nb/src/spi.rs @@ -13,13 +13,13 @@ pub use embedded_hal::spi::{ /// - 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 +/// 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. +/// 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. +/// `Word` types to allow operation in both modes. pub trait FullDuplex: ErrorType { /// Reads the word stored in the shift register /// diff --git a/embedded-hal/README.md b/embedded-hal/README.md index 79667661e..e47182b71 100644 --- a/embedded-hal/README.md +++ b/embedded-hal/README.md @@ -37,37 +37,37 @@ or a console to operate either on hardware serial ports or on virtual ones like The HAL - Must *erase* device specific details. Neither register, register blocks, nor magic values should -appear in the API. + appear in the API. - Must be generic *within* a device and *across* devices. The API to use a serial interface must -be the same regardless of whether the implementation uses the USART1 or UART4 peripheral of a -device or the UART0 peripheral of another device. + be the same regardless of whether the implementation uses the USART1 or UART4 peripheral of a + device or the UART0 peripheral of another device. - Where possible must *not* be tied to a specific asynchronous model. The API should be usable -in blocking mode, with the `futures` model, with an async/await model or with a callback model. -(cf. the [`nb`](https://docs.rs/nb) crate) + in blocking mode, with the `futures` model, with an async/await model or with a callback model. + (cf. the [`nb`](https://docs.rs/nb) crate) - Must be minimal, and thus easy to implement and zero cost, yet highly composable. People that -want higher level abstraction should *prefer to use this HAL* rather than *re-implement* -register manipulation code. + want higher level abstraction should *prefer to use this HAL* rather than *re-implement* + register manipulation code. - Serve as a foundation for building an ecosystem of platform-agnostic drivers. Here driver -means a library crate that lets a target platform interface an external device like a digital -sensor or a wireless transceiver. The advantage of this system is that by writing the driver as -a generic library on top of `embedded-hal` driver authors can support any number of target -platforms (e.g. Cortex-M microcontrollers, AVR microcontrollers, embedded Linux, etc.). The -advantage for application developers is that by adopting `embedded-hal` they can unlock all -these drivers for their platform. + means a library crate that lets a target platform interface an external device like a digital + sensor or a wireless transceiver. The advantage of this system is that by writing the driver as + a generic library on top of `embedded-hal` driver authors can support any number of target + platforms (e.g. Cortex-M microcontrollers, AVR microcontrollers, embedded Linux, etc.). The + advantage for application developers is that by adopting `embedded-hal` they can unlock all + these drivers for their platform. - Trait methods must be fallible so that they can be used in any possible situation. -Nevertheless, HAL implementations can additionally provide infallible versions of the same methods -if they can never fail in their platform. This way, generic code can use the fallible abstractions -provided here but platform-specific code can avoid fallibility-related boilerplate if possible. + Nevertheless, HAL implementations can additionally provide infallible versions of the same methods + if they can never fail in their platform. This way, generic code can use the fallible abstractions + provided here but platform-specific code can avoid fallibility-related boilerplate if possible. ## Out of scope - Initialization and configuration stuff like "ensure this serial interface and that SPI -interface are not using the same pins". The HAL will focus on *doing I/O*. + interface are not using the same pins". The HAL will focus on *doing I/O*. ## Platform agnostic drivers diff --git a/embedded-io-adapters/src/futures_03.rs b/embedded-io-adapters/src/futures_03.rs index a40370a24..d1127a8c6 100644 --- a/embedded-io-adapters/src/futures_03.rs +++ b/embedded-io-adapters/src/futures_03.rs @@ -1,5 +1,10 @@ //! Adapters to/from `futures::io` traits. +// MSRV is 1.60 if you don't enable async, 1.80 if you do. +// Cargo.toml has 1.60, which makes Clippy complain that `poll_fn` was introduced +// in 1.64. So, just silence it for this file. +#![allow(clippy::incompatible_msrv)] + use core::future::poll_fn; use core::pin::Pin; diff --git a/embedded-io-adapters/src/tokio_1.rs b/embedded-io-adapters/src/tokio_1.rs index 418284b93..0dd4d265b 100644 --- a/embedded-io-adapters/src/tokio_1.rs +++ b/embedded-io-adapters/src/tokio_1.rs @@ -1,5 +1,10 @@ //! Adapters to/from `tokio::io` traits. +// MSRV is 1.60 if you don't enable async, 1.80 if you do. +// Cargo.toml has 1.60, which makes Clippy complain that `poll_fn` was introduced +// in 1.64. So, just silence it for this file. +#![allow(clippy::incompatible_msrv)] + use core::future::poll_fn; use core::pin::Pin; use core::task::Poll; diff --git a/embedded-io/README.md b/embedded-io/README.md index d647ffe9d..04ea97771 100644 --- a/embedded-io/README.md +++ b/embedded-io/README.md @@ -15,9 +15,9 @@ targets. ## Differences with `std::io` - `Error` is an associated type. This allows each implementor to return its own error type, -while avoiding `dyn` or `Box`. This is consistent with how errors are handled in [`embedded-hal`](https://github.com/rust-embedded/embedded-hal/). + while avoiding `dyn` or `Box`. This is consistent with how errors are handled in [`embedded-hal`](https://github.com/rust-embedded/embedded-hal/). - In `std::io`, the `Read`/`Write` traits might be blocking or non-blocking (i.e. returning `WouldBlock` errors) depending on the file descriptor's mode, which is only known at run-time. This allows passing a non-blocking stream to code that expects a blocking -stream, causing unexpected errors. To solve this, `embedded-io` specifies `Read`/`Write` are always blocking, and adds new `ReadReady`/`WriteReady` traits to allow using streams in a non-blocking way. + stream, causing unexpected errors. To solve this, `embedded-io` specifies `Read`/`Write` are always blocking, and adds new `ReadReady`/`WriteReady` traits to allow using streams in a non-blocking way. ## Optional Cargo features From be43383966f562ef42cc9de3126f90f7a51fff68 Mon Sep 17 00:00:00 2001 From: Emilie Burgun Date: Fri, 5 Jul 2024 15:46:51 +0200 Subject: [PATCH 177/199] Add a Rc>-based implementation of SpiDevice --- embedded-hal-bus/Cargo.toml | 4 +- embedded-hal-bus/src/spi/mod.rs | 5 ++ embedded-hal-bus/src/spi/rc.rs | 90 +++++++++++++++++++++++++++++++++ 3 files changed, 98 insertions(+), 1 deletion(-) create mode 100644 embedded-hal-bus/src/spi/rc.rs diff --git a/embedded-hal-bus/Cargo.toml b/embedded-hal-bus/Cargo.toml index 2c54bfbd0..6ab831fbe 100644 --- a/embedded-hal-bus/Cargo.toml +++ b/embedded-hal-bus/Cargo.toml @@ -15,9 +15,11 @@ repository = "https://github.com/rust-embedded/embedded-hal" version = "0.2.0" [features] -std = [] +std = ["alloc"] async = ["dep:embedded-hal-async"] defmt-03 = ["dep:defmt-03", "embedded-hal/defmt-03", "embedded-hal-async?/defmt-03"] +# Enables additional utilities requiring a global allocator. +alloc = [] [dependencies] embedded-hal = { version = "1.0.0", path = "../embedded-hal" } diff --git a/embedded-hal-bus/src/spi/mod.rs b/embedded-hal-bus/src/spi/mod.rs index d55fa5eaf..b654a54c6 100644 --- a/embedded-hal-bus/src/spi/mod.rs +++ b/embedded-hal-bus/src/spi/mod.rs @@ -16,6 +16,11 @@ mod critical_section; mod shared; pub use atomic::*; +#[cfg(feature = "alloc")] +mod rc; +#[cfg(feature = "alloc")] +pub use rc::*; + pub use self::critical_section::*; #[cfg(feature = "defmt-03")] diff --git a/embedded-hal-bus/src/spi/rc.rs b/embedded-hal-bus/src/spi/rc.rs new file mode 100644 index 000000000..1d58db37d --- /dev/null +++ b/embedded-hal-bus/src/spi/rc.rs @@ -0,0 +1,90 @@ +extern crate alloc; +use alloc::rc::Rc; + +use core::cell::RefCell; +use embedded_hal::delay::DelayNs; +use embedded_hal::digital::OutputPin; +use embedded_hal::spi::{ErrorType, Operation, SpiBus, SpiDevice}; + +use super::DeviceError; +use crate::spi::shared::transaction; + +/// Implementation of [`SpiDevice`] around a bus shared with `Rc>`. +/// This is the reference-counting equivalent of [`RefCellDevice`](super::RefCellDevice), requiring allocation. +/// +/// A single [`SpiBus`] is shared via [`RefCell`], and its ownership is handled by [`Rc`]. +/// Both of these mechanisms only allow sharing within a single thread (or interrupt priority level). +/// For this reason, this does not implement [`Send`]. +/// +/// When this structure is dropped, the reference count of the `Bus` will be decremented, +/// and the bus driver will be cleaned up when that count reaches zero. +#[cfg_attr(docsrs, doc(cfg(any(feature = "std", feature = "alloc"))))] +pub struct RcDevice { + bus: Rc>, + cs: Cs, + delay: Delay, +} + +impl RcDevice { + /// Creates a new [`RcDevice`]. + /// + /// This sets the `cs` pin high, and returns an error if that fails. + /// It is recommended to have already set that pin high the moment it has been configured as an output, to avoid glitches. + /// + /// This function does not increment the reference count: + /// you will need to call `Rc::clone(&bus)` if you only have a `&RefCell`. + #[inline] + pub fn new(bus: Rc>, mut cs: Cs, delay: Delay) -> Result + where + Cs: OutputPin, + { + cs.set_high()?; + + Ok(Self { bus, cs, delay }) + } +} + +impl RcDevice { + /// Creates a new [`RcDevice`] without support for in-transaction delays. + /// + /// **Warning**: It's advised to prefer [`RcDevice::new`], + /// as the contract of [`SpiDevice`] requests support for in-transaction delays. + /// + /// Refer to [`RefCellDevice::new_no_delay`](super::RefCellDevice::new_no_delay) for more information. + #[inline] + pub fn new_no_delay(bus: Rc>, mut cs: Cs) -> Result + where + Cs: OutputPin, + { + cs.set_high()?; + + Ok(Self { + bus, + cs, + delay: super::NoDelay, + }) + } +} + +impl ErrorType for RcDevice +where + Bus: ErrorType, + Cs: OutputPin, +{ + type Error = DeviceError; +} + +impl SpiDevice for RcDevice +where + Word: Copy + 'static, + Bus: SpiBus, + Cs: OutputPin, + Delay: DelayNs, +{ + #[inline] + fn transaction(&mut self, operations: &mut [Operation<'_, Word>]) -> Result<(), Self::Error> { + let bus = &mut *self.bus.borrow_mut(); + + transaction(operations, bus, &mut self.delay, &mut self.cs) + } +} From aaa29c0ec44e1a65fa5b4a34d1deb3a21d0ab3e6 Mon Sep 17 00:00:00 2001 From: Emilie Burgun Date: Fri, 5 Jul 2024 17:01:28 +0200 Subject: [PATCH 178/199] Fix small mistake in the documentation of RcDevice::new --- embedded-hal-bus/src/spi/rc.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/embedded-hal-bus/src/spi/rc.rs b/embedded-hal-bus/src/spi/rc.rs index 1d58db37d..5a1c558bd 100644 --- a/embedded-hal-bus/src/spi/rc.rs +++ b/embedded-hal-bus/src/spi/rc.rs @@ -16,8 +16,8 @@ use crate::spi::shared::transaction; /// Both of these mechanisms only allow sharing within a single thread (or interrupt priority level). /// For this reason, this does not implement [`Send`]. /// -/// When this structure is dropped, the reference count of the `Bus` will be decremented, -/// and the bus driver will be cleaned up when that count reaches zero. +/// When this structure is dropped, the reference count of the `Bus` instance will be decremented, +/// and it will be cleaned up once the reference count reaches zero. #[cfg_attr(docsrs, doc(cfg(any(feature = "std", feature = "alloc"))))] pub struct RcDevice { bus: Rc>, @@ -32,7 +32,7 @@ impl RcDevice { /// It is recommended to have already set that pin high the moment it has been configured as an output, to avoid glitches. /// /// This function does not increment the reference count: - /// you will need to call `Rc::clone(&bus)` if you only have a `&RefCell`. + /// you will need to call `Rc::clone(&bus)` if you only have a `&Rc>`. #[inline] pub fn new(bus: Rc>, mut cs: Cs, delay: Delay) -> Result where From 9c609615afe25e9c7ee05fee7225da53ed309e84 Mon Sep 17 00:00:00 2001 From: Emilie Burgun Date: Fri, 5 Jul 2024 17:15:54 +0200 Subject: [PATCH 179/199] Add Rc>-based implementation of shared I2c buses --- embedded-hal-bus/Cargo.toml | 2 + embedded-hal-bus/src/i2c/mod.rs | 5 +++ embedded-hal-bus/src/i2c/rc.rs | 75 +++++++++++++++++++++++++++++++++ 3 files changed, 82 insertions(+) create mode 100644 embedded-hal-bus/src/i2c/rc.rs diff --git a/embedded-hal-bus/Cargo.toml b/embedded-hal-bus/Cargo.toml index 6ab831fbe..836f050b2 100644 --- a/embedded-hal-bus/Cargo.toml +++ b/embedded-hal-bus/Cargo.toml @@ -20,6 +20,8 @@ async = ["dep:embedded-hal-async"] defmt-03 = ["dep:defmt-03", "embedded-hal/defmt-03", "embedded-hal-async?/defmt-03"] # Enables additional utilities requiring a global allocator. alloc = [] +# TODO: remove this +default = ["alloc"] [dependencies] embedded-hal = { version = "1.0.0", path = "../embedded-hal" } diff --git a/embedded-hal-bus/src/i2c/mod.rs b/embedded-hal-bus/src/i2c/mod.rs index 5295492f1..6420d06c7 100644 --- a/embedded-hal-bus/src/i2c/mod.rs +++ b/embedded-hal-bus/src/i2c/mod.rs @@ -10,3 +10,8 @@ mod critical_section; pub use self::critical_section::*; mod atomic; pub use atomic::*; + +#[cfg(feature = "alloc")] +mod rc; +#[cfg(feature = "alloc")] +pub use rc::*; diff --git a/embedded-hal-bus/src/i2c/rc.rs b/embedded-hal-bus/src/i2c/rc.rs new file mode 100644 index 000000000..2f1621687 --- /dev/null +++ b/embedded-hal-bus/src/i2c/rc.rs @@ -0,0 +1,75 @@ +extern crate alloc; +use alloc::rc::Rc; + +use core::cell::RefCell; +use embedded_hal::i2c::{ErrorType, I2c}; + +/// `Rc>`-based shared bus [`I2c`] implementation. +/// This is the reference-counting equivalent of [`RefCellDevice`](super::RefCellDevice). +/// +/// Sharing is implemented with a [`RefCell`] and ownership is managed by [`Rc`]. +/// Like [`RefCellDevice`](super::RefCellDevice), `RcDevice` instances are not [`Send`], +/// so they can only be shared within a single thread (interrupt priority level). +/// +/// When this `RcDevice` is dropped, the reference count of the I2C bus will be decremented. +/// Once that reference count hits zero, it will be cleaned up. +#[cfg_attr(docsrs, doc(cfg(any(feature = "std", feature = "alloc"))))] +pub struct RcDevice { + bus: Rc>, +} + +impl RcDevice { + /// Creates a new `RcDevice`. + /// + /// This function does not increment the reference count for the bus: + /// you will need to call `Rc::clone(&bus)` if you only have a `&Rc>`. + #[inline] + pub fn new(bus: Rc>) -> Self { + Self { bus } + } +} + +impl ErrorType for RcDevice +where + Bus: ErrorType, +{ + type Error = Bus::Error; +} + +impl I2c for RcDevice +where + Bus: I2c, +{ + #[inline] + fn read(&mut self, address: u8, read: &mut [u8]) -> Result<(), Self::Error> { + let bus = &mut *self.bus.borrow_mut(); + bus.read(address, read) + } + + #[inline] + fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Self::Error> { + let bus = &mut *self.bus.borrow_mut(); + bus.write(address, write) + } + + #[inline] + fn write_read( + &mut self, + address: u8, + write: &[u8], + read: &mut [u8], + ) -> Result<(), Self::Error> { + let bus = &mut *self.bus.borrow_mut(); + bus.write_read(address, write, read) + } + + #[inline] + fn transaction( + &mut self, + address: u8, + operations: &mut [embedded_hal::i2c::Operation<'_>], + ) -> Result<(), Self::Error> { + let bus = &mut *self.bus.borrow_mut(); + bus.transaction(address, operations) + } +} From 1c02ef9f8644d388cafc46cd391dab6e89e8506b Mon Sep 17 00:00:00 2001 From: Shad Amethyst Date: Fri, 26 Jul 2024 11:45:03 +0200 Subject: [PATCH 180/199] Remove the alloc feature from default for testing Co-authored-by: Diego Barrios Romero --- embedded-hal-bus/Cargo.toml | 2 -- 1 file changed, 2 deletions(-) diff --git a/embedded-hal-bus/Cargo.toml b/embedded-hal-bus/Cargo.toml index 836f050b2..6ab831fbe 100644 --- a/embedded-hal-bus/Cargo.toml +++ b/embedded-hal-bus/Cargo.toml @@ -20,8 +20,6 @@ async = ["dep:embedded-hal-async"] defmt-03 = ["dep:defmt-03", "embedded-hal/defmt-03", "embedded-hal-async?/defmt-03"] # Enables additional utilities requiring a global allocator. alloc = [] -# TODO: remove this -default = ["alloc"] [dependencies] embedded-hal = { version = "1.0.0", path = "../embedded-hal" } From 21e82bb21d7b02b299299c0bdae493ac27d71c56 Mon Sep 17 00:00:00 2001 From: Emilie Burgun Date: Fri, 26 Jul 2024 11:52:08 +0200 Subject: [PATCH 181/199] Document changes and document the `alloc` feature --- embedded-hal-bus/CHANGELOG.md | 3 ++- embedded-hal-bus/README.md | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/embedded-hal-bus/CHANGELOG.md b/embedded-hal-bus/CHANGELOG.md index d92532d3f..45d0f6cc8 100644 --- a/embedded-hal-bus/CHANGELOG.md +++ b/embedded-hal-bus/CHANGELOG.md @@ -7,7 +7,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] -(Add unreleased changes here) +- Added the `alloc` feature. +- Added a new `RcDevice` for I2C and SPI, a reference-counting equivalent to `RefCellDevice`. ## [v0.2.0] - 2024-04-23 diff --git a/embedded-hal-bus/README.md b/embedded-hal-bus/README.md index ce71eec92..2089cb8a6 100644 --- a/embedded-hal-bus/README.md +++ b/embedded-hal-bus/README.md @@ -34,6 +34,7 @@ provides mechanisms to obtain multiple `I2c` instances out of a single `I2c` ins `std::error::Error` for `DeviceError`. - **`async`**: enable `embedded-hal-async` support. - **`defmt-03`**: Derive `defmt::Format` from `defmt` 0.3 for enums and structs. +- **`alloc`**: enable implementations using `alloc` (for instance, `spi::RcDevice`, which makes use of `alloc::rc::Rc`) ## Minimum Supported Rust Version (MSRV) From 3ddc31a81087b92effcc00cee5f0a0003f73fbd4 Mon Sep 17 00:00:00 2001 From: 9names <60134748+9names@users.noreply.github.com> Date: Sat, 27 Jul 2024 21:03:20 +1000 Subject: [PATCH 182/199] Remove features that enable atomic-polyfill flags --- embedded-hal-bus/Cargo.toml | 20 +++++++------------- embedded-hal-bus/README.md | 14 +++++--------- embedded-hal-bus/src/i2c/mod.rs | 4 ++-- embedded-hal-bus/src/spi/mod.rs | 4 ++-- embedded-hal-bus/src/util.rs | 8 ++++---- 5 files changed, 20 insertions(+), 30 deletions(-) diff --git a/embedded-hal-bus/Cargo.toml b/embedded-hal-bus/Cargo.toml index 07bff536f..1ef572e9e 100644 --- a/embedded-hal-bus/Cargo.toml +++ b/embedded-hal-bus/Cargo.toml @@ -17,23 +17,17 @@ version = "0.2.0" [features] # Enable shared bus implementations using `std::sync::Mutex`, and implement `std::error::Error` for `DeviceError` std = [] -# Enable shared bus implementations that require Atomic CAS operations -atomic-device = [] +# Use `portable-atomic` to enable `atomic-device` on devices without native atomic CAS +# +# `portable-atomic` emulates atomic CAS functionality, allowing `embedded-hal-bus` to use `atomic-device` on hardware +# that does not natively support atomic CAS. If you enable this, you must also add `portable-atomic` to your crate with +# a feature flag such as `unsafe-assume-single-core` or `critical-section` to choose how atomic CAS is implemented. +# See https://docs.rs/portable-atomic/1.7.0/portable_atomic/#optional-features for more info. +portable-atomic = [] # Enable `embedded-hal-async` support. async = ["dep:embedded-hal-async"] # Derive `defmt::Format` from `defmt` 0.3 for enums and structs. See https://github.com/knurling-rs/defmt for more info defmt-03 = ["dep:defmt-03", "embedded-hal/defmt-03", "embedded-hal-async?/defmt-03"] -# Enable critical-section feature in portable-atomic. -# -# `portable-atomic` emulates atomic CAS functionality, allowing `embedded-hal-bus` to use `atomic-device` on hardware that does not natively support atomic CAS. -# This feature requires a critical-section implementation, which is most often provided by your arch crate (cortex-m / riscv / msp430 / avr-device / etc) when the `critical-section-single-core` feature is enabled. -# A list of critical-section impls is available [in the critical section docs](https://github.com/rust-embedded/critical-section?tab=readme-ov-file#usage-in-no-std-binaries) -portable-atomic-critical-section = ["atomic-device", "portable-atomic/critical-section"] -# Enable unsafe-assume-single-core feature of portable-atomic. -# -# `portable-atomic` emulates atomic CAS functionality, allowing `embedded-hal-bus` to use `atomic-device` on hardware that does not natively support atomic CAS. -# This feature is only safe on single core systems -portable-atomic-unsafe-assume-single-core = ["atomic-device", "portable-atomic/unsafe-assume-single-core"] [dependencies] embedded-hal = { version = "1.0.0", path = "../embedded-hal" } diff --git a/embedded-hal-bus/README.md b/embedded-hal-bus/README.md index 4922c7f5f..c2d9a77cf 100644 --- a/embedded-hal-bus/README.md +++ b/embedded-hal-bus/README.md @@ -31,17 +31,13 @@ provides mechanisms to obtain multiple `I2c` instances out of a single `I2c` ins ## Optional Cargo features - **`async`**: enable `embedded-hal-async` support. -- **`atomic-device`**: enable shared bus implementations that require Atomic CAS operations. - **`defmt-03`**: Derive `defmt::Format` from `defmt` 0.3 for enums and structs. -- **`portable-atomic-critical-section`**: Enable critical-section feature in portable-atomic. +- **`portable-atomic`**: Use `portable-atomic` to enable `atomic-device` on devices without native atomic CAS - `portable-atomic` emulates atomic CAS functionality, allowing `embedded-hal-bus` to use `atomic-device` on hardware that does not natively support atomic CAS. - This feature requires a critical-section implementation, which is most often provided by your arch crate (cortex-m / riscv / msp430 / avr-device / etc) when the `critical-section-single-core` feature is enabled. - A list of critical-section impls is available [in the critical section docs](https://github.com/rust-embedded/critical-section?tab=readme-ov-file#usage-in-no-std-binaries) -- **`portable-atomic-unsafe-assume-single-core`**: Enable unsafe-assume-single-core feature of portable-atomic. - - `portable-atomic` emulates atomic CAS functionality, allowing `embedded-hal-bus` to use `atomic-device` on hardware that does not natively support atomic CAS. - This feature is only safe on single core systems + `portable-atomic` emulates atomic CAS functionality, allowing `embedded-hal-bus` to use `atomic-device` on hardware + that does not natively support atomic CAS. If you enable this, you must also add `portable-atomic` to your crate with + a feature flag such as `unsafe-assume-single-core` or `critical-section` to choose how atomic CAS is implemented. + See https://docs.rs/portable-atomic/1.7.0/portable_atomic/#optional-features for more info. - **`std`**: enable shared bus implementations using `std::sync::Mutex`, and implement `std::error::Error` for `DeviceError`. diff --git a/embedded-hal-bus/src/i2c/mod.rs b/embedded-hal-bus/src/i2c/mod.rs index ef8f7ae9c..0353ea710 100644 --- a/embedded-hal-bus/src/i2c/mod.rs +++ b/embedded-hal-bus/src/i2c/mod.rs @@ -8,7 +8,7 @@ mod mutex; pub use mutex::*; mod critical_section; pub use self::critical_section::*; -#[cfg(any(feature = "atomic-device", target_has_atomic = "8"))] +#[cfg(any(feature = "portable-atomic", target_has_atomic = "8"))] mod atomic; -#[cfg(any(feature = "atomic-device", target_has_atomic = "8"))] +#[cfg(any(feature = "portable-atomic", target_has_atomic = "8"))] pub use atomic::*; diff --git a/embedded-hal-bus/src/spi/mod.rs b/embedded-hal-bus/src/spi/mod.rs index a7ad6b989..c14c8fdca 100644 --- a/embedded-hal-bus/src/spi/mod.rs +++ b/embedded-hal-bus/src/spi/mod.rs @@ -11,11 +11,11 @@ pub use refcell::*; mod mutex; #[cfg(feature = "std")] pub use mutex::*; -#[cfg(any(feature = "atomic-device", target_has_atomic = "8"))] +#[cfg(any(feature = "portable-atomic", target_has_atomic = "8"))] mod atomic; mod critical_section; mod shared; -#[cfg(any(feature = "atomic-device", target_has_atomic = "8"))] +#[cfg(any(feature = "portable-atomic", target_has_atomic = "8"))] pub use atomic::*; pub use self::critical_section::*; diff --git a/embedded-hal-bus/src/util.rs b/embedded-hal-bus/src/util.rs index f0cb8f16d..91add2324 100644 --- a/embedded-hal-bus/src/util.rs +++ b/embedded-hal-bus/src/util.rs @@ -3,7 +3,7 @@ #[allow(unused_imports)] use core::cell::UnsafeCell; -#[cfg(any(feature = "atomic-device", target_has_atomic = "8"))] +#[cfg(any(feature = "portable-atomic", target_has_atomic = "8"))] /// Cell type used by [`spi::AtomicDevice`](crate::spi::AtomicDevice) and [`i2c::AtomicDevice`](crate::i2c::AtomicDevice). /// /// To use `AtomicDevice`, you must wrap the bus with this struct, and then @@ -12,12 +12,12 @@ pub struct AtomicCell { pub(crate) bus: UnsafeCell, pub(crate) busy: portable_atomic::AtomicBool, } -#[cfg(any(feature = "atomic-device", target_has_atomic = "8"))] +#[cfg(any(feature = "portable-atomic", target_has_atomic = "8"))] unsafe impl Send for AtomicCell {} -#[cfg(any(feature = "atomic-device", target_has_atomic = "8"))] +#[cfg(any(feature = "portable-atomic", target_has_atomic = "8"))] unsafe impl Sync for AtomicCell {} -#[cfg(any(feature = "atomic-device", target_has_atomic = "8"))] +#[cfg(any(feature = "portable-atomic", target_has_atomic = "8"))] impl AtomicCell { /// Create a new `AtomicCell` pub fn new(bus: BUS) -> Self { From 33f5255ce8fe88f33755078c0386eb46d2ee0a08 Mon Sep 17 00:00:00 2001 From: 9names <60134748+9names@users.noreply.github.com> Date: Sat, 27 Jul 2024 21:18:37 +1000 Subject: [PATCH 183/199] Fix broken URL in README.md --- embedded-hal-bus/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embedded-hal-bus/README.md b/embedded-hal-bus/README.md index 027612642..2e6d9db0d 100644 --- a/embedded-hal-bus/README.md +++ b/embedded-hal-bus/README.md @@ -38,7 +38,7 @@ provides mechanisms to obtain multiple `I2c` instances out of a single `I2c` ins `portable-atomic` emulates atomic CAS functionality, allowing `embedded-hal-bus` to use `atomic-device` on hardware that does not natively support atomic CAS. If you enable this, you must also add `portable-atomic` to your crate with a feature flag such as `unsafe-assume-single-core` or `critical-section` to choose how atomic CAS is implemented. - See https://docs.rs/portable-atomic/1.7.0/portable_atomic/#optional-features for more info. + See for more info. - **`std`**: enable shared bus implementations using `std::sync::Mutex`, and implement `std::error::Error` for `DeviceError`. From 54fa71e32ae3334088b9f8c9e59dfca5fe4e8e53 Mon Sep 17 00:00:00 2001 From: 9names <60134748+9names@users.noreply.github.com> Date: Sat, 27 Jul 2024 21:20:52 +1000 Subject: [PATCH 184/199] Add feature require-cas to portable-atomic --- embedded-hal-bus/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embedded-hal-bus/Cargo.toml b/embedded-hal-bus/Cargo.toml index 61f59f3ec..8898d961f 100644 --- a/embedded-hal-bus/Cargo.toml +++ b/embedded-hal-bus/Cargo.toml @@ -36,7 +36,7 @@ embedded-hal = { version = "1.0.0", path = "../embedded-hal" } embedded-hal-async = { version = "1.0.0", path = "../embedded-hal-async", optional = true } critical-section = { version = "1.0" } defmt-03 = { package = "defmt", version = "0.3", optional = true } -portable-atomic = {version = "1", default-features = false} +portable-atomic = {version = "1", default-features = false, features = ["require-cas"]} [package.metadata.docs.rs] features = ["std", "async", "atomic-device"] From a6be9a76bb09aba26337fb0c42990f4ea4537f43 Mon Sep 17 00:00:00 2001 From: 9names <60134748+9names@users.noreply.github.com> Date: Sat, 27 Jul 2024 21:24:46 +1000 Subject: [PATCH 185/199] Set minimum portable-atomic version to allow 'require-cas' --- embedded-hal-bus/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embedded-hal-bus/Cargo.toml b/embedded-hal-bus/Cargo.toml index 8898d961f..333aef89b 100644 --- a/embedded-hal-bus/Cargo.toml +++ b/embedded-hal-bus/Cargo.toml @@ -36,7 +36,7 @@ embedded-hal = { version = "1.0.0", path = "../embedded-hal" } embedded-hal-async = { version = "1.0.0", path = "../embedded-hal-async", optional = true } critical-section = { version = "1.0" } defmt-03 = { package = "defmt", version = "0.3", optional = true } -portable-atomic = {version = "1", default-features = false, features = ["require-cas"]} +portable-atomic = {version = "1.3", default-features = false, features = ["require-cas"]} [package.metadata.docs.rs] features = ["std", "async", "atomic-device"] From e7cbd52fb889de617566495dc405abac7de7c52d Mon Sep 17 00:00:00 2001 From: 9names <60134748+9names@users.noreply.github.com> Date: Sat, 27 Jul 2024 21:27:29 +1000 Subject: [PATCH 186/199] Remove atomic-device feature from docs.rs metadata --- embedded-hal-bus/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embedded-hal-bus/Cargo.toml b/embedded-hal-bus/Cargo.toml index 333aef89b..0b3d444c7 100644 --- a/embedded-hal-bus/Cargo.toml +++ b/embedded-hal-bus/Cargo.toml @@ -39,5 +39,5 @@ defmt-03 = { package = "defmt", version = "0.3", optional = true } portable-atomic = {version = "1.3", default-features = false, features = ["require-cas"]} [package.metadata.docs.rs] -features = ["std", "async", "atomic-device"] +features = ["std", "async"] rustdoc-args = ["--cfg", "docsrs"] From cc868a31cd9999bd86467463e62cae8eeeb4e9c3 Mon Sep 17 00:00:00 2001 From: 9names <60134748+9names@users.noreply.github.com> Date: Sun, 28 Jul 2024 10:13:26 +1000 Subject: [PATCH 187/199] Make portable-atomic optional --- embedded-hal-bus/Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/embedded-hal-bus/Cargo.toml b/embedded-hal-bus/Cargo.toml index 0b3d444c7..d0ebecf7d 100644 --- a/embedded-hal-bus/Cargo.toml +++ b/embedded-hal-bus/Cargo.toml @@ -23,7 +23,7 @@ std = ["alloc"] # that does not natively support atomic CAS. If you enable this, you must also add `portable-atomic` to your crate with # a feature flag such as `unsafe-assume-single-core` or `critical-section` to choose how atomic CAS is implemented. # See https://docs.rs/portable-atomic/1.7.0/portable_atomic/#optional-features for more info. -portable-atomic = [] +portable-atomic = ["dep:portable-atomic"] # Enable `embedded-hal-async` support. async = ["dep:embedded-hal-async"] # Derive `defmt::Format` from `defmt` 0.3 for enums and structs. See https://github.com/knurling-rs/defmt for more info @@ -36,7 +36,7 @@ embedded-hal = { version = "1.0.0", path = "../embedded-hal" } embedded-hal-async = { version = "1.0.0", path = "../embedded-hal-async", optional = true } critical-section = { version = "1.0" } defmt-03 = { package = "defmt", version = "0.3", optional = true } -portable-atomic = {version = "1.3", default-features = false, features = ["require-cas"]} +portable-atomic = {version = "1.3", default-features = false, optional = true, features = ["require-cas"]} [package.metadata.docs.rs] features = ["std", "async"] From 2acbd7773cb30b3c8c5e78720a9fbc6b304954d8 Mon Sep 17 00:00:00 2001 From: 9names <60134748+9names@users.noreply.github.com> Date: Sun, 28 Jul 2024 10:19:09 +1000 Subject: [PATCH 188/199] Use AtomicBool from core if not using portable-atomic --- embedded-hal-bus/src/util.rs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/embedded-hal-bus/src/util.rs b/embedded-hal-bus/src/util.rs index 91add2324..bb16577c4 100644 --- a/embedded-hal-bus/src/util.rs +++ b/embedded-hal-bus/src/util.rs @@ -3,6 +3,11 @@ #[allow(unused_imports)] use core::cell::UnsafeCell; +#[cfg(not(feature = "portable-atomic"))] +use core::sync::atomic::AtomicBool; +#[cfg(feature = "portable-atomic")] +use portable_atomic::AtomicBool; + #[cfg(any(feature = "portable-atomic", target_has_atomic = "8"))] /// Cell type used by [`spi::AtomicDevice`](crate::spi::AtomicDevice) and [`i2c::AtomicDevice`](crate::i2c::AtomicDevice). /// @@ -10,7 +15,7 @@ use core::cell::UnsafeCell; /// construct multiple `AtomicDevice` instances with references to it. pub struct AtomicCell { pub(crate) bus: UnsafeCell, - pub(crate) busy: portable_atomic::AtomicBool, + pub(crate) busy: AtomicBool, } #[cfg(any(feature = "portable-atomic", target_has_atomic = "8"))] unsafe impl Send for AtomicCell {} @@ -23,7 +28,7 @@ impl AtomicCell { pub fn new(bus: BUS) -> Self { Self { bus: UnsafeCell::new(bus), - busy: portable_atomic::AtomicBool::from(false), + busy: AtomicBool::from(false), } } } From 64d822764dddae3cf27a71f694d6c7f810af5ba4 Mon Sep 17 00:00:00 2001 From: pupu Date: Tue, 6 Aug 2024 00:37:08 +0800 Subject: [PATCH 189/199] Clarify I2C transaction contract for NACK behavior Update the I2C trait documentation in both embedded-hal and embedded-hal-async to clarify the NACK (No Acknowledge) behavior during read operations. The previous wording suggested that a NACK was only sent for the last byte of the final read operation in a transaction. This has been corrected to specify that a NACK should be sent at the end of each read operation, whether it's followed by a stop condition or a repeated start condition. This change ensures correct implementation of the I2C protocol across different scenarios and prevents potential communication errors. Changes: - Updated comment in embedded-hal/src/i2c.rs - Updated comment in embedded-hal-async/src/i2c.rs --- embedded-hal-async/src/i2c.rs | 2 +- embedded-hal/src/i2c.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/embedded-hal-async/src/i2c.rs b/embedded-hal-async/src/i2c.rs index 445cd9e6b..86014eec0 100644 --- a/embedded-hal-async/src/i2c.rs +++ b/embedded-hal-async/src/i2c.rs @@ -112,7 +112,7 @@ pub trait I2c: ErrorType { /// - 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. + /// - At the end of each read operation (before SP or SR), 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 diff --git a/embedded-hal/src/i2c.rs b/embedded-hal/src/i2c.rs index 634d52248..36052afaf 100644 --- a/embedded-hal/src/i2c.rs +++ b/embedded-hal/src/i2c.rs @@ -392,7 +392,7 @@ pub trait I2c: ErrorType { /// - 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. + /// - At the end of each read operation (before SP or SR), 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 From 9b1e75480bb133a10f454903e38d3fcd9abe7f54 Mon Sep 17 00:00:00 2001 From: ivmarkov Date: Sat, 24 Aug 2024 07:32:28 +0000 Subject: [PATCH 190/199] Clarify Read trait blocking behavior --- embedded-io/src/lib.rs | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/embedded-io/src/lib.rs b/embedded-io/src/lib.rs index 9f95e636a..62028c222 100644 --- a/embedded-io/src/lib.rs +++ b/embedded-io/src/lib.rs @@ -300,16 +300,23 @@ impl std::error::Error for WriteFmtError {} pub trait Read: ErrorType { /// Read some bytes from this source into the specified buffer, returning how many bytes were read. /// - /// If no bytes are currently available to read, this function blocks until at least one byte is available. + /// If no bytes are currently available to read: + /// - The method blocks until at least one byte becomes available; + /// - Once at least one (or more) bytes become available, a non-zero amount of those is read to the + /// beginning of `buf`, and the amount is returned, *without waiting or blocking any further for + /// more bytes to become available*. /// - /// If bytes are available, a non-zero amount of bytes is read to the beginning of `buf`, and the amount - /// is returned. It is not guaranteed that *all* available bytes are returned, it is possible for the - /// implementation to read an amount of bytes less than `buf.len()` while there are more bytes immediately - /// available. + /// If bytes are available to read: + /// - A non-zero amount of bytes is read to the beginning of `buf`, and the amount is returned immediately, + /// *without blocking and waiting for more bytes to become available*; + /// - It is not guaranteed that *all* available bytes are returned, it is possible for the implementation to + /// read an amount of bytes less than `buf.len()` while there are more bytes immediately available. /// - /// If the reader is at end-of-file (EOF), `Ok(0)` is returned. There is no guarantee that a reader at EOF - /// will always be so in the future, for example a reader can stop being at EOF if another process appends - /// more bytes to the underlying file. + /// This blocking behavior is important for the cases where `Read` represents the "read" leg of a pipe-like + /// protocol (a socket, a pipe, a serial line etc.). The semantics is that the caller - by passing a non-empty + /// buffer - does expect _some_ data (one or more bytes) - but _not necessarily `buf.len()` or more bytes_ - + /// to become available, before the peer represented by `Read` would stop sending bytes due to + /// application-specific reasons (as in the peer waiting for a response to the data it had sent so far). /// /// If `buf.len() == 0`, `read` returns without blocking, with either `Ok(0)` or an error. /// The `Ok(0)` doesn't indicate EOF, unlike when called with a non-empty buffer. From 45fcad0cd5c329a90df7f066aaffb6741a414498 Mon Sep 17 00:00:00 2001 From: ivmarkov Date: Sat, 24 Aug 2024 13:01:06 +0300 Subject: [PATCH 191/199] Update embedded-io/src/lib.rs Co-authored-by: James Munns --- embedded-io/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embedded-io/src/lib.rs b/embedded-io/src/lib.rs index 62028c222..fcf465f42 100644 --- a/embedded-io/src/lib.rs +++ b/embedded-io/src/lib.rs @@ -302,7 +302,7 @@ pub trait Read: ErrorType { /// /// If no bytes are currently available to read: /// - The method blocks until at least one byte becomes available; - /// - Once at least one (or more) bytes become available, a non-zero amount of those is read to the + /// - Once at least one (or more) bytes become available, a non-zero amount of those is copied to the /// beginning of `buf`, and the amount is returned, *without waiting or blocking any further for /// more bytes to become available*. /// From 92dbaf2d03dbf8d6910cee4fc3b867c1bcf3746a Mon Sep 17 00:00:00 2001 From: ivmarkov Date: Sat, 24 Aug 2024 11:48:15 +0000 Subject: [PATCH 192/199] Re-add an unintentionally removed paragraph --- embedded-io/src/lib.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/embedded-io/src/lib.rs b/embedded-io/src/lib.rs index fcf465f42..a9abf9f42 100644 --- a/embedded-io/src/lib.rs +++ b/embedded-io/src/lib.rs @@ -318,6 +318,10 @@ pub trait Read: ErrorType { /// to become available, before the peer represented by `Read` would stop sending bytes due to /// application-specific reasons (as in the peer waiting for a response to the data it had sent so far). /// + /// If the reader is at end-of-file (EOF), `Ok(0)` is returned. There is no guarantee that a reader at EOF + /// will always be so in the future, for example a reader can stop being at EOF if another process appends + /// more bytes to the underlying file. + /// /// If `buf.len() == 0`, `read` returns without blocking, with either `Ok(0)` or an error. /// The `Ok(0)` doesn't indicate EOF, unlike when called with a non-empty buffer. fn read(&mut self, buf: &mut [u8]) -> Result; From f3558ac6f10a2f9d6403237f86edea210a9bfc0b Mon Sep 17 00:00:00 2001 From: ivmarkov Date: Mon, 26 Aug 2024 07:32:57 +0000 Subject: [PATCH 193/199] Add identical changes to the async Read variant --- embedded-io-async/src/lib.rs | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/embedded-io-async/src/lib.rs b/embedded-io-async/src/lib.rs index 7c0ac2d55..0fb8a2576 100644 --- a/embedded-io-async/src/lib.rs +++ b/embedded-io-async/src/lib.rs @@ -19,12 +19,23 @@ pub use embedded_io::{ pub trait Read: ErrorType { /// Read some bytes from this source into the specified buffer, returning how many bytes were read. /// - /// If no bytes are currently available to read, this function waits until at least one byte is available. - /// - /// If bytes are available, a non-zero amount of bytes is read to the beginning of `buf`, and the amount - /// is returned. It is not guaranteed that *all* available bytes are returned, it is possible for the - /// implementation to read an amount of bytes less than `buf.len()` while there are more bytes immediately - /// available. + /// If no bytes are currently available to read: + /// - The method waits until at least one byte becomes available; + /// - Once at least one (or more) bytes become available, a non-zero amount of those is copied to the + /// beginning of `buf`, and the amount is returned, *without waiting any further for more bytes to + /// become available*. + /// + /// If bytes are available to read: + /// - A non-zero amount of bytes is read to the beginning of `buf`, and the amount is returned immediately, + /// *without waiting for more bytes to become available*; + /// - It is not guaranteed that *all* available bytes are returned, it is possible for the implementation to + /// read an amount of bytes less than `buf.len()` while there are more bytes immediately available. + /// + /// This waiting behavior is important for the cases where `Read` represents the "read" leg of a pipe-like + /// protocol (a socket, a pipe, a serial line etc.). The semantics is that the caller - by passing a non-empty + /// buffer - does expect _some_ data (one or more bytes) - but _not necessarily `buf.len()` or more bytes_ - + /// to become available, before the peer represented by `Read` would stop sending bytes due to + /// application-specific reasons (as in the peer waiting for a response to the data it had sent so far). /// /// If the reader is at end-of-file (EOF), `Ok(0)` is returned. There is no guarantee that a reader at EOF /// will always be so in the future, for example a reader can stop being at EOF if another process appends From d7454184366fdbefb590bdee0cb7a8a21ce4e886 Mon Sep 17 00:00:00 2001 From: ivmarkov Date: Sun, 8 Sep 2024 07:51:22 +0000 Subject: [PATCH 194/199] Address review feedback --- embedded-io-async/src/lib.rs | 6 ++++-- embedded-io/src/lib.rs | 6 ++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/embedded-io-async/src/lib.rs b/embedded-io-async/src/lib.rs index 0fb8a2576..eef50ebcd 100644 --- a/embedded-io-async/src/lib.rs +++ b/embedded-io-async/src/lib.rs @@ -28,8 +28,10 @@ pub trait Read: ErrorType { /// If bytes are available to read: /// - A non-zero amount of bytes is read to the beginning of `buf`, and the amount is returned immediately, /// *without waiting for more bytes to become available*; - /// - It is not guaranteed that *all* available bytes are returned, it is possible for the implementation to - /// read an amount of bytes less than `buf.len()` while there are more bytes immediately available. + /// + /// Note that once some bytes are available to read, it is *not* guaranteed that all available bytes are returned. + /// It is possible for the implementation to read an amount of bytes less than `buf.len()` while there are more + /// bytes immediately available. /// /// This waiting behavior is important for the cases where `Read` represents the "read" leg of a pipe-like /// protocol (a socket, a pipe, a serial line etc.). The semantics is that the caller - by passing a non-empty diff --git a/embedded-io/src/lib.rs b/embedded-io/src/lib.rs index a9abf9f42..f050abb1d 100644 --- a/embedded-io/src/lib.rs +++ b/embedded-io/src/lib.rs @@ -309,8 +309,10 @@ pub trait Read: ErrorType { /// If bytes are available to read: /// - A non-zero amount of bytes is read to the beginning of `buf`, and the amount is returned immediately, /// *without blocking and waiting for more bytes to become available*; - /// - It is not guaranteed that *all* available bytes are returned, it is possible for the implementation to - /// read an amount of bytes less than `buf.len()` while there are more bytes immediately available. + /// + /// Note that once some bytes are available to read, it is *not* guaranteed that all available bytes are returned. + /// It is possible for the implementation to read an amount of bytes less than `buf.len()` while there are more + /// bytes immediately available. /// /// This blocking behavior is important for the cases where `Read` represents the "read" leg of a pipe-like /// protocol (a socket, a pipe, a serial line etc.). The semantics is that the caller - by passing a non-empty From 81ee92a99080c602334a3149d508f797857ac1de Mon Sep 17 00:00:00 2001 From: Ralph Ursprung Date: Mon, 9 Sep 2024 13:42:44 +0200 Subject: [PATCH 195/199] CI: fix label of rustdoc job --- .github/workflows/rustdoc.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/rustdoc.yml b/.github/workflows/rustdoc.yml index 7829e62b6..1a99f8d92 100644 --- a/.github/workflows/rustdoc.yml +++ b/.github/workflows/rustdoc.yml @@ -7,7 +7,7 @@ on: name: Rustdoc check jobs: - clippy: + rustdoc: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 From 91cf7e18bdff43a2a70345d07fafeaaf627c5ee4 Mon Sep 17 00:00:00 2001 From: Ralph Ursprung Date: Mon, 9 Sep 2024 13:26:01 +0200 Subject: [PATCH 196/199] implement `core::error::Error` this trait has been stabilised in Rust 1.81.0. the existing custom `Error` types cannot be removed / replaced as that'd be a breaking change. for the same reason it's not possible for them to require `core::error::Error` being implemented. however, we can already implement the new trait for all cases where the custom trait has been implemented so far. existing `std` feature-gated implementations of `std::error::Error` have also been moved to `core::error::Error` and the feature gate removed. this raises the MSRV to 1.81.0 for most crates, but based on the MSRV policy this should not be an issue. --- .github/workflows/test.yml | 18 ++---------------- README.md | 2 +- embedded-can/CHANGELOG.md | 3 ++- embedded-can/Cargo.toml | 2 +- embedded-can/src/lib.rs | 2 ++ embedded-hal-bus/CHANGELOG.md | 2 ++ embedded-hal-bus/Cargo.toml | 4 ++-- embedded-hal-bus/README.md | 3 +-- embedded-hal-bus/src/spi/mod.rs | 3 +-- embedded-hal-nb/CHANGELOG.md | 3 ++- embedded-hal-nb/Cargo.toml | 2 +- embedded-hal-nb/src/serial.rs | 2 ++ embedded-hal/CHANGELOG.md | 3 ++- embedded-hal/Cargo.toml | 2 +- embedded-hal/src/digital.rs | 2 ++ embedded-hal/src/i2c.rs | 2 ++ embedded-hal/src/pwm.rs | 2 ++ embedded-hal/src/spi.rs | 2 ++ embedded-io-async/README.md | 2 +- embedded-io/CHANGELOG.md | 6 ++++++ embedded-io/Cargo.toml | 2 +- embedded-io/README.md | 2 +- embedded-io/src/impls/slice_mut.rs | 4 +--- embedded-io/src/lib.rs | 16 ++++++++++------ 24 files changed, 50 insertions(+), 41 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 46fb03b26..586d42513 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -38,23 +38,9 @@ jobs: --target thumbv7m-none-eabi --features async,defmt-03 - msrv-1-60: + msrv-1-81: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - uses: dtolnay/rust-toolchain@1.60 - - run: > - cargo test - -p embedded-hal:1.0.0 - -p embedded-hal-bus - -p embedded-hal-nb - -p embedded-io - -p embedded-io-adapters - -p embedded-can - - msrv-1-75: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: dtolnay/rust-toolchain@1.75 + - uses: dtolnay/rust-toolchain@1.81 - run: cargo test --workspace --all-features diff --git a/README.md b/README.md index da60c3fed..63c347628 100644 --- a/README.md +++ b/README.md @@ -64,7 +64,7 @@ on crates.io. ## Minimum Supported Rust Version (MSRV) -This crate is guaranteed to compile on stable Rust 1.60 and up. It *might* +This crate is guaranteed to compile on stable Rust 1.81 and up. It *might* compile with older versions but that may change in any new patch release. See [here](docs/msrv.md) for details on how the MSRV may be upgraded. diff --git a/embedded-can/CHANGELOG.md b/embedded-can/CHANGELOG.md index 302c85a09..f8719d632 100644 --- a/embedded-can/CHANGELOG.md +++ b/embedded-can/CHANGELOG.md @@ -7,7 +7,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] -... +- Added `core::error::Error` implementations for every custom `impl Error` +- Increased MSRV to 1.81 due to `core::error::Error` ## [v0.4.1] - 2022-09-28 diff --git a/embedded-can/Cargo.toml b/embedded-can/Cargo.toml index 07ef7cc53..105f286b0 100644 --- a/embedded-can/Cargo.toml +++ b/embedded-can/Cargo.toml @@ -2,7 +2,7 @@ name = "embedded-can" version = "0.4.1" edition = "2021" -rust-version = "1.56" +rust-version = "1.81" description = "HAL traits for Controller Area Network (CAN) devices." categories = ["embedded", "hardware-support", "no-std"] diff --git a/embedded-can/src/lib.rs b/embedded-can/src/lib.rs index da561f58c..bb010b965 100644 --- a/embedded-can/src/lib.rs +++ b/embedded-can/src/lib.rs @@ -110,6 +110,8 @@ impl Error for ErrorKind { } } +impl core::error::Error for ErrorKind {} + impl core::fmt::Display for ErrorKind { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { match self { diff --git a/embedded-hal-bus/CHANGELOG.md b/embedded-hal-bus/CHANGELOG.md index 45d0f6cc8..d9009d9c3 100644 --- a/embedded-hal-bus/CHANGELOG.md +++ b/embedded-hal-bus/CHANGELOG.md @@ -9,6 +9,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - Added the `alloc` feature. - Added a new `RcDevice` for I2C and SPI, a reference-counting equivalent to `RefCellDevice`. +- Migrated `std` feature-gated `std::error::Error` implementations to `core::error::Error` +- Increased MSRV to 1.81 due to `core::error::Error` ## [v0.2.0] - 2024-04-23 diff --git a/embedded-hal-bus/Cargo.toml b/embedded-hal-bus/Cargo.toml index d0ebecf7d..605f01b85 100644 --- a/embedded-hal-bus/Cargo.toml +++ b/embedded-hal-bus/Cargo.toml @@ -6,7 +6,7 @@ categories = ["embedded", "hardware-support", "no-std"] description = "Bus/Device connection mechanisms for embedded-hal, a Hardware Abstraction Layer (HAL) for embedded systems" documentation = "https://docs.rs/embedded-hal-bus" edition = "2021" -rust-version = "1.60" +rust-version = "1.81" keywords = ["hal", "IO"] license = "MIT OR Apache-2.0" name = "embedded-hal-bus" @@ -15,7 +15,7 @@ repository = "https://github.com/rust-embedded/embedded-hal" version = "0.2.0" [features] -# Enable shared bus implementations using `std::sync::Mutex`, and implement `std::error::Error` for `DeviceError` +# Enable shared bus implementations using `std::sync::Mutex` std = ["alloc"] # Use `portable-atomic` to enable `atomic-device` on devices without native atomic CAS # diff --git a/embedded-hal-bus/README.md b/embedded-hal-bus/README.md index 2e6d9db0d..6f767aae7 100644 --- a/embedded-hal-bus/README.md +++ b/embedded-hal-bus/README.md @@ -39,8 +39,7 @@ provides mechanisms to obtain multiple `I2c` instances out of a single `I2c` ins that does not natively support atomic CAS. If you enable this, you must also add `portable-atomic` to your crate with a feature flag such as `unsafe-assume-single-core` or `critical-section` to choose how atomic CAS is implemented. See for more info. -- **`std`**: enable shared bus implementations using `std::sync::Mutex`, and implement - `std::error::Error` for `DeviceError`. +- **`std`**: enable shared bus implementations using `std::sync::Mutex`. ## Minimum Supported Rust Version (MSRV) diff --git a/embedded-hal-bus/src/spi/mod.rs b/embedded-hal-bus/src/spi/mod.rs index 5a8356e28..c7a793f90 100644 --- a/embedded-hal-bus/src/spi/mod.rs +++ b/embedded-hal-bus/src/spi/mod.rs @@ -47,8 +47,7 @@ impl Display for DeviceError { } } -#[cfg(feature = "std")] -impl std::error::Error for DeviceError {} +impl core::error::Error for DeviceError {} impl Error for DeviceError where diff --git a/embedded-hal-nb/CHANGELOG.md b/embedded-hal-nb/CHANGELOG.md index f31190acb..63314c7cf 100644 --- a/embedded-hal-nb/CHANGELOG.md +++ b/embedded-hal-nb/CHANGELOG.md @@ -7,7 +7,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] -No unreleased changes +- Added `core::error::Error` implementations for every custom `impl Error` +- Increased MSRV to 1.81 due to `core::error::Error` ## [v1.0.0] - 2023-12-28 diff --git a/embedded-hal-nb/Cargo.toml b/embedded-hal-nb/Cargo.toml index d4575ead0..43a6cd7be 100644 --- a/embedded-hal-nb/Cargo.toml +++ b/embedded-hal-nb/Cargo.toml @@ -2,7 +2,7 @@ name = "embedded-hal-nb" version = "1.0.0" edition = "2021" -rust-version = "1.56" +rust-version = "1.81" categories = ["embedded", "hardware-support", "no-std"] description = "Non-blocking Hardware Abstraction Layer (HAL) for embedded systems using the `nb` crate." diff --git a/embedded-hal-nb/src/serial.rs b/embedded-hal-nb/src/serial.rs index 18d1265c2..2b0d504ef 100644 --- a/embedded-hal-nb/src/serial.rs +++ b/embedded-hal-nb/src/serial.rs @@ -45,6 +45,8 @@ impl Error for ErrorKind { } } +impl core::error::Error for ErrorKind {} + impl core::fmt::Display for ErrorKind { #[inline] fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { diff --git a/embedded-hal/CHANGELOG.md b/embedded-hal/CHANGELOG.md index 4a1af0e3f..e919e25ad 100644 --- a/embedded-hal/CHANGELOG.md +++ b/embedded-hal/CHANGELOG.md @@ -7,7 +7,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] -No unreleased changes yet. +- Added `core::error::Error` implementations for every custom `impl Error` +- Increased MSRV to 1.81 due to `core::error::Error` ## [v1.0.0] - 2023-12-28 diff --git a/embedded-hal/Cargo.toml b/embedded-hal/Cargo.toml index f67b2527b..011f3cc9d 100644 --- a/embedded-hal/Cargo.toml +++ b/embedded-hal/Cargo.toml @@ -8,7 +8,7 @@ categories = ["asynchronous", "embedded", "hardware-support", "no-std"] description = " A Hardware Abstraction Layer (HAL) for embedded systems " documentation = "https://docs.rs/embedded-hal" edition = "2021" -rust-version = "1.60" +rust-version = "1.81" keywords = ["hal", "IO"] license = "MIT OR Apache-2.0" name = "embedded-hal" diff --git a/embedded-hal/src/digital.rs b/embedded-hal/src/digital.rs index 201cd7637..a38040c0c 100644 --- a/embedded-hal/src/digital.rs +++ b/embedded-hal/src/digital.rs @@ -41,6 +41,8 @@ impl Error for ErrorKind { } } +impl core::error::Error for ErrorKind {} + impl core::fmt::Display for ErrorKind { #[inline] fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { diff --git a/embedded-hal/src/i2c.rs b/embedded-hal/src/i2c.rs index 36052afaf..3d042cd36 100644 --- a/embedded-hal/src/i2c.rs +++ b/embedded-hal/src/i2c.rs @@ -232,6 +232,8 @@ impl Error for ErrorKind { } } +impl core::error::Error for ErrorKind {} + impl core::fmt::Display for ErrorKind { #[inline] fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { diff --git a/embedded-hal/src/pwm.rs b/embedded-hal/src/pwm.rs index 68a94f54f..4d4ff1508 100644 --- a/embedded-hal/src/pwm.rs +++ b/embedded-hal/src/pwm.rs @@ -40,6 +40,8 @@ impl Error for ErrorKind { } } +impl core::error::Error for ErrorKind {} + impl core::fmt::Display for ErrorKind { #[inline] fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { diff --git a/embedded-hal/src/spi.rs b/embedded-hal/src/spi.rs index 508a4a9b1..55fc7e9fb 100644 --- a/embedded-hal/src/spi.rs +++ b/embedded-hal/src/spi.rs @@ -275,6 +275,8 @@ impl Error for ErrorKind { } } +impl core::error::Error for ErrorKind {} + impl core::fmt::Display for ErrorKind { #[inline] fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { diff --git a/embedded-io-async/README.md b/embedded-io-async/README.md index 744f6e6f2..8c5043593 100644 --- a/embedded-io-async/README.md +++ b/embedded-io-async/README.md @@ -12,7 +12,7 @@ This project is developed and maintained by the [HAL team](https://github.com/ru ## Optional Cargo features -- **`std`**: Adds `From` impls to convert to/from `std::io` structs, adds `std::error::Error` impls. +- **`std`**: Adds `From` impls to convert to/from `std::io` structs. - **`alloc`**: Adds blanket impls for `Box`, adds `Write` impl to `Vec`. - **`defmt-03`**: Derive `defmt::Format` from `defmt` 0.3 for enums and structs. diff --git a/embedded-io/CHANGELOG.md b/embedded-io/CHANGELOG.md index 286d1b707..1c8ea414c 100644 --- a/embedded-io/CHANGELOG.md +++ b/embedded-io/CHANGELOG.md @@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## Unreleased + +- Added `core::error::Error` implementations for every custom `impl Error` +- Migrated `std` feature-gated `std::error::Error` implementations to `core::error::Error` +- Increased MSRV to 1.81 due to `core::error::Error` + ## 0.6.1 - 2023-10-22 - Make `SliceWriteError` publicly available. diff --git a/embedded-io/Cargo.toml b/embedded-io/Cargo.toml index e92714f7a..dc878f7b6 100644 --- a/embedded-io/Cargo.toml +++ b/embedded-io/Cargo.toml @@ -2,7 +2,7 @@ name = "embedded-io" version = "0.6.1" edition = "2021" -rust-version = "1.60" +rust-version = "1.81" description = "Embedded IO traits" repository = "https://github.com/rust-embedded/embedded-hal" readme = "README.md" diff --git a/embedded-io/README.md b/embedded-io/README.md index 04ea97771..6c228d8b4 100644 --- a/embedded-io/README.md +++ b/embedded-io/README.md @@ -21,7 +21,7 @@ targets. ## Optional Cargo features -- **`std`**: Adds `From` impls to convert to/from `std::io` structs, adds `std::error::Error` impls. +- **`std`**: Adds `From` impls to convert to/from `std::io` structs. - **`alloc`**: Adds blanket impls for `Box`, adds `Write` impl to `Vec`. - **`defmt-03`**: Derive `defmt::Format` from `defmt` 0.3 for enums and structs. diff --git a/embedded-io/src/impls/slice_mut.rs b/embedded-io/src/impls/slice_mut.rs index 4608ee45b..67606561b 100644 --- a/embedded-io/src/impls/slice_mut.rs +++ b/embedded-io/src/impls/slice_mut.rs @@ -19,9 +19,7 @@ impl core::fmt::Display for SliceWriteError { } } -#[cfg(feature = "std")] -#[cfg_attr(docsrs, doc(cfg(feature = "std")))] -impl std::error::Error for SliceWriteError {} +impl core::error::Error for SliceWriteError {} /// Write is implemented for `&mut [u8]` by copying into the slice, overwriting /// its data. diff --git a/embedded-io/src/lib.rs b/embedded-io/src/lib.rs index f050abb1d..ae5ff4853 100644 --- a/embedded-io/src/lib.rs +++ b/embedded-io/src/lib.rs @@ -193,6 +193,14 @@ impl Error for ErrorKind { } } +impl core::error::Error for ErrorKind {} + +impl fmt::Display for ErrorKind { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{:?}", self) + } +} + #[cfg(feature = "std")] #[cfg_attr(docsrs, doc(cfg(feature = "std")))] impl Error for std::io::Error { @@ -255,9 +263,7 @@ impl fmt::Display for ReadExactError { } } -#[cfg(feature = "std")] -#[cfg_attr(docsrs, doc(cfg(feature = "std")))] -impl std::error::Error for ReadExactError {} +impl core::error::Error for ReadExactError {} /// Errors that could be returned by `Write` on `&mut [u8]`. #[derive(Debug, Copy, Clone, Eq, PartialEq)] @@ -290,9 +296,7 @@ impl fmt::Display for WriteFmtError { } } -#[cfg(feature = "std")] -#[cfg_attr(docsrs, doc(cfg(feature = "std")))] -impl std::error::Error for WriteFmtError {} +impl core::error::Error for WriteFmtError {} /// Blocking reader. /// From d3f300cdd3e8c5ced313fea1ac112951db45da33 Mon Sep 17 00:00:00 2001 From: Jacob Birkett Date: Mon, 7 Oct 2024 13:23:22 -0700 Subject: [PATCH 197/199] i2c: AddressMode: add bound Copy --- embedded-hal/src/i2c.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embedded-hal/src/i2c.rs b/embedded-hal/src/i2c.rs index 3d042cd36..8b99197ef 100644 --- a/embedded-hal/src/i2c.rs +++ b/embedded-hal/src/i2c.rs @@ -276,7 +276,7 @@ impl ErrorType for &mut T { /// Address mode (7-bit / 10-bit). /// /// Note: This trait is sealed and should not be implemented outside of this crate. -pub trait AddressMode: private::Sealed + 'static {} +pub trait AddressMode: Copy + 'static + private::Sealed {} /// 7-bit address mode type. /// From 1646f05e5e17de934c205f24b66b6f21b54e9993 Mon Sep 17 00:00:00 2001 From: fpagliughi Date: Sun, 4 Dec 2022 15:49:50 -0500 Subject: [PATCH 198/199] Added impl to Id make it more directly usable. Added MAX_RAW to StandardId and ExtendedId. --- embedded-can/src/id.rs | 89 +++++++++++++++++++++++++++++++++++++----- 1 file changed, 80 insertions(+), 9 deletions(-) diff --git a/embedded-can/src/id.rs b/embedded-can/src/id.rs index b0d6d4d5f..a77c0fa57 100644 --- a/embedded-can/src/id.rs +++ b/embedded-can/src/id.rs @@ -10,7 +10,10 @@ impl StandardId { pub const ZERO: Self = Self(0); /// CAN ID `0x7FF`, the lowest priority. - pub const MAX: Self = Self(0x7FF); + pub const MAX: Self = Self(Self::MAX_RAW); + + /// Raw CAN ID `0x7FF`, the lowest priority. + pub const MAX_RAW: u16 = 0x7FF; /// Tries to create a `StandardId` from a raw 16-bit integer. /// @@ -18,7 +21,7 @@ impl StandardId { #[inline] #[must_use] pub const fn new(raw: u16) -> Option { - if raw <= 0x7FF { + if raw <= Self::MAX_RAW { Some(Self(raw)) } else { None @@ -53,7 +56,10 @@ impl ExtendedId { pub const ZERO: Self = Self(0); /// CAN ID `0x1FFFFFFF`, the lowest priority. - pub const MAX: Self = Self(0x1FFF_FFFF); + pub const MAX: Self = Self(Self::MAX_RAW); + + /// Raw CAN ID `0x1FFFFFFF`, the lowest priority. + pub const MAX_RAW: u32 = 0x1FFF_FFFF; /// Tries to create a `ExtendedId` from a raw 32-bit integer. /// @@ -61,7 +67,7 @@ impl ExtendedId { #[inline] #[must_use] pub const fn new(raw: u32) -> Option { - if raw <= 0x1FFF_FFFF { + if raw <= Self::MAX_RAW { Some(Self(raw)) } else { None @@ -104,6 +110,37 @@ pub enum Id { Extended(ExtendedId), } +impl Id { + /// Creates a CAN identifier as a standard ID. + pub fn new_standard_id(raw: u16) -> Option { + let id = StandardId::new(raw)?; + Some(Id::Standard(id)) + } + + /// Creates a CAN identifier as an extended ID. + pub fn new_extended_id(raw: u32) -> Option { + let id = ExtendedId::new(raw)?; + Some(Id::Extended(id)) + } + + /// Returns this CAN Identifier as a raw 32-bit integer, regardless of whether it's + /// a standard or extended identifier. + pub fn as_raw(&self) -> u32 { + match self { + Id::Standard(id) => id.as_raw() as u32, + Id::Extended(id) => id.as_raw(), + } + } + + /// Determines if the value is an extended identifier. + pub fn is_extended(&self) -> bool { + match self { + Id::Extended(_) => true, + _ => false + } + } +} + /// Implement `Ord` according to the CAN arbitration rules /// /// When performing arbitration, frames are looked at bit for bit starting @@ -163,19 +200,19 @@ mod tests { #[test] fn standard_id_new() { assert_eq!( - StandardId::new(StandardId::MAX.as_raw()), + StandardId::new(StandardId::MAX_RAW), Some(StandardId::MAX) ); } #[test] fn standard_id_new_out_of_range() { - assert_eq!(StandardId::new(StandardId::MAX.as_raw() + 1), None); + assert_eq!(StandardId::new(StandardId::MAX_RAW + 1), None); } #[test] fn standard_id_new_unchecked_out_of_range() { - let id = StandardId::MAX.as_raw() + 1; + let id = StandardId::MAX_RAW + 1; assert_eq!(unsafe { StandardId::new_unchecked(id) }, StandardId(id)); } @@ -189,12 +226,12 @@ mod tests { #[test] fn extended_id_new_out_of_range() { - assert_eq!(ExtendedId::new(ExtendedId::MAX.as_raw() + 1), None); + assert_eq!(ExtendedId::new(ExtendedId::MAX_RAW + 1), None); } #[test] fn extended_id_new_unchecked_out_of_range() { - let id = ExtendedId::MAX.as_raw() + 1; + let id = ExtendedId::MAX_RAW + 1; assert_eq!(unsafe { ExtendedId::new_unchecked(id) }, ExtendedId(id)); } @@ -216,4 +253,38 @@ mod tests { assert!(Id::Extended(ExtendedId((1 << 11) - 1)) < Id::Standard(StandardId(1))); assert!(Id::Standard(StandardId(1)) < Id::Extended(ExtendedId::MAX)); } + + #[test] + fn id_new() { + let id = Id::new_standard_id(StandardId::MAX_RAW); + match id { + Some(Id::Standard(id)) => assert_eq!(StandardId::MAX, id), + _ => assert!(false), + } + + let id = Id::new_extended_id(ExtendedId::MAX_RAW); + match id { + Some(Id::Extended(id)) => assert_eq!(ExtendedId::MAX, id), + _ => assert!(false), + } + } + + #[test] + fn id_raw() { + const RAW_ID: u32 = StandardId::MAX_RAW as u32; + + let id = StandardId::new(RAW_ID as u16).unwrap(); + assert_eq!(RAW_ID as u16, id.as_raw()); + + let id = Id::from(id); + assert!(!id.is_extended()); + assert_eq!(RAW_ID, id.as_raw()); + + let id = ExtendedId::new(RAW_ID).unwrap(); + assert_eq!(RAW_ID, id.as_raw()); + + let id = Id::from(id); + assert!(id.is_extended()); + assert_eq!(RAW_ID, id.as_raw()); + } } From 89572ca842666ef7808c642e18db668e99e0f66e Mon Sep 17 00:00:00 2001 From: fpagliughi Date: Wed, 5 Apr 2023 16:51:18 -0400 Subject: [PATCH 199/199] Removed Id::as_raw() and cleaned up a little. --- embedded-can/src/id.rs | 38 +++++++------------------------------- 1 file changed, 7 insertions(+), 31 deletions(-) diff --git a/embedded-can/src/id.rs b/embedded-can/src/id.rs index a77c0fa57..071e7b933 100644 --- a/embedded-can/src/id.rs +++ b/embedded-can/src/id.rs @@ -113,31 +113,17 @@ pub enum Id { impl Id { /// Creates a CAN identifier as a standard ID. pub fn new_standard_id(raw: u16) -> Option { - let id = StandardId::new(raw)?; - Some(Id::Standard(id)) + Some(Id::from(StandardId::new(raw)?)) } /// Creates a CAN identifier as an extended ID. pub fn new_extended_id(raw: u32) -> Option { - let id = ExtendedId::new(raw)?; - Some(Id::Extended(id)) - } - - /// Returns this CAN Identifier as a raw 32-bit integer, regardless of whether it's - /// a standard or extended identifier. - pub fn as_raw(&self) -> u32 { - match self { - Id::Standard(id) => id.as_raw() as u32, - Id::Extended(id) => id.as_raw(), - } + Some(Id::from(ExtendedId::new(raw)?)) } /// Determines if the value is an extended identifier. pub fn is_extended(&self) -> bool { - match self { - Id::Extended(_) => true, - _ => false - } + matches!(self, Id::Extended(_)) } } @@ -271,20 +257,10 @@ mod tests { #[test] fn id_raw() { - const RAW_ID: u32 = StandardId::MAX_RAW as u32; - - let id = StandardId::new(RAW_ID as u16).unwrap(); - assert_eq!(RAW_ID as u16, id.as_raw()); - - let id = Id::from(id); - assert!(!id.is_extended()); - assert_eq!(RAW_ID, id.as_raw()); - - let id = ExtendedId::new(RAW_ID).unwrap(); - assert_eq!(RAW_ID, id.as_raw()); + let id = StandardId::new(StandardId::MAX_RAW).unwrap(); + assert_eq!(StandardId::MAX_RAW, id.as_raw()); - let id = Id::from(id); - assert!(id.is_extended()); - assert_eq!(RAW_ID, id.as_raw()); + let id = ExtendedId::new(ExtendedId::MAX_RAW).unwrap(); + assert_eq!(ExtendedId::MAX_RAW, id.as_raw()); } }