Skip to content

Commit 45d3170

Browse files
Merge #314
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]>
2 parents 683e41e + 7387323 commit 45d3170

File tree

10 files changed

+278
-6
lines changed

10 files changed

+278
-6
lines changed

.github/bors.toml

+2-2
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@ status = [
55
"ci-linux (stable, x86_64-unknown-linux-gnu)",
66
"ci-linux (stable, thumbv6m-none-eabi)",
77
"ci-linux (stable, thumbv7m-none-eabi)",
8-
"ci-linux (1.40.0, x86_64-unknown-linux-gnu)",
8+
"ci-linux (1.46.0, x86_64-unknown-linux-gnu)",
99
"ci-linux-test (stable)",
10-
"ci-linux-test (1.40.0, x86_64-unknown-linux-gnu)",
10+
"ci-linux-test (1.46.0, x86_64-unknown-linux-gnu)",
1111
"fmt",
1212
]

.github/workflows/ci.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ jobs:
2121

2222
include:
2323
# Test MSRV
24-
- rust: 1.40.0
24+
- rust: 1.46.0
2525
TARGET: x86_64-unknown-linux-gnu
2626

2727
# Test nightly but don't fail

.github/workflows/test.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ jobs:
1616
rust: [stable]
1717

1818
include:
19-
- rust: 1.40.0
19+
- rust: 1.46.0
2020
TARGET: x86_64-unknown-linux-gnu
2121

2222
# Test nightly but don't fail

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
88
## [Unreleased]
99

1010
### Added
11+
- Added `Can` Controller Area Network traits.
1112
- `Error` traits for SPI, I2C and Serial traits. The error types used in those must
1213
implement these `Error` traits, which implies providing a conversion to a common
1314
set of error kinds. Generic drivers using these interfaces can then convert the errors

