diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index c9a13ab0f..fcca011ea 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1 +1 @@ -* @rust-embedded/hal @ilya-epifanov @thejpster +* @rust-embedded/hal diff --git a/.github/bors.toml b/.github/bors.toml index ca42be0a5..c646f2461 100644 --- a/.github/bors.toml +++ b/.github/bors.toml @@ -1,4 +1,10 @@ block_labels = ["needs-decision"] delete_merged_branches = true required_approvals = 1 -status = ["continuous-integration/travis-ci/push"] +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", +] diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 000000000..1df8088de --- /dev/null +++ b/.github/workflows/ci.yml @@ -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 + diff --git a/.github/workflows/clippy.yml b/.github/workflows/clippy.yml new file mode 100644 index 000000000..adc3a6ed1 --- /dev/null +++ b/.github/workflows/clippy.yml @@ -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 }} diff --git a/.github/workflows/rustfmt.yml b/.github/workflows/rustfmt.yml new file mode 100644 index 000000000..80439edbd --- /dev/null +++ b/.github/workflows/rustfmt.yml @@ -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 diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 000000000..ff46d17df --- /dev/null +++ b/.github/workflows/test.yml @@ -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 diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index a7cc51a5f..000000000 --- a/.travis.yml +++ /dev/null @@ -1,46 +0,0 @@ -language: rust - -matrix: - include: - - env: TARGET=x86_64-unknown-linux-gnu - if: (branch = staging OR branch = trying) OR (type = pull_request AND branch = master) - - - env: TARGET=thumbv6m-none-eabi - rust: beta - if: (branch = staging OR branch = trying) OR (type = pull_request AND branch = master) - - - env: TARGET=thumbv7m-none-eabi - rust: beta - if: (branch = staging OR branch = trying) OR (type = pull_request AND branch = master) - - - env: TARGET=x86_64-unknown-linux-gnu - rust: nightly - if: (branch = staging OR branch = trying) OR (type = pull_request AND branch = master) - -before_install: set -e - -install: - - bash ci/install.sh - -script: - - bash ci/script.sh - -after_script: set +e - -after_success: - - bash ci/after_success.sh - -cache: cargo -before_cache: - # Travis can't cache files that are not readable by "others" - - chmod -R a+r $HOME/.cargo - -branches: - only: - - master - - staging - - trying - -notifications: - email: - on_success: never diff --git a/CHANGELOG.md b/CHANGELOG.md index 4d7261970..7fc2bbecc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,11 +7,43 @@ 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 @@ -98,8 +130,14 @@ and this project adheres to [Semantic Versioning](http://semver.org/). Initial release -[Unreleased]: https://github.com/japaric/embedded-hal/compare/v0.2.1...HEAD -[v0.2.1]: https://github.com/japaric/embedded-hal/compare/v0.2.0...v0.2.1 -[v0.2.0]: https://github.com/japaric/embedded-hal/compare/v0.1.2...v0.2.0 -[v0.1.2]: https://github.com/japaric/embedded-hal/compare/v0.1.1...v0.1.2 -[v0.1.1]: https://github.com/japaric/embedded-hal/compare/v0.1.0...v0.1.1 +[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 diff --git a/Cargo.toml b/Cargo.toml index 239db690e..8b9249410 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,6 @@ [package] authors = [ + "The Embedded HAL Team ", "Jorge Aparicio ", "Jonathan 'theJPster' Pallant " ] @@ -10,18 +11,18 @@ keywords = ["hal", "IO"] license = "MIT OR Apache-2.0" name = "embedded-hal" readme = "README.md" -repository = "https://github.com/japaric/embedded-hal" -version = "0.2.3" +repository = "https://github.com/rust-embedded/embedded-hal" +version = "0.2.7" [dependencies.void] default-features = false version = "1.0.2" [dependencies.nb] -version = "0.1.1" +version = "0.1.3" [dev-dependencies] -stm32f30x = "0.6.0" +stm32f30x = "0.8.0" futures = "0.1.17" [features] diff --git a/ci/after_success.sh b/ci/after_success.sh deleted file mode 100644 index c44255907..000000000 --- a/ci/after_success.sh +++ /dev/null @@ -1,20 +0,0 @@ -set -euxo pipefail - -main() { - cargo doc --target $TARGET - - mkdir ghp-import - - curl -Ls https://github.com/davisp/ghp-import/archive/master.tar.gz | \ - tar --strip-components 1 -C ghp-import -xz - - ./ghp-import/ghp_import.py target/$TARGET/doc - - set +x - git push -fq https://$GH_TOKEN@github.com/$TRAVIS_REPO_SLUG.git gh-pages && \ - echo OK -} - -if [ "$TRAVIS_EVENT_TYPE" == "push" ] && [ "$TRAVIS_BRANCH" == "master" ]; then - main -fi diff --git a/ci/install.sh b/ci/install.sh deleted file mode 100644 index 3c4192119..000000000 --- a/ci/install.sh +++ /dev/null @@ -1,9 +0,0 @@ -set -euxo pipefail - -main() { - if [ $TARGET != x86_64-unknown-linux-gnu ]; then - rustup target add $TARGET - fi -} - -main diff --git a/ci/script.sh b/ci/script.sh deleted file mode 100644 index 12c05e27b..000000000 --- a/ci/script.sh +++ /dev/null @@ -1,12 +0,0 @@ -set -euxo pipefail - -main() { - cargo check --target $TARGET - cargo check --target $TARGET --features unproven - - if [ $TRAVIS_RUST_VERSION = nightly ]; then - cargo test --target $TARGET --features unproven - fi -} - -main diff --git a/src/blocking/can.rs b/src/blocking/can.rs new file mode 100644 index 000000000..b13885abe --- /dev/null +++ b/src/blocking/can.rs @@ -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; +} diff --git a/src/blocking/i2c.rs b/src/blocking/i2c.rs index 1c654f770..60bb1538d 100644 --- a/src/blocking/i2c.rs +++ b/src/blocking/i2c.rs @@ -1,12 +1,122 @@ //! Blocking I2C API //! -//! Slave addresses used by this API are 7-bit I2C addresses ranging from 0 to 127. +//! 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`. //! -//! Operations on 10-bit slave addresses are not supported by the API yet (but applications might -//! be able to emulate some operations). +//! 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 for I2c0 +//! { +//! # type Error = (); +//! # +//! fn write(&mut self, addr: u8, output: &[u8]) -> Result<(), Self::Error> { +//! // ... +//! # Ok(()) +//! } +//! } +//! +//! impl Write 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, +//! } +//! +//! impl TemperatureSensorDriver +//! where +//! I2C: WriteRead, +//! { +//! pub fn read_temperature(&mut self) -> Result { +//! 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, +//! } +//! +//! impl TemperatureSensorDriver +//! where +//! I2C: WriteRead, +//! { +//! pub fn read_temperature(&mut self) -> Result { +//! 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 { +pub trait Read { /// Error type type Error; @@ -28,15 +138,15 @@ pub trait Read { /// - `MAK` = master acknowledge /// - `NMAK` = master no acknowledge /// - `SP` = stop condition - fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Self::Error>; + fn read(&mut self, address: A, buffer: &mut [u8]) -> Result<(), Self::Error>; } /// Blocking write -pub trait Write { +pub trait Write { /// Error type type Error; - /// Sends bytes to slave with address `addr` + /// Writes bytes to slave with address `address` /// /// # I2C Events (contract) /// @@ -52,31 +162,30 @@ pub trait Write { /// - `SAK` = slave acknowledge /// - `Bi` = ith byte of data /// - `SP` = stop condition - fn write(&mut self, addr: u8, bytes: &[u8]) -> Result<(), Self::Error>; + fn write(&mut self, address: A, bytes: &[u8]) -> Result<(), Self::Error>; } /// Blocking write (iterator version) -#[cfg(feature = "unproven")] -pub trait WriteIter { +pub trait WriteIter { /// Error type type Error; - /// Sends bytes to slave with address `addr` + /// Writes bytes to slave with address `address` /// /// # I2C Events (contract) /// /// Same as `Write` - fn write(&mut self, addr: u8, bytes: B) -> Result<(), Self::Error> + fn write(&mut self, address: A, bytes: B) -> Result<(), Self::Error> where B: IntoIterator; } /// Blocking write + read -pub trait WriteRead { +pub trait WriteRead { /// Error type type Error; - /// Sends bytes to slave with address `addr` and then reads enough bytes to fill `buffer` *in a + /// Writes bytes to slave with address `address` and then reads enough bytes to fill `buffer` *in a /// single transaction* /// /// # I2C Events (contract) @@ -100,19 +209,18 @@ pub trait WriteRead { /// - `SP` = stop condition fn write_read( &mut self, - address: u8, + address: A, bytes: &[u8], buffer: &mut [u8], ) -> Result<(), Self::Error>; } /// Blocking write (iterator version) + read -#[cfg(feature = "unproven")] -pub trait WriteIterRead { +pub trait WriteIterRead { /// Error type type Error; - /// Sends bytes to slave with address `addr` and then reads enough bytes to fill `buffer` *in a + /// Writes bytes to slave with address `address` and then reads enough bytes to fill `buffer` *in a /// single transaction* /// /// # I2C Events (contract) @@ -120,10 +228,70 @@ pub trait WriteIterRead { /// Same as the `WriteRead` trait fn write_iter_read( &mut self, - address: u8, + address: A, bytes: B, buffer: &mut [u8], ) -> Result<(), Self::Error> - where + where B: IntoIterator; } + +/// 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 { + /// 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 { + /// 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>; +} diff --git a/src/blocking/mod.rs b/src/blocking/mod.rs index 3a050f6d2..e3c132826 100644 --- a/src/blocking/mod.rs +++ b/src/blocking/mod.rs @@ -4,6 +4,7 @@ //! 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; diff --git a/src/blocking/spi.rs b/src/blocking/spi.rs index 84e48029f..76623e290 100644 --- a/src/blocking/spi.rs +++ b/src/blocking/spi.rs @@ -104,3 +104,24 @@ pub mod write_iter { } } } + +/// 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 { + /// Associated error type + type Error; + + /// Execute the provided transactions + fn exec<'a>(&mut self, operations: &mut [Operation<'a, W>]) -> Result<(), Self::Error>; +} diff --git a/src/can/id.rs b/src/can/id.rs new file mode 100644 index 000000000..811baa5f1 --- /dev/null +++ b/src/can/id.rs @@ -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 { + 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 { + 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 for Id { + #[inline] + fn from(id: StandardId) -> Self { + Id::Standard(id) + } +} + +impl From 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) + ); + } +} diff --git a/src/can/mod.rs b/src/can/mod.rs new file mode 100644 index 000000000..c12e40a7f --- /dev/null +++ b/src/can/mod.rs @@ -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, data: &[u8]) -> Option; + + /// 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, dlc: usize) -> Option; + + /// 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" + ), + } + } +} diff --git a/src/can/nb.rs b/src/can/nb.rs new file mode 100644 index 000000000..2ab6050a5 --- /dev/null +++ b/src/can/nb.rs @@ -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, Self::Error>; + + /// Returns a received frame if available. + fn receive(&mut self) -> nb::Result; +} diff --git a/src/digital/mod.rs b/src/digital/mod.rs index 4e67f9851..5e1848f02 100644 --- a/src/digital/mod.rs +++ b/src/digital/mod.rs @@ -1,11 +1,14 @@ //! 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.")] +#[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 @@ -22,4 +25,3 @@ pub mod v2_compat; // Re-export old traits so this isn't a breaking change #[allow(deprecated)] pub use self::v1::*; - diff --git a/src/digital/v1_compat.rs b/src/digital/v1_compat.rs index aed9aaf7a..83a9ffce7 100644 --- a/src/digital/v1_compat.rs +++ b/src/digital/v1_compat.rs @@ -1,7 +1,38 @@ -//! v1 compatibility wrapper -//! this module adds reverse support for v2 digital traits -//! v2 traits must be explicitly cast to the v1 version using `.into()`, -//! and will panic on internal errors +//! 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 { +//! _pin: T, +//! } +//! +//! impl OldOutputPinConsumer +//! where T: v1::OutputPin { +//! pub fn new(pin: T) -> OldOutputPinConsumer { +//! OldOutputPinConsumer{ _pin: pin } +//! } +//! } +//! +//! fn main() { +//! let pin = NewOutputPinImpl{}; +//! let _consumer: OldOutputPinConsumer> = OldOutputPinConsumer::new(pin.into()); +//! } +//! ``` +//! #[allow(deprecated)] use super::v1; @@ -12,14 +43,14 @@ pub struct OldOutputPin { pin: T, } -impl OldOutputPin +impl OldOutputPin where - T: v2::OutputPin, + T: v2::OutputPin, E: core::fmt::Debug, { /// Create a new OldOutputPin wrapper around a `v2::OutputPin` pub fn new(pin: T) -> Self { - Self{pin} + Self { pin } } /// Fetch a reference to the inner `v2::OutputPin` impl @@ -29,22 +60,22 @@ where } } -impl From for OldOutputPin +impl From for OldOutputPin where - T: v2::OutputPin, + T: v2::OutputPin, E: core::fmt::Debug, { fn from(pin: T) -> Self { - OldOutputPin{pin} + OldOutputPin { pin } } } /// Implementation of `v1::OutputPin` trait for fallible `v2::OutputPin` output pins /// where errors will panic. #[allow(deprecated)] -impl v1::OutputPin for OldOutputPin +impl v1::OutputPin for OldOutputPin where - T: v2::OutputPin, + T: v2::OutputPin, E: core::fmt::Debug, { fn set_low(&mut self) { @@ -60,9 +91,9 @@ where /// where errors will panic. #[cfg(feature = "unproven")] #[allow(deprecated)] -impl v1::StatefulOutputPin for OldOutputPin +impl v1::StatefulOutputPin for OldOutputPin where - T: v2::StatefulOutputPin, + T: v2::StatefulOutputPin, E: core::fmt::Debug, { fn is_set_low(&self) -> bool { @@ -82,26 +113,25 @@ pub struct OldInputPin { } #[cfg(feature = "unproven")] -impl OldInputPin +impl OldInputPin where - T: v2::OutputPin, + T: v2::InputPin, E: core::fmt::Debug, { /// Create an `OldInputPin` wrapper around a `v2::InputPin`. pub fn new(pin: T) -> Self { - Self{pin} + Self { pin } } - } #[cfg(feature = "unproven")] -impl From for OldInputPin +impl From for OldInputPin where - T: v2::InputPin, + T: v2::InputPin, E: core::fmt::Debug, { fn from(pin: T) -> Self { - OldInputPin{pin} + OldInputPin { pin } } } @@ -109,9 +139,9 @@ where /// where errors will panic. #[cfg(feature = "unproven")] #[allow(deprecated)] -impl v1::InputPin for OldInputPin +impl v1::InputPin for OldInputPin where - T: v2::InputPin, + T: v2::InputPin, E: core::fmt::Debug, { fn is_low(&self) -> bool { @@ -137,7 +167,7 @@ mod tests { #[derive(Clone)] struct NewOutputPinImpl { state: bool, - res: Result<(), ()> + res: Result<(), ()>, } impl v2::OutputPin for NewOutputPinImpl { @@ -147,7 +177,7 @@ mod tests { self.state = false; self.res } - fn set_high(&mut self) -> Result<(), Self::Error>{ + fn set_high(&mut self) -> Result<(), Self::Error> { self.state = true; self.res } @@ -159,35 +189,47 @@ mod tests { } #[allow(deprecated)] - impl OldOutputPinConsumer - where T: v1::OutputPin + impl OldOutputPinConsumer + where + T: v1::OutputPin, { pub fn new(pin: T) -> OldOutputPinConsumer { - OldOutputPinConsumer{ _pin: pin } + OldOutputPinConsumer { _pin: pin } } } #[test] fn v1_v2_output_explicit() { - let i = NewOutputPinImpl{state: false, res: Ok(())}; + let i = NewOutputPinImpl { + state: false, + res: Ok(()), + }; let _c: OldOutputPinConsumer> = OldOutputPinConsumer::new(i.into()); } #[test] fn v1_v2_output_state() { - let mut o: OldOutputPin<_> = NewOutputPinImpl{state: false, res: Ok(())}.into(); + 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); + assert_eq!(o.inner().state, false); } #[test] #[should_panic] fn v1_v2_output_panic() { - let mut o: OldOutputPin<_> = NewOutputPinImpl{state: false, res: Err(())}.into(); + let mut o: OldOutputPin<_> = NewOutputPinImpl { + state: false, + res: Err(()), + } + .into(); o.set_high(); } @@ -207,7 +249,7 @@ mod tests { fn is_low(&self) -> Result { self.state.map(|v| v == false) } - fn is_high(&self) -> Result{ + fn is_high(&self) -> Result { self.state.map(|v| v == true) } } @@ -220,25 +262,26 @@ mod tests { #[cfg(feature = "unproven")] #[allow(deprecated)] - impl OldInputPinConsumer - where T: v1::InputPin + impl OldInputPinConsumer + where + T: v1::InputPin, { pub fn new(pin: T) -> OldInputPinConsumer { - OldInputPinConsumer{ _pin: pin } + OldInputPinConsumer { _pin: pin } } } #[cfg(feature = "unproven")] #[test] fn v1_v2_input_explicit() { - let i = NewInputPinImpl{state: Ok(false)}; + let i = NewInputPinImpl { state: Ok(false) }; let _c: OldInputPinConsumer> = OldInputPinConsumer::new(i.into()); } #[cfg(feature = "unproven")] #[test] fn v1_v2_input_state() { - let i: OldInputPin<_> = NewInputPinImpl{state: Ok(false)}.into(); + let i: OldInputPin<_> = NewInputPinImpl { state: Ok(false) }.into(); assert_eq!(i.is_low(), true); assert_eq!(i.is_high(), false); @@ -248,9 +291,8 @@ mod tests { #[test] #[should_panic] fn v1_v2_input_panic() { - let i: OldInputPin<_> = NewInputPinImpl{state: Err(())}.into(); + let i: OldInputPin<_> = NewInputPinImpl { state: Err(()) }.into(); i.is_low(); } - } diff --git a/src/digital/v2.rs b/src/digital/v2.rs index 748807225..81fd67cbb 100644 --- a/src/digital/v2.rs +++ b/src/digital/v2.rs @@ -2,6 +2,46 @@ //! //! 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 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 @@ -18,13 +58,24 @@ pub trait OutputPin { /// *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 { +pub trait StatefulOutputPin: OutputPin { /// Is the pin in drive high mode? /// /// *NOTE* this does *not* read the electrical state of the pin @@ -136,3 +187,30 @@ pub trait InputPin { /// Is the input pin low? fn is_low(&self) -> Result; } + +/// 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 +where + TInput: InputPin + IoPin, + TOutput: OutputPin + IoPin, +{ + /// 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; + + /// 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; +} diff --git a/src/digital/v2_compat.rs b/src/digital/v2_compat.rs index 779c089d3..929f413dd 100644 --- a/src/digital/v2_compat.rs +++ b/src/digital/v2_compat.rs @@ -1,5 +1,36 @@ //! v2 compatibility shims -//! this module adds implicit forward support to v1 digital traits +//! +//! 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 { +//! _pin: T, +//! } +//! +//! impl NewOutputPinConsumer +//! where T: v2::OutputPin { +//! pub fn new(pin: T) -> NewOutputPinConsumer { +//! NewOutputPinConsumer{ _pin: pin } +//! } +//! } +//! +//! fn main() { +//! let pin = OldOutputPinImpl{}; +//! let _consumer = NewOutputPinConsumer::new(pin); +//! } +//! ``` +//! #[allow(deprecated)] use super::v1; @@ -7,7 +38,7 @@ use super::v2; /// Implementation of fallible `v2::OutputPin` for `v1::OutputPin` traits #[allow(deprecated)] -impl v2::OutputPin for T +impl v2::OutputPin for T where T: v1::OutputPin, { @@ -19,14 +50,14 @@ where } fn set_high(&mut self) -> Result<(), Self::Error> { - Ok(self.set_high()) - } + Ok(self.set_high()) + } } /// Implementation of fallible `v2::StatefulOutputPin` for `v1::StatefulOutputPin` digital traits #[cfg(feature = "unproven")] #[allow(deprecated)] -impl v2::StatefulOutputPin for T +impl v2::StatefulOutputPin for T where T: v1::StatefulOutputPin + v1::OutputPin, { @@ -34,18 +65,21 @@ where Ok(self.is_set_low()) } - fn is_set_high(&self) -> Result { - Ok(self.is_set_high()) - } + fn is_set_high(&self) -> Result { + Ok(self.is_set_high()) + } } +#[cfg(feature = "unproven")] +#[allow(deprecated)] +impl 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 v2::InputPin for T +impl v2::InputPin for T where - T: v1::InputPin + T: v1::InputPin, { // TODO: update to ! when never_type is stabilized type Error = (); @@ -54,9 +88,9 @@ where Ok(self.is_low()) } - fn is_high(&self) -> Result { - Ok(self.is_high()) - } + fn is_high(&self) -> Result { + Ok(self.is_high()) + } } #[cfg(test)] @@ -67,8 +101,8 @@ mod tests { use crate::digital::v2; #[allow(deprecated)] - struct OldOutputPinImpl { - state: bool + struct OldOutputPinImpl { + state: bool, } #[allow(deprecated)] @@ -81,27 +115,67 @@ mod tests { } } + #[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 { _pin: T, } - impl NewOutputPinConsumer - where T: v2::OutputPin { + impl NewOutputPinConsumer + where + T: v2::OutputPin, + { pub fn new(pin: T) -> NewOutputPinConsumer { - NewOutputPinConsumer{ _pin: pin } + NewOutputPinConsumer { _pin: pin } + } + } + + #[cfg(feature = "unproven")] + struct NewToggleablePinConsumer { + _pin: T, + } + + #[cfg(feature = "unproven")] + impl NewToggleablePinConsumer + where + T: v2::ToggleableOutputPin, + { + pub fn new(pin: T) -> NewToggleablePinConsumer { + 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 i = OldOutputPinImpl { state: false }; let _c = NewOutputPinConsumer::new(i); } #[test] fn v2_v1_output_state() { - let mut o = OldOutputPinImpl{state: false}; - + let mut o = OldOutputPinImpl { state: false }; + v2::OutputPin::set_high(&mut o).unwrap(); assert_eq!(o.state, true); @@ -111,8 +185,8 @@ mod tests { #[cfg(feature = "unproven")] #[allow(deprecated)] - struct OldInputPinImpl { - state: bool + struct OldInputPinImpl { + state: bool, } #[cfg(feature = "unproven")] @@ -132,26 +206,29 @@ mod tests { } #[cfg(feature = "unproven")] - impl NewInputPinConsumer - where T: v2::InputPin { + impl NewInputPinConsumer + where + T: v2::InputPin, + { pub fn new(pin: T) -> NewInputPinConsumer { - NewInputPinConsumer{ _pin: pin } + NewInputPinConsumer { _pin: pin } } } #[cfg(feature = "unproven")] #[test] + #[cfg(feature = "unproven")] fn v2_v1_input_implicit() { - let i = OldInputPinImpl{state: false}; + 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}; - + 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); } -} \ No newline at end of file +} diff --git a/src/fmt.rs b/src/fmt.rs index 3f66c89f3..33041a84f 100644 --- a/src/fmt.rs +++ b/src/fmt.rs @@ -3,12 +3,13 @@ //! TODO write example of usage use core::fmt::{Result, Write}; -impl Write for ::serial::Write +impl Write for dyn (::serial::Write) where Word: From, { fn write_str(&mut self, s: &str) -> Result { - let _ = s.as_bytes() + let _ = s + .as_bytes() .into_iter() .map(|c| block!(self.write(Word::from(*c)))) .last(); diff --git a/src/lib.rs b/src/lib.rs index 1a903081a..f0289b2f7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -439,8 +439,8 @@ //! //! // Event loop //! loop { -//! Pin::new(&mut blinky).resume(); -//! Pin::new(&mut loopback).resume(); +//! Pin::new(&mut blinky).resume(()); +//! Pin::new(&mut loopback).resume(()); //! # break; //! } //! } @@ -683,7 +683,6 @@ //! ``` #![deny(missing_docs)] -#![deny(warnings)] #![no_std] #[macro_use] @@ -692,6 +691,7 @@ extern crate void; pub mod adc; pub mod blocking; +pub mod can; pub mod digital; pub mod fmt; pub mod prelude; @@ -993,3 +993,7 @@ pub enum Direction { /// 1, 2, 3 Upcounting, } + +mod private { + pub trait Sealed {} +} diff --git a/src/spi.rs b/src/spi.rs index b91e6d68b..20d8538cd 100644 --- a/src/spi.rs +++ b/src/spi.rs @@ -75,4 +75,4 @@ pub const MODE_2: Mode = Mode { pub const MODE_3: Mode = Mode { polarity: Polarity::IdleHigh, phase: Phase::CaptureOnSecondTransition, -}; \ No newline at end of file +}; diff --git a/src/timer.rs b/src/timer.rs index 74cf088f3..08ba27606 100644 --- a/src/timer.rs +++ b/src/timer.rs @@ -81,7 +81,7 @@ pub trait CountDown { pub trait Periodic {} /// Trait for cancelable countdowns. -pub trait Cancel : CountDown { +pub trait Cancel: CountDown { /// Error returned when a countdown can't be canceled. type Error; diff --git a/src/watchdog.rs b/src/watchdog.rs index ee8cefc32..41e76f6ba 100644 --- a/src/watchdog.rs +++ b/src/watchdog.rs @@ -1,7 +1,5 @@ //! 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")] @@ -11,19 +9,19 @@ pub trait Watchdog { fn feed(&mut self); } - -/// Enables A watchdog timer to reset the processor if software is frozen or +/// 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(&mut self, period: T) where T: Into; + /// 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(&mut self, period: T) + where + T: Into; } - /// Disables a running watchdog timer so the processor won't be reset. #[cfg(feature = "unproven")] pub trait WatchdogDisable {