Skip to content

Commit 56ac760

Browse files
bors[bot]burrbull
andauthored
Merge #319
319: backport CAN r=eldruin a=burrbull Co-authored-by: Andrey Zgarbul <[email protected]>
2 parents bccb903 + 8281dd3 commit 56ac760

File tree

7 files changed

+332
-0
lines changed

7 files changed

+332
-0
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
77

88
## [Unreleased]
99

10+
- Backport CAN interface from the upcoming 1.0 release.
11+
1012
## [v0.2.6] - 2021-08-03
1113

1214
### Added

src/blocking/can.rs

Lines changed: 17 additions & 0 deletions
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/blocking/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
//! traits. To save boilerplate when that's the case a `Default` marker trait may be provided.
55
//! Implementing that marker trait will opt in your type into a blanket implementation.
66
7+
pub mod can;
78
pub mod delay;
89
pub mod i2c;
910
pub mod rng;

src/can/id.rs

Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
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 = StandardId(0);
10+
11+
/// CAN ID `0x7FF`, the lowest priority.
12+
pub const MAX: Self = StandardId(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 fn new(raw: u16) -> Option<Self> {
19+
if raw <= 0x7FF {
20+
Some(StandardId(raw))
21+
} else {
22+
None
23+
}
24+
}
25+
26+
/// Creates a new `StandardId` without checking if it is inside the valid range.
27+
///
28+
/// # Safety
29+
/// Using this method can create an invalid ID and is thus marked as unsafe.
30+
#[inline]
31+
pub const unsafe fn new_unchecked(raw: u16) -> Self {
32+
StandardId(raw)
33+
}
34+
35+
/// Returns this CAN Identifier as a raw 16-bit integer.
36+
#[inline]
37+
pub fn as_raw(&self) -> u16 {
38+
self.0
39+
}
40+
}
41+
42+
/// Extended 29-bit CAN Identifier (`0..=1FFF_FFFF`).
43+
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
44+
pub struct ExtendedId(u32);
45+
46+
impl ExtendedId {
47+
/// CAN ID `0`, the highest priority.
48+
pub const ZERO: Self = ExtendedId(0);
49+
50+
/// CAN ID `0x1FFFFFFF`, the lowest priority.
51+
pub const MAX: Self = ExtendedId(0x1FFF_FFFF);
52+
53+
/// Tries to create a `ExtendedId` from a raw 32-bit integer.
54+
///
55+
/// This will return `None` if `raw` is out of range of an 29-bit integer (`> 0x1FFF_FFFF`).
56+
#[inline]
57+
pub fn new(raw: u32) -> Option<Self> {
58+
if raw <= 0x1FFF_FFFF {
59+
Some(ExtendedId(raw))
60+
} else {
61+
None
62+
}
63+
}
64+
65+
/// Creates a new `ExtendedId` without checking if it is inside the valid range.
66+
///
67+
/// # Safety
68+
/// Using this method can create an invalid ID and is thus marked as unsafe.
69+
#[inline]
70+
pub const unsafe fn new_unchecked(raw: u32) -> Self {
71+
ExtendedId(raw)
72+
}
73+
74+
/// Returns this CAN Identifier as a raw 32-bit integer.
75+
#[inline]
76+
pub fn as_raw(&self) -> u32 {
77+
self.0
78+
}
79+
80+
/// Returns the Base ID part of this extended identifier.
81+
pub fn standard_id(&self) -> StandardId {
82+
// ID-28 to ID-18
83+
StandardId((self.0 >> 18) as u16)
84+
}
85+
}
86+
87+
/// A CAN Identifier (standard or extended).
88+
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
89+
pub enum Id {
90+
/// Standard 11-bit Identifier (`0..=0x7FF`).
91+
Standard(StandardId),
92+
93+
/// Extended 29-bit Identifier (`0..=0x1FFF_FFFF`).
94+
Extended(ExtendedId),
95+
}
96+
97+
impl From<StandardId> for Id {
98+
#[inline]
99+
fn from(id: StandardId) -> Self {
100+
Id::Standard(id)
101+
}
102+
}
103+
104+
impl From<ExtendedId> for Id {
105+
#[inline]
106+
fn from(id: ExtendedId) -> Self {
107+
Id::Extended(id)
108+
}
109+
}
110+
111+
#[cfg(test)]
112+
mod tests {
113+
use super::*;
114+
115+
#[test]
116+
fn standard_id_new() {
117+
assert_eq!(
118+
StandardId::new(StandardId::MAX.as_raw()),
119+
Some(StandardId::MAX)
120+
);
121+
}
122+
123+
#[test]
124+
fn standard_id_new_out_of_range() {
125+
assert_eq!(StandardId::new(StandardId::MAX.as_raw() + 1), None);
126+
}
127+
128+
#[test]
129+
fn standard_id_new_unchecked_out_of_range() {
130+
let id = StandardId::MAX.as_raw() + 1;
131+
assert_eq!(unsafe { StandardId::new_unchecked(id) }, StandardId(id));
132+
}
133+
134+
#[test]
135+
fn extended_id_new() {
136+
assert_eq!(
137+
ExtendedId::new(ExtendedId::MAX.as_raw()),
138+
Some(ExtendedId::MAX)
139+
);
140+
}
141+
142+
#[test]
143+
fn extended_id_new_out_of_range() {
144+
assert_eq!(ExtendedId::new(ExtendedId::MAX.as_raw() + 1), None);
145+
}
146+
147+
#[test]
148+
fn extended_id_new_unchecked_out_of_range() {
149+
let id = ExtendedId::MAX.as_raw() + 1;
150+
assert_eq!(unsafe { ExtendedId::new_unchecked(id) }, ExtendedId(id));
151+
}
152+
153+
#[test]
154+
fn get_standard_id_from_extended_id() {
155+
assert_eq!(
156+
Some(ExtendedId::MAX.standard_id()),
157+
StandardId::new((ExtendedId::MAX.0 >> 18) as u16)
158+
);
159+
}
160+
}

src/can/mod.rs

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

src/can/nb.rs

Lines changed: 28 additions & 0 deletions
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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -691,6 +691,7 @@ extern crate void;
691691

692692
pub mod adc;
693693
pub mod blocking;
694+
pub mod can;
694695
pub mod digital;
695696
pub mod fmt;
696697
pub mod prelude;

0 commit comments

Comments
 (0)