├── .gitattributes ├── .github ├── CODEOWNERS └── workflows │ ├── clippy.yml │ ├── rustdoc.yml │ ├── rustfmt.yml │ └── test.yml ├── .gitignore ├── CODE_OF_CONDUCT.md ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md ├── docs ├── how-to-add-a-new-trait.md ├── migrating-from-0.2-to-1.0.md └── msrv.md ├── embedded-can ├── CHANGELOG.md ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md └── src │ ├── blocking.rs │ ├── id.rs │ ├── lib.rs │ └── nb.rs ├── embedded-hal-async ├── CHANGELOG.md ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md └── src │ ├── delay.rs │ ├── digital.rs │ ├── i2c.rs │ ├── lib.rs │ └── spi.rs ├── embedded-hal-bus ├── CHANGELOG.md ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md └── src │ ├── i2c │ ├── atomic.rs │ ├── critical_section.rs │ ├── mod.rs │ ├── mutex.rs │ ├── rc.rs │ └── refcell.rs │ ├── lib.rs │ ├── spi │ ├── atomic.rs │ ├── critical_section.rs │ ├── exclusive.rs │ ├── mod.rs │ ├── mutex.rs │ ├── rc.rs │ ├── refcell.rs │ └── shared.rs │ └── util.rs ├── embedded-hal-nb ├── CHANGELOG.md ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md └── src │ ├── lib.rs │ ├── serial.rs │ └── spi.rs ├── embedded-hal ├── CHANGELOG.md ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md └── src │ ├── delay.rs │ ├── digital.rs │ ├── i2c-shared-bus.svg │ ├── i2c.rs │ ├── lib.rs │ ├── pwm.rs │ ├── spi-shared-bus.svg │ └── spi.rs ├── embedded-io-adapters ├── CHANGELOG.md ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md └── src │ ├── fmt.rs │ ├── futures_03.rs │ ├── lib.rs │ ├── std.rs │ └── tokio_1.rs ├── embedded-io-async ├── CHANGELOG.md ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md └── src │ ├── impls │ ├── boxx.rs │ ├── mod.rs │ ├── slice_mut.rs │ ├── slice_ref.rs │ └── vec.rs │ └── lib.rs ├── embedded-io ├── CHANGELOG.md ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md └── src │ ├── impls │ ├── boxx.rs │ ├── mod.rs │ ├── slice_mut.rs │ ├── slice_ref.rs │ └── vec.rs │ └── lib.rs └── triagebot.toml /.gitattributes: -------------------------------------------------------------------------------- 1 | CHANGELOG.md merge=union -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @rust-embedded/hal 2 | -------------------------------------------------------------------------------- /.github/workflows/clippy.yml: -------------------------------------------------------------------------------- 1 | on: 2 | push: # Run CI for all branches except GitHub merge queue tmp branches 3 | branches-ignore: 4 | - "gh-readonly-queue/**" 5 | pull_request: # Run CI for PRs on any branch 6 | merge_group: # Run CI for the GitHub merge queue 7 | 8 | name: Clippy check 9 | jobs: 10 | clippy: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v4 14 | - uses: dtolnay/rust-toolchain@stable 15 | with: 16 | components: clippy 17 | - run: cargo clippy --all-features -- --deny=warnings 18 | -------------------------------------------------------------------------------- /.github/workflows/rustdoc.yml: -------------------------------------------------------------------------------- 1 | on: 2 | push: # Run CI for all branches except GitHub merge queue tmp branches 3 | branches-ignore: 4 | - "gh-readonly-queue/**" 5 | pull_request: # Run CI for PRs on any branch 6 | merge_group: # Run CI for the GitHub merge queue 7 | 8 | name: Rustdoc check 9 | jobs: 10 | rustdoc: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v4 14 | - uses: dtolnay/rust-toolchain@master 15 | with: 16 | toolchain: nightly-2024-07-26 17 | # tokio/net required to workaround https://github.com/tokio-rs/tokio/issues/6165 18 | - run: RUSTDOCFLAGS="--deny=warnings --cfg=docsrs" cargo doc --all-features --features tokio/net 19 | -------------------------------------------------------------------------------- /.github/workflows/rustfmt.yml: -------------------------------------------------------------------------------- 1 | on: 2 | push: # Run CI for all branches except GitHub merge queue tmp branches 3 | branches-ignore: 4 | - "gh-readonly-queue/**" 5 | pull_request: # Run CI for PRs on any branch 6 | merge_group: # Run CI for the GitHub merge queue 7 | 8 | name: Code formatting check 9 | 10 | jobs: 11 | fmt: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v4 15 | - uses: dtolnay/rust-toolchain@stable 16 | with: 17 | components: rustfmt 18 | - run: cargo fmt --check 19 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | on: 2 | push: # Run CI for all branches except GitHub merge queue tmp branches 3 | branches-ignore: 4 | - "gh-readonly-queue/**" 5 | pull_request: # Run CI for PRs on any branch 6 | merge_group: # Run CI for the GitHub merge queue 7 | 8 | name: Continuous integration 9 | 10 | env: 11 | RUSTFLAGS: '--deny warnings' 12 | 13 | jobs: 14 | test: 15 | runs-on: ubuntu-latest 16 | steps: 17 | - uses: actions/checkout@v4 18 | - uses: dtolnay/rust-toolchain@stable 19 | - run: cargo test --workspace 20 | 21 | test-all-features: 22 | runs-on: ubuntu-latest 23 | steps: 24 | - uses: actions/checkout@v4 25 | - uses: dtolnay/rust-toolchain@stable 26 | - run: cargo test --workspace --all-features 27 | 28 | build-nostd: 29 | runs-on: ubuntu-latest 30 | steps: 31 | - uses: actions/checkout@v4 32 | - uses: dtolnay/rust-toolchain@stable 33 | with: 34 | target: thumbv7m-none-eabi 35 | - run: > 36 | cargo build 37 | --workspace 38 | --target thumbv7m-none-eabi 39 | --features async,defmt-03 40 | 41 | msrv-1-81: 42 | runs-on: ubuntu-latest 43 | steps: 44 | - uses: actions/checkout@v4 45 | - uses: dtolnay/rust-toolchain@1.81 46 | - run: cargo test --workspace --all-features 47 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | **/*.rs.bk 2 | .#* 3 | target 4 | Cargo.lock 5 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # The Rust Code of Conduct 2 | 3 | ## Conduct 4 | 5 | **Contact**: [HAL team][team] 6 | 7 | * We are committed to providing a friendly, safe and welcoming environment for all, regardless of level of experience, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, ethnicity, age, religion, nationality, or other similar characteristic. 8 | * On IRC, please avoid using overtly sexual nicknames or other nicknames that might detract from a friendly, safe and welcoming environment for all. 9 | * Please be kind and courteous. There's no need to be mean or rude. 10 | * Respect that people have differences of opinion and that every design or implementation choice carries a trade-off and numerous costs. There is seldom a right answer. 11 | * Please keep unstructured critique to a minimum. If you have solid ideas you want to experiment with, make a fork and see how it works. 12 | * We will exclude you from interaction if you insult, demean or harass anyone. That is not welcome behavior. We interpret the term "harassment" as including the definition in the [Citizen Code of Conduct](http://citizencodeofconduct.org/); if you have any lack of clarity about what might be included in that concept, please read their definition. In particular, we don't tolerate behavior that excludes people in socially marginalized groups. 13 | * Private harassment is also unacceptable. No matter who you are, if you feel you have been or are being harassed or made uncomfortable by a community member, please contact one of the channel ops or any of the [HAL team][team] immediately. Whether you're a regular contributor or a newcomer, we care about making this community a safe place for you and we've got your back. 14 | * Likewise any spamming, trolling, flaming, baiting or other attention-stealing behavior is not welcome. 15 | 16 | ## Moderation 17 | 18 | These are the policies for upholding our community's standards of conduct. 19 | 20 | 1. Remarks that violate the Rust standards of conduct, including hateful, hurtful, oppressive, or exclusionary remarks, are not allowed. (Cursing is allowed, but never targeting another user, and never in a hateful manner.) 21 | 2. Remarks that moderators find inappropriate, whether listed in the code of conduct or not, are also not allowed. 22 | 3. Moderators will first respond to such remarks with a warning. 23 | 4. If the warning is unheeded, the user will be "kicked," i.e., kicked out of the communication channel to cool off. 24 | 5. If the user comes back and continues to make trouble, they will be banned, i.e., indefinitely excluded. 25 | 6. Moderators may choose at their discretion to un-ban the user if it was a first offense and they offer the offended party a genuine apology. 26 | 7. If a moderator bans someone and you think it was unjustified, please take it up with that moderator, or with a different moderator, **in private**. Complaints about bans in-channel are not allowed. 27 | 8. Moderators are held to a higher standard than other community members. If a moderator creates an inappropriate situation, they should expect less leeway than others. 28 | 29 | In the Rust community we strive to go the extra step to look out for each other. Don't just aim to be technically unimpeachable, try to be your best self. In particular, avoid flirting with offensive or sensitive issues, particularly if they're off-topic; this all too often leads to unnecessary fights, hurt feelings, and damaged trust; worse, it can drive people away from the community entirely. 30 | 31 | And if someone takes issue with something you said or did, resist the urge to be defensive. Just stop doing what it was they complained about and apologize. Even if you feel you were misinterpreted or unfairly accused, chances are good there was something you could've communicated better — remember that it's your responsibility to make your fellow Rustaceans comfortable. Everyone wants to get along and we are all here first and foremost because we want to talk about cool technology. You will find that people will be eager to assume good intent and forgive as long as you earn their trust. 32 | 33 | The enforcement policies listed above apply to all official embedded WG venues; including official IRC channels (#rust-embedded); GitHub repositories under rust-embedded; and all forums under rust-embedded.org (forum.rust-embedded.org). 34 | 35 | *Adapted from the [Node.js Policy on Trolling](http://blog.izs.me/post/30036893703/policy-on-trolling) as well as the [Contributor Covenant v1.3.0](https://www.contributor-covenant.org/version/1/3/0/).* 36 | 37 | [team]: https://github.com/rust-embedded/wg#the-hal-team 38 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | resolver = "2" 3 | 4 | members = [ 5 | "embedded-hal", 6 | "embedded-hal-async", 7 | "embedded-hal-nb", 8 | "embedded-hal-bus", 9 | "embedded-can", 10 | "embedded-io", 11 | "embedded-io-async", 12 | "embedded-io-adapters", 13 | ] 14 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) 2017-2018 Jorge Aparicio 2 | 3 | Permission is hereby granted, free of charge, to any 4 | person obtaining a copy of this software and associated 5 | documentation files (the "Software"), to deal in the 6 | Software without restriction, including without 7 | limitation the rights to use, copy, modify, merge, 8 | publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software 10 | is furnished to do so, subject to the following 11 | conditions: 12 | 13 | The above copyright notice and this permission notice 14 | shall be included in all copies or substantial portions 15 | of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 18 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 19 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 20 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 21 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 22 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 24 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 25 | DEALINGS IN THE SOFTWARE. 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # `embedded-hal` 2 | 3 | > A Hardware Abstraction Layer (HAL) for embedded systems 4 | 5 | This project is developed and maintained by the [HAL team](https://github.com/rust-embedded/wg#the-hal-team). 6 | 7 | > [!IMPORTANT] 8 | > 📣 `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). 9 | 10 | ## Scope 11 | 12 | `embedded-hal` serves as a foundation for building an ecosystem of platform-agnostic drivers. 13 | (driver meaning library crates that let a target platform interface an external device like a digital 14 | sensor or a wireless transceiver). 15 | 16 | The advantage of this system is that by writing the driver as a generic library on top 17 | of `embedded-hal` driver authors can support any number of target 18 | platforms (e.g. Cortex-M microcontrollers, AVR microcontrollers, embedded Linux, etc.). 19 | 20 | The advantage for application developers is that by adopting `embedded-hal` they can unlock all 21 | these drivers for their platform. 22 | 23 | For functionality that goes beyond what is provided by `embedded-hal`, users are encouraged 24 | to use the target platform directly. Abstractions of common functionality can be proposed to be 25 | included into `embedded-hal` as described [in this guide](docs/how-to-add-a-new-trait.md), though. 26 | 27 | See more about the design goals in [this documentation section](https://docs.rs/embedded-hal/latest/embedded_hal/#design-goals). 28 | 29 | ## Crates 30 | 31 | The main `embedded-hal` project is not tied to a specific execution model like blocking or non-blocking. 32 | 33 | | Crate | crates.io | Docs | | 34 | |-|-|-|-| 35 | | [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 | 36 | | [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 | 37 | | [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 | 38 | | [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 | 39 | | [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 | 40 | | [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. | 41 | | [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 | 42 | | [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`...) | 43 | 44 | ## Documents 45 | 46 | - [Migrating from v0.2 to v1.0](docs/migrating-from-0.2-to-1.0.md). 47 | - [How-to: add a new trait](docs/how-to-add-a-new-trait.md) 48 | - [MSRV](docs/msrv.md) 49 | 50 | ## Implementations and drivers 51 | 52 | For a non-exhaustive list of `embedded-hal` implementations and driver crates check the 53 | [awesome-embedded-rust] list. 54 | 55 | You may be able to find even more HAL implementation crates and driver crates by searching for the 56 | [`embedded-hal-impl`], [`embedded-hal-driver`] and [`embedded-hal`][embedded-hal-kw] keywords 57 | on crates.io. 58 | 59 | [`embedded-hal-impl`]: https://crates.io/keywords/embedded-hal-impl 60 | [`embedded-hal-driver`]: https://crates.io/keywords/embedded-hal-driver 61 | [embedded-hal-kw]: https://crates.io/keywords/embedded-hal 62 | 63 | [awesome-embedded-rust]: https://github.com/rust-embedded/awesome-embedded-rust#driver-crates 64 | 65 | ## Minimum Supported Rust Version (MSRV) 66 | 67 | This crate is guaranteed to compile on stable Rust 1.81 and up. It *might* 68 | compile with older versions but that may change in any new patch release. 69 | 70 | See [here](docs/msrv.md) for details on how the MSRV may be upgraded. 71 | 72 | ## License 73 | 74 | Licensed under either of 75 | 76 | - Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or 77 | http://www.apache.org/licenses/LICENSE-2.0) 78 | - MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) 79 | 80 | at your option. 81 | 82 | ### Contribution 83 | 84 | Unless you explicitly state otherwise, any contribution intentionally submitted 85 | for inclusion in the work by you, as defined in the Apache-2.0 license, shall be 86 | dual licensed as above, without any additional terms or conditions. 87 | 88 | ## Code of Conduct 89 | 90 | Contribution to this repository is organized under the terms of the [Rust Code of 91 | Conduct](CODE_OF_CONDUCT.md), the maintainers of this repository, the [HAL team](https://github.com/rust-embedded/wg#the-hal-team), promise 92 | to intervene to uphold that code of conduct. 93 | -------------------------------------------------------------------------------- /docs/how-to-add-a-new-trait.md: -------------------------------------------------------------------------------- 1 | # How-to: add a new trait 2 | 3 | This is the suggested approach to adding a new trait to `embedded-hal` 4 | 5 | ## Research / Discussion 6 | 7 | Ideally, before proposing a new trait, or set of traits, you should check for an existing issue 8 | suggesting the need for the trait, as well as any related works / use cases / requirements that 9 | are useful to consider in the design of the trait. 10 | 11 | These issues will be labeled as `discussion` in the issue tracker. 12 | 13 | ## Implementation / Demonstration 14 | 15 | Proposed traits should then be implemented and demonstrated, either by forking `embedded-hal` or by creating a new crate with the intent of integrating this into `embedded-hal` once the traits have stabilized. You may find [cargo workspaces](https://doc.rust-lang.org/book/ch14-03-cargo-workspaces.html) and [patch](https://doc.rust-lang.org/edition-guide/rust-2018/cargo-and-crates-io/replacing-dependencies-with-patch.html) useful for the forking approach. 16 | 17 | Traits should be demonstrated with at least *two* implementations on different platforms and *one* generic driver built on the trait. Where it is possible we suggest an implementation on a microcontroller, and implementation for [linux](https://github.com/rust-embedded/linux-embedded-hal), and a driver (or drivers where requirements are more complex) with bounds using the trait. 18 | 19 | ## Proposing a trait 20 | 21 | Once the trait has been demonstrated a PR should be opened to merge the new trait(s) into `embedded-hal`. This should include a link to the previous discussion issue. 22 | 23 | If there is determined to be more than one alternative then there should be further discussion to 24 | try to single out the best option. Once there is consensus this will be merged into the `embedded-hal` repository. 25 | 26 | These issues / PRs will be labeled as `proposal`s in the issue tracker. 27 | -------------------------------------------------------------------------------- /docs/msrv.md: -------------------------------------------------------------------------------- 1 | # Minimum Supported Rust Version (MSRV) 2 | 3 | This crate is guaranteed to compile on all stable Rust versions going back to 4 | the version stated as MSRV in the README. It *might* compile with even older versions but 5 | that may change in any new patch release. 6 | 7 | ## How the MSRV will be upgraded 8 | 9 | For this library, we do not consider upgrading the MSRV a strictly breaking change as defined by 10 | [SemVer](https://semver.org). 11 | 12 | We follow these rules when upgrading it: 13 | 14 | - We will not update the MSRV on any patch release: \_.\_.*Z*. 15 | - We may upgrade the MSRV on any *major* or *minor* release: *X*.*Y*.\_. 16 | - We may upgrade the MSRV in any preliminary version release (e.g. an `-alpha` release) as 17 | these serve as preparation for the final release. 18 | - MSRV upgrades will be clearly stated in the changelog. 19 | 20 | This applies both to `0._._` releases as well as `>=1._._` releases. 21 | 22 | For example: 23 | 24 | For a given `x.y.z` release, we may upgrade the MSRV on `x` and `y` releases but not on `z` releases. 25 | 26 | If your MSRV upgrade policy differs from this, you are advised to specify the dependency in your `Cargo.toml` accordingly. 27 | 28 | See the [Rust Embedded Working Group MSRV RFC](https://github.com/rust-embedded/wg/blob/master/rfcs/0523-msrv-2020.md) 29 | for more background information and reasoning. 30 | -------------------------------------------------------------------------------- /embedded-can/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | The format is based on [Keep a Changelog](http://keepachangelog.com/) 6 | and this project adheres to [Semantic Versioning](http://semver.org/). 7 | 8 | ## [Unreleased] 9 | 10 | - Added `core::error::Error` implementations for every custom `impl Error` 11 | - Increased MSRV to 1.81 due to `core::error::Error` 12 | 13 | ## [v0.4.1] - 2022-09-28 14 | 15 | ### Removed 16 | - Unnecessary `embedded-hal` dependency. 17 | 18 | ## [v0.4.0] - 2022-09-28 19 | 20 | Release of version of the traits extracted from embedded-hal. 21 | 22 | [Unreleased]: https://github.com/rust-embedded/embedded-hal/compare/embedded-can-v0.4.1...HEAD 23 | [v0.4.1]: https://github.com/rust-embedded/embedded-hal/compare/embedded-can-v0.4.0...embedded-can-v0.4.1 24 | [v0.4.0]: https://github.com/rust-embedded/embedded-hal/tree/embedded-can-v0.4.0 25 | -------------------------------------------------------------------------------- /embedded-can/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "embedded-can" 3 | version = "0.4.1" 4 | edition = "2021" 5 | rust-version = "1.81" 6 | 7 | description = "HAL traits for Controller Area Network (CAN) devices." 8 | categories = ["embedded", "hardware-support", "no-std"] 9 | documentation = "https://docs.rs/embedded-can" 10 | keywords = ["hal", "IO"] 11 | license = "MIT OR Apache-2.0" 12 | readme = "README.md" 13 | repository = "https://github.com/rust-embedded/embedded-hal" 14 | 15 | [dependencies] 16 | nb = "1" 17 | defmt = { version = "0.3", optional = true } 18 | 19 | [features] 20 | defmt-03 = ["dep:defmt"] 21 | -------------------------------------------------------------------------------- /embedded-can/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) 2021-2025 The Rust embedded HAL team and contributors. 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 7 | of the Software, and to permit persons to whom the Software is furnished to do 8 | so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /embedded-can/README.md: -------------------------------------------------------------------------------- 1 | [![crates.io](https://img.shields.io/crates/d/embedded-can.svg)](https://crates.io/crates/embedded-can) 2 | [![crates.io](https://img.shields.io/crates/v/embedded-can.svg)](https://crates.io/crates/embedded-can) 3 | [![Documentation](https://docs.rs/embedded-can/badge.svg)](https://docs.rs/embedded-can) 4 | ![Minimum Supported Rust Version](https://img.shields.io/badge/rustc-1.60+-blue.svg) 5 | 6 | # `embedded-can` 7 | 8 | An embedded Controller Area Network (CAN) abstraction layer. This crate defines generic traits to be implemented by CAN driver and MCU HAL crates. 9 | 10 | This project is developed and maintained by the [HAL team](https://github.com/rust-embedded/wg#the-hal-team). 11 | 12 | ## [API reference] 13 | 14 | [API reference]: https://docs.rs/embedded-can 15 | 16 | ## Optional features 17 | 18 | - **`defmt-03`**: Derive `defmt::Format` from `defmt` 0.3 for enums and structs. 19 | 20 | ## Minimum Supported Rust Version (MSRV) 21 | 22 | This crate is guaranteed to compile on stable Rust 1.81 and up. It *might* 23 | compile with older versions but that may change in any new patch release. 24 | 25 | See [here](../docs/msrv.md) for details on how the MSRV may be upgraded. 26 | 27 | ## License 28 | 29 | Licensed under either of 30 | 31 | - Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or 32 | http://www.apache.org/licenses/LICENSE-2.0) 33 | - MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) 34 | 35 | at your option. 36 | 37 | ### Contribution 38 | 39 | Unless you explicitly state otherwise, any contribution intentionally submitted 40 | for inclusion in the work by you, as defined in the Apache-2.0 license, shall be 41 | dual licensed as above, without any additional terms or conditions. 42 | -------------------------------------------------------------------------------- /embedded-can/src/blocking.rs: -------------------------------------------------------------------------------- 1 | //! Blocking CAN API 2 | 3 | /// A blocking CAN interface that is able to transmit and receive frames. 4 | pub trait Can { 5 | /// Associated frame type. 6 | type Frame: crate::Frame; 7 | 8 | /// Associated error type. 9 | type Error: crate::Error; 10 | 11 | /// Puts a frame in the transmit buffer. Blocks until space is available in 12 | /// the transmit buffer. 13 | fn transmit(&mut self, frame: &Self::Frame) -> Result<(), Self::Error>; 14 | 15 | /// Blocks until a frame was received or an error occurred. 16 | fn receive(&mut self) -> Result; 17 | } 18 | -------------------------------------------------------------------------------- /embedded-can/src/id.rs: -------------------------------------------------------------------------------- 1 | //! CAN Identifiers. 2 | 3 | /// Standard 11-bit CAN Identifier (`0..=0x7FF`). 4 | #[derive(Debug, Copy, Clone, Eq, PartialEq, PartialOrd, Ord, Hash)] 5 | #[cfg_attr(feature = "defmt-03", derive(defmt::Format))] 6 | pub struct StandardId(u16); 7 | 8 | impl StandardId { 9 | /// CAN ID `0`, the highest priority. 10 | pub const ZERO: Self = Self(0); 11 | 12 | /// CAN ID `0x7FF`, the lowest priority. 13 | pub const MAX: Self = Self(0x7FF); 14 | 15 | /// Tries to create a `StandardId` from a raw 16-bit integer. 16 | /// 17 | /// This will return `None` if `raw` is out of range of an 11-bit integer (`> 0x7FF`). 18 | #[inline] 19 | #[must_use] 20 | pub const fn new(raw: u16) -> Option { 21 | if raw <= 0x7FF { 22 | Some(Self(raw)) 23 | } else { 24 | None 25 | } 26 | } 27 | 28 | /// Creates a new `StandardId` without checking if it is inside the valid range. 29 | /// 30 | /// # Safety 31 | /// Using this method can create an invalid ID and is thus marked as unsafe. 32 | #[inline] 33 | #[must_use] 34 | pub const unsafe fn new_unchecked(raw: u16) -> Self { 35 | Self(raw) 36 | } 37 | 38 | /// Returns this CAN Identifier as a raw 16-bit integer. 39 | #[inline] 40 | #[must_use] 41 | pub const fn as_raw(&self) -> u16 { 42 | self.0 43 | } 44 | } 45 | 46 | /// Extended 29-bit CAN Identifier (`0..=1FFF_FFFF`). 47 | #[derive(Debug, Copy, Clone, Eq, PartialEq, PartialOrd, Ord, Hash)] 48 | #[cfg_attr(feature = "defmt-03", derive(defmt::Format))] 49 | pub struct ExtendedId(u32); 50 | 51 | impl ExtendedId { 52 | /// CAN ID `0`, the highest priority. 53 | pub const ZERO: Self = Self(0); 54 | 55 | /// CAN ID `0x1FFFFFFF`, the lowest priority. 56 | pub const MAX: Self = Self(0x1FFF_FFFF); 57 | 58 | /// Tries to create a `ExtendedId` from a raw 32-bit integer. 59 | /// 60 | /// This will return `None` if `raw` is out of range of an 29-bit integer (`> 0x1FFF_FFFF`). 61 | #[inline] 62 | #[must_use] 63 | pub const fn new(raw: u32) -> Option { 64 | if raw <= 0x1FFF_FFFF { 65 | Some(Self(raw)) 66 | } else { 67 | None 68 | } 69 | } 70 | 71 | /// Creates a new `ExtendedId` without checking if it is inside the valid range. 72 | /// 73 | /// # Safety 74 | /// Using this method can create an invalid ID and is thus marked as unsafe. 75 | #[inline] 76 | #[must_use] 77 | pub const unsafe fn new_unchecked(raw: u32) -> Self { 78 | Self(raw) 79 | } 80 | 81 | /// Returns this CAN Identifier as a raw 32-bit integer. 82 | #[inline] 83 | #[must_use] 84 | pub const fn as_raw(&self) -> u32 { 85 | self.0 86 | } 87 | 88 | /// Returns the Base ID part of this extended identifier. 89 | #[must_use] 90 | pub fn standard_id(&self) -> StandardId { 91 | // ID-28 to ID-18 92 | StandardId((self.0 >> 18) as u16) 93 | } 94 | } 95 | 96 | /// A CAN Identifier (standard or extended). 97 | #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] 98 | #[cfg_attr(feature = "defmt-03", derive(defmt::Format))] 99 | pub enum Id { 100 | /// Standard 11-bit Identifier (`0..=0x7FF`). 101 | Standard(StandardId), 102 | 103 | /// Extended 29-bit Identifier (`0..=0x1FFF_FFFF`). 104 | Extended(ExtendedId), 105 | } 106 | 107 | /// Implement `Ord` according to the CAN arbitration rules 108 | /// 109 | /// When performing arbitration, frames are looked at bit for bit starting 110 | /// from the beginning. A bit with the value 0 is dominant and a bit with 111 | /// value of 1 is recessive. 112 | /// 113 | /// When two devices are sending frames at the same time, as soon as the first 114 | /// bit is found which differs, the frame with the corresponding dominant 115 | /// 0 bit will win and get to send the rest of the frame. 116 | /// 117 | /// This implementation of `Ord` for `Id` will take this into consideration 118 | /// and when comparing two different instances of `Id` the "smallest" will 119 | /// always be the ID which would form the most dominant frame, all other 120 | /// things being equal. 121 | impl Ord for Id { 122 | fn cmp(&self, other: &Self) -> core::cmp::Ordering { 123 | let split_id = |id: &Id| { 124 | let (standard_id_part, ide_bit, extended_id_part) = match id { 125 | Id::Standard(StandardId(x)) => (*x, 0, 0), 126 | Id::Extended(x) => ( 127 | x.standard_id().0, 128 | 1, 129 | x.0 & ((1 << 18) - 1), // Bit ID-17 to ID-0 130 | ), 131 | }; 132 | (standard_id_part, ide_bit, extended_id_part) 133 | }; 134 | 135 | split_id(self).cmp(&split_id(other)) 136 | } 137 | } 138 | 139 | impl PartialOrd for Id { 140 | fn partial_cmp(&self, other: &Id) -> Option { 141 | Some(self.cmp(other)) 142 | } 143 | } 144 | 145 | impl From for Id { 146 | #[inline] 147 | fn from(id: StandardId) -> Self { 148 | Id::Standard(id) 149 | } 150 | } 151 | 152 | impl From for Id { 153 | #[inline] 154 | fn from(id: ExtendedId) -> Self { 155 | Id::Extended(id) 156 | } 157 | } 158 | 159 | #[cfg(test)] 160 | mod tests { 161 | use super::*; 162 | 163 | #[test] 164 | fn standard_id_new() { 165 | assert_eq!( 166 | StandardId::new(StandardId::MAX.as_raw()), 167 | Some(StandardId::MAX) 168 | ); 169 | } 170 | 171 | #[test] 172 | fn standard_id_new_out_of_range() { 173 | assert_eq!(StandardId::new(StandardId::MAX.as_raw() + 1), None); 174 | } 175 | 176 | #[test] 177 | fn standard_id_new_unchecked_out_of_range() { 178 | let id = StandardId::MAX.as_raw() + 1; 179 | assert_eq!(unsafe { StandardId::new_unchecked(id) }, StandardId(id)); 180 | } 181 | 182 | #[test] 183 | fn extended_id_new() { 184 | assert_eq!( 185 | ExtendedId::new(ExtendedId::MAX.as_raw()), 186 | Some(ExtendedId::MAX) 187 | ); 188 | } 189 | 190 | #[test] 191 | fn extended_id_new_out_of_range() { 192 | assert_eq!(ExtendedId::new(ExtendedId::MAX.as_raw() + 1), None); 193 | } 194 | 195 | #[test] 196 | fn extended_id_new_unchecked_out_of_range() { 197 | let id = ExtendedId::MAX.as_raw() + 1; 198 | assert_eq!(unsafe { ExtendedId::new_unchecked(id) }, ExtendedId(id)); 199 | } 200 | 201 | #[test] 202 | fn get_standard_id_from_extended_id() { 203 | assert_eq!( 204 | Some(ExtendedId::MAX.standard_id()), 205 | StandardId::new((ExtendedId::MAX.0 >> 18) as u16) 206 | ); 207 | } 208 | 209 | #[test] 210 | fn cmp_id() { 211 | assert!(StandardId::ZERO < StandardId::MAX); 212 | assert!(ExtendedId::ZERO < ExtendedId::MAX); 213 | 214 | assert!(Id::Standard(StandardId::ZERO) < Id::Extended(ExtendedId::ZERO)); 215 | assert!(Id::Extended(ExtendedId::ZERO) < Id::Extended(ExtendedId::MAX)); 216 | assert!(Id::Extended(ExtendedId((1 << 11) - 1)) < Id::Standard(StandardId(1))); 217 | assert!(Id::Standard(StandardId(1)) < Id::Extended(ExtendedId::MAX)); 218 | } 219 | } 220 | -------------------------------------------------------------------------------- /embedded-can/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! Controller Area Network (CAN) traits 2 | 3 | #![warn(missing_docs)] 4 | #![no_std] 5 | 6 | pub mod blocking; 7 | pub mod nb; 8 | 9 | mod id; 10 | 11 | pub use id::*; 12 | 13 | /// A CAN2.0 Frame 14 | pub trait Frame: Sized { 15 | /// Creates a new frame. 16 | /// 17 | /// This will return `None` if the data slice is too long. 18 | fn new(id: impl Into, data: &[u8]) -> Option; 19 | 20 | /// Creates a new remote frame (RTR bit set). 21 | /// 22 | /// This will return `None` if the data length code (DLC) is not valid. 23 | fn new_remote(id: impl Into, dlc: usize) -> Option; 24 | 25 | /// Returns true if this frame is an extended frame. 26 | fn is_extended(&self) -> bool; 27 | 28 | /// Returns true if this frame is a standard frame. 29 | fn is_standard(&self) -> bool { 30 | !self.is_extended() 31 | } 32 | 33 | /// Returns true if this frame is a remote frame. 34 | fn is_remote_frame(&self) -> bool; 35 | 36 | /// Returns true if this frame is a data frame. 37 | fn is_data_frame(&self) -> bool { 38 | !self.is_remote_frame() 39 | } 40 | 41 | /// Returns the frame identifier. 42 | fn id(&self) -> Id; 43 | 44 | /// Returns the data length code (DLC) which is in the range 0..8. 45 | /// 46 | /// For data frames the DLC value always matches the length of the data. 47 | /// Remote frames do not carry any data, yet the DLC can be greater than 0. 48 | fn dlc(&self) -> usize; 49 | 50 | /// Returns the frame data (0..8 bytes in length). 51 | fn data(&self) -> &[u8]; 52 | } 53 | 54 | /// CAN error 55 | pub trait Error: core::fmt::Debug { 56 | /// Convert error to a generic CAN error kind 57 | /// 58 | /// By using this method, CAN errors freely defined by HAL implementations 59 | /// can be converted to a set of generic serial errors upon which generic 60 | /// code can act. 61 | fn kind(&self) -> ErrorKind; 62 | } 63 | 64 | impl Error for core::convert::Infallible { 65 | fn kind(&self) -> ErrorKind { 66 | match *self {} 67 | } 68 | } 69 | 70 | /// CAN error kind 71 | /// 72 | /// This represents a common set of CAN operation errors. HAL implementations are 73 | /// free to define more specific or additional error types. However, by providing 74 | /// a mapping to these common CAN errors, generic code can still react to them. 75 | #[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] 76 | #[cfg_attr(feature = "defmt-03", derive(defmt::Format))] 77 | #[non_exhaustive] 78 | pub enum ErrorKind { 79 | /// The peripheral receive buffer was overrun. 80 | Overrun, 81 | 82 | // MAC sublayer errors 83 | /// A bit error is detected at that bit time when the bit value that is 84 | /// monitored differs from the bit value sent. 85 | Bit, 86 | 87 | /// A stuff error is detected at the bit time of the sixth consecutive 88 | /// equal bit level in a frame field that shall be coded by the method 89 | /// of bit stuffing. 90 | Stuff, 91 | 92 | /// Calculated CRC sequence does not equal the received one. 93 | Crc, 94 | 95 | /// A form error shall be detected when a fixed-form bit field contains 96 | /// one or more illegal bits. 97 | Form, 98 | 99 | /// An ACK error shall be detected by a transmitter whenever it does not 100 | /// monitor a dominant bit during the ACK slot. 101 | Acknowledge, 102 | 103 | /// A different error occurred. The original error may contain more information. 104 | Other, 105 | } 106 | 107 | impl Error for ErrorKind { 108 | fn kind(&self) -> ErrorKind { 109 | *self 110 | } 111 | } 112 | 113 | impl core::error::Error for ErrorKind {} 114 | 115 | impl core::fmt::Display for ErrorKind { 116 | fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { 117 | match self { 118 | Self::Overrun => write!(f, "The peripheral receive buffer was overrun"), 119 | Self::Bit => write!( 120 | f, 121 | "Bit value that is monitored differs from the bit value sent" 122 | ), 123 | Self::Stuff => write!(f, "Sixth consecutive equal bits detected"), 124 | Self::Crc => write!(f, "Calculated CRC sequence does not equal the received one"), 125 | Self::Form => write!( 126 | f, 127 | "A fixed-form bit field contains one or more illegal bits" 128 | ), 129 | Self::Acknowledge => write!(f, "Transmitted frame was not acknowledged"), 130 | Self::Other => write!( 131 | f, 132 | "A different error occurred. The original error may contain more information" 133 | ), 134 | } 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /embedded-can/src/nb.rs: -------------------------------------------------------------------------------- 1 | //! Non-blocking CAN API 2 | 3 | /// A CAN interface that is able to transmit and receive frames. 4 | pub trait Can { 5 | /// Associated frame type. 6 | type Frame: crate::Frame; 7 | 8 | /// Associated error type. 9 | type Error: crate::Error; 10 | 11 | /// Puts a frame in the transmit buffer to be sent on the bus. 12 | /// 13 | /// If the transmit buffer is full, this function will try to replace a pending 14 | /// lower priority frame and return the frame that was replaced. 15 | /// Returns `Err(WouldBlock)` if the transmit buffer is full and no frame can be 16 | /// replaced. 17 | /// 18 | /// # Notes for implementers 19 | /// 20 | /// * Frames of equal identifier shall be transmitted in FIFO fashion when more 21 | /// than one transmit buffer is available. 22 | /// * When replacing pending frames make sure the frame is not in the process of 23 | /// being send to the bus. 24 | fn transmit(&mut self, frame: &Self::Frame) -> nb::Result, Self::Error>; 25 | 26 | /// Returns a received frame if available. 27 | fn receive(&mut self) -> nb::Result; 28 | } 29 | -------------------------------------------------------------------------------- /embedded-hal-async/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | The format is based on [Keep a Changelog](http://keepachangelog.com/) 6 | and this project adheres to [Semantic Versioning](http://semver.org/). 7 | 8 | ## [Unreleased] 9 | 10 | No unreleased changes yet. 11 | 12 | ## [v1.0.0] - 2023-12-28 13 | 14 | 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. 15 | 16 | - Updated `embedded-hal` to version `1.0.0`. 17 | 18 | ## [v1.0.0-rc.3] - 2023-12-14 19 | 20 | - Updated `embedded-hal` to version `1.0.0-rc.3`. 21 | 22 | ## [v1.0.0-rc.2] - 2023-11-28 23 | 24 | - Updated `embedded-hal` to version `1.0.0-rc.2`. 25 | - Minor document fixes. 26 | - Add #[inline] hints to most of `embedded-hal-async` functions. 27 | - delay: Rename `DelayUs` to `DelayNs` 28 | - delay: Add `DelayNs::delay_ns()` 29 | - delay: Add default impls of `delay_ms` and `delay_us` based on `delay_ns`. 30 | - spi: Rename `Operation::DelayUs` to `Operation::DelayNs`, with nanosecond precision. 31 | - Use `feature()` on nightly toolchains only. This adds support for 1.75 beta and stable. 32 | 33 | ## [v1.0.0-rc.1] - 2023-08-15 34 | 35 | - Updated `embedded-hal` to version `1.0.0-rc.1`. 36 | - Add optional `defmt` 0.3 support. 37 | - Remove serial traits, the replacement is the `embedded-io` crate. 38 | - Added `+ ?Sized` to all blanket impls. 39 | - Moved `ExclusiveDevice` to `embedded-hal-bus`. 40 | 41 | ## [v0.2.0-alpha.2] - 2023-07-04 42 | 43 | ### Added 44 | - spi: added `Operation::DelayUs(u32)`. 45 | 46 | ### Changed 47 | - Updated `embedded-hal` to version `1.0.0-alpha.11`. 48 | - spi: removed redundant lifetime annotations. Note that recent nightlies care about them and require impls to match, so you might have to adjust them. 49 | 50 | ### Removed 51 | - spi: removed read-only and write-only traits. 52 | 53 | ## [v0.2.0-alpha.1] - 2023-04-04 54 | 55 | ### Added 56 | - Added a `serial::Write` trait. 57 | 58 | ### Changed 59 | - Updated `embedded-hal` to version `1.0.0-alpha.10`. 60 | - delay: make infallible. 61 | - i2c: remove `_iter()` methods. 62 | - i2c: add default implementations for all methods based on `transaction()`. 63 | - spi: SpiDevice transaction now takes an operation slice instead of a closure 64 | 65 | ## [v0.2.0-alpha.0] - 2022-11-23 66 | 67 | - 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. 68 | 69 | ## [v0.1.0-alpha.3] - 2022-10-26 70 | 71 | - Fix build on newer Rust nightlies. 72 | 73 | ## [v0.1.0-alpha.2] - 2022-09-28 74 | 75 | ### Added 76 | - spi: added a transaction helper macro as a workaround for the raw pointer workaround. 77 | 78 | ### Changed 79 | - Updated `embedded-hal` to version `1.0.0-alpha.9`. 80 | 81 | ## [v0.1.0-alpha.1] - 2022-05-24 82 | 83 | ### Changed 84 | 85 | - spi: device helper methods (`read`, `write`, `transfer`...) are now default methods in `SpiDevice` instead of an `SpiDeviceExt` extension trait. 86 | - spi: the `SpiDevice::transaction` closure now gets a raw pointer to the `SpiBus` to work around Rust borrow checker limitations. 87 | - spi: the `SpiDevice` trait is now unsafe to implement due to the raw pointer workaround. 88 | 89 | 90 | ## [v0.1.0-alpha.0] - 2022-04-17 91 | 92 | First release to crates.io 93 | 94 | 95 | [Unreleased]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-async-v1.0.0...HEAD 96 | [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 97 | [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 98 | [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 99 | [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 100 | [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 101 | [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 102 | [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 103 | [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 104 | [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 105 | [v0.1.0-alpha.1]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-async-v0.1.0-alpha.0...embedded-hal-async-v0.1.0-alpha.1 106 | [v0.1.0-alpha.0]: https://github.com/rust-embedded/embedded-hal/tree/embedded-hal-async-v0.1.0-alpha.0 107 | -------------------------------------------------------------------------------- /embedded-hal-async/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | authors = [ 3 | "The Embedded HAL Team and Contributors ", 4 | ] 5 | categories = ["asynchronous", "embedded", "hardware-support", "no-std"] 6 | description = "An asynchronous Hardware Abstraction Layer (HAL) for embedded systems" 7 | documentation = "https://docs.rs/embedded-hal-async" 8 | edition = "2021" 9 | keywords = ["hal", "IO"] 10 | license = "MIT OR Apache-2.0" 11 | name = "embedded-hal-async" 12 | readme = "README.md" 13 | repository = "https://github.com/rust-embedded/embedded-hal" 14 | version = "1.0.0" 15 | rust-version = "1.75" 16 | 17 | [features] 18 | defmt-03 = ["dep:defmt-03", "embedded-hal/defmt-03"] 19 | 20 | [dependencies] 21 | embedded-hal = { version = "1.0.0", path = "../embedded-hal" } 22 | defmt-03 = { package = "defmt", version = "0.3", optional = true } 23 | -------------------------------------------------------------------------------- /embedded-hal-async/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) 2021-2025 The Rust embedded HAL team and contributors. 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 7 | of the Software, and to permit persons to whom the Software is furnished to do 8 | so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /embedded-hal-async/README.md: -------------------------------------------------------------------------------- 1 | [![crates.io](https://img.shields.io/crates/d/embedded-hal-async.svg)](https://crates.io/crates/embedded-hal-async) 2 | [![crates.io](https://img.shields.io/crates/v/embedded-hal-async.svg)](https://crates.io/crates/embedded-hal-async) 3 | [![Documentation](https://docs.rs/embedded-hal-async/badge.svg)](https://docs.rs/embedded-hal-async) 4 | 5 | # `embedded-hal-async` 6 | 7 | An asynchronous Hardware Abstraction Layer (HAL) for embedded systems. 8 | 9 | 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). 10 | 11 | This project is developed and maintained by the [HAL team](https://github.com/rust-embedded/wg#the-hal-team). 12 | 13 | ## Serial/UART traits 14 | 15 | There is no serial traits in `embedded-hal-async`. Instead, use [`embedded-io-async`](https://crates.io/crates/embedded-io-async). 16 | A serial port is essentially a byte-oriented stream, and that's what `embedded-io-async` models. Sharing the traits 17 | with all byte streams has some advantages. For example, it allows generic code providing a command-line interface 18 | or a console to operate either on hardware serial ports or on virtual ones like Telnet or USB CDC-ACM. 19 | 20 | ## Optional Cargo features 21 | 22 | - **`defmt-03`**: Derive `defmt::Format` from `defmt` 0.3 for enums and structs. 23 | 24 | ## Minimum Supported Rust Version (MSRV) 25 | 26 | This crate is guaranteed to compile on stable Rust 1.75 and up. It *might* 27 | compile with older versions but that may change in any new patch release. 28 | 29 | See [here](../docs/msrv.md) for details on how the MSRV may be upgraded. 30 | 31 | ## License 32 | 33 | Licensed under either of 34 | 35 | - Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or 36 | ) 37 | - MIT license ([LICENSE-MIT](LICENSE-MIT) or ) 38 | 39 | at your option. 40 | 41 | ### Contribution 42 | 43 | Unless you explicitly state otherwise, any contribution intentionally submitted 44 | for inclusion in the work by you, as defined in the Apache-2.0 license, shall be 45 | dual licensed as above, without any additional terms or conditions. 46 | -------------------------------------------------------------------------------- /embedded-hal-async/src/delay.rs: -------------------------------------------------------------------------------- 1 | //! Delays. 2 | 3 | /// Delay with up to nanosecond precision. 4 | pub trait DelayNs { 5 | /// Pauses execution for at minimum `ns` nanoseconds. Pause can be longer 6 | /// if the implementation requires it due to precision/timing issues. 7 | async fn delay_ns(&mut self, ns: u32); 8 | 9 | /// Pauses execution for at minimum `us` microseconds. Pause can be longer 10 | /// if the implementation requires it due to precision/timing issues. 11 | async fn delay_us(&mut self, mut us: u32) { 12 | while us > 4_294_967 { 13 | us -= 4_294_967; 14 | self.delay_ns(4_294_967_000).await; 15 | } 16 | self.delay_ns(us * 1_000).await; 17 | } 18 | 19 | /// Pauses execution for at minimum `ms` milliseconds. Pause can be longer 20 | /// if the implementation requires it due to precision/timing issues. 21 | #[inline] 22 | async fn delay_ms(&mut self, mut ms: u32) { 23 | while ms > 4294 { 24 | ms -= 4294; 25 | self.delay_ns(4_294_000_000).await; 26 | } 27 | self.delay_ns(ms * 1_000_000).await; 28 | } 29 | } 30 | 31 | impl DelayNs for &mut T 32 | where 33 | T: DelayNs + ?Sized, 34 | { 35 | #[inline] 36 | async fn delay_ns(&mut self, ns: u32) { 37 | T::delay_ns(self, ns).await; 38 | } 39 | 40 | #[inline] 41 | async fn delay_us(&mut self, us: u32) { 42 | T::delay_us(self, us).await; 43 | } 44 | 45 | #[inline] 46 | async fn delay_ms(&mut self, ms: u32) { 47 | T::delay_ms(self, ms).await; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /embedded-hal-async/src/digital.rs: -------------------------------------------------------------------------------- 1 | //! Asynchronous digital I/O. 2 | //! 3 | //! The [`OutputPin`], [`StatefulOutputPin`] and [`InputPin`] traits are `async` variants 4 | //! of the [blocking traits](embedded_hal::digital). These traits are useful for when 5 | //! digital I/O may block execution, such as access through an I/O expander or over some 6 | //! other transport. 7 | //! 8 | //! The [`Wait`] trait allows asynchronously waiting for a change in pin level. 9 | //! 10 | //! # Example 11 | //! 12 | //! ```rust 13 | //! # use embedded_hal_async::digital::Wait; 14 | //! /// Asynchronously wait until the `ready_pin` becomes high. 15 | //! async fn wait_until_ready

