Skip to content

Commit

Permalink
Merge #314
Browse files Browse the repository at this point in the history
314: Controller Area Network (CAN) Take 4 r=eldruin a=timokroeger

Updated to the latest HAL changes:
* Removed `try_` prefix
* Moved non-blocking implementation to `nb` module
* Removed default `blocking` implementaions

## Usage Example
[stm32-fwupdate](https://github.com/timokroeger/pcan-basic-rs/blob/eh-take-4/pcan-basic/examples/stm32-fwupdate.rs)

## Implementations
Updated for this PR:
* [pcan-basic](https://github.com/timokroeger/pcan-basic-rs/blob/eh-take-4/pcan-basic/src/lib.rs) on top of an existing software API
* [bxcan](https://github.com/timokroeger/bxcan/blob/eh-take-4/src/lib.rs#L460)

Based on the very similar predecessor traits `embedded-can` v0.3 ([diff v0.3 -> this PR](https://github.com/timokroeger/embedded-can/compare/eh-take-4))
* [candev](https://github.com/reneherrero/candev) implementing the traits for linux SocketCAN
* [socketcan-isotc](https://github.com/marcelbuesing/socketcan-isotp)

## Previous Discussion
* #212 
* #77
* #21
* #53 

Co-authored-by: Timo Kröger <[email protected]>
  • Loading branch information
bors[bot] and timokroeger authored Oct 28, 2021
2 parents 683e41e + 7387323 commit 45d3170
Show file tree
Hide file tree
Showing 10 changed files with 278 additions and 6 deletions.
4 changes: 2 additions & 2 deletions .github/bors.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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",
]
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -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`

Expand Down Expand Up @@ -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
Expand Down
17 changes: 17 additions & 0 deletions src/can/blocking.rs
Original file line number Diff line number Diff line change
@@ -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>;
}
103 changes: 103 additions & 0 deletions src/can/id.rs
Original file line number Diff line number Diff line change
@@ -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<Self> {
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<Self> {
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<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)
}
}
122 changes: 122 additions & 0 deletions src/can/mod.rs
Original file line number Diff line number Diff line change
@@ -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<Id>, data: &[u8]) -> Result<Self, ()>;

/// 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<Id>, dlc: usize) -> Result<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)]
#[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"
),
}
}
}
28 changes: 28 additions & 0 deletions src/can/nb.rs
Original file line number Diff line number Diff line change
@@ -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>;
}
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down

0 comments on commit 45d3170

Please sign in to comment.