diff --git a/.github/bors.toml b/.github/bors.toml index 904b48136..a7f0129db 100644 --- a/.github/bors.toml +++ b/.github/bors.toml @@ -2,11 +2,14 @@ block_labels = ["needs-decision"] delete_merged_branches = true required_approvals = 1 status = [ - "ci-linux (stable, x86_64-unknown-linux-gnu)", - "ci-linux (stable, thumbv6m-none-eabi)", - "ci-linux (stable, thumbv7m-none-eabi)", - "ci-linux (1.54.0, x86_64-unknown-linux-gnu)", - "ci-linux-test (stable)", - "ci-linux-test (1.54.0, x86_64-unknown-linux-gnu)", + "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 (nightly, x86_64-unknown-linux-gnu)", + "test (nightly, thumbv6m-none-eabi)", + "test (nightly, thumbv7m-none-eabi)", "fmt", ] diff --git a/.github/workflows/changelog.yml b/.github/workflows/changelog.yml deleted file mode 100644 index 29ca1399b..000000000 --- a/.github/workflows/changelog.yml +++ /dev/null @@ -1,20 +0,0 @@ -on: - pull_request_target: - -name: Changelog check - -jobs: - changelog: - name: Changelog check - runs-on: ubuntu-latest - steps: - - name: Checkout sources - uses: actions/checkout@v2 - - - name: Changelog updated - uses: Zomzog/changelog-checker@v1.2.0 - with: - fileName: CHANGELOG.md - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - diff --git a/.github/workflows/ci-async.yml b/.github/workflows/ci-async.yml deleted file mode 100644 index 741d2be74..000000000 --- a/.github/workflows/ci-async.yml +++ /dev/null @@ -1,32 +0,0 @@ -on: - push: - branches: [ staging, trying, master ] - pull_request: - -name: CI Async - -env: - RUSTFLAGS: '--deny warnings' - -jobs: - ci-linux-async: - runs-on: ubuntu-latest - strategy: - matrix: - rust: [nightly] - TARGET: [x86_64-unknown-linux-gnu, thumbv6m-none-eabi, thumbv7m-none-eabi] - - steps: - - uses: actions/checkout@v2 - - uses: actions-rs/toolchain@v1 - with: - profile: minimal - toolchain: ${{ matrix.rust }} - target: ${{ matrix.TARGET }} - override: true - - run: cargo check --target=${{ matrix.TARGET }} - working-directory: embedded-hal-async - - - run: cargo test --target=${{ matrix.TARGET }} - if: contains(matrix.TARGET, 'linux') - working-directory: embedded-hal-async diff --git a/.github/workflows/ci-bus.yml b/.github/workflows/ci-bus.yml deleted file mode 100644 index 2463ed4de..000000000 --- a/.github/workflows/ci-bus.yml +++ /dev/null @@ -1,32 +0,0 @@ -on: - push: - branches: [ staging, trying, master ] - pull_request: - -name: CI Bus - -env: - RUSTFLAGS: '--deny warnings' - -jobs: - ci-linux-bus: - runs-on: ubuntu-latest - strategy: - matrix: - rust: [nightly] - TARGET: [x86_64-unknown-linux-gnu, thumbv6m-none-eabi, thumbv7m-none-eabi] - - steps: - - uses: actions/checkout@v2 - - uses: actions-rs/toolchain@v1 - with: - profile: minimal - toolchain: ${{ matrix.rust }} - target: ${{ matrix.TARGET }} - override: true - - run: cargo check --target=${{ matrix.TARGET }} - working-directory: embedded-hal-bus - - - run: cargo test --target=${{ matrix.TARGET }} - if: contains(matrix.TARGET, 'linux') - working-directory: embedded-hal-bus diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml deleted file mode 100644 index 93fe80602..000000000 --- a/.github/workflows/ci.yml +++ /dev/null @@ -1,43 +0,0 @@ -on: - push: - branches: [ staging, trying, master ] - pull_request: - -name: Continuous integration - -env: - RUSTFLAGS: '--deny warnings' - -jobs: - ci-linux: - runs-on: ubuntu-latest - strategy: - matrix: - # All generated code should be running on stable now - rust: [stable] - - # The default target we're compiling on and for - TARGET: [x86_64-unknown-linux-gnu, thumbv6m-none-eabi, thumbv7m-none-eabi] - - include: - # Test MSRV - - rust: 1.54.0 - TARGET: x86_64-unknown-linux-gnu - - # Test nightly but don't fail - - rust: nightly - experimental: true - TARGET: x86_64-unknown-linux-gnu - - steps: - - uses: actions/checkout@v2 - - uses: actions-rs/toolchain@v1 - with: - profile: minimal - toolchain: ${{ matrix.rust }} - target: ${{ matrix.TARGET }} - override: true - - uses: actions-rs/cargo@v1 - with: - command: check - args: --target=${{ matrix.TARGET }} diff --git a/.github/workflows/clippy-async.yml b/.github/workflows/clippy-async.yml deleted file mode 100644 index ce6abdb9f..000000000 --- a/.github/workflows/clippy-async.yml +++ /dev/null @@ -1,19 +0,0 @@ -on: - push: - branches: [ staging, trying, master ] - pull_request: - -name: Clippy check -jobs: - clippy_check-async: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - uses: actions-rs/toolchain@v1 - with: - profile: minimal - toolchain: nightly - override: true - components: clippy - - run: cargo clippy - working-directory: embedded-hal-async diff --git a/.github/workflows/clippy-bus.yml b/.github/workflows/clippy-bus.yml deleted file mode 100644 index bc08e1763..000000000 --- a/.github/workflows/clippy-bus.yml +++ /dev/null @@ -1,19 +0,0 @@ -on: - push: - branches: [ staging, trying, master ] - pull_request: - -name: Clippy check -jobs: - clippy_check-bus: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - uses: actions-rs/toolchain@v1 - with: - profile: minimal - toolchain: nightly - override: true - components: clippy - - run: cargo clippy - working-directory: embedded-hal-bus diff --git a/.github/workflows/clippy.yml b/.github/workflows/clippy.yml index adc3a6ed1..c4f5fca7b 100644 --- a/.github/workflows/clippy.yml +++ b/.github/workflows/clippy.yml @@ -5,14 +5,16 @@ on: name: Clippy check jobs: - clippy_check: + clippy: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - uses: actions-rs/toolchain@v1 with: profile: minimal - toolchain: stable + # embedded-hal-async needs nightly. + # Use a pinned version to avoid spontaneous breakages (new clippy lints are added often) + toolchain: nightly-2022-09-05 override: true components: clippy - uses: actions-rs/clippy-check@v1 diff --git a/.github/workflows/rustfmt.yml b/.github/workflows/rustfmt.yml index 45b32bae4..7ffd30a99 100644 --- a/.github/workflows/rustfmt.yml +++ b/.github/workflows/rustfmt.yml @@ -16,9 +16,4 @@ jobs: toolchain: nightly override: true components: rustfmt - - uses: actions-rs/cargo@v1 - with: - command: fmt - args: --all -- --check - - run: cargo fmt --all -- --check - working-directory: embedded-hal-async + - run: cargo fmt --check diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 9307a27c8..46740f048 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -3,26 +3,27 @@ on: branches: [ staging, trying, master ] pull_request: -name: Test Suite +name: Continuous integration env: RUSTFLAGS: '--deny warnings' jobs: - ci-linux-test: + test: runs-on: ubuntu-latest strategy: matrix: - rust: [stable] + # All generated code should be running on stable now + rust: + - stable + - 1.54.0 # MSRV + - nightly - include: - - rust: 1.54.0 - TARGET: x86_64-unknown-linux-gnu - - # Test nightly but don't fail - - rust: nightly - experimental: true - TARGET: x86_64-unknown-linux-gnu + # The default target we're compiling on and for + target: + - x86_64-unknown-linux-gnu + - thumbv6m-none-eabi + - thumbv7m-none-eabi steps: - uses: actions/checkout@v2 @@ -30,8 +31,13 @@ jobs: with: profile: minimal toolchain: ${{ matrix.rust }} - target: ${{ matrix.TARGET }} + target: ${{ matrix.target }} override: true - - uses: actions-rs/cargo@v1 - with: - command: test + + - run: sed -i '/nightly-only/d' Cargo.toml + if: matrix.toolchain != 'nightly' + + - run: cargo check --target=${{ matrix.target }} + + - run: cargo test --target=${{ matrix.target }} + if: contains(matrix.target, 'linux') diff --git a/Cargo.toml b/Cargo.toml index 230893d82..4322dde4e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,23 +1,10 @@ -[package] -authors = [ - "The Embedded HAL Team ", - "Jorge Aparicio ", - "Jonathan 'theJPster' Pallant " -] -categories = ["asynchronous", "embedded", "hardware-support", "no-std"] -description = " A Hardware Abstraction Layer (HAL) for embedded systems " -documentation = "https://docs.rs/embedded-hal" -edition = "2018" -keywords = ["hal", "IO"] -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.8" - -[dependencies] -nb = "1" +[workspace] -[dev-dependencies.stm32f1] -version = "0.14" -features = ["stm32f103", "rt"] +# CI removes lines containing 'nightly-only' when not building with nightly. +members = [ + "embedded-hal", + "embedded-hal-async", # nightly-only + "embedded-hal-nb", + "embedded-hal-bus", + "embedded-can", +] diff --git a/README.md b/README.md index 03f5bd1ce..9b83eea6d 100644 --- a/README.md +++ b/README.md @@ -1,17 +1,8 @@ -[![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) - # `embedded-hal` > A Hardware Abstraction Layer (HAL) for embedded systems -This project is developed and maintained by the [HAL team][team]. - -## [API reference] - -[API reference]: https://docs.rs/embedded-hal +This project is developed and maintained by the [HAL team][https://github.com/rust-embedded/wg#the-hal-team]. ## Scope @@ -26,14 +17,24 @@ platforms (e.g. Cortex-M microcontrollers, AVR microcontrollers, embedded Linux, The advantage for application developers is that by adopting `embedded-hal` they can unlock all these drivers for their platform. -`embedded-hal` is not tied to a specific execution model like blocking or non-blocking. - For functionality that goes beyond what is provided by `embedded-hal`, users are encouraged to use the target platform directly. Abstractions of common functionality can be proposed to be included into `embedded-hal` as described [in this guide](docs/how-to-add-a-new-trait.md), though. See more about the design goals in [this documentation section](https://docs.rs/embedded-hal/latest/embedded_hal/#design-goals). +## Crates + +The main `embedded-hal` project is not tied to a specific execution model like blocking or non-blocking. + +| 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 | + ## Releases At the moment we are working towards a `1.0.0` release (see [#177]). During this process we will @@ -94,9 +95,6 @@ dual licensed as above, without any additional terms or conditions. ## Code of Conduct -Contribution to this crate is organized under the terms of the [Rust Code of -Conduct][CoC], the maintainer of this crate, the [HAL team][team], promises +Contribution to this repository is organized under the terms of the [Rust Code of +Conduct](CODE_OF_CONDUCT.md), the maintainers of this repository, the [HAL team](https://github.com/rust-embedded/wg#the-hal-team), promise to intervene to uphold that code of conduct. - -[CoC]: CODE_OF_CONDUCT.md -[team]: https://github.com/rust-embedded/wg#the-hal-team diff --git a/embedded-can/Cargo.toml b/embedded-can/Cargo.toml new file mode 100644 index 000000000..47b5ef404 --- /dev/null +++ b/embedded-can/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "embedded-can" +version = "0.4.0" +edition = "2018" + +description = "HAL traits for Controller Area Network (CAN) devices." +categories = ["embedded", "hardware-support", "no-std"] +documentation = "https://docs.rs/embedded-can" +keywords = ["hal", "IO"] +license = "MIT OR Apache-2.0" +readme = "README.md" +repository = "https://github.com/rust-embedded/embedded-hal" + +[dependencies] +embedded-hal = { version = "=1.0.0-alpha.8", path = "../embedded-hal" } +nb = "1" diff --git a/embedded-can/README.md b/embedded-can/README.md new file mode 100644 index 000000000..fc3a90ab2 --- /dev/null +++ b/embedded-can/README.md @@ -0,0 +1,37 @@ +[![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) + +# `embedded-can` + +An embedded Controller Area Network (CAN) abstraction layer. This crate defines generic traits to be implemented by CAN driver and MCU HAL crates. + +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-can + +## Minimum Supported Rust Version (MSRV) + +This crate is guaranteed to compile on stable Rust 1.54 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) + +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/src/can/blocking.rs b/embedded-can/src/blocking.rs similarity index 87% rename from src/can/blocking.rs rename to embedded-can/src/blocking.rs index b13885abe..943cfa7a3 100644 --- a/src/can/blocking.rs +++ b/embedded-can/src/blocking.rs @@ -3,10 +3,10 @@ /// A blocking CAN interface that is able to transmit and receive frames. pub trait Can { /// Associated frame type. - type Frame: crate::can::Frame; + type Frame: crate::Frame; /// Associated error type. - type Error: crate::can::Error; + type Error: crate::Error; /// Puts a frame in the transmit buffer. Blocks until space is available in /// the transmit buffer. diff --git a/src/can/id.rs b/embedded-can/src/id.rs similarity index 100% rename from src/can/id.rs rename to embedded-can/src/id.rs diff --git a/src/can/mod.rs b/embedded-can/src/lib.rs similarity index 98% rename from src/can/mod.rs rename to embedded-can/src/lib.rs index b70212bec..db80c455b 100644 --- a/src/can/mod.rs +++ b/embedded-can/src/lib.rs @@ -1,4 +1,7 @@ -//! Controller Area Network +//! Controller Area Network (CAN) traits + +#![warn(missing_docs)] +#![no_std] pub mod blocking; pub mod nb; diff --git a/src/can/nb.rs b/embedded-can/src/nb.rs similarity index 93% rename from src/can/nb.rs rename to embedded-can/src/nb.rs index 2ab6050a5..b17894602 100644 --- a/src/can/nb.rs +++ b/embedded-can/src/nb.rs @@ -3,10 +3,10 @@ /// A CAN interface that is able to transmit and receive frames. pub trait Can { /// Associated frame type. - type Frame: crate::can::Frame; + type Frame: crate::Frame; /// Associated error type. - type Error: crate::can::Error; + type Error: crate::Error; /// Puts a frame in the transmit buffer to be sent on the bus. /// diff --git a/embedded-hal-async/Cargo.toml b/embedded-hal-async/Cargo.toml index 198825e4b..d3ec056cf 100644 --- a/embedded-hal-async/Cargo.toml +++ b/embedded-hal-async/Cargo.toml @@ -14,4 +14,4 @@ repository = "https://github.com/rust-embedded/embedded-hal" version = "0.1.0-alpha.1" [dependencies] -embedded-hal = { version = "=1.0.0-alpha.8", path = ".." } +embedded-hal = { version = "=1.0.0-alpha.8", path = "../embedded-hal" } diff --git a/embedded-hal-async/README.md b/embedded-hal-async/README.md index f2347f841..05153e593 100644 --- a/embedded-hal-async/README.md +++ b/embedded-hal-async/README.md @@ -9,12 +9,12 @@ An asynchronous Hardware Abstraction Layer (HAL) for embedded systems. -This crate contains asynchronous versions of the [`embedded-hal`] traits and shares its scope and [design goals]. -The purpose of this crate is to iterate over these trait versions before integrating them into [`embedded-hal`]. +This crate contains asynchronous versions of the [`embedded-hal`](https://crates.io/crates/embedded-hal) traits and shares its scope and [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][team]. +This project is developed and maintained by the [HAL team][https://github.com/rust-embedded/wg#the-hal-team]. ## [API reference] @@ -42,14 +42,3 @@ at your option. 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. - -## Code of Conduct - -Contribution to this crate is organized under the terms of the [Rust Code of -Conduct][CoC], the maintainer of this crate, the [HAL team][team], promises -to intervene to uphold that code of conduct. - -[CoC]: ../CODE_OF_CONDUCT.md -[team]: https://github.com/rust-embedded/wg#the-hal-team -[`embedded-hal`]: https://crates.io/crates/embedded-hal -[design goals]: https://docs.rs/embedded-hal/latest/embedded_hal/#design-goals \ No newline at end of file diff --git a/embedded-hal-async/src/i2c.rs b/embedded-hal-async/src/i2c.rs index 51b6139ac..30133ce12 100644 --- a/embedded-hal-async/src/i2c.rs +++ b/embedded-hal-async/src/i2c.rs @@ -17,7 +17,7 @@ //! `SevenBitAddress` has been set as default mode and thus can be omitted if desired. use core::future::Future; -pub use embedded_hal::i2c::blocking::Operation; +pub use embedded_hal::i2c::Operation; pub use embedded_hal::i2c::{ AddressMode, Error, ErrorKind, ErrorType, NoAcknowledgeSource, SevenBitAddress, TenBitAddress, }; diff --git a/embedded-hal-async/src/spi.rs b/embedded-hal-async/src/spi.rs index da4f9cc7c..2b33af751 100644 --- a/embedded-hal-async/src/spi.rs +++ b/embedded-hal-async/src/spi.rs @@ -2,10 +2,11 @@ use core::{fmt::Debug, future::Future}; +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, }; -use embedded_hal::{digital::blocking::OutputPin, spi::blocking}; type ReadFuture<'a, T, Word> where diff --git a/embedded-hal-bus/Cargo.toml b/embedded-hal-bus/Cargo.toml index 17037163a..78c759f56 100644 --- a/embedded-hal-bus/Cargo.toml +++ b/embedded-hal-bus/Cargo.toml @@ -14,4 +14,4 @@ repository = "https://github.com/rust-embedded/embedded-hal" version = "0.1.0-alpha.0" [dependencies] -embedded-hal = { version = "=1.0.0-alpha.8", path = ".." } +embedded-hal = { version = "=1.0.0-alpha.8", path = "../embedded-hal" } diff --git a/embedded-hal-bus/README.md b/embedded-hal-bus/README.md index b95144c80..85ad7cd05 100644 --- a/embedded-hal-bus/README.md +++ b/embedded-hal-bus/README.md @@ -5,7 +5,7 @@ # `embedded-hal-bus` -Bus/Device connection mechanisms for [`embedded-hal`], a Hardware Abstraction Layer (HAL) for embedded systems. +Bus/Device connection mechanisms 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. @@ -18,7 +18,7 @@ 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). -This project is developed and maintained by the [HAL team][team]. +This project is developed and maintained by the [HAL team][https://github.com/rust-embedded/wg#the-hal-team]. ## [API reference] @@ -48,13 +48,3 @@ at your option. 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. - -## Code of Conduct - -Contribution to this crate is organized under the terms of the [Rust Code of -Conduct][CoC], the maintainer of this crate, the [HAL team][team], promises -to intervene to uphold that code of conduct. - -[CoC]: ../CODE_OF_CONDUCT.md -[team]: https://github.com/rust-embedded/wg#the-hal-team -[`embedded-hal`]: https://crates.io/crates/embedded-hal \ No newline at end of file diff --git a/embedded-hal-bus/src/spi.rs b/embedded-hal-bus/src/spi.rs index 9302fffc8..22ef300be 100644 --- a/embedded-hal-bus/src/spi.rs +++ b/embedded-hal-bus/src/spi.rs @@ -1,86 +1,78 @@ //! SPI bus sharing mechanisms. -/// SPI bus sharing with blocking traits -pub mod blocking { - use core::fmt::Debug; - use embedded_hal::{ - digital::blocking::OutputPin, - spi::{ - blocking::{SpiBusFlush, SpiDevice}, - Error, ErrorKind, ErrorType, - }, - }; +use core::fmt::Debug; +use embedded_hal::digital::OutputPin; +use embedded_hal::spi::{Error, ErrorKind, ErrorType, SpiBusFlush, SpiDevice}; - /// 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), - } +/// 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, - } +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::blocking::SpiBus), - /// ideal for when no sharing is required (only one SPI device is present on the bus). - pub struct ExclusiveDevice { - bus: BUS, - cs: CS, - } +/// [`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), +/// 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 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 ErrorType for ExclusiveDevice +where + BUS: ErrorType, + CS: OutputPin, +{ + type Error = ExclusiveDeviceError; +} - impl SpiDevice for ExclusiveDevice - where - BUS: SpiBusFlush, - CS: OutputPin, - { - type Bus = BUS; +impl SpiDevice for ExclusiveDevice +where + BUS: SpiBusFlush, + CS: OutputPin, +{ + type Bus = BUS; - fn transaction( - &mut self, - f: impl FnOnce(&mut Self::Bus) -> Result::Error>, - ) -> Result { - self.cs.set_low().map_err(ExclusiveDeviceError::Cs)?; + fn transaction( + &mut self, + f: impl FnOnce(&mut Self::Bus) -> Result::Error>, + ) -> Result { + self.cs.set_low().map_err(ExclusiveDeviceError::Cs)?; - let f_res = f(&mut self.bus); + let f_res = f(&mut self.bus); - // On failure, it's important to still flush and deassert CS. - let flush_res = self.bus.flush(); - let cs_res = self.cs.set_high(); + // 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)?; - flush_res.map_err(ExclusiveDeviceError::Spi)?; - cs_res.map_err(ExclusiveDeviceError::Cs)?; + let f_res = f_res.map_err(ExclusiveDeviceError::Spi)?; + flush_res.map_err(ExclusiveDeviceError::Spi)?; + cs_res.map_err(ExclusiveDeviceError::Cs)?; - Ok(f_res) - } + Ok(f_res) } } diff --git a/embedded-hal-nb/Cargo.toml b/embedded-hal-nb/Cargo.toml new file mode 100644 index 000000000..a7b644541 --- /dev/null +++ b/embedded-hal-nb/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "embedded-hal-nb" +version = "1.0.0-alpha.1" +edition = "2018" + +categories = ["embedded", "hardware-support", "no-std"] +description = "Non-blocking Hardware Abstraction Layer (HAL) for embedded systems using the `nb` crate." +documentation = "https://docs.rs/embedded-hal-nb" +keywords = ["hal", "IO"] +license = "MIT OR Apache-2.0" +readme = "README.md" +repository = "https://github.com/rust-embedded/embedded-hal" + +[dependencies] +embedded-hal = { version = "=1.0.0-alpha.8", path = "../embedded-hal" } +nb = "1" + +[dev-dependencies.stm32f1] +version = "0.14" +features = ["stm32f103", "rt"] diff --git a/embedded-hal-nb/README.md b/embedded-hal-nb/README.md new file mode 100644 index 000000000..82f836651 --- /dev/null +++ b/embedded-hal-nb/README.md @@ -0,0 +1,39 @@ +[![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) + +# `embedded-hal-nb` + +A non-blocking Hardware Abstraction Layer (HAL) for embedded systems, using the `nb` crate. + +This crate contains versions of some [`embedded-hal`](https://crates.io/crates/embedded-hal) traits using `nb`, 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]. + +## [API reference] + +[API reference]: https://docs.rs/embedded-hal-nb + +## Minimum Supported Rust Version (MSRV) + +This crate is guaranteed to compile on stable Rust 1.54 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) + +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/src/lib.rs b/embedded-hal-nb/src/lib.rs similarity index 63% rename from src/lib.rs rename to embedded-hal-nb/src/lib.rs index a178c51d6..8262d008b 100644 --- a/src/lib.rs +++ b/embedded-hal-nb/src/lib.rs @@ -1,69 +1,6 @@ -//! A Hardware Abstraction Layer (HAL) for embedded systems +//! Non-blocking Hardware Abstraction Layer (HAL) traits for embedded systems, using the `nb` crate. //! -//! **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"`. -//! -//! # 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`] 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! -//! -//! # Detailed design -//! -//! ## Traits -//! -//! The HAL is specified as traits to allow generic programming. These traits make use of the +//! The `embedded-hal-nb` traits make use of the //! [`nb`][] crate (*please go read that crate documentation before continuing*) to abstract over //! the asynchronous model and to also provide a blocking operation mode. //! @@ -72,7 +9,7 @@ //! Here's how a HAL trait may look like: //! //! ``` -//! use embedded_hal::nb; +//! use embedded_hal_nb; //! //! /// A serial interface //! pub trait Serial { @@ -98,8 +35,6 @@ //! those cases `nb::Result<_, Infallible>` is used. //! //! ``` -//! use embedded_hal::nb; -//! //! # use std as core; //! use ::core::convert::Infallible; //! @@ -130,8 +65,8 @@ //! // crate: stm32f1xx-hal //! // An implementation of the `embedded-hal` traits for STM32F1xx microcontrollers //! -//! use embedded_hal as hal; -//! use hal::nb; +//! use embedded_hal_nb::serial; +//! use nb; //! //! // device crate //! use stm32f1::stm32f103::USART1; @@ -143,11 +78,11 @@ //! // convenience type alias //! pub type Serial1 = Serial; //! -//! impl hal::serial::ErrorType for Serial { -//! type Error = hal::serial::ErrorKind; +//! impl serial::ErrorType for Serial { +//! type Error = serial::ErrorKind; //! } //! -//! impl hal::serial::nb::Read for Serial { +//! impl embedded_hal_nb::serial::Read for Serial { //! fn read(&mut self) -> nb::Result { //! // read the status register //! let isr = self.usart.sr.read(); @@ -167,7 +102,7 @@ //! } //! } //! -//! impl hal::serial::nb::Write for Serial { +//! impl embedded_hal_nb::serial::Write for Serial { //! fn write(&mut self, byte: u8) -> nb::Result<(), Self::Error> { //! // Similar to the `read` implementation //! # Ok(()) @@ -195,9 +130,9 @@ //! fashion: //! //! ``` -//! use crate::stm32f1xx_hal::Serial1; -//! use embedded_hal::serial::nb::Write; -//! use embedded_hal::nb::block; +//! use stm32f1xx_hal::Serial1; +//! use embedded_hal_nb::serial::Write; +//! use nb::block; //! //! # fn main() { //! let mut serial: Serial1 = { @@ -213,7 +148,7 @@ //! # } //! //! # mod stm32f1xx_hal { -//! # use embedded_hal::nb; +//! # use embedded_hal_nb; //! # use core::convert::Infallible; //! # pub struct Serial1; //! # impl Serial1 { @@ -240,13 +175,12 @@ //! - Write a whole buffer to a serial device in blocking a fashion. //! //! ``` -//! use embedded_hal as hal; -//! use hal::nb::block; -//! use hal::serial::nb::Write; +//! use embedded_hal_nb::serial::Write; +//! use nb::block; //! //! fn write_all(serial: &mut S, buffer: &[u8]) -> Result<(), S::Error> //! where -//! S: hal::serial::nb::Write +//! S: Write //! { //! for &byte in buffer { //! block!(serial.write(byte))?; @@ -262,14 +196,12 @@ //! //! ``` //! # use std as core; -//! use embedded_hal as hal; -//! use hal::nb; -//! -//! use hal::serial::{ErrorKind, nb::Write}; +//! use embedded_hal_nb::serial::{ErrorKind, Write}; +//! use nb::block; //! //! fn flush(serial: &mut S, cb: &mut CircularBuffer) //! where -//! S: hal::serial::nb::Write, +//! S: Write, //! { //! loop { //! if let Some(byte) = cb.peek() { @@ -333,12 +265,12 @@ //! # fn deref_mut(&mut self) -> &mut T { self.0 } //! # } //! # struct Serial1; -//! # impl hal::serial::ErrorType for Serial1 { +//! # impl embedded_hal_nb::serial::ErrorType for Serial1 { //! # type Error = ErrorKind; //! # } -//! # impl hal::serial::nb::Write for Serial1 { -//! # fn write(&mut self, _: u8) -> nb::Result<(), Self::Error> { Err(::nb::Error::WouldBlock) } -//! # fn flush(&mut self) -> nb::Result<(), Self::Error> { Err(::nb::Error::WouldBlock) } +//! # impl embedded_hal_nb::serial::Write for Serial1 { +//! # fn write(&mut self, _: u8) -> nb::Result<(), Self::Error> { Err(nb::Error::WouldBlock) } +//! # fn flush(&mut self) -> nb::Result<(), Self::Error> { Err(nb::Error::WouldBlock) } //! # } //! # struct CircularBuffer; //! # impl CircularBuffer { @@ -353,19 +285,7 @@ #![warn(missing_docs)] #![no_std] -pub mod fmt; pub use nb; -pub mod can; -pub mod delay; -pub mod digital; -pub mod i2c; + pub mod serial; pub mod spi; - -mod private { - use crate::i2c::{SevenBitAddress, TenBitAddress}; - pub trait Sealed {} - - impl Sealed for SevenBitAddress {} - impl Sealed for TenBitAddress {} -} diff --git a/src/serial/nb.rs b/embedded-hal-nb/src/serial.rs similarity index 62% rename from src/serial/nb.rs rename to embedded-hal-nb/src/serial.rs index dc2dde74e..e01803ab1 100644 --- a/src/serial/nb.rs +++ b/embedded-hal-nb/src/serial.rs @@ -1,10 +1,12 @@ //! Serial interface +pub use embedded_hal::serial::{Error, ErrorKind, ErrorType}; + /// Read half of a serial interface /// /// Some serial interfaces support different data sizes (8 bits, 9 bits, etc.); /// This can be encoded in this trait via the `Word` type parameter. -pub trait Read: super::ErrorType { +pub trait Read: ErrorType { /// Reads a single word from the serial interface fn read(&mut self) -> nb::Result; } @@ -16,7 +18,7 @@ impl, Word: Copy> Read for &mut T { } /// Write half of a serial interface -pub trait Write: super::ErrorType { +pub trait Write: ErrorType { /// Writes a single word to the serial interface fn write(&mut self, word: Word) -> nb::Result<(), Self::Error>; @@ -33,3 +35,21 @@ impl, Word: Copy> Write for &mut T { T::flush(self) } } + +/// Implementation of `core::fmt::Write` for the HAL's `serial::Write`. +/// +/// TODO write example of usage + +impl core::fmt::Write + for dyn Write + '_ +where + Word: Copy + From, +{ + fn write_str(&mut self, s: &str) -> core::fmt::Result { + let _ = s + .bytes() + .map(|c| nb::block!(self.write(Word::from(c)))) + .last(); + Ok(()) + } +} diff --git a/src/spi/nb.rs b/embedded-hal-nb/src/spi.rs similarity index 89% rename from src/spi/nb.rs rename to embedded-hal-nb/src/spi.rs index 5257f930a..a9e7c94f0 100644 --- a/src/spi/nb.rs +++ b/embedded-hal-nb/src/spi.rs @@ -1,8 +1,10 @@ //! SPI master mode traits using `nb`. -use super::ErrorType; +pub use embedded_hal::spi::{ + Error, ErrorKind, ErrorType, Mode, Phase, Polarity, MODE_0, MODE_1, MODE_2, MODE_3, +}; -/// Full duplex (master mode) +/// Full duplex SPI (master mode) /// /// # Notes /// diff --git a/CHANGELOG.md b/embedded-hal/CHANGELOG.md similarity index 97% rename from CHANGELOG.md rename to embedded-hal/CHANGELOG.md index 87bde6573..67813d6e0 100644 --- a/CHANGELOG.md +++ b/embedded-hal/CHANGELOG.md @@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] +### Changed + +- The `embedded-hal` crate now contains blocking traits only. Import paths no longer contain `::blocking`. `nb` traits are now available in a separate `embedded-hal-nb` crate. +- Moved CAN traits to a separate `embedded-can` crate. + ### Added - Implement `PartialOrd`, `Ord`, `Hash` for `can::StandardId`, `can::ExtendedId` and `can::Id` according to CAN bus arbitration rules diff --git a/embedded-hal/Cargo.toml b/embedded-hal/Cargo.toml new file mode 100644 index 000000000..70544070b --- /dev/null +++ b/embedded-hal/Cargo.toml @@ -0,0 +1,16 @@ +[package] +authors = [ + "The Embedded HAL Team ", + "Jorge Aparicio ", + "Jonathan 'theJPster' Pallant " +] +categories = ["asynchronous", "embedded", "hardware-support", "no-std"] +description = " A Hardware Abstraction Layer (HAL) for embedded systems " +documentation = "https://docs.rs/embedded-hal" +edition = "2018" +keywords = ["hal", "IO"] +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.8" diff --git a/LICENSE-APACHE b/embedded-hal/LICENSE-APACHE similarity index 100% rename from LICENSE-APACHE rename to embedded-hal/LICENSE-APACHE diff --git a/LICENSE-MIT b/embedded-hal/LICENSE-MIT similarity index 100% rename from LICENSE-MIT rename to embedded-hal/LICENSE-MIT diff --git a/embedded-hal/README.md b/embedded-hal/README.md new file mode 100644 index 000000000..3e36460d3 --- /dev/null +++ b/embedded-hal/README.md @@ -0,0 +1,37 @@ +[![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) + +# `embedded-hal` + +> A Hardware Abstraction Layer (HAL) for embedded systems + +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 + +## Minimum Supported Rust Version (MSRV) + +This crate is guaranteed to compile on stable Rust 1.54 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) + +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-hal/src/delay.rs b/embedded-hal/src/delay.rs new file mode 100644 index 000000000..52c1fdd5a --- /dev/null +++ b/embedded-hal/src/delay.rs @@ -0,0 +1,37 @@ +//! Delays + +/// 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>; + + /// 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> { + for _ in 0..ms { + self.delay_us(1000)?; + } + + Ok(()) + } +} + +impl DelayUs for &mut T +where + T: DelayUs, +{ + type Error = T::Error; + + fn delay_us(&mut self, us: u32) -> Result<(), Self::Error> { + T::delay_us(self, us) + } + + fn delay_ms(&mut self, ms: u32) -> Result<(), Self::Error> { + T::delay_ms(self, ms) + } +} diff --git a/embedded-hal/src/digital.rs b/embedded-hal/src/digital.rs new file mode 100644 index 000000000..c1fb82fe6 --- /dev/null +++ b/embedded-hal/src/digital.rs @@ -0,0 +1,150 @@ +//! Digital I/O + +use core::{convert::From, ops::Not}; + +/// GPIO 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; +} + +impl ErrorType for &T { + type Error = T::Error; +} +impl ErrorType for &mut T { + type Error = T::Error; +} + +/// Digital output pin state +/// +/// Conversion from `bool` and logical negation are also implemented +/// for this type. +/// ```rust +/// # use embedded_hal::digital::PinState; +/// let state = PinState::from(false); +/// assert_eq!(state, PinState::Low); +/// assert_eq!(!state, PinState::High); +/// ``` +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +pub enum PinState { + /// Low pin state + Low, + /// High pin state + High, +} + +impl From for PinState { + fn from(value: bool) -> Self { + match value { + false => PinState::Low, + true => PinState::High, + } + } +} + +impl Not for PinState { + type Output = PinState; + + fn not(self) -> Self::Output { + match self { + PinState::High => PinState::Low, + PinState::Low => PinState::High, + } + } +} + +/// Single digital push-pull output pin +pub trait OutputPin: ErrorType { + /// Drives the pin low + /// + /// *NOTE* the actual electrical state of the pin may not actually be low, e.g. due to external + /// electrical sources + fn set_low(&mut self) -> Result<(), Self::Error>; + + /// Drives the pin high + /// + /// *NOTE* the actual electrical state of the pin may not actually be high, e.g. due to external + /// electrical sources + fn set_high(&mut self) -> Result<(), Self::Error>; + + /// 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 + fn set_state(&mut self, state: PinState) -> Result<(), Self::Error> { + match state { + PinState::Low => self.set_low(), + PinState::High => self.set_high(), + } + } +} + +impl OutputPin for &mut T { + fn set_low(&mut self) -> Result<(), Self::Error> { + T::set_low(self) + } + + fn set_high(&mut self) -> Result<(), Self::Error> { + T::set_high(self) + } + + 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 +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; + + /// Is the pin in drive low mode? + /// + /// *NOTE* this does *not* read the electrical state of the pin + fn is_set_low(&self) -> Result; +} + +impl StatefulOutputPin for &mut T { + fn is_set_high(&self) -> Result { + T::is_set_high(self) + } + + fn is_set_low(&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 { + fn toggle(&mut self) -> Result<(), Self::Error> { + T::toggle(self) + } +} + +/// Single digital input pin +pub trait InputPin: ErrorType { + /// Is the input pin high? + fn is_high(&self) -> Result; + + /// Is the input pin low? + fn is_low(&self) -> Result; +} + +impl InputPin for &T { + fn is_high(&self) -> Result { + T::is_high(self) + } + + fn is_low(&self) -> Result { + T::is_low(self) + } +} diff --git a/src/i2c.rs b/embedded-hal/src/i2c.rs similarity index 54% rename from src/i2c.rs rename to embedded-hal/src/i2c.rs index 9b6298da4..f14348887 100644 --- a/src/i2c.rs +++ b/embedded-hal/src/i2c.rs @@ -22,7 +22,7 @@ //! Here is an example of an embedded-hal implementation of the `Write` trait //! for both modes: //! ``` -//! # use embedded_hal::i2c::{ErrorKind, ErrorType, SevenBitAddress, TenBitAddress, blocking::{I2c, Operation}}; +//! # 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; //! @@ -97,7 +97,7 @@ //! For demonstration purposes the address mode parameter has been omitted in this example. //! //! ``` -//! # use embedded_hal::i2c::{blocking::I2c, Error}; +//! # use embedded_hal::i2c::{I2c, Error}; //! const ADDR: u8 = 0x15; //! # const TEMP_REGISTER: u8 = 0x1; //! pub struct TemperatureSensorDriver { @@ -120,7 +120,7 @@ //! ### Device driver compatible only with 10-bit addresses //! //! ``` -//! # use embedded_hal::i2c::{Error, TenBitAddress, blocking::I2c}; +//! # use embedded_hal::i2c::{Error, TenBitAddress, I2c}; //! const ADDR: u16 = 0x158; //! # const TEMP_REGISTER: u8 = 0x1; //! pub struct TemperatureSensorDriver { @@ -256,202 +256,196 @@ impl AddressMode for SevenBitAddress {} impl AddressMode for TenBitAddress {} -/// Blocking I2C traits -pub mod blocking { - - use super::{AddressMode, ErrorType, SevenBitAddress}; +/// Transactional I2C operation. +/// +/// Several operations can be combined as part of a transaction. +#[derive(Debug, PartialEq)] +pub enum Operation<'a> { + /// Read data into the provided buffer + Read(&'a mut [u8]), + /// Write data from the provided buffer + Write(&'a [u8]), +} - /// Transactional I2C operation. +/// Blocking I2C +pub trait I2c: ErrorType { + /// Reads enough bytes from slave with `address` to fill `buffer` /// - /// Several operations can be combined as part of a transaction. - #[derive(Debug, PartialEq)] - pub enum Operation<'a> { - /// Read data into the provided buffer - Read(&'a mut [u8]), - /// Write data from the provided buffer - Write(&'a [u8]), - } - - /// Blocking I2C - pub trait I2c: ErrorType { - /// Reads enough bytes from slave with `address` to fill `buffer` - /// - /// # I2C Events (contract) - /// - /// ``` text - /// Master: ST SAD+R MAK MAK ... NMAK SP - /// Slave: SAK B0 B1 ... BN - /// ``` - /// - /// Where - /// - /// - `ST` = start condition - /// - `SAD+R` = slave address followed by bit 1 to indicate reading - /// - `SAK` = slave acknowledge - /// - `Bi` = ith byte of data - /// - `MAK` = master acknowledge - /// - `NMAK` = master no acknowledge - /// - `SP` = stop condition - fn read(&mut self, address: A, buffer: &mut [u8]) -> Result<(), Self::Error>; - - /// Writes bytes to slave with address `address` - /// - /// # I2C Events (contract) - /// - /// ``` text - /// Master: ST SAD+W B0 B1 ... BN SP - /// Slave: SAK SAK SAK ... SAK - /// ``` - /// - /// Where - /// - /// - `ST` = start condition - /// - `SAD+W` = slave address followed by bit 0 to indicate writing - /// - `SAK` = slave acknowledge - /// - `Bi` = ith byte of data - /// - `SP` = stop condition - fn write(&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* - /// - /// # I2C Events (contract) - /// - /// ``` text - /// Master: ST SAD+W O0 O1 ... OM SR SAD+R MAK MAK ... NMAK SP - /// Slave: SAK SAK SAK ... SAK SAK I0 I1 ... IN - /// ``` - /// - /// Where - /// - /// - `ST` = start condition - /// - `SAD+W` = slave address followed by bit 0 to indicate writing - /// - `SAK` = slave acknowledge - /// - `Oi` = ith outgoing byte of data - /// - `SR` = repeated start condition - /// - `SAD+R` = slave address followed by bit 1 to indicate reading - /// - `Ii` = ith incoming byte of data - /// - `MAK` = master acknowledge - /// - `NMAK` = master no acknowledge - /// - `SP` = stop condition - fn write_read( - &mut self, - address: A, - bytes: &[u8], - 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; + /// # I2C Events (contract) + /// + /// ``` text + /// Master: ST SAD+R MAK MAK ... NMAK SP + /// Slave: SAK B0 B1 ... BN + /// ``` + /// + /// Where + /// + /// - `ST` = start condition + /// - `SAD+R` = slave address followed by bit 1 to indicate reading + /// - `SAK` = slave acknowledge + /// - `Bi` = ith byte of data + /// - `MAK` = master acknowledge + /// - `NMAK` = master no acknowledge + /// - `SP` = stop condition + fn read(&mut self, address: A, buffer: &mut [u8]) -> Result<(), Self::Error>; + + /// Writes bytes to slave with address `address` + /// + /// # I2C Events (contract) + /// + /// ``` text + /// Master: ST SAD+W B0 B1 ... BN SP + /// Slave: SAK SAK SAK ... SAK + /// ``` + /// + /// Where + /// + /// - `ST` = start condition + /// - `SAD+W` = slave address followed by bit 0 to indicate writing + /// - `SAK` = slave acknowledge + /// - `Bi` = ith byte of data + /// - `SP` = stop condition + fn write(&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; - /// Execute the provided operations on the I2C bus. - /// - /// Transaction contract: - /// - Before executing the first operation an ST is sent automatically. This is followed by SAD+R/W as appropriate. - /// - Data from adjacent operations of the same type are sent after each other without an SP or SR. - /// - Between adjacent operations of a different type an SR and SAD+R/W is sent. - /// - After executing the last operation an SP is sent automatically. - /// - If the last operation is a `Read` the master does not send an acknowledge for the last byte. - /// - /// - `ST` = start condition - /// - `SAD+R/W` = slave address followed by bit 1 to indicate reading or 0 to indicate writing - /// - `SR` = repeated start condition - /// - `SP` = stop condition - fn transaction<'a>( - &mut self, - address: A, - operations: &mut [Operation<'a>], - ) -> 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) + /// + /// ``` text + /// Master: ST SAD+W O0 O1 ... OM SR SAD+R MAK MAK ... NMAK SP + /// Slave: SAK SAK SAK ... SAK SAK I0 I1 ... IN + /// ``` + /// + /// Where + /// + /// - `ST` = start condition + /// - `SAD+W` = slave address followed by bit 0 to indicate writing + /// - `SAK` = slave acknowledge + /// - `Oi` = ith outgoing byte of data + /// - `SR` = repeated start condition + /// - `SAD+R` = slave address followed by bit 1 to indicate reading + /// - `Ii` = ith incoming byte of data + /// - `MAK` = master acknowledge + /// - `NMAK` = master no acknowledge + /// - `SP` = stop condition + fn write_read( + &mut self, + address: A, + bytes: &[u8], + 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: + /// - 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<'a>( + &mut self, + 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>; +} - /// 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 { + fn read(&mut self, address: A, buffer: &mut [u8]) -> Result<(), Self::Error> { + T::read(self, address, buffer) } - impl> I2c for &mut T { - fn read(&mut self, address: A, buffer: &mut [u8]) -> Result<(), Self::Error> { - T::read(self, address, buffer) - } - - fn write(&mut self, address: A, bytes: &[u8]) -> Result<(), Self::Error> { - T::write(self, address, bytes) - } + fn write(&mut self, address: A, bytes: &[u8]) -> Result<(), Self::Error> { + 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_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, - bytes: &[u8], - buffer: &mut [u8], - ) -> Result<(), Self::Error> { - T::write_read(self, address, bytes, buffer) - } + fn write_read( + &mut self, + address: A, + bytes: &[u8], + buffer: &mut [u8], + ) -> Result<(), Self::Error> { + 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 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, - operations: &mut [Operation<'a>], - ) -> Result<(), Self::Error> { - T::transaction(self, address, operations) - } + fn transaction<'a>( + &mut self, + address: A, + operations: &mut [Operation<'a>], + ) -> 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) - } + fn transaction_iter<'a, O>(&mut self, address: A, operations: O) -> Result<(), Self::Error> + where + O: IntoIterator>, + { + T::transaction_iter(self, address, operations) } } diff --git a/embedded-hal/src/lib.rs b/embedded-hal/src/lib.rs new file mode 100644 index 000000000..8b65ded47 --- /dev/null +++ b/embedded-hal/src/lib.rs @@ -0,0 +1,92 @@ +//! 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`] 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! + +#![warn(missing_docs)] +#![no_std] + +pub mod delay; +pub mod digital; +pub mod i2c; +pub mod serial; +pub mod spi; + +mod private { + use crate::i2c::{SevenBitAddress, TenBitAddress}; + pub trait Sealed {} + + impl Sealed for SevenBitAddress {} + impl Sealed for TenBitAddress {} +} diff --git a/src/serial/mod.rs b/embedded-hal/src/serial.rs similarity index 70% rename from src/serial/mod.rs rename to embedded-hal/src/serial.rs index 1dd4b23b5..f0c05f98b 100644 --- a/src/serial/mod.rs +++ b/embedded-hal/src/serial.rs @@ -1,8 +1,5 @@ //! Serial traits -pub mod blocking; -pub mod nb; - /// Serial error pub trait Error: core::fmt::Debug { /// Convert error to a generic serial error kind @@ -75,3 +72,29 @@ pub trait ErrorType { 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`] after this function returns. + /// + /// [`flush`]: #tymethod.flush + 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) + } +} diff --git a/src/spi/shared-bus.svg b/embedded-hal/src/spi-shared-bus.svg similarity index 100% rename from src/spi/shared-bus.svg rename to embedded-hal/src/spi-shared-bus.svg diff --git a/src/spi/blocking.rs b/embedded-hal/src/spi.rs similarity index 79% rename from src/spi/blocking.rs rename to embedded-hal/src/spi.rs index 67f8adc1a..72be4bb1b 100644 --- a/src/spi/blocking.rs +++ b/embedded-hal/src/spi.rs @@ -5,7 +5,7 @@ //! SPI allows sharing a single bus between many SPI devices. The SCK, MOSI and MISO lines are //! wired in parallel to all the devices, and each device gets a dedicated chip-select (CS) line from the MCU, like this: //! -#![doc= include_str!("shared-bus.svg")] +#![doc= include_str!("spi-shared-bus.svg")] //! //! CS is usually active-low. When CS is high (not asserted), SPI devices ignore all incoming data, and //! don't drive MISO. When CS is low (asserted), the device is active: reacts to incoming data on MOSI and @@ -59,7 +59,7 @@ //! By using [`SpiDevice`], your driver will cooperate nicely with other drivers for other devices in the same shared SPI bus. //! //! ``` -//! # use embedded_hal::spi::blocking::{SpiBus, SpiBusRead, SpiBusWrite, SpiDevice}; +//! # use embedded_hal::spi::{SpiBus, SpiBusRead, SpiBusWrite, SpiDevice}; //! pub struct MyDriver { //! spi: SPI, //! } @@ -98,7 +98,7 @@ //! a bus without CS pins. By requiring [`SpiBus`] you disallow sharing, ensuring correct operation. //! //! ``` -//! # use embedded_hal::spi::blocking::{SpiBus, SpiBusRead, SpiBusWrite}; +//! # use embedded_hal::spi::{SpiBus, SpiBusRead, SpiBusWrite}; //! pub struct MyDriver { //! spi: SPI, //! } @@ -167,7 +167,136 @@ //! 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. -use crate::spi::ErrorType; +use core::fmt::Debug; + +/// Clock polarity +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum Polarity { + /// Clock signal low when idle + IdleLow, + /// Clock signal high when idle + IdleHigh, +} + +/// Clock phase +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum Phase { + /// Data in "captured" on the first clock transition + CaptureOnFirstTransition, + /// Data in "captured" on the second clock transition + CaptureOnSecondTransition, +} + +/// SPI mode +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct Mode { + /// Clock polarity + pub polarity: Polarity, + /// Clock phase + pub phase: Phase, +} + +/// Helper for CPOL = 0, CPHA = 0 +pub const MODE_0: Mode = Mode { + polarity: Polarity::IdleLow, + phase: Phase::CaptureOnFirstTransition, +}; + +/// Helper for CPOL = 0, CPHA = 1 +pub const MODE_1: Mode = Mode { + polarity: Polarity::IdleLow, + phase: Phase::CaptureOnSecondTransition, +}; + +/// Helper for CPOL = 1, CPHA = 0 +pub const MODE_2: Mode = Mode { + polarity: Polarity::IdleHigh, + phase: Phase::CaptureOnFirstTransition, +}; + +/// Helper for CPOL = 1, CPHA = 1 +pub const MODE_3: Mode = Mode { + polarity: Polarity::IdleHigh, + phase: Phase::CaptureOnSecondTransition, +}; + +/// SPI error +pub trait Error: core::fmt::Debug { + /// 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 + /// code can act. + fn kind(&self) -> ErrorKind; +} + +impl Error for core::convert::Infallible { + fn kind(&self) -> ErrorKind { + match *self {} + } +} + +/// 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 +/// a mapping to these common SPI 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, + /// 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 + FrameFormat, + /// An error occured while asserting or deasserting the Chip Select pin. + ChipSelectFault, + /// 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::ModeFault => write!( + f, + "Multiple devices on the SPI bus are trying to drive the slave select pin" + ), + Self::FrameFormat => write!( + f, + "Received data does not conform to the peripheral configuration" + ), + Self::ChipSelectFault => write!( + f, + "An error occured while asserting or deasserting the Chip Select pin" + ), + Self::Other => write!( + f, + "A different error occurred. The original error may contain more information" + ), + } + } +} + +/// SPI error type trait +/// +/// This just defines the error type, to be used by the other SPI traits. +pub trait ErrorType { + /// Error type + type Error: Error; +} + +impl ErrorType for &mut T { + type Error = T::Error; +} /// SPI device trait /// diff --git a/src/delay.rs b/src/delay.rs deleted file mode 100644 index e30bc5378..000000000 --- a/src/delay.rs +++ /dev/null @@ -1,40 +0,0 @@ -//! Delays - -/// Blocking delay traits -pub mod blocking { - /// 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>; - - /// 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> { - for _ in 0..ms { - self.delay_us(1000)?; - } - - Ok(()) - } - } - - impl DelayUs for &mut T - where - T: DelayUs, - { - type Error = T::Error; - - fn delay_us(&mut self, us: u32) -> Result<(), Self::Error> { - T::delay_us(self, us) - } - - fn delay_ms(&mut self, ms: u32) -> Result<(), Self::Error> { - T::delay_ms(self, ms) - } - } -} diff --git a/src/digital.rs b/src/digital.rs deleted file mode 100644 index b0d67768b..000000000 --- a/src/digital.rs +++ /dev/null @@ -1,155 +0,0 @@ -//! Digital I/O - -use core::{convert::From, ops::Not}; - -/// GPIO 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; -} - -impl ErrorType for &T { - type Error = T::Error; -} -impl ErrorType for &mut T { - type Error = T::Error; -} - -/// Digital output pin state -/// -/// Conversion from `bool` and logical negation are also implemented -/// for this type. -/// ```rust -/// # use embedded_hal::digital::PinState; -/// let state = PinState::from(false); -/// assert_eq!(state, PinState::Low); -/// assert_eq!(!state, PinState::High); -/// ``` -#[derive(Debug, PartialEq, Eq, Clone, Copy)] -pub enum PinState { - /// Low pin state - Low, - /// High pin state - High, -} - -impl From for PinState { - fn from(value: bool) -> Self { - match value { - false => PinState::Low, - true => PinState::High, - } - } -} - -impl Not for PinState { - type Output = PinState; - - fn not(self) -> Self::Output { - match self { - PinState::High => PinState::Low, - PinState::Low => PinState::High, - } - } -} - -/// Blocking digital I/O traits -pub mod blocking { - use super::PinState; - - /// Single digital push-pull output pin - pub trait OutputPin: super::ErrorType { - /// Drives the pin low - /// - /// *NOTE* the actual electrical state of the pin may not actually be low, e.g. due to external - /// electrical sources - fn set_low(&mut self) -> Result<(), Self::Error>; - - /// Drives the pin high - /// - /// *NOTE* the actual electrical state of the pin may not actually be high, e.g. due to external - /// electrical sources - fn set_high(&mut self) -> Result<(), Self::Error>; - - /// 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 - fn set_state(&mut self, state: PinState) -> Result<(), Self::Error> { - match state { - PinState::Low => self.set_low(), - PinState::High => self.set_high(), - } - } - } - - impl OutputPin for &mut T { - fn set_low(&mut self) -> Result<(), Self::Error> { - T::set_low(self) - } - - fn set_high(&mut self) -> Result<(), Self::Error> { - T::set_high(self) - } - - 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 - 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; - - /// Is the pin in drive low mode? - /// - /// *NOTE* this does *not* read the electrical state of the pin - fn is_set_low(&self) -> Result; - } - - impl StatefulOutputPin for &mut T { - fn is_set_high(&self) -> Result { - T::is_set_high(self) - } - - fn is_set_low(&self) -> Result { - T::is_set_low(self) - } - } - - /// Output pin that can be toggled - pub trait ToggleableOutputPin: super::ErrorType { - /// Toggle pin output. - fn toggle(&mut self) -> Result<(), Self::Error>; - } - - impl ToggleableOutputPin for &mut T { - fn toggle(&mut self) -> Result<(), Self::Error> { - T::toggle(self) - } - } - - /// Single digital input pin - pub trait InputPin: super::ErrorType { - /// Is the input pin high? - fn is_high(&self) -> Result; - - /// Is the input pin low? - fn is_low(&self) -> Result; - } - - impl InputPin for &T { - fn is_high(&self) -> Result { - T::is_high(self) - } - - fn is_low(&self) -> Result { - T::is_low(self) - } - } -} diff --git a/src/fmt.rs b/src/fmt.rs deleted file mode 100644 index 7b89f71e4..000000000 --- a/src/fmt.rs +++ /dev/null @@ -1,18 +0,0 @@ -//! Implementation of `core::fmt::Write` for the HAL's `serial::Write`. -//! -//! TODO write example of usage -use core::fmt::{Result, Write}; - -impl Write - for dyn crate::serial::nb::Write + '_ -where - Word: Copy + From, -{ - fn write_str(&mut self, s: &str) -> Result { - let _ = s - .bytes() - .map(|c| nb::block!(self.write(Word::from(c)))) - .last(); - Ok(()) - } -} diff --git a/src/serial/blocking.rs b/src/serial/blocking.rs deleted file mode 100644 index c5cfad4f8..000000000 --- a/src/serial/blocking.rs +++ /dev/null @@ -1,27 +0,0 @@ -//! Blocking serial API - -/// Write half of a serial interface (blocking variant) -pub trait Write: super::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`] after this function returns. - /// - /// [`flush`]: #tymethod.flush - 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) - } -} diff --git a/src/spi/mod.rs b/src/spi/mod.rs deleted file mode 100644 index 85254e262..000000000 --- a/src/spi/mod.rs +++ /dev/null @@ -1,133 +0,0 @@ -//! SPI master mode traits. - -pub mod blocking; -pub mod nb; - -/// Clock polarity -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -pub enum Polarity { - /// Clock signal low when idle - IdleLow, - /// Clock signal high when idle - IdleHigh, -} - -/// Clock phase -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -pub enum Phase { - /// Data in "captured" on the first clock transition - CaptureOnFirstTransition, - /// Data in "captured" on the second clock transition - CaptureOnSecondTransition, -} - -/// SPI mode -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -pub struct Mode { - /// Clock polarity - pub polarity: Polarity, - /// Clock phase - pub phase: Phase, -} - -/// Helper for CPOL = 0, CPHA = 0 -pub const MODE_0: Mode = Mode { - polarity: Polarity::IdleLow, - phase: Phase::CaptureOnFirstTransition, -}; - -/// Helper for CPOL = 0, CPHA = 1 -pub const MODE_1: Mode = Mode { - polarity: Polarity::IdleLow, - phase: Phase::CaptureOnSecondTransition, -}; - -/// Helper for CPOL = 1, CPHA = 0 -pub const MODE_2: Mode = Mode { - polarity: Polarity::IdleHigh, - phase: Phase::CaptureOnFirstTransition, -}; - -/// Helper for CPOL = 1, CPHA = 1 -pub const MODE_3: Mode = Mode { - polarity: Polarity::IdleHigh, - phase: Phase::CaptureOnSecondTransition, -}; - -/// SPI error -pub trait Error: core::fmt::Debug { - /// 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 - /// code can act. - fn kind(&self) -> ErrorKind; -} - -impl Error for core::convert::Infallible { - fn kind(&self) -> ErrorKind { - match *self {} - } -} - -/// 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 -/// a mapping to these common SPI 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, - /// 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 - FrameFormat, - /// An error occured while asserting or deasserting the Chip Select pin. - ChipSelectFault, - /// 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::ModeFault => write!( - f, - "Multiple devices on the SPI bus are trying to drive the slave select pin" - ), - Self::FrameFormat => write!( - f, - "Received data does not conform to the peripheral configuration" - ), - Self::ChipSelectFault => write!( - f, - "An error occured while asserting or deasserting the Chip Select pin" - ), - Self::Other => write!( - f, - "A different error occurred. The original error may contain more information" - ), - } - } -} - -/// SPI error type trait -/// -/// This just defines the error type, to be used by the other SPI traits. -pub trait ErrorType { - /// Error type - type Error: Error; -} - -impl ErrorType for &mut T { - type Error = T::Error; -}