(ready_pin: &mut P) 16 | //! where 17 | //! P: Wait, 18 | //! { 19 | //! ready_pin 20 | //! .wait_for_high() 21 | //! .await 22 | //! .expect("failed to await input pin") 23 | //! } 24 | //! ``` 25 | //! 26 | //! # For HAL authors 27 | //! 28 | //! If the digital I/O is implemented using memory mapped I/O and acts immediately, then the async traits 29 | //! (except for [`Wait`]) can be implemented by calling the blocking traits and wrapping the result in 30 | //! [`Poll::Ready`](core::task::Poll::Ready). 31 | pub use embedded_hal::digital::{Error, ErrorKind, ErrorType, PinState}; 32 | 33 | /// Asynchronous single digital push-pull output pin. 34 | pub trait OutputPin: ErrorType { 35 | /// Drives the pin low. 36 | /// 37 | /// This returns [`Ready`](core::task::Poll::Ready) when the pin has been driven low. 38 | /// 39 | /// *NOTE* the actual electrical state of the pin may not actually be low, e.g. due to external 40 | /// electrical sources. 41 | async fn set_low(&mut self) -> Result<(), Self::Error>; 42 | 43 | /// Drives the pin high. 44 | /// 45 | /// This returns [`Ready`](core::task::Poll::Ready) when the pin has been driven high. 46 | /// 47 | /// *NOTE* the actual electrical state of the pin may not actually be high, e.g. due to external 48 | /// electrical sources. 49 | async fn set_high(&mut self) -> Result<(), Self::Error>; 50 | 51 | /// Drives the pin high or low depending on the provided value. 52 | /// 53 | /// This returns [`Ready`](core::task::Poll::Ready) when the pin has been driven to the provided state. 54 | /// 55 | /// *NOTE* the actual electrical state of the pin may not actually be high or low, e.g. due to external 56 | /// electrical sources. 57 | #[inline] 58 | async fn set_state(&mut self, state: PinState) -> Result<(), Self::Error> { 59 | match state { 60 | PinState::Low => self.set_low().await, 61 | PinState::High => self.set_high().await, 62 | } 63 | } 64 | } 65 | 66 | impl OutputPin for &mut T { 67 | #[inline] 68 | async fn set_low(&mut self) -> Result<(), Self::Error> { 69 | T::set_low(self).await 70 | } 71 | 72 | #[inline] 73 | async fn set_high(&mut self) -> Result<(), Self::Error> { 74 | T::set_high(self).await 75 | } 76 | 77 | #[inline] 78 | async fn set_state(&mut self, state: PinState) -> Result<(), Self::Error> { 79 | T::set_state(self, state).await 80 | } 81 | } 82 | 83 | /// Asynchronous push-pull output pin that can read its output state. 84 | pub trait StatefulOutputPin: OutputPin { 85 | /// Is the pin in drive high mode? 86 | /// 87 | /// This returns [`Ready`](core::task::Poll::Ready) when the pin's drive mode been read. 88 | /// 89 | /// *NOTE* this does *not* read the electrical state of the pin. 90 | async fn is_set_high(&mut self) -> Result; 91 | 92 | /// Is the pin in drive low mode? 93 | /// 94 | /// This returns [`Ready`](core::task::Poll::Ready) when the pin's drive mode been read. 95 | /// 96 | /// *NOTE* this does *not* read the electrical state of the pin. 97 | async fn is_set_low(&mut self) -> Result; 98 | 99 | /// Toggle pin output. 100 | /// 101 | /// This returns [`Ready`](core::task::Poll::Ready) when the pin has been toggled. 102 | async fn toggle(&mut self) -> Result<(), Self::Error> { 103 | let was_low: bool = self.is_set_low().await?; 104 | self.set_state(PinState::from(was_low)).await 105 | } 106 | } 107 | 108 | impl StatefulOutputPin for &mut T { 109 | #[inline] 110 | async fn is_set_high(&mut self) -> Result { 111 | T::is_set_high(self).await 112 | } 113 | 114 | #[inline] 115 | async fn is_set_low(&mut self) -> Result { 116 | T::is_set_low(self).await 117 | } 118 | 119 | #[inline] 120 | async fn toggle(&mut self) -> Result<(), Self::Error> { 121 | T::toggle(self).await 122 | } 123 | } 124 | 125 | /// Asynchronous single digital input pin. 126 | pub trait InputPin: ErrorType { 127 | /// Is the input pin high? 128 | /// 129 | /// This returns [`Ready`](core::task::Poll::Ready) when the pin's electrical state has been read. 130 | /// 131 | /// *NOTE* the input state of the pin may have changed before the future is polled. 132 | async fn is_high(&mut self) -> Result; 133 | 134 | /// Is the input pin low? 135 | /// 136 | /// This returns [`Ready`](core::task::Poll::Ready) when the pin's electrical state has been read. 137 | /// 138 | /// *NOTE* the input state of the pin may have changed before the future is polled. 139 | async fn is_low(&mut self) -> Result; 140 | } 141 | 142 | impl InputPin for &mut T { 143 | #[inline] 144 | async fn is_high(&mut self) -> Result { 145 | T::is_high(self).await 146 | } 147 | 148 | #[inline] 149 | async fn is_low(&mut self) -> Result { 150 | T::is_low(self).await 151 | } 152 | } 153 | 154 | /// Asynchronously wait for GPIO pin state. 155 | pub trait Wait: ErrorType { 156 | /// Wait until the pin is high. If it is already high, return immediately. 157 | /// 158 | /// # Note for implementers 159 | /// The pin may have switched back to low before the task was run after 160 | /// being woken. The future should still resolve in that case. 161 | async fn wait_for_high(&mut self) -> Result<(), Self::Error>; 162 | 163 | /// Wait until the pin is low. If it is already low, return immediately. 164 | /// 165 | /// # Note for implementers 166 | /// The pin may have switched back to high before the task was run after 167 | /// being woken. The future should still resolve in that case. 168 | async fn wait_for_low(&mut self) -> Result<(), Self::Error>; 169 | 170 | /// Wait for the pin to undergo a transition from low to high. 171 | /// 172 | /// If the pin is already high, this does *not* return immediately, it'll wait for the 173 | /// pin to go low and then high again. 174 | async fn wait_for_rising_edge(&mut self) -> Result<(), Self::Error>; 175 | 176 | /// Wait for the pin to undergo a transition from high to low. 177 | /// 178 | /// If the pin is already low, this does *not* return immediately, it'll wait for the 179 | /// pin to go high and then low again. 180 | async fn wait_for_falling_edge(&mut self) -> Result<(), Self::Error>; 181 | 182 | /// Wait for the pin to undergo any transition, i.e low to high OR high to low. 183 | async fn wait_for_any_edge(&mut self) -> Result<(), Self::Error>; 184 | } 185 | 186 | impl Wait for &mut T { 187 | #[inline] 188 | async fn wait_for_high(&mut self) -> Result<(), Self::Error> { 189 | T::wait_for_high(self).await 190 | } 191 | 192 | #[inline] 193 | async fn wait_for_low(&mut self) -> Result<(), Self::Error> { 194 | T::wait_for_low(self).await 195 | } 196 | 197 | #[inline] 198 | async fn wait_for_rising_edge(&mut self) -> Result<(), Self::Error> { 199 | T::wait_for_rising_edge(self).await 200 | } 201 | 202 | #[inline] 203 | async fn wait_for_falling_edge(&mut self) -> Result<(), Self::Error> { 204 | T::wait_for_falling_edge(self).await 205 | } 206 | 207 | #[inline] 208 | async fn wait_for_any_edge(&mut self) -> Result<(), Self::Error> { 209 | T::wait_for_any_edge(self).await 210 | } 211 | } 212 | -------------------------------------------------------------------------------- /embedded-hal-async/src/i2c.rs: -------------------------------------------------------------------------------- 1 | //! Async I2C API. 2 | //! 3 | //! This API supports 7-bit and 10-bit addresses. Traits feature an `AddressMode` 4 | //! marker type parameter. Two implementation of the `AddressMode` exist: 5 | //! `SevenBitAddress` and `TenBitAddress`. 6 | //! 7 | //! Through this marker types it is possible to implement each address mode for 8 | //! the traits independently in `embedded-hal` implementations and device drivers 9 | //! can depend only on the mode that they support. 10 | //! 11 | //! Additionally, the I2C 10-bit address mode has been developed to be fully 12 | //! backwards compatible with the 7-bit address mode. This allows for a 13 | //! software-emulated 10-bit addressing implementation if the address mode 14 | //! is not supported by the hardware. 15 | //! 16 | //! Since 7-bit addressing is the mode of the majority of I2C devices, 17 | //! `SevenBitAddress` has been set as default mode and thus can be omitted if desired. 18 | 19 | pub use embedded_hal::i2c::{ 20 | AddressMode, Error, ErrorKind, ErrorType, NoAcknowledgeSource, Operation, SevenBitAddress, 21 | TenBitAddress, 22 | }; 23 | 24 | /// Async I2c. 25 | pub trait I2c: ErrorType { 26 | /// Reads enough bytes from slave with `address` to fill `buffer`. 27 | /// 28 | /// # I2C Events (contract) 29 | /// 30 | /// ``` text 31 | /// Master: ST SAD+R MAK MAK ... NMAK SP 32 | /// Slave: SAK B0 B1 ... BN 33 | /// ``` 34 | /// 35 | /// Where 36 | /// 37 | /// - `ST` = start condition 38 | /// - `SAD+R` = slave address followed by bit 1 to indicate reading 39 | /// - `SAK` = slave acknowledge 40 | /// - `Bi` = ith byte of data 41 | /// - `MAK` = master acknowledge 42 | /// - `NMAK` = master no acknowledge 43 | /// - `SP` = stop condition 44 | #[inline] 45 | async fn read(&mut self, address: A, read: &mut [u8]) -> Result<(), Self::Error> { 46 | self.transaction(address, &mut [Operation::Read(read)]) 47 | .await 48 | } 49 | 50 | /// Writes bytes to slave with address `address`. 51 | /// 52 | /// # I2C Events (contract) 53 | /// 54 | /// ``` text 55 | /// Master: ST SAD+W B0 B1 ... BN SP 56 | /// Slave: SAK SAK SAK ... SAK 57 | /// ``` 58 | /// 59 | /// Where 60 | /// 61 | /// - `ST` = start condition 62 | /// - `SAD+W` = slave address followed by bit 0 to indicate writing 63 | /// - `SAK` = slave acknowledge 64 | /// - `Bi` = ith byte of data 65 | /// - `SP` = stop condition 66 | #[inline] 67 | async fn write(&mut self, address: A, write: &[u8]) -> Result<(), Self::Error> { 68 | self.transaction(address, &mut [Operation::Write(write)]) 69 | .await 70 | } 71 | 72 | /// Writes bytes to slave with address `address` and then reads enough bytes to fill `read` *in a 73 | /// single transaction*. 74 | /// 75 | /// # I2C Events (contract) 76 | /// 77 | /// ``` text 78 | /// Master: ST SAD+W O0 O1 ... OM SR SAD+R MAK MAK ... NMAK SP 79 | /// Slave: SAK SAK SAK ... SAK SAK I0 I1 ... IN 80 | /// ``` 81 | /// 82 | /// Where 83 | /// 84 | /// - `ST` = start condition 85 | /// - `SAD+W` = slave address followed by bit 0 to indicate writing 86 | /// - `SAK` = slave acknowledge 87 | /// - `Oi` = ith outgoing byte of data 88 | /// - `SR` = repeated start condition 89 | /// - `SAD+R` = slave address followed by bit 1 to indicate reading 90 | /// - `Ii` = ith incoming byte of data 91 | /// - `MAK` = master acknowledge 92 | /// - `NMAK` = master no acknowledge 93 | /// - `SP` = stop condition 94 | #[inline] 95 | async fn write_read( 96 | &mut self, 97 | address: A, 98 | write: &[u8], 99 | read: &mut [u8], 100 | ) -> Result<(), Self::Error> { 101 | self.transaction( 102 | address, 103 | &mut [Operation::Write(write), Operation::Read(read)], 104 | ) 105 | .await 106 | } 107 | 108 | /// Execute the provided operations on the I2C bus as a single transaction. 109 | /// 110 | /// Transaction contract: 111 | /// - Before executing the first operation an ST is sent automatically. This is followed by SAD+R/W as appropriate. 112 | /// - Data from adjacent operations of the same type are sent after each other without an SP or SR. 113 | /// - Between adjacent operations of a different type an SR and SAD+R/W is sent. 114 | /// - After executing the last operation an SP is sent automatically. 115 | /// - At the end of each read operation (before SP or SR), the master does not send an acknowledge for the last byte. 116 | /// 117 | /// - `ST` = start condition 118 | /// - `SAD+R/W` = slave address followed by bit 1 to indicate reading or 0 to indicate writing 119 | /// - `SR` = repeated start condition 120 | /// - `SP` = stop condition 121 | async fn transaction( 122 | &mut self, 123 | address: A, 124 | operations: &mut [Operation<'_>], 125 | ) -> Result<(), Self::Error>; 126 | } 127 | 128 | impl + ?Sized> I2c for &mut T { 129 | #[inline] 130 | async fn read(&mut self, address: A, read: &mut [u8]) -> Result<(), Self::Error> { 131 | T::read(self, address, read).await 132 | } 133 | 134 | #[inline] 135 | async fn write(&mut self, address: A, write: &[u8]) -> Result<(), Self::Error> { 136 | T::write(self, address, write).await 137 | } 138 | 139 | #[inline] 140 | async fn write_read( 141 | &mut self, 142 | address: A, 143 | write: &[u8], 144 | read: &mut [u8], 145 | ) -> Result<(), Self::Error> { 146 | T::write_read(self, address, write, read).await 147 | } 148 | 149 | #[inline] 150 | async fn transaction( 151 | &mut self, 152 | address: A, 153 | operations: &mut [Operation<'_>], 154 | ) -> Result<(), Self::Error> { 155 | T::transaction(self, address, operations).await 156 | } 157 | } 158 | -------------------------------------------------------------------------------- /embedded-hal-async/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![doc = include_str!("../README.md")] 2 | #![warn(missing_docs)] 3 | #![no_std] 4 | #![allow(async_fn_in_trait)] 5 | 6 | pub mod delay; 7 | pub mod digital; 8 | pub mod i2c; 9 | pub mod spi; 10 | -------------------------------------------------------------------------------- /embedded-hal-async/src/spi.rs: -------------------------------------------------------------------------------- 1 | //! SPI master mode traits. 2 | 3 | pub use embedded_hal::spi::{ 4 | Error, ErrorKind, ErrorType, Mode, Operation, Phase, Polarity, MODE_0, MODE_1, MODE_2, MODE_3, 5 | }; 6 | 7 | /// SPI device trait. 8 | /// 9 | /// `SpiDevice` represents ownership over a single SPI device on a (possibly shared) bus, selected 10 | /// with a CS (Chip Select) pin. 11 | /// 12 | /// See [the docs on embedded-hal](embedded_hal::spi) for important information on SPI Bus vs Device traits. 13 | pub trait SpiDevice: ErrorType { 14 | /// Perform a transaction against the device. 15 | /// 16 | /// - Locks the bus 17 | /// - Asserts the CS (Chip Select) pin. 18 | /// - Performs all the operations. 19 | /// - [Flushes](SpiBus::flush) the bus. 20 | /// - Deasserts the CS pin. 21 | /// - Unlocks the bus. 22 | /// 23 | /// The locking mechanism is implementation-defined. The only requirement is it must prevent two 24 | /// transactions from executing concurrently against the same bus. Examples of implementations are: 25 | /// critical sections, blocking mutexes, returning an error or panicking if the bus is already busy. 26 | /// 27 | /// On bus errors the implementation should try to deassert CS. 28 | /// If an error occurs while deasserting CS the bus error should take priority as the return value. 29 | async fn transaction( 30 | &mut self, 31 | operations: &mut [Operation<'_, Word>], 32 | ) -> Result<(), Self::Error>; 33 | 34 | /// Do a read within a transaction. 35 | /// 36 | /// This is a convenience method equivalent to `device.read_transaction(&mut [buf])`. 37 | /// 38 | /// See also: [`SpiDevice::transaction`], [`SpiDevice::read`] 39 | #[inline] 40 | async fn read(&mut self, buf: &mut [Word]) -> Result<(), Self::Error> { 41 | self.transaction(&mut [Operation::Read(buf)]).await 42 | } 43 | 44 | /// Do a write within a transaction. 45 | /// 46 | /// This is a convenience method equivalent to `device.write_transaction(&mut [buf])`. 47 | /// 48 | /// See also: [`SpiDevice::transaction`], [`SpiDevice::write`] 49 | #[inline] 50 | async fn write(&mut self, buf: &[Word]) -> Result<(), Self::Error> { 51 | self.transaction(&mut [Operation::Write(buf)]).await 52 | } 53 | 54 | /// Do a transfer within a transaction. 55 | /// 56 | /// This is a convenience method equivalent to `device.transaction(&mut [Operation::Transfer(read, write)])`. 57 | /// 58 | /// See also: [`SpiDevice::transaction`], [`SpiBus::transfer`] 59 | #[inline] 60 | async fn transfer(&mut self, read: &mut [Word], write: &[Word]) -> Result<(), Self::Error> { 61 | self.transaction(&mut [Operation::Transfer(read, write)]) 62 | .await 63 | } 64 | 65 | /// Do an in-place transfer within a transaction. 66 | /// 67 | /// This is a convenience method equivalent to `device.transaction(&mut [Operation::TransferInPlace(buf)])`. 68 | /// 69 | /// See also: [`SpiDevice::transaction`], [`SpiBus::transfer_in_place`] 70 | #[inline] 71 | async fn transfer_in_place(&mut self, buf: &mut [Word]) -> Result<(), Self::Error> { 72 | self.transaction(&mut [Operation::TransferInPlace(buf)]) 73 | .await 74 | } 75 | } 76 | 77 | impl + ?Sized> SpiDevice for &mut T { 78 | #[inline] 79 | async fn transaction( 80 | &mut self, 81 | operations: &mut [Operation<'_, Word>], 82 | ) -> Result<(), Self::Error> { 83 | T::transaction(self, operations).await 84 | } 85 | 86 | #[inline] 87 | async fn read(&mut self, buf: &mut [Word]) -> Result<(), Self::Error> { 88 | T::read(self, buf).await 89 | } 90 | 91 | #[inline] 92 | async fn write(&mut self, buf: &[Word]) -> Result<(), Self::Error> { 93 | T::write(self, buf).await 94 | } 95 | 96 | #[inline] 97 | async fn transfer(&mut self, read: &mut [Word], write: &[Word]) -> Result<(), Self::Error> { 98 | T::transfer(self, read, write).await 99 | } 100 | 101 | #[inline] 102 | async fn transfer_in_place(&mut self, buf: &mut [Word]) -> Result<(), Self::Error> { 103 | T::transfer_in_place(self, buf).await 104 | } 105 | } 106 | 107 | /// SPI bus. 108 | /// 109 | /// `SpiBus` represents **exclusive ownership** over the whole SPI bus, with SCK, MOSI and MISO pins. 110 | /// 111 | /// See [the docs on embedded-hal][embedded_hal::spi] for important information on SPI Bus vs Device traits. 112 | pub trait SpiBus: ErrorType { 113 | /// Read `words` from the slave. 114 | /// 115 | /// The word value sent on MOSI during reading is implementation-defined, 116 | /// typically `0x00`, `0xFF`, or configurable. 117 | /// 118 | /// Implementations are allowed to return before the operation is 119 | /// complete. See [the docs on embedded-hal][embedded_hal::spi] for details on flushing. 120 | async fn read(&mut self, words: &mut [Word]) -> Result<(), Self::Error>; 121 | 122 | /// Write `words` to the slave, ignoring all the incoming words. 123 | /// 124 | /// Implementations are allowed to return before the operation is 125 | /// complete. See [the docs on embedded-hal][embedded_hal::spi] for details on flushing. 126 | async fn write(&mut self, words: &[Word]) -> Result<(), Self::Error>; 127 | 128 | /// Write and read simultaneously. `write` is written to the slave on MOSI and 129 | /// words received on MISO are stored in `read`. 130 | /// 131 | /// It is allowed for `read` and `write` to have different lengths, even zero length. 132 | /// The transfer runs for `max(read.len(), write.len())` words. If `read` is shorter, 133 | /// incoming words after `read` has been filled will be discarded. If `write` is shorter, 134 | /// the value of words sent in MOSI after all `write` has been sent is implementation-defined, 135 | /// typically `0x00`, `0xFF`, or configurable. 136 | /// 137 | /// Implementations are allowed to return before the operation is 138 | /// complete. See [the docs on embedded-hal][embedded_hal::spi] for details on flushing. 139 | async fn transfer(&mut self, read: &mut [Word], write: &[Word]) -> Result<(), Self::Error>; 140 | 141 | /// Write and read simultaneously. The contents of `words` are 142 | /// written to the slave, and the received words are stored into the same 143 | /// `words` buffer, overwriting it. 144 | /// 145 | /// Implementations are allowed to return before the operation is 146 | /// complete. See [the docs on embedded-hal][embedded_hal::spi] for details on flushing. 147 | async fn transfer_in_place(&mut self, words: &mut [Word]) -> Result<(), Self::Error>; 148 | 149 | /// Wait until all operations have completed and the bus is idle. 150 | /// 151 | /// See [the docs on embedded-hal][embedded_hal::spi] for information on flushing. 152 | async fn flush(&mut self) -> Result<(), Self::Error>; 153 | } 154 | 155 | impl + ?Sized, Word: 'static + Copy> SpiBus for &mut T { 156 | #[inline] 157 | async fn read(&mut self, words: &mut [Word]) -> Result<(), Self::Error> { 158 | T::read(self, words).await 159 | } 160 | 161 | #[inline] 162 | async fn write(&mut self, words: &[Word]) -> Result<(), Self::Error> { 163 | T::write(self, words).await 164 | } 165 | 166 | #[inline] 167 | async fn transfer(&mut self, read: &mut [Word], write: &[Word]) -> Result<(), Self::Error> { 168 | T::transfer(self, read, write).await 169 | } 170 | 171 | #[inline] 172 | async fn transfer_in_place(&mut self, words: &mut [Word]) -> Result<(), Self::Error> { 173 | T::transfer_in_place(self, words).await 174 | } 175 | 176 | #[inline] 177 | async fn flush(&mut self) -> Result<(), Self::Error> { 178 | T::flush(self).await 179 | } 180 | } 181 | -------------------------------------------------------------------------------- /embedded-hal-bus/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | The format is based on [Keep a Changelog](http://keepachangelog.com/) 6 | and this project adheres to [Semantic Versioning](http://semver.org/). 7 | 8 | ## [Unreleased] 9 | 10 | - Your change here! 11 | 12 | ## [v0.3.0] - 2025-01-21 13 | 14 | - `AtomicDevice` now requires enabling feature `portable-atomic` on `thumbv6m-none-eabi` and other targets that don't have atomic CAS. 15 | - Added the `alloc` feature. 16 | - Added a new `RcDevice` for I2C and SPI, a reference-counting equivalent to `RefCellDevice`. 17 | - Migrated `std` feature-gated `std::error::Error` implementations to `core::error::Error` 18 | - Increased MSRV to 1.81 due to `core::error::Error` 19 | 20 | ## [v0.2.0] - 2024-04-23 21 | 22 | - Added a new `AtomicDevice` for I2C and SPI to enable bus sharing across multiple contexts. 23 | - SPI shared bus constructors now set `CS` high, to prevent sharing issues if it was low. 24 | 25 | ## [v0.1.0] - 2023-12-28 26 | 27 | - Updated `embedded-hal` to version `1.0.0`. 28 | 29 | ## [v0.1.0-rc.3] - 2023-12-14 30 | 31 | - Updated `embedded-hal` to version `1.0.0-rc.3`. 32 | 33 | ## [v0.1.0-rc.2] - 2023-11-28 34 | 35 | - Updated `embedded-hal(-async)` to version `1.0.0-rc.2`. 36 | - Minor document fixes. 37 | - Add #[inline] hints to most of `embedded-hal-bus` functions. 38 | - Use `feature()` on nightly toolchains only. This adds async support for 1.75 beta and stable. 39 | 40 | ## [v0.1.0-rc.1] - 2023-08-15 41 | 42 | - Updated `embedded-hal`, `embedded-hal-async` to version `1.0.0-rc.1`. 43 | - The Minimum Supported Rust Version (MSRV) is now 1.60.0 44 | - Added `embedded-hal-async` support to SPI `ExclusiveDevice`. 45 | - Added methods to access the inner bus to SPI `ExclusiveDevice`. 46 | - Add optional `defmt` 0.3 support. 47 | 48 | ## [v0.1.0-alpha.3] - 2023-07-04 49 | 50 | ### Changed 51 | - Updated `embedded-hal` to version `1.0.0-alpha.11`. 52 | 53 | 54 | ## [v0.1.0-alpha.2] - 2023-04-04 55 | 56 | ### Changed 57 | - Updated `embedded-hal` to version `1.0.0-alpha.10`. 58 | 59 | ### Added 60 | - i2c: add bus sharing implementations. 61 | - spi: add bus sharing implementations. 62 | 63 | ## [v0.1.0-alpha.1] - 2022-09-28 64 | 65 | ### Changed 66 | - Updated `embedded-hal` to version `1.0.0-alpha.9`. 67 | 68 | ## [v0.1.0-alpha.0] - 2022-08-17 69 | 70 | First release to crates.io 71 | 72 | [Unreleased]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-bus-v0.3.0...HEAD 73 | [v0.3.0]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-bus-v0.2.0...embedded-hal-bus-v0.3.0 74 | [v0.2.0]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-bus-v0.1.0...embedded-hal-bus-v0.2.0 75 | [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 76 | [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 77 | [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 78 | [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 79 | [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 80 | [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 81 | [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 82 | [v0.1.0-alpha.0]: https://github.com/rust-embedded/embedded-hal/tree/embedded-hal-bus-v0.1.0-alpha.0 83 | -------------------------------------------------------------------------------- /embedded-hal-bus/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | authors = [ 3 | "The Embedded HAL Team and Contributors ", 4 | ] 5 | categories = ["embedded", "hardware-support", "no-std"] 6 | description = "Bus/Device connection mechanisms for embedded-hal, a Hardware Abstraction Layer (HAL) for embedded systems" 7 | documentation = "https://docs.rs/embedded-hal-bus" 8 | edition = "2021" 9 | rust-version = "1.81" 10 | keywords = ["hal", "IO"] 11 | license = "MIT OR Apache-2.0" 12 | name = "embedded-hal-bus" 13 | readme = "README.md" 14 | repository = "https://github.com/rust-embedded/embedded-hal" 15 | version = "0.3.0" 16 | 17 | [features] 18 | # Enable shared bus implementations using `std::sync::Mutex` 19 | std = ["alloc"] 20 | # Use `portable-atomic` to enable `atomic-device` on devices without native atomic CAS 21 | # 22 | # `portable-atomic` emulates atomic CAS functionality, allowing `embedded-hal-bus` to use `atomic-device` on hardware 23 | # that does not natively support atomic CAS. If you enable this, you must also add `portable-atomic` to your crate with 24 | # a feature flag such as `unsafe-assume-single-core` or `critical-section` to choose how atomic CAS is implemented. 25 | # See https://docs.rs/portable-atomic/1.7.0/portable_atomic/#optional-features for more info. 26 | portable-atomic = ["dep:portable-atomic"] 27 | # Enable `embedded-hal-async` support. 28 | async = ["dep:embedded-hal-async"] 29 | # Derive `defmt::Format` from `defmt` 0.3 for enums and structs. See https://github.com/knurling-rs/defmt for more info 30 | defmt-03 = ["dep:defmt-03", "embedded-hal/defmt-03", "embedded-hal-async?/defmt-03"] 31 | # Enables additional utilities requiring a global allocator. 32 | alloc = [] 33 | 34 | [dependencies] 35 | embedded-hal = { version = "1.0.0", path = "../embedded-hal" } 36 | embedded-hal-async = { version = "1.0.0", path = "../embedded-hal-async", optional = true } 37 | critical-section = { version = "1.0" } 38 | defmt-03 = { package = "defmt", version = "0.3", optional = true } 39 | portable-atomic = {version = "1.3", default-features = false, optional = true, features = ["require-cas"]} 40 | 41 | [package.metadata.docs.rs] 42 | features = ["std", "async"] 43 | rustdoc-args = ["--cfg", "docsrs"] 44 | -------------------------------------------------------------------------------- /embedded-hal-bus/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) 2021-2025 The Rust embedded HAL team and contributors. 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 7 | of the Software, and to permit persons to whom the Software is furnished to do 8 | so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /embedded-hal-bus/README.md: -------------------------------------------------------------------------------- 1 | [![crates.io](https://img.shields.io/crates/d/embedded-hal-bus.svg)](https://crates.io/crates/embedded-hal-bus) 2 | [![crates.io](https://img.shields.io/crates/v/embedded-hal-bus.svg)](https://crates.io/crates/embedded-hal-bus) 3 | [![Documentation](https://docs.rs/embedded-hal-bus/badge.svg)](https://docs.rs/embedded-hal-bus) 4 | ![Minimum Supported Rust Version](https://img.shields.io/badge/rustc-1.60+-blue.svg) 5 | 6 | # `embedded-hal-bus` 7 | 8 | Bus sharing utilities for [`embedded-hal`](https://crates.io/crates/embedded-hal), a Hardware Abstraction Layer (HAL) for embedded systems. 9 | 10 | `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. 11 | 12 | This project is developed and maintained by the [HAL team](https://github.com/rust-embedded/wg#the-hal-team). 13 | 14 | ## SPI 15 | 16 | To support bus sharing, `embedded-hal` provides the `SpiBus` and `SpiDevice` traits. `SpiBus` represents an entire bus, 17 | while `SpiDevice` represents a device on that bus. For further details on these traits, please consult the 18 | [`embedded-hal` documentation](https://docs.rs/embedded-hal/latest/embedded_hal/spi/index.html). 19 | 20 | `embedded-hal` trait implementations for microcontrollers should implement the `SpiBus` trait. 21 | However, device drivers should use the `SpiDevice` traits, _not the `SpiBus` traits_ if at all possible 22 | in order to allow for sharing of the bus they are connected to. 23 | 24 | This crate provides mechanisms to connect a `SpiBus` and a `SpiDevice`. 25 | 26 | ## I2C 27 | 28 | In the case of I2C, the same `I2c` `embedded-hal` trait represents either an entire bus, or a device on a bus. This crate 29 | provides mechanisms to obtain multiple `I2c` instances out of a single `I2c` instance, sharing the bus. 30 | 31 | ## Optional Cargo features 32 | 33 | - **`async`**: enable `embedded-hal-async` support. 34 | - **`defmt-03`**: Derive `defmt::Format` from `defmt` 0.3 for enums and structs. 35 | - **`alloc`**: enable implementations using `alloc` (for instance, `spi::RcDevice`, which makes use of `alloc::rc::Rc`) 36 | - **`portable-atomic`**: Use `portable-atomic` to enable `atomic-device` on devices without native atomic CAS 37 | 38 | `portable-atomic` emulates atomic CAS functionality, allowing `embedded-hal-bus` to use `atomic-device` on hardware 39 | that does not natively support atomic CAS. If you enable this, you must also add `portable-atomic` to your crate with 40 | a feature flag such as `unsafe-assume-single-core` or `critical-section` to choose how atomic CAS is implemented. 41 | See for more info. 42 | - **`std`**: enable shared bus implementations using `std::sync::Mutex`. 43 | 44 | ## Minimum Supported Rust Version (MSRV) 45 | 46 | This crate is guaranteed to compile on stable Rust 1.60 and up. It *might* 47 | compile with older versions but that may change in any new patch release. 48 | 49 | See [here](../docs/msrv.md) for details on how the MSRV may be upgraded. 50 | 51 | Enabling the `async` Cargo features requires Rust 1.75 or higher. 52 | 53 | ## License 54 | 55 | Licensed under either of 56 | 57 | - Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or 58 | ) 59 | - MIT license ([LICENSE-MIT](LICENSE-MIT) or ) 60 | 61 | at your option. 62 | 63 | ### Contribution 64 | 65 | Unless you explicitly state otherwise, any contribution intentionally submitted 66 | for inclusion in the work by you, as defined in the Apache-2.0 license, shall be 67 | dual licensed as above, without any additional terms or conditions. 68 | -------------------------------------------------------------------------------- /embedded-hal-bus/src/i2c/atomic.rs: -------------------------------------------------------------------------------- 1 | use embedded_hal::i2c::{Error, ErrorKind, ErrorType, I2c}; 2 | 3 | use crate::util::AtomicCell; 4 | 5 | /// Atomics-based shared bus [`I2c`] implementation. 6 | /// 7 | /// Sharing is implemented with a [`AtomicDevice`], which consists of an `UnsafeCell` and an `AtomicBool` "locked" flag. 8 | /// This means it has low overhead, like [`RefCellDevice`](crate::i2c::RefCellDevice). Aditionally, it is `Send`, 9 | /// which allows sharing a single bus across multiple threads (interrupt priority level), like [`CriticalSectionDevice`](crate::i2c::CriticalSectionDevice), 10 | /// while not using critical sections and therefore impacting real-time performance less. 11 | /// 12 | /// The downside is using a simple `AtomicBool` for locking doesn't prevent two threads from trying to lock it at once. 13 | /// For example, the main thread can be doing an I2C transaction, and an interrupt fires and tries to do another. In this 14 | /// case, a `Busy` error is returned that must be handled somehow, usually dropping the data or trying again later. 15 | /// 16 | /// Note that retrying in a loop on `Busy` errors usually leads to deadlocks. In the above example, it'll prevent the 17 | /// interrupt handler from returning, which will starve the main thread and prevent it from releasing the lock. If 18 | /// this is an issue for your use case, you most likely should use [`CriticalSectionDevice`](crate::i2c::CriticalSectionDevice) instead. 19 | /// 20 | /// This primitive is particularly well-suited for applications that have external arbitration 21 | /// rules that prevent `Busy` errors in the first place, such as the RTIC framework. 22 | /// 23 | /// # Examples 24 | /// 25 | /// Assuming there is a pressure sensor with address `0x42` on the same bus as a temperature sensor 26 | /// with address `0x20`; [`AtomicDevice`] can be used to give access to both of these sensors 27 | /// from a single `i2c` instance. 28 | /// 29 | /// ``` 30 | /// use embedded_hal_bus::i2c; 31 | /// use embedded_hal_bus::util::AtomicCell; 32 | /// # use embedded_hal::i2c::{self as hali2c, SevenBitAddress, TenBitAddress, I2c, Operation, ErrorKind}; 33 | /// # pub struct Sensor { 34 | /// # i2c: I2C, 35 | /// # address: u8, 36 | /// # } 37 | /// # impl Sensor { 38 | /// # pub fn new(i2c: I2C, address: u8) -> Self { 39 | /// # Self { i2c, address } 40 | /// # } 41 | /// # } 42 | /// # type PressureSensor = Sensor; 43 | /// # type TemperatureSensor = Sensor; 44 | /// # pub struct I2c0; 45 | /// # #[derive(Debug, Copy, Clone, Eq, PartialEq)] 46 | /// # pub enum Error { } 47 | /// # impl hali2c::Error for Error { 48 | /// # fn kind(&self) -> hali2c::ErrorKind { 49 | /// # ErrorKind::Other 50 | /// # } 51 | /// # } 52 | /// # impl hali2c::ErrorType for I2c0 { 53 | /// # type Error = Error; 54 | /// # } 55 | /// # impl I2c for I2c0 { 56 | /// # fn transaction(&mut self, address: u8, operations: &mut [Operation<'_>]) -> Result<(), Self::Error> { 57 | /// # Ok(()) 58 | /// # } 59 | /// # } 60 | /// # struct Hal; 61 | /// # impl Hal { 62 | /// # fn i2c(&self) -> I2c0 { 63 | /// # I2c0 64 | /// # } 65 | /// # } 66 | /// # let hal = Hal; 67 | /// 68 | /// let i2c = hal.i2c(); 69 | /// let i2c_cell = AtomicCell::new(i2c); 70 | /// let mut temperature_sensor = TemperatureSensor::new( 71 | /// i2c::AtomicDevice::new(&i2c_cell), 72 | /// 0x20, 73 | /// ); 74 | /// let mut pressure_sensor = PressureSensor::new( 75 | /// i2c::AtomicDevice::new(&i2c_cell), 76 | /// 0x42, 77 | /// ); 78 | /// ``` 79 | pub struct AtomicDevice<'a, T> { 80 | bus: &'a AtomicCell, 81 | } 82 | 83 | #[derive(Debug, Copy, Clone)] 84 | /// Wrapper type for errors originating from the atomically-checked I2C bus manager. 85 | pub enum AtomicError { 86 | /// This error is returned if the I2C bus was already in use when an operation was attempted, 87 | /// which indicates that the driver requirements are not being met with regard to 88 | /// synchronization. 89 | Busy, 90 | 91 | /// An I2C-related error occurred, and the internal error should be inspected. 92 | Other(T), 93 | } 94 | 95 | impl Error for AtomicError { 96 | fn kind(&self) -> ErrorKind { 97 | match self { 98 | AtomicError::Other(e) => e.kind(), 99 | _ => ErrorKind::Other, 100 | } 101 | } 102 | } 103 | 104 | unsafe impl Send for AtomicDevice<'_, T> {} 105 | 106 | impl<'a, T> AtomicDevice<'a, T> 107 | where 108 | T: I2c, 109 | { 110 | /// Create a new `AtomicDevice`. 111 | #[inline] 112 | pub fn new(bus: &'a AtomicCell) -> Self { 113 | Self { bus } 114 | } 115 | 116 | fn lock(&self, f: F) -> Result> 117 | where 118 | F: FnOnce(&mut T) -> Result::Error>, 119 | { 120 | self.bus 121 | .busy 122 | .compare_exchange( 123 | false, 124 | true, 125 | core::sync::atomic::Ordering::SeqCst, 126 | core::sync::atomic::Ordering::SeqCst, 127 | ) 128 | .map_err(|_| AtomicError::::Busy)?; 129 | 130 | let result = f(unsafe { &mut *self.bus.bus.get() }); 131 | 132 | self.bus 133 | .busy 134 | .store(false, core::sync::atomic::Ordering::SeqCst); 135 | 136 | result.map_err(AtomicError::Other) 137 | } 138 | } 139 | 140 | impl ErrorType for AtomicDevice<'_, T> 141 | where 142 | T: I2c, 143 | { 144 | type Error = AtomicError; 145 | } 146 | 147 | impl I2c for AtomicDevice<'_, T> 148 | where 149 | T: I2c, 150 | { 151 | #[inline] 152 | fn read(&mut self, address: u8, read: &mut [u8]) -> Result<(), Self::Error> { 153 | self.lock(|bus| bus.read(address, read)) 154 | } 155 | 156 | #[inline] 157 | fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Self::Error> { 158 | self.lock(|bus| bus.write(address, write)) 159 | } 160 | 161 | #[inline] 162 | fn write_read( 163 | &mut self, 164 | address: u8, 165 | write: &[u8], 166 | read: &mut [u8], 167 | ) -> Result<(), Self::Error> { 168 | self.lock(|bus| bus.write_read(address, write, read)) 169 | } 170 | 171 | #[inline] 172 | fn transaction( 173 | &mut self, 174 | address: u8, 175 | operations: &mut [embedded_hal::i2c::Operation<'_>], 176 | ) -> Result<(), Self::Error> { 177 | self.lock(|bus| bus.transaction(address, operations)) 178 | } 179 | } 180 | -------------------------------------------------------------------------------- /embedded-hal-bus/src/i2c/critical_section.rs: -------------------------------------------------------------------------------- 1 | use core::cell::RefCell; 2 | use critical_section::Mutex; 3 | use embedded_hal::i2c::{ErrorType, I2c}; 4 | 5 | /// `critical-section`-based shared bus [`I2c`] implementation. 6 | /// 7 | /// Sharing is implemented with a `critical-section` [`Mutex`]. A critical section is taken for 8 | /// the entire duration of a transaction. This allows sharing a single bus across multiple threads (interrupt priority levels). 9 | /// The downside is critical sections typically require globally disabling interrupts, so `CriticalSectionDevice` will likely 10 | /// negatively impact real-time properties, such as interrupt latency. If you can, prefer using 11 | /// [`RefCellDevice`](super::RefCellDevice) instead, which does not require taking critical sections. 12 | pub struct CriticalSectionDevice<'a, T> { 13 | bus: &'a Mutex>, 14 | } 15 | 16 | impl<'a, T> CriticalSectionDevice<'a, T> { 17 | /// Create a new `CriticalSectionDevice`. 18 | #[inline] 19 | pub fn new(bus: &'a Mutex>) -> Self { 20 | Self { bus } 21 | } 22 | } 23 | 24 | impl ErrorType for CriticalSectionDevice<'_, T> 25 | where 26 | T: I2c, 27 | { 28 | type Error = T::Error; 29 | } 30 | 31 | impl I2c for CriticalSectionDevice<'_, T> 32 | where 33 | T: I2c, 34 | { 35 | #[inline] 36 | fn read(&mut self, address: u8, read: &mut [u8]) -> Result<(), Self::Error> { 37 | critical_section::with(|cs| { 38 | let bus = &mut *self.bus.borrow_ref_mut(cs); 39 | bus.read(address, read) 40 | }) 41 | } 42 | 43 | #[inline] 44 | fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Self::Error> { 45 | critical_section::with(|cs| { 46 | let bus = &mut *self.bus.borrow_ref_mut(cs); 47 | bus.write(address, write) 48 | }) 49 | } 50 | 51 | #[inline] 52 | fn write_read( 53 | &mut self, 54 | address: u8, 55 | write: &[u8], 56 | read: &mut [u8], 57 | ) -> Result<(), Self::Error> { 58 | critical_section::with(|cs| { 59 | let bus = &mut *self.bus.borrow_ref_mut(cs); 60 | bus.write_read(address, write, read) 61 | }) 62 | } 63 | 64 | #[inline] 65 | fn transaction( 66 | &mut self, 67 | address: u8, 68 | operations: &mut [embedded_hal::i2c::Operation<'_>], 69 | ) -> Result<(), Self::Error> { 70 | critical_section::with(|cs| { 71 | let bus = &mut *self.bus.borrow_ref_mut(cs); 72 | bus.transaction(address, operations) 73 | }) 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /embedded-hal-bus/src/i2c/mod.rs: -------------------------------------------------------------------------------- 1 | //! `I2c` shared bus implementations. 2 | 3 | mod refcell; 4 | pub use refcell::*; 5 | #[cfg(feature = "std")] 6 | mod mutex; 7 | #[cfg(feature = "std")] 8 | pub use mutex::*; 9 | mod critical_section; 10 | pub use self::critical_section::*; 11 | #[cfg(any(feature = "portable-atomic", target_has_atomic = "8"))] 12 | mod atomic; 13 | #[cfg(any(feature = "portable-atomic", target_has_atomic = "8"))] 14 | pub use atomic::*; 15 | 16 | #[cfg(feature = "alloc")] 17 | mod rc; 18 | #[cfg(feature = "alloc")] 19 | pub use rc::*; 20 | -------------------------------------------------------------------------------- /embedded-hal-bus/src/i2c/mutex.rs: -------------------------------------------------------------------------------- 1 | use embedded_hal::i2c::{ErrorType, I2c}; 2 | use std::sync::Mutex; 3 | 4 | /// `std` `Mutex`-based shared bus [`I2c`] implementation. 5 | /// 6 | /// Sharing is implemented with an `std` [`Mutex`]. It allows a single bus across multiple threads, 7 | /// with finer-grained locking than [`CriticalSectionDevice`](super::CriticalSectionDevice). The downside is that 8 | /// it is only available in `std` targets. 9 | #[cfg_attr(docsrs, doc(cfg(feature = "std")))] 10 | pub struct MutexDevice<'a, T> { 11 | bus: &'a Mutex, 12 | } 13 | 14 | impl<'a, T> MutexDevice<'a, T> { 15 | /// Create a new `MutexDevice`. 16 | #[inline] 17 | pub fn new(bus: &'a Mutex) -> Self { 18 | Self { bus } 19 | } 20 | } 21 | 22 | impl ErrorType for MutexDevice<'_, T> 23 | where 24 | T: I2c, 25 | { 26 | type Error = T::Error; 27 | } 28 | 29 | impl I2c for MutexDevice<'_, T> 30 | where 31 | T: I2c, 32 | { 33 | #[inline] 34 | fn read(&mut self, address: u8, read: &mut [u8]) -> Result<(), Self::Error> { 35 | let bus = &mut *self.bus.lock().unwrap(); 36 | bus.read(address, read) 37 | } 38 | 39 | #[inline] 40 | fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Self::Error> { 41 | let bus = &mut *self.bus.lock().unwrap(); 42 | bus.write(address, write) 43 | } 44 | 45 | #[inline] 46 | fn write_read( 47 | &mut self, 48 | address: u8, 49 | write: &[u8], 50 | read: &mut [u8], 51 | ) -> Result<(), Self::Error> { 52 | let bus = &mut *self.bus.lock().unwrap(); 53 | bus.write_read(address, write, read) 54 | } 55 | 56 | #[inline] 57 | fn transaction( 58 | &mut self, 59 | address: u8, 60 | operations: &mut [embedded_hal::i2c::Operation<'_>], 61 | ) -> Result<(), Self::Error> { 62 | let bus = &mut *self.bus.lock().unwrap(); 63 | bus.transaction(address, operations) 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /embedded-hal-bus/src/i2c/rc.rs: -------------------------------------------------------------------------------- 1 | extern crate alloc; 2 | use alloc::rc::Rc; 3 | 4 | use core::cell::RefCell; 5 | use embedded_hal::i2c::{ErrorType, I2c}; 6 | 7 | /// `Rc>`-based shared bus [`I2c`] implementation. 8 | /// This is the reference-counting equivalent of [`RefCellDevice`](super::RefCellDevice). 9 | /// 10 | /// Sharing is implemented with a [`RefCell`] and ownership is managed by [`Rc`]. 11 | /// Like [`RefCellDevice`](super::RefCellDevice), `RcDevice` instances are not [`Send`], 12 | /// so they can only be shared within a single thread (interrupt priority level). 13 | /// 14 | /// When this `RcDevice` is dropped, the reference count of the I2C bus will be decremented. 15 | /// Once that reference count hits zero, it will be cleaned up. 16 | #[cfg_attr(docsrs, doc(cfg(any(feature = "std", feature = "alloc"))))] 17 | pub struct RcDevice { 18 | bus: Rc>, 19 | } 20 | 21 | impl RcDevice { 22 | /// Creates a new `RcDevice`. 23 | /// 24 | /// This function does not increment the reference count for the bus: 25 | /// you will need to call `Rc::clone(&bus)` if you only have a `&Rc>`. 26 | #[inline] 27 | pub fn new(bus: Rc>) -> Self { 28 | Self { bus } 29 | } 30 | } 31 | 32 | impl ErrorType for RcDevice 33 | where 34 | Bus: ErrorType, 35 | { 36 | type Error = Bus::Error; 37 | } 38 | 39 | impl I2c for RcDevice 40 | where 41 | Bus: I2c, 42 | { 43 | #[inline] 44 | fn read(&mut self, address: u8, read: &mut [u8]) -> Result<(), Self::Error> { 45 | let bus = &mut *self.bus.borrow_mut(); 46 | bus.read(address, read) 47 | } 48 | 49 | #[inline] 50 | fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Self::Error> { 51 | let bus = &mut *self.bus.borrow_mut(); 52 | bus.write(address, write) 53 | } 54 | 55 | #[inline] 56 | fn write_read( 57 | &mut self, 58 | address: u8, 59 | write: &[u8], 60 | read: &mut [u8], 61 | ) -> Result<(), Self::Error> { 62 | let bus = &mut *self.bus.borrow_mut(); 63 | bus.write_read(address, write, read) 64 | } 65 | 66 | #[inline] 67 | fn transaction( 68 | &mut self, 69 | address: u8, 70 | operations: &mut [embedded_hal::i2c::Operation<'_>], 71 | ) -> Result<(), Self::Error> { 72 | let bus = &mut *self.bus.borrow_mut(); 73 | bus.transaction(address, operations) 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /embedded-hal-bus/src/i2c/refcell.rs: -------------------------------------------------------------------------------- 1 | use core::cell::RefCell; 2 | use embedded_hal::i2c::{ErrorType, I2c}; 3 | 4 | /// `RefCell`-based shared bus [`I2c`] implementation. 5 | /// 6 | /// Sharing is implemented with a `RefCell`. This means it has low overhead, but `RefCellDevice` instances are not `Send`, 7 | /// so it only allows sharing within a single thread (interrupt priority level). If you need to share a bus across several 8 | /// threads, use [`CriticalSectionDevice`](super::CriticalSectionDevice) instead. 9 | /// 10 | /// # Examples 11 | /// 12 | /// Assuming there is a pressure sensor with address `0x42` on the same bus as a temperature sensor 13 | /// with address `0x20`; [`RefCellDevice`] can be used to give access to both of these sensors 14 | /// from a single `i2c` instance. 15 | /// 16 | /// ``` 17 | /// use embedded_hal_bus::i2c; 18 | /// use core::cell::RefCell; 19 | /// # use embedded_hal::i2c::{self as hali2c, SevenBitAddress, TenBitAddress, I2c, Operation, ErrorKind}; 20 | /// # pub struct Sensor { 21 | /// # i2c: I2C, 22 | /// # address: u8, 23 | /// # } 24 | /// # impl Sensor { 25 | /// # pub fn new(i2c: I2C, address: u8) -> Self { 26 | /// # Self { i2c, address } 27 | /// # } 28 | /// # } 29 | /// # type PressureSensor = Sensor; 30 | /// # type TemperatureSensor = Sensor; 31 | /// # pub struct I2c0; 32 | /// # #[derive(Debug, Copy, Clone, Eq, PartialEq)] 33 | /// # pub enum Error { } 34 | /// # impl hali2c::Error for Error { 35 | /// # fn kind(&self) -> hali2c::ErrorKind { 36 | /// # ErrorKind::Other 37 | /// # } 38 | /// # } 39 | /// # impl hali2c::ErrorType for I2c0 { 40 | /// # type Error = Error; 41 | /// # } 42 | /// # impl I2c for I2c0 { 43 | /// # fn transaction(&mut self, address: u8, operations: &mut [Operation<'_>]) -> Result<(), Self::Error> { 44 | /// # Ok(()) 45 | /// # } 46 | /// # } 47 | /// # struct Hal; 48 | /// # impl Hal { 49 | /// # fn i2c(&self) -> I2c0 { 50 | /// # I2c0 51 | /// # } 52 | /// # } 53 | /// # let hal = Hal; 54 | /// 55 | /// let i2c = hal.i2c(); 56 | /// let i2c_ref_cell = RefCell::new(i2c); 57 | /// let mut temperature_sensor = TemperatureSensor::new( 58 | /// i2c::RefCellDevice::new(&i2c_ref_cell), 59 | /// 0x20, 60 | /// ); 61 | /// let mut pressure_sensor = PressureSensor::new( 62 | /// i2c::RefCellDevice::new(&i2c_ref_cell), 63 | /// 0x42, 64 | /// ); 65 | /// ``` 66 | pub struct RefCellDevice<'a, T> { 67 | bus: &'a RefCell, 68 | } 69 | 70 | impl<'a, T> RefCellDevice<'a, T> { 71 | /// Create a new `RefCellDevice`. 72 | #[inline] 73 | pub fn new(bus: &'a RefCell) -> Self { 74 | Self { bus } 75 | } 76 | } 77 | 78 | impl ErrorType for RefCellDevice<'_, T> 79 | where 80 | T: I2c, 81 | { 82 | type Error = T::Error; 83 | } 84 | 85 | impl I2c for RefCellDevice<'_, T> 86 | where 87 | T: I2c, 88 | { 89 | #[inline] 90 | fn read(&mut self, address: u8, read: &mut [u8]) -> Result<(), Self::Error> { 91 | let bus = &mut *self.bus.borrow_mut(); 92 | bus.read(address, read) 93 | } 94 | 95 | #[inline] 96 | fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Self::Error> { 97 | let bus = &mut *self.bus.borrow_mut(); 98 | bus.write(address, write) 99 | } 100 | 101 | #[inline] 102 | fn write_read( 103 | &mut self, 104 | address: u8, 105 | write: &[u8], 106 | read: &mut [u8], 107 | ) -> Result<(), Self::Error> { 108 | let bus = &mut *self.bus.borrow_mut(); 109 | bus.write_read(address, write, read) 110 | } 111 | 112 | #[inline] 113 | fn transaction( 114 | &mut self, 115 | address: u8, 116 | operations: &mut [embedded_hal::i2c::Operation<'_>], 117 | ) -> Result<(), Self::Error> { 118 | let bus = &mut *self.bus.borrow_mut(); 119 | bus.transaction(address, operations) 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /embedded-hal-bus/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![doc = include_str!("../README.md")] 2 | #![warn(missing_docs)] 3 | #![cfg_attr(not(feature = "std"), no_std)] 4 | #![cfg_attr(docsrs, feature(doc_cfg))] 5 | 6 | // needed to prevent defmt macros from breaking, since they emit code that does `defmt::blahblah`. 7 | #[cfg(feature = "defmt-03")] 8 | use defmt_03 as defmt; 9 | 10 | pub mod i2c; 11 | pub mod spi; 12 | pub mod util; 13 | -------------------------------------------------------------------------------- /embedded-hal-bus/src/spi/atomic.rs: -------------------------------------------------------------------------------- 1 | use embedded_hal::delay::DelayNs; 2 | use embedded_hal::digital::OutputPin; 3 | use embedded_hal::spi::{Error, ErrorKind, ErrorType, Operation, SpiBus, SpiDevice}; 4 | 5 | use super::DeviceError; 6 | use crate::spi::shared::transaction; 7 | use crate::util::AtomicCell; 8 | 9 | /// Atomics-based shared bus [`SpiDevice`] implementation. 10 | /// 11 | /// This allows for sharing an [`SpiBus`], obtaining multiple [`SpiDevice`] instances, 12 | /// each with its own `CS` pin. 13 | /// 14 | /// Sharing is implemented with a [`AtomicDevice`], which consists of an `UnsafeCell` and an `AtomicBool` "locked" flag. 15 | /// This means it has low overhead, like [`RefCellDevice`](crate::spi::RefCellDevice). Aditionally, it is `Send`, 16 | /// which allows sharing a single bus across multiple threads (interrupt priority level), like [`CriticalSectionDevice`](crate::spi::CriticalSectionDevice), 17 | /// while not using critical sections and therefore impacting real-time performance less. 18 | /// 19 | /// The downside is using a simple `AtomicBool` for locking doesn't prevent two threads from trying to lock it at once. 20 | /// For example, the main thread can be doing a SPI transaction, and an interrupt fires and tries to do another. In this 21 | /// case, a `Busy` error is returned that must be handled somehow, usually dropping the data or trying again later. 22 | /// 23 | /// Note that retrying in a loop on `Busy` errors usually leads to deadlocks. In the above example, it'll prevent the 24 | /// interrupt handler from returning, which will starve the main thread and prevent it from releasing the lock. If 25 | /// this is an issue for your use case, you most likely should use [`CriticalSectionDevice`](crate::spi::CriticalSectionDevice) instead. 26 | /// 27 | /// This primitive is particularly well-suited for applications that have external arbitration 28 | /// rules that prevent `Busy` errors in the first place, such as the RTIC framework. 29 | #[cfg_attr( 30 | docsrs, 31 | doc(cfg(any(feature = "portable-atomic", target_has_atomic = "8"))) 32 | )] 33 | pub struct AtomicDevice<'a, BUS, CS, D> { 34 | bus: &'a AtomicCell, 35 | cs: CS, 36 | delay: D, 37 | } 38 | 39 | #[derive(Debug, Copy, Clone)] 40 | /// Wrapper type for errors returned by [`AtomicDevice`]. 41 | #[cfg_attr( 42 | docsrs, 43 | doc(cfg(any(feature = "portable-atomic", target_has_atomic = "8"))) 44 | )] 45 | pub enum AtomicError { 46 | /// This error is returned if the SPI bus was already in use when an operation was attempted, 47 | /// which indicates that the driver requirements are not being met with regard to 48 | /// synchronization. 49 | Busy, 50 | 51 | /// An SPI-related error occurred, and the internal error should be inspected. 52 | Other(T), 53 | } 54 | 55 | impl<'a, BUS, CS, D> AtomicDevice<'a, BUS, CS, D> { 56 | /// Create a new [`AtomicDevice`]. 57 | /// 58 | /// This sets the `cs` pin high, and returns an error if that fails. It is recommended 59 | /// to set the pin high the moment it's configured as an output, to avoid glitches. 60 | #[inline] 61 | pub fn new(bus: &'a AtomicCell, mut cs: CS, delay: D) -> Result 62 | where 63 | CS: OutputPin, 64 | { 65 | cs.set_high()?; 66 | Ok(Self { bus, cs, delay }) 67 | } 68 | } 69 | 70 | impl<'a, BUS, CS> AtomicDevice<'a, BUS, CS, super::NoDelay> 71 | where 72 | BUS: ErrorType, 73 | CS: OutputPin, 74 | { 75 | /// Create a new [`AtomicDevice`] without support for in-transaction delays. 76 | /// 77 | /// This sets the `cs` pin high, and returns an error if that fails. It is recommended 78 | /// to set the pin high the moment it's configured as an output, to avoid glitches. 79 | /// 80 | /// **Warning**: The returned instance *technically* doesn't comply with the `SpiDevice` 81 | /// contract, which mandates delay support. It is relatively rare for drivers to use 82 | /// in-transaction delays, so you might still want to use this method because it's more practical. 83 | /// 84 | /// Note that a future version of the driver might start using delays, causing your 85 | /// code to panic. This wouldn't be considered a breaking change from the driver side, because 86 | /// drivers are allowed to assume `SpiDevice` implementations comply with the contract. 87 | /// If you feel this risk outweighs the convenience of having `cargo` automatically upgrade 88 | /// the driver crate, you might want to pin the driver's version. 89 | /// 90 | /// # Panics 91 | /// 92 | /// The returned device will panic if you try to execute a transaction 93 | /// that contains any operations of type [`Operation::DelayNs`]. 94 | #[inline] 95 | pub fn new_no_delay(bus: &'a AtomicCell, mut cs: CS) -> Result 96 | where 97 | CS: OutputPin, 98 | { 99 | cs.set_high()?; 100 | Ok(Self { 101 | bus, 102 | cs, 103 | delay: super::NoDelay, 104 | }) 105 | } 106 | } 107 | 108 | impl Error for AtomicError { 109 | fn kind(&self) -> ErrorKind { 110 | match self { 111 | AtomicError::Other(e) => e.kind(), 112 | _ => ErrorKind::Other, 113 | } 114 | } 115 | } 116 | 117 | impl ErrorType for AtomicDevice<'_, BUS, CS, D> 118 | where 119 | BUS: ErrorType, 120 | CS: OutputPin, 121 | { 122 | type Error = AtomicError>; 123 | } 124 | 125 | impl SpiDevice for AtomicDevice<'_, BUS, CS, D> 126 | where 127 | BUS: SpiBus, 128 | CS: OutputPin, 129 | D: DelayNs, 130 | { 131 | #[inline] 132 | fn transaction(&mut self, operations: &mut [Operation<'_, Word>]) -> Result<(), Self::Error> { 133 | self.bus 134 | .busy 135 | .compare_exchange( 136 | false, 137 | true, 138 | core::sync::atomic::Ordering::SeqCst, 139 | core::sync::atomic::Ordering::SeqCst, 140 | ) 141 | .map_err(|_| AtomicError::Busy)?; 142 | 143 | let bus = unsafe { &mut *self.bus.bus.get() }; 144 | 145 | let result = transaction(operations, bus, &mut self.delay, &mut self.cs); 146 | 147 | self.bus 148 | .busy 149 | .store(false, core::sync::atomic::Ordering::SeqCst); 150 | 151 | result.map_err(AtomicError::Other) 152 | } 153 | } 154 | -------------------------------------------------------------------------------- /embedded-hal-bus/src/spi/critical_section.rs: -------------------------------------------------------------------------------- 1 | use core::cell::RefCell; 2 | use critical_section::Mutex; 3 | use embedded_hal::delay::DelayNs; 4 | use embedded_hal::digital::OutputPin; 5 | use embedded_hal::spi::{ErrorType, Operation, SpiBus, SpiDevice}; 6 | 7 | use super::DeviceError; 8 | use crate::spi::shared::transaction; 9 | 10 | /// `critical-section`-based shared bus [`SpiDevice`] implementation. 11 | /// 12 | /// This allows for sharing an [`SpiBus`], obtaining multiple [`SpiDevice`] instances, 13 | /// each with its own `CS` pin. 14 | /// 15 | /// Sharing is implemented with a `critical-section` [`Mutex`]. A critical section is taken for 16 | /// the entire duration of a transaction. This allows sharing a single bus across multiple threads (interrupt priority levels). 17 | /// The downside is critical sections typically require globally disabling interrupts, so `CriticalSectionDevice` will likely 18 | /// negatively impact real-time properties, such as interrupt latency. If you can, prefer using 19 | /// [`RefCellDevice`](super::RefCellDevice) instead, which does not require taking critical sections. 20 | pub struct CriticalSectionDevice<'a, BUS, CS, D> { 21 | bus: &'a Mutex>, 22 | cs: CS, 23 | delay: D, 24 | } 25 | 26 | impl<'a, BUS, CS, D> CriticalSectionDevice<'a, BUS, CS, D> { 27 | /// Create a new [`CriticalSectionDevice`]. 28 | /// 29 | /// This sets the `cs` pin high, and returns an error if that fails. It is recommended 30 | /// to set the pin high the moment it's configured as an output, to avoid glitches. 31 | #[inline] 32 | pub fn new(bus: &'a Mutex>, mut cs: CS, delay: D) -> Result 33 | where 34 | CS: OutputPin, 35 | { 36 | cs.set_high()?; 37 | Ok(Self { bus, cs, delay }) 38 | } 39 | } 40 | 41 | impl<'a, BUS, CS> CriticalSectionDevice<'a, BUS, CS, super::NoDelay> { 42 | /// Create a new [`CriticalSectionDevice`] without support for in-transaction delays. 43 | /// 44 | /// This sets the `cs` pin high, and returns an error if that fails. It is recommended 45 | /// to set the pin high the moment it's configured as an output, to avoid glitches. 46 | /// 47 | /// **Warning**: The returned instance *technically* doesn't comply with the `SpiDevice` 48 | /// contract, which mandates delay support. It is relatively rare for drivers to use 49 | /// in-transaction delays, so you might still want to use this method because it's more practical. 50 | /// 51 | /// Note that a future version of the driver might start using delays, causing your 52 | /// code to panic. This wouldn't be considered a breaking change from the driver side, because 53 | /// drivers are allowed to assume `SpiDevice` implementations comply with the contract. 54 | /// If you feel this risk outweighs the convenience of having `cargo` automatically upgrade 55 | /// the driver crate, you might want to pin the driver's version. 56 | /// 57 | /// # Panics 58 | /// 59 | /// The returned device will panic if you try to execute a transaction 60 | /// that contains any operations of type [`Operation::DelayNs`]. 61 | #[inline] 62 | pub fn new_no_delay(bus: &'a Mutex>, mut cs: CS) -> Result 63 | where 64 | CS: OutputPin, 65 | { 66 | cs.set_high()?; 67 | Ok(Self { 68 | bus, 69 | cs, 70 | delay: super::NoDelay, 71 | }) 72 | } 73 | } 74 | 75 | impl ErrorType for CriticalSectionDevice<'_, BUS, CS, D> 76 | where 77 | BUS: ErrorType, 78 | CS: OutputPin, 79 | { 80 | type Error = DeviceError; 81 | } 82 | 83 | impl SpiDevice for CriticalSectionDevice<'_, BUS, CS, D> 84 | where 85 | BUS: SpiBus, 86 | CS: OutputPin, 87 | D: DelayNs, 88 | { 89 | #[inline] 90 | fn transaction(&mut self, operations: &mut [Operation<'_, Word>]) -> Result<(), Self::Error> { 91 | critical_section::with(|cs| { 92 | let bus = &mut *self.bus.borrow_ref_mut(cs); 93 | 94 | transaction(operations, bus, &mut self.delay, &mut self.cs) 95 | }) 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /embedded-hal-bus/src/spi/exclusive.rs: -------------------------------------------------------------------------------- 1 | //! SPI bus sharing mechanisms. 2 | 3 | use embedded_hal::delay::DelayNs; 4 | use embedded_hal::digital::OutputPin; 5 | use embedded_hal::spi::{ErrorType, Operation, SpiBus, SpiDevice}; 6 | #[cfg(feature = "async")] 7 | use embedded_hal_async::{ 8 | delay::DelayNs as AsyncDelayNs, 9 | spi::{SpiBus as AsyncSpiBus, SpiDevice as AsyncSpiDevice}, 10 | }; 11 | 12 | use super::shared::transaction; 13 | use super::DeviceError; 14 | 15 | /// [`SpiDevice`] implementation with exclusive access to the bus (not shared). 16 | /// 17 | /// This is the most straightforward way of obtaining an [`SpiDevice`] from an [`SpiBus`], 18 | /// ideal for when no sharing is required (only one SPI device is present on the bus). 19 | pub struct ExclusiveDevice { 20 | bus: BUS, 21 | cs: CS, 22 | delay: D, 23 | } 24 | 25 | impl ExclusiveDevice { 26 | /// Create a new [`ExclusiveDevice`]. 27 | /// 28 | /// This sets the `cs` pin high, and returns an error if that fails. It is recommended 29 | /// to set the pin high the moment it's configured as an output, to avoid glitches. 30 | #[inline] 31 | pub fn new(bus: BUS, mut cs: CS, delay: D) -> Result 32 | where 33 | CS: OutputPin, 34 | { 35 | cs.set_high()?; 36 | Ok(Self { bus, cs, delay }) 37 | } 38 | 39 | /// Returns a reference to the underlying bus object. 40 | #[inline] 41 | pub fn bus(&self) -> &BUS { 42 | &self.bus 43 | } 44 | 45 | /// Returns a mutable reference to the underlying bus object. 46 | #[inline] 47 | pub fn bus_mut(&mut self) -> &mut BUS { 48 | &mut self.bus 49 | } 50 | } 51 | 52 | impl ExclusiveDevice { 53 | /// Create a new [`ExclusiveDevice`] without support for in-transaction delays. 54 | /// 55 | /// This sets the `cs` pin high, and returns an error if that fails. It is recommended 56 | /// to set the pin high the moment it's configured as an output, to avoid glitches. 57 | /// 58 | /// **Warning**: The returned instance *technically* doesn't comply with the `SpiDevice` 59 | /// contract, which mandates delay support. It is relatively rare for drivers to use 60 | /// in-transaction delays, so you might still want to use this method because it's more practical. 61 | /// 62 | /// Note that a future version of the driver might start using delays, causing your 63 | /// code to panic. This wouldn't be considered a breaking change from the driver side, because 64 | /// drivers are allowed to assume `SpiDevice` implementations comply with the contract. 65 | /// If you feel this risk outweighs the convenience of having `cargo` automatically upgrade 66 | /// the driver crate, you might want to pin the driver's version. 67 | /// 68 | /// # Panics 69 | /// 70 | /// The returned device will panic if you try to execute a transaction 71 | /// that contains any operations of type [`Operation::DelayNs`]. 72 | #[inline] 73 | pub fn new_no_delay(bus: BUS, mut cs: CS) -> Result 74 | where 75 | CS: OutputPin, 76 | { 77 | cs.set_high()?; 78 | Ok(Self { 79 | bus, 80 | cs, 81 | delay: super::NoDelay, 82 | }) 83 | } 84 | } 85 | 86 | impl ErrorType for ExclusiveDevice 87 | where 88 | BUS: ErrorType, 89 | CS: OutputPin, 90 | { 91 | type Error = DeviceError; 92 | } 93 | 94 | impl SpiDevice for ExclusiveDevice 95 | where 96 | BUS: SpiBus, 97 | CS: OutputPin, 98 | D: DelayNs, 99 | { 100 | #[inline] 101 | fn transaction(&mut self, operations: &mut [Operation<'_, Word>]) -> Result<(), Self::Error> { 102 | transaction(operations, &mut self.bus, &mut self.delay, &mut self.cs) 103 | } 104 | } 105 | 106 | #[cfg(feature = "async")] 107 | #[cfg_attr(docsrs, doc(cfg(feature = "async")))] 108 | impl AsyncSpiDevice for ExclusiveDevice 109 | where 110 | BUS: AsyncSpiBus, 111 | CS: OutputPin, 112 | D: AsyncDelayNs, 113 | { 114 | #[inline] 115 | async fn transaction( 116 | &mut self, 117 | operations: &mut [Operation<'_, Word>], 118 | ) -> Result<(), Self::Error> { 119 | self.cs.set_low().map_err(DeviceError::Cs)?; 120 | 121 | let op_res = 'ops: { 122 | for op in operations { 123 | let res = match op { 124 | Operation::Read(buf) => self.bus.read(buf).await, 125 | Operation::Write(buf) => self.bus.write(buf).await, 126 | Operation::Transfer(read, write) => self.bus.transfer(read, write).await, 127 | Operation::TransferInPlace(buf) => self.bus.transfer_in_place(buf).await, 128 | Operation::DelayNs(ns) => match self.bus.flush().await { 129 | Err(e) => Err(e), 130 | Ok(()) => { 131 | self.delay.delay_ns(*ns).await; 132 | Ok(()) 133 | } 134 | }, 135 | }; 136 | if let Err(e) = res { 137 | break 'ops Err(e); 138 | } 139 | } 140 | Ok(()) 141 | }; 142 | 143 | // On failure, it's important to still flush and deassert CS. 144 | let flush_res = self.bus.flush().await; 145 | let cs_res = self.cs.set_high(); 146 | 147 | op_res.map_err(DeviceError::Spi)?; 148 | flush_res.map_err(DeviceError::Spi)?; 149 | cs_res.map_err(DeviceError::Cs)?; 150 | 151 | Ok(()) 152 | } 153 | } 154 | -------------------------------------------------------------------------------- /embedded-hal-bus/src/spi/mod.rs: -------------------------------------------------------------------------------- 1 | //! `SpiDevice` implementations. 2 | 3 | use core::fmt::{self, Debug, Display, Formatter}; 4 | use embedded_hal::spi::{Error, ErrorKind}; 5 | 6 | mod exclusive; 7 | pub use exclusive::*; 8 | mod refcell; 9 | pub use refcell::*; 10 | #[cfg(feature = "std")] 11 | mod mutex; 12 | #[cfg(feature = "std")] 13 | pub use mutex::*; 14 | #[cfg(any(feature = "portable-atomic", target_has_atomic = "8"))] 15 | mod atomic; 16 | mod critical_section; 17 | mod shared; 18 | #[cfg(any(feature = "portable-atomic", target_has_atomic = "8"))] 19 | pub use atomic::*; 20 | 21 | #[cfg(feature = "alloc")] 22 | mod rc; 23 | #[cfg(feature = "alloc")] 24 | pub use rc::*; 25 | 26 | pub use self::critical_section::*; 27 | 28 | #[cfg(feature = "defmt-03")] 29 | use crate::defmt; 30 | 31 | /// Error type for [`ExclusiveDevice`] operations. 32 | #[derive(Copy, Clone, Eq, PartialEq, Debug)] 33 | #[cfg_attr(feature = "defmt-03", derive(defmt::Format))] 34 | pub enum DeviceError { 35 | /// An inner SPI bus operation failed. 36 | Spi(BUS), 37 | /// Asserting or deasserting CS failed. 38 | Cs(CS), 39 | } 40 | 41 | impl Display for DeviceError { 42 | fn fmt(&self, f: &mut Formatter) -> fmt::Result { 43 | match self { 44 | Self::Spi(bus) => write!(f, "SPI bus error: {}", bus), 45 | Self::Cs(cs) => write!(f, "SPI CS error: {}", cs), 46 | } 47 | } 48 | } 49 | 50 | impl core::error::Error for DeviceError {} 51 | 52 | impl Error for DeviceError 53 | where 54 | BUS: Error + Debug, 55 | CS: Debug, 56 | { 57 | #[inline] 58 | fn kind(&self) -> ErrorKind { 59 | match self { 60 | Self::Spi(e) => e.kind(), 61 | Self::Cs(_) => ErrorKind::ChipSelectFault, 62 | } 63 | } 64 | } 65 | 66 | /// Dummy [`DelayNs`](embedded_hal::delay::DelayNs) implementation that panics on use. 67 | #[derive(Copy, Clone, Eq, PartialEq, Debug)] 68 | #[cfg_attr(feature = "defmt-03", derive(defmt::Format))] 69 | pub struct NoDelay; 70 | 71 | #[cold] 72 | fn no_delay_panic() { 73 | 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."); 74 | } 75 | 76 | impl embedded_hal::delay::DelayNs for NoDelay { 77 | #[inline] 78 | fn delay_ns(&mut self, _ns: u32) { 79 | no_delay_panic(); 80 | } 81 | } 82 | 83 | #[cfg(feature = "async")] 84 | #[cfg_attr(docsrs, doc(cfg(feature = "async")))] 85 | impl embedded_hal_async::delay::DelayNs for NoDelay { 86 | #[inline] 87 | async fn delay_ns(&mut self, _ns: u32) { 88 | no_delay_panic(); 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /embedded-hal-bus/src/spi/mutex.rs: -------------------------------------------------------------------------------- 1 | use embedded_hal::delay::DelayNs; 2 | use embedded_hal::digital::OutputPin; 3 | use embedded_hal::spi::{ErrorType, Operation, SpiBus, SpiDevice}; 4 | use std::sync::Mutex; 5 | 6 | use super::DeviceError; 7 | use crate::spi::shared::transaction; 8 | 9 | /// `std` `Mutex`-based shared bus [`SpiDevice`] implementation. 10 | /// 11 | /// This allows for sharing an [`SpiBus`], obtaining multiple [`SpiDevice`] instances, 12 | /// each with its own `CS` pin. 13 | /// 14 | /// Sharing is implemented with a `std` [`Mutex`]. It allows a single bus across multiple threads, 15 | /// with finer-grained locking than [`CriticalSectionDevice`](super::CriticalSectionDevice). The downside is 16 | /// it is only available in `std` targets. 17 | #[cfg_attr(docsrs, doc(cfg(feature = "std")))] 18 | pub struct MutexDevice<'a, BUS, CS, D> { 19 | bus: &'a Mutex, 20 | cs: CS, 21 | delay: D, 22 | } 23 | 24 | impl<'a, BUS, CS, D> MutexDevice<'a, BUS, CS, D> { 25 | /// Create a new [`MutexDevice`]. 26 | /// 27 | /// This sets the `cs` pin high, and returns an error if that fails. It is recommended 28 | /// to set the pin high the moment it's configured as an output, to avoid glitches. 29 | #[inline] 30 | pub fn new(bus: &'a Mutex, mut cs: CS, delay: D) -> Result 31 | where 32 | CS: OutputPin, 33 | { 34 | cs.set_high()?; 35 | Ok(Self { bus, cs, delay }) 36 | } 37 | } 38 | 39 | impl<'a, BUS, CS> MutexDevice<'a, BUS, CS, super::NoDelay> { 40 | /// Create a new [`MutexDevice`] without support for in-transaction delays. 41 | /// 42 | /// This sets the `cs` pin high, and returns an error if that fails. It is recommended 43 | /// to set the pin high the moment it's configured as an output, to avoid glitches. 44 | /// 45 | /// **Warning**: The returned instance *technically* doesn't comply with the `SpiDevice` 46 | /// contract, which mandates delay support. It is relatively rare for drivers to use 47 | /// in-transaction delays, so you might still want to use this method because it's more practical. 48 | /// 49 | /// Note that a future version of the driver might start using delays, causing your 50 | /// code to panic. This wouldn't be considered a breaking change from the driver side, because 51 | /// drivers are allowed to assume `SpiDevice` implementations comply with the contract. 52 | /// If you feel this risk outweighs the convenience of having `cargo` automatically upgrade 53 | /// the driver crate, you might want to pin the driver's version. 54 | /// 55 | /// # Panics 56 | /// 57 | /// The returned device will panic if you try to execute a transaction 58 | /// that contains any operations of type [`Operation::DelayNs`]. 59 | #[inline] 60 | pub fn new_no_delay(bus: &'a Mutex, mut cs: CS) -> Result 61 | where 62 | CS: OutputPin, 63 | { 64 | cs.set_high()?; 65 | Ok(Self { 66 | bus, 67 | cs, 68 | delay: super::NoDelay, 69 | }) 70 | } 71 | } 72 | 73 | impl ErrorType for MutexDevice<'_, BUS, CS, D> 74 | where 75 | BUS: ErrorType, 76 | CS: OutputPin, 77 | { 78 | type Error = DeviceError; 79 | } 80 | 81 | impl SpiDevice for MutexDevice<'_, BUS, CS, D> 82 | where 83 | BUS: SpiBus, 84 | CS: OutputPin, 85 | D: DelayNs, 86 | { 87 | #[inline] 88 | fn transaction(&mut self, operations: &mut [Operation<'_, Word>]) -> Result<(), Self::Error> { 89 | let bus = &mut *self.bus.lock().unwrap(); 90 | 91 | transaction(operations, bus, &mut self.delay, &mut self.cs) 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /embedded-hal-bus/src/spi/rc.rs: -------------------------------------------------------------------------------- 1 | extern crate alloc; 2 | use alloc::rc::Rc; 3 | 4 | use core::cell::RefCell; 5 | use embedded_hal::delay::DelayNs; 6 | use embedded_hal::digital::OutputPin; 7 | use embedded_hal::spi::{ErrorType, Operation, SpiBus, SpiDevice}; 8 | 9 | use super::DeviceError; 10 | use crate::spi::shared::transaction; 11 | 12 | /// Implementation of [`SpiDevice`] around a bus shared with `Rc>`. 13 | /// This is the reference-counting equivalent of [`RefCellDevice`](super::RefCellDevice), requiring allocation. 14 | /// 15 | /// A single [`SpiBus`] is shared via [`RefCell`], and its ownership is handled by [`Rc`]. 16 | /// Both of these mechanisms only allow sharing within a single thread (or interrupt priority level). 17 | /// For this reason, this does not implement [`Send`]. 18 | /// 19 | /// When this structure is dropped, the reference count of the `Bus` instance will be decremented, 20 | /// and it will be cleaned up once the reference count reaches zero. 21 | #[cfg_attr(docsrs, doc(cfg(any(feature = "std", feature = "alloc"))))] 22 | pub struct RcDevice { 23 | bus: Rc>, 24 | cs: Cs, 25 | delay: Delay, 26 | } 27 | 28 | impl RcDevice { 29 | /// Creates a new [`RcDevice`]. 30 | /// 31 | /// This sets the `cs` pin high, and returns an error if that fails. 32 | /// It is recommended to have already set that pin high the moment it has been configured as an output, to avoid glitches. 33 | /// 34 | /// This function does not increment the reference count: 35 | /// you will need to call `Rc::clone(&bus)` if you only have a `&Rc>`. 36 | #[inline] 37 | pub fn new(bus: Rc>, mut cs: Cs, delay: Delay) -> Result 38 | where 39 | Cs: OutputPin, 40 | { 41 | cs.set_high()?; 42 | 43 | Ok(Self { bus, cs, delay }) 44 | } 45 | } 46 | 47 | impl RcDevice { 48 | /// Creates a new [`RcDevice`] without support for in-transaction delays. 49 | /// 50 | /// **Warning**: It's advised to prefer [`RcDevice::new`], 51 | /// as the contract of [`SpiDevice`] requests support for in-transaction delays. 52 | /// 53 | /// Refer to [`RefCellDevice::new_no_delay`](super::RefCellDevice::new_no_delay) for more information. 54 | #[inline] 55 | pub fn new_no_delay(bus: Rc>, mut cs: Cs) -> Result 56 | where 57 | Cs: OutputPin, 58 | { 59 | cs.set_high()?; 60 | 61 | Ok(Self { 62 | bus, 63 | cs, 64 | delay: super::NoDelay, 65 | }) 66 | } 67 | } 68 | 69 | impl ErrorType for RcDevice 70 | where 71 | Bus: ErrorType, 72 | Cs: OutputPin, 73 | { 74 | type Error = DeviceError; 75 | } 76 | 77 | impl SpiDevice for RcDevice 78 | where 79 | Word: Copy + 'static, 80 | Bus: SpiBus, 81 | Cs: OutputPin, 82 | Delay: DelayNs, 83 | { 84 | #[inline] 85 | fn transaction(&mut self, operations: &mut [Operation<'_, Word>]) -> Result<(), Self::Error> { 86 | let bus = &mut *self.bus.borrow_mut(); 87 | 88 | transaction(operations, bus, &mut self.delay, &mut self.cs) 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /embedded-hal-bus/src/spi/refcell.rs: -------------------------------------------------------------------------------- 1 | use core::cell::RefCell; 2 | use embedded_hal::delay::DelayNs; 3 | use embedded_hal::digital::OutputPin; 4 | use embedded_hal::spi::{ErrorType, Operation, SpiBus, SpiDevice}; 5 | 6 | use super::DeviceError; 7 | use crate::spi::shared::transaction; 8 | 9 | /// `RefCell`-based shared bus [`SpiDevice`] implementation. 10 | /// 11 | /// This allows for sharing an [`SpiBus`], obtaining multiple [`SpiDevice`] instances, 12 | /// each with its own `CS` pin. 13 | /// 14 | /// Sharing is implemented with a `RefCell`. This means it has low overhead, but `RefCellDevice` instances are not `Send`, 15 | /// so it only allows sharing within a single thread (interrupt priority level). If you need to share a bus across several 16 | /// threads, use [`CriticalSectionDevice`](super::CriticalSectionDevice) instead. 17 | pub struct RefCellDevice<'a, BUS, CS, D> { 18 | bus: &'a RefCell, 19 | cs: CS, 20 | delay: D, 21 | } 22 | 23 | impl<'a, BUS, CS, D> RefCellDevice<'a, BUS, CS, D> { 24 | /// Create a new [`RefCellDevice`]. 25 | /// 26 | /// This sets the `cs` pin high, and returns an error if that fails. It is recommended 27 | /// to set the pin high the moment it's configured as an output, to avoid glitches. 28 | #[inline] 29 | pub fn new(bus: &'a RefCell, mut cs: CS, delay: D) -> Result 30 | where 31 | CS: OutputPin, 32 | { 33 | cs.set_high()?; 34 | Ok(Self { bus, cs, delay }) 35 | } 36 | } 37 | 38 | impl<'a, BUS, CS> RefCellDevice<'a, BUS, CS, super::NoDelay> { 39 | /// Create a new [`RefCellDevice`] without support for in-transaction delays. 40 | /// 41 | /// This sets the `cs` pin high, and returns an error if that fails. It is recommended 42 | /// to set the pin high the moment it's configured as an output, to avoid glitches. 43 | /// 44 | /// **Warning**: The returned instance *technically* doesn't comply with the `SpiDevice` 45 | /// contract, which mandates delay support. It is relatively rare for drivers to use 46 | /// in-transaction delays, so you might still want to use this method because it's more practical. 47 | /// 48 | /// Note that a future version of the driver might start using delays, causing your 49 | /// code to panic. This wouldn't be considered a breaking change from the driver side, because 50 | /// drivers are allowed to assume `SpiDevice` implementations comply with the contract. 51 | /// If you feel this risk outweighs the convenience of having `cargo` automatically upgrade 52 | /// the driver crate, you might want to pin the driver's version. 53 | /// 54 | /// # Panics 55 | /// 56 | /// The returned device will panic if you try to execute a transaction 57 | /// that contains any operations of type [`Operation::DelayNs`]. 58 | #[inline] 59 | pub fn new_no_delay(bus: &'a RefCell, mut cs: CS) -> Result 60 | where 61 | CS: OutputPin, 62 | { 63 | cs.set_high()?; 64 | Ok(Self { 65 | bus, 66 | cs, 67 | delay: super::NoDelay, 68 | }) 69 | } 70 | } 71 | 72 | impl ErrorType for RefCellDevice<'_, BUS, CS, D> 73 | where 74 | BUS: ErrorType, 75 | CS: OutputPin, 76 | { 77 | type Error = DeviceError; 78 | } 79 | 80 | impl SpiDevice for RefCellDevice<'_, BUS, CS, D> 81 | where 82 | BUS: SpiBus, 83 | CS: OutputPin, 84 | D: DelayNs, 85 | { 86 | #[inline] 87 | fn transaction(&mut self, operations: &mut [Operation<'_, Word>]) -> Result<(), Self::Error> { 88 | let bus = &mut *self.bus.borrow_mut(); 89 | 90 | transaction(operations, bus, &mut self.delay, &mut self.cs) 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /embedded-hal-bus/src/spi/shared.rs: -------------------------------------------------------------------------------- 1 | use embedded_hal::delay::DelayNs; 2 | use embedded_hal::digital::OutputPin; 3 | use embedded_hal::spi::{ErrorType, Operation, SpiBus}; 4 | 5 | use crate::spi::DeviceError; 6 | 7 | /// Common implementation to perform a transaction against the device. 8 | #[inline] 9 | pub fn transaction( 10 | operations: &mut [Operation], 11 | bus: &mut BUS, 12 | delay: &mut D, 13 | cs: &mut CS, 14 | ) -> Result<(), DeviceError> 15 | where 16 | BUS: SpiBus + ErrorType, 17 | CS: OutputPin, 18 | D: DelayNs, 19 | Word: Copy, 20 | { 21 | cs.set_low().map_err(DeviceError::Cs)?; 22 | 23 | let op_res = operations.iter_mut().try_for_each(|op| match op { 24 | Operation::Read(buf) => bus.read(buf), 25 | Operation::Write(buf) => bus.write(buf), 26 | Operation::Transfer(read, write) => bus.transfer(read, write), 27 | Operation::TransferInPlace(buf) => bus.transfer_in_place(buf), 28 | Operation::DelayNs(ns) => { 29 | bus.flush()?; 30 | delay.delay_ns(*ns); 31 | Ok(()) 32 | } 33 | }); 34 | 35 | // On failure, it's important to still flush and deassert CS. 36 | let flush_res = bus.flush(); 37 | let cs_res = cs.set_high(); 38 | 39 | op_res.map_err(DeviceError::Spi)?; 40 | flush_res.map_err(DeviceError::Spi)?; 41 | cs_res.map_err(DeviceError::Cs)?; 42 | 43 | Ok(()) 44 | } 45 | -------------------------------------------------------------------------------- /embedded-hal-bus/src/util.rs: -------------------------------------------------------------------------------- 1 | //! Utilities shared by all bus types. 2 | 3 | #[allow(unused_imports)] 4 | use core::cell::UnsafeCell; 5 | 6 | #[cfg(not(feature = "portable-atomic"))] 7 | use core::sync::atomic::AtomicBool; 8 | #[cfg(feature = "portable-atomic")] 9 | use portable_atomic::AtomicBool; 10 | 11 | #[cfg(any(feature = "portable-atomic", target_has_atomic = "8"))] 12 | /// Cell type used by [`spi::AtomicDevice`](crate::spi::AtomicDevice) and [`i2c::AtomicDevice`](crate::i2c::AtomicDevice). 13 | /// 14 | /// To use `AtomicDevice`, you must wrap the bus with this struct, and then 15 | /// construct multiple `AtomicDevice` instances with references to it. 16 | pub struct AtomicCell { 17 | pub(crate) bus: UnsafeCell, 18 | pub(crate) busy: AtomicBool, 19 | } 20 | #[cfg(any(feature = "portable-atomic", target_has_atomic = "8"))] 21 | unsafe impl Send for AtomicCell {} 22 | #[cfg(any(feature = "portable-atomic", target_has_atomic = "8"))] 23 | unsafe impl Sync for AtomicCell {} 24 | 25 | #[cfg(any(feature = "portable-atomic", target_has_atomic = "8"))] 26 | impl AtomicCell { 27 | /// Create a new `AtomicCell` 28 | pub fn new(bus: BUS) -> Self { 29 | Self { 30 | bus: UnsafeCell::new(bus), 31 | busy: AtomicBool::from(false), 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /embedded-hal-nb/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | The format is based on [Keep a Changelog](http://keepachangelog.com/) 6 | and this project adheres to [Semantic Versioning](http://semver.org/). 7 | 8 | ## [Unreleased] 9 | 10 | - Added `core::error::Error` implementations for every custom `impl Error` 11 | - Increased MSRV to 1.81 due to `core::error::Error` 12 | 13 | ## [v1.0.0] - 2023-12-28 14 | 15 | 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. 16 | 17 | - Updated `embedded-hal` to version `1.0.0`. 18 | 19 | ## [v1.0.0-rc.3] - 2023-12-14 20 | 21 | - Updated `embedded-hal` to version `1.0.0-rc.3`. 22 | 23 | ## [v1.0.0-rc.2] - 2023-11-28 24 | 25 | - Updated `embedded-hal` to version `1.0.0-rc.2`. 26 | - Minor document fixes. 27 | - Add #[inline] hints to most of `embedded-hal-nb` functions. 28 | 29 | ## [v1.0.0-rc.1] - 2023-08-15 30 | 31 | - Updated `embedded-hal` to version `1.0.0-rc.1`. 32 | - Added `+ ?Sized` to all blanket impls. 33 | 34 | ## [v1.0.0-alpha.3] - 2023-07-04 35 | 36 | ### Changed 37 | - Updated `embedded-hal` to version `1.0.0-alpha.11`. 38 | 39 | 40 | ## [v1.0.0-alpha.2] - 2023-04-04 41 | 42 | ### Changed 43 | - Updated `embedded-hal` to version `1.0.0-alpha.10`. 44 | 45 | ## [v1.0.0-alpha.1] - 2022-09-28 46 | 47 | ### Changed 48 | - Updated `embedded-hal` to version `1.0.0-alpha.9`. 49 | 50 | ## [v1.0.0-alpha.0] - 2022-09-27 51 | 52 | First release to crates.io 53 | 54 | [Unreleased]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-nb-v1.0.0...HEAD 55 | [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 56 | [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 57 | [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 58 | [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 59 | [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 60 | [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 61 | [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 62 | [v1.0.0-alpha.0]: https://github.com/rust-embedded/embedded-hal/tree/embedded-hal-nb-v1.0.0-alpha.0 63 | -------------------------------------------------------------------------------- /embedded-hal-nb/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "embedded-hal-nb" 3 | version = "1.0.0" 4 | edition = "2021" 5 | rust-version = "1.81" 6 | 7 | categories = ["embedded", "hardware-support", "no-std"] 8 | description = "Non-blocking Hardware Abstraction Layer (HAL) for embedded systems using the `nb` crate." 9 | documentation = "https://docs.rs/embedded-hal-nb" 10 | keywords = ["hal", "IO"] 11 | license = "MIT OR Apache-2.0" 12 | readme = "README.md" 13 | repository = "https://github.com/rust-embedded/embedded-hal" 14 | 15 | [dependencies] 16 | embedded-hal = { version = "1.0.0", path = "../embedded-hal" } 17 | nb = "1" 18 | 19 | [dev-dependencies] 20 | cortex-m-rt = "0.7" 21 | stm32f1 = { version = "0.15", features = ["stm32f103", "rt"] } 22 | -------------------------------------------------------------------------------- /embedded-hal-nb/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) 2021-2025 The Rust embedded HAL team and contributors. 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 7 | of the Software, and to permit persons to whom the Software is furnished to do 8 | so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /embedded-hal-nb/README.md: -------------------------------------------------------------------------------- 1 | [![crates.io](https://img.shields.io/crates/d/embedded-hal-nb.svg)](https://crates.io/crates/embedded-hal-nb) 2 | [![crates.io](https://img.shields.io/crates/v/embedded-hal-nb.svg)](https://crates.io/crates/embedded-hal-nb) 3 | [![Documentation](https://docs.rs/embedded-hal-nb/badge.svg)](https://docs.rs/embedded-hal-nb) 4 | ![Minimum Supported Rust Version](https://img.shields.io/badge/rustc-1.60+-blue.svg) 5 | 6 | # `embedded-hal-nb` 7 | 8 | A non-blocking Hardware Abstraction Layer (HAL) for embedded systems, using the `nb` crate. 9 | 10 | This crate contains versions of some [`embedded-hal`](https://crates.io/crates/embedded-hal) traits using `nb`, and shares its scope and [design goals](https://docs.rs/embedded-hal/latest/embedded_hal/#design-goals). 11 | 12 | This project is developed and maintained by the [HAL team](https://github.com/rust-embedded/wg#the-hal-team). 13 | 14 | ## [API reference] 15 | 16 | [API reference]: https://docs.rs/embedded-hal-nb 17 | 18 | ## Minimum Supported Rust Version (MSRV) 19 | 20 | This crate is guaranteed to compile on stable Rust 1.60 and up. It *might* 21 | compile with older versions but that may change in any new patch release. 22 | 23 | See [here](../docs/msrv.md) for details on how the MSRV may be upgraded. 24 | 25 | ## License 26 | 27 | Licensed under either of 28 | 29 | - Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or 30 | http://www.apache.org/licenses/LICENSE-2.0) 31 | - MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) 32 | 33 | at your option. 34 | 35 | ### Contribution 36 | 37 | Unless you explicitly state otherwise, any contribution intentionally submitted 38 | for inclusion in the work by you, as defined in the Apache-2.0 license, shall be 39 | dual licensed as above, without any additional terms or conditions. 40 | -------------------------------------------------------------------------------- /embedded-hal-nb/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! Non-blocking Hardware Abstraction Layer (HAL) traits for embedded systems, using the `nb` crate. 2 | //! 3 | //! The `embedded-hal-nb` traits make use of the 4 | //! [`nb`][] crate (*please go read that crate documentation before continuing*) to abstract over 5 | //! the asynchronous model and to also provide a blocking operation mode. 6 | //! 7 | //! [`nb`]: https://crates.io/crates/nb 8 | //! 9 | //! Here's how a HAL trait may look like: 10 | //! 11 | //! ``` 12 | //! use embedded_hal_nb; 13 | //! 14 | //! /// A serial interface 15 | //! pub trait Serial { 16 | //! /// Error type associated to this serial interface 17 | //! type Error: core::fmt::Debug; 18 | //! 19 | //! /// Reads a single byte 20 | //! fn read(&mut self) -> nb::Result; 21 | //! 22 | //! /// Writes a single byte 23 | //! fn write(&mut self, byte: u8) -> nb::Result<(), Self::Error>; 24 | //! } 25 | //! ``` 26 | //! 27 | //! The `nb::Result` enum is used to add a [`WouldBlock`] variant to the errors 28 | //! of the serial interface. As explained in the documentation of the `nb` crate this single API, 29 | //! when paired with the macros in the `nb` crate, can operate in a blocking manner, or be adapted 30 | //! to other asynchronous execution schemes. 31 | //! 32 | //! [`WouldBlock`]: https://docs.rs/nb/1.0.0/nb/enum.Error.html 33 | //! 34 | //! Some traits, like the one shown below, may expose possibly blocking APIs that can't fail. In 35 | //! those cases `nb::Result<_, Infallible>` is used. 36 | //! 37 | //! ``` 38 | //! # use std as core; 39 | //! use ::core::convert::Infallible; 40 | //! 41 | //! /// A count down timer 42 | //! pub trait CountDown { 43 | //! // .. 44 | //! 45 | //! /// "waits" until the count down is over 46 | //! fn wait(&mut self) -> nb::Result<(), Infallible>; 47 | //! } 48 | //! 49 | //! # fn main() {} 50 | //! ``` 51 | //! 52 | //! ## Suggested implementation 53 | //! 54 | //! The HAL traits should be implemented for device crates generated via [`svd2rust`] to maximize 55 | //! code reuse. 56 | //! 57 | //! [`svd2rust`]: https://crates.io/crates/svd2rust 58 | //! 59 | //! Shown below is an implementation of some of the HAL traits for the [`stm32f1xx-hal`] crate. This 60 | //! single implementation will work for *any* microcontroller in the `STM32F1xx` family. 61 | //! 62 | //! [`stm32f1`]: https://crates.io/crates/stm32f1 63 | //! 64 | //! ```no_run 65 | //! // crate: stm32f1xx-hal 66 | //! // An implementation of the `embedded-hal` traits for STM32F1xx microcontrollers 67 | //! 68 | //! use embedded_hal_nb::serial; 69 | //! use nb; 70 | //! 71 | //! // device crate 72 | //! use stm32f1::stm32f103::USART1; 73 | //! 74 | //! /// A serial interface 75 | //! // NOTE generic over the USART peripheral 76 | //! pub struct Serial { usart: USART } 77 | //! 78 | //! // convenience type alias 79 | //! pub type Serial1 = Serial; 80 | //! 81 | //! impl serial::ErrorType for Serial { 82 | //! type Error = serial::ErrorKind; 83 | //! } 84 | //! 85 | //! impl embedded_hal_nb::serial::Read for Serial { 86 | //! fn read(&mut self) -> nb::Result { 87 | //! // read the status register 88 | //! let isr = self.usart.sr.read(); 89 | //! 90 | //! if isr.ore().bit_is_set() { 91 | //! // Error: Buffer overrun 92 | //! Err(nb::Error::Other(Self::Error::Overrun)) 93 | //! } 94 | //! // omitted: checks for other errors 95 | //! else if isr.rxne().bit_is_set() { 96 | //! // Data available: read the data register 97 | //! Ok(self.usart.dr.read().bits() as u8) 98 | //! } else { 99 | //! // No data available yet 100 | //! Err(nb::Error::WouldBlock) 101 | //! } 102 | //! } 103 | //! } 104 | //! 105 | //! impl embedded_hal_nb::serial::Write for Serial { 106 | //! fn write(&mut self, byte: u8) -> nb::Result<(), Self::Error> { 107 | //! // Similar to the `read` implementation 108 | //! # Ok(()) 109 | //! } 110 | //! 111 | //! fn flush(&mut self) -> nb::Result<(), Self::Error> { 112 | //! // Similar to the `read` implementation 113 | //! # Ok(()) 114 | //! } 115 | //! } 116 | //! 117 | //! # fn main() {} 118 | //! ``` 119 | //! 120 | //! ## Intended usage 121 | //! 122 | //! Thanks to the [`nb`] crate the HAL API can be used in a blocking manner 123 | //! with the [`block!`] macro or with `futures`. 124 | //! 125 | //! [`block!`]: https://docs.rs/nb/1.0.0/nb/macro.block.html 126 | //! 127 | //! ### Blocking mode 128 | //! 129 | //! An example of writing a string over the serial interface in a blocking 130 | //! fashion: 131 | //! 132 | //! ``` 133 | //! use stm32f1xx_hal::Serial1; 134 | //! use embedded_hal_nb::serial::Write; 135 | //! use nb::block; 136 | //! 137 | //! # fn main() { 138 | //! let mut serial: Serial1 = { 139 | //! // .. 140 | //! # Serial1 141 | //! }; 142 | //! 143 | //! for byte in b"Hello, world!" { 144 | //! // NOTE `block!` blocks until `serial.write()` completes and returns 145 | //! // `Result<(), Error>` 146 | //! block!(serial.write(*byte)).unwrap(); 147 | //! } 148 | //! # } 149 | //! 150 | //! # mod stm32f1xx_hal { 151 | //! # use embedded_hal_nb; 152 | //! # use core::convert::Infallible; 153 | //! # pub struct Serial1; 154 | //! # impl Serial1 { 155 | //! # pub fn write(&mut self, _: u8) -> nb::Result<(), Infallible> { 156 | //! # Ok(()) 157 | //! # } 158 | //! # } 159 | //! # } 160 | //! ``` 161 | //! 162 | //! ## Generic programming and higher level abstractions 163 | //! 164 | //! The core of the HAL has been kept minimal on purpose to encourage building **generic** higher 165 | //! level abstractions on top of it. Some higher level abstractions that pick an asynchronous model 166 | //! or that have blocking behavior and that are deemed useful to build other abstractions can be 167 | //! found in the `blocking` module. 168 | //! 169 | //! Some examples: 170 | //! 171 | //! **NOTE** All the functions shown below could have been written as trait 172 | //! methods with default implementation to allow specialization, but they have 173 | //! been written as functions to keep things simple. 174 | //! 175 | //! - Write a whole buffer to a serial device in blocking a fashion. 176 | //! 177 | //! ``` 178 | //! use embedded_hal_nb::serial::Write; 179 | //! use nb::block; 180 | //! 181 | //! fn write_all(serial: &mut S, buffer: &[u8]) -> Result<(), S::Error> 182 | //! where 183 | //! S: Write 184 | //! { 185 | //! for &byte in buffer { 186 | //! block!(serial.write(byte))?; 187 | //! } 188 | //! 189 | //! Ok(()) 190 | //! } 191 | //! 192 | //! # fn main() {} 193 | //! ``` 194 | //! 195 | //! - Buffered serial interface with periodic flushing in interrupt handler 196 | //! 197 | //! ``` 198 | //! # use std as core; 199 | //! use embedded_hal_nb::serial::{ErrorKind, Write}; 200 | //! use nb::block; 201 | //! 202 | //! fn flush(serial: &mut S, cb: &mut CircularBuffer) 203 | //! where 204 | //! S: Write, 205 | //! { 206 | //! loop { 207 | //! if let Some(byte) = cb.peek() { 208 | //! match serial.write(*byte) { 209 | //! Err(nb::Error::Other(_)) => unreachable!(), 210 | //! Err(nb::Error::WouldBlock) => return, 211 | //! Ok(()) => {}, // keep flushing data 212 | //! } 213 | //! } 214 | //! 215 | //! cb.pop(); 216 | //! } 217 | //! } 218 | //! 219 | //! // The stuff below could be in some other crate 220 | //! 221 | //! /// Global singleton 222 | //! pub struct BufferedSerial1; 223 | //! 224 | //! // NOTE private 225 | //! static BUFFER1: Mutex = { 226 | //! // .. 227 | //! # Mutex(CircularBuffer) 228 | //! }; 229 | //! static SERIAL1: Mutex = { 230 | //! // .. 231 | //! # Mutex(Serial1) 232 | //! }; 233 | //! 234 | //! impl BufferedSerial1 { 235 | //! pub fn write(&self, byte: u8) { 236 | //! self.write_all(&[byte]) 237 | //! } 238 | //! 239 | //! pub fn write_all(&self, bytes: &[u8]) { 240 | //! let mut buffer = BUFFER1.lock(); 241 | //! for byte in bytes { 242 | //! buffer.push(*byte).expect("buffer overrun"); 243 | //! } 244 | //! // omitted: pend / enable interrupt_handler 245 | //! } 246 | //! } 247 | //! 248 | //! fn interrupt_handler() { 249 | //! let mut serial = SERIAL1.lock(); 250 | //! let mut buffer = BUFFER1.lock(); 251 | //! 252 | //! flush(&mut *serial, &mut buffer); 253 | //! } 254 | //! 255 | //! # struct Mutex(T); 256 | //! # impl Mutex { 257 | //! # fn lock(&self) -> RefMut { unimplemented!() } 258 | //! # } 259 | //! # struct RefMut<'a, T>(&'a mut T) where T: 'a; 260 | //! # impl<'a, T> ::core::ops::Deref for RefMut<'a, T> { 261 | //! # type Target = T; 262 | //! # fn deref(&self) -> &T { self.0 } 263 | //! # } 264 | //! # impl<'a, T> ::core::ops::DerefMut for RefMut<'a, T> { 265 | //! # fn deref_mut(&mut self) -> &mut T { self.0 } 266 | //! # } 267 | //! # struct Serial1; 268 | //! # impl embedded_hal_nb::serial::ErrorType for Serial1 { 269 | //! # type Error = ErrorKind; 270 | //! # } 271 | //! # impl embedded_hal_nb::serial::Write for Serial1 { 272 | //! # fn write(&mut self, _: u8) -> nb::Result<(), Self::Error> { Err(nb::Error::WouldBlock) } 273 | //! # fn flush(&mut self) -> nb::Result<(), Self::Error> { Err(nb::Error::WouldBlock) } 274 | //! # } 275 | //! # struct CircularBuffer; 276 | //! # impl CircularBuffer { 277 | //! # pub fn peek(&mut self) -> Option<&u8> { None } 278 | //! # pub fn pop(&mut self) -> Option { None } 279 | //! # pub fn push(&mut self, _: u8) -> Result<(), ()> { Ok(()) } 280 | //! # } 281 | //! 282 | //! # fn main() {} 283 | //! ``` 284 | 285 | #![warn(missing_docs)] 286 | #![no_std] 287 | 288 | pub use nb; 289 | 290 | pub mod serial; 291 | pub mod spi; 292 | -------------------------------------------------------------------------------- /embedded-hal-nb/src/serial.rs: -------------------------------------------------------------------------------- 1 | //! Serial interface. 2 | 3 | /// Serial error. 4 | pub trait Error: core::fmt::Debug { 5 | /// Convert error to a generic serial error kind 6 | /// 7 | /// By using this method, serial errors freely defined by HAL implementations 8 | /// can be converted to a set of generic serial errors upon which generic 9 | /// code can act. 10 | fn kind(&self) -> ErrorKind; 11 | } 12 | 13 | impl Error for core::convert::Infallible { 14 | #[inline] 15 | fn kind(&self) -> ErrorKind { 16 | match *self {} 17 | } 18 | } 19 | 20 | /// Serial error kind. 21 | /// 22 | /// This represents a common set of serial operation errors. HAL implementations are 23 | /// free to define more specific or additional error types. However, by providing 24 | /// a mapping to these common serial errors, generic code can still react to them. 25 | #[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] 26 | #[non_exhaustive] 27 | pub enum ErrorKind { 28 | /// The peripheral receive buffer was overrun. 29 | Overrun, 30 | /// Received data does not conform to the peripheral configuration. 31 | /// Can be caused by a misconfigured device on either end of the serial line. 32 | FrameFormat, 33 | /// Parity check failed. 34 | Parity, 35 | /// Serial line is too noisy to read valid data. 36 | Noise, 37 | /// A different error occurred. The original error may contain more information. 38 | Other, 39 | } 40 | 41 | impl Error for ErrorKind { 42 | #[inline] 43 | fn kind(&self) -> ErrorKind { 44 | *self 45 | } 46 | } 47 | 48 | impl core::error::Error for ErrorKind {} 49 | 50 | impl core::fmt::Display for ErrorKind { 51 | #[inline] 52 | fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { 53 | match self { 54 | Self::Overrun => write!(f, "The peripheral receive buffer was overrun"), 55 | Self::Parity => write!(f, "Parity check failed"), 56 | Self::Noise => write!(f, "Serial line is too noisy to read valid data"), 57 | Self::FrameFormat => write!( 58 | f, 59 | "Received data does not conform to the peripheral configuration" 60 | ), 61 | Self::Other => write!( 62 | f, 63 | "A different error occurred. The original error may contain more information" 64 | ), 65 | } 66 | } 67 | } 68 | 69 | /// Serial error type trait. 70 | /// 71 | /// This just defines the error type, to be used by the other traits. 72 | pub trait ErrorType { 73 | /// Error type 74 | type Error: Error; 75 | } 76 | 77 | impl ErrorType for &mut T { 78 | type Error = T::Error; 79 | } 80 | 81 | /// Read half of a serial interface. 82 | /// 83 | /// Some serial interfaces support different data sizes (8 bits, 9 bits, etc.); 84 | /// This can be encoded in this trait via the `Word` type parameter. 85 | pub trait Read: ErrorType { 86 | /// Reads a single word from the serial interface 87 | fn read(&mut self) -> nb::Result; 88 | } 89 | 90 | impl + ?Sized, Word: Copy> Read for &mut T { 91 | #[inline] 92 | fn read(&mut self) -> nb::Result { 93 | T::read(self) 94 | } 95 | } 96 | 97 | /// Write half of a serial interface. 98 | pub trait Write: ErrorType { 99 | /// Writes a single word to the serial interface. 100 | fn write(&mut self, word: Word) -> nb::Result<(), Self::Error>; 101 | 102 | /// Ensures that none of the previously written words are still buffered. 103 | fn flush(&mut self) -> nb::Result<(), Self::Error>; 104 | } 105 | 106 | impl + ?Sized, Word: Copy> Write for &mut T { 107 | #[inline] 108 | fn write(&mut self, word: Word) -> nb::Result<(), Self::Error> { 109 | T::write(self, word) 110 | } 111 | 112 | #[inline] 113 | fn flush(&mut self) -> nb::Result<(), Self::Error> { 114 | T::flush(self) 115 | } 116 | } 117 | 118 | /// Implementation of `core::fmt::Write` for the HAL's `serial::Write`. 119 | /// 120 | /// TODO write example of usage 121 | impl core::fmt::Write for dyn Write + '_ 122 | where 123 | Word: Copy + From, 124 | { 125 | #[inline] 126 | fn write_str(&mut self, s: &str) -> core::fmt::Result { 127 | for c in s.bytes() { 128 | nb::block!(self.write(Word::from(c))).map_err(|_| core::fmt::Error)?; 129 | } 130 | Ok(()) 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /embedded-hal-nb/src/spi.rs: -------------------------------------------------------------------------------- 1 | //! SPI master mode traits using `nb`. 2 | 3 | pub use embedded_hal::spi::{ 4 | Error, ErrorKind, ErrorType, Mode, Phase, Polarity, MODE_0, MODE_1, MODE_2, MODE_3, 5 | }; 6 | 7 | /// Full duplex SPI (master mode). 8 | /// 9 | /// # Notes 10 | /// 11 | /// - It's the task of the user of this interface to manage the slave select lines. 12 | /// 13 | /// - Due to how full duplex SPI works each `read` call must be preceded by a `write` call. 14 | /// 15 | /// - `read` calls only return the data received with the last `write` call. 16 | /// Previously received data is discarded 17 | /// 18 | /// - Data is only guaranteed to be clocked out when the `read` call succeeds. 19 | /// The slave select line shouldn't be released before that. 20 | /// 21 | /// - Some SPIs can work with 8-bit *and* 16-bit words. You can overload this trait with different 22 | /// `Word` types to allow operation in both modes. 23 | pub trait FullDuplex: ErrorType { 24 | /// Reads the word stored in the shift register 25 | /// 26 | /// **NOTE** A word must be sent to the slave before attempting to call this 27 | /// method. 28 | fn read(&mut self) -> nb::Result; 29 | 30 | /// Writes a word to the slave 31 | fn write(&mut self, word: Word) -> nb::Result<(), Self::Error>; 32 | } 33 | 34 | impl + ?Sized, Word: Copy> FullDuplex for &mut T { 35 | #[inline] 36 | fn read(&mut self) -> nb::Result { 37 | T::read(self) 38 | } 39 | 40 | #[inline] 41 | fn write(&mut self, word: Word) -> nb::Result<(), Self::Error> { 42 | T::write(self, word) 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /embedded-hal/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | authors = [ 3 | "The Embedded HAL Team ", 4 | "Jorge Aparicio ", 5 | "Jonathan 'theJPster' Pallant ", 6 | ] 7 | categories = ["asynchronous", "embedded", "hardware-support", "no-std"] 8 | description = " A Hardware Abstraction Layer (HAL) for embedded systems " 9 | documentation = "https://docs.rs/embedded-hal" 10 | edition = "2021" 11 | rust-version = "1.81" 12 | keywords = ["hal", "IO"] 13 | license = "MIT OR Apache-2.0" 14 | name = "embedded-hal" 15 | readme = "README.md" 16 | repository = "https://github.com/rust-embedded/embedded-hal" 17 | version = "1.0.0" 18 | 19 | [features] 20 | defmt-03 = ["dep:defmt-03"] 21 | 22 | [dependencies] 23 | defmt-03 = { package = "defmt", version = "0.3", optional = true } 24 | -------------------------------------------------------------------------------- /embedded-hal/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) 2017-2018 Jorge Aparicio 2 | 3 | Permission is hereby granted, free of charge, to any 4 | person obtaining a copy of this software and associated 5 | documentation files (the "Software"), to deal in the 6 | Software without restriction, including without 7 | limitation the rights to use, copy, modify, merge, 8 | publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software 10 | is furnished to do so, subject to the following 11 | conditions: 12 | 13 | The above copyright notice and this permission notice 14 | shall be included in all copies or substantial portions 15 | of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 18 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 19 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 20 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 21 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 22 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 24 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 25 | DEALINGS IN THE SOFTWARE. 26 | -------------------------------------------------------------------------------- /embedded-hal/README.md: -------------------------------------------------------------------------------- 1 | [![crates.io](https://img.shields.io/crates/d/embedded-hal.svg)](https://crates.io/crates/embedded-hal) 2 | [![crates.io](https://img.shields.io/crates/v/embedded-hal.svg)](https://crates.io/crates/embedded-hal) 3 | [![Documentation](https://docs.rs/embedded-hal/badge.svg)](https://docs.rs/embedded-hal) 4 | ![Minimum Supported Rust Version](https://img.shields.io/badge/rustc-1.60+-blue.svg) 5 | 6 | # `embedded-hal` 7 | 8 | > A Hardware Abstraction Layer (HAL) for embedded systems 9 | 10 | This project is developed and maintained by the [HAL team](https://github.com/rust-embedded/wg#the-hal-team). 11 | 12 | ## Companion crates 13 | 14 | The main `embedded-hal` crate contains only blocking traits, where the operation is done 15 | synchronously before returning. Check out the following crates, which contain versions 16 | of the traits for other execution models: 17 | 18 | - [`embedded-hal-async`](https://docs.rs/embedded-hal-async): async/await-based. 19 | - [`embedded-hal-nb`](https://docs.rs/embedded-hal-nb): polling-based, using the `nb` crate. 20 | 21 | The [`embedded-hal-bus`](https://docs.rs/embedded-hal-bus) crate provides utilities for sharing 22 | SPI and I2C buses. 23 | 24 | Additionally, more domain-specific traits are available in separate crates: 25 | - [`embedded-can`](https://docs.rs/embedded-can): Controller Area Network (CAN) 26 | - [`embedded-io`](https://docs.rs/embedded-io): I/O byte streams (like `std::io`, but `no-std`-compatible). 27 | 28 | ## Serial/UART traits 29 | 30 | There is no serial traits in `embedded-hal`. Instead, use [`embedded-io`](https://crates.io/crates/embedded-io). 31 | A serial port is essentially a byte-oriented stream, and that's what `embedded-io` models. Sharing the traits 32 | with all byte streams has some advantages. For example, it allows generic code providing a command-line interface 33 | or a console to operate either on hardware serial ports or on virtual ones like Telnet or USB CDC-ACM. 34 | 35 | ## Design goals 36 | 37 | The HAL 38 | 39 | - Must *erase* device specific details. Neither register, register blocks, nor magic values should 40 | appear in the API. 41 | 42 | - Must be generic *within* a device and *across* devices. The API to use a serial interface must 43 | be the same regardless of whether the implementation uses the USART1 or UART4 peripheral of a 44 | device or the UART0 peripheral of another device. 45 | 46 | - Where possible must *not* be tied to a specific asynchronous model. The API should be usable 47 | in blocking mode, with the `futures` model, with an async/await model or with a callback model. 48 | (cf. the [`nb`](https://docs.rs/nb) crate) 49 | 50 | - Must be minimal, and thus easy to implement and zero cost, yet highly composable. People that 51 | want higher level abstraction should *prefer to use this HAL* rather than *re-implement* 52 | register manipulation code. 53 | 54 | - Serve as a foundation for building an ecosystem of platform-agnostic drivers. Here driver 55 | means a library crate that lets a target platform interface an external device like a digital 56 | sensor or a wireless transceiver. The advantage of this system is that by writing the driver as 57 | a generic library on top of `embedded-hal` driver authors can support any number of target 58 | platforms (e.g. Cortex-M microcontrollers, AVR microcontrollers, embedded Linux, etc.). The 59 | advantage for application developers is that by adopting `embedded-hal` they can unlock all 60 | these drivers for their platform. 61 | 62 | - Trait methods must be fallible so that they can be used in any possible situation. 63 | Nevertheless, HAL implementations can additionally provide infallible versions of the same methods 64 | if they can never fail in their platform. This way, generic code can use the fallible abstractions 65 | provided here but platform-specific code can avoid fallibility-related boilerplate if possible. 66 | 67 | ## Out of scope 68 | 69 | - Initialization and configuration stuff like "ensure this serial interface and that SPI 70 | interface are not using the same pins". The HAL will focus on *doing I/O*. 71 | 72 | ## Platform agnostic drivers 73 | 74 | You can find platform-agnostic drivers built on top of `embedded-hal` on crates.io by [searching 75 | for the *embedded-hal* keyword](https://crates.io/keywords/embedded-hal). 76 | 77 | If you are writing a platform-agnostic driver yourself you are highly encouraged to [add the 78 | embedded-hal keyword](https://doc.rust-lang.org/cargo/reference/manifest.html#package-metadata) 79 | to your crate before publishing it! 80 | 81 | ## Optional Cargo features 82 | 83 | - **`defmt-03`**: Derive `defmt::Format` from `defmt` 0.3 for enums and structs. 84 | 85 | ## Minimum Supported Rust Version (MSRV) 86 | 87 | This crate is guaranteed to compile on stable Rust 1.60 and up. It *might* 88 | compile with older versions but that may change in any new patch release. 89 | 90 | See [here](../docs/msrv.md) for details on how the MSRV may be upgraded. 91 | 92 | ## License 93 | 94 | Licensed under either of 95 | 96 | - Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or 97 | ) 98 | - MIT license ([LICENSE-MIT](LICENSE-MIT) or ) 99 | 100 | at your option. 101 | 102 | ### Contribution 103 | 104 | Unless you explicitly state otherwise, any contribution intentionally submitted 105 | for inclusion in the work by you, as defined in the Apache-2.0 license, shall be 106 | dual licensed as above, without any additional terms or conditions. 107 | -------------------------------------------------------------------------------- /embedded-hal/src/delay.rs: -------------------------------------------------------------------------------- 1 | //! Delays. 2 | 3 | /// Nanoseconds per microsecond 4 | const NANOS_PER_MICRO: u32 = 1_000; 5 | /// Nanoseconds per millisecond 6 | const NANOS_PER_MILLI: u32 = 1_000_000; 7 | 8 | /// Delay with up to nanosecond precision. 9 | pub trait DelayNs { 10 | /// Pauses execution for at minimum `ns` nanoseconds. Pause can be longer 11 | /// if the implementation requires it due to precision/timing issues. 12 | fn delay_ns(&mut self, ns: u32); 13 | 14 | /// Pauses execution for at minimum `us` microseconds. Pause can be longer 15 | /// if the implementation requires it due to precision/timing issues. 16 | fn delay_us(&mut self, mut us: u32) { 17 | const MAX_MICROS: u32 = u32::MAX / NANOS_PER_MICRO; 18 | 19 | // Avoid potential overflow if micro -> nano conversion is too large 20 | while us > MAX_MICROS { 21 | us -= MAX_MICROS; 22 | self.delay_ns(MAX_MICROS * NANOS_PER_MICRO); 23 | } 24 | 25 | self.delay_ns(us * NANOS_PER_MICRO); 26 | } 27 | 28 | /// Pauses execution for at minimum `ms` milliseconds. Pause can be longer 29 | /// if the implementation requires it due to precision/timing issues. 30 | #[inline] 31 | fn delay_ms(&mut self, mut ms: u32) { 32 | const MAX_MILLIS: u32 = u32::MAX / NANOS_PER_MILLI; 33 | 34 | // Avoid potential overflow if milli -> nano conversion is too large 35 | while ms > MAX_MILLIS { 36 | ms -= MAX_MILLIS; 37 | self.delay_ns(MAX_MILLIS * NANOS_PER_MILLI); 38 | } 39 | 40 | self.delay_ns(ms * NANOS_PER_MILLI); 41 | } 42 | } 43 | 44 | impl DelayNs for &mut T 45 | where 46 | T: DelayNs + ?Sized, 47 | { 48 | #[inline] 49 | fn delay_ns(&mut self, ns: u32) { 50 | T::delay_ns(self, ns); 51 | } 52 | 53 | #[inline] 54 | fn delay_us(&mut self, us: u32) { 55 | T::delay_us(self, us); 56 | } 57 | 58 | #[inline] 59 | fn delay_ms(&mut self, ms: u32) { 60 | T::delay_ms(self, ms); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /embedded-hal/src/digital.rs: -------------------------------------------------------------------------------- 1 | //! Blocking Digital I/O. 2 | 3 | use core::ops::Not; 4 | 5 | #[cfg(feature = "defmt-03")] 6 | use crate::defmt; 7 | 8 | /// Error. 9 | pub trait Error: core::fmt::Debug { 10 | /// Convert error to a generic error kind 11 | /// 12 | /// By using this method, errors freely defined by HAL implementations 13 | /// can be converted to a set of generic errors upon which generic 14 | /// code can act. 15 | fn kind(&self) -> ErrorKind; 16 | } 17 | 18 | impl Error for core::convert::Infallible { 19 | fn kind(&self) -> ErrorKind { 20 | match *self {} 21 | } 22 | } 23 | 24 | /// Error kind. 25 | /// 26 | /// This represents a common set of operation errors. HAL implementations are 27 | /// free to define more specific or additional error types. However, by providing 28 | /// a mapping to these common errors, generic code can still react to them. 29 | #[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] 30 | #[cfg_attr(feature = "defmt-03", derive(defmt::Format))] 31 | #[non_exhaustive] 32 | pub enum ErrorKind { 33 | /// A different error occurred. The original error may contain more information. 34 | Other, 35 | } 36 | 37 | impl Error for ErrorKind { 38 | #[inline] 39 | fn kind(&self) -> ErrorKind { 40 | *self 41 | } 42 | } 43 | 44 | impl core::error::Error for ErrorKind {} 45 | 46 | impl core::fmt::Display for ErrorKind { 47 | #[inline] 48 | fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { 49 | match self { 50 | Self::Other => write!( 51 | f, 52 | "A different error occurred. The original error may contain more information" 53 | ), 54 | } 55 | } 56 | } 57 | 58 | /// Error type trait. 59 | /// 60 | /// This just defines the error type, to be used by the other traits. 61 | pub trait ErrorType { 62 | /// Error type 63 | type Error: Error; 64 | } 65 | 66 | impl ErrorType for &T { 67 | type Error = T::Error; 68 | } 69 | 70 | impl ErrorType for &mut T { 71 | type Error = T::Error; 72 | } 73 | 74 | /// Digital output pin state. 75 | /// 76 | /// Conversion from `bool` and logical negation are also implemented 77 | /// for this type. 78 | /// ```rust 79 | /// # use embedded_hal::digital::PinState; 80 | /// let state = PinState::from(false); 81 | /// assert_eq!(state, PinState::Low); 82 | /// assert_eq!(!state, PinState::High); 83 | /// ``` 84 | #[derive(Debug, PartialEq, Eq, Clone, Copy)] 85 | #[cfg_attr(feature = "defmt-03", derive(defmt::Format))] 86 | pub enum PinState { 87 | /// Low pin state. 88 | Low, 89 | /// High pin state. 90 | High, 91 | } 92 | 93 | impl From for PinState { 94 | #[inline] 95 | fn from(value: bool) -> Self { 96 | match value { 97 | false => PinState::Low, 98 | true => PinState::High, 99 | } 100 | } 101 | } 102 | 103 | impl Not for PinState { 104 | type Output = PinState; 105 | 106 | #[inline] 107 | fn not(self) -> Self::Output { 108 | match self { 109 | PinState::High => PinState::Low, 110 | PinState::Low => PinState::High, 111 | } 112 | } 113 | } 114 | 115 | impl From for bool { 116 | #[inline] 117 | fn from(value: PinState) -> bool { 118 | match value { 119 | PinState::Low => false, 120 | PinState::High => true, 121 | } 122 | } 123 | } 124 | 125 | /// Single digital push-pull output pin. 126 | pub trait OutputPin: ErrorType { 127 | /// Drives the pin low. 128 | /// 129 | /// *NOTE* the actual electrical state of the pin may not actually be low, e.g. due to external 130 | /// electrical sources. 131 | fn set_low(&mut self) -> Result<(), Self::Error>; 132 | 133 | /// Drives the pin high. 134 | /// 135 | /// *NOTE* the actual electrical state of the pin may not actually be high, e.g. due to external 136 | /// electrical sources. 137 | fn set_high(&mut self) -> Result<(), Self::Error>; 138 | 139 | /// Drives the pin high or low depending on the provided value. 140 | /// 141 | /// *NOTE* the actual electrical state of the pin may not actually be high or low, e.g. due to external 142 | /// electrical sources. 143 | #[inline] 144 | fn set_state(&mut self, state: PinState) -> Result<(), Self::Error> { 145 | match state { 146 | PinState::Low => self.set_low(), 147 | PinState::High => self.set_high(), 148 | } 149 | } 150 | } 151 | 152 | impl OutputPin for &mut T { 153 | #[inline] 154 | fn set_low(&mut self) -> Result<(), Self::Error> { 155 | T::set_low(self) 156 | } 157 | 158 | #[inline] 159 | fn set_high(&mut self) -> Result<(), Self::Error> { 160 | T::set_high(self) 161 | } 162 | 163 | #[inline] 164 | fn set_state(&mut self, state: PinState) -> Result<(), Self::Error> { 165 | T::set_state(self, state) 166 | } 167 | } 168 | 169 | /// Push-pull output pin that can read its output state. 170 | pub trait StatefulOutputPin: OutputPin { 171 | /// Is the pin in drive high mode? 172 | /// 173 | /// *NOTE* this does *not* read the electrical state of the pin. 174 | fn is_set_high(&mut self) -> Result; 175 | 176 | /// Is the pin in drive low mode? 177 | /// 178 | /// *NOTE* this does *not* read the electrical state of the pin. 179 | fn is_set_low(&mut self) -> Result; 180 | 181 | /// Toggle pin output. 182 | fn toggle(&mut self) -> Result<(), Self::Error> { 183 | let was_low: bool = self.is_set_low()?; 184 | self.set_state(PinState::from(was_low)) 185 | } 186 | } 187 | 188 | impl StatefulOutputPin for &mut T { 189 | #[inline] 190 | fn is_set_high(&mut self) -> Result { 191 | T::is_set_high(self) 192 | } 193 | 194 | #[inline] 195 | fn is_set_low(&mut self) -> Result { 196 | T::is_set_low(self) 197 | } 198 | 199 | #[inline] 200 | fn toggle(&mut self) -> Result<(), Self::Error> { 201 | T::toggle(self) 202 | } 203 | } 204 | 205 | /// Single digital input pin. 206 | pub trait InputPin: ErrorType { 207 | /// Is the input pin high? 208 | fn is_high(&mut self) -> Result; 209 | 210 | /// Is the input pin low? 211 | fn is_low(&mut self) -> Result; 212 | } 213 | 214 | impl InputPin for &mut T { 215 | #[inline] 216 | fn is_high(&mut self) -> Result { 217 | T::is_high(self) 218 | } 219 | 220 | #[inline] 221 | fn is_low(&mut self) -> Result { 222 | T::is_low(self) 223 | } 224 | } 225 | -------------------------------------------------------------------------------- /embedded-hal/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![doc = include_str!("../README.md")] 2 | #![warn(missing_docs)] 3 | #![no_std] 4 | 5 | pub mod delay; 6 | pub mod digital; 7 | pub mod i2c; 8 | pub mod pwm; 9 | pub mod spi; 10 | 11 | mod private { 12 | use crate::i2c::{SevenBitAddress, TenBitAddress}; 13 | pub trait Sealed {} 14 | 15 | impl Sealed for SevenBitAddress {} 16 | impl Sealed for TenBitAddress {} 17 | } 18 | 19 | // needed to prevent defmt macros from breaking, since they emit code that does `defmt::blahblah`. 20 | #[cfg(feature = "defmt-03")] 21 | use defmt_03 as defmt; 22 | -------------------------------------------------------------------------------- /embedded-hal/src/pwm.rs: -------------------------------------------------------------------------------- 1 | //! Pulse Width Modulation (PWM) traits. 2 | 3 | #[cfg(feature = "defmt-03")] 4 | use crate::defmt; 5 | 6 | /// Error 7 | pub trait Error: core::fmt::Debug { 8 | /// Convert error to a generic error kind. 9 | /// 10 | /// By using this method, errors freely defined by HAL implementations 11 | /// can be converted to a set of generic errors upon which generic 12 | /// code can act. 13 | fn kind(&self) -> ErrorKind; 14 | } 15 | 16 | impl Error for core::convert::Infallible { 17 | #[inline] 18 | fn kind(&self) -> ErrorKind { 19 | match *self {} 20 | } 21 | } 22 | 23 | /// Error kind. 24 | /// 25 | /// This represents a common set of operation errors. HAL implementations are 26 | /// free to define more specific or additional error types. However, by providing 27 | /// a mapping to these common errors, generic code can still react to them. 28 | #[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] 29 | #[cfg_attr(feature = "defmt-03", derive(defmt::Format))] 30 | #[non_exhaustive] 31 | pub enum ErrorKind { 32 | /// A different error occurred. The original error may contain more information. 33 | Other, 34 | } 35 | 36 | impl Error for ErrorKind { 37 | #[inline] 38 | fn kind(&self) -> ErrorKind { 39 | *self 40 | } 41 | } 42 | 43 | impl core::error::Error for ErrorKind {} 44 | 45 | impl core::fmt::Display for ErrorKind { 46 | #[inline] 47 | fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { 48 | match self { 49 | Self::Other => write!( 50 | f, 51 | "A different error occurred. The original error may contain more information" 52 | ), 53 | } 54 | } 55 | } 56 | 57 | /// Error type trait. 58 | /// 59 | /// This just defines the error type, to be used by the other traits. 60 | pub trait ErrorType { 61 | /// Error type 62 | type Error: Error; 63 | } 64 | 65 | impl ErrorType for &mut T { 66 | type Error = T::Error; 67 | } 68 | 69 | /// Single PWM channel / pin. 70 | pub trait SetDutyCycle: ErrorType { 71 | /// Get the maximum duty cycle value. 72 | /// 73 | /// This value corresponds to a 100% duty cycle. 74 | fn max_duty_cycle(&self) -> u16; 75 | 76 | /// Set the duty cycle to `duty / max_duty`. 77 | /// 78 | /// The caller is responsible for ensuring that the duty cycle value is less than or equal to the maximum duty cycle value, 79 | /// as reported by [`max_duty_cycle`]. 80 | /// 81 | /// [`max_duty_cycle`]: SetDutyCycle::max_duty_cycle 82 | fn set_duty_cycle(&mut self, duty: u16) -> Result<(), Self::Error>; 83 | 84 | /// Set the duty cycle to 0%, or always inactive. 85 | #[inline] 86 | fn set_duty_cycle_fully_off(&mut self) -> Result<(), Self::Error> { 87 | self.set_duty_cycle(0) 88 | } 89 | 90 | /// Set the duty cycle to 100%, or always active. 91 | #[inline] 92 | fn set_duty_cycle_fully_on(&mut self) -> Result<(), Self::Error> { 93 | self.set_duty_cycle(self.max_duty_cycle()) 94 | } 95 | 96 | /// Set the duty cycle to `num / denom`. 97 | /// 98 | /// The caller is responsible for ensuring that `num` is less than or equal to `denom`, 99 | /// and that `denom` is not zero. 100 | #[inline] 101 | fn set_duty_cycle_fraction(&mut self, num: u16, denom: u16) -> Result<(), Self::Error> { 102 | debug_assert!(denom != 0); 103 | debug_assert!(num <= denom); 104 | let duty = u32::from(num) * u32::from(self.max_duty_cycle()) / u32::from(denom); 105 | 106 | // This is safe because we know that `num <= denom`, so `duty <= self.max_duty_cycle()` (u16) 107 | #[allow(clippy::cast_possible_truncation)] 108 | { 109 | self.set_duty_cycle(duty as u16) 110 | } 111 | } 112 | 113 | /// Set the duty cycle to `percent / 100` 114 | /// 115 | /// The caller is responsible for ensuring that `percent` is less than or equal to 100. 116 | #[inline] 117 | fn set_duty_cycle_percent(&mut self, percent: u8) -> Result<(), Self::Error> { 118 | self.set_duty_cycle_fraction(u16::from(percent), 100) 119 | } 120 | } 121 | 122 | impl SetDutyCycle for &mut T { 123 | #[inline] 124 | fn max_duty_cycle(&self) -> u16 { 125 | T::max_duty_cycle(self) 126 | } 127 | 128 | #[inline] 129 | fn set_duty_cycle(&mut self, duty: u16) -> Result<(), Self::Error> { 130 | T::set_duty_cycle(self, duty) 131 | } 132 | 133 | #[inline] 134 | fn set_duty_cycle_fully_off(&mut self) -> Result<(), Self::Error> { 135 | T::set_duty_cycle_fully_off(self) 136 | } 137 | 138 | #[inline] 139 | fn set_duty_cycle_fully_on(&mut self) -> Result<(), Self::Error> { 140 | T::set_duty_cycle_fully_on(self) 141 | } 142 | 143 | #[inline] 144 | fn set_duty_cycle_fraction(&mut self, num: u16, denom: u16) -> Result<(), Self::Error> { 145 | T::set_duty_cycle_fraction(self, num, denom) 146 | } 147 | 148 | #[inline] 149 | fn set_duty_cycle_percent(&mut self, percent: u8) -> Result<(), Self::Error> { 150 | T::set_duty_cycle_percent(self, percent) 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /embedded-io-adapters/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 6 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 7 | 8 | ## Unreleased 9 | 10 | - Added `ToFmt` adapter for `core::fmt::Write`. 11 | 12 | ## 0.6.1 - 2023-11-28 13 | 14 | - Handle reading from `FromTokio` with empty buffer, ensuring `Ok(0)` is always returned. 15 | - Use `feature()` on nightly toolchains only. This adds async support for 1.75 beta and stable. 16 | 17 | ## 0.6.0 - 2023-10-02 18 | 19 | - Add support for adapting `BufRead` from `futures` and `tokio`. 20 | - Return an error when a wrapped `std`/`futures`/`tokio` `write()` call returns 21 | `Ok(0)` to comply with `embedded_io::Write` requirements. 22 | 23 | ## 0.5.0 - 2023-08-06 24 | 25 | - First release 26 | -------------------------------------------------------------------------------- /embedded-io-adapters/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "embedded-io-adapters" 3 | version = "0.6.1" 4 | edition = "2021" 5 | rust-version = "1.60" 6 | description = "Adapters between the `embedded-io` traits and other I/O traits" 7 | repository = "https://github.com/rust-embedded/embedded-hal" 8 | readme = "README.md" 9 | license = "MIT OR Apache-2.0" 10 | categories = [ 11 | "embedded", 12 | "no-std", 13 | ] 14 | 15 | [features] 16 | std = ["embedded-io/std"] 17 | tokio-1 = ["std", "dep:tokio", "dep:embedded-io-async", "embedded-io-async?/std"] 18 | futures-03 = ["std", "dep:futures", "dep:embedded-io-async", "embedded-io-async?/std"] 19 | 20 | [dependencies] 21 | embedded-io = { version = "0.6", path = "../embedded-io" } 22 | embedded-io-async = { version = "0.6.1", path = "../embedded-io-async", optional = true } 23 | 24 | futures = { version = "0.3.21", features = ["std"], default-features = false, optional = true } 25 | tokio = { version = "1", features = ["io-util"], default-features = false, optional = true } 26 | 27 | [package.metadata.docs.rs] 28 | features = ["std", "tokio-1", "futures-03"] 29 | rustdoc-args = ["--cfg", "docsrs"] 30 | -------------------------------------------------------------------------------- /embedded-io-adapters/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) 2023 The embedded-io authors 2 | 3 | Permission is hereby granted, free of charge, to any 4 | person obtaining a copy of this software and associated 5 | documentation files (the "Software"), to deal in the 6 | Software without restriction, including without 7 | limitation the rights to use, copy, modify, merge, 8 | publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software 10 | is furnished to do so, subject to the following 11 | conditions: 12 | 13 | The above copyright notice and this permission notice 14 | shall be included in all copies or substantial portions 15 | of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 18 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 19 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 20 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 21 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 22 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 24 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 25 | DEALINGS IN THE SOFTWARE. 26 | -------------------------------------------------------------------------------- /embedded-io-adapters/README.md: -------------------------------------------------------------------------------- 1 | [![crates.io](https://img.shields.io/crates/d/embedded-io-adapters.svg)](https://crates.io/crates/embedded-io-adapters) 2 | [![crates.io](https://img.shields.io/crates/v/embedded-io-adapters.svg)](https://crates.io/crates/embedded-io-adapters) 3 | [![Documentation](https://docs.rs/embedded-io-adapters/badge.svg)](https://docs.rs/embedded-io-adapters) 4 | 5 | # `embedded-io-adapters` 6 | 7 | This project is developed and maintained by the [HAL team](https://github.com/rust-embedded/wg#the-hal-team). 8 | 9 | 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. 10 | 11 | The adapters are structs that wrap an I/O stream and implement another family of I/O traits 12 | based on the wrapped streams. This allows "converting" from an `embedded_io::Read` 13 | to a `std::io::Read` or vice versa, for example. 14 | 15 | There are no separate adapters for `Read`/`ReadBuf`/`Write` traits. Instead, a single 16 | adapter implements the right traits based on what the inner type implements. 17 | This allows using these adapters when using combinations of traits, like `Read+Write`. 18 | 19 | ## Supported traits 20 | 21 | For `embedded-io`: 22 | 23 | - [`std::io`](https://doc.rust-lang.org/stable/std/io/index.html) traits. Needs the `std` feature. 24 | 25 | For `embedded-io-async`: 26 | 27 | - [`futures` 0.3](https://crates.io/crates/futures) traits. Needs the `futures-03` feature. 28 | - [`tokio` 1.x](https://crates.io/crates/tokio) traits. Needs the `tokio-1` feature. 29 | 30 | ## Minimum Supported Rust Version (MSRV) 31 | 32 | This crate is guaranteed to compile on stable Rust 1.60 and up. It *might* 33 | compile with older versions but that may change in any new patch release. 34 | 35 | See [here](../docs/msrv.md) for details on how the MSRV may be upgraded. 36 | 37 | Enabling any of the `tokio-*` or `futures-*` Cargo features requires Rust 1.75 or higher. 38 | 39 | ## License 40 | 41 | Licensed under either of 42 | 43 | - Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or 44 | ) 45 | - MIT license ([LICENSE-MIT](LICENSE-MIT) or ) 46 | 47 | at your option. 48 | 49 | ### Contribution 50 | 51 | Unless you explicitly state otherwise, any contribution intentionally submitted 52 | for inclusion in the work by you, as defined in the Apache-2.0 license, shall be 53 | dual licensed as above, without any additional terms or conditions. 54 | -------------------------------------------------------------------------------- /embedded-io-adapters/src/fmt.rs: -------------------------------------------------------------------------------- 1 | //! Adapters to the `core::fmt::Write`. 2 | 3 | /// Adapter to the `core::fmt::Write` trait. 4 | #[derive(Clone, Default, PartialEq, Debug)] 5 | pub struct ToFmt { 6 | inner: T, 7 | } 8 | 9 | impl ToFmt { 10 | /// Create a new adapter. 11 | pub fn new(inner: T) -> Self { 12 | Self { inner } 13 | } 14 | 15 | /// Consume the adapter, returning the inner object. 16 | pub fn into_inner(self) -> T { 17 | self.inner 18 | } 19 | } 20 | 21 | impl ToFmt { 22 | /// Borrow the inner object. 23 | pub fn inner(&self) -> &T { 24 | &self.inner 25 | } 26 | 27 | /// Mutably borrow the inner object. 28 | pub fn inner_mut(&mut self) -> &mut T { 29 | &mut self.inner 30 | } 31 | } 32 | 33 | impl core::fmt::Write for ToFmt { 34 | fn write_str(&mut self, s: &str) -> core::fmt::Result { 35 | self.inner.write_all(s.as_bytes()).or(Err(core::fmt::Error)) 36 | } 37 | 38 | // Use fmt::Write default impls for 39 | // * write_fmt(): better here than e-io::Write::write_fmt 40 | // since we don't need to bother with saving the Error 41 | // * write_char(): would be the same 42 | } 43 | -------------------------------------------------------------------------------- /embedded-io-adapters/src/futures_03.rs: -------------------------------------------------------------------------------- 1 | //! Adapters to/from `futures::io` traits. 2 | 3 | // MSRV is 1.60 if you don't enable async, 1.80 if you do. 4 | // Cargo.toml has 1.60, which makes Clippy complain that `poll_fn` was introduced 5 | // in 1.64. So, just silence it for this file. 6 | #![allow(clippy::incompatible_msrv)] 7 | 8 | use core::future::poll_fn; 9 | use core::pin::Pin; 10 | 11 | use futures::AsyncBufReadExt; 12 | 13 | /// Adapter from `futures::io` traits. 14 | #[derive(Clone)] 15 | pub struct FromFutures { 16 | inner: T, 17 | } 18 | 19 | impl FromFutures { 20 | /// Create a new adapter. 21 | pub fn new(inner: T) -> Self { 22 | Self { inner } 23 | } 24 | 25 | /// Consume the adapter, returning the inner object. 26 | pub fn into_inner(self) -> T { 27 | self.inner 28 | } 29 | } 30 | 31 | impl FromFutures { 32 | /// Borrow the inner object. 33 | pub fn inner(&self) -> &T { 34 | &self.inner 35 | } 36 | 37 | /// Mutably borrow the inner object. 38 | pub fn inner_mut(&mut self) -> &mut T { 39 | &mut self.inner 40 | } 41 | } 42 | 43 | impl embedded_io::ErrorType for FromFutures { 44 | type Error = std::io::Error; 45 | } 46 | 47 | impl embedded_io_async::Read for FromFutures { 48 | async fn read(&mut self, buf: &mut [u8]) -> Result { 49 | poll_fn(|cx| Pin::new(&mut self.inner).poll_read(cx, buf)).await 50 | } 51 | } 52 | 53 | impl embedded_io_async::BufRead for FromFutures { 54 | async fn fill_buf(&mut self) -> Result<&[u8], Self::Error> { 55 | self.inner.fill_buf().await 56 | } 57 | 58 | fn consume(&mut self, amt: usize) { 59 | Pin::new(&mut self.inner).consume(amt); 60 | } 61 | } 62 | 63 | impl embedded_io_async::Write for FromFutures { 64 | async fn write(&mut self, buf: &[u8]) -> Result { 65 | match poll_fn(|cx| Pin::new(&mut self.inner).poll_write(cx, buf)).await { 66 | Ok(0) if !buf.is_empty() => Err(std::io::ErrorKind::WriteZero.into()), 67 | Ok(n) => Ok(n), 68 | Err(e) => Err(e), 69 | } 70 | } 71 | 72 | async fn flush(&mut self) -> Result<(), Self::Error> { 73 | poll_fn(|cx| Pin::new(&mut self.inner).poll_flush(cx)).await 74 | } 75 | } 76 | 77 | impl embedded_io_async::Seek for FromFutures { 78 | async fn seek(&mut self, pos: embedded_io::SeekFrom) -> Result { 79 | poll_fn(move |cx| Pin::new(&mut self.inner).poll_seek(cx, pos.into())).await 80 | } 81 | } 82 | 83 | // TODO: ToFutures. 84 | // It's a bit tricky because futures::io is "stateless", while we're "stateful" (we 85 | // return futures that borrow Self and get polled for the duration of the operation.) 86 | // It can probably done by storing the futures in Self, with unsafe Pin hacks because 87 | // we're a self-referential struct 88 | -------------------------------------------------------------------------------- /embedded-io-adapters/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![cfg_attr(not(feature = "std"), no_std)] 2 | #![cfg_attr(docsrs, feature(doc_cfg))] 3 | #![warn(missing_docs)] 4 | #![doc = include_str!("../README.md")] 5 | 6 | pub mod fmt; 7 | 8 | #[cfg(feature = "std")] 9 | #[cfg_attr(docsrs, doc(cfg(feature = "std")))] 10 | pub mod std; 11 | 12 | #[cfg(feature = "futures-03")] 13 | #[cfg_attr(docsrs, doc(cfg(feature = "futures-03")))] 14 | pub mod futures_03; 15 | 16 | #[cfg(feature = "tokio-1")] 17 | #[cfg_attr(docsrs, doc(cfg(feature = "tokio-1")))] 18 | pub mod tokio_1; 19 | -------------------------------------------------------------------------------- /embedded-io-adapters/src/std.rs: -------------------------------------------------------------------------------- 1 | //! Adapters to/from `std::io` traits. 2 | 3 | use embedded_io::Error as _; 4 | 5 | /// Adapter from `std::io` traits. 6 | #[derive(Clone)] 7 | pub struct FromStd { 8 | inner: T, 9 | } 10 | 11 | impl FromStd { 12 | /// Create a new adapter. 13 | pub fn new(inner: T) -> Self { 14 | Self { inner } 15 | } 16 | 17 | /// Consume the adapter, returning the inner object. 18 | pub fn into_inner(self) -> T { 19 | self.inner 20 | } 21 | } 22 | 23 | impl FromStd { 24 | /// Borrow the inner object. 25 | pub fn inner(&self) -> &T { 26 | &self.inner 27 | } 28 | 29 | /// Mutably borrow the inner object. 30 | pub fn inner_mut(&mut self) -> &mut T { 31 | &mut self.inner 32 | } 33 | } 34 | 35 | impl embedded_io::ErrorType for FromStd { 36 | type Error = std::io::Error; 37 | } 38 | 39 | impl embedded_io::Read for FromStd { 40 | fn read(&mut self, buf: &mut [u8]) -> Result { 41 | self.inner.read(buf) 42 | } 43 | } 44 | 45 | impl embedded_io::BufRead for FromStd { 46 | fn fill_buf(&mut self) -> Result<&[u8], Self::Error> { 47 | self.inner.fill_buf() 48 | } 49 | 50 | fn consume(&mut self, amt: usize) { 51 | self.inner.consume(amt); 52 | } 53 | } 54 | 55 | impl embedded_io::Write for FromStd { 56 | fn write(&mut self, buf: &[u8]) -> Result { 57 | match self.inner.write(buf) { 58 | Ok(0) if !buf.is_empty() => Err(std::io::ErrorKind::WriteZero.into()), 59 | Ok(n) => Ok(n), 60 | Err(e) => Err(e), 61 | } 62 | } 63 | fn flush(&mut self) -> Result<(), Self::Error> { 64 | self.inner.flush() 65 | } 66 | } 67 | 68 | impl embedded_io::Seek for FromStd { 69 | fn seek(&mut self, pos: embedded_io::SeekFrom) -> Result { 70 | self.inner.seek(pos.into()) 71 | } 72 | } 73 | 74 | /// Adapter to `std::io` traits. 75 | #[derive(Clone)] 76 | pub struct ToStd { 77 | inner: T, 78 | } 79 | 80 | impl ToStd { 81 | /// Create a new adapter. 82 | pub fn new(inner: T) -> Self { 83 | Self { inner } 84 | } 85 | 86 | /// Consume the adapter, returning the inner object. 87 | pub fn into_inner(self) -> T { 88 | self.inner 89 | } 90 | } 91 | 92 | impl ToStd { 93 | /// Borrow the inner object. 94 | pub fn inner(&self) -> &T { 95 | &self.inner 96 | } 97 | 98 | /// Mutably borrow the inner object. 99 | pub fn inner_mut(&mut self) -> &mut T { 100 | &mut self.inner 101 | } 102 | } 103 | 104 | impl std::io::Read for ToStd { 105 | fn read(&mut self, buf: &mut [u8]) -> Result { 106 | self.inner.read(buf).map_err(to_std_error) 107 | } 108 | } 109 | 110 | impl std::io::Write for ToStd { 111 | fn write(&mut self, buf: &[u8]) -> Result { 112 | match self.inner.write(buf) { 113 | Ok(n) => Ok(n), 114 | Err(e) if e.kind() == embedded_io::ErrorKind::WriteZero => Ok(0), 115 | Err(e) => Err(to_std_error(e)), 116 | } 117 | } 118 | fn flush(&mut self) -> Result<(), std::io::Error> { 119 | self.inner.flush().map_err(to_std_error) 120 | } 121 | } 122 | 123 | impl std::io::Seek for ToStd { 124 | fn seek(&mut self, pos: std::io::SeekFrom) -> Result { 125 | self.inner.seek(pos.into()).map_err(to_std_error) 126 | } 127 | } 128 | 129 | /// Convert a embedded-io error to a [`std::io::Error`] 130 | pub fn to_std_error(err: T) -> std::io::Error { 131 | std::io::Error::new(err.kind().into(), format!("{err:?}")) 132 | } 133 | -------------------------------------------------------------------------------- /embedded-io-adapters/src/tokio_1.rs: -------------------------------------------------------------------------------- 1 | //! Adapters to/from `tokio::io` traits. 2 | 3 | // MSRV is 1.60 if you don't enable async, 1.80 if you do. 4 | // Cargo.toml has 1.60, which makes Clippy complain that `poll_fn` was introduced 5 | // in 1.64. So, just silence it for this file. 6 | #![allow(clippy::incompatible_msrv)] 7 | 8 | use core::future::poll_fn; 9 | use core::pin::Pin; 10 | use core::task::Poll; 11 | 12 | use tokio::io::AsyncBufReadExt; 13 | 14 | /// Adapter from `tokio::io` traits. 15 | #[derive(Clone)] 16 | pub struct FromTokio { 17 | inner: T, 18 | } 19 | 20 | impl FromTokio { 21 | /// Create a new adapter. 22 | pub fn new(inner: T) -> Self { 23 | Self { inner } 24 | } 25 | 26 | /// Consume the adapter, returning the inner object. 27 | pub fn into_inner(self) -> T { 28 | self.inner 29 | } 30 | } 31 | 32 | impl FromTokio { 33 | /// Borrow the inner object. 34 | pub fn inner(&self) -> &T { 35 | &self.inner 36 | } 37 | 38 | /// Mutably borrow the inner object. 39 | pub fn inner_mut(&mut self) -> &mut T { 40 | &mut self.inner 41 | } 42 | } 43 | 44 | impl embedded_io::ErrorType for FromTokio { 45 | type Error = std::io::Error; 46 | } 47 | 48 | impl embedded_io_async::Read for FromTokio { 49 | async fn read(&mut self, buf: &mut [u8]) -> Result { 50 | // The current tokio implementation (https://github.com/tokio-rs/tokio/blob/tokio-1.33.0/tokio/src/io/poll_evented.rs#L165) 51 | // does not consider the case of buf.is_empty() as a special case, 52 | // which can cause Poll::Pending to be returned at the end of the stream when called with an empty buffer. 53 | // This poll will, however, never become ready, as no more bytes will be received. 54 | if buf.is_empty() { 55 | return Ok(0); 56 | } 57 | 58 | poll_fn(|cx| { 59 | let mut buf = tokio::io::ReadBuf::new(buf); 60 | match Pin::new(&mut self.inner).poll_read(cx, &mut buf) { 61 | Poll::Ready(r) => match r { 62 | Ok(()) => Poll::Ready(Ok(buf.filled().len())), 63 | Err(e) => Poll::Ready(Err(e)), 64 | }, 65 | Poll::Pending => Poll::Pending, 66 | } 67 | }) 68 | .await 69 | } 70 | } 71 | 72 | impl embedded_io_async::BufRead for FromTokio { 73 | async fn fill_buf(&mut self) -> Result<&[u8], Self::Error> { 74 | self.inner.fill_buf().await 75 | } 76 | 77 | fn consume(&mut self, amt: usize) { 78 | Pin::new(&mut self.inner).consume(amt); 79 | } 80 | } 81 | 82 | impl embedded_io_async::Write for FromTokio { 83 | async fn write(&mut self, buf: &[u8]) -> Result { 84 | match poll_fn(|cx| Pin::new(&mut self.inner).poll_write(cx, buf)).await { 85 | Ok(0) if !buf.is_empty() => Err(std::io::ErrorKind::WriteZero.into()), 86 | Ok(n) => Ok(n), 87 | Err(e) => Err(e), 88 | } 89 | } 90 | 91 | async fn flush(&mut self) -> Result<(), Self::Error> { 92 | poll_fn(|cx| Pin::new(&mut self.inner).poll_flush(cx)).await 93 | } 94 | } 95 | 96 | impl embedded_io_async::Seek for FromTokio { 97 | async fn seek(&mut self, pos: embedded_io::SeekFrom) -> Result { 98 | // Note: `start_seek` can return an error if there is another seek in progress. 99 | // Therefor it is recommended to call `poll_complete` before any call to `start_seek`. 100 | poll_fn(|cx| Pin::new(&mut self.inner).poll_complete(cx)).await?; 101 | Pin::new(&mut self.inner).start_seek(pos.into())?; 102 | poll_fn(|cx| Pin::new(&mut self.inner).poll_complete(cx)).await 103 | } 104 | } 105 | 106 | // TODO: ToTokio. 107 | // It's a bit tricky because tokio::io is "stateless", while we're "stateful" (we 108 | // return futures that borrow Self and get polled for the duration of the operation.) 109 | // It can probably done by storing the futures in Self, with unsafe Pin hacks because 110 | // we're a self-referential struct 111 | -------------------------------------------------------------------------------- /embedded-io-async/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 6 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 7 | 8 | ## Unreleased 9 | 10 | Add unreleased changes here 11 | 12 | ## 0.6.1 - 2023-11-28 13 | 14 | - Use `feature()` on nightly toolchains only. This adds support for 1.75 beta and stable. 15 | 16 | ## 0.6.0 - 2023-10-02 17 | 18 | - Prohibit `Write::write` implementations returning `Ok(0)` unless there is no 19 | data to write; consequently remove `WriteAllError`. 20 | Update the `&mut [u8]` impl to possibly return 21 | a new `SliceWriteError` if the slice is full instead of `Ok(0)`. 22 | - Add `WriteZero` variant to `ErrorKind` for implementations that previously 23 | may have returned `Ok(0)` to indicate no further data could be written. 24 | - `Write::write_all` now panics if the `write()` implementation returns `Ok(0)`. 25 | 26 | ## 0.5.0 - 2023-08-06 27 | 28 | - First release 29 | -------------------------------------------------------------------------------- /embedded-io-async/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "embedded-io-async" 3 | version = "0.6.1" 4 | edition = "2021" 5 | rust-version = "1.75" 6 | description = "Async embedded IO traits" 7 | repository = "https://github.com/rust-embedded/embedded-hal" 8 | readme = "README.md" 9 | license = "MIT OR Apache-2.0" 10 | categories = [ 11 | "embedded", 12 | "no-std", 13 | ] 14 | 15 | [features] 16 | std = ["alloc", "embedded-io/std"] 17 | alloc = ["embedded-io/alloc"] 18 | defmt-03 = ["dep:defmt-03", "embedded-io/defmt-03"] 19 | 20 | [dependencies] 21 | embedded-io = { version = "0.6.1", path = "../embedded-io" } 22 | defmt-03 = { package = "defmt", version = "0.3", optional = true } 23 | 24 | [package.metadata.docs.rs] 25 | features = ["std"] 26 | rustdoc-args = ["--cfg", "docsrs"] 27 | -------------------------------------------------------------------------------- /embedded-io-async/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) 2023 The embedded-io-async authors 2 | 3 | Permission is hereby granted, free of charge, to any 4 | person obtaining a copy of this software and associated 5 | documentation files (the "Software"), to deal in the 6 | Software without restriction, including without 7 | limitation the rights to use, copy, modify, merge, 8 | publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software 10 | is furnished to do so, subject to the following 11 | conditions: 12 | 13 | The above copyright notice and this permission notice 14 | shall be included in all copies or substantial portions 15 | of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 18 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 19 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 20 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 21 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 22 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 24 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 25 | DEALINGS IN THE SOFTWARE. 26 | -------------------------------------------------------------------------------- /embedded-io-async/README.md: -------------------------------------------------------------------------------- 1 | [![crates.io](https://img.shields.io/crates/d/embedded-io-async.svg)](https://crates.io/crates/embedded-io-async) 2 | [![crates.io](https://img.shields.io/crates/v/embedded-io-async.svg)](https://crates.io/crates/embedded-io-async) 3 | [![Documentation](https://docs.rs/embedded-io-async/badge.svg)](https://docs.rs/embedded-io-async) 4 | 5 | # `embedded-io-async` 6 | 7 | Async IO traits for embedded systems. 8 | 9 | This crate contains asynchronous versions of the [`embedded-io`](https://crates.io/crates/embedded-io) traits and shares its scope and design goals. 10 | 11 | This project is developed and maintained by the [HAL team](https://github.com/rust-embedded/wg#the-hal-team). 12 | 13 | ## Optional Cargo features 14 | 15 | - **`std`**: Adds `From` impls to convert to/from `std::io` structs. 16 | - **`alloc`**: Adds blanket impls for `Box`, adds `Write` impl to `Vec`. 17 | - **`defmt-03`**: Derive `defmt::Format` from `defmt` 0.3 for enums and structs. 18 | 19 | ## Minimum Supported Rust Version (MSRV) 20 | 21 | This crate is guaranteed to compile on stable Rust 1.75 and up. It *might* 22 | compile with older versions but that may change in any new patch release. 23 | 24 | See [here](../docs/msrv.md) for details on how the MSRV may be upgraded. 25 | 26 | ## License 27 | 28 | Licensed under either of 29 | 30 | - Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or 31 | ) 32 | - MIT license ([LICENSE-MIT](LICENSE-MIT) or ) 33 | 34 | at your option. 35 | 36 | ### Contribution 37 | 38 | Unless you explicitly state otherwise, any contribution intentionally submitted 39 | for inclusion in the work by you, as defined in the Apache-2.0 license, shall be 40 | dual licensed as above, without any additional terms or conditions. 41 | -------------------------------------------------------------------------------- /embedded-io-async/src/impls/boxx.rs: -------------------------------------------------------------------------------- 1 | use crate::{BufRead, Read, Seek, SeekFrom, Write}; 2 | use alloc::boxed::Box; 3 | 4 | #[cfg_attr(docsrs, doc(cfg(any(feature = "std", feature = "alloc"))))] 5 | impl Read for Box { 6 | #[inline] 7 | async fn read(&mut self, buf: &mut [u8]) -> Result { 8 | T::read(self, buf).await 9 | } 10 | } 11 | 12 | #[cfg_attr(docsrs, doc(cfg(any(feature = "std", feature = "alloc"))))] 13 | impl BufRead for Box { 14 | #[inline] 15 | async fn fill_buf(&mut self) -> Result<&[u8], Self::Error> { 16 | T::fill_buf(self).await 17 | } 18 | 19 | #[inline] 20 | fn consume(&mut self, amt: usize) { 21 | T::consume(self, amt); 22 | } 23 | } 24 | 25 | #[cfg_attr(docsrs, doc(cfg(any(feature = "std", feature = "alloc"))))] 26 | impl Write for Box { 27 | #[inline] 28 | async fn write(&mut self, buf: &[u8]) -> Result { 29 | T::write(self, buf).await 30 | } 31 | 32 | #[inline] 33 | async fn flush(&mut self) -> Result<(), Self::Error> { 34 | T::flush(self).await 35 | } 36 | } 37 | 38 | #[cfg_attr(docsrs, doc(cfg(any(feature = "std", feature = "alloc"))))] 39 | impl Seek for Box { 40 | #[inline] 41 | async fn seek(&mut self, pos: SeekFrom) -> Result { 42 | T::seek(self, pos).await 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /embedded-io-async/src/impls/mod.rs: -------------------------------------------------------------------------------- 1 | mod slice_mut; 2 | mod slice_ref; 3 | 4 | #[cfg(feature = "alloc")] 5 | mod boxx; 6 | #[cfg(feature = "alloc")] 7 | mod vec; 8 | -------------------------------------------------------------------------------- /embedded-io-async/src/impls/slice_mut.rs: -------------------------------------------------------------------------------- 1 | use core::mem; 2 | use embedded_io::SliceWriteError; 3 | 4 | use crate::Write; 5 | 6 | /// Write is implemented for `&mut [u8]` by copying into the slice, overwriting 7 | /// its data. 8 | /// 9 | /// Note that writing updates the slice to point to the yet unwritten part. 10 | /// The slice will be empty when it has been completely overwritten. 11 | /// 12 | /// If the number of bytes to be written exceeds the size of the slice, write operations will 13 | /// return short writes: ultimately, `Ok(0)`; in this situation, `write_all` returns an error of 14 | /// kind `ErrorKind::WriteZero`. 15 | impl Write for &mut [u8] { 16 | #[inline] 17 | async fn write(&mut self, buf: &[u8]) -> Result { 18 | let amt = core::cmp::min(buf.len(), self.len()); 19 | if !buf.is_empty() && amt == 0 { 20 | return Err(SliceWriteError::Full); 21 | } 22 | let (a, b) = mem::take(self).split_at_mut(amt); 23 | a.copy_from_slice(&buf[..amt]); 24 | *self = b; 25 | Ok(amt) 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /embedded-io-async/src/impls/slice_ref.rs: -------------------------------------------------------------------------------- 1 | use crate::{BufRead, Read}; 2 | 3 | /// Read is implemented for `&[u8]` by copying from the slice. 4 | /// 5 | /// Note that reading updates the slice to point to the yet unread part. 6 | /// The slice will be empty when EOF is reached. 7 | impl Read for &[u8] { 8 | #[inline] 9 | async fn read(&mut self, buf: &mut [u8]) -> Result { 10 | let amt = core::cmp::min(buf.len(), self.len()); 11 | let (a, b) = self.split_at(amt); 12 | 13 | // First check if the amount of bytes we want to read is small: 14 | // `copy_from_slice` will generally expand to a call to `memcpy`, and 15 | // for a single byte the overhead is significant. 16 | if amt == 1 { 17 | buf[0] = a[0]; 18 | } else { 19 | buf[..amt].copy_from_slice(a); 20 | } 21 | 22 | *self = b; 23 | Ok(amt) 24 | } 25 | } 26 | 27 | impl BufRead for &[u8] { 28 | #[inline] 29 | async fn fill_buf(&mut self) -> Result<&[u8], Self::Error> { 30 | Ok(*self) 31 | } 32 | 33 | #[inline] 34 | fn consume(&mut self, amt: usize) { 35 | *self = &self[amt..]; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /embedded-io-async/src/impls/vec.rs: -------------------------------------------------------------------------------- 1 | use alloc::vec::Vec; 2 | 3 | use crate::Write; 4 | 5 | #[cfg_attr(docsrs, doc(cfg(any(feature = "std", feature = "alloc"))))] 6 | impl Write for Vec { 7 | #[inline] 8 | async fn write(&mut self, buf: &[u8]) -> Result { 9 | self.extend_from_slice(buf); 10 | Ok(buf.len()) 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /embedded-io/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 6 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 7 | 8 | ## Unreleased 9 | 10 | - Added `core::error::Error` implementations for every custom `impl Error` 11 | - Migrated `std` feature-gated `std::error::Error` implementations to `core::error::Error` 12 | - Increased MSRV to 1.81 due to `core::error::Error` 13 | - Implemented `ReadReady` for `&[u8]` and `WriteReady` for `&mut [u8]` 14 | 15 | ## 0.6.1 - 2023-10-22 16 | 17 | - Make `SliceWriteError` publicly available. 18 | 19 | ## 0.6.0 - 2023-10-02 20 | 21 | - Prohibit `Write::write` implementations returning `Ok(0)` unless there is no 22 | data to write; consequently remove `WriteAllError` and the `WriteAllError` 23 | variant of `WriteFmtError`. Update the `&mut [u8]` impl to possibly return 24 | a new `SliceWriteError` if the slice is full instead of `Ok(0)`. 25 | - Add `WriteZero` variant to `ErrorKind` for implementations that previously 26 | may have returned `Ok(0)` to indicate no further data could be written. 27 | - `Write::write_all` now panics if the `write()` implementation returns `Ok(0)`. 28 | 29 | ## 0.5.0 - 2023-08-06 30 | 31 | - 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. 32 | - Add variants to `ErrorKind` mirroring `std::io::ErrorKind`. 33 | - Add `From` impls to convert between `ErrorKind` and `std::io::ErrorKind`. 34 | - Moved `embedded_io::blocking` to the crate root. 35 | - Split async traits to the `embedded-io-async` crate. 36 | - Split trait adapters to the `embedded-io-adapters` crate. 37 | - Add `std::error` impls for `ReadExactError` & `WriteAllError`. 38 | - Rename trait `Io` to `ErrorType`, for consistency with `embedded-hal`. 39 | - Added optional `defmt` 0.3 support. 40 | 41 | ## 0.4.0 - 2022-11-25 42 | 43 | - 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. 44 | 45 | ## 0.3.1 - 2022-10-26 46 | 47 | - Fix compilation on recent nightlies (#5) 48 | 49 | ## 0.3.0 - 2022-05-19 50 | 51 | - `FromFutures` adapter now requires `futures` Cargo feature. (breaking change) 52 | - Add `FromTokio` adapter. 53 | - Add blanket impls for `&mut T`, `Box`. 54 | - Add impl `Read`, `BufRead` for `&[u8]` 55 | - Add impl `Write` for `&mut [u8]` 56 | - Add impl `Write` for `Vec` 57 | - impl `std::error::Error` for `ReadExactError`, `WriteFmtError`. 58 | 59 | ## 0.2.0 - 2022-05-07 60 | 61 | - First release 62 | -------------------------------------------------------------------------------- /embedded-io/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "embedded-io" 3 | version = "0.6.1" 4 | edition = "2021" 5 | rust-version = "1.81" 6 | description = "Embedded IO traits" 7 | repository = "https://github.com/rust-embedded/embedded-hal" 8 | readme = "README.md" 9 | license = "MIT OR Apache-2.0" 10 | categories = [ 11 | "embedded", 12 | "no-std", 13 | ] 14 | 15 | [features] 16 | std = ["alloc"] 17 | alloc = [] 18 | defmt-03 = ["dep:defmt-03"] 19 | 20 | [dependencies] 21 | defmt-03 = { package = "defmt", version = "0.3", optional = true } 22 | 23 | [package.metadata.docs.rs] 24 | features = ["std"] 25 | rustdoc-args = ["--cfg", "docsrs"] 26 | -------------------------------------------------------------------------------- /embedded-io/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) 2023 The embedded-io authors 2 | 3 | Permission is hereby granted, free of charge, to any 4 | person obtaining a copy of this software and associated 5 | documentation files (the "Software"), to deal in the 6 | Software without restriction, including without 7 | limitation the rights to use, copy, modify, merge, 8 | publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software 10 | is furnished to do so, subject to the following 11 | conditions: 12 | 13 | The above copyright notice and this permission notice 14 | shall be included in all copies or substantial portions 15 | of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 18 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 19 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 20 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 21 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 22 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 24 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 25 | DEALINGS IN THE SOFTWARE. 26 | -------------------------------------------------------------------------------- /embedded-io/README.md: -------------------------------------------------------------------------------- 1 | [![crates.io](https://img.shields.io/crates/d/embedded-io.svg)](https://crates.io/crates/embedded-io) 2 | [![crates.io](https://img.shields.io/crates/v/embedded-io.svg)](https://crates.io/crates/embedded-io) 3 | [![Documentation](https://docs.rs/embedded-io/badge.svg)](https://docs.rs/embedded-io) 4 | 5 | # `embedded-io` 6 | 7 | This project is developed and maintained by the [HAL team](https://github.com/rust-embedded/wg#the-hal-team). 8 | 9 | Input/Output traits for embedded systems. 10 | 11 | Rust's `std::io` traits are not available in `no_std` targets, mainly because `std::io::Error` 12 | requires allocation. This crate contains replacement equivalent traits, usable in `no_std` 13 | targets. 14 | 15 | ## Differences with `std::io` 16 | 17 | - `Error` is an associated type. This allows each implementor to return its own error type, 18 | while avoiding `dyn` or `Box`. This is consistent with how errors are handled in [`embedded-hal`](https://github.com/rust-embedded/embedded-hal/). 19 | - 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 20 | 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. 21 | 22 | ## Optional Cargo features 23 | 24 | - **`std`**: Adds `From` impls to convert to/from `std::io` structs. 25 | - **`alloc`**: Adds blanket impls for `Box`, adds `Write` impl to `Vec`. 26 | - **`defmt-03`**: Derive `defmt::Format` from `defmt` 0.3 for enums and structs. 27 | 28 | ## Minimum Supported Rust Version (MSRV) 29 | 30 | This crate is guaranteed to compile on stable Rust 1.60 and up. It *might* 31 | compile with older versions but that may change in any new patch release. 32 | 33 | See [here](../docs/msrv.md) for details on how the MSRV may be upgraded. 34 | 35 | ## License 36 | 37 | Licensed under either of 38 | 39 | - Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or 40 | ) 41 | - MIT license ([LICENSE-MIT](LICENSE-MIT) or ) 42 | 43 | at your option. 44 | 45 | ### Contribution 46 | 47 | Unless you explicitly state otherwise, any contribution intentionally submitted 48 | for inclusion in the work by you, as defined in the Apache-2.0 license, shall be 49 | dual licensed as above, without any additional terms or conditions. 50 | -------------------------------------------------------------------------------- /embedded-io/src/impls/boxx.rs: -------------------------------------------------------------------------------- 1 | use crate::{BufRead, ErrorType, Read, ReadReady, Seek, Write, WriteReady}; 2 | use alloc::boxed::Box; 3 | 4 | #[cfg_attr(docsrs, doc(cfg(any(feature = "std", feature = "alloc"))))] 5 | impl ErrorType for Box { 6 | type Error = T::Error; 7 | } 8 | 9 | #[cfg_attr(docsrs, doc(cfg(any(feature = "std", feature = "alloc"))))] 10 | impl Read for Box { 11 | #[inline] 12 | fn read(&mut self, buf: &mut [u8]) -> Result { 13 | T::read(self, buf) 14 | } 15 | } 16 | 17 | #[cfg_attr(docsrs, doc(cfg(any(feature = "std", feature = "alloc"))))] 18 | impl BufRead for Box { 19 | fn fill_buf(&mut self) -> Result<&[u8], Self::Error> { 20 | T::fill_buf(self) 21 | } 22 | 23 | fn consume(&mut self, amt: usize) { 24 | T::consume(self, amt); 25 | } 26 | } 27 | 28 | #[cfg_attr(docsrs, doc(cfg(any(feature = "std", feature = "alloc"))))] 29 | impl Write for Box { 30 | #[inline] 31 | fn write(&mut self, buf: &[u8]) -> Result { 32 | T::write(self, buf) 33 | } 34 | 35 | #[inline] 36 | fn flush(&mut self) -> Result<(), Self::Error> { 37 | T::flush(self) 38 | } 39 | } 40 | 41 | #[cfg_attr(docsrs, doc(cfg(any(feature = "std", feature = "alloc"))))] 42 | impl Seek for Box { 43 | #[inline] 44 | fn seek(&mut self, pos: crate::SeekFrom) -> Result { 45 | T::seek(self, pos) 46 | } 47 | } 48 | 49 | #[cfg_attr(docsrs, doc(cfg(any(feature = "std", feature = "alloc"))))] 50 | impl ReadReady for Box { 51 | #[inline] 52 | fn read_ready(&mut self) -> Result { 53 | T::read_ready(self) 54 | } 55 | } 56 | 57 | #[cfg_attr(docsrs, doc(cfg(any(feature = "std", feature = "alloc"))))] 58 | impl WriteReady for Box { 59 | #[inline] 60 | fn write_ready(&mut self) -> Result { 61 | T::write_ready(self) 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /embedded-io/src/impls/mod.rs: -------------------------------------------------------------------------------- 1 | mod slice_mut; 2 | mod slice_ref; 3 | 4 | #[cfg(feature = "alloc")] 5 | mod boxx; 6 | #[cfg(feature = "alloc")] 7 | mod vec; 8 | -------------------------------------------------------------------------------- /embedded-io/src/impls/slice_mut.rs: -------------------------------------------------------------------------------- 1 | use crate::{Error, ErrorKind, ErrorType, SliceWriteError, Write, WriteReady}; 2 | use core::mem; 3 | 4 | impl Error for SliceWriteError { 5 | fn kind(&self) -> ErrorKind { 6 | match self { 7 | SliceWriteError::Full => ErrorKind::WriteZero, 8 | } 9 | } 10 | } 11 | 12 | impl ErrorType for &mut [u8] { 13 | type Error = SliceWriteError; 14 | } 15 | 16 | impl core::fmt::Display for SliceWriteError { 17 | fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { 18 | write!(f, "{self:?}") 19 | } 20 | } 21 | 22 | impl core::error::Error for SliceWriteError {} 23 | 24 | /// Write is implemented for `&mut [u8]` by copying into the slice, overwriting 25 | /// its data. 26 | /// 27 | /// Note that writing updates the slice to point to the yet unwritten part. 28 | /// The slice will be empty when it has been completely overwritten. 29 | /// 30 | /// If the number of bytes to be written exceeds the size of the slice, write operations will 31 | /// return short writes: ultimately, a `SliceWriteError::Full`. 32 | impl Write for &mut [u8] { 33 | #[inline] 34 | fn write(&mut self, buf: &[u8]) -> Result { 35 | let amt = core::cmp::min(buf.len(), self.len()); 36 | if !buf.is_empty() && amt == 0 { 37 | return Err(SliceWriteError::Full); 38 | } 39 | let (a, b) = mem::take(self).split_at_mut(amt); 40 | a.copy_from_slice(&buf[..amt]); 41 | *self = b; 42 | Ok(amt) 43 | } 44 | 45 | #[inline] 46 | fn flush(&mut self) -> Result<(), Self::Error> { 47 | Ok(()) 48 | } 49 | } 50 | 51 | impl WriteReady for &mut [u8] { 52 | #[inline] 53 | fn write_ready(&mut self) -> Result { 54 | Ok(true) 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /embedded-io/src/impls/slice_ref.rs: -------------------------------------------------------------------------------- 1 | use crate::{BufRead, ErrorType, Read, ReadReady}; 2 | 3 | impl ErrorType for &[u8] { 4 | type Error = core::convert::Infallible; 5 | } 6 | 7 | /// Read is implemented for `&[u8]` by copying from the slice. 8 | /// 9 | /// Note that reading updates the slice to point to the yet unread part. 10 | /// The slice will be empty when EOF is reached. 11 | impl Read for &[u8] { 12 | #[inline] 13 | fn read(&mut self, buf: &mut [u8]) -> Result { 14 | let amt = core::cmp::min(buf.len(), self.len()); 15 | let (a, b) = self.split_at(amt); 16 | 17 | // First check if the amount of bytes we want to read is small: 18 | // `copy_from_slice` will generally expand to a call to `memcpy`, and 19 | // for a single byte the overhead is significant. 20 | if amt == 1 { 21 | buf[0] = a[0]; 22 | } else { 23 | buf[..amt].copy_from_slice(a); 24 | } 25 | 26 | *self = b; 27 | Ok(amt) 28 | } 29 | } 30 | 31 | impl BufRead for &[u8] { 32 | #[inline] 33 | fn fill_buf(&mut self) -> Result<&[u8], Self::Error> { 34 | Ok(*self) 35 | } 36 | 37 | #[inline] 38 | fn consume(&mut self, amt: usize) { 39 | *self = &self[amt..]; 40 | } 41 | } 42 | 43 | impl ReadReady for &[u8] { 44 | #[inline] 45 | fn read_ready(&mut self) -> Result { 46 | Ok(true) 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /embedded-io/src/impls/vec.rs: -------------------------------------------------------------------------------- 1 | use crate::{ErrorType, Write}; 2 | use alloc::vec::Vec; 3 | 4 | #[cfg_attr(docsrs, doc(cfg(any(feature = "std", feature = "alloc"))))] 5 | impl ErrorType for Vec { 6 | type Error = core::convert::Infallible; 7 | } 8 | 9 | #[cfg_attr(docsrs, doc(cfg(any(feature = "std", feature = "alloc"))))] 10 | impl Write for Vec { 11 | #[inline] 12 | fn write(&mut self, buf: &[u8]) -> Result { 13 | self.extend_from_slice(buf); 14 | Ok(buf.len()) 15 | } 16 | 17 | #[inline] 18 | fn flush(&mut self) -> Result<(), Self::Error> { 19 | Ok(()) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /triagebot.toml: -------------------------------------------------------------------------------- 1 | [assign] 2 | --------------------------------------------------------------------------------