feat: add display UI with splash screen, dashboard, and RSSI sparkline
Implement full SSD1306 OLED display support: - Splash screen on boot (1.5s) with project name, version, board - Dashboard showing radio state, config, counters, RSSI/SNR - RSSI history sparkline (64-sample ring buffer, 2px-wide bars) - DisplayOn/DisplayOff host commands routed via dedicated channel - Pure render functions against generic DrawTarget (no hardware coupling) - Display on/off control bypasses radio task entirely Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
17624 files changed
+4564928
-38
No files matched your search
@@ -0,0 +1 @@
|
||||
{"files":{".cargo_vcs_info.json":"5c0733d8dec352278c56237ef6d04883583fd2d4f7f00239a7bff2eb55f402e1",".github/CODEOWNERS":"98381d61605d07c59738b89a670eef5c979388b42432d207ecbd5bc3f896cad0",".github/bors.toml":"efa55f9ff852dcb9c6dd02d896f73fee592a25edda32da88b100c53ac0533257",".github/workflows/ci.yml":"bf68ec49ebd107755acd1b3dbdfe9d85157c525dfe76ecc6a0e530bc8b68ed36",".github/workflows/clippy.yml":"8a15989ed6db6d1f415c50827e61178b5ff1f53d6edadf9553fdccb99f123939",".github/workflows/rustfmt.yml":"b10c1932f701d813ec1f46de9e6f209f93e1f59fa3863c16b72511196b12eb63",".github/workflows/test.yml":"72a22242a9da32e180f5ea4bcaa3abe9bf913949e387ecb49e14500e91cebf2d","CHANGELOG.md":"77ffedb0fed8fc0ca01187ff0e49ac5789de197cf9395873bcf8fe7dad4a07af","CODE_OF_CONDUCT.md":"8e25e95078b1a582086587adf8e1d907d43aacee6a072b8630d54a6289e5e0b9","Cargo.toml":"f38086e8578b2292f3c519060d723d6142e59930d72d12cdb1bbc5aaed09bf3c","Cargo.toml.orig":"6ee220243f97161486f86eb301bbb714364c822d5983fd308e318b03c9c285e6","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"520bc4942f199aad3dfe939c475d67ffbddfea482e41fc4fcad8829b71affc99","README.md":"629413fa55785448460269d379312aed6618869606a1c761d86aad32eb76c2cc","src/adc.rs":"bfa0ae49cef222ec2b04178ce1d6dc8db4fbc8acebf1a82657de4b1447ecf313","src/blocking/can.rs":"4db197e3f8d77967a7bac0beca9605063a1c6abbc5cc03d9b74fc09fbf36ba42","src/blocking/delay.rs":"a483acfe72b033f8e18273bf744c237a8de803cbbaf820aeb2ce32458ac3b684","src/blocking/i2c.rs":"966589f5f9d3102bb1e88150a503967b51cd81dda7527c803c03a10e4aa2ffa8","src/blocking/mod.rs":"02bbd31f9e68bb7fa10977c6934b9ae046138fbd6f5ebe6c5dfac6fa3332a63a","src/blocking/rng.rs":"eed57d8770235cc2454452fd5b86948a2143bdb5220b0e14fc36003f4223b7c9","src/blocking/serial.rs":"1587824b545e6e781efafc8720f1a3c19f0e9a7cab3ad0cd96cc99f5e84f8667","src/blocking/spi.rs":"ddb233a3bcf8a20b8da0d95c0cb2bdcbefd729a93b8a568a879680b026e00e19","src/can/id.rs":"691582de769af2c893cdceab783ad0b28b984de7466c419cbe17efd9f60f2e51","src/can/mod.rs":"b82b04a537415180918c2de36af9f5d7e8ecc76091bec5fc6f7208a14bfaae89","src/can/nb.rs":"6c4325d38b6aebbaa7293286482ab9645337b8236e054bed8835456f2aa32019","src/digital/mod.rs":"08f5144d2bcf8d4cdde1995b3617f5da14309926f66f4e9568100d7794da1b91","src/digital/v1.rs":"afd83c7e86c4957adf682e2280f7ac14a94dc8cdd43d70cc8890c2d8cf06be51","src/digital/v1_compat.rs":"192ec293c4e7f6bfbf9f65f3b3b1e2b47190761cc9e2e5ea230e0d5e7d69d4f3","src/digital/v2.rs":"89fa6a89cdbbb68f498fb7a2d87842b5b4391a978c7205ff1a0479983223c9f9","src/digital/v2_compat.rs":"bae546f4583eeedafa3e5c7e93f8fa5a56716e05a0ff3f5cc5aaf4a461e17adf","src/fmt.rs":"e0e657a52545f5099f2f0d49625b1448d1d566ece2c5badd8611e40f4afd7aba","src/lib.rs":"f9abd6a03322386af8099bc790bf247ab246351e965d6fab9650f433ec2f6584","src/prelude.rs":"d72d971fa63105e0ef6a0278fdecd4eb97f7fc47520455575c3165ef33ddb29d","src/serial.rs":"73f2e98712e12cc06ffd47d4c797ef8709ff56ebbad0db2e726aea1bf2ae383b","src/spi.rs":"f925b243fd86d7768a63605e8d3f035174be25df002c4abeedf4cd4c00b91594","src/timer.rs":"6c8e09a566375cfa90f7dd619544e84205dcaba8a87afa61ee447d57104b78ad","src/watchdog.rs":"e507438a87ee8a2a4162f717cc0472deecc8a29385b15a2c8ecc44e602d96e9a"},"package":"35949884794ad573cf46071e41c9b60efb0cb311e3ca01f7af807af1debc66ff"}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"git": {
|
||||
"sha1": "2e91b430c5f3c2e06cb3f12a7812e17cafd2d3bc"
|
||||
},
|
||||
"path_in_vcs": ""
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
* @rust-embedded/hal
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
block_labels = ["needs-decision"]
|
||||
delete_merged_branches = true
|
||||
required_approvals = 1
|
||||
status = [
|
||||
"ci-linux (stable, x86_64-unknown-linux-gnu)",
|
||||
"ci-linux (stable, thumbv6m-none-eabi)",
|
||||
"ci-linux (stable, thumbv7m-none-eabi)",
|
||||
"ci-linux (1.31.0, x86_64-unknown-linux-gnu)",
|
||||
"fmt",
|
||||
]
|
||||
@@ -0,0 +1,48 @@
|
||||
on:
|
||||
push:
|
||||
branches: [ staging, trying, master ]
|
||||
pull_request:
|
||||
|
||||
name: Continuous integration
|
||||
|
||||
env:
|
||||
RUSTFLAGS: '--deny warnings'
|
||||
|
||||
jobs:
|
||||
ci-linux:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
# All generated code should be running on stable now
|
||||
rust: [stable]
|
||||
|
||||
# The default target we're compiling on and for
|
||||
TARGET: [x86_64-unknown-linux-gnu, thumbv6m-none-eabi, thumbv7m-none-eabi]
|
||||
|
||||
include:
|
||||
- rust: 1.31.0
|
||||
TARGET: x86_64-unknown-linux-gnu
|
||||
|
||||
# Test nightly but don't fail
|
||||
- rust: nightly
|
||||
experimental: true
|
||||
TARGET: x86_64-unknown-linux-gnu
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
profile: minimal
|
||||
toolchain: ${{ matrix.rust }}
|
||||
target: ${{ matrix.TARGET }}
|
||||
override: true
|
||||
- uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: check
|
||||
args: --target=${{ matrix.TARGET }}
|
||||
|
||||
- uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: check
|
||||
args: --target=${{ matrix.TARGET }} --features unproven
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
on:
|
||||
push:
|
||||
branches: [ staging, trying, master ]
|
||||
pull_request:
|
||||
|
||||
name: Clippy check
|
||||
jobs:
|
||||
clippy_check:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
profile: minimal
|
||||
toolchain: stable
|
||||
override: true
|
||||
components: clippy
|
||||
- uses: actions-rs/clippy-check@v1
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
@@ -0,0 +1,22 @@
|
||||
on:
|
||||
push:
|
||||
branches: [ staging, trying, master ]
|
||||
pull_request:
|
||||
|
||||
name: Code formatting check
|
||||
|
||||
jobs:
|
||||
fmt:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
profile: minimal
|
||||
toolchain: stable
|
||||
override: true
|
||||
components: rustfmt
|
||||
- uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: fmt
|
||||
args: --all -- --check
|
||||
@@ -0,0 +1,29 @@
|
||||
on:
|
||||
push:
|
||||
branches: [ staging, trying, master ]
|
||||
pull_request:
|
||||
|
||||
name: Test Suite
|
||||
|
||||
env:
|
||||
RUSTFLAGS: '--deny warnings'
|
||||
|
||||
jobs:
|
||||
ci-linux:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
rust: [nightly]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
profile: minimal
|
||||
toolchain: ${{ matrix.rust }}
|
||||
target: ${{ matrix.TARGET }}
|
||||
override: true
|
||||
- uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: test
|
||||
args: --features unproven
|
||||
+143
@@ -0,0 +1,143 @@
|
||||
# Change Log
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
The format is based on [Keep a Changelog](http://keepachangelog.com/)
|
||||
and this project adheres to [Semantic Versioning](http://semver.org/).
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
## [v0.2.7] - 2022-02-09
|
||||
|
||||
### Added
|
||||
|
||||
- Backport CAN interface from the upcoming 1.0 release.
|
||||
|
||||
## [v0.2.6] - 2021-08-03
|
||||
|
||||
### Added
|
||||
|
||||
Backported non-breaking changes from the upcoming 1.0 release:
|
||||
|
||||
- `Transactional` SPI interface for executing groups of SPI transactions.
|
||||
- `Transactional` I2C interface for executing groups of I2C transactions.
|
||||
- 10-bit addressing mode for I2C traits.
|
||||
- `set_state` method for `OutputPin` using an input `PinState` value.
|
||||
- `IoPin` trait for pins that can change between being inputs or outputs
|
||||
dynamically.
|
||||
|
||||
|
||||
## [v0.2.5] - 2021-04-28
|
||||
|
||||
### Changed
|
||||
|
||||
- Updated `nb` dependency to version `0.1.3` to ensure compatibility with `nb` version `1.0`.
|
||||
|
||||
|
||||
## [v0.2.4] - 2020-06-17
|
||||
|
||||
### Changed
|
||||
|
||||
- Fix for `dyn` traits in fmt.rs
|
||||
- Remove `#![deny(warnings)]`, now imposed y CI
|
||||
- Updates stm32f30x from 0.6.0 to 0.8.0
|
||||
- Fix the input pin v2->v1 compatibility shim constructor, where `OldInputPin::new`
|
||||
was incorrectly implemented for `v1::OutputPin` values.
|
||||
|
||||
|
||||
## [v0.2.3] - 2019-05-09
|
||||
|
||||
### Added
|
||||
- A new version of the digital `OutputPin`, `StatefulOutputPin`, `ToggleableOutputPin`
|
||||
and `InputPin` traits has been added under `digital::v2`. These traits are now
|
||||
fallible and their methods now return a `Result` type as setting an output pin
|
||||
and reading an input pin could potentially fail.
|
||||
See [here](https://github.com/rust-embedded/embedded-hal/issues/95) for more info.
|
||||
- Compatibility shims between `digital::v1` and `digital::v2` traits allowing v1 traits
|
||||
to be implicitly promoted to v2, and for v2 traits to be explicitly cast to v1 wrappers.
|
||||
|
||||
### Changed
|
||||
- The current versions of the `OutputPin`, `StatefulOutputPin`, `ToggleableOutputPin`
|
||||
and `InputPin` traits have been marked as deprecated. Please use the new versions
|
||||
included in `digital::v2`.
|
||||
See [here](https://github.com/rust-embedded/embedded-hal/issues/95) for more info.
|
||||
|
||||
|
||||
## [v0.2.2] - 2018-11-03
|
||||
|
||||
### Added
|
||||
|
||||
- Added the Rust Code of Conduct to this repository
|
||||
- The first ADC-related trait. This is a simple trait for one-shot conversions.
|
||||
- Iterator-based blocking write and write+read traits have been added to I2C and SPI.
|
||||
- New helper constants for SPI modes.
|
||||
- A new trait for a cancellable countdown.
|
||||
- New traits for watchdog timer management, including startup, feeding, and stopping.
|
||||
|
||||
### Changed
|
||||
- Updated docs to clarify I2C address bit widths and expectations.
|
||||
|
||||
|
||||
## [v0.2.1] - 2018-05-14
|
||||
|
||||
### Changed
|
||||
|
||||
- Auto-generated documentation (docs.rs) now includes the unproven traits.
|
||||
|
||||
## [v0.2.0] - 2018-05-12
|
||||
|
||||
### Added
|
||||
|
||||
- A `ToggeableOutputPin` trait has been added. This trait contains a single method: `toggle` that
|
||||
can be used to toggle the state of a push-pull pin.
|
||||
|
||||
### Changed
|
||||
|
||||
- [breaking-change] The signature of `CountDown.wait` changed; it now returns `nb::Result<(),
|
||||
Void>`. Where [`Void`] is the stable alternative to the never type, `!`, provided by the stable
|
||||
[`void`] crate. Implementations of the `CountDown` trait will have to be updated to use the new
|
||||
signature. With this change this crate compiles on the stable and beta channels.
|
||||
|
||||
[`Void`]: https://docs.rs/void/1.0.2/void/enum.Void.html
|
||||
[`void`]: https://crates.io/crates/void
|
||||
|
||||
- [breaking-change] the `OutputPin.is_{low,high}` methods have been moved into its own trait
|
||||
`StatefulOutputPin` and renamed to `is_set_{low,high}`.
|
||||
|
||||
- It has been clarified in the documentation that `OutputPin` must be implemented for push-pull
|
||||
output pins (and e.g. not for open drain output pins).
|
||||
|
||||
## [v0.1.3] - 2018-05-14
|
||||
|
||||
### Changed
|
||||
|
||||
- Re-export most / unchanged traits from embedded-hal v0.2.x to allow inter-operation between HAL
|
||||
implementations and drivers that are using different minor versions.
|
||||
|
||||
## [v0.1.2] - 2018-02-14
|
||||
|
||||
### Added
|
||||
|
||||
- Unproven `blocking::serial::*` traits
|
||||
|
||||
## [v0.1.1] - 2018-02-06
|
||||
|
||||
### Added
|
||||
|
||||
- Unproven `digital::InputPin` trait
|
||||
|
||||
## v0.1.0 - 2018-01-16
|
||||
|
||||
Initial release
|
||||
|
||||
[Unreleased]: https://github.com/rust-embedded/embedded-hal/compare/v0.2.7...v0.2.x
|
||||
[v0.2.7]: https://github.com/rust-embedded/embedded-hal/compare/v0.2.6...v0.2.7
|
||||
[v0.2.6]: https://github.com/rust-embedded/embedded-hal/compare/v0.2.5...v0.2.6
|
||||
[v0.2.5]: https://github.com/rust-embedded/embedded-hal/compare/v0.2.4...v0.2.5
|
||||
[v0.2.4]: https://github.com/rust-embedded/embedded-hal/compare/v0.2.3...v0.2.4
|
||||
[v0.2.3]: https://github.com/rust-embedded/embedded-hal/compare/v0.2.2...v0.2.3
|
||||
[v0.2.2]: https://github.com/rust-embedded/embedded-hal/compare/v0.2.1...v0.2.2
|
||||
[v0.2.1]: https://github.com/rust-embedded/embedded-hal/compare/v0.2.0...v0.2.1
|
||||
[v0.2.0]: https://github.com/rust-embedded/embedded-hal/compare/v0.1.2...v0.2.0
|
||||
[v0.1.2]: https://github.com/rust-embedded/embedded-hal/compare/v0.1.1...v0.1.2
|
||||
[v0.1.1]: https://github.com/rust-embedded/embedded-hal/compare/v0.1.0...v0.1.1
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
# The Rust Code of Conduct
|
||||
|
||||
## Conduct
|
||||
|
||||
**Contact**: [HAL team][team]
|
||||
|
||||
* 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.
|
||||
* On IRC, please avoid using overtly sexual nicknames or other nicknames that might detract from a friendly, safe and welcoming environment for all.
|
||||
* Please be kind and courteous. There's no need to be mean or rude.
|
||||
* 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.
|
||||
* 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.
|
||||
* 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.
|
||||
* 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.
|
||||
* Likewise any spamming, trolling, flaming, baiting or other attention-stealing behavior is not welcome.
|
||||
|
||||
## Moderation
|
||||
|
||||
These are the policies for upholding our community's standards of conduct.
|
||||
|
||||
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.)
|
||||
2. Remarks that moderators find inappropriate, whether listed in the code of conduct or not, are also not allowed.
|
||||
3. Moderators will first respond to such remarks with a warning.
|
||||
4. If the warning is unheeded, the user will be "kicked," i.e., kicked out of the communication channel to cool off.
|
||||
5. If the user comes back and continues to make trouble, they will be banned, i.e., indefinitely excluded.
|
||||
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.
|
||||
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.
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
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).
|
||||
|
||||
*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/).*
|
||||
|
||||
[team]: https://github.com/rust-embedded/wg#the-hal-team
|
||||
Vendored
+38
@@ -0,0 +1,38 @@
|
||||
# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
|
||||
#
|
||||
# When uploading crates to the registry Cargo will automatically
|
||||
# "normalize" Cargo.toml files for maximal compatibility
|
||||
# with all versions of Cargo and also rewrite `path` dependencies
|
||||
# to registry (e.g., crates.io) dependencies.
|
||||
#
|
||||
# If you are reading this file be aware that the original Cargo.toml
|
||||
# will likely look very different (and much more reasonable).
|
||||
# See Cargo.toml.orig for the original contents.
|
||||
|
||||
[package]
|
||||
name = "embedded-hal"
|
||||
version = "0.2.7"
|
||||
authors = ["The Embedded HAL Team <embedded-hal@teams.rust-embedded.org>", "Jorge Aparicio <jorge@japaric.io>", "Jonathan 'theJPster' Pallant <github@thejpster.org.uk>"]
|
||||
description = " A Hardware Abstraction Layer (HAL) for embedded systems "
|
||||
documentation = "https://docs.rs/embedded-hal"
|
||||
readme = "README.md"
|
||||
keywords = ["hal", "IO"]
|
||||
categories = ["asynchronous", "embedded", "hardware-support", "no-std"]
|
||||
license = "MIT OR Apache-2.0"
|
||||
repository = "https://github.com/rust-embedded/embedded-hal"
|
||||
[package.metadata.docs.rs]
|
||||
features = ["unproven"]
|
||||
[dependencies.nb]
|
||||
version = "0.1.3"
|
||||
|
||||
[dependencies.void]
|
||||
version = "1.0.2"
|
||||
default-features = false
|
||||
[dev-dependencies.futures]
|
||||
version = "0.1.17"
|
||||
|
||||
[dev-dependencies.stm32f30x]
|
||||
version = "0.8.0"
|
||||
|
||||
[features]
|
||||
unproven = ["nb/unstable"]
|
||||
+201
@@ -0,0 +1,201 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
Copyright (c) 2017-2018 Jorge Aparicio
|
||||
|
||||
Permission is hereby granted, free of charge, to any
|
||||
person obtaining a copy of this software and associated
|
||||
documentation files (the "Software"), to deal in the
|
||||
Software without restriction, including without
|
||||
limitation the rights to use, copy, modify, merge,
|
||||
publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software
|
||||
is furnished to do so, subject to the following
|
||||
conditions:
|
||||
|
||||
The above copyright notice and this permission notice
|
||||
shall be included in all copies or substantial portions
|
||||
of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
|
||||
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
|
||||
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
||||
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
|
||||
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
|
||||
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
Vendored
+86
@@ -0,0 +1,86 @@
|
||||
# `embedded-hal`
|
||||
|
||||
> A Hardware Abstraction Layer (HAL) for embedded systems
|
||||
|
||||
This project is developed and maintained by the [HAL team][team].
|
||||
|
||||
## [API reference]
|
||||
|
||||
[API reference]: https://docs.rs/embedded-hal
|
||||
|
||||
## How-to: add a new trait
|
||||
|
||||
This is the suggested approach to adding a new trait to `embedded-hal`
|
||||
|
||||
### Discussion
|
||||
|
||||
Ideally, before proposing a new trait, or set of traits, you should create an issue where the use
|
||||
cases and requirements of the trait(s) are discussed.
|
||||
|
||||
These issues will be labeled as `discussion`s in the issue tracker.
|
||||
|
||||
### Proposing a trait
|
||||
|
||||
Once there's consensus on the requirements of the trait(s) a new issue, or a PR, with a proposal
|
||||
should be opened. The proposal should include the actual trait definitions as well as a link to the
|
||||
issue with previous discussion, if there was one.
|
||||
|
||||
If the proposal includes more than one alternative then there should be further discussion to try to
|
||||
single out the best alternative.
|
||||
|
||||
These issues / PRs will be labeled as `proposal`s in the issue tracker.
|
||||
|
||||
### Testing period
|
||||
|
||||
If there are no objections to the proposal the new trait(s) will land behind the "unproven" Cargo
|
||||
feature and an issue about the new trait(s) will be created. If the proposal includes several
|
||||
alternatives and a single one couldn't be chosen as the best then each alternative will land behind
|
||||
a different Cargo feature, e.g. "alt1" or "alt2".
|
||||
|
||||
The traits will undergo a testing period before they move into the set of proven traits. During
|
||||
this period users are encouraged to try to implement the unproven traits for their platforms and to
|
||||
build drivers on top of them. Problems implementing the trait(s) as well as successful
|
||||
implementations should be reported on the corresponding issue.
|
||||
|
||||
To leave the unproven state at least *two* implementations of the trait(s) for different platforms
|
||||
(ideally, the two platforms should be from different vendors) and *one* generic driver built on top
|
||||
of the trait(s), or alternatively one demo program that exercises the trait (via generic function /
|
||||
trait object), *should* be demonstrated. If, instead, reports indicate that the proposed trait(s)
|
||||
can't be implemented for a certain platform then the trait(s) will be removed and we'll go back to
|
||||
the drawing board.
|
||||
|
||||
Issues used to track unproven APIs will be labeled as `unproven-api`s in the issue tracker and they
|
||||
may also include the labels `needs-impl` and `needs-driver` to signal what's required for them to
|
||||
move to the set of proven traits.
|
||||
|
||||
## Implementations and drivers
|
||||
|
||||
For a list of `embedded-hal` implementations and driver crates check the [awesome-embedded-rust]
|
||||
list.
|
||||
|
||||
[awesome-embedded-rust]: https://github.com/rust-embedded/awesome-embedded-rust#driver-crates
|
||||
|
||||
## License
|
||||
|
||||
Licensed under either of
|
||||
|
||||
- Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or
|
||||
http://www.apache.org/licenses/LICENSE-2.0)
|
||||
- MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
|
||||
|
||||
at your option.
|
||||
|
||||
### Contribution
|
||||
|
||||
Unless you explicitly state otherwise, any contribution intentionally submitted
|
||||
for inclusion in the work by you, as defined in the Apache-2.0 license, shall be
|
||||
dual licensed as above, without any additional terms or conditions.
|
||||
|
||||
## Code of Conduct
|
||||
|
||||
Contribution to this crate is organized under the terms of the [Rust Code of
|
||||
Conduct][CoC], the maintainer of this crate, the [HAL team][team], promises
|
||||
to intervene to uphold that code of conduct.
|
||||
|
||||
[CoC]: CODE_OF_CONDUCT.md
|
||||
[team]: https://github.com/rust-embedded/wg#the-hal-team
|
||||
Vendored
+98
@@ -0,0 +1,98 @@
|
||||
//! Analog-digital conversion traits
|
||||
|
||||
#[cfg(feature = "unproven")]
|
||||
use nb;
|
||||
|
||||
/// A marker trait to identify MCU pins that can be used as inputs to an ADC channel.
|
||||
///
|
||||
/// This marker trait denotes an object, i.e. a GPIO pin, that is ready for use as an input to the
|
||||
/// ADC. As ADCs channels can be supplied by multiple pins, this trait defines the relationship
|
||||
/// between the physical interface and the ADC sampling buffer.
|
||||
///
|
||||
/// ```
|
||||
/// # use std::marker::PhantomData;
|
||||
/// # use embedded_hal::adc::Channel;
|
||||
///
|
||||
/// struct Adc1; // Example ADC with single bank of 8 channels
|
||||
/// struct Gpio1Pin1<MODE>(PhantomData<MODE>);
|
||||
/// struct Analog(()); // marker type to denote a pin in "analog" mode
|
||||
///
|
||||
/// // GPIO 1 pin 1 can supply an ADC channel when it is configured in Analog mode
|
||||
/// impl Channel<Adc1> for Gpio1Pin1<Analog> {
|
||||
/// type ID = u8; // ADC channels are identified numerically
|
||||
///
|
||||
/// fn channel() -> u8 { 7_u8 } // GPIO pin 1 is connected to ADC channel 7
|
||||
/// }
|
||||
///
|
||||
/// struct Adc2; // ADC with two banks of 16 channels
|
||||
/// struct Gpio2PinA<MODE>(PhantomData<MODE>);
|
||||
/// struct AltFun(()); // marker type to denote some alternate function mode for the pin
|
||||
///
|
||||
/// // GPIO 2 pin A can supply an ADC channel when it's configured in some alternate function mode
|
||||
/// impl Channel<Adc2> for Gpio2PinA<AltFun> {
|
||||
/// type ID = (u8, u8); // ADC channels are identified by bank number and channel number
|
||||
///
|
||||
/// fn channel() -> (u8, u8) { (0, 3) } // bank 0 channel 3
|
||||
/// }
|
||||
/// ```
|
||||
#[cfg(feature = "unproven")]
|
||||
pub trait Channel<ADC> {
|
||||
/// Channel ID type
|
||||
///
|
||||
/// A type used to identify this ADC channel. For example, if the ADC has eight channels, this
|
||||
/// might be a `u8`. If the ADC has multiple banks of channels, it could be a tuple, like
|
||||
/// `(u8: bank_id, u8: channel_id)`.
|
||||
type ID;
|
||||
|
||||
/// Get the specific ID that identifies this channel, for example `0_u8` for the first ADC
|
||||
/// channel, if Self::ID is u8.
|
||||
fn channel() -> Self::ID;
|
||||
|
||||
// `channel` is a function due to [this reported
|
||||
// issue](https://github.com/rust-lang/rust/issues/54973). Something about blanket impls
|
||||
// combined with `type ID; const CHANNEL: Self::ID;` causes problems.
|
||||
//const CHANNEL: Self::ID;
|
||||
}
|
||||
|
||||
/// ADCs that sample on single channels per request, and do so at the time of the request.
|
||||
///
|
||||
/// This trait is the interface to an ADC that is configured to read a specific channel at the time
|
||||
/// of the request (in contrast to continuous asynchronous sampling).
|
||||
///
|
||||
/// ```
|
||||
/// use embedded_hal::adc::{Channel, OneShot};
|
||||
///
|
||||
/// struct MyAdc; // 10-bit ADC, with 5 channels
|
||||
/// # impl MyAdc {
|
||||
/// # pub fn power_up(&mut self) {}
|
||||
/// # pub fn power_down(&mut self) {}
|
||||
/// # pub fn do_conversion(&mut self, chan: u8) -> u16 { 0xAA55_u16 }
|
||||
/// # }
|
||||
///
|
||||
/// impl<WORD, PIN> OneShot<MyAdc, WORD, PIN> for MyAdc
|
||||
/// where
|
||||
/// WORD: From<u16>,
|
||||
/// PIN: Channel<MyAdc, ID=u8>,
|
||||
/// {
|
||||
/// type Error = ();
|
||||
///
|
||||
/// fn read(&mut self, _pin: &mut PIN) -> nb::Result<WORD, Self::Error> {
|
||||
/// let chan = 1 << PIN::channel();
|
||||
/// self.power_up();
|
||||
/// let result = self.do_conversion(chan);
|
||||
/// self.power_down();
|
||||
/// Ok(result.into())
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
#[cfg(feature = "unproven")]
|
||||
pub trait OneShot<ADC, Word, Pin: Channel<ADC>> {
|
||||
/// Error type returned by ADC methods
|
||||
type Error;
|
||||
|
||||
/// Request that the ADC begin a conversion on the specified pin
|
||||
///
|
||||
/// This method takes a `Pin` reference, as it is expected that the ADC will be able to sample
|
||||
/// whatever channel underlies the pin.
|
||||
fn read(&mut self, pin: &mut Pin) -> nb::Result<Word, Self::Error>;
|
||||
}
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
//! Blocking CAN API
|
||||
|
||||
/// A blocking CAN interface that is able to transmit and receive frames.
|
||||
pub trait Can {
|
||||
/// Associated frame type.
|
||||
type Frame: crate::can::Frame;
|
||||
|
||||
/// Associated error type.
|
||||
type Error: crate::can::Error;
|
||||
|
||||
/// Puts a frame in the transmit buffer. Blocks until space is available in
|
||||
/// the transmit buffer.
|
||||
fn transmit(&mut self, frame: &Self::Frame) -> Result<(), Self::Error>;
|
||||
|
||||
/// Blocks until a frame was received or an error occured.
|
||||
fn receive(&mut self) -> Result<Self::Frame, Self::Error>;
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
//! Delays
|
||||
//!
|
||||
//! # What's the difference between these traits and the `timer::CountDown` trait?
|
||||
//!
|
||||
//! The `Timer` trait provides a *non-blocking* timer abstraction and it's meant to be used to build
|
||||
//! higher level abstractions like I/O operations with timeouts. OTOH, these delays traits only
|
||||
//! provide *blocking* functionality. Note that you can also use the `timer::CountDown` trait to
|
||||
//! implement blocking delays.
|
||||
|
||||
/// Millisecond delay
|
||||
///
|
||||
/// `UXX` denotes the range type of the delay time. `UXX` can be `u8`, `u16`, etc. A single type can
|
||||
/// implement this trait for different types of `UXX`.
|
||||
pub trait DelayMs<UXX> {
|
||||
/// Pauses execution for `ms` milliseconds
|
||||
fn delay_ms(&mut self, ms: UXX);
|
||||
}
|
||||
|
||||
/// Microsecond delay
|
||||
///
|
||||
/// `UXX` denotes the range type of the delay time. `UXX` can be `u8`, `u16`, etc. A single type can
|
||||
/// implement this trait for different types of `UXX`.
|
||||
pub trait DelayUs<UXX> {
|
||||
/// Pauses execution for `us` microseconds
|
||||
fn delay_us(&mut self, us: UXX);
|
||||
}
|
||||
+297
@@ -0,0 +1,297 @@
|
||||
//! Blocking I2C API
|
||||
//!
|
||||
//! This API supports 7-bit and 10-bit addresses. Traits feature an `AddressMode`
|
||||
//! marker type parameter. Two implementation of the `AddressMode` exist:
|
||||
//! `SevenBitAddress` and `TenBitAddress`.
|
||||
//!
|
||||
//! Through this marker types it is possible to implement each address mode for
|
||||
//! the traits independently in `embedded-hal` implementations and device drivers
|
||||
//! can depend only on the mode that they support.
|
||||
//!
|
||||
//! Additionally, the I2C 10-bit address mode has been developed to be fully
|
||||
//! backwards compatible with the 7-bit address mode. This allows for a
|
||||
//! software-emulated 10-bit addressing implementation if the address mode
|
||||
//! is not supported by the hardware.
|
||||
//!
|
||||
//! Since 7-bit addressing is the mode of the majority of I2C devices,
|
||||
//! `SevenBitAddress` has been set as default mode and thus can be omitted if desired.
|
||||
//!
|
||||
//! ## Examples
|
||||
//!
|
||||
//! ### `embedded-hal` implementation for an MCU
|
||||
//! Here is an example of an embedded-hal implementation of the `Write` trait
|
||||
//! for both modes:
|
||||
//! ```
|
||||
//! # use embedded_hal::blocking::i2c::{SevenBitAddress, TenBitAddress, Write};
|
||||
//! /// I2C0 hardware peripheral which supports both 7-bit and 10-bit addressing.
|
||||
//! pub struct I2c0;
|
||||
//!
|
||||
//! impl Write<SevenBitAddress> for I2c0
|
||||
//! {
|
||||
//! # type Error = ();
|
||||
//! #
|
||||
//! fn write(&mut self, addr: u8, output: &[u8]) -> Result<(), Self::Error> {
|
||||
//! // ...
|
||||
//! # Ok(())
|
||||
//! }
|
||||
//! }
|
||||
//!
|
||||
//! impl Write<TenBitAddress> for I2c0
|
||||
//! {
|
||||
//! # type Error = ();
|
||||
//! #
|
||||
//! fn write(&mut self, addr: u16, output: &[u8]) -> Result<(), Self::Error> {
|
||||
//! // ...
|
||||
//! # Ok(())
|
||||
//! }
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! ### Device driver compatible only with 7-bit addresses
|
||||
//!
|
||||
//! For demonstration purposes the address mode parameter has been omitted in this example.
|
||||
//!
|
||||
//! ```
|
||||
//! # use embedded_hal::blocking::i2c::WriteRead;
|
||||
//! const ADDR: u8 = 0x15;
|
||||
//! # const TEMP_REGISTER: u8 = 0x1;
|
||||
//! pub struct TemperatureSensorDriver<I2C> {
|
||||
//! i2c: I2C,
|
||||
//! }
|
||||
//!
|
||||
//! impl<I2C, E> TemperatureSensorDriver<I2C>
|
||||
//! where
|
||||
//! I2C: WriteRead<Error = E>,
|
||||
//! {
|
||||
//! pub fn read_temperature(&mut self) -> Result<u8, E> {
|
||||
//! let mut temp = [0];
|
||||
//! self.i2c
|
||||
//! .write_read(ADDR, &[TEMP_REGISTER], &mut temp)
|
||||
//! .and(Ok(temp[0]))
|
||||
//! }
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! ### Device driver compatible only with 10-bit addresses
|
||||
//!
|
||||
//! ```
|
||||
//! # use embedded_hal::blocking::i2c::{TenBitAddress, WriteRead};
|
||||
//! const ADDR: u16 = 0x158;
|
||||
//! # const TEMP_REGISTER: u8 = 0x1;
|
||||
//! pub struct TemperatureSensorDriver<I2C> {
|
||||
//! i2c: I2C,
|
||||
//! }
|
||||
//!
|
||||
//! impl<I2C, E> TemperatureSensorDriver<I2C>
|
||||
//! where
|
||||
//! I2C: WriteRead<TenBitAddress, Error = E>,
|
||||
//! {
|
||||
//! pub fn read_temperature(&mut self) -> Result<u8, E> {
|
||||
//! let mut temp = [0];
|
||||
//! self.i2c
|
||||
//! .write_read(ADDR, &[TEMP_REGISTER], &mut temp)
|
||||
//! .and(Ok(temp[0]))
|
||||
//! }
|
||||
//! }
|
||||
//! ```
|
||||
|
||||
use crate::private;
|
||||
|
||||
impl private::Sealed for SevenBitAddress {}
|
||||
impl private::Sealed for TenBitAddress {}
|
||||
|
||||
/// Address mode (7-bit / 10-bit)
|
||||
///
|
||||
/// Note: This trait is sealed and should not be implemented outside of this crate.
|
||||
pub trait AddressMode: private::Sealed {}
|
||||
|
||||
/// 7-bit address mode type
|
||||
pub type SevenBitAddress = u8;
|
||||
|
||||
/// 10-bit address mode type
|
||||
pub type TenBitAddress = u16;
|
||||
|
||||
impl AddressMode for SevenBitAddress {}
|
||||
|
||||
impl AddressMode for TenBitAddress {}
|
||||
|
||||
/// Blocking read
|
||||
pub trait Read<A: AddressMode = SevenBitAddress> {
|
||||
/// Error type
|
||||
type Error;
|
||||
|
||||
/// Reads enough bytes from slave with `address` to fill `buffer`
|
||||
///
|
||||
/// # I2C Events (contract)
|
||||
///
|
||||
/// ``` text
|
||||
/// Master: ST SAD+R MAK MAK ... NMAK SP
|
||||
/// Slave: SAK B0 B1 ... BN
|
||||
/// ```
|
||||
///
|
||||
/// Where
|
||||
///
|
||||
/// - `ST` = start condition
|
||||
/// - `SAD+R` = slave address followed by bit 1 to indicate reading
|
||||
/// - `SAK` = slave acknowledge
|
||||
/// - `Bi` = ith byte of data
|
||||
/// - `MAK` = master acknowledge
|
||||
/// - `NMAK` = master no acknowledge
|
||||
/// - `SP` = stop condition
|
||||
fn read(&mut self, address: A, buffer: &mut [u8]) -> Result<(), Self::Error>;
|
||||
}
|
||||
|
||||
/// Blocking write
|
||||
pub trait Write<A: AddressMode = SevenBitAddress> {
|
||||
/// Error type
|
||||
type Error;
|
||||
|
||||
/// Writes bytes to slave with address `address`
|
||||
///
|
||||
/// # I2C Events (contract)
|
||||
///
|
||||
/// ``` text
|
||||
/// Master: ST SAD+W B0 B1 ... BN SP
|
||||
/// Slave: SAK SAK SAK ... SAK
|
||||
/// ```
|
||||
///
|
||||
/// Where
|
||||
///
|
||||
/// - `ST` = start condition
|
||||
/// - `SAD+W` = slave address followed by bit 0 to indicate writing
|
||||
/// - `SAK` = slave acknowledge
|
||||
/// - `Bi` = ith byte of data
|
||||
/// - `SP` = stop condition
|
||||
fn write(&mut self, address: A, bytes: &[u8]) -> Result<(), Self::Error>;
|
||||
}
|
||||
|
||||
/// Blocking write (iterator version)
|
||||
pub trait WriteIter<A: AddressMode = SevenBitAddress> {
|
||||
/// Error type
|
||||
type Error;
|
||||
|
||||
/// Writes bytes to slave with address `address`
|
||||
///
|
||||
/// # I2C Events (contract)
|
||||
///
|
||||
/// Same as `Write`
|
||||
fn write<B>(&mut self, address: A, bytes: B) -> Result<(), Self::Error>
|
||||
where
|
||||
B: IntoIterator<Item = u8>;
|
||||
}
|
||||
|
||||
/// Blocking write + read
|
||||
pub trait WriteRead<A: AddressMode = SevenBitAddress> {
|
||||
/// Error type
|
||||
type Error;
|
||||
|
||||
/// Writes bytes to slave with address `address` and then reads enough bytes to fill `buffer` *in a
|
||||
/// single transaction*
|
||||
///
|
||||
/// # I2C Events (contract)
|
||||
///
|
||||
/// ``` text
|
||||
/// Master: ST SAD+W O0 O1 ... OM SR SAD+R MAK MAK ... NMAK SP
|
||||
/// Slave: SAK SAK SAK ... SAK SAK I0 I1 ... IN
|
||||
/// ```
|
||||
///
|
||||
/// Where
|
||||
///
|
||||
/// - `ST` = start condition
|
||||
/// - `SAD+W` = slave address followed by bit 0 to indicate writing
|
||||
/// - `SAK` = slave acknowledge
|
||||
/// - `Oi` = ith outgoing byte of data
|
||||
/// - `SR` = repeated start condition
|
||||
/// - `SAD+R` = slave address followed by bit 1 to indicate reading
|
||||
/// - `Ii` = ith incoming byte of data
|
||||
/// - `MAK` = master acknowledge
|
||||
/// - `NMAK` = master no acknowledge
|
||||
/// - `SP` = stop condition
|
||||
fn write_read(
|
||||
&mut self,
|
||||
address: A,
|
||||
bytes: &[u8],
|
||||
buffer: &mut [u8],
|
||||
) -> Result<(), Self::Error>;
|
||||
}
|
||||
|
||||
/// Blocking write (iterator version) + read
|
||||
pub trait WriteIterRead<A: AddressMode = SevenBitAddress> {
|
||||
/// Error type
|
||||
type Error;
|
||||
|
||||
/// Writes bytes to slave with address `address` and then reads enough bytes to fill `buffer` *in a
|
||||
/// single transaction*
|
||||
///
|
||||
/// # I2C Events (contract)
|
||||
///
|
||||
/// Same as the `WriteRead` trait
|
||||
fn write_iter_read<B>(
|
||||
&mut self,
|
||||
address: A,
|
||||
bytes: B,
|
||||
buffer: &mut [u8],
|
||||
) -> Result<(), Self::Error>
|
||||
where
|
||||
B: IntoIterator<Item = u8>;
|
||||
}
|
||||
|
||||
/// Transactional I2C operation.
|
||||
///
|
||||
/// Several operations can be combined as part of a transaction.
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum Operation<'a> {
|
||||
/// Read data into the provided buffer
|
||||
Read(&'a mut [u8]),
|
||||
/// Write data from the provided buffer
|
||||
Write(&'a [u8]),
|
||||
}
|
||||
|
||||
/// Transactional I2C interface.
|
||||
///
|
||||
/// This allows combining operations within an I2C transaction.
|
||||
pub trait Transactional<A: AddressMode = SevenBitAddress> {
|
||||
/// Error type
|
||||
type Error;
|
||||
|
||||
/// Execute the provided operations on the I2C bus.
|
||||
///
|
||||
/// Transaction contract:
|
||||
/// - Before executing the first operation an ST is sent automatically. This is followed by SAD+R/W as appropriate.
|
||||
/// - Data from adjacent operations of the same type are sent after each other without an SP or SR.
|
||||
/// - Between adjacent operations of a different type an SR and SAD+R/W is sent.
|
||||
/// - After executing the last operation an SP is sent automatically.
|
||||
/// - If the last operation is a `Read` the master does not send an acknowledge for the last byte.
|
||||
///
|
||||
/// - `ST` = start condition
|
||||
/// - `SAD+R/W` = slave address followed by bit 1 to indicate reading or 0 to indicate writing
|
||||
/// - `SR` = repeated start condition
|
||||
/// - `SP` = stop condition
|
||||
fn exec<'a>(&mut self, address: A, operations: &mut [Operation<'a>])
|
||||
-> Result<(), Self::Error>;
|
||||
}
|
||||
|
||||
/// Transactional I2C interface (iterator version).
|
||||
///
|
||||
/// This allows combining operation within an I2C transaction.
|
||||
pub trait TransactionalIter<A: AddressMode = SevenBitAddress> {
|
||||
/// Error type
|
||||
type Error;
|
||||
|
||||
/// Execute the provided operations on the I2C bus (iterator version).
|
||||
///
|
||||
/// Transaction contract:
|
||||
/// - Before executing the first operation an ST is sent automatically. This is followed by SAD+R/W as appropriate.
|
||||
/// - Data from adjacent operations of the same type are sent after each other without an SP or SR.
|
||||
/// - Between adjacent operations of a different type an SR and SAD+R/W is sent.
|
||||
/// - After executing the last operation an SP is sent automatically.
|
||||
/// - If the last operation is a `Read` the master does not send an acknowledge for the last byte.
|
||||
///
|
||||
/// - `ST` = start condition
|
||||
/// - `SAD+R/W` = slave address followed by bit 1 to indicate reading or 0 to indicate writing
|
||||
/// - `SR` = repeated start condition
|
||||
/// - `SP` = stop condition
|
||||
fn exec_iter<'a, O>(&mut self, address: A, operations: O) -> Result<(), Self::Error>
|
||||
where
|
||||
O: IntoIterator<Item = Operation<'a>>;
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
//! Blocking API
|
||||
//!
|
||||
//! In some cases it's possible to implement these blocking traits on top of one of the core HAL
|
||||
//! traits. To save boilerplate when that's the case a `Default` marker trait may be provided.
|
||||
//! Implementing that marker trait will opt in your type into a blanket implementation.
|
||||
|
||||
pub mod can;
|
||||
pub mod delay;
|
||||
pub mod i2c;
|
||||
pub mod rng;
|
||||
pub mod serial;
|
||||
pub mod spi;
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
//! Blocking hardware random number generator
|
||||
|
||||
/// Blocking read
|
||||
///
|
||||
/// *This trait is available if embedded-hal is built with the `"unproven"` feature.*
|
||||
#[cfg(feature = "unproven")]
|
||||
pub trait Read {
|
||||
/// Error type
|
||||
type Error;
|
||||
|
||||
/// Reads enough bytes from hardware random number generator to fill `buffer`
|
||||
///
|
||||
/// If any error is encountered then this function immediately returns. The contents of buf are
|
||||
/// unspecified in this case.
|
||||
///
|
||||
/// If this function returns an error, it is unspecified how many bytes it has read, but it
|
||||
/// will never read more than would be necessary to completely fill the buffer.
|
||||
fn read(&mut self, buffer: &mut [u8]) -> Result<(), Self::Error>;
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
//! Blocking serial API
|
||||
|
||||
/// Write half of a serial interface (blocking variant)
|
||||
pub trait Write<Word> {
|
||||
/// The type of error that can occur when writing
|
||||
type Error;
|
||||
|
||||
/// Writes a slice, blocking until everything has been written
|
||||
///
|
||||
/// An implementation can choose to buffer the write, returning `Ok(())`
|
||||
/// after the complete slice has been written to a buffer, but before all
|
||||
/// words have been sent via the serial interface. To make sure that
|
||||
/// everything has been sent, call [`bflush`] after this function returns.
|
||||
///
|
||||
/// [`bflush`]: #tymethod.bflush
|
||||
fn bwrite_all(&mut self, buffer: &[Word]) -> Result<(), Self::Error>;
|
||||
|
||||
/// Block until the serial interface has sent all buffered words
|
||||
fn bflush(&mut self) -> Result<(), Self::Error>;
|
||||
}
|
||||
|
||||
/// Blocking serial write
|
||||
pub mod write {
|
||||
/// Marker trait to opt into default blocking write implementation
|
||||
///
|
||||
/// Implementers of [`serial::Write`] can implement this marker trait
|
||||
/// for their type. Doing so will automatically provide the default
|
||||
/// implementation of [`blocking::serial::Write`] for the type.
|
||||
///
|
||||
/// [`serial::Write`]: ../../serial/trait.Write.html
|
||||
/// [`blocking::serial::Write`]: ../trait.Write.html
|
||||
pub trait Default<Word>: ::serial::Write<Word> {}
|
||||
|
||||
impl<S, Word> ::blocking::serial::Write<Word> for S
|
||||
where
|
||||
S: Default<Word>,
|
||||
Word: Clone,
|
||||
{
|
||||
type Error = S::Error;
|
||||
|
||||
fn bwrite_all(&mut self, buffer: &[Word]) -> Result<(), Self::Error> {
|
||||
for word in buffer {
|
||||
block!(self.write(word.clone()))?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn bflush(&mut self) -> Result<(), Self::Error> {
|
||||
block!(self.flush())?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
+127
@@ -0,0 +1,127 @@
|
||||
//! Blocking SPI API
|
||||
|
||||
/// Blocking transfer
|
||||
pub trait Transfer<W> {
|
||||
/// Error type
|
||||
type Error;
|
||||
|
||||
/// Sends `words` to the slave. Returns the `words` received from the slave
|
||||
fn transfer<'w>(&mut self, words: &'w mut [W]) -> Result<&'w [W], Self::Error>;
|
||||
}
|
||||
|
||||
/// Blocking write
|
||||
pub trait Write<W> {
|
||||
/// Error type
|
||||
type Error;
|
||||
|
||||
/// Sends `words` to the slave, ignoring all the incoming words
|
||||
fn write(&mut self, words: &[W]) -> Result<(), Self::Error>;
|
||||
}
|
||||
|
||||
/// Blocking write (iterator version)
|
||||
#[cfg(feature = "unproven")]
|
||||
pub trait WriteIter<W> {
|
||||
/// Error type
|
||||
type Error;
|
||||
|
||||
/// Sends `words` to the slave, ignoring all the incoming words
|
||||
fn write_iter<WI>(&mut self, words: WI) -> Result<(), Self::Error>
|
||||
where
|
||||
WI: IntoIterator<Item = W>;
|
||||
}
|
||||
|
||||
/// Blocking transfer
|
||||
pub mod transfer {
|
||||
/// Default implementation of `blocking::spi::Transfer<W>` for implementers of
|
||||
/// `spi::FullDuplex<W>`
|
||||
pub trait Default<W>: ::spi::FullDuplex<W> {}
|
||||
|
||||
impl<W, S> ::blocking::spi::Transfer<W> for S
|
||||
where
|
||||
S: Default<W>,
|
||||
W: Clone,
|
||||
{
|
||||
type Error = S::Error;
|
||||
|
||||
fn transfer<'w>(&mut self, words: &'w mut [W]) -> Result<&'w [W], S::Error> {
|
||||
for word in words.iter_mut() {
|
||||
block!(self.send(word.clone()))?;
|
||||
*word = block!(self.read())?;
|
||||
}
|
||||
|
||||
Ok(words)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Blocking write
|
||||
pub mod write {
|
||||
/// Default implementation of `blocking::spi::Write<W>` for implementers of `spi::FullDuplex<W>`
|
||||
pub trait Default<W>: ::spi::FullDuplex<W> {}
|
||||
|
||||
impl<W, S> ::blocking::spi::Write<W> for S
|
||||
where
|
||||
S: Default<W>,
|
||||
W: Clone,
|
||||
{
|
||||
type Error = S::Error;
|
||||
|
||||
fn write(&mut self, words: &[W]) -> Result<(), S::Error> {
|
||||
for word in words {
|
||||
block!(self.send(word.clone()))?;
|
||||
block!(self.read())?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Blocking write (iterator version)
|
||||
#[cfg(feature = "unproven")]
|
||||
pub mod write_iter {
|
||||
/// Default implementation of `blocking::spi::WriteIter<W>` for implementers of
|
||||
/// `spi::FullDuplex<W>`
|
||||
pub trait Default<W>: ::spi::FullDuplex<W> {}
|
||||
|
||||
impl<W, S> ::blocking::spi::WriteIter<W> for S
|
||||
where
|
||||
S: Default<W>,
|
||||
W: Clone,
|
||||
{
|
||||
type Error = S::Error;
|
||||
|
||||
fn write_iter<WI>(&mut self, words: WI) -> Result<(), S::Error>
|
||||
where
|
||||
WI: IntoIterator<Item = W>,
|
||||
{
|
||||
for word in words.into_iter() {
|
||||
block!(self.send(word.clone()))?;
|
||||
block!(self.read())?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Operation for transactional SPI trait
|
||||
///
|
||||
/// This allows composition of SPI operations into a single bus transaction
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum Operation<'a, W: 'static> {
|
||||
/// Write data from the provided buffer, discarding read data
|
||||
Write(&'a [W]),
|
||||
/// Write data out while reading data into the provided buffer
|
||||
Transfer(&'a mut [W]),
|
||||
}
|
||||
|
||||
/// Transactional trait allows multiple actions to be executed
|
||||
/// as part of a single SPI transaction
|
||||
pub trait Transactional<W: 'static> {
|
||||
/// Associated error type
|
||||
type Error;
|
||||
|
||||
/// Execute the provided transactions
|
||||
fn exec<'a>(&mut self, operations: &mut [Operation<'a, W>]) -> Result<(), Self::Error>;
|
||||
}
|
||||
+160
@@ -0,0 +1,160 @@
|
||||
//! CAN Identifiers.
|
||||
|
||||
/// Standard 11-bit CAN Identifier (`0..=0x7FF`).
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
pub struct StandardId(u16);
|
||||
|
||||
impl StandardId {
|
||||
/// CAN ID `0`, the highest priority.
|
||||
pub const ZERO: Self = StandardId(0);
|
||||
|
||||
/// CAN ID `0x7FF`, the lowest priority.
|
||||
pub const MAX: Self = StandardId(0x7FF);
|
||||
|
||||
/// Tries to create a `StandardId` from a raw 16-bit integer.
|
||||
///
|
||||
/// This will return `None` if `raw` is out of range of an 11-bit integer (`> 0x7FF`).
|
||||
#[inline]
|
||||
pub fn new(raw: u16) -> Option<Self> {
|
||||
if raw <= 0x7FF {
|
||||
Some(StandardId(raw))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a new `StandardId` without checking if it is inside the valid range.
|
||||
///
|
||||
/// # Safety
|
||||
/// Using this method can create an invalid ID and is thus marked as unsafe.
|
||||
#[inline]
|
||||
pub const unsafe fn new_unchecked(raw: u16) -> Self {
|
||||
StandardId(raw)
|
||||
}
|
||||
|
||||
/// Returns this CAN Identifier as a raw 16-bit integer.
|
||||
#[inline]
|
||||
pub fn as_raw(&self) -> u16 {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
/// Extended 29-bit CAN Identifier (`0..=1FFF_FFFF`).
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
pub struct ExtendedId(u32);
|
||||
|
||||
impl ExtendedId {
|
||||
/// CAN ID `0`, the highest priority.
|
||||
pub const ZERO: Self = ExtendedId(0);
|
||||
|
||||
/// CAN ID `0x1FFFFFFF`, the lowest priority.
|
||||
pub const MAX: Self = ExtendedId(0x1FFF_FFFF);
|
||||
|
||||
/// Tries to create a `ExtendedId` from a raw 32-bit integer.
|
||||
///
|
||||
/// This will return `None` if `raw` is out of range of an 29-bit integer (`> 0x1FFF_FFFF`).
|
||||
#[inline]
|
||||
pub fn new(raw: u32) -> Option<Self> {
|
||||
if raw <= 0x1FFF_FFFF {
|
||||
Some(ExtendedId(raw))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a new `ExtendedId` without checking if it is inside the valid range.
|
||||
///
|
||||
/// # Safety
|
||||
/// Using this method can create an invalid ID and is thus marked as unsafe.
|
||||
#[inline]
|
||||
pub const unsafe fn new_unchecked(raw: u32) -> Self {
|
||||
ExtendedId(raw)
|
||||
}
|
||||
|
||||
/// Returns this CAN Identifier as a raw 32-bit integer.
|
||||
#[inline]
|
||||
pub fn as_raw(&self) -> u32 {
|
||||
self.0
|
||||
}
|
||||
|
||||
/// Returns the Base ID part of this extended identifier.
|
||||
pub fn standard_id(&self) -> StandardId {
|
||||
// ID-28 to ID-18
|
||||
StandardId((self.0 >> 18) as u16)
|
||||
}
|
||||
}
|
||||
|
||||
/// A CAN Identifier (standard or extended).
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
pub enum Id {
|
||||
/// Standard 11-bit Identifier (`0..=0x7FF`).
|
||||
Standard(StandardId),
|
||||
|
||||
/// Extended 29-bit Identifier (`0..=0x1FFF_FFFF`).
|
||||
Extended(ExtendedId),
|
||||
}
|
||||
|
||||
impl From<StandardId> for Id {
|
||||
#[inline]
|
||||
fn from(id: StandardId) -> Self {
|
||||
Id::Standard(id)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ExtendedId> for Id {
|
||||
#[inline]
|
||||
fn from(id: ExtendedId) -> Self {
|
||||
Id::Extended(id)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn standard_id_new() {
|
||||
assert_eq!(
|
||||
StandardId::new(StandardId::MAX.as_raw()),
|
||||
Some(StandardId::MAX)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn standard_id_new_out_of_range() {
|
||||
assert_eq!(StandardId::new(StandardId::MAX.as_raw() + 1), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn standard_id_new_unchecked_out_of_range() {
|
||||
let id = StandardId::MAX.as_raw() + 1;
|
||||
assert_eq!(unsafe { StandardId::new_unchecked(id) }, StandardId(id));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn extended_id_new() {
|
||||
assert_eq!(
|
||||
ExtendedId::new(ExtendedId::MAX.as_raw()),
|
||||
Some(ExtendedId::MAX)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn extended_id_new_out_of_range() {
|
||||
assert_eq!(ExtendedId::new(ExtendedId::MAX.as_raw() + 1), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn extended_id_new_unchecked_out_of_range() {
|
||||
let id = ExtendedId::MAX.as_raw() + 1;
|
||||
assert_eq!(unsafe { ExtendedId::new_unchecked(id) }, ExtendedId(id));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn get_standard_id_from_extended_id() {
|
||||
assert_eq!(
|
||||
Some(ExtendedId::MAX.standard_id()),
|
||||
StandardId::new((ExtendedId::MAX.0 >> 18) as u16)
|
||||
);
|
||||
}
|
||||
}
|
||||
+123
@@ -0,0 +1,123 @@
|
||||
//! Controller Area Network
|
||||
|
||||
pub mod nb;
|
||||
|
||||
mod id;
|
||||
|
||||
pub use self::id::*;
|
||||
pub use self::nb::*;
|
||||
|
||||
/// A CAN2.0 Frame
|
||||
pub trait Frame: Sized {
|
||||
/// Creates a new frame.
|
||||
///
|
||||
/// This will return `None` if the data slice is too long.
|
||||
fn new(id: impl Into<Id>, data: &[u8]) -> Option<Self>;
|
||||
|
||||
/// Creates a new remote frame (RTR bit set).
|
||||
///
|
||||
/// This will return `None` if the data length code (DLC) is not valid.
|
||||
fn new_remote(id: impl Into<Id>, dlc: usize) -> Option<Self>;
|
||||
|
||||
/// Returns true if this frame is a extended frame.
|
||||
fn is_extended(&self) -> bool;
|
||||
|
||||
/// Returns true if this frame is a standard frame.
|
||||
fn is_standard(&self) -> bool {
|
||||
!self.is_extended()
|
||||
}
|
||||
|
||||
/// Returns true if this frame is a remote frame.
|
||||
fn is_remote_frame(&self) -> bool;
|
||||
|
||||
/// Returns true if this frame is a data frame.
|
||||
fn is_data_frame(&self) -> bool {
|
||||
!self.is_remote_frame()
|
||||
}
|
||||
|
||||
/// Returns the frame identifier.
|
||||
fn id(&self) -> Id;
|
||||
|
||||
/// Returns the data length code (DLC) which is in the range 0..8.
|
||||
///
|
||||
/// For data frames the DLC value always matches the length of the data.
|
||||
/// Remote frames do not carry any data, yet the DLC can be greater than 0.
|
||||
fn dlc(&self) -> usize;
|
||||
|
||||
/// Returns the frame data (0..8 bytes in length).
|
||||
fn data(&self) -> &[u8];
|
||||
}
|
||||
|
||||
/// CAN error
|
||||
pub trait Error: core::fmt::Debug {
|
||||
/// Convert error to a generic CAN error kind
|
||||
///
|
||||
/// By using this method, CAN errors freely defined by HAL implementations
|
||||
/// can be converted to a set of generic serial errors upon which generic
|
||||
/// code can act.
|
||||
fn kind(&self) -> ErrorKind;
|
||||
}
|
||||
|
||||
/// CAN error kind
|
||||
///
|
||||
/// This represents a common set of CAN operation errors. HAL implementations are
|
||||
/// free to define more specific or additional error types. However, by providing
|
||||
/// a mapping to these common CAN errors, generic code can still react to them.
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
|
||||
pub enum ErrorKind {
|
||||
/// The peripheral receive buffer was overrun.
|
||||
Overrun,
|
||||
|
||||
// MAC sublayer errors
|
||||
/// A bit error is detected at that bit time when the bit value that is
|
||||
/// monitored differs from the bit value sent.
|
||||
Bit,
|
||||
|
||||
/// A stuff error is detected at the bit time of the sixth consecutive
|
||||
/// equal bit level in a frame field that shall be coded by the method
|
||||
/// of bit stuffing.
|
||||
Stuff,
|
||||
|
||||
/// Calculated CRC sequence does not equal the received one.
|
||||
Crc,
|
||||
|
||||
/// A form error shall be detected when a fixed-form bit field contains
|
||||
/// one or more illegal bits.
|
||||
Form,
|
||||
|
||||
/// An ACK error shall be detected by a transmitter whenever it does not
|
||||
/// monitor a dominant bit during the ACK slot.
|
||||
Acknowledge,
|
||||
|
||||
/// A different error occurred. The original error may contain more information.
|
||||
Other,
|
||||
}
|
||||
|
||||
impl Error for ErrorKind {
|
||||
fn kind(&self) -> ErrorKind {
|
||||
*self
|
||||
}
|
||||
}
|
||||
|
||||
impl core::fmt::Display for ErrorKind {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
match self {
|
||||
ErrorKind::Overrun => write!(f, "The peripheral receive buffer was overrun"),
|
||||
ErrorKind::Bit => write!(
|
||||
f,
|
||||
"Bit value that is monitored differs from the bit value sent"
|
||||
),
|
||||
ErrorKind::Stuff => write!(f, "Sixth consecutive equal bits detected"),
|
||||
ErrorKind::Crc => write!(f, "Calculated CRC sequence does not equal the received one"),
|
||||
ErrorKind::Form => write!(
|
||||
f,
|
||||
"A fixed-form bit field contains one or more illegal bits"
|
||||
),
|
||||
ErrorKind::Acknowledge => write!(f, "Transmitted frame was not acknowledged"),
|
||||
ErrorKind::Other => write!(
|
||||
f,
|
||||
"A different error occurred. The original error may contain more information"
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
//! Non-blocking CAN API
|
||||
|
||||
/// A CAN interface that is able to transmit and receive frames.
|
||||
pub trait Can {
|
||||
/// Associated frame type.
|
||||
type Frame: crate::can::Frame;
|
||||
|
||||
/// Associated error type.
|
||||
type Error: crate::can::Error;
|
||||
|
||||
/// Puts a frame in the transmit buffer to be sent on the bus.
|
||||
///
|
||||
/// If the transmit buffer is full, this function will try to replace a pending
|
||||
/// lower priority frame and return the frame that was replaced.
|
||||
/// Returns `Err(WouldBlock)` if the transmit buffer is full and no frame can be
|
||||
/// replaced.
|
||||
///
|
||||
/// # Notes for implementers
|
||||
///
|
||||
/// * Frames of equal identifier shall be transmited in FIFO fashion when more
|
||||
/// than one transmit buffer is available.
|
||||
/// * When replacing pending frames make sure the frame is not in the process of
|
||||
/// being send to the bus.
|
||||
fn transmit(&mut self, frame: &Self::Frame) -> nb::Result<Option<Self::Frame>, Self::Error>;
|
||||
|
||||
/// Returns a received frame if available.
|
||||
fn receive(&mut self) -> nb::Result<Self::Frame, Self::Error>;
|
||||
}
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
//! Digital I/O
|
||||
//!
|
||||
//!
|
||||
//!
|
||||
|
||||
// Deprecated / infallible traits
|
||||
#[deprecated(
|
||||
since = "0.2.2",
|
||||
note = "Deprecated because the methods cannot return errors. \
|
||||
Users should use the traits in digital::v2."
|
||||
)]
|
||||
pub mod v1;
|
||||
|
||||
// New / fallible traits
|
||||
pub mod v2;
|
||||
|
||||
// v2 -> v1 compatibility wrappers
|
||||
// These require explicit casts from v2 -> v1
|
||||
pub mod v1_compat;
|
||||
|
||||
// v1 -> v2 compatibility shims
|
||||
// These are implicit over v1 implementations
|
||||
pub mod v2_compat;
|
||||
|
||||
// Re-export old traits so this isn't a breaking change
|
||||
#[allow(deprecated)]
|
||||
pub use self::v1::*;
|
||||
+145
@@ -0,0 +1,145 @@
|
||||
//! Digital I/O
|
||||
//!
|
||||
//! The traits in this module are now deprecated. Please use the new versions included
|
||||
//! in `digital::v2`.
|
||||
|
||||
#![allow(deprecated)]
|
||||
|
||||
/// Single digital push-pull output pin
|
||||
///
|
||||
/// *This version of the trait is now deprecated. Please use the new `OutputPin` trait in
|
||||
/// `digital::v2::OutputPin`*.
|
||||
|
||||
pub trait OutputPin {
|
||||
/// Drives the pin low
|
||||
///
|
||||
/// *NOTE* the actual electrical state of the pin may not actually be low, e.g. due to external
|
||||
/// electrical sources
|
||||
fn set_low(&mut self);
|
||||
|
||||
/// Drives the pin high
|
||||
///
|
||||
/// *NOTE* the actual electrical state of the pin may not actually be high, e.g. due to external
|
||||
/// electrical sources
|
||||
fn set_high(&mut self);
|
||||
}
|
||||
|
||||
/// Push-pull output pin that can read its output state
|
||||
///
|
||||
/// *This trait is available if embedded-hal is built with the `"unproven"` feature.*
|
||||
///
|
||||
/// *This version of the trait is now deprecated. Please use the new `StatefulOutputPin` trait in
|
||||
/// `digital::v2::StatefulOutputPin`*.
|
||||
#[cfg(feature = "unproven")]
|
||||
pub trait StatefulOutputPin {
|
||||
/// Is the pin in drive high mode?
|
||||
///
|
||||
/// *NOTE* this does *not* read the electrical state of the pin
|
||||
fn is_set_high(&self) -> bool;
|
||||
|
||||
/// Is the pin in drive low mode?
|
||||
///
|
||||
/// *NOTE* this does *not* read the electrical state of the pin
|
||||
fn is_set_low(&self) -> bool;
|
||||
}
|
||||
|
||||
/// Output pin that can be toggled
|
||||
///
|
||||
/// *This trait is available if embedded-hal is built with the `"unproven"` feature.*
|
||||
///
|
||||
/// *This version of the trait is now deprecated. Please use the new `ToggleableOutputPin`
|
||||
/// trait in `digital::v2::ToggleableOutputPin`*.
|
||||
///
|
||||
/// See [toggleable](toggleable) to use a software implementation if
|
||||
/// both [OutputPin](trait.OutputPin.html) and
|
||||
/// [StatefulOutputPin](trait.StatefulOutputPin.html) are
|
||||
/// implemented. Otherwise, implement this using hardware mechanisms.
|
||||
#[cfg(feature = "unproven")]
|
||||
pub trait ToggleableOutputPin {
|
||||
/// Toggle pin output.
|
||||
fn toggle(&mut self);
|
||||
}
|
||||
|
||||
/// If you can read **and** write the output state, a pin is
|
||||
/// toggleable by software.
|
||||
///
|
||||
/// *This version of the module is now deprecated. Please use the new `toggleable` module in
|
||||
/// `digital::v2::toggleable`*.
|
||||
///
|
||||
/// ```
|
||||
/// use embedded_hal::digital::{OutputPin, StatefulOutputPin, ToggleableOutputPin};
|
||||
/// use embedded_hal::digital::toggleable;
|
||||
///
|
||||
/// /// A virtual output pin that exists purely in software
|
||||
/// struct MyPin {
|
||||
/// state: bool
|
||||
/// }
|
||||
///
|
||||
/// impl OutputPin for MyPin {
|
||||
/// fn set_low(&mut self) {
|
||||
/// self.state = false;
|
||||
/// }
|
||||
/// fn set_high(&mut self) {
|
||||
/// self.state = true;
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// impl StatefulOutputPin for MyPin {
|
||||
/// fn is_set_low(&self) -> bool {
|
||||
/// !self.state
|
||||
/// }
|
||||
/// fn is_set_high(&self) -> bool {
|
||||
/// self.state
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// /// Opt-in to the software implementation.
|
||||
/// impl toggleable::Default for MyPin {}
|
||||
///
|
||||
/// let mut pin = MyPin { state: false };
|
||||
/// pin.toggle();
|
||||
/// assert!(pin.is_set_high());
|
||||
/// pin.toggle();
|
||||
/// assert!(pin.is_set_low());
|
||||
/// ```
|
||||
#[cfg(feature = "unproven")]
|
||||
pub mod toggleable {
|
||||
#[allow(deprecated)]
|
||||
use super::{OutputPin, StatefulOutputPin, ToggleableOutputPin};
|
||||
|
||||
/// Software-driven `toggle()` implementation.
|
||||
///
|
||||
/// *This trait is available if embedded-hal is built with the `"unproven"` feature.*
|
||||
#[allow(deprecated)]
|
||||
pub trait Default: OutputPin + StatefulOutputPin {}
|
||||
|
||||
#[allow(deprecated)]
|
||||
impl<P> ToggleableOutputPin for P
|
||||
where
|
||||
P: Default,
|
||||
{
|
||||
/// Toggle pin output
|
||||
fn toggle(&mut self) {
|
||||
if self.is_set_low() {
|
||||
self.set_high();
|
||||
} else {
|
||||
self.set_low();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Single digital input pin
|
||||
///
|
||||
/// *This trait is available if embedded-hal is built with the `"unproven"` feature.*
|
||||
///
|
||||
/// *This version of the trait is now deprecated. Please use the new `InputPin` trait in
|
||||
/// `digital::v2::InputPin`*.
|
||||
#[cfg(feature = "unproven")]
|
||||
pub trait InputPin {
|
||||
/// Is the input pin high?
|
||||
fn is_high(&self) -> bool;
|
||||
|
||||
/// Is the input pin low?
|
||||
fn is_low(&self) -> bool;
|
||||
}
|
||||
+298
@@ -0,0 +1,298 @@
|
||||
//! v1 compatibility wrappers
|
||||
//!
|
||||
//! This module provides wrappers to support use of v2 implementations with
|
||||
//! v1 consumers. v2 traits must be explicitly cast to the v1 version using
|
||||
//! `.into()`, and will panic on internal errors
|
||||
//!
|
||||
//! ```
|
||||
//! extern crate embedded_hal;
|
||||
//! use embedded_hal::digital::{v1, v2, v1_compat::OldOutputPin};
|
||||
//!
|
||||
//! struct NewOutputPinImpl {}
|
||||
//!
|
||||
//! impl v2::OutputPin for NewOutputPinImpl {
|
||||
//! type Error = ();
|
||||
//! fn set_low(&mut self) -> Result<(), Self::Error> { Ok(()) }
|
||||
//! fn set_high(&mut self) -> Result<(), Self::Error>{ Ok(()) }
|
||||
//! }
|
||||
//!
|
||||
//! struct OldOutputPinConsumer<T: v1::OutputPin> {
|
||||
//! _pin: T,
|
||||
//! }
|
||||
//!
|
||||
//! impl <T>OldOutputPinConsumer<T>
|
||||
//! where T: v1::OutputPin {
|
||||
//! pub fn new(pin: T) -> OldOutputPinConsumer<T> {
|
||||
//! OldOutputPinConsumer{ _pin: pin }
|
||||
//! }
|
||||
//! }
|
||||
//!
|
||||
//! fn main() {
|
||||
//! let pin = NewOutputPinImpl{};
|
||||
//! let _consumer: OldOutputPinConsumer<OldOutputPin<_>> = OldOutputPinConsumer::new(pin.into());
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
|
||||
#[allow(deprecated)]
|
||||
use super::v1;
|
||||
use super::v2;
|
||||
|
||||
/// Wrapper to allow fallible `v2::OutputPin` traits to be converted to `v1::OutputPin` traits
|
||||
pub struct OldOutputPin<T> {
|
||||
pin: T,
|
||||
}
|
||||
|
||||
impl<T, E> OldOutputPin<T>
|
||||
where
|
||||
T: v2::OutputPin<Error = E>,
|
||||
E: core::fmt::Debug,
|
||||
{
|
||||
/// Create a new OldOutputPin wrapper around a `v2::OutputPin`
|
||||
pub fn new(pin: T) -> Self {
|
||||
Self { pin }
|
||||
}
|
||||
|
||||
/// Fetch a reference to the inner `v2::OutputPin` impl
|
||||
#[cfg(test)]
|
||||
fn inner(&self) -> &T {
|
||||
&self.pin
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, E> From<T> for OldOutputPin<T>
|
||||
where
|
||||
T: v2::OutputPin<Error = E>,
|
||||
E: core::fmt::Debug,
|
||||
{
|
||||
fn from(pin: T) -> Self {
|
||||
OldOutputPin { pin }
|
||||
}
|
||||
}
|
||||
|
||||
/// Implementation of `v1::OutputPin` trait for fallible `v2::OutputPin` output pins
|
||||
/// where errors will panic.
|
||||
#[allow(deprecated)]
|
||||
impl<T, E> v1::OutputPin for OldOutputPin<T>
|
||||
where
|
||||
T: v2::OutputPin<Error = E>,
|
||||
E: core::fmt::Debug,
|
||||
{
|
||||
fn set_low(&mut self) {
|
||||
self.pin.set_low().unwrap()
|
||||
}
|
||||
|
||||
fn set_high(&mut self) {
|
||||
self.pin.set_high().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
/// Implementation of `v1::StatefulOutputPin` trait for `v2::StatefulOutputPin` fallible pins
|
||||
/// where errors will panic.
|
||||
#[cfg(feature = "unproven")]
|
||||
#[allow(deprecated)]
|
||||
impl<T, E> v1::StatefulOutputPin for OldOutputPin<T>
|
||||
where
|
||||
T: v2::StatefulOutputPin<Error = E>,
|
||||
E: core::fmt::Debug,
|
||||
{
|
||||
fn is_set_low(&self) -> bool {
|
||||
self.pin.is_set_low().unwrap()
|
||||
}
|
||||
|
||||
fn is_set_high(&self) -> bool {
|
||||
self.pin.is_set_high().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
/// Wrapper to allow fallible `v2::InputPin` traits to be converted to `v1::InputPin` traits
|
||||
/// where errors will panic.
|
||||
#[cfg(feature = "unproven")]
|
||||
pub struct OldInputPin<T> {
|
||||
pin: T,
|
||||
}
|
||||
|
||||
#[cfg(feature = "unproven")]
|
||||
impl<T, E> OldInputPin<T>
|
||||
where
|
||||
T: v2::InputPin<Error = E>,
|
||||
E: core::fmt::Debug,
|
||||
{
|
||||
/// Create an `OldInputPin` wrapper around a `v2::InputPin`.
|
||||
pub fn new(pin: T) -> Self {
|
||||
Self { pin }
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "unproven")]
|
||||
impl<T, E> From<T> for OldInputPin<T>
|
||||
where
|
||||
T: v2::InputPin<Error = E>,
|
||||
E: core::fmt::Debug,
|
||||
{
|
||||
fn from(pin: T) -> Self {
|
||||
OldInputPin { pin }
|
||||
}
|
||||
}
|
||||
|
||||
/// Implementation of `v1::InputPin` trait for `v2::InputPin` fallible pins
|
||||
/// where errors will panic.
|
||||
#[cfg(feature = "unproven")]
|
||||
#[allow(deprecated)]
|
||||
impl<T, E> v1::InputPin for OldInputPin<T>
|
||||
where
|
||||
T: v2::InputPin<Error = E>,
|
||||
E: core::fmt::Debug,
|
||||
{
|
||||
fn is_low(&self) -> bool {
|
||||
self.pin.is_low().unwrap()
|
||||
}
|
||||
|
||||
fn is_high(&self) -> bool {
|
||||
self.pin.is_high().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[allow(deprecated)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[allow(deprecated)]
|
||||
use crate::digital::v1;
|
||||
use crate::digital::v2;
|
||||
|
||||
use crate::digital::v1::OutputPin;
|
||||
|
||||
#[derive(Clone)]
|
||||
struct NewOutputPinImpl {
|
||||
state: bool,
|
||||
res: Result<(), ()>,
|
||||
}
|
||||
|
||||
impl v2::OutputPin for NewOutputPinImpl {
|
||||
type Error = ();
|
||||
|
||||
fn set_low(&mut self) -> Result<(), Self::Error> {
|
||||
self.state = false;
|
||||
self.res
|
||||
}
|
||||
fn set_high(&mut self) -> Result<(), Self::Error> {
|
||||
self.state = true;
|
||||
self.res
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(deprecated)]
|
||||
struct OldOutputPinConsumer<T: v1::OutputPin> {
|
||||
_pin: T,
|
||||
}
|
||||
|
||||
#[allow(deprecated)]
|
||||
impl<T> OldOutputPinConsumer<T>
|
||||
where
|
||||
T: v1::OutputPin,
|
||||
{
|
||||
pub fn new(pin: T) -> OldOutputPinConsumer<T> {
|
||||
OldOutputPinConsumer { _pin: pin }
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn v1_v2_output_explicit() {
|
||||
let i = NewOutputPinImpl {
|
||||
state: false,
|
||||
res: Ok(()),
|
||||
};
|
||||
let _c: OldOutputPinConsumer<OldOutputPin<_>> = OldOutputPinConsumer::new(i.into());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn v1_v2_output_state() {
|
||||
let mut o: OldOutputPin<_> = NewOutputPinImpl {
|
||||
state: false,
|
||||
res: Ok(()),
|
||||
}
|
||||
.into();
|
||||
|
||||
o.set_high();
|
||||
assert_eq!(o.inner().state, true);
|
||||
|
||||
o.set_low();
|
||||
assert_eq!(o.inner().state, false);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn v1_v2_output_panic() {
|
||||
let mut o: OldOutputPin<_> = NewOutputPinImpl {
|
||||
state: false,
|
||||
res: Err(()),
|
||||
}
|
||||
.into();
|
||||
|
||||
o.set_high();
|
||||
}
|
||||
|
||||
#[cfg(feature = "unproven")]
|
||||
use crate::digital::v1::InputPin;
|
||||
|
||||
#[cfg(feature = "unproven")]
|
||||
struct NewInputPinImpl {
|
||||
state: Result<bool, ()>,
|
||||
}
|
||||
|
||||
#[cfg(feature = "unproven")]
|
||||
impl v2::InputPin for NewInputPinImpl {
|
||||
type Error = ();
|
||||
|
||||
fn is_low(&self) -> Result<bool, Self::Error> {
|
||||
self.state.map(|v| v == false)
|
||||
}
|
||||
fn is_high(&self) -> Result<bool, Self::Error> {
|
||||
self.state.map(|v| v == true)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "unproven")]
|
||||
#[allow(deprecated)]
|
||||
struct OldInputPinConsumer<T: v1::InputPin> {
|
||||
_pin: T,
|
||||
}
|
||||
|
||||
#[cfg(feature = "unproven")]
|
||||
#[allow(deprecated)]
|
||||
impl<T> OldInputPinConsumer<T>
|
||||
where
|
||||
T: v1::InputPin,
|
||||
{
|
||||
pub fn new(pin: T) -> OldInputPinConsumer<T> {
|
||||
OldInputPinConsumer { _pin: pin }
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "unproven")]
|
||||
#[test]
|
||||
fn v1_v2_input_explicit() {
|
||||
let i = NewInputPinImpl { state: Ok(false) };
|
||||
let _c: OldInputPinConsumer<OldInputPin<_>> = OldInputPinConsumer::new(i.into());
|
||||
}
|
||||
|
||||
#[cfg(feature = "unproven")]
|
||||
#[test]
|
||||
fn v1_v2_input_state() {
|
||||
let i: OldInputPin<_> = NewInputPinImpl { state: Ok(false) }.into();
|
||||
|
||||
assert_eq!(i.is_low(), true);
|
||||
assert_eq!(i.is_high(), false);
|
||||
}
|
||||
|
||||
#[cfg(feature = "unproven")]
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn v1_v2_input_panic() {
|
||||
let i: OldInputPin<_> = NewInputPinImpl { state: Err(()) }.into();
|
||||
|
||||
i.is_low();
|
||||
}
|
||||
}
|
||||
+216
@@ -0,0 +1,216 @@
|
||||
//! Digital I/O
|
||||
//!
|
||||
//! Version 2 / fallible traits. Infallible implementations should set Error to `!`.
|
||||
|
||||
use core::{convert::From, ops::Not};
|
||||
|
||||
/// Digital output pin state
|
||||
///
|
||||
/// Conversion from `bool` and logical negation are also implemented
|
||||
/// for this type.
|
||||
/// ```rust
|
||||
/// # use embedded_hal::digital::v2::PinState;
|
||||
/// let state = PinState::from(false);
|
||||
/// assert_eq!(state, PinState::Low);
|
||||
/// assert_eq!(!state, PinState::High);
|
||||
/// ```
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||
pub enum PinState {
|
||||
/// Low pin state
|
||||
Low,
|
||||
/// High pin state
|
||||
High,
|
||||
}
|
||||
|
||||
impl From<bool> for PinState {
|
||||
fn from(value: bool) -> Self {
|
||||
match value {
|
||||
false => PinState::Low,
|
||||
true => PinState::High,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Not for PinState {
|
||||
type Output = PinState;
|
||||
|
||||
fn not(self) -> Self::Output {
|
||||
match self {
|
||||
PinState::High => PinState::Low,
|
||||
PinState::Low => PinState::High,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Single digital push-pull output pin
|
||||
pub trait OutputPin {
|
||||
/// Error type
|
||||
type Error;
|
||||
|
||||
/// Drives the pin low
|
||||
///
|
||||
/// *NOTE* the actual electrical state of the pin may not actually be low, e.g. due to external
|
||||
/// electrical sources
|
||||
fn set_low(&mut self) -> Result<(), Self::Error>;
|
||||
|
||||
/// Drives the pin high
|
||||
///
|
||||
/// *NOTE* the actual electrical state of the pin may not actually be high, e.g. due to external
|
||||
/// electrical sources
|
||||
fn set_high(&mut self) -> Result<(), Self::Error>;
|
||||
|
||||
/// Drives the pin high or low depending on the provided value
|
||||
///
|
||||
/// *NOTE* the actual electrical state of the pin may not actually be high or low, e.g. due to external
|
||||
/// electrical sources
|
||||
fn set_state(&mut self, state: PinState) -> Result<(), Self::Error> {
|
||||
match state {
|
||||
PinState::Low => self.set_low(),
|
||||
PinState::High => self.set_high(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Push-pull output pin that can read its output state
|
||||
///
|
||||
/// *This trait is available if embedded-hal is built with the `"unproven"` feature.*
|
||||
#[cfg(feature = "unproven")]
|
||||
pub trait StatefulOutputPin: OutputPin {
|
||||
/// Is the pin in drive high mode?
|
||||
///
|
||||
/// *NOTE* this does *not* read the electrical state of the pin
|
||||
fn is_set_high(&self) -> Result<bool, Self::Error>;
|
||||
|
||||
/// Is the pin in drive low mode?
|
||||
///
|
||||
/// *NOTE* this does *not* read the electrical state of the pin
|
||||
fn is_set_low(&self) -> Result<bool, Self::Error>;
|
||||
}
|
||||
|
||||
/// Output pin that can be toggled
|
||||
///
|
||||
/// *This trait is available if embedded-hal is built with the `"unproven"` feature.*
|
||||
///
|
||||
/// See [toggleable](toggleable) to use a software implementation if
|
||||
/// both [OutputPin](trait.OutputPin.html) and
|
||||
/// [StatefulOutputPin](trait.StatefulOutputPin.html) are
|
||||
/// implemented. Otherwise, implement this using hardware mechanisms.
|
||||
#[cfg(feature = "unproven")]
|
||||
pub trait ToggleableOutputPin {
|
||||
/// Error type
|
||||
type Error;
|
||||
|
||||
/// Toggle pin output.
|
||||
fn toggle(&mut self) -> Result<(), Self::Error>;
|
||||
}
|
||||
|
||||
/// If you can read **and** write the output state, a pin is
|
||||
/// toggleable by software.
|
||||
///
|
||||
/// ```
|
||||
/// use embedded_hal::digital::v2::{OutputPin, StatefulOutputPin, ToggleableOutputPin};
|
||||
/// use embedded_hal::digital::v2::toggleable;
|
||||
///
|
||||
/// /// A virtual output pin that exists purely in software
|
||||
/// struct MyPin {
|
||||
/// state: bool
|
||||
/// }
|
||||
///
|
||||
/// impl OutputPin for MyPin {
|
||||
/// type Error = void::Void;
|
||||
///
|
||||
/// fn set_low(&mut self) -> Result<(), Self::Error> {
|
||||
/// self.state = false;
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// fn set_high(&mut self) -> Result<(), Self::Error> {
|
||||
/// self.state = true;
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// impl StatefulOutputPin for MyPin {
|
||||
/// fn is_set_low(&self) -> Result<bool, Self::Error> {
|
||||
/// Ok(!self.state)
|
||||
/// }
|
||||
/// fn is_set_high(&self) -> Result<bool, Self::Error> {
|
||||
/// Ok(self.state)
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// /// Opt-in to the software implementation.
|
||||
/// impl toggleable::Default for MyPin {}
|
||||
///
|
||||
/// let mut pin = MyPin { state: false };
|
||||
/// pin.toggle().unwrap();
|
||||
/// assert!(pin.is_set_high().unwrap());
|
||||
/// pin.toggle().unwrap();
|
||||
/// assert!(pin.is_set_low().unwrap());
|
||||
/// ```
|
||||
#[cfg(feature = "unproven")]
|
||||
pub mod toggleable {
|
||||
use super::{OutputPin, StatefulOutputPin, ToggleableOutputPin};
|
||||
|
||||
/// Software-driven `toggle()` implementation.
|
||||
///
|
||||
/// *This trait is available if embedded-hal is built with the `"unproven"` feature.*
|
||||
pub trait Default: OutputPin + StatefulOutputPin {}
|
||||
|
||||
impl<P> ToggleableOutputPin for P
|
||||
where
|
||||
P: Default,
|
||||
{
|
||||
type Error = P::Error;
|
||||
|
||||
/// Toggle pin output
|
||||
fn toggle(&mut self) -> Result<(), Self::Error> {
|
||||
if self.is_set_low()? {
|
||||
self.set_high()
|
||||
} else {
|
||||
self.set_low()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Single digital input pin
|
||||
///
|
||||
/// *This trait is available if embedded-hal is built with the `"unproven"` feature.*
|
||||
#[cfg(feature = "unproven")]
|
||||
pub trait InputPin {
|
||||
/// Error type
|
||||
type Error;
|
||||
|
||||
/// Is the input pin high?
|
||||
fn is_high(&self) -> Result<bool, Self::Error>;
|
||||
|
||||
/// Is the input pin low?
|
||||
fn is_low(&self) -> Result<bool, Self::Error>;
|
||||
}
|
||||
|
||||
/// Single pin that can switch from input to output mode, and vice-versa.
|
||||
///
|
||||
/// Example use (assumes the `Error` type is the same for the `IoPin`,
|
||||
/// `InputPin`, and `OutputPin`):
|
||||
///
|
||||
/// *This trait is available if embedded-hal is built with the `"unproven"` feature.*
|
||||
#[cfg(feature = "unproven")]
|
||||
pub trait IoPin<TInput, TOutput>
|
||||
where
|
||||
TInput: InputPin + IoPin<TInput, TOutput>,
|
||||
TOutput: OutputPin + IoPin<TInput, TOutput>,
|
||||
{
|
||||
/// Error type.
|
||||
type Error;
|
||||
|
||||
/// Tries to convert this pin to input mode.
|
||||
///
|
||||
/// If the pin is already in input mode, this method should succeed.
|
||||
fn into_input_pin(self) -> Result<TInput, Self::Error>;
|
||||
|
||||
/// Tries to convert this pin to output mode with the given initial state.
|
||||
///
|
||||
/// If the pin is already in the requested state, this method should
|
||||
/// succeed.
|
||||
fn into_output_pin(self, state: PinState) -> Result<TOutput, Self::Error>;
|
||||
}
|
||||
+234
@@ -0,0 +1,234 @@
|
||||
//! v2 compatibility shims
|
||||
//!
|
||||
//! This module adds implicit forward support to v1 digital traits,
|
||||
//! allowing v1 implementations to be directly used with v2 consumers.
|
||||
//!
|
||||
//! ```
|
||||
//! extern crate embedded_hal;
|
||||
//! use embedded_hal::digital::{v1, v2};
|
||||
//!
|
||||
//! struct OldOutputPinImpl { }
|
||||
//!
|
||||
//! impl v1::OutputPin for OldOutputPinImpl {
|
||||
//! fn set_low(&mut self) { }
|
||||
//! fn set_high(&mut self) { }
|
||||
//! }
|
||||
//!
|
||||
//! struct NewOutputPinConsumer<T: v2::OutputPin> {
|
||||
//! _pin: T,
|
||||
//! }
|
||||
//!
|
||||
//! impl <T>NewOutputPinConsumer<T>
|
||||
//! where T: v2::OutputPin {
|
||||
//! pub fn new(pin: T) -> NewOutputPinConsumer<T> {
|
||||
//! NewOutputPinConsumer{ _pin: pin }
|
||||
//! }
|
||||
//! }
|
||||
//!
|
||||
//! fn main() {
|
||||
//! let pin = OldOutputPinImpl{};
|
||||
//! let _consumer = NewOutputPinConsumer::new(pin);
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
|
||||
#[allow(deprecated)]
|
||||
use super::v1;
|
||||
use super::v2;
|
||||
|
||||
/// Implementation of fallible `v2::OutputPin` for `v1::OutputPin` traits
|
||||
#[allow(deprecated)]
|
||||
impl<T> v2::OutputPin for T
|
||||
where
|
||||
T: v1::OutputPin,
|
||||
{
|
||||
// TODO: update to ! when never_type is stabilized
|
||||
type Error = ();
|
||||
|
||||
fn set_low(&mut self) -> Result<(), Self::Error> {
|
||||
Ok(self.set_low())
|
||||
}
|
||||
|
||||
fn set_high(&mut self) -> Result<(), Self::Error> {
|
||||
Ok(self.set_high())
|
||||
}
|
||||
}
|
||||
|
||||
/// Implementation of fallible `v2::StatefulOutputPin` for `v1::StatefulOutputPin` digital traits
|
||||
#[cfg(feature = "unproven")]
|
||||
#[allow(deprecated)]
|
||||
impl<T> v2::StatefulOutputPin for T
|
||||
where
|
||||
T: v1::StatefulOutputPin + v1::OutputPin,
|
||||
{
|
||||
fn is_set_low(&self) -> Result<bool, Self::Error> {
|
||||
Ok(self.is_set_low())
|
||||
}
|
||||
|
||||
fn is_set_high(&self) -> Result<bool, Self::Error> {
|
||||
Ok(self.is_set_high())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "unproven")]
|
||||
#[allow(deprecated)]
|
||||
impl<T> v2::toggleable::Default for T where T: v1::toggleable::Default {}
|
||||
|
||||
/// Implementation of fallible `v2::InputPin` for `v1::InputPin` digital traits
|
||||
#[cfg(feature = "unproven")]
|
||||
#[allow(deprecated)]
|
||||
impl<T> v2::InputPin for T
|
||||
where
|
||||
T: v1::InputPin,
|
||||
{
|
||||
// TODO: update to ! when never_type is stabilized
|
||||
type Error = ();
|
||||
|
||||
fn is_low(&self) -> Result<bool, Self::Error> {
|
||||
Ok(self.is_low())
|
||||
}
|
||||
|
||||
fn is_high(&self) -> Result<bool, Self::Error> {
|
||||
Ok(self.is_high())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
#[allow(deprecated)]
|
||||
use crate::digital::v1;
|
||||
use crate::digital::v2;
|
||||
|
||||
#[allow(deprecated)]
|
||||
struct OldOutputPinImpl {
|
||||
state: bool,
|
||||
}
|
||||
|
||||
#[allow(deprecated)]
|
||||
impl v1::OutputPin for OldOutputPinImpl {
|
||||
fn set_low(&mut self) {
|
||||
self.state = false;
|
||||
}
|
||||
fn set_high(&mut self) {
|
||||
self.state = true;
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(deprecated)]
|
||||
#[cfg(feature = "unproven")]
|
||||
impl v1::StatefulOutputPin for OldOutputPinImpl {
|
||||
fn is_set_low(&self) -> bool {
|
||||
self.state == false
|
||||
}
|
||||
|
||||
fn is_set_high(&self) -> bool {
|
||||
self.state == true
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(deprecated)]
|
||||
#[cfg(feature = "unproven")]
|
||||
impl v1::toggleable::Default for OldOutputPinImpl {}
|
||||
|
||||
struct NewOutputPinConsumer<T: v2::OutputPin> {
|
||||
_pin: T,
|
||||
}
|
||||
|
||||
impl<T> NewOutputPinConsumer<T>
|
||||
where
|
||||
T: v2::OutputPin,
|
||||
{
|
||||
pub fn new(pin: T) -> NewOutputPinConsumer<T> {
|
||||
NewOutputPinConsumer { _pin: pin }
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "unproven")]
|
||||
struct NewToggleablePinConsumer<T: v2::ToggleableOutputPin> {
|
||||
_pin: T,
|
||||
}
|
||||
|
||||
#[cfg(feature = "unproven")]
|
||||
impl<T> NewToggleablePinConsumer<T>
|
||||
where
|
||||
T: v2::ToggleableOutputPin,
|
||||
{
|
||||
pub fn new(pin: T) -> NewToggleablePinConsumer<T> {
|
||||
NewToggleablePinConsumer { _pin: pin }
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "unproven")]
|
||||
fn v2_v1_toggleable_implicit() {
|
||||
let i = OldOutputPinImpl { state: false };
|
||||
let _c = NewToggleablePinConsumer::new(i);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn v2_v1_output_implicit() {
|
||||
let i = OldOutputPinImpl { state: false };
|
||||
let _c = NewOutputPinConsumer::new(i);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn v2_v1_output_state() {
|
||||
let mut o = OldOutputPinImpl { state: false };
|
||||
|
||||
v2::OutputPin::set_high(&mut o).unwrap();
|
||||
assert_eq!(o.state, true);
|
||||
|
||||
v2::OutputPin::set_low(&mut o).unwrap();
|
||||
assert_eq!(o.state, false);
|
||||
}
|
||||
|
||||
#[cfg(feature = "unproven")]
|
||||
#[allow(deprecated)]
|
||||
struct OldInputPinImpl {
|
||||
state: bool,
|
||||
}
|
||||
|
||||
#[cfg(feature = "unproven")]
|
||||
#[allow(deprecated)]
|
||||
impl v1::InputPin for OldInputPinImpl {
|
||||
fn is_low(&self) -> bool {
|
||||
!self.state
|
||||
}
|
||||
fn is_high(&self) -> bool {
|
||||
self.state
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "unproven")]
|
||||
struct NewInputPinConsumer<T: v2::InputPin> {
|
||||
_pin: T,
|
||||
}
|
||||
|
||||
#[cfg(feature = "unproven")]
|
||||
impl<T> NewInputPinConsumer<T>
|
||||
where
|
||||
T: v2::InputPin,
|
||||
{
|
||||
pub fn new(pin: T) -> NewInputPinConsumer<T> {
|
||||
NewInputPinConsumer { _pin: pin }
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "unproven")]
|
||||
#[test]
|
||||
#[cfg(feature = "unproven")]
|
||||
fn v2_v1_input_implicit() {
|
||||
let i = OldInputPinImpl { state: false };
|
||||
let _c = NewInputPinConsumer::new(i);
|
||||
}
|
||||
|
||||
#[cfg(feature = "unproven")]
|
||||
#[test]
|
||||
fn v2_v1_input_state() {
|
||||
let mut i = OldInputPinImpl { state: false };
|
||||
|
||||
assert_eq!(v2::InputPin::is_high(&mut i).unwrap(), false);
|
||||
assert_eq!(v2::InputPin::is_low(&mut i).unwrap(), true);
|
||||
}
|
||||
}
|
||||
Vendored
+18
@@ -0,0 +1,18 @@
|
||||
//! Implementation of `core::fmt::Write` for the HAL's `serial::Write`.
|
||||
//!
|
||||
//! TODO write example of usage
|
||||
use core::fmt::{Result, Write};
|
||||
|
||||
impl<Word, Error> Write for dyn (::serial::Write<Word, Error = Error>)
|
||||
where
|
||||
Word: From<u8>,
|
||||
{
|
||||
fn write_str(&mut self, s: &str) -> Result {
|
||||
let _ = s
|
||||
.as_bytes()
|
||||
.into_iter()
|
||||
.map(|c| block!(self.write(Word::from(*c))))
|
||||
.last();
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
Vendored
+999
@@ -0,0 +1,999 @@
|
||||
//! A Hardware Abstraction Layer (HAL) for embedded systems
|
||||
//!
|
||||
//! **NOTE** This HAL is still is active development. Expect the traits presented here to be
|
||||
//! tweaked, split or be replaced wholesale before being stabilized, i.e. before hitting the 1.0.0
|
||||
//! release. That being said there's a part of the HAL that's currently considered unproven and is
|
||||
//! hidden behind an "unproven" Cargo feature. This API is even more volatile and it's exempt from
|
||||
//! semver rules: it can change in a non-backward compatible fashion or even disappear in between
|
||||
//! patch releases.
|
||||
//!
|
||||
//! # Design goals
|
||||
//!
|
||||
//! The HAL
|
||||
//!
|
||||
//! - Must *erase* device specific details. Neither register, register blocks or magic values should
|
||||
//! appear in the API.
|
||||
//!
|
||||
//! - Must be generic *within* a device and *across* devices. The API to use a serial interface must
|
||||
//! be the same regardless of whether the implementation uses the USART1 or UART4 peripheral of a
|
||||
//! device or the UART0 peripheral of another device.
|
||||
//!
|
||||
//! - Where possible must *not* be tied to a specific asynchronous model. The API should be usable
|
||||
//! in blocking mode, with the `futures` model, with an async/await model or with a callback model.
|
||||
//! (cf. the [`nb`] crate)
|
||||
//!
|
||||
//! - Must be minimal, and thus easy to implement and zero cost, yet highly composable. People that
|
||||
//! want higher level abstraction should *prefer to use this HAL* rather than *re-implement*
|
||||
//! register manipulation code.
|
||||
//!
|
||||
//! - Serve as a foundation for building an ecosystem of platform agnostic drivers. Here driver
|
||||
//! means a library crate that lets a target platform interface an external device like a digital
|
||||
//! sensor or a wireless transceiver. The advantage of this system is that by writing the driver as
|
||||
//! a generic library on top of `embedded-hal` driver authors can support any number of target
|
||||
//! platforms (e.g. Cortex-M microcontrollers, AVR microcontrollers, embedded Linux, etc.). The
|
||||
//! advantage for application developers is that by adopting `embedded-hal` they can unlock all
|
||||
//! these drivers for their platform.
|
||||
//!
|
||||
//! # Out of scope
|
||||
//!
|
||||
//! - Initialization and configuration stuff like "ensure this serial interface and that SPI
|
||||
//! interface are not using the same pins". The HAL will focus on *doing I/O*.
|
||||
//!
|
||||
//! # Reference implementation
|
||||
//!
|
||||
//! The [`stm32f30x-hal`] crate contains a reference implementation of this HAL.
|
||||
//!
|
||||
//! [`stm32f30x-hal`]: https://crates.io/crates/stm32f30x-hal/0.1.0
|
||||
//!
|
||||
//! # Platform agnostic drivers
|
||||
//!
|
||||
//! You can find platform agnostic drivers built on top of `embedded-hal` on crates.io by [searching
|
||||
//! for the *embedded-hal* keyword](https://crates.io/keywords/embedded-hal).
|
||||
//!
|
||||
//! If you writing a platform agnostic driver yourself you are highly encouraged to [add the
|
||||
//! embedded-hal keyword](https://doc.rust-lang.org/cargo/reference/manifest.html#package-metadata)
|
||||
//! to your crate before publishing it!
|
||||
//!
|
||||
//! # Detailed design
|
||||
//!
|
||||
//! ## Traits
|
||||
//!
|
||||
//! The HAL is specified as traits to allow generic programming. These traits make use of the
|
||||
//! [`nb`][] crate (*please go read that crate documentation before continuing*) to abstract over
|
||||
//! the asynchronous model and to also provide a blocking operation mode.
|
||||
//!
|
||||
//! [`nb`]: https://crates.io/crates/nb
|
||||
//!
|
||||
//! Here's how a HAL trait may look like:
|
||||
//!
|
||||
//! ```
|
||||
//! extern crate nb;
|
||||
//!
|
||||
//! /// A serial interface
|
||||
//! pub trait Serial {
|
||||
//! /// Error type associated to this serial interface
|
||||
//! type Error;
|
||||
//!
|
||||
//! /// Reads a single byte
|
||||
//! fn read(&mut self) -> nb::Result<u8, Self::Error>;
|
||||
//!
|
||||
//! /// Writes a single byte
|
||||
//! fn write(&mut self, byte: u8) -> nb::Result<(), Self::Error>;
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! The `nb::Result` enum is used to add a [`WouldBlock`] variant to the errors
|
||||
//! of the serial interface. As explained in the documentation of the `nb` crate this single API,
|
||||
//! when paired with the macros in the `nb` crate, can operate in a blocking manner, or in a
|
||||
//! non-blocking manner compatible with `futures` and with the `await!` operator.
|
||||
//!
|
||||
//! [`WouldBlock`]: https://docs.rs/nb/0.1.0/nb/enum.Error.html
|
||||
//!
|
||||
//! Some traits, like the one shown below, may expose possibly blocking APIs that can't fail. In
|
||||
//! those cases `nb::Result<_, Void>` is used.
|
||||
//!
|
||||
//! ```
|
||||
//! extern crate nb;
|
||||
//! extern crate void;
|
||||
//!
|
||||
//! use void::Void;
|
||||
//!
|
||||
//! /// A count down timer
|
||||
//! pub trait CountDown {
|
||||
//! // ..
|
||||
//!
|
||||
//! /// "waits" until the count down is over
|
||||
//! fn wait(&mut self) -> nb::Result<(), Void>;
|
||||
//! }
|
||||
//!
|
||||
//! # fn main() {}
|
||||
//! ```
|
||||
//!
|
||||
//! ## Suggested implementation
|
||||
//!
|
||||
//! The HAL traits should be implemented for device crates generated via [`svd2rust`] to maximize
|
||||
//! code reuse.
|
||||
//!
|
||||
//! [`svd2rust`]: https://crates.io/crates/svd2rust
|
||||
//!
|
||||
//! Shown below is an implementation of some of the HAL traits for the [`stm32f30x`] crate. This
|
||||
//! single implementation will work for *any* microcontroller in the STM32F30x family.
|
||||
//!
|
||||
//! [`stm32f30x`]: https://crates.io/crates/stm32f30x
|
||||
//!
|
||||
//! ```
|
||||
//! // crate: stm32f30x-hal
|
||||
//! // An implementation of the `embedded-hal` traits for STM32F30x microcontrollers
|
||||
//!
|
||||
//! extern crate embedded_hal as hal;
|
||||
//! extern crate nb;
|
||||
//!
|
||||
//! // device crate
|
||||
//! extern crate stm32f30x;
|
||||
//!
|
||||
//! use stm32f30x::USART1;
|
||||
//!
|
||||
//! /// A serial interface
|
||||
//! // NOTE generic over the USART peripheral
|
||||
//! pub struct Serial<USART> { usart: USART }
|
||||
//!
|
||||
//! // convenience type alias
|
||||
//! pub type Serial1 = Serial<USART1>;
|
||||
//!
|
||||
//! /// Serial interface error
|
||||
//! pub enum Error {
|
||||
//! /// Buffer overrun
|
||||
//! Overrun,
|
||||
//! // omitted: other error variants
|
||||
//! }
|
||||
//!
|
||||
//! impl hal::serial::Read<u8> for Serial<USART1> {
|
||||
//! type Error = Error;
|
||||
//!
|
||||
//! fn read(&mut self) -> nb::Result<u8, Error> {
|
||||
//! // read the status register
|
||||
//! let isr = self.usart.isr.read();
|
||||
//!
|
||||
//! if isr.ore().bit_is_set() {
|
||||
//! // Error: Buffer overrun
|
||||
//! Err(nb::Error::Other(Error::Overrun))
|
||||
//! }
|
||||
//! // omitted: checks for other errors
|
||||
//! else if isr.rxne().bit_is_set() {
|
||||
//! // Data available: read the data register
|
||||
//! Ok(self.usart.rdr.read().bits() as u8)
|
||||
//! } else {
|
||||
//! // No data available yet
|
||||
//! Err(nb::Error::WouldBlock)
|
||||
//! }
|
||||
//! }
|
||||
//! }
|
||||
//!
|
||||
//! impl hal::serial::Write<u8> for Serial<USART1> {
|
||||
//! type Error = Error;
|
||||
//!
|
||||
//! fn write(&mut self, byte: u8) -> nb::Result<(), Error> {
|
||||
//! // Similar to the `read` implementation
|
||||
//! # Ok(())
|
||||
//! }
|
||||
//!
|
||||
//! fn flush(&mut self) -> nb::Result<(), Error> {
|
||||
//! // Similar to the `read` implementation
|
||||
//! # Ok(())
|
||||
//! }
|
||||
//! }
|
||||
//!
|
||||
//! # fn main() {}
|
||||
//! ```
|
||||
//!
|
||||
//! ## Intended usage
|
||||
//!
|
||||
//! Thanks to the [`nb`] crate the HAL API can be used in a blocking manner,
|
||||
//! with `futures` or with the `await` operator using the [`block!`],
|
||||
//! [`try_nb!`] and [`await!`] macros respectively.
|
||||
//!
|
||||
//! [`block!`]: https://docs.rs/nb/0.1.0/nb/macro.block.html
|
||||
//! [`try_nb!`]: https://docs.rs/nb/0.1.0/nb/index.html#how-to-use-this-crate
|
||||
//! [`await!`]: https://docs.rs/nb/0.1.0/nb/index.html#how-to-use-this-crate
|
||||
//!
|
||||
//! ### Blocking mode
|
||||
//!
|
||||
//! An example of sending a string over the serial interface in a blocking
|
||||
//! fashion:
|
||||
//!
|
||||
//! ```
|
||||
//! extern crate embedded_hal;
|
||||
//! #[macro_use(block)]
|
||||
//! extern crate nb;
|
||||
//!
|
||||
//! use stm32f30x_hal::Serial1;
|
||||
//! use embedded_hal::serial::Write;
|
||||
//!
|
||||
//! # fn main() {
|
||||
//! let mut serial: Serial1 = {
|
||||
//! // ..
|
||||
//! # Serial1
|
||||
//! };
|
||||
//!
|
||||
//! for byte in b"Hello, world!" {
|
||||
//! // NOTE `block!` blocks until `serial.write()` completes and returns
|
||||
//! // `Result<(), Error>`
|
||||
//! block!(serial.write(*byte)).unwrap();
|
||||
//! }
|
||||
//! # }
|
||||
//!
|
||||
//! # mod stm32f30x_hal {
|
||||
//! # extern crate void;
|
||||
//! # use self::void::Void;
|
||||
//! # pub struct Serial1;
|
||||
//! # impl Serial1 {
|
||||
//! # pub fn write(&mut self, _: u8) -> ::nb::Result<(), Void> {
|
||||
//! # Ok(())
|
||||
//! # }
|
||||
//! # }
|
||||
//! # }
|
||||
//! ```
|
||||
//!
|
||||
//! ### `futures`
|
||||
//!
|
||||
//! An example of running two tasks concurrently. First task: blink an LED every
|
||||
//! second. Second task: loop back data over the serial interface.
|
||||
//!
|
||||
//! ```
|
||||
//! extern crate embedded_hal as hal;
|
||||
//! extern crate futures;
|
||||
//! extern crate void;
|
||||
//!
|
||||
//! #[macro_use(try_nb)]
|
||||
//! extern crate nb;
|
||||
//!
|
||||
//! use hal::prelude::*;
|
||||
//! use futures::{
|
||||
//! future,
|
||||
//! Async,
|
||||
//! Future,
|
||||
//! };
|
||||
//! use futures::future::Loop;
|
||||
//! use stm32f30x_hal::{Led, Serial1, Timer6};
|
||||
//! use void::Void;
|
||||
//!
|
||||
//! /// `futures` version of `CountDown.wait`
|
||||
//! ///
|
||||
//! /// This returns a future that must be polled to completion
|
||||
//! fn wait<T>(mut timer: T) -> impl Future<Item = T, Error = Void>
|
||||
//! where
|
||||
//! T: hal::timer::CountDown,
|
||||
//! {
|
||||
//! let mut timer = Some(timer);
|
||||
//! future::poll_fn(move || {
|
||||
//! try_nb!(timer.as_mut().unwrap().wait());
|
||||
//!
|
||||
//! Ok(Async::Ready(timer.take().unwrap()))
|
||||
//! })
|
||||
//! }
|
||||
//!
|
||||
//! /// `futures` version of `Serial.read`
|
||||
//! ///
|
||||
//! /// This returns a future that must be polled to completion
|
||||
//! fn read<S>(mut serial: S) -> impl Future<Item = (S, u8), Error = S::Error>
|
||||
//! where
|
||||
//! S: hal::serial::Read<u8>,
|
||||
//! {
|
||||
//! let mut serial = Some(serial);
|
||||
//! future::poll_fn(move || {
|
||||
//! let byte = try_nb!(serial.as_mut().unwrap().read());
|
||||
//!
|
||||
//! Ok(Async::Ready((serial.take().unwrap(), byte)))
|
||||
//! })
|
||||
//! }
|
||||
//!
|
||||
//! /// `futures` version of `Serial.write`
|
||||
//! ///
|
||||
//! /// This returns a future that must be polled to completion
|
||||
//! fn write<S>(mut serial: S, byte: u8) -> impl Future<Item = S, Error = S::Error>
|
||||
//! where
|
||||
//! S: hal::serial::Write<u8>,
|
||||
//! {
|
||||
//! let mut serial = Some(serial);
|
||||
//! future::poll_fn(move || {
|
||||
//! try_nb!(serial.as_mut().unwrap().write(byte));
|
||||
//!
|
||||
//! Ok(Async::Ready(serial.take().unwrap()))
|
||||
//! })
|
||||
//! }
|
||||
//!
|
||||
//! fn main() {
|
||||
//! // HAL implementers
|
||||
//! let timer: Timer6 = {
|
||||
//! // ..
|
||||
//! # Timer6
|
||||
//! };
|
||||
//! let serial: Serial1 = {
|
||||
//! // ..
|
||||
//! # Serial1
|
||||
//! };
|
||||
//! let led: Led = {
|
||||
//! // ..
|
||||
//! # Led
|
||||
//! };
|
||||
//!
|
||||
//! // Tasks
|
||||
//! let mut blinky = future::loop_fn::<_, (), _, _>(
|
||||
//! (led, timer, true),
|
||||
//! |(mut led, mut timer, state)| {
|
||||
//! wait(timer).map(move |timer| {
|
||||
//! if state {
|
||||
//! led.on();
|
||||
//! } else {
|
||||
//! led.off();
|
||||
//! }
|
||||
//!
|
||||
//! Loop::Continue((led, timer, !state))
|
||||
//! })
|
||||
//! });
|
||||
//!
|
||||
//! let mut loopback = future::loop_fn::<_, (), _, _>(serial, |mut serial| {
|
||||
//! read(serial).and_then(|(serial, byte)| {
|
||||
//! write(serial, byte)
|
||||
//! }).map(|serial| {
|
||||
//! Loop::Continue(serial)
|
||||
//! })
|
||||
//! });
|
||||
//!
|
||||
//! // Event loop
|
||||
//! loop {
|
||||
//! blinky.poll().unwrap(); // NOTE(unwrap) E = Void
|
||||
//! loopback.poll().unwrap();
|
||||
//! # break;
|
||||
//! }
|
||||
//! }
|
||||
//!
|
||||
//! # mod stm32f30x_hal {
|
||||
//! # extern crate void;
|
||||
//! # use self::void::Void;
|
||||
//! # pub struct Timer6;
|
||||
//! # impl ::hal::timer::CountDown for Timer6 {
|
||||
//! # type Time = ();
|
||||
//! #
|
||||
//! # fn start<T>(&mut self, _: T) where T: Into<()> {}
|
||||
//! # fn wait(&mut self) -> ::nb::Result<(), Void> { Err(::nb::Error::WouldBlock) }
|
||||
//! # }
|
||||
//! #
|
||||
//! # pub struct Serial1;
|
||||
//! # impl ::hal::serial::Read<u8> for Serial1 {
|
||||
//! # type Error = Void;
|
||||
//! # fn read(&mut self) -> ::nb::Result<u8, Void> { Err(::nb::Error::WouldBlock) }
|
||||
//! # }
|
||||
//! # impl ::hal::serial::Write<u8> for Serial1 {
|
||||
//! # type Error = Void;
|
||||
//! # fn flush(&mut self) -> ::nb::Result<(), Void> { Err(::nb::Error::WouldBlock) }
|
||||
//! # fn write(&mut self, _: u8) -> ::nb::Result<(), Void> { Err(::nb::Error::WouldBlock) }
|
||||
//! # }
|
||||
//! #
|
||||
//! # pub struct Led;
|
||||
//! # impl Led {
|
||||
//! # pub fn off(&mut self) {}
|
||||
//! # pub fn on(&mut self) {}
|
||||
//! # }
|
||||
//! # }
|
||||
//! ```
|
||||
//!
|
||||
//! ### `await`
|
||||
//!
|
||||
//! Same example as above but using `await!` instead of `futures`.
|
||||
//!
|
||||
//! ```
|
||||
//! #![feature(generator_trait)]
|
||||
//! #![feature(generators)]
|
||||
//!
|
||||
//! extern crate embedded_hal as hal;
|
||||
//!
|
||||
//! #[macro_use(await)]
|
||||
//! extern crate nb;
|
||||
//!
|
||||
//! use std::ops::Generator;
|
||||
//! use std::pin::Pin;
|
||||
//!
|
||||
//! use hal::prelude::*;
|
||||
//! use stm32f30x_hal::{Led, Serial1, Timer6};
|
||||
//!
|
||||
//! fn main() {
|
||||
//! // HAL implementers
|
||||
//! let mut timer: Timer6 = {
|
||||
//! // ..
|
||||
//! # Timer6
|
||||
//! };
|
||||
//! let mut serial: Serial1 = {
|
||||
//! // ..
|
||||
//! # Serial1
|
||||
//! };
|
||||
//! let mut led: Led = {
|
||||
//! // ..
|
||||
//! # Led
|
||||
//! };
|
||||
//!
|
||||
//! // Tasks
|
||||
//! let mut blinky = (move || {
|
||||
//! let mut state = false;
|
||||
//! loop {
|
||||
//! // `await!` means "suspend / yield here" instead of "block until
|
||||
//! // completion"
|
||||
//! await!(timer.wait()).unwrap(); // NOTE(unwrap) E = Void
|
||||
//!
|
||||
//! state = !state;
|
||||
//!
|
||||
//! if state {
|
||||
//! led.on();
|
||||
//! } else {
|
||||
//! led.off();
|
||||
//! }
|
||||
//! }
|
||||
//! });
|
||||
//!
|
||||
//! let mut loopback = (move || {
|
||||
//! loop {
|
||||
//! let byte = await!(serial.read()).unwrap();
|
||||
//! await!(serial.write(byte)).unwrap();
|
||||
//! }
|
||||
//! });
|
||||
//!
|
||||
//! // Event loop
|
||||
//! loop {
|
||||
//! Pin::new(&mut blinky).resume(());
|
||||
//! Pin::new(&mut loopback).resume(());
|
||||
//! # break;
|
||||
//! }
|
||||
//! }
|
||||
//!
|
||||
//! # mod stm32f30x_hal {
|
||||
//! # extern crate void;
|
||||
//! # use self::void::Void;
|
||||
//! # pub struct Serial1;
|
||||
//! # impl Serial1 {
|
||||
//! # pub fn read(&mut self) -> ::nb::Result<u8, Void> { Err(::nb::Error::WouldBlock) }
|
||||
//! # pub fn write(&mut self, _: u8) -> ::nb::Result<(), Void> { Err(::nb::Error::WouldBlock) }
|
||||
//! # }
|
||||
//! # pub struct Timer6;
|
||||
//! # impl Timer6 {
|
||||
//! # pub fn wait(&mut self) -> ::nb::Result<(), Void> { Err(::nb::Error::WouldBlock) }
|
||||
//! # }
|
||||
//! # pub struct Led;
|
||||
//! # impl Led {
|
||||
//! # pub fn off(&mut self) {}
|
||||
//! # pub fn on(&mut self) {}
|
||||
//! # }
|
||||
//! # }
|
||||
//! ```
|
||||
//!
|
||||
//! ## Generic programming and higher level abstractions
|
||||
//!
|
||||
//! The core of the HAL has been kept minimal on purpose to encourage building **generic** higher
|
||||
//! level abstractions on top of it. Some higher level abstractions that pick an asynchronous model
|
||||
//! or that have blocking behavior and that are deemed useful to build other abstractions can be
|
||||
//! found in the `blocking` module and, in the future, in the `futures` and `async` modules.
|
||||
//!
|
||||
//! Some examples:
|
||||
//!
|
||||
//! **NOTE** All the functions shown below could have been written as trait
|
||||
//! methods with default implementation to allow specialization, but they have
|
||||
//! been written as functions to keep things simple.
|
||||
//!
|
||||
//! - Write a whole buffer to a serial device in blocking a fashion.
|
||||
//!
|
||||
//! ```
|
||||
//! extern crate embedded_hal as hal;
|
||||
//! #[macro_use(block)]
|
||||
//! extern crate nb;
|
||||
//!
|
||||
//! use hal::prelude::*;
|
||||
//!
|
||||
//! fn write_all<S>(serial: &mut S, buffer: &[u8]) -> Result<(), S::Error>
|
||||
//! where
|
||||
//! S: hal::serial::Write<u8>
|
||||
//! {
|
||||
//! for &byte in buffer {
|
||||
//! block!(serial.write(byte))?;
|
||||
//! }
|
||||
//!
|
||||
//! Ok(())
|
||||
//! }
|
||||
//!
|
||||
//! # fn main() {}
|
||||
//! ```
|
||||
//!
|
||||
//! - Blocking serial read with timeout
|
||||
//!
|
||||
//! ```
|
||||
//! extern crate embedded_hal as hal;
|
||||
//! extern crate nb;
|
||||
//!
|
||||
//! use hal::prelude::*;
|
||||
//!
|
||||
//! enum Error<E> {
|
||||
//! /// Serial interface error
|
||||
//! Serial(E),
|
||||
//! TimedOut,
|
||||
//! }
|
||||
//!
|
||||
//! fn read_with_timeout<S, T>(
|
||||
//! serial: &mut S,
|
||||
//! timer: &mut T,
|
||||
//! timeout: T::Time,
|
||||
//! ) -> Result<u8, Error<S::Error>>
|
||||
//! where
|
||||
//! T: hal::timer::CountDown,
|
||||
//! S: hal::serial::Read<u8>,
|
||||
//! {
|
||||
//! timer.start(timeout);
|
||||
//!
|
||||
//! loop {
|
||||
//! match serial.read() {
|
||||
//! // raise error
|
||||
//! Err(nb::Error::Other(e)) => return Err(Error::Serial(e)),
|
||||
//! Err(nb::Error::WouldBlock) => {
|
||||
//! // no data available yet, check the timer below
|
||||
//! },
|
||||
//! Ok(byte) => return Ok(byte),
|
||||
//! }
|
||||
//!
|
||||
//! match timer.wait() {
|
||||
//! Err(nb::Error::Other(e)) => {
|
||||
//! // The error type specified by `timer.wait()` is `!`, which
|
||||
//! // means no error can actually occur. The Rust compiler
|
||||
//! // still forces us to provide this match arm, though.
|
||||
//! unreachable!()
|
||||
//! },
|
||||
//! // no timeout yet, try again
|
||||
//! Err(nb::Error::WouldBlock) => continue,
|
||||
//! Ok(()) => return Err(Error::TimedOut),
|
||||
//! }
|
||||
//! }
|
||||
//! }
|
||||
//!
|
||||
//! # fn main() {}
|
||||
//! ```
|
||||
//!
|
||||
//! - Asynchronous SPI transfer
|
||||
//!
|
||||
//! ```
|
||||
//! #![feature(conservative_impl_trait)]
|
||||
//! #![feature(generators)]
|
||||
//! #![feature(generator_trait)]
|
||||
//!
|
||||
//! extern crate embedded_hal as hal;
|
||||
//! #[macro_use(await)]
|
||||
//! extern crate nb;
|
||||
//!
|
||||
//! use std::ops::Generator;
|
||||
//!
|
||||
//! /// Transfers a byte buffer of size N
|
||||
//! ///
|
||||
//! /// Returns the same byte buffer but filled with the data received from the
|
||||
//! /// slave device
|
||||
//! fn transfer<S, B>(
|
||||
//! mut spi: S,
|
||||
//! mut buffer: [u8; 16], // NOTE this should be generic over the size of the array
|
||||
//! ) -> impl Generator<Return = Result<(S, [u8; 16]), S::Error>, Yield = ()>
|
||||
//! where
|
||||
//! S: hal::spi::FullDuplex<u8>,
|
||||
//! {
|
||||
//! move || {
|
||||
//! let n = buffer.len();
|
||||
//! for i in 0..n {
|
||||
//! await!(spi.send(buffer[i]))?;
|
||||
//! buffer[i] = await!(spi.read())?;
|
||||
//! }
|
||||
//!
|
||||
//! Ok((spi, buffer))
|
||||
//! }
|
||||
//! }
|
||||
//!
|
||||
//! # fn main() {}
|
||||
//! ```
|
||||
//!
|
||||
//! - Buffered serial interface with periodic flushing in interrupt handler
|
||||
//!
|
||||
//! ```
|
||||
//! extern crate embedded_hal as hal;
|
||||
//! extern crate nb;
|
||||
//! extern crate void;
|
||||
//!
|
||||
//! use hal::prelude::*;
|
||||
//! use void::Void;
|
||||
//!
|
||||
//! fn flush<S>(serial: &mut S, cb: &mut CircularBuffer)
|
||||
//! where
|
||||
//! S: hal::serial::Write<u8, Error = Void>,
|
||||
//! {
|
||||
//! loop {
|
||||
//! if let Some(byte) = cb.peek() {
|
||||
//! match serial.write(*byte) {
|
||||
//! Err(nb::Error::Other(_)) => unreachable!(),
|
||||
//! Err(nb::Error::WouldBlock) => return,
|
||||
//! Ok(()) => {}, // keep flushing data
|
||||
//! }
|
||||
//! }
|
||||
//!
|
||||
//! cb.pop();
|
||||
//! }
|
||||
//! }
|
||||
//!
|
||||
//! // The stuff below could be in some other crate
|
||||
//!
|
||||
//! /// Global singleton
|
||||
//! pub struct BufferedSerial1;
|
||||
//!
|
||||
//! // NOTE private
|
||||
//! static BUFFER1: Mutex<CircularBuffer> = {
|
||||
//! // ..
|
||||
//! # Mutex(CircularBuffer)
|
||||
//! };
|
||||
//! static SERIAL1: Mutex<Serial1> = {
|
||||
//! // ..
|
||||
//! # Mutex(Serial1)
|
||||
//! };
|
||||
//!
|
||||
//! impl BufferedSerial1 {
|
||||
//! pub fn write(&self, byte: u8) {
|
||||
//! self.write_all(&[byte])
|
||||
//! }
|
||||
//!
|
||||
//! pub fn write_all(&self, bytes: &[u8]) {
|
||||
//! let mut buffer = BUFFER1.lock();
|
||||
//! for byte in bytes {
|
||||
//! buffer.push(*byte).expect("buffer overrun");
|
||||
//! }
|
||||
//! // omitted: pend / enable interrupt_handler
|
||||
//! }
|
||||
//! }
|
||||
//!
|
||||
//! fn interrupt_handler() {
|
||||
//! let mut serial = SERIAL1.lock();
|
||||
//! let mut buffer = BUFFER1.lock();
|
||||
//!
|
||||
//! flush(&mut *serial, &mut buffer);
|
||||
//! }
|
||||
//!
|
||||
//! # struct Mutex<T>(T);
|
||||
//! # impl<T> Mutex<T> {
|
||||
//! # fn lock(&self) -> RefMut<T> { unimplemented!() }
|
||||
//! # }
|
||||
//! # struct RefMut<'a, T>(&'a mut T) where T: 'a;
|
||||
//! # impl<'a, T> ::std::ops::Deref for RefMut<'a, T> {
|
||||
//! # type Target = T;
|
||||
//! # fn deref(&self) -> &T { self.0 }
|
||||
//! # }
|
||||
//! # impl<'a, T> ::std::ops::DerefMut for RefMut<'a, T> {
|
||||
//! # fn deref_mut(&mut self) -> &mut T { self.0 }
|
||||
//! # }
|
||||
//! # struct Serial1;
|
||||
//! # impl ::hal::serial::Write<u8> for Serial1 {
|
||||
//! # type Error = Void;
|
||||
//! # fn write(&mut self, _: u8) -> nb::Result<(), Void> { Err(::nb::Error::WouldBlock) }
|
||||
//! # fn flush(&mut self) -> nb::Result<(), Void> { Err(::nb::Error::WouldBlock) }
|
||||
//! # }
|
||||
//! # struct CircularBuffer;
|
||||
//! # impl CircularBuffer {
|
||||
//! # pub fn peek(&mut self) -> Option<&u8> { None }
|
||||
//! # pub fn pop(&mut self) -> Option<u8> { None }
|
||||
//! # pub fn push(&mut self, _: u8) -> Result<(), ()> { Ok(()) }
|
||||
//! # }
|
||||
//!
|
||||
//! # fn main() {}
|
||||
//! ```
|
||||
|
||||
#![deny(missing_docs)]
|
||||
#![no_std]
|
||||
|
||||
#[macro_use]
|
||||
extern crate nb;
|
||||
extern crate void;
|
||||
|
||||
pub mod adc;
|
||||
pub mod blocking;
|
||||
pub mod can;
|
||||
pub mod digital;
|
||||
pub mod fmt;
|
||||
pub mod prelude;
|
||||
pub mod serial;
|
||||
pub mod spi;
|
||||
pub mod timer;
|
||||
pub mod watchdog;
|
||||
|
||||
/// Input capture
|
||||
///
|
||||
/// *This trait is available if embedded-hal is built with the `"unproven"` feature.*
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// You can use this interface to measure the period of (quasi) periodic signals
|
||||
/// / events
|
||||
///
|
||||
/// ```
|
||||
/// extern crate embedded_hal as hal;
|
||||
/// #[macro_use(block)]
|
||||
/// extern crate nb;
|
||||
///
|
||||
/// use hal::prelude::*;
|
||||
///
|
||||
/// fn main() {
|
||||
/// let mut capture: Capture1 = {
|
||||
/// // ..
|
||||
/// # Capture1
|
||||
/// };
|
||||
///
|
||||
/// capture.set_resolution(1.ms());
|
||||
///
|
||||
/// let before = block!(capture.capture(Channel::_1)).unwrap();
|
||||
/// let after = block!(capture.capture(Channel::_1)).unwrap();
|
||||
///
|
||||
/// let period = after.wrapping_sub(before);
|
||||
///
|
||||
/// println!("Period: {} ms", period);
|
||||
/// }
|
||||
///
|
||||
/// # extern crate void;
|
||||
/// # use void::Void;
|
||||
/// # struct MilliSeconds(u32);
|
||||
/// # trait U32Ext { fn ms(self) -> MilliSeconds; }
|
||||
/// # impl U32Ext for u32 { fn ms(self) -> MilliSeconds { MilliSeconds(self) } }
|
||||
/// # struct Capture1;
|
||||
/// # enum Channel { _1 }
|
||||
/// # impl hal::Capture for Capture1 {
|
||||
/// # type Capture = u16;
|
||||
/// # type Channel = Channel;
|
||||
/// # type Error = Void;
|
||||
/// # type Time = MilliSeconds;
|
||||
/// # fn capture(&mut self, _: Channel) -> ::nb::Result<u16, Void> { Ok(0) }
|
||||
/// # fn disable(&mut self, _: Channel) { unimplemented!() }
|
||||
/// # fn enable(&mut self, _: Channel) { unimplemented!() }
|
||||
/// # fn get_resolution(&self) -> MilliSeconds { unimplemented!() }
|
||||
/// # fn set_resolution<T>(&mut self, _: T) where T: Into<MilliSeconds> {}
|
||||
/// # }
|
||||
/// ```
|
||||
#[cfg(feature = "unproven")]
|
||||
// reason: pre-singletons API. With singletons a `CapturePin` (cf. `PwmPin`) trait seems more
|
||||
// appropriate
|
||||
pub trait Capture {
|
||||
/// Enumeration of `Capture` errors
|
||||
///
|
||||
/// Possible errors:
|
||||
///
|
||||
/// - *overcapture*, the previous capture value was overwritten because it
|
||||
/// was not read in a timely manner
|
||||
type Error;
|
||||
|
||||
/// Enumeration of channels that can be used with this `Capture` interface
|
||||
///
|
||||
/// If your `Capture` interface has no channels you can use the type `()`
|
||||
/// here
|
||||
type Channel;
|
||||
|
||||
/// A time unit that can be converted into a human time unit (e.g. seconds)
|
||||
type Time;
|
||||
|
||||
/// The type of the value returned by `capture`
|
||||
type Capture;
|
||||
|
||||
/// "Waits" for a transition in the capture `channel` and returns the value
|
||||
/// of counter at that instant
|
||||
///
|
||||
/// NOTE that you must multiply the returned value by the *resolution* of
|
||||
/// this `Capture` interface to get a human time unit (e.g. seconds)
|
||||
fn capture(&mut self, channel: Self::Channel) -> nb::Result<Self::Capture, Self::Error>;
|
||||
|
||||
/// Disables a capture `channel`
|
||||
fn disable(&mut self, channel: Self::Channel);
|
||||
|
||||
/// Enables a capture `channel`
|
||||
fn enable(&mut self, channel: Self::Channel);
|
||||
|
||||
/// Returns the current resolution
|
||||
fn get_resolution(&self) -> Self::Time;
|
||||
|
||||
/// Sets the resolution of the capture timer
|
||||
fn set_resolution<R>(&mut self, resolution: R)
|
||||
where
|
||||
R: Into<Self::Time>;
|
||||
}
|
||||
|
||||
/// Pulse Width Modulation
|
||||
///
|
||||
/// *This trait is available if embedded-hal is built with the `"unproven"` feature.*
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Use this interface to control the power output of some actuator
|
||||
///
|
||||
/// ```
|
||||
/// extern crate embedded_hal as hal;
|
||||
///
|
||||
/// use hal::prelude::*;
|
||||
///
|
||||
/// fn main() {
|
||||
/// let mut pwm: Pwm1 = {
|
||||
/// // ..
|
||||
/// # Pwm1
|
||||
/// };
|
||||
///
|
||||
/// pwm.set_period(1.khz());
|
||||
///
|
||||
/// let max_duty = pwm.get_max_duty();
|
||||
///
|
||||
/// // brightest LED
|
||||
/// pwm.set_duty(Channel::_1, max_duty);
|
||||
///
|
||||
/// // dimmer LED
|
||||
/// pwm.set_duty(Channel::_2, max_duty / 4);
|
||||
/// }
|
||||
///
|
||||
/// # struct KiloHertz(u32);
|
||||
/// # trait U32Ext { fn khz(self) -> KiloHertz; }
|
||||
/// # impl U32Ext for u32 { fn khz(self) -> KiloHertz { KiloHertz(self) } }
|
||||
/// # enum Channel { _1, _2 }
|
||||
/// # struct Pwm1;
|
||||
/// # impl hal::Pwm for Pwm1 {
|
||||
/// # type Channel = Channel;
|
||||
/// # type Time = KiloHertz;
|
||||
/// # type Duty = u16;
|
||||
/// # fn disable(&mut self, _: Channel) { unimplemented!() }
|
||||
/// # fn enable(&mut self, _: Channel) { unimplemented!() }
|
||||
/// # fn get_duty(&self, _: Channel) -> u16 { unimplemented!() }
|
||||
/// # fn get_max_duty(&self) -> u16 { 0 }
|
||||
/// # fn set_duty(&mut self, _: Channel, _: u16) {}
|
||||
/// # fn get_period(&self) -> KiloHertz { unimplemented!() }
|
||||
/// # fn set_period<T>(&mut self, _: T) where T: Into<KiloHertz> {}
|
||||
/// # }
|
||||
/// ```
|
||||
#[cfg(feature = "unproven")]
|
||||
// reason: pre-singletons API. The `PwmPin` trait seems more useful because it models independent
|
||||
// PWM channels. Here a certain number of channels are multiplexed in a single implementer.
|
||||
pub trait Pwm {
|
||||
/// Enumeration of channels that can be used with this `Pwm` interface
|
||||
///
|
||||
/// If your `Pwm` interface has no channels you can use the type `()`
|
||||
/// here
|
||||
type Channel;
|
||||
|
||||
/// A time unit that can be converted into a human time unit (e.g. seconds)
|
||||
type Time;
|
||||
|
||||
/// Type for the `duty` methods
|
||||
///
|
||||
/// The implementer is free to choose a float / percentage representation
|
||||
/// (e.g. `0.0 .. 1.0`) or an integer representation (e.g. `0 .. 65535`)
|
||||
type Duty;
|
||||
|
||||
/// Disables a PWM `channel`
|
||||
fn disable(&mut self, channel: Self::Channel);
|
||||
|
||||
/// Enables a PWM `channel`
|
||||
fn enable(&mut self, channel: Self::Channel);
|
||||
|
||||
/// Returns the current PWM period
|
||||
fn get_period(&self) -> Self::Time;
|
||||
|
||||
/// Returns the current duty cycle
|
||||
fn get_duty(&self, channel: Self::Channel) -> Self::Duty;
|
||||
|
||||
/// Returns the maximum duty cycle value
|
||||
fn get_max_duty(&self) -> Self::Duty;
|
||||
|
||||
/// Sets a new duty cycle
|
||||
fn set_duty(&mut self, channel: Self::Channel, duty: Self::Duty);
|
||||
|
||||
/// Sets a new PWM period
|
||||
fn set_period<P>(&mut self, period: P)
|
||||
where
|
||||
P: Into<Self::Time>;
|
||||
}
|
||||
|
||||
/// A single PWM channel / pin
|
||||
///
|
||||
/// See `Pwm` for details
|
||||
pub trait PwmPin {
|
||||
/// Type for the `duty` methods
|
||||
///
|
||||
/// The implementer is free to choose a float / percentage representation
|
||||
/// (e.g. `0.0 .. 1.0`) or an integer representation (e.g. `0 .. 65535`)
|
||||
type Duty;
|
||||
|
||||
/// Disables a PWM `channel`
|
||||
fn disable(&mut self);
|
||||
|
||||
/// Enables a PWM `channel`
|
||||
fn enable(&mut self);
|
||||
|
||||
/// Returns the current duty cycle
|
||||
fn get_duty(&self) -> Self::Duty;
|
||||
|
||||
/// Returns the maximum duty cycle value
|
||||
fn get_max_duty(&self) -> Self::Duty;
|
||||
|
||||
/// Sets a new duty cycle
|
||||
fn set_duty(&mut self, duty: Self::Duty);
|
||||
}
|
||||
|
||||
/// Quadrature encoder interface
|
||||
///
|
||||
/// *This trait is available if embedded-hal is built with the `"unproven"` feature.*
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// You can use this interface to measure the speed of a motor
|
||||
///
|
||||
/// ```
|
||||
/// extern crate embedded_hal as hal;
|
||||
/// #[macro_use(block)]
|
||||
/// extern crate nb;
|
||||
///
|
||||
/// use hal::prelude::*;
|
||||
///
|
||||
/// fn main() {
|
||||
/// let mut qei: Qei1 = {
|
||||
/// // ..
|
||||
/// # Qei1
|
||||
/// };
|
||||
/// let mut timer: Timer6 = {
|
||||
/// // ..
|
||||
/// # Timer6
|
||||
/// };
|
||||
///
|
||||
///
|
||||
/// let before = qei.count();
|
||||
/// timer.start(1.s());
|
||||
/// block!(timer.wait());
|
||||
/// let after = qei.count();
|
||||
///
|
||||
/// let speed = after.wrapping_sub(before);
|
||||
/// println!("Speed: {} pulses per second", speed);
|
||||
/// }
|
||||
///
|
||||
/// # extern crate void;
|
||||
/// # use void::Void;
|
||||
/// # struct Seconds(u32);
|
||||
/// # trait U32Ext { fn s(self) -> Seconds; }
|
||||
/// # impl U32Ext for u32 { fn s(self) -> Seconds { Seconds(self) } }
|
||||
/// # struct Qei1;
|
||||
/// # impl hal::Qei for Qei1 {
|
||||
/// # type Count = u16;
|
||||
/// # fn count(&self) -> u16 { 0 }
|
||||
/// # fn direction(&self) -> ::hal::Direction { unimplemented!() }
|
||||
/// # }
|
||||
/// # struct Timer6;
|
||||
/// # impl hal::timer::CountDown for Timer6 {
|
||||
/// # type Time = Seconds;
|
||||
/// # fn start<T>(&mut self, _: T) where T: Into<Seconds> {}
|
||||
/// # fn wait(&mut self) -> ::nb::Result<(), Void> { Ok(()) }
|
||||
/// # }
|
||||
/// ```
|
||||
#[cfg(feature = "unproven")]
|
||||
// reason: needs to be re-evaluated in the new singletons world. At the very least this needs a
|
||||
// reference implementation
|
||||
pub trait Qei {
|
||||
/// The type of the value returned by `count`
|
||||
type Count;
|
||||
|
||||
/// Returns the current pulse count of the encoder
|
||||
fn count(&self) -> Self::Count;
|
||||
|
||||
/// Returns the count direction
|
||||
fn direction(&self) -> Direction;
|
||||
}
|
||||
|
||||
/// Count direction
|
||||
///
|
||||
/// *This enumeration is available if embedded-hal is built with the `"unproven"` feature.*
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||
#[cfg(feature = "unproven")]
|
||||
// reason: part of the unproven `Qei` interface
|
||||
pub enum Direction {
|
||||
/// 3, 2, 1
|
||||
Downcounting,
|
||||
/// 1, 2, 3
|
||||
Upcounting,
|
||||
}
|
||||
|
||||
mod private {
|
||||
pub trait Sealed {}
|
||||
}
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
//! The prelude is a collection of all the traits in this crate
|
||||
//!
|
||||
//! The traits have been renamed to avoid collisions with other items when
|
||||
//! performing a glob import.
|
||||
|
||||
#[cfg(feature = "unproven")]
|
||||
pub use adc::OneShot as _embedded_hal_adc_OneShot;
|
||||
pub use blocking::delay::DelayMs as _embedded_hal_blocking_delay_DelayMs;
|
||||
pub use blocking::delay::DelayUs as _embedded_hal_blocking_delay_DelayUs;
|
||||
pub use blocking::i2c::{
|
||||
Read as _embedded_hal_blocking_i2c_Read, Write as _embedded_hal_blocking_i2c_Write,
|
||||
WriteRead as _embedded_hal_blocking_i2c_WriteRead,
|
||||
};
|
||||
#[cfg(feature = "unproven")]
|
||||
pub use blocking::rng::Read as _embedded_hal_blocking_rng_Read;
|
||||
pub use blocking::serial::Write as _embedded_hal_blocking_serial_Write;
|
||||
pub use blocking::spi::{
|
||||
Transfer as _embedded_hal_blocking_spi_Transfer, Write as _embedded_hal_blocking_spi_Write,
|
||||
};
|
||||
#[allow(deprecated)]
|
||||
#[cfg(feature = "unproven")]
|
||||
pub use digital::InputPin as _embedded_hal_digital_InputPin;
|
||||
#[allow(deprecated)]
|
||||
pub use digital::OutputPin as _embedded_hal_digital_OutputPin;
|
||||
#[cfg(feature = "unproven")]
|
||||
#[allow(deprecated)]
|
||||
pub use digital::ToggleableOutputPin as _embedded_hal_digital_ToggleableOutputPin;
|
||||
pub use serial::Read as _embedded_hal_serial_Read;
|
||||
pub use serial::Write as _embedded_hal_serial_Write;
|
||||
pub use spi::FullDuplex as _embedded_hal_spi_FullDuplex;
|
||||
pub use timer::CountDown as _embedded_hal_timer_CountDown;
|
||||
#[cfg(feature = "unproven")]
|
||||
pub use watchdog::Watchdog as _embedded_hal_watchdog_Watchdog;
|
||||
#[cfg(feature = "unproven")]
|
||||
pub use watchdog::WatchdogDisable as _embedded_hal_watchdog_WatchdogDisable;
|
||||
#[cfg(feature = "unproven")]
|
||||
pub use watchdog::WatchdogEnable as _embedded_hal_watchdog_WatchdogEnable;
|
||||
#[cfg(feature = "unproven")]
|
||||
pub use Capture as _embedded_hal_Capture;
|
||||
#[cfg(feature = "unproven")]
|
||||
pub use Pwm as _embedded_hal_Pwm;
|
||||
pub use PwmPin as _embedded_hal_PwmPin;
|
||||
#[cfg(feature = "unproven")]
|
||||
pub use Qei as _embedded_hal_Qei;
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
//! Serial interface
|
||||
|
||||
use nb;
|
||||
|
||||
/// Read half of a serial interface
|
||||
///
|
||||
/// Some serial interfaces support different data sizes (8 bits, 9 bits, etc.);
|
||||
/// This can be encoded in this trait via the `Word` type parameter.
|
||||
pub trait Read<Word> {
|
||||
/// Read error
|
||||
type Error;
|
||||
|
||||
/// Reads a single word from the serial interface
|
||||
fn read(&mut self) -> nb::Result<Word, Self::Error>;
|
||||
}
|
||||
|
||||
/// Write half of a serial interface
|
||||
pub trait Write<Word> {
|
||||
/// Write error
|
||||
type Error;
|
||||
|
||||
/// Writes a single word to the serial interface
|
||||
fn write(&mut self, word: Word) -> nb::Result<(), Self::Error>;
|
||||
|
||||
/// Ensures that none of the previously written words are still buffered
|
||||
fn flush(&mut self) -> nb::Result<(), Self::Error>;
|
||||
}
|
||||
Vendored
+78
@@ -0,0 +1,78 @@
|
||||
//! Serial Peripheral Interface
|
||||
|
||||
use nb;
|
||||
|
||||
/// Full duplex (master mode)
|
||||
///
|
||||
/// # Notes
|
||||
///
|
||||
/// - It's the task of the user of this interface to manage the slave select lines
|
||||
///
|
||||
/// - Due to how full duplex SPI works each `read` call must be preceded by a `send` call.
|
||||
///
|
||||
/// - Some SPIs can work with 8-bit *and* 16-bit words. You can overload this trait with different
|
||||
/// `Word` types to allow operation in both modes.
|
||||
pub trait FullDuplex<Word> {
|
||||
/// An enumeration of SPI errors
|
||||
type Error;
|
||||
|
||||
/// Reads the word stored in the shift register
|
||||
///
|
||||
/// **NOTE** A word must be sent to the slave before attempting to call this
|
||||
/// method.
|
||||
fn read(&mut self) -> nb::Result<Word, Self::Error>;
|
||||
|
||||
/// Sends a word to the slave
|
||||
fn send(&mut self, word: Word) -> nb::Result<(), Self::Error>;
|
||||
}
|
||||
|
||||
/// Clock polarity
|
||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||
pub enum Polarity {
|
||||
/// Clock signal low when idle
|
||||
IdleLow,
|
||||
/// Clock signal high when idle
|
||||
IdleHigh,
|
||||
}
|
||||
|
||||
/// Clock phase
|
||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||
pub enum Phase {
|
||||
/// Data in "captured" on the first clock transition
|
||||
CaptureOnFirstTransition,
|
||||
/// Data in "captured" on the second clock transition
|
||||
CaptureOnSecondTransition,
|
||||
}
|
||||
|
||||
/// SPI mode
|
||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||
pub struct Mode {
|
||||
/// Clock polarity
|
||||
pub polarity: Polarity,
|
||||
/// Clock phase
|
||||
pub phase: Phase,
|
||||
}
|
||||
|
||||
/// Helper for CPOL = 0, CPHA = 0
|
||||
pub const MODE_0: Mode = Mode {
|
||||
polarity: Polarity::IdleLow,
|
||||
phase: Phase::CaptureOnFirstTransition,
|
||||
};
|
||||
|
||||
/// Helper for CPOL = 0, CPHA = 1
|
||||
pub const MODE_1: Mode = Mode {
|
||||
polarity: Polarity::IdleLow,
|
||||
phase: Phase::CaptureOnSecondTransition,
|
||||
};
|
||||
|
||||
/// Helper for CPOL = 1, CPHA = 0
|
||||
pub const MODE_2: Mode = Mode {
|
||||
polarity: Polarity::IdleHigh,
|
||||
phase: Phase::CaptureOnFirstTransition,
|
||||
};
|
||||
|
||||
/// Helper for CPOL = 1, CPHA = 1
|
||||
pub const MODE_3: Mode = Mode {
|
||||
polarity: Polarity::IdleHigh,
|
||||
phase: Phase::CaptureOnSecondTransition,
|
||||
};
|
||||
+95
@@ -0,0 +1,95 @@
|
||||
//! Timers
|
||||
|
||||
use nb;
|
||||
use void::Void;
|
||||
|
||||
/// A count down timer
|
||||
///
|
||||
/// # Contract
|
||||
///
|
||||
/// - `self.start(count); block!(self.wait());` MUST block for AT LEAST the time specified by
|
||||
/// `count`.
|
||||
///
|
||||
/// *Note* that the implementer doesn't necessarily have to be a *downcounting* timer; it could also
|
||||
/// be an *upcounting* timer as long as the above contract is upheld.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// You can use this timer to create delays
|
||||
///
|
||||
/// ```
|
||||
/// extern crate embedded_hal as hal;
|
||||
/// #[macro_use(block)]
|
||||
/// extern crate nb;
|
||||
///
|
||||
/// use hal::prelude::*;
|
||||
///
|
||||
/// fn main() {
|
||||
/// let mut led: Led = {
|
||||
/// // ..
|
||||
/// # Led
|
||||
/// };
|
||||
/// let mut timer: Timer6 = {
|
||||
/// // ..
|
||||
/// # Timer6
|
||||
/// };
|
||||
///
|
||||
/// Led.on();
|
||||
/// timer.start(1.s());
|
||||
/// block!(timer.wait()); // blocks for 1 second
|
||||
/// Led.off();
|
||||
/// }
|
||||
///
|
||||
/// # extern crate void;
|
||||
/// # use void::Void;
|
||||
/// # struct Seconds(u32);
|
||||
/// # trait U32Ext { fn s(self) -> Seconds; }
|
||||
/// # impl U32Ext for u32 { fn s(self) -> Seconds { Seconds(self) } }
|
||||
/// # struct Led;
|
||||
/// # impl Led {
|
||||
/// # pub fn off(&mut self) {}
|
||||
/// # pub fn on(&mut self) {}
|
||||
/// # }
|
||||
/// # struct Timer6;
|
||||
/// # impl hal::timer::CountDown for Timer6 {
|
||||
/// # type Time = Seconds;
|
||||
/// # fn start<T>(&mut self, _: T) where T: Into<Seconds> {}
|
||||
/// # fn wait(&mut self) -> ::nb::Result<(), Void> { Ok(()) }
|
||||
/// # }
|
||||
/// ```
|
||||
pub trait CountDown {
|
||||
/// The unit of time used by this timer
|
||||
type Time;
|
||||
|
||||
/// Starts a new count down
|
||||
fn start<T>(&mut self, count: T)
|
||||
where
|
||||
T: Into<Self::Time>;
|
||||
|
||||
/// Non-blockingly "waits" until the count down finishes
|
||||
///
|
||||
/// # Contract
|
||||
///
|
||||
/// - If `Self: Periodic`, the timer will start a new count down right after the last one
|
||||
/// finishes.
|
||||
/// - Otherwise the behavior of calling `wait` after the last call returned `Ok` is UNSPECIFIED.
|
||||
/// Implementers are suggested to panic on this scenario to signal a programmer error.
|
||||
fn wait(&mut self) -> nb::Result<(), Void>;
|
||||
}
|
||||
|
||||
/// Marker trait that indicates that a timer is periodic
|
||||
pub trait Periodic {}
|
||||
|
||||
/// Trait for cancelable countdowns.
|
||||
pub trait Cancel: CountDown {
|
||||
/// Error returned when a countdown can't be canceled.
|
||||
type Error;
|
||||
|
||||
/// Tries to cancel this countdown.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// An error will be returned if the countdown has already been canceled or was never started.
|
||||
/// An error is also returned if the countdown is not `Periodic` and has already expired.
|
||||
fn cancel(&mut self) -> Result<(), Self::Error>;
|
||||
}
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
//! Traits for interactions with a processors watchdog timer.
|
||||
|
||||
/// Feeds an existing watchdog to ensure the processor isn't reset. Sometimes
|
||||
/// commonly referred to as "kicking" or "refreshing".
|
||||
#[cfg(feature = "unproven")]
|
||||
pub trait Watchdog {
|
||||
/// Triggers the watchdog. This must be done once the watchdog is started
|
||||
/// to prevent the processor being reset.
|
||||
fn feed(&mut self);
|
||||
}
|
||||
|
||||
/// Enables A watchdog timer to reset the processor if software is frozen or
|
||||
/// stalled.
|
||||
#[cfg(feature = "unproven")]
|
||||
pub trait WatchdogEnable {
|
||||
/// Unit of time used by the watchdog
|
||||
type Time;
|
||||
/// Starts the watchdog with a given period, typically once this is done
|
||||
/// the watchdog needs to be kicked periodically or the processor is reset.
|
||||
fn start<T>(&mut self, period: T)
|
||||
where
|
||||
T: Into<Self::Time>;
|
||||
}
|
||||
|
||||
/// Disables a running watchdog timer so the processor won't be reset.
|
||||
#[cfg(feature = "unproven")]
|
||||
pub trait WatchdogDisable {
|
||||
/// Disables the watchdog
|
||||
fn disable(&mut self);
|
||||
}
|
||||
Reference in new issue
Block a user