README.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
[![crates.io](https://img.shields.io/crates/d/embedded-hal.svg)](https://crates.io/crates/embedded-hal)
22
[![crates.io](https://img.shields.io/crates/v/embedded-hal.svg)](https://crates.io/crates/embedded-hal)
33
[![Documentation](https://docs.rs/embedded-hal/badge.svg)](https://docs.rs/embedded-hal)
4-
![Minimum Supported Rust Version](https://img.shields.io/badge/rustc-1.40+-blue.svg)
4+
![Minimum Supported Rust Version](https://img.shields.io/badge/rustc-1.46+-blue.svg)
55

66
# `embedded-hal`
77

@@ -108,7 +108,7 @@ As stated before, `embedded-hal` `-alpha` versions are _not guaranteed_ to be co
108108

109109
## Minimum Supported Rust Version (MSRV)
110110

111-
This crate is guaranteed to compile on stable Rust 1.40 and up. It *might*
111+
This crate is guaranteed to compile on stable Rust 1.46 and up. It *might*
112112
compile with older versions but that may change in any new patch release.
113113

114114
## License

src/can/blocking.rs

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
//! Blocking CAN API
2+
3+
/// A blocking CAN interface that is able to transmit and receive frames.
4+
pub trait Can {
5+
/// Associated frame type.
6+
type Frame: crate::can::Frame;
7+
8+
/// Associated error type.
9+
type Error: crate::can::Error;
10+
11+
/// Puts a frame in the transmit buffer. Blocks until space is available in
12+
/// the transmit buffer.
13+
fn transmit(&mut self, frame: &Self::Frame) -> Result<(), Self::Error>;
14+
15+
/// Blocks until a frame was received or an error occured.
16+
fn receive(&mut self) -> Result<Self::Frame, Self::Error>;
17+
}

src/can/id.rs

+103
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
//! CAN Identifiers.
2+
3+
/// Standard 11-bit CAN Identifier (`0..=0x7FF`).
4+
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
5+
pub struct StandardId(u16);
6+
7+
impl StandardId {
8+
/// CAN ID `0`, the highest priority.
9+
pub const ZERO: Self = Self(0);
10+
11+
/// CAN ID `0x7FF`, the lowest priority.
12+
pub const MAX: Self = Self(0x7FF);
13+
14+
/// Tries to create a `StandardId` from a raw 16-bit integer.
15+
///
16+
/// This will return `None` if `raw` is out of range of an 11-bit integer (`> 0x7FF`).
17+
#[inline]
18+
pub const fn new(raw: u16) -> Option<Self> {
19+
if raw <= 0x7FF {
20+
Some(Self(raw))
21+
} else {
22+
None
23+
}
24+
}
25+
26+
/// Creates a new `StandardId` without checking if it is inside the valid range.
27+
#[inline]
28+
pub const unsafe fn new_unchecked(raw: u16) -> Self {
29+
Self(raw)
30+
}
31+
32+
/// Returns this CAN Identifier as a raw 16-bit integer.
33+
#[inline]
34+
pub fn as_raw(&self) -> u16 {
35+
self.0
36+
}
37+
}
38+
39+
/// Extended 29-bit CAN Identifier (`0..=1FFF_FFFF`).
40+
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
41+
pub struct ExtendedId(u32);
42+
43+
impl ExtendedId {
44+
/// CAN ID `0`, the highest priority.
45+
pub const ZERO: Self = Self(0);
46+
47+
/// CAN ID `0x1FFFFFFF`, the lowest priority.
48+
pub const MAX: Self = Self(0x1FFF_FFFF);
49+
50+
/// Tries to create a `ExtendedId` from a raw 32-bit integer.
51+
///
52+
/// This will return `None` if `raw` is out of range of an 29-bit integer (`> 0x1FFF_FFFF`).
53+
#[inline]
54+
pub const fn new(raw: u32) -> Option<Self> {
55+
if raw <= 0x1FFF_FFFF {
56+
Some(Self(raw))
57+
} else {
58+
None
59+
}
60+
}
61+
62+
/// Creates a new `ExtendedId` without checking if it is inside the valid range.
63+
#[inline]
64+
pub const unsafe fn new_unchecked(raw: u32) -> Self {
65+
Self(raw)
66+
}
67+
68+
/// Returns this CAN Identifier as a raw 32-bit integer.
69+
#[inline]
70+
pub fn as_raw(&self) -> u32 {
71+
self.0
72+
}
73+
74+
/// Returns the Base ID part of this extended identifier.
75+
pub fn standard_id(&self) -> StandardId {
76+
// ID-28 to ID-18
77+
StandardId((self.0 >> 18) as u16)
78+
}
79+
}
80+
81+
/// A CAN Identifier (standard or extended).
82+
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
83+
pub enum Id {
84+
/// Standard 11-bit Identifier (`0..=0x7FF`).
85+
Standard(StandardId),
86+
87+
/// Extended 29-bit Identifier (`0..=0x1FFF_FFFF`).
88+
Extended(ExtendedId),
89+
}
90+
91+
impl From<StandardId> for Id {
92+
#[inline]
93+
fn from(id: StandardId) -> Self {
94+
Id::Standard(id)
95+
}
96+
}
97+
98+
impl From<ExtendedId> for Id {
99+
#[inline]
100+
fn from(id: ExtendedId) -> Self {
101+
Id::Extended(id)
102+
}
103+
}

src/can/mod.rs

+122
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
//! Controller Area Network
2+
3+
pub mod blocking;
4+
pub mod nb;
5+
6+
mod id;
7+
8+
pub use id::*;
9+
10+
/// A CAN2.0 Frame
11+
pub trait Frame: Sized {
12+
/// Creates a new frame.
13+
/// Returns an error when the data slice is too long.
14+
fn new(id: impl Into<Id>, data: &[u8]) -> Result<Self, ()>;
15+
16+
/// Creates a new remote frame (RTR bit set).
17+
/// Returns an error when the data length code (DLC) is not valid.
18+
fn new_remote(id: impl Into<Id>, dlc: usize) -> Result<Self, ()>;
19+
20+
/// Returns true if this frame is a extended frame.
21+
fn is_extended(&self) -> bool;
22+
23+
/// Returns true if this frame is a standard frame.
24+
fn is_standard(&self) -> bool {
25+
!self.is_extended()
26+
}
27+
28+
/// Returns true if this frame is a remote frame.
29+
fn is_remote_frame(&self) -> bool;
30+
31+
/// Returns true if this frame is a data frame.
32+
fn is_data_frame(&self) -> bool {
33+
!self.is_remote_frame()
34+
}
35+
36+
/// Returns the frame identifier.
37+
fn id(&self) -> Id;
38+
39+
/// Returns the data length code (DLC) which is in the range 0..8.
40+
///
41+
/// For data frames the DLC value always matches the length of the data.
42+
/// Remote frames do not carry any data, yet the DLC can be greater than 0.
43+
fn dlc(&self) -> usize;
44+
45+
/// Returns the frame data (0..8 bytes in length).
46+
fn data(&self) -> &[u8];
47+
}
48+
49+
/// CAN error
50+
pub trait Error: core::fmt::Debug {
51+
/// Convert error to a generic CAN error kind
52+
///
53+
/// By using this method, CAN errors freely defined by HAL implementations
54+
/// can be converted to a set of generic serial errors upon which generic
55+
/// code can act.
56+
fn kind(&self) -> ErrorKind;
57+
}
58+
59+
/// CAN error kind
60+
///
61+
/// This represents a common set of CAN operation errors. HAL implementations are
62+
/// free to define more specific or additional error types. However, by providing
63+
/// a mapping to these common CAN errors, generic code can still react to them.
64+
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
65+
#[non_exhaustive]
66+
pub enum ErrorKind {
67+
/// The peripheral receive buffer was overrun.
68+
Overrun,
69+
70+
// MAC sublayer errors
71+
/// A bit error is detected at that bit time when the bit value that is
72+
/// monitored differs from the bit value sent.
73+
Bit,
74+
75+
/// A stuff error is detected at the bit time of the sixth consecutive
76+
/// equal bit level in a frame field that shall be coded by the method
77+
/// of bit stuffing.
78+
Stuff,
79+
80+
/// Calculated CRC sequence does not equal the received one.
81+
Crc,
82+
83+
/// A form error shall be detected when a fixed-form bit field contains
84+
/// one or more illegal bits.
85+
Form,
86+
87+
/// An ACK error shall be detected by a transmitter whenever it does not
88+
/// monitor a dominant bit during the ACK slot.
89+
Acknowledge,
90+
91+
/// A different error occurred. The original error may contain more information.
92+
Other,
93+
}
94+
95+
impl Error for ErrorKind {
96+
fn kind(&self) -> ErrorKind {
97+
*self
98+
}
99+
}
100+
101+
impl core::fmt::Display for ErrorKind {
102+
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
103+
match self {
104+
Self::Overrun => write!(f, "The peripheral receive buffer was overrun"),
105+
Self::Bit => write!(
106+
f,
107+
"Bit value that is monitored differs from the bit value sent"
108+
),
109+
Self::Stuff => write!(f, "Sixth consecutive equal bits detected"),
110+
Self::Crc => write!(f, "Calculated CRC sequence does not equal the received one"),
111+
Self::Form => write!(
112+
f,
113+
"A fixed-form bit field contains one or more illegal bits"
114+
),
115+
Self::Acknowledge => write!(f, "Transmitted frame was not acknowledged"),
116+
Self::Other => write!(
117+
f,
118+
"A different error occurred. The original error may contain more information"
119+
),
120+
}
121+
}
122+
}

src/can/nb.rs

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
//! Non-blocking CAN API
2+
3+
/// A CAN interface that is able to transmit and receive frames.
4+
pub trait Can {
5+
/// Associated frame type.
6+
type Frame: crate::can::Frame;
7+
8+
/// Associated error type.
9+
type Error: crate::can::Error;
10+
11+
/// Puts a frame in the transmit buffer to be sent on the bus.
12+
///
13+
/// If the transmit buffer is full, this function will try to replace a pending
14+
/// lower priority frame and return the frame that was replaced.
15+
/// Returns `Err(WouldBlock)` if the transmit buffer is full and no frame can be
16+
/// replaced.
17+
///
18+
/// # Notes for implementers
19+
///
20+
/// * Frames of equal identifier shall be transmited in FIFO fashion when more
21+
/// than one transmit buffer is available.
22+
/// * When replacing pending frames make sure the frame is not in the process of
23+
/// being send to the bus.
24+
fn transmit(&mut self, frame: &Self::Frame) -> nb::Result<Option<Self::Frame>, Self::Error>;
25+
26+
/// Returns a received frame if available.
27+
fn receive(&mut self) -> nb::Result<Self::Frame, Self::Error>;
28+
}

src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -409,6 +409,7 @@
409409
pub mod fmt;
410410
pub use nb;
411411
pub mod adc;
412+
pub mod can;
412413
pub mod capture;
413414
pub mod delay;
414415
pub mod digital;

0 commit comments

Comments
 (0)