From 7558350349bf9f64994a87c7df592001fdfe3e19 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20Kr=C3=B6ger?= Date: Wed, 22 Sep 2021 19:48:52 +0200 Subject: [PATCH 01/10] Traits for CAN (Controller Area Network) --- src/can/blocking.rs | 17 ++++++++ src/can/id.rs | 103 ++++++++++++++++++++++++++++++++++++++++++++ src/can/mod.rs | 47 ++++++++++++++++++++ src/can/nb.rs | 28 ++++++++++++ src/lib.rs | 1 + 5 files changed, 196 insertions(+) create mode 100644 src/can/blocking.rs create mode 100644 src/can/id.rs create mode 100644 src/can/mod.rs create mode 100644 src/can/nb.rs diff --git a/src/can/blocking.rs b/src/can/blocking.rs new file mode 100644 index 000000000..a4d73e1a2 --- /dev/null +++ b/src/can/blocking.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; + + /// Puts a frame in the transmit buffer. Blocks until space is available in + /// the transmit buffer. + fn write(&mut self, frame: &Self::Frame) -> Result<(), Self::Error>; + + /// Blocks until a frame was received or an error occured. + fn read(&mut self) -> Result; +} diff --git a/src/can/id.rs b/src/can/id.rs new file mode 100644 index 000000000..861bf6b2b --- /dev/null +++ b/src/can/id.rs @@ -0,0 +1,103 @@ +//! 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 = Self(0); + + /// CAN ID `0x7FF`, the lowest priority. + pub const MAX: Self = Self(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 const fn new(raw: u16) -> Option { + if raw <= 0x7FF { + Some(Self(raw)) + } else { + None + } + } + + /// Creates a new `StandardId` without checking if it is inside the valid range. + #[inline] + pub const unsafe fn new_unchecked(raw: u16) -> Self { + Self(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 = Self(0); + + /// CAN ID `0x1FFFFFFF`, the lowest priority. + pub const MAX: Self = Self(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 const fn new(raw: u32) -> Option { + if raw <= 0x1FFF_FFFF { + Some(Self(raw)) + } else { + None + } + } + + /// Creates a new `ExtendedId` without checking if it is inside the valid range. + #[inline] + pub const unsafe fn new_unchecked(raw: u32) -> Self { + Self(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) + } +} diff --git a/src/can/mod.rs b/src/can/mod.rs new file mode 100644 index 000000000..e669e942d --- /dev/null +++ b/src/can/mod.rs @@ -0,0 +1,47 @@ +//! Controller Area Network + +pub mod blocking; +pub mod nb; + +mod id; + +pub use id::*; + +/// A CAN2.0 Frame +pub trait Frame: Sized { + /// Creates a new frame. + /// Returns an error when the data slice is too long. + fn new(id: impl Into, data: &[u8]) -> Result; + + /// Creates a new remote frame (RTR bit set). + /// Returns an error when the data length code (DLC) is not valid. + fn new_remote(id: impl Into, dlc: usize) -> Result; + + /// 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]; +} diff --git a/src/can/nb.rs b/src/can/nb.rs new file mode 100644 index 000000000..72cebe9ca --- /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; + + /// 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/lib.rs b/src/lib.rs index 37e6335f1..05b6ad5a7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -409,6 +409,7 @@ pub mod fmt; pub use nb; pub mod adc; +pub mod can; pub mod capture; pub mod delay; pub mod digital; From b2d00d1c6837f7046aea674fda802bda72fa1dbc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20Kr=C3=B6ger?= Date: Mon, 4 Oct 2021 20:30:06 +0200 Subject: [PATCH 02/10] can: Make `Id::new_unchecked()` safe Invalid CAN identifiers are still memory safe. --- src/can/id.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/can/id.rs b/src/can/id.rs index 861bf6b2b..8d7d8f2bf 100644 --- a/src/can/id.rs +++ b/src/can/id.rs @@ -25,7 +25,7 @@ impl StandardId { /// Creates a new `StandardId` without checking if it is inside the valid range. #[inline] - pub const unsafe fn new_unchecked(raw: u16) -> Self { + pub const fn new_unchecked(raw: u16) -> Self { Self(raw) } From 5f8ed23f20bf1670c6612666a35f8d32684273a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20Kr=C3=B6ger?= Date: Mon, 4 Oct 2021 20:32:02 +0200 Subject: [PATCH 03/10] can: Rename blocking methods The blocking traits borrowed the naming convention from socketcan. Rename the methods to `transmit()` and `receive()` to make them consistent with the `nb` traits. --- src/can/blocking.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/can/blocking.rs b/src/can/blocking.rs index a4d73e1a2..7783e3f1c 100644 --- a/src/can/blocking.rs +++ b/src/can/blocking.rs @@ -10,8 +10,8 @@ pub trait Can { /// Puts a frame in the transmit buffer. Blocks until space is available in /// the transmit buffer. - fn write(&mut self, frame: &Self::Frame) -> Result<(), Self::Error>; + fn transmit(&mut self, frame: &Self::Frame) -> Result<(), Self::Error>; /// Blocks until a frame was received or an error occured. - fn read(&mut self) -> Result; + fn receive(&mut self) -> Result; } From 5f7cd3b7241410e562fffac81c247e8f497eda3c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20Kr=C3=B6ger?= Date: Mon, 4 Oct 2021 21:02:20 +0200 Subject: [PATCH 04/10] can: Add error traits like #296 --- src/can/blocking.rs | 2 +- src/can/mod.rs | 68 +++++++++++++++++++++++++++++++++++++++++++++ src/can/nb.rs | 2 +- 3 files changed, 70 insertions(+), 2 deletions(-) diff --git a/src/can/blocking.rs b/src/can/blocking.rs index 7783e3f1c..b13885abe 100644 --- a/src/can/blocking.rs +++ b/src/can/blocking.rs @@ -6,7 +6,7 @@ pub trait Can { type Frame: crate::can::Frame; /// Associated error type. - type Error; + type Error: crate::can::Error; /// Puts a frame in the transmit buffer. Blocks until space is available in /// the transmit buffer. diff --git a/src/can/mod.rs b/src/can/mod.rs index e669e942d..f4a6c347b 100644 --- a/src/can/mod.rs +++ b/src/can/mod.rs @@ -45,3 +45,71 @@ pub trait Frame: Sized { /// 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)] +#[non_exhaustive] +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. + Ack, +} + +impl Error for ErrorKind { + fn kind(&self) -> ErrorKind { + *self + } +} + +impl core::fmt::Display for ErrorKind { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + match self { + Self::Overrun => write!(f, "The peripheral receive buffer was overrun"), + Self::Bit => write!( + f, + "Bit value that is monitored differs from the bit value sent" + ), + Self::Stuff => write!(f, "Sixth consecutive equal bits detected"), + Self::Crc => write!(f, "Calculated CRC sequence does not equal the received one"), + Self::Form => write!( + f, + "A fixed-form bit field contains one or more illegal bits" + ), + Self::Ack => write!(f, "Transmitted frame was not acknowledged"), + } + } +} diff --git a/src/can/nb.rs b/src/can/nb.rs index 72cebe9ca..2ab6050a5 100644 --- a/src/can/nb.rs +++ b/src/can/nb.rs @@ -6,7 +6,7 @@ pub trait Can { type Frame: crate::can::Frame; /// Associated error type. - type Error; + type Error: crate::can::Error; /// Puts a frame in the transmit buffer to be sent on the bus. /// From 402f1cf8ea55b14482e9d29009e002bf47585555 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20Kr=C3=B6ger?= Date: Mon, 4 Oct 2021 21:06:28 +0200 Subject: [PATCH 05/10] can: Changelog entry --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 145c9a223..951ba7b47 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] ### Added +- Added `Can` Controller Area Network traits. - `Error` traits for SPI, I2C and Serial traits. The error types used in those must implement these `Error` traits, which implies providing a conversion to a common set of error kinds. Generic drivers using these interfaces can then convert the errors From 6a6ad3feb3396052102da1e6f8b229403b8ff38d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20Kr=C3=B6ger?= Date: Tue, 5 Oct 2021 20:46:21 +0200 Subject: [PATCH 06/10] can: Add `Other` error kind --- src/can/mod.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/can/mod.rs b/src/can/mod.rs index f4a6c347b..e72581779 100644 --- a/src/can/mod.rs +++ b/src/can/mod.rs @@ -87,6 +87,9 @@ pub enum ErrorKind { /// An ACK error shall be detected by a transmitter whenever it does not /// monitor a dominant bit during the ACK slot. Ack, + + /// A different error occurred. The original error may contain more information. + Other, } impl Error for ErrorKind { @@ -110,6 +113,10 @@ impl core::fmt::Display for ErrorKind { "A fixed-form bit field contains one or more illegal bits" ), Self::Ack => write!(f, "Transmitted frame was not acknowledged"), + Self::Other => write!( + f, + "A different error occurred. The original error may contain more information" + ), } } } From 6b3820ec3e329e0dee630c5a3a71c5ac2befd96c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20Kr=C3=B6ger?= Date: Mon, 11 Oct 2021 20:23:19 +0200 Subject: [PATCH 07/10] can: Rename 'Ack' error to 'Acknowledge' --- src/can/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/can/mod.rs b/src/can/mod.rs index e72581779..88929fd85 100644 --- a/src/can/mod.rs +++ b/src/can/mod.rs @@ -86,7 +86,7 @@ pub enum ErrorKind { /// An ACK error shall be detected by a transmitter whenever it does not /// monitor a dominant bit during the ACK slot. - Ack, + Acknowledge, /// A different error occurred. The original error may contain more information. Other, @@ -112,7 +112,7 @@ impl core::fmt::Display for ErrorKind { f, "A fixed-form bit field contains one or more illegal bits" ), - Self::Ack => write!(f, "Transmitted frame was not acknowledged"), + Self::Acknowledge => write!(f, "Transmitted frame was not acknowledged"), Self::Other => write!( f, "A different error occurred. The original error may contain more information" From 31eabd5a920c6c7c7de448b5ba4c365b8eb04876 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20Kr=C3=B6ger?= Date: Mon, 25 Oct 2021 15:39:39 +0200 Subject: [PATCH 08/10] can: Make `ExtendedId::new_unchecked()` safe Invalid CAN identifiers are still memory safe. --- src/can/id.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/can/id.rs b/src/can/id.rs index 8d7d8f2bf..ba42cf00f 100644 --- a/src/can/id.rs +++ b/src/can/id.rs @@ -61,7 +61,7 @@ impl ExtendedId { /// Creates a new `ExtendedId` without checking if it is inside the valid range. #[inline] - pub const unsafe fn new_unchecked(raw: u32) -> Self { + pub const fn new_unchecked(raw: u32) -> Self { Self(raw) } From 1f43e5dbc7579cf24bd523deb96e824bbea77a00 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20Kr=C3=B6ger?= Date: Tue, 26 Oct 2021 11:26:20 +0200 Subject: [PATCH 09/10] Revert "can: Make `new_unchecked()` safe" This reverts commit b2d00d1c6837f7046aea674fda802bda72fa1dbc and 31eabd5a920c6c7c7de448b5ba4c365b8eb04876. Mark the constructor as unsafe again to prevent UB in safe code that assumes the CAN IDs to contain valid data. This is in line with functions like `from_utf8_unchecked()` from the std library. --- src/can/id.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/can/id.rs b/src/can/id.rs index ba42cf00f..861bf6b2b 100644 --- a/src/can/id.rs +++ b/src/can/id.rs @@ -25,7 +25,7 @@ impl StandardId { /// Creates a new `StandardId` without checking if it is inside the valid range. #[inline] - pub const fn new_unchecked(raw: u16) -> Self { + pub const unsafe fn new_unchecked(raw: u16) -> Self { Self(raw) } @@ -61,7 +61,7 @@ impl ExtendedId { /// Creates a new `ExtendedId` without checking if it is inside the valid range. #[inline] - pub const fn new_unchecked(raw: u32) -> Self { + pub const unsafe fn new_unchecked(raw: u32) -> Self { Self(raw) } From 738732369a602b2f6e961971f57044751bb08620 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20Kr=C3=B6ger?= Date: Tue, 26 Oct 2021 14:20:20 +0200 Subject: [PATCH 10/10] Raise MSRV to Rust 1.46.0 --- .github/bors.toml | 4 ++-- .github/workflows/ci.yml | 2 +- .github/workflows/test.yml | 2 +- README.md | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/bors.toml b/.github/bors.toml index 19e66e614..ee44ab591 100644 --- a/.github/bors.toml +++ b/.github/bors.toml @@ -5,8 +5,8 @@ status = [ "ci-linux (stable, x86_64-unknown-linux-gnu)", "ci-linux (stable, thumbv6m-none-eabi)", "ci-linux (stable, thumbv7m-none-eabi)", - "ci-linux (1.40.0, x86_64-unknown-linux-gnu)", + "ci-linux (1.46.0, x86_64-unknown-linux-gnu)", "ci-linux-test (stable)", - "ci-linux-test (1.40.0, x86_64-unknown-linux-gnu)", + "ci-linux-test (1.46.0, x86_64-unknown-linux-gnu)", "fmt", ] diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 11153d04f..92ef663de 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -21,7 +21,7 @@ jobs: include: # Test MSRV - - rust: 1.40.0 + - rust: 1.46.0 TARGET: x86_64-unknown-linux-gnu # Test nightly but don't fail diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 1a49c83c4..869d79920 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -16,7 +16,7 @@ jobs: rust: [stable] include: - - rust: 1.40.0 + - rust: 1.46.0 TARGET: x86_64-unknown-linux-gnu # Test nightly but don't fail diff --git a/README.md b/README.md index 3d4df0664..1f9e3a49a 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ [![crates.io](https://img.shields.io/crates/d/embedded-hal.svg)](https://crates.io/crates/embedded-hal) [![crates.io](https://img.shields.io/crates/v/embedded-hal.svg)](https://crates.io/crates/embedded-hal) [![Documentation](https://docs.rs/embedded-hal/badge.svg)](https://docs.rs/embedded-hal) -![Minimum Supported Rust Version](https://img.shields.io/badge/rustc-1.40+-blue.svg) +![Minimum Supported Rust Version](https://img.shields.io/badge/rustc-1.46+-blue.svg) # `embedded-hal` @@ -108,7 +108,7 @@ As stated before, `embedded-hal` `-alpha` versions are _not guaranteed_ to be co ## Minimum Supported Rust Version (MSRV) -This crate is guaranteed to compile on stable Rust 1.40 and up. It *might* +This crate is guaranteed to compile on stable Rust 1.46 and up. It *might* compile with older versions but that may change in any new patch release. ## License