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/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 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 diff --git a/src/can/blocking.rs b/src/can/blocking.rs new file mode 100644 index 000000000..b13885abe --- /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: 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/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..88929fd85 --- /dev/null +++ b/src/can/mod.rs @@ -0,0 +1,122 @@ +//! 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]; +} + +/// 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. + 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 { + 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::Acknowledge => write!(f, "Transmitted frame was not acknowledged"), + Self::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/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;