From 0f323aad41866aa780b190a9e9dd57c33830e75a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20Kr=C3=B6ger?= Date: Sun, 26 Apr 2020 09:22:39 +0200 Subject: [PATCH 1/8] can: `Frame`, `Transmitter` and `Receiver` traits # Conflicts: # src/lib.rs --- src/can.rs | 68 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 1 + 2 files changed, 69 insertions(+) create mode 100644 src/can.rs diff --git a/src/can.rs b/src/can.rs new file mode 100644 index 000000000..446837430 --- /dev/null +++ b/src/can.rs @@ -0,0 +1,68 @@ +//! Controller Area Network + +/// A CAN2.0 Frame +pub trait Frame { + /// Creates a new frame with a standard identifier. + fn new_standard(id: u32, data: &[u8]) -> Self; + + /// Creates a new frame with an extended identifier. + fn new_extended(id: u32, data: &[u8]) -> Self; + + /// Marks this frame as a remote frame (by setting the RTR bit). + fn with_rtr(&mut self, dlc: usize) -> &mut 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) -> u32; + + /// 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]; +} + +/// A CAN interface that is able to transmit frames. +pub trait Transmitter { + /// Associated frame type. + type Frame: Frame; + + /// Associated error type. + type Error; + + /// Puts a frame in the transmit buffer. + /// + /// If the buffer is full, this function will try to replace a lower priority frame + /// and return it. This is to avoid the priority inversion problem. + fn transmit(&mut self, frame: &Self::Frame) -> nb::Result, Self::Error>; +} + +/// A CAN interface that is able to receive frames. +pub trait Receiver { + /// Associated frame type. + type Frame: Frame; + + /// Associated error type. + type Error; + + /// Returns a received frame if available. + fn receive(&mut self) -> nb::Result; +} diff --git a/src/lib.rs b/src/lib.rs index 8b89f203a..f8a7c0838 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -406,6 +406,7 @@ pub mod adc; pub mod blocking; +pub mod can; pub mod capture; pub mod digital; pub mod fmt; From c5b4816653d5fc61adcd00ad54f4f92b72957cbb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20Kr=C3=B6ger?= Date: Mon, 3 Aug 2020 13:09:26 +0200 Subject: [PATCH 2/8] can: Make frame constructors return `Result` As noted by @marcelbuesing @reneherrero invalid identfiers can be passed which --- src/can.rs | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/can.rs b/src/can.rs index 446837430..4f8bd3184 100644 --- a/src/can.rs +++ b/src/can.rs @@ -1,12 +1,14 @@ //! Controller Area Network /// A CAN2.0 Frame -pub trait Frame { - /// Creates a new frame with a standard identifier. - fn new_standard(id: u32, data: &[u8]) -> Self; - - /// Creates a new frame with an extended identifier. - fn new_extended(id: u32, data: &[u8]) -> Self; +pub trait Frame: Sized { + /// Creates a new frame with a standard identifier (0..=0x7FF). + /// Returns an error when the the identifier is not valid. + fn new_standard(id: u32, data: &[u8]) -> Result; + + /// Creates a new frame with an extended identifier (0..=0x1FFF_FFFF). + /// Returns an error when the the identifier is not valid. + fn new_extended(id: u32, data: &[u8]) -> Result; /// Marks this frame as a remote frame (by setting the RTR bit). fn with_rtr(&mut self, dlc: usize) -> &mut Self; From 449b801f74dfe8df19713905d718045c59ff8fed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20Kr=C3=B6ger?= Date: Fri, 28 Aug 2020 11:43:34 +0200 Subject: [PATCH 3/8] can: Merge `Transmitter` and `Receiver` traits --- src/can.rs | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/src/can.rs b/src/can.rs index 4f8bd3184..d1bd82bf7 100644 --- a/src/can.rs +++ b/src/can.rs @@ -42,8 +42,8 @@ pub trait Frame: Sized { fn data(&self) -> &[u8]; } -/// A CAN interface that is able to transmit frames. -pub trait Transmitter { +/// A CAN interface that is able to transmit and receive frames. +pub trait Can { /// Associated frame type. type Frame: Frame; @@ -54,16 +54,8 @@ pub trait Transmitter { /// /// If the buffer is full, this function will try to replace a lower priority frame /// and return it. This is to avoid the priority inversion problem. + /// Transmits frames of equal identifier in FIFO fashion. fn transmit(&mut self, frame: &Self::Frame) -> nb::Result, Self::Error>; -} - -/// A CAN interface that is able to receive frames. -pub trait Receiver { - /// Associated frame type. - type Frame: Frame; - - /// Associated error type. - type Error; /// Returns a received frame if available. fn receive(&mut self) -> nb::Result; From 285efaa0dcfcad5773c0759adc42bafd1ab2a1ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20Kr=C3=B6ger?= Date: Fri, 28 Aug 2020 20:34:34 +0200 Subject: [PATCH 4/8] can: Use enum for the identifier --- src/can.rs | 38 ++++++++++++++++++++++++++++---------- 1 file changed, 28 insertions(+), 10 deletions(-) diff --git a/src/can.rs b/src/can.rs index d1bd82bf7..a3609e841 100644 --- a/src/can.rs +++ b/src/can.rs @@ -1,17 +1,35 @@ //! Controller Area Network +/// CAN Identifier +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +pub enum Id { + /// Standard 11bit Identifier (0..=0x7FF) + Standard(u32), + + /// Extended 29bit Identifier (0..=0x1FFF_FFFF) + Extended(u32), +} + +impl Id { + /// Returs true when the identifier is valid, false otherwise. + pub fn valid(self) -> bool { + match self { + Id::Standard(id) if id <= 0x7FF => true, + Id::Extended(id) if id <= 0x1FFF_FFFF => true, + _ => false, + } + } +} + /// A CAN2.0 Frame pub trait Frame: Sized { - /// Creates a new frame with a standard identifier (0..=0x7FF). - /// Returns an error when the the identifier is not valid. - fn new_standard(id: u32, data: &[u8]) -> Result; - - /// Creates a new frame with an extended identifier (0..=0x1FFF_FFFF). - /// Returns an error when the the identifier is not valid. - fn new_extended(id: u32, data: &[u8]) -> Result; + /// Creates a new frame. + /// Returns an error when the the identifier is not valid or the data slice is too long. + fn new(id: Id, data: &[u8]) -> Result; - /// Marks this frame as a remote frame (by setting the RTR bit). - fn with_rtr(&mut self, dlc: usize) -> &mut Self; + /// Creates a new remote frame (RTR bit set). + /// Returns an error when the the identifier is or the data length code (DLC) not valid. + fn new_remote(id: Id, dlc: usize) -> Result; /// Returns true if this frame is a extended frame. fn is_extended(&self) -> bool; @@ -30,7 +48,7 @@ pub trait Frame: Sized { } /// Returns the frame identifier. - fn id(&self) -> u32; + fn id(&self) -> Id; /// Returns the data length code (DLC) which is in the range 0..8. /// From 439242ea01e9f9c8cc2d50316782c74bde01990a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20Kr=C3=B6ger?= Date: Tue, 13 Oct 2020 15:37:24 +0200 Subject: [PATCH 5/8] can: `try_` prefixes and improved documentation --- src/can.rs | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/src/can.rs b/src/can.rs index a3609e841..538a993ce 100644 --- a/src/can.rs +++ b/src/can.rs @@ -68,13 +68,22 @@ pub trait Can { /// Associated error type. type Error; - /// Puts a frame in the transmit buffer. + /// Puts a frame in the transmit buffer to be sent on the bus. /// - /// If the buffer is full, this function will try to replace a lower priority frame - /// and return it. This is to avoid the priority inversion problem. - /// Transmits frames of equal identifier in FIFO fashion. - fn transmit(&mut self, frame: &Self::Frame) -> nb::Result, Self::Error>; + /// 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 try_transmit(&mut self, frame: &Self::Frame) + -> nb::Result, Self::Error>; /// Returns a received frame if available. - fn receive(&mut self) -> nb::Result; + fn try_receive(&mut self) -> nb::Result; } From 95b17a3f84e37b6a34b0432a2d2d0bcca1cf9282 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20Kr=C3=B6ger?= Date: Tue, 13 Oct 2020 16:05:33 +0200 Subject: [PATCH 6/8] can: Add blocking API with default implementation --- src/blocking/can.rs | 42 ++++++++++++++++++++++++++++++++++++++++++ src/blocking/mod.rs | 1 + 2 files changed, 43 insertions(+) create mode 100644 src/blocking/can.rs diff --git a/src/blocking/can.rs b/src/blocking/can.rs new file mode 100644 index 000000000..d0155fcfa --- /dev/null +++ b/src/blocking/can.rs @@ -0,0 +1,42 @@ +//! 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 try_transmit(&mut self, frame: &Self::Frame) -> Result<(), Self::Error>; + + /// Blocks until a frame was received or an error occured. + fn try_receive(&mut self) -> Result; +} + +/// Default implementation of `blocking::can::Can` for implementers of `can::Can` +pub trait Default: crate::can::Can {} + +impl crate::blocking::can::Can for S +where + S: Default, +{ + type Frame = S::Frame; + type Error = S::Error; + + fn try_transmit(&mut self, frame: &Self::Frame) -> Result<(), Self::Error> { + let mut replaced_frame; + let mut frame_to_transmit = frame; + while let Some(f) = nb::block!(self.try_transmit(&frame_to_transmit))? { + replaced_frame = f; + frame_to_transmit = &replaced_frame; + } + Ok(()) + } + + fn try_receive(&mut self) -> Result { + nb::block!(self.try_receive()) + } +} diff --git a/src/blocking/mod.rs b/src/blocking/mod.rs index 3a050f6d2..e3c132826 100644 --- a/src/blocking/mod.rs +++ b/src/blocking/mod.rs @@ -4,6 +4,7 @@ //! traits. To save boilerplate when that's the case a `Default` marker trait may be provided. //! Implementing that marker trait will opt in your type into a blanket implementation. +pub mod can; pub mod delay; pub mod i2c; pub mod rng; From 103b41e0f41cb7f4bef949323e77ab3139a4703d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20Kr=C3=B6ger?= Date: Sat, 14 Nov 2020 10:26:21 +0100 Subject: [PATCH 7/8] Rename blocking trait methods Use `try_read()` / `try_write()` as suggested by @marcelbuesing --- src/blocking/can.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/blocking/can.rs b/src/blocking/can.rs index d0155fcfa..ca01faa2a 100644 --- a/src/blocking/can.rs +++ b/src/blocking/can.rs @@ -10,10 +10,10 @@ pub trait Can { /// Puts a frame in the transmit buffer. Blocks until space is available in /// the transmit buffer. - fn try_transmit(&mut self, frame: &Self::Frame) -> Result<(), Self::Error>; + fn try_write(&mut self, frame: &Self::Frame) -> Result<(), Self::Error>; /// Blocks until a frame was received or an error occured. - fn try_receive(&mut self) -> Result; + fn try_read(&mut self) -> Result; } /// Default implementation of `blocking::can::Can` for implementers of `can::Can` @@ -26,7 +26,7 @@ where type Frame = S::Frame; type Error = S::Error; - fn try_transmit(&mut self, frame: &Self::Frame) -> Result<(), Self::Error> { + fn try_write(&mut self, frame: &Self::Frame) -> Result<(), Self::Error> { let mut replaced_frame; let mut frame_to_transmit = frame; while let Some(f) = nb::block!(self.try_transmit(&frame_to_transmit))? { @@ -36,7 +36,7 @@ where Ok(()) } - fn try_receive(&mut self) -> Result { + fn try_read(&mut self) -> Result { nb::block!(self.try_receive()) } } From 7daeaba21a7380b8ce7eaec820fcd4a48159fa7b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20Kr=C3=B6ger?= Date: Tue, 17 Nov 2020 19:03:47 +0100 Subject: [PATCH 8/8] can: Only allow valid `Id` s to be constructed Use newtypes for the enum variants so they cannot be constructed anymore. Implement the `From` trait for the variants to access the id value as integer. https://github.com/timokroeger/embedded-can/pull/7 --- src/can.rs | 84 +++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 73 insertions(+), 11 deletions(-) diff --git a/src/can.rs b/src/can.rs index 538a993ce..d41673357 100644 --- a/src/can.rs +++ b/src/can.rs @@ -1,34 +1,96 @@ //! Controller Area Network +/// Standard 11bit Identifier (0..=0x7FF) +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +pub struct StandardId(u16); + +impl StandardId { + /// Creates a new standard identifier. + pub fn new(id: u16) -> Result { + if id <= 0x7FF { + Ok(StandardId(id)) + } else { + Err(()) + } + } +} + +impl core::convert::From for u16 { + fn from(id: StandardId) -> u16 { + id.0 + } +} + +impl core::convert::From for u32 { + fn from(id: StandardId) -> u32 { + id.0 as u32 + } +} + +impl ExtendedId { + /// Creates a new extended identifier. + pub fn new(id: u32) -> Result { + if id <= 0x1FFF_FFFF { + Ok(ExtendedId(id)) + } else { + Err(()) + } + } +} + +impl core::convert::From for u32 { + fn from(id: ExtendedId) -> u32 { + id.0 + } +} + +/// Extended 29bit Identifier (0..=0x1FFF_FFFF) +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +pub struct ExtendedId(u32); /// CAN Identifier +/// +/// The variants are wrapped in newtypes so they can only be costructed with valid values. #[derive(Debug, Copy, Clone, Eq, PartialEq)] pub enum Id { /// Standard 11bit Identifier (0..=0x7FF) - Standard(u32), + Standard(StandardId), /// Extended 29bit Identifier (0..=0x1FFF_FFFF) - Extended(u32), + Extended(ExtendedId), } impl Id { - /// Returs true when the identifier is valid, false otherwise. - pub fn valid(self) -> bool { - match self { - Id::Standard(id) if id <= 0x7FF => true, - Id::Extended(id) if id <= 0x1FFF_FFFF => true, - _ => false, - } + /// Creates a new standard identifier. + pub fn new_standard(id: u16) -> Result { + Ok(StandardId::new(id)?.into()) + } + + /// Creates a new extended identifier. + pub fn new_extended(id: u32) -> Result { + Ok(ExtendedId::new(id)?.into()) + } +} + +impl core::convert::From for Id { + fn from(id: StandardId) -> Id { + Id::Standard(id) + } +} + +impl core::convert::From for Id { + fn from(id: ExtendedId) -> Id { + Id::Extended(id) } } /// A CAN2.0 Frame pub trait Frame: Sized { /// Creates a new frame. - /// Returns an error when the the identifier is not valid or the data slice is too long. + /// Returns an error when the data slice is too long. fn new(id: Id, data: &[u8]) -> Result; /// Creates a new remote frame (RTR bit set). - /// Returns an error when the the identifier is or the data length code (DLC) not valid. + /// Returns an error when the data length code (DLC) is not valid. fn new_remote(id: Id, dlc: usize) -> Result; /// Returns true if this frame is a extended frame.