From 8319e5d1d919204097e7424e0561303cc2097648 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20Kr=C3=B6ger?= Date: Fri, 1 May 2020 07:48:02 +0200 Subject: [PATCH 01/43] can: Add basic types This lays the groundwork for the upcoming high level CAN driver implementation. The types are directly modelled after STM32 register and bit layouts. With `#[repr(C)]` the types could be copied directly to the memory mapped CAN registers. It has to be investigated if this makes sense considering the tradeoff between performance and the use of unsafe code. --- src/can.rs | 151 +++++++++++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 5 ++ 2 files changed, 156 insertions(+) create mode 100644 src/can.rs diff --git a/src/can.rs b/src/can.rs new file mode 100644 index 00000000..7e39de75 --- /dev/null +++ b/src/can.rs @@ -0,0 +1,151 @@ +//! # Controller Area Network (CAN) Interface +//! +//! ## Alternate function remapping +//! +//! TX: Alternate Push-Pull Output +//! RX: Input Floating Input +//! +//! ### CAN1 +//! +//! | Function | NoRemap | Remap | +//! |----------|---------|-------| +//! | TX | PA12 | PB9 | +//! | RX | PA11 | PB8 | +//! +//! ### CAN2 +//! +//! | Function | NoRemap | Remap | +//! |----------|---------|-------| +//! | TX | PB6 | PB13 | +//! | RX | PB5 | PB12 | + +/// Identifier of a CAN message. +/// +/// Can be either a standard identifier (11bit, Range: 0..0x3FF) +/// or a extendended identifier (29bit , Range: 0..0x1FFFFFFF). +#[derive(Clone, Copy)] +pub struct Id(u32); + +impl Id { + const STANDARD_SHIFT: u32 = 21; // 11 valid bits. Mask: 0xFFE0_0000 + const EXTENDED_SHIFT: u32 = 3; // 29 valid bits. Mask: 0xFFFF_FFF8 + const EID_MASK: u32 = 0x0000_0004; + const RTR_MASK: u32 = 0x0000_0002; + + /// Creates a new standard identifier (11bit, Range: 0..0x7FF) + /// + /// Ids outside the allowed range are silently truncated. + pub fn new_standard(id: u32) -> Self { + assert!(id < 0x7FF); + Self(id << Self::STANDARD_SHIFT) + } + + /// Creates a new extendended identifier (29bit , Range: 0..0x1FFFFFFF). + /// + /// Ids outside the allowed range are silently truncated. + pub fn new_extended(id: u32) -> Id { + assert!(id < 0x1FFF_FFFF); + Self(id << Self::EXTENDED_SHIFT | Self::EID_MASK) + } + + /// Sets the remote transmission (RTR) flag. This marks the identifier as + /// being part of a remote frame. + fn with_rtr(&self) -> Id { + Self(self.0 | Self::RTR_MASK) + } + + /// Returns the identifier. + /// + /// It is up to the user to check if it is an standard or extended id. + pub fn id(&self) -> u32 { + if self.is_extended() { + self.0 >> Self::EXTENDED_SHIFT + } else { + self.0 >> Self::STANDARD_SHIFT + } + } + + /// Returns `true` if the identifier is an extended identifier. + pub fn is_extended(&self) -> bool { + self.0 & Self::EID_MASK != 0 + } + + /// Returns `true` if the identifier is a standard identifier. + pub fn is_standard(&self) -> bool { + !self.is_extended() + } + + /// Returns `true` if the identifer is part of a remote frame (RTR bit set). + fn rtr(&self) -> bool { + self.0 & Self::RTR_MASK != 0 + } +} + +/// A CAN data or remote frame. +pub struct Frame { + id: Id, + dlc: usize, + data: [u8; 8], +} + +impl Frame { + /// Creates a new data frame. + pub fn new(id: Id, data: &[u8]) -> Self { + assert!(!id.rtr()); + + let mut frame = Self { + id, + dlc: data.len(), + data: [0; 8], + }; + frame.data[0..data.len()].copy_from_slice(data); + frame + } + + /// Creates ane new frame with a standard identifier. + pub fn new_standard(id: u32, data: &[u8]) -> Self { + Self::new(Id::new_standard(id), data) + } + + /// Creates ane new frame with an extended identifier. + pub fn new_extended(id: u32, data: &[u8]) -> Self { + Self::new(Id::new_extended(id), data) + } + + /// Marks this frame as a remote frame. + pub fn set_remote(&mut self) -> &mut Self { + self.id = self.id.with_rtr(); + self.dlc = 0; + self + } + + /// Returns true if this `Frame` is a extended frame + pub fn is_extended(&self) -> bool { + self.id.is_extended() + } + + /// Returns true if this `Frame` is a standard frame + pub fn is_standard(&self) -> bool { + self.id.is_standard() + } + + /// Returns true if this `Frame` is a remote frame + pub fn is_remote_frame(&self) -> bool { + self.id.rtr() + } + + /// Returns true if this `Frame` is a data frame + pub fn is_data_frame(&self) -> bool { + !self.is_remote_frame() + } + + /// Returns the frame identifier. + pub fn id(&self) -> u32 { + self.id.id() + } + + // Returns the frame data. + pub fn data(&self) -> &[u8] { + &self.data[0..self.dlc] + } +} diff --git a/src/lib.rs b/src/lib.rs index f1bf27dd..55847c2b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -144,6 +144,11 @@ pub mod afio; pub mod backup_domain; #[cfg(feature = "device-selected")] pub mod bb; +#[cfg(all( + feature = "device-selected", + any(feature = "stm32f103", feature = "connectivity") +))] +pub mod can; #[cfg(feature = "device-selected")] pub mod crc; #[cfg(feature = "device-selected")] From 0974b15898cf1de98a8003cd5fb4ac964efc2bf9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20Kr=C3=B6ger?= Date: Fri, 1 May 2020 08:50:15 +0200 Subject: [PATCH 02/43] can: API constructors Use traits to abstract away differences between CAN1, CAN2 and the different devices. --- src/can.rs | 128 +++++++++++++++++++++++++++++++++++++++++++++++++++++ src/rcc.rs | 2 + 2 files changed, 130 insertions(+) diff --git a/src/can.rs b/src/can.rs index 7e39de75..0f353952 100644 --- a/src/can.rs +++ b/src/can.rs @@ -19,6 +19,22 @@ //! | TX | PB6 | PB13 | //! | RX | PB5 | PB12 | +use crate::afio::MAPR; +#[cfg(feature = "connectivity")] +use crate::gpio::gpiob::{PB12, PB13, PB5, PB6}; +use crate::gpio::{ + gpioa::{PA11, PA12}, + gpiob::{PB8, PB9}, + Alternate, Floating, Input, PushPull, +}; +use crate::pac::CAN1; +#[cfg(feature = "connectivity")] +use crate::pac::CAN2; +#[cfg(not(feature = "connectivity"))] +use crate::pac::USB; +use crate::rcc::sealed::RccBus; +use core::marker::PhantomData; + /// Identifier of a CAN message. /// /// Can be either a standard identifier (11bit, Range: 0..0x3FF) @@ -149,3 +165,115 @@ impl Frame { &self.data[0..self.dlc] } } + +// Seal the traits so that they cannot be implemented outside side this crate. +mod traits { + pub trait Instance: crate::rcc::Enable { + const REGISTERS: *const crate::pac::can1::RegisterBlock; + } + + pub trait Pins { + type CAN: Instance; + fn remap(mapr: &mut super::MAPR); + } +} + +impl traits::Instance for CAN1 { + const REGISTERS: *const crate::pac::can1::RegisterBlock = Self::ptr(); +} + +#[cfg(feature = "connectivity")] +impl traits::Instance for CAN2 { + // Register blocks are the same except for the filter registers. + // Those are only available on CAN1. + const REGISTERS: *const crate::pac::can1::RegisterBlock = Self::ptr() as *const _; +} + +impl traits::Pins for (PA12>, PA11>) { + type CAN = CAN1; + + fn remap(mapr: &mut MAPR) { + #[cfg(not(feature = "connectivity"))] + mapr.modify_mapr(|_, w| unsafe { w.can_remap().bits(0) }); + #[cfg(feature = "connectivity")] + mapr.modify_mapr(|_, w| unsafe { w.can1_remap().bits(0) }); + } +} + +impl traits::Pins for (PB9>, PB8>) { + type CAN = CAN1; + + fn remap(mapr: &mut MAPR) { + #[cfg(not(feature = "connectivity"))] + mapr.modify_mapr(|_, w| unsafe { w.can_remap().bits(0x10) }); + #[cfg(feature = "connectivity")] + mapr.modify_mapr(|_, w| unsafe { w.can1_remap().bits(0x10) }); + } +} + +#[cfg(feature = "connectivity")] +impl traits::Pins for (PB13>, PB12>) { + type CAN = CAN2; + + fn remap(mapr: &mut MAPR) { + mapr.modify_mapr(|_, w| w.can2_remap().clear_bit()); + } +} + +#[cfg(feature = "connectivity")] +impl traits::Pins for (PB6>, PB5>) { + type CAN = CAN2; + + fn remap(mapr: &mut MAPR) { + mapr.modify_mapr(|_, w| w.can2_remap().set_bit()); + } +} + +/// Interface to the CAN peripheral. +pub struct Can { + _can: PhantomData, +} + +impl Can +where + Instance: traits::Instance, +{ + /// Creates a CAN interaface. + /// + /// CAN shares SRAM with the USB peripheral. Take ownership of USB to + /// prevent accidental shared usage. + #[cfg(not(feature = "connectivity"))] + pub fn new( + _can: Instance, + _pins: Pins, + mapr: &mut MAPR, + apb: &mut ::Bus, + _usb: USB, + ) -> Can + where + Pins: traits::Pins, + { + Pins::remap(mapr); + Self::new_internal(apb) + } + + /// Creates a CAN interaface. + #[cfg(feature = "connectivity")] + pub fn new( + _can: Instance, + _pins: Pins, + mapr: &mut MAPR, + apb: &mut ::Bus, + ) -> Can + where + Pins: traits::Pins, + { + Pins::remap(mapr); + Self::new_internal(apb) + } + + fn new_internal(apb: &mut ::Bus) -> Can { + Instance::enable(apb); + Can { _can: PhantomData } + } +} diff --git a/src/rcc.rs b/src/rcc.rs index 74cdde62..edb31030 100644 --- a/src/rcc.rs +++ b/src/rcc.rs @@ -651,6 +651,8 @@ bus! { #[cfg(feature = "connectivity")] bus! { ADC2 => (APB2, adc2en, adc2rst), + CAN1 => (APB1, can1en, can1rst), + CAN2 => (APB1, can2en, can2rst), } #[cfg(all(feature = "stm32f103", feature = "high",))] bus! { From 80ae548fb9c90cd3efedbf4b9810b687dec438f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20Kr=C3=B6ger?= Date: Fri, 1 May 2020 11:19:06 +0200 Subject: [PATCH 03/43] can: Basic configuration to enable the peripheral Verified by checking that the CAN peripheral sets the ACK bit for messages on the bus. --- src/can.rs | 47 ++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 44 insertions(+), 3 deletions(-) diff --git a/src/can.rs b/src/can.rs index 0f353952..637ae9a6 100644 --- a/src/can.rs +++ b/src/can.rs @@ -33,7 +33,7 @@ use crate::pac::CAN2; #[cfg(not(feature = "connectivity"))] use crate::pac::USB; use crate::rcc::sealed::RccBus; -use core::marker::PhantomData; +use core::{convert::Infallible, marker::PhantomData}; /// Identifier of a CAN message. /// @@ -179,14 +179,14 @@ mod traits { } impl traits::Instance for CAN1 { - const REGISTERS: *const crate::pac::can1::RegisterBlock = Self::ptr(); + const REGISTERS: *const crate::pac::can1::RegisterBlock = CAN1::ptr(); } #[cfg(feature = "connectivity")] impl traits::Instance for CAN2 { // Register blocks are the same except for the filter registers. // Those are only available on CAN1. - const REGISTERS: *const crate::pac::can1::RegisterBlock = Self::ptr() as *const _; + const REGISTERS: *const crate::pac::can1::RegisterBlock = CAN2::ptr() as *const _; } impl traits::Pins for (PA12>, PA11>) { @@ -276,4 +276,45 @@ where Instance::enable(apb); Can { _can: PhantomData } } + + /// Configures the bit timings. + /// + /// Use http://www.bittiming.can-wiki.info/ to calculate a safe parameter + /// value. Puts the peripheral in sleep mode. `Can::enable()` must be called + /// afterwards to start reception and transmission. + pub unsafe fn set_bit_timing(&mut self, btr: u32) { + let can = &*Instance::REGISTERS; + + can.mcr.modify(|_, w| w.sleep().clear_bit().inrq().set_bit()); + while can.msr.read().inak().bit_is_clear() {} + can.btr.write(|w| w.bits(btr)); + self.sleep(); + } + + /// Start reception and transmission. + /// + /// Waits for 11 consecutive recessive bits to sync to the CAN bus. + /// When automatic wakup functionality is not used this function must be + /// called to enable the peripheral after a wakeup interrupt. + pub fn enable(&mut self) -> nb::Result<(), Infallible> { + let can = unsafe { &*Instance::REGISTERS }; + let msr = can.msr.read(); + if msr.slak().bit_is_set() { + // TODO: Make automatic bus-off management configurable. + // TODO: Make automatic wakeup configurable. + can.mcr.modify(|_, w| w.abom().set_bit().sleep().clear_bit()); + Err(nb::Error::WouldBlock) + } else { + Ok(()) + } + } + + /// Puts the peripheral in a sleep mode to save power. + /// + /// Reception and transmission is disabled. + pub fn sleep(&mut self) { + let can = unsafe { &*Instance::REGISTERS }; + can.mcr.modify(|_, w| w.sleep().set_bit().inrq().clear_bit()); + while can.msr.read().slak().bit_is_clear() {} + } } From 8ddd345f7a01e663aed3e7273082c714bcef5237 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20Kr=C3=B6ger?= Date: Fri, 1 May 2020 11:45:34 +0200 Subject: [PATCH 04/43] can: Basic transmission --- src/can.rs | 59 +++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 54 insertions(+), 5 deletions(-) diff --git a/src/can.rs b/src/can.rs index 637ae9a6..5c19406b 100644 --- a/src/can.rs +++ b/src/can.rs @@ -33,7 +33,10 @@ use crate::pac::CAN2; #[cfg(not(feature = "connectivity"))] use crate::pac::USB; use crate::rcc::sealed::RccBus; -use core::{convert::Infallible, marker::PhantomData}; +use core::{ + convert::{Infallible, TryInto}, + marker::PhantomData, +}; /// Identifier of a CAN message. /// @@ -232,6 +235,7 @@ impl traits::Pins for (PB6>, PB5>) { /// Interface to the CAN peripheral. pub struct Can { _can: PhantomData, + tx: Option>, } impl Can @@ -274,7 +278,10 @@ where fn new_internal(apb: &mut ::Bus) -> Can { Instance::enable(apb); - Can { _can: PhantomData } + Can { + _can: PhantomData, + tx: Some(Tx { _can: PhantomData }), + } } /// Configures the bit timings. @@ -285,7 +292,8 @@ where pub unsafe fn set_bit_timing(&mut self, btr: u32) { let can = &*Instance::REGISTERS; - can.mcr.modify(|_, w| w.sleep().clear_bit().inrq().set_bit()); + can.mcr + .modify(|_, w| w.sleep().clear_bit().inrq().set_bit()); while can.msr.read().inak().bit_is_clear() {} can.btr.write(|w| w.bits(btr)); self.sleep(); @@ -302,7 +310,8 @@ where if msr.slak().bit_is_set() { // TODO: Make automatic bus-off management configurable. // TODO: Make automatic wakeup configurable. - can.mcr.modify(|_, w| w.abom().set_bit().sleep().clear_bit()); + can.mcr + .modify(|_, w| w.abom().set_bit().sleep().clear_bit()); Err(nb::Error::WouldBlock) } else { Ok(()) @@ -314,7 +323,47 @@ where /// Reception and transmission is disabled. pub fn sleep(&mut self) { let can = unsafe { &*Instance::REGISTERS }; - can.mcr.modify(|_, w| w.sleep().set_bit().inrq().clear_bit()); + can.mcr + .modify(|_, w| w.sleep().set_bit().inrq().clear_bit()); while can.msr.read().slak().bit_is_clear() {} } + + /// Returns the transmitter interface. + /// + /// Only the first calls returns a valid transmitter. Subsequent calls + /// return `None`. + pub fn take_tx(&mut self) -> Option> { + self.tx.take() + } +} + +/// Interface to the CAN transmitter. +pub struct Tx { + _can: PhantomData, +} + +impl Tx +where + Instance: traits::Instance, +{ + // TODO: Use more than just the first mailbox. + pub fn transmit(&mut self, frame: &Frame) -> nb::Result, Infallible> { + let can = unsafe { &*Instance::REGISTERS }; + + let tsr = can.tsr.read(); + if tsr.tme0().bit_is_clear() { + return Err(nb::Error::WouldBlock) + } + + let tx = &can.tx[0]; + tx.tdtr.write(|w| unsafe { w.dlc().bits(frame.dlc as u8) }); + tx.tdlr + .write(|w| unsafe { w.bits(u32::from_ne_bytes(frame.data[0..4].try_into().unwrap())) }); + tx.tdhr + .write(|w| unsafe { w.bits(u32::from_ne_bytes(frame.data[4..8].try_into().unwrap())) }); + tx.tir + .write(|w| unsafe { w.bits(frame.id.0).txrq().set_bit() }); + + Ok(None) + } } From e2861e8fcd830f52e6c0607d6748ac1b8de738bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20Kr=C3=B6ger?= Date: Fri, 1 May 2020 22:42:35 +0200 Subject: [PATCH 05/43] can: More sophisticated transmit logic --- src/can.rs | 101 +++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 90 insertions(+), 11 deletions(-) diff --git a/src/can.rs b/src/can.rs index 5c19406b..a0b77200 100644 --- a/src/can.rs +++ b/src/can.rs @@ -42,7 +42,7 @@ use core::{ /// /// Can be either a standard identifier (11bit, Range: 0..0x3FF) /// or a extendended identifier (29bit , Range: 0..0x1FFFFFFF). -#[derive(Clone, Copy)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)] pub struct Id(u32); impl Id { @@ -67,6 +67,10 @@ impl Id { Self(id << Self::EXTENDED_SHIFT | Self::EID_MASK) } + fn from_register(reg_bits: u32) -> Id { + Self(reg_bits & 0xFFFF_FFFE) + } + /// Sets the remote transmission (RTR) flag. This marks the identifier as /// being part of a remote frame. fn with_rtr(&self) -> Id { @@ -101,6 +105,7 @@ impl Id { } /// A CAN data or remote frame. +#[derive(Clone, Debug)] pub struct Frame { id: Id, dlc: usize, @@ -342,28 +347,102 @@ pub struct Tx { _can: PhantomData, } +const fn ok_mask(idx: usize) -> u32 { + 0x02 << (8 * idx) +} + +const fn abort_mask(idx: usize) -> u32 { + 0x80 << (8 * idx) +} + impl Tx where Instance: traits::Instance, { - // TODO: Use more than just the first mailbox. pub fn transmit(&mut self, frame: &Frame) -> nb::Result, Infallible> { let can = unsafe { &*Instance::REGISTERS }; let tsr = can.tsr.read(); - if tsr.tme0().bit_is_clear() { - return Err(nb::Error::WouldBlock) + + // Get a free mailbox or the one with the lowest priority. + let idx = tsr.code().bits() as usize; + let tx = &can.tx[idx]; + + // Check for a free mailbox. + if tsr.tme0().bit_is_set() || tsr.tme1().bit_is_set() || tsr.tme2().bit_is_set() { + Self::write_tx_mailbox(tx, frame); + return Ok(None); } - let tx = &can.tx[0]; - tx.tdtr.write(|w| unsafe { w.dlc().bits(frame.dlc as u8) }); - tx.tdlr + // Read the pending frame's id to check its priority. + let tir = tx.tir.read(); + if tir.txrq().bit_is_clear() { + // All pending frames where transmitted in the meantime and all + // mailboxes are free. This can only happen when a higher priority + // interrupt routine is running preemtively. Otherwise this routine + // always executes faster than a pending frames can be sent. + Self::write_tx_mailbox(tx, frame); + return Ok(None); + } + + // Check priority by comparing the identifiers including the EID and RTR + // bits: Standard frames have a higher priority than extended frames and + // data frames have a higher priority than remote frames. + if frame.id < Id::from_register(tir.bits()) { + // The new frame has a higher priority (lower identifier value). + + // Abort the pending transfer. + can.tsr.write(|w| unsafe { w.bits(abort_mask(idx)) }); + + // Wait for the transfer to be finished either because it was + // aborted or because it was successfull in the meantime. + let aborted = loop { + let tsr = can.tsr.read().bits(); + if tsr & abort_mask(idx) == 0 { + break tsr & ok_mask(idx) == 0; + } + }; + + if !aborted { + // An abort was requested while the frame was already being sent + // on the bus. The transfer finished successfully and all + // mailboxes are free. This can happen for small prescaler + // values (e.g. 1MBit/s bit timing with a source clock of 8MHz) + // or when a higher priority ISR runs. + Self::write_tx_mailbox(tx, frame); + return Ok(None); + } + + // Read the prending frame. + let tir = tx.tir.read(); + let mut pending_frame = Frame { + id: Id(tir.bits()), + dlc: tx.tdtr.read().dlc().bits() as usize, + data: [0; 8], + }; + pending_frame.data[0..4].copy_from_slice(&tx.tdlr.read().bits().to_ne_bytes()); + pending_frame.data[4..8].copy_from_slice(&tx.tdhr.read().bits().to_ne_bytes()); + + Self::write_tx_mailbox(tx, frame); + Ok(Some(pending_frame)) + } else { + // All mailboxes filled with messages of higher priority. + Err(nb::Error::WouldBlock) + } + } + + fn write_tx_mailbox(tx_mb: &crate::pac::can1::TX, frame: &Frame) { + tx_mb + .tdtr + .write(|w| unsafe { w.dlc().bits(frame.dlc as u8) }); + tx_mb + .tdlr .write(|w| unsafe { w.bits(u32::from_ne_bytes(frame.data[0..4].try_into().unwrap())) }); - tx.tdhr + tx_mb + .tdhr .write(|w| unsafe { w.bits(u32::from_ne_bytes(frame.data[4..8].try_into().unwrap())) }); - tx.tir + tx_mb + .tir .write(|w| unsafe { w.bits(frame.id.0).txrq().set_bit() }); - - Ok(None) } } From 2a121bc379f3a61416a23b8c0f934077a1fc3158 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20Kr=C3=B6ger?= Date: Sat, 2 May 2020 15:46:45 +0200 Subject: [PATCH 06/43] can: Use UAVCAN transmit approach For details see the UAVCAN Specification v1.0-alpha Section 4.2.4.3. Additional swap out frames so that even when rare a priority inversion does not happen. --- src/can.rs | 143 +++++++++++++++++++++++++++++------------------------ 1 file changed, 78 insertions(+), 65 deletions(-) diff --git a/src/can.rs b/src/can.rs index a0b77200..840e1ed1 100644 --- a/src/can.rs +++ b/src/can.rs @@ -27,11 +27,11 @@ use crate::gpio::{ gpiob::{PB8, PB9}, Alternate, Floating, Input, PushPull, }; -use crate::pac::CAN1; #[cfg(feature = "connectivity")] use crate::pac::CAN2; #[cfg(not(feature = "connectivity"))] use crate::pac::USB; +use crate::pac::{can1::TX, CAN1}; use crate::rcc::sealed::RccBus; use core::{ convert::{Infallible, TryInto}, @@ -42,7 +42,7 @@ use core::{ /// /// Can be either a standard identifier (11bit, Range: 0..0x3FF) /// or a extendended identifier (29bit , Range: 0..0x1FFFFFFF). -#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)] +#[derive(Clone, Copy, Debug)] pub struct Id(u32); impl Id { @@ -67,10 +67,6 @@ impl Id { Self(id << Self::EXTENDED_SHIFT | Self::EID_MASK) } - fn from_register(reg_bits: u32) -> Id { - Self(reg_bits & 0xFFFF_FFFE) - } - /// Sets the remote transmission (RTR) flag. This marks the identifier as /// being part of a remote frame. fn with_rtr(&self) -> Id { @@ -362,73 +358,90 @@ where pub fn transmit(&mut self, frame: &Frame) -> nb::Result, Infallible> { let can = unsafe { &*Instance::REGISTERS }; + // Get the index of free mailbox or of one with the lowest priority. let tsr = can.tsr.read(); - - // Get a free mailbox or the one with the lowest priority. let idx = tsr.code().bits() as usize; - let tx = &can.tx[idx]; + let mb = unsafe { &can.tx.get_unchecked(idx) }; - // Check for a free mailbox. - if tsr.tme0().bit_is_set() || tsr.tme1().bit_is_set() || tsr.tme2().bit_is_set() { - Self::write_tx_mailbox(tx, frame); - return Ok(None); - } + let empty_flags = (tsr.bits() >> 26) & 0b111; + let pending_frame = if empty_flags == 0b111 { + // All mailboxes are available: Send frame without performing any checks. + None + } else { + // High priority frames are transmitted first by the mailbox system. + // Frames with identical identifier shall be transmitted in FIFO order. + // The controller schedules pending frames of same priority based on the + // mailbox index instead. As a workaround check all pending mailboxes + // and only accept higher priority frames. + Self::check_priority(&can.tx[0], frame.id)?; + Self::check_priority(&can.tx[1], frame.id)?; + Self::check_priority(&can.tx[2], frame.id)?; + + if empty_flags != 0b000 { + // There was a free mailbox. + None + } else { + // No free mailbox is available. This can only happen when three frames with + // descending priority were requested for transmission and all of them are + // blocked by bus traffic with even higher priority. + // To prevent a priority inversion abort and replace the lowest priority frame. + if Self::abort(idx) { + // Read back the pending frame. + let mut pending_frame = Frame { + id: Id(mb.tir.read().bits()), + dlc: mb.tdtr.read().dlc().bits() as usize, + data: [0; 8], + }; + pending_frame.data[0..4].copy_from_slice(&mb.tdlr.read().bits().to_ne_bytes()); + pending_frame.data[4..8].copy_from_slice(&mb.tdhr.read().bits().to_ne_bytes()); + + Some(pending_frame) + } else { + // Abort request failed because the frame was already sent (or being sent) on + // the bus. All mailboxes are now free. This can happen for small prescaler + // values (e.g. 1MBit/s bit timing with a source clock of 8MHz) or when an ISR + // has preemted the execution. + None + } + } + }; - // Read the pending frame's id to check its priority. - let tir = tx.tir.read(); - if tir.txrq().bit_is_clear() { - // All pending frames where transmitted in the meantime and all - // mailboxes are free. This can only happen when a higher priority - // interrupt routine is running preemtively. Otherwise this routine - // always executes faster than a pending frames can be sent. - Self::write_tx_mailbox(tx, frame); - return Ok(None); - } + Self::write_tx_mailbox(mb, frame); + Ok(pending_frame) + } - // Check priority by comparing the identifiers including the EID and RTR - // bits: Standard frames have a higher priority than extended frames and - // data frames have a higher priority than remote frames. - if frame.id < Id::from_register(tir.bits()) { - // The new frame has a higher priority (lower identifier value). - - // Abort the pending transfer. - can.tsr.write(|w| unsafe { w.bits(abort_mask(idx)) }); - - // Wait for the transfer to be finished either because it was - // aborted or because it was successfull in the meantime. - let aborted = loop { - let tsr = can.tsr.read().bits(); - if tsr & abort_mask(idx) == 0 { - break tsr & ok_mask(idx) == 0; - } - }; - - if !aborted { - // An abort was requested while the frame was already being sent - // on the bus. The transfer finished successfully and all - // mailboxes are free. This can happen for small prescaler - // values (e.g. 1MBit/s bit timing with a source clock of 8MHz) - // or when a higher priority ISR runs. - Self::write_tx_mailbox(tx, frame); - return Ok(None); + /// Tries to abort a pending frame. Returns `true` when aborted. + fn abort(idx: usize) -> bool { + let can = unsafe { &*Instance::REGISTERS }; + + can.tsr.write(|w| unsafe { w.bits(abort_mask(idx)) }); + + // Wait for the abort request to be finished. + loop { + let tsr = can.tsr.read().bits(); + if tsr & abort_mask(idx) == 0 { + break tsr & ok_mask(idx) == 0; } + } + } - // Read the prending frame. - let tir = tx.tir.read(); - let mut pending_frame = Frame { - id: Id(tir.bits()), - dlc: tx.tdtr.read().dlc().bits() as usize, - data: [0; 8], - }; - pending_frame.data[0..4].copy_from_slice(&tx.tdlr.read().bits().to_ne_bytes()); - pending_frame.data[4..8].copy_from_slice(&tx.tdhr.read().bits().to_ne_bytes()); - - Self::write_tx_mailbox(tx, frame); - Ok(Some(pending_frame)) - } else { - // All mailboxes filled with messages of higher priority. - Err(nb::Error::WouldBlock) + /// Returns `Ok` when the mailbox is free or has a lower priority than + /// identifier than `id`. + fn check_priority(mb: &TX, id: Id) -> nb::Result<(), Infallible> { + // Read the pending frame's id to check its priority. + let tir = mb.tir.read(); + + // Check the priority by comparing the identifiers including the EID and RTR + // bits: Standard frames have a higher priority than extended frames and data + // frames have a higher priority than remote frames. + // Also make sure the frame has not finished transmission in the meantime. + if tir.txrq().bit_is_set() && id.0 >= tir.bits() & 0xFFFF_FFFE { + // There's a mailbox whose priority is higher or equal + // the priority of the new frame. + return Err(nb::Error::WouldBlock); } + + Ok(()) } fn write_tx_mailbox(tx_mb: &crate::pac::can1::TX, frame: &Frame) { From e937ed8ed9345d382fc202cef6384a876cd2cf7b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20Kr=C3=B6ger?= Date: Sun, 3 May 2020 11:14:00 +0200 Subject: [PATCH 07/43] can: Document `Tx::transmit()` --- src/can.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/can.rs b/src/can.rs index 840e1ed1..3ef0e58a 100644 --- a/src/can.rs +++ b/src/can.rs @@ -355,6 +355,12 @@ impl Tx where Instance: traits::Instance, { + /// Puts a CAN frame in a free transmit mailbox for transmission on the bus. + /// + /// Frames are transmitted to the bus based on their priority (identifier). + /// Transmit order is preserved for frames with identical identifiers. + /// If all transmit mailboxes are full a higher priority frame replaces the + /// lowest priority frame which is returned as `Ok(Some(frame))`. pub fn transmit(&mut self, frame: &Frame) -> nb::Result, Infallible> { let can = unsafe { &*Instance::REGISTERS }; From 77e66b1bfd316cac7a2f763e6158bf9455ae62fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20Kr=C3=B6ger?= Date: Sun, 3 May 2020 11:29:52 +0200 Subject: [PATCH 08/43] can: clippy suggestions --- src/can.rs | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/can.rs b/src/can.rs index 3ef0e58a..8f931cb4 100644 --- a/src/can.rs +++ b/src/can.rs @@ -69,14 +69,14 @@ impl Id { /// Sets the remote transmission (RTR) flag. This marks the identifier as /// being part of a remote frame. - fn with_rtr(&self) -> Id { + fn with_rtr(self) -> Id { Self(self.0 | Self::RTR_MASK) } /// Returns the identifier. /// /// It is up to the user to check if it is an standard or extended id. - pub fn id(&self) -> u32 { + pub fn id(self) -> u32 { if self.is_extended() { self.0 >> Self::EXTENDED_SHIFT } else { @@ -85,17 +85,17 @@ impl Id { } /// Returns `true` if the identifier is an extended identifier. - pub fn is_extended(&self) -> bool { + pub fn is_extended(self) -> bool { self.0 & Self::EID_MASK != 0 } /// Returns `true` if the identifier is a standard identifier. - pub fn is_standard(&self) -> bool { + pub fn is_standard(self) -> bool { !self.is_extended() } /// Returns `true` if the identifer is part of a remote frame (RTR bit set). - fn rtr(&self) -> bool { + fn rtr(self) -> bool { self.0 & Self::RTR_MASK != 0 } } @@ -287,9 +287,11 @@ where /// Configures the bit timings. /// - /// Use http://www.bittiming.can-wiki.info/ to calculate a safe parameter - /// value. Puts the peripheral in sleep mode. `Can::enable()` must be called - /// afterwards to start reception and transmission. + /// Puts the peripheral in sleep mode. `Can::enable()` must be called afterwards + /// to start reception and transmission. + /// + /// # Safety + /// Use http://www.bittiming.can-wiki.info/ to calculate a safe parameter value. pub unsafe fn set_bit_timing(&mut self, btr: u32) { let can = &*Instance::REGISTERS; From d6fb675627af03690a2a75f6d34fa2140e6682ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20Kr=C3=B6ger?= Date: Sun, 3 May 2020 12:56:05 +0200 Subject: [PATCH 09/43] can: Implement `Ord` for ids --- src/can.rs | 31 ++++++++++++++++++++----------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/src/can.rs b/src/can.rs index 8f931cb4..642cfd2d 100644 --- a/src/can.rs +++ b/src/can.rs @@ -40,9 +40,16 @@ use core::{ /// Identifier of a CAN message. /// -/// Can be either a standard identifier (11bit, Range: 0..0x3FF) -/// or a extendended identifier (29bit , Range: 0..0x1FFFFFFF). -#[derive(Clone, Copy, Debug)] +/// Can be either a standard identifier (11bit, Range: 0..0x3FF) or a +/// extendended identifier (29bit , Range: 0..0x1FFFFFFF). +/// +/// The `Ord` trait is can be used to determine the frame’s priority this ID +/// belongs to. This works because the EID and RTR flags are included in the +/// underlying integer representaiton. +/// Lower identifier values mean higher priority. Additionally standard frames +/// have a higher priority than extended frames and data frames have a higher +/// priority than remote frames. +#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)] pub struct Id(u32); impl Id { @@ -67,6 +74,10 @@ impl Id { Self(id << Self::EXTENDED_SHIFT | Self::EID_MASK) } + fn from_register(reg: u32) -> Id { + Self(reg & 0xFFFF_FFFE) + } + /// Sets the remote transmission (RTR) flag. This marks the identifier as /// being part of a remote frame. fn with_rtr(self) -> Id { @@ -76,7 +87,7 @@ impl Id { /// Returns the identifier. /// /// It is up to the user to check if it is an standard or extended id. - pub fn id(self) -> u32 { + pub fn as_u32(self) -> u32 { if self.is_extended() { self.0 >> Self::EXTENDED_SHIFT } else { @@ -160,8 +171,8 @@ impl Frame { } /// Returns the frame identifier. - pub fn id(&self) -> u32 { - self.id.id() + pub fn id(&self) -> Id { + self.id } // Returns the frame data. @@ -439,11 +450,9 @@ where // Read the pending frame's id to check its priority. let tir = mb.tir.read(); - // Check the priority by comparing the identifiers including the EID and RTR - // bits: Standard frames have a higher priority than extended frames and data - // frames have a higher priority than remote frames. - // Also make sure the frame has not finished transmission in the meantime. - if tir.txrq().bit_is_set() && id.0 >= tir.bits() & 0xFFFF_FFFE { + // Check the priority by comparing the identifiers. But first make sure the + // frame has not finished transmission (`TXRQ` == 0) in the meantime. + if tir.txrq().bit_is_set() && id >= Id::from_register(tir.bits()) { // There's a mailbox whose priority is higher or equal // the priority of the new frame. return Err(nb::Error::WouldBlock); From c0869f4b3ec06797f0efd127124acfce8be77347 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20Kr=C3=B6ger?= Date: Wed, 6 May 2020 21:05:06 +0200 Subject: [PATCH 10/43] can: Impl `Ord` trait for frames --- src/can.rs | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/can.rs b/src/can.rs index 642cfd2d..ccfb8c08 100644 --- a/src/can.rs +++ b/src/can.rs @@ -181,6 +181,26 @@ impl Frame { } } +impl core::cmp::Ord for Frame { + fn cmp(&self, other: &Self) -> core::cmp::Ordering { + self.id().cmp(&other.id()) + } +} + +impl core::cmp::PartialOrd for Frame { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl core::cmp::PartialEq for Frame { + fn eq(&self, other: &Self) -> bool { + self.id() == other.id() + } +} + +impl core::cmp::Eq for Frame {} + // Seal the traits so that they cannot be implemented outside side this crate. mod traits { pub trait Instance: crate::rcc::Enable { From a17809779a8d60afa02d0eca0a352fc501788e9f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20Kr=C3=B6ger?= Date: Wed, 6 May 2020 21:09:51 +0200 Subject: [PATCH 11/43] can: TX interrupt support --- src/can.rs | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/can.rs b/src/can.rs index ccfb8c08..51a75f1e 100644 --- a/src/can.rs +++ b/src/can.rs @@ -369,6 +369,20 @@ where pub fn take_tx(&mut self) -> Option> { self.tx.take() } + + /// Enables the transmit interrupt. + /// + /// The interrupt flags must be cleared with `Tx::clear_interrupt_flags()`. + pub fn enable_tx_interrupt(&mut self) { + let can = unsafe { &*Instance::REGISTERS }; + can.ier.modify(|_, w| w.tmeie().set_bit()); + } + + /// Disables the transmit interrupt. + pub fn disable_tx_interrupt(&mut self) { + let can = unsafe { &*Instance::REGISTERS }; + can.ier.modify(|_, w| w.tmeie().clear_bit()); + } } /// Interface to the CAN transmitter. @@ -495,4 +509,11 @@ where .tir .write(|w| unsafe { w.bits(frame.id.0).txrq().set_bit() }); } + + /// Clears the request complete flag for all mailboxes. + pub fn clear_interrupt_flags(&mut self) { + let can = unsafe { &*Instance::REGISTERS }; + can.tsr + .write(|w| w.rqcp2().set_bit().rqcp1().set_bit().rqcp0().set_bit()); + } } From a1e6c61c03572d71a89151e6c255298d819d808c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20Kr=C3=B6ger?= Date: Wed, 6 May 2020 21:12:35 +0200 Subject: [PATCH 12/43] can: Interrupt based CAN tx example --- Cargo.toml | 6 +- examples/can-rtfm.rs | 160 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 165 insertions(+), 1 deletion(-) create mode 100644 examples/can-rtfm.rs diff --git a/Cargo.toml b/Cargo.toml index e543e37d..58cb6f96 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -45,7 +45,7 @@ panic-semihosting = "0.5.2" panic-itm = "0.4.1" cortex-m-rtfm = "0.5" cortex-m-semihosting = "0.3.3" -heapless = "0.4.3" +heapless = "0.5.5" m = "0.1.1" mfrc522 = "0.2.0" serde_derive = "1.0.90" @@ -131,3 +131,7 @@ required-features = ["rt", "medium"] [[example]] name = "exti" required-features = ["rt"] + +[[example]] +name = "can-rtfm" +required-features = ["rt"] diff --git a/examples/can-rtfm.rs b/examples/can-rtfm.rs new file mode 100644 index 00000000..e1b05cff --- /dev/null +++ b/examples/can-rtfm.rs @@ -0,0 +1,160 @@ +//! Interrupt driven CAN transmitter with RTFM. +//! +//! CAN frames are allocated from a static memory pool and stored in a priority +//! queue (min heap) for transmisison. To start transmission the CAN TX +//! interrupt has to be triggered manually once. With each successful +//! transmission the interrupt is reentered and more data is fetched from the +//! queue. +#![no_main] +#![no_std] + +use heapless::{ + binary_heap::{BinaryHeap, Min}, + consts::*, + pool, + pool::{ + singleton::{Box, Pool}, + Init, + }, +}; +use panic_halt as _; +use rtfm::app; +use stm32f1xx_hal::{ + can::{Can, Frame, Tx}, + pac::{Interrupt, CAN2}, + prelude::*, +}; + +pool!(CanFramePool: Frame); + +fn alloc_frame(id: u32, data: &[u8]) -> Box { + let frame_box = CanFramePool::alloc().unwrap(); + frame_box.init(Frame::new_standard(id, data)) +} + +#[app(device = stm32f1xx_hal::pac, peripherals = true)] +const APP: () = { + struct Resources { + can_tx: Tx, + can_tx_queue: BinaryHeap, U8, Min>, + tx_count: usize, + } + + #[init] + fn init(cx: init::Context) -> init::LateResources { + static mut CAN_POOL_MEMORY: [u8; 256] = [0; 256]; + + let mut flash = cx.device.FLASH.constrain(); + let mut rcc = cx.device.RCC.constrain(); + + let _clocks = rcc.cfgr.use_hse(8.mhz()).freeze(&mut flash.acr); + + let mut gpiob = cx.device.GPIOB.split(&mut rcc.apb2); + let mut afio = cx.device.AFIO.constrain(&mut rcc.apb2); + + let canrx = gpiob.pb5.into_floating_input(&mut gpiob.crl); + let cantx = gpiob.pb6.into_alternate_push_pull(&mut gpiob.crl); + let mut can = Can::new( + cx.device.CAN2, + (cantx, canrx), + &mut afio.mapr, + &mut rcc.apb1, + ); + // APB1 (PCLK1): 8MHz, Bit rate: 125kBit/s, Sample Point 87.5% + // Value was calculated with http://www.bittiming.can-wiki.info/ + unsafe { can.set_bit_timing(0x001c_0003) }; + can.enable_tx_interrupt(); + can.enable().ok(); + + let can_tx = can.take_tx().unwrap(); + let can_tx_queue = BinaryHeap::new(); + CanFramePool::grow(CAN_POOL_MEMORY); + + init::LateResources { + can_tx, + can_tx_queue, + tx_count: 0, + } + } + + #[idle(resources = [can_tx_queue, tx_count])] + fn idle(mut cx: idle::Context) -> ! { + static mut HIGH_PRIORITY_MESSAGES_PENDING: bool = true; + + // Enqueue some messages. Higher ID means lower priority. + cx.resources.can_tx_queue.lock(|tx_queue| { + tx_queue.push(alloc_frame(9, &[0, 1, 2, 4])).unwrap(); + tx_queue.push(alloc_frame(9, &[0, 1, 2, 4])).unwrap(); + tx_queue.push(alloc_frame(8, &[0, 1, 2, 4])).unwrap(); + tx_queue.push(alloc_frame(8, &[0, 1, 2, 4])).unwrap(); + tx_queue.push(alloc_frame(7, &[0, 1, 2, 4])).unwrap(); + tx_queue.push(alloc_frame(7, &[0, 1, 2, 4])).unwrap(); + }); + + // Manually trigger the tx interrupt to start the transmission. + rtfm::pend(Interrupt::CAN2_TX); + + loop { + let tx_count = cx.resources.tx_count.lock(|tx_count| *tx_count); + + // Add some higher priority messages when 3 messages have been sent. + if *HIGH_PRIORITY_MESSAGES_PENDING && tx_count >= 3 { + *HIGH_PRIORITY_MESSAGES_PENDING = false; + + cx.resources.can_tx_queue.lock(|tx_queue| { + tx_queue.push(alloc_frame(3, &[0, 1, 2, 4])).unwrap(); + tx_queue.push(alloc_frame(2, &[0, 1, 2, 4])).unwrap(); + tx_queue.push(alloc_frame(1, &[0, 1, 2, 4])).unwrap(); + }); + } + cortex_m::asm::wfi(); + } + + // Expected bus traffic: + // + // 1. ID: 7 DATA: 00 01 02 04 + // 2. ID: 7 DATA: 00 01 02 04 + // 3. ID: 8 DATA: 00 01 02 04 + // 4. ID: 1 DATA: 00 01 02 04 + // 5. ID: 2 DATA: 00 01 02 04 + // 6. ID: 3 DATA: 00 01 02 04 + // 7. ID: 8 DATA: 00 01 02 04 + // 8. ID: 9 DATA: 00 01 02 04 + // 9. ID: 9 DATA: 00 01 02 04 + // + // The output can look different if there are other nodes on the bus that send + // higher priority messages at the same time. + } + + // This ISR is triggered by each finished frame transmission. + #[task(binds = CAN2_TX, resources = [can_tx, can_tx_queue, tx_count])] + fn can2_tx(cx: can2_tx::Context) { + let tx = cx.resources.can_tx; + let tx_queue = cx.resources.can_tx_queue; + + tx.clear_interrupt_flags(); + + // There is now a free mailbox. Send the next frame if there is still someting + // in the queue. + while let Some(frame) = tx_queue.peek() { + match tx.transmit(&frame) { + Ok(None) => { + tx_queue.pop(); + *cx.resources.tx_count += 1; + } + Ok(pending_frame) => { + // A lower priority frame was replaced with our high priority frame. + // Put the low priority frame back in the transmit queue. + tx_queue.pop(); + if let Some(frame) = pending_frame { + tx_queue + .push(CanFramePool::alloc().unwrap().init(frame)) + .unwrap(); + } + } + Err(nb::Error::WouldBlock) => break, + _ => unreachable!(), + } + } + } +}; From 2b585cc1dd1e908066cff9987bd53775ee81277a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20Kr=C3=B6ger?= Date: Fri, 8 May 2020 16:15:51 +0200 Subject: [PATCH 13/43] can: Split pin assigment from constructor This is needed in preperation for Rx code. Filters are shared between the peripherals so there needs to be a way to enable the clock of CAN1 without taking ownership of the pins (because they may not be used). --- src/can.rs | 37 +++++++++++++------------------------ 1 file changed, 13 insertions(+), 24 deletions(-) diff --git a/src/can.rs b/src/can.rs index 51a75f1e..6e26f567 100644 --- a/src/can.rs +++ b/src/can.rs @@ -32,7 +32,7 @@ use crate::pac::CAN2; #[cfg(not(feature = "connectivity"))] use crate::pac::USB; use crate::pac::{can1::TX, CAN1}; -use crate::rcc::sealed::RccBus; +use crate::rcc::APB1; use core::{ convert::{Infallible, TryInto}, marker::PhantomData, @@ -272,43 +272,24 @@ pub struct Can { impl Can where - Instance: traits::Instance, + Instance: traits::Instance, { /// Creates a CAN interaface. /// /// CAN shares SRAM with the USB peripheral. Take ownership of USB to /// prevent accidental shared usage. #[cfg(not(feature = "connectivity"))] - pub fn new( - _can: Instance, - _pins: Pins, - mapr: &mut MAPR, - apb: &mut ::Bus, - _usb: USB, - ) -> Can - where - Pins: traits::Pins, - { - Pins::remap(mapr); + pub fn new(_can: Instance, apb: &mut APB1, _usb: USB) -> Can { Self::new_internal(apb) } /// Creates a CAN interaface. #[cfg(feature = "connectivity")] - pub fn new( - _can: Instance, - _pins: Pins, - mapr: &mut MAPR, - apb: &mut ::Bus, - ) -> Can - where - Pins: traits::Pins, - { - Pins::remap(mapr); + pub fn new(_can: Instance, apb: &mut APB1) -> Can { Self::new_internal(apb) } - fn new_internal(apb: &mut ::Bus) -> Can { + fn new_internal(apb: &mut APB1) -> Can { Instance::enable(apb); Can { _can: PhantomData, @@ -316,6 +297,14 @@ where } } + /// Routes CAN TX signals and RX signals to pins. + pub fn assign_pins(&self, _pins: Pins, mapr: &mut MAPR) + where + Pins: traits::Pins, + { + Pins::remap(mapr); + } + /// Configures the bit timings. /// /// Puts the peripheral in sleep mode. `Can::enable()` must be called afterwards From 26a7b5f129d942811a471ed0119cf0bf219f7d73 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20Kr=C3=B6ger?= Date: Fri, 8 May 2020 17:52:59 +0200 Subject: [PATCH 14/43] can: Basic RX code --- Cargo.toml | 1 + src/can.rs | 127 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 128 insertions(+) diff --git a/Cargo.toml b/Cargo.toml index 58cb6f96..45ed515d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,6 +21,7 @@ nb = "0.1.2" cortex-m-rt = "0.6.8" stm32f1 = "0.11.0" as-slice = "0.1" +typenum = "1.12.0" [dependencies.void] default-features = false diff --git a/src/can.rs b/src/can.rs index 6e26f567..bebf9370 100644 --- a/src/can.rs +++ b/src/can.rs @@ -20,6 +20,7 @@ //! | RX | PB5 | PB12 | use crate::afio::MAPR; +use crate::bb; #[cfg(feature = "connectivity")] use crate::gpio::gpiob::{PB12, PB13, PB5, PB6}; use crate::gpio::{ @@ -37,6 +38,7 @@ use core::{ convert::{Infallible, TryInto}, marker::PhantomData, }; +pub use typenum::{U0, U1}; /// Identifier of a CAN message. /// @@ -205,6 +207,8 @@ impl core::cmp::Eq for Frame {} mod traits { pub trait Instance: crate::rcc::Enable { const REGISTERS: *const crate::pac::can1::RegisterBlock; + const FILTER_BANK_START: usize; + const FILTER_BANK_STOP: usize; } pub trait Pins { @@ -215,6 +219,8 @@ mod traits { impl traits::Instance for CAN1 { const REGISTERS: *const crate::pac::can1::RegisterBlock = CAN1::ptr(); + const FILTER_BANK_START: usize = 0; + const FILTER_BANK_STOP: usize = 14; } #[cfg(feature = "connectivity")] @@ -222,6 +228,8 @@ impl traits::Instance for CAN2 { // Register blocks are the same except for the filter registers. // Those are only available on CAN1. const REGISTERS: *const crate::pac::can1::RegisterBlock = CAN2::ptr() as *const _; + const FILTER_BANK_START: usize = CAN1::FILTER_BANK_STOP; + const FILTER_BANK_STOP: usize = 28; } impl traits::Pins for (PA12>, PA11>) { @@ -268,6 +276,7 @@ impl traits::Pins for (PB6>, PB5>) { pub struct Can { _can: PhantomData, tx: Option>, + rx: Option<(Rx, Rx)>, } impl Can @@ -291,9 +300,20 @@ where fn new_internal(apb: &mut APB1) -> Can { Instance::enable(apb); + Can { _can: PhantomData, tx: Some(Tx { _can: PhantomData }), + rx: Some(( + Rx { + _can: PhantomData, + _fifo: PhantomData, + }, + Rx { + _can: PhantomData, + _fifo: PhantomData, + }, + )), } } @@ -372,6 +392,76 @@ where let can = unsafe { &*Instance::REGISTERS }; can.ier.modify(|_, w| w.tmeie().clear_bit()); } + + pub fn take_rx( + &mut self, + _filters: Filters, + ) -> Option<(Rx, Rx)> { + self.rx.take() + } +} + +impl Can { + /// Returns the filter part of the CAN peripheral. + #[cfg(not(feature = "connectivity"))] + pub fn split_filters(&mut self) -> Option> { + self.split_filters_internal()?; + Some(Filters(PhantomData)) + } + + /// Returns the filter part of the CAN peripheral. + #[cfg(feature = "connectivity")] + pub fn split_filters(&mut self) -> Option<(Filters, Filters)> { + self.split_filters_internal()?; + Some((Filters(PhantomData), Filters(PhantomData))) + } + + fn split_filters_internal(&mut self) -> Option<()> { + let can = unsafe { &*CAN1::ptr() }; + + if can.fmr.read().finit().bit_is_clear() { + return None; + } + + // Same configuration for all filters banks. + can.fm1r.write(|w| unsafe { w.bits(0x0000_0000) }); // Mask mode + can.fs1r.write(|w| unsafe { w.bits(0xFFFF_FFFF) }); // 32bit scale + + // Filters are alternating between between the FIFO0 and FIFO1. + can.ffa1r.write(|w| unsafe { w.bits(0xAAAA_AAAA) }); + + // Init filter banks. Each used filter must still be enabled individually. + #[allow(unused_unsafe)] + can.fmr.modify(|_, w| unsafe { + #[cfg(feature = "connectivity")] + w.can2sb() + .bits(::FILTER_BANK_STOP as u8); + w.finit().clear_bit() + }); + + Some(()) + } +} + +pub struct Filters(PhantomData); + +impl Filters +where + Instance: traits::Instance, +{ + /// Enables a filter that accepts all messages. + pub fn accept_all(&mut self, fifo_nr: usize) { + let can = unsafe { &*CAN1::ptr() }; + + assert!(fifo_nr == 0 || fifo_nr == 1); + let fb_idx = Instance::FILTER_BANK_START + fifo_nr; + + // For more details on the quirks of bxCAN see: + // https://github.com/UAVCAN/libcanard/blob/8ee343c4edae0e0e4e1c040852aa3d8430f7bf76/drivers/stm32/canard_stm32.c#L471-L513 + can.fb[fb_idx].fr1.write(|w| unsafe { w.bits(0xFFFF_FFFE) }); + can.fb[fb_idx].fr2.write(|w| unsafe { w.bits(0) }); + bb::set(&can.fa1r, fb_idx as u8); + } } /// Interface to the CAN transmitter. @@ -506,3 +596,40 @@ where .write(|w| w.rqcp2().set_bit().rqcp1().set_bit().rqcp0().set_bit()); } } + +/// Interface to a CAN receiver. +pub struct Rx { + _can: PhantomData, + _fifo: PhantomData, +} + +impl Rx +where + Instance: traits::Instance, + FifoNr: typenum::marker_traits::Unsigned, +{ + /// Return a received frame if available. + pub fn receive(&mut self) -> nb::Result { + let rfr = &(unsafe { &*Instance::REGISTERS }.rfr[FifoNr::to_usize()]); + let rx = &(unsafe { &*Instance::REGISTERS }.rx[FifoNr::to_usize()]); + + // Check if a frame is available in the mailbox. + if rfr.read().fmp().bits() == 0 { + return Err(nb::Error::WouldBlock); + } + + // Read the frame. + let mut frame = Frame { + id: Id(rx.rir.read().bits()), + dlc: rx.rdtr.read().dlc().bits() as usize, + data: [0; 8], + }; + frame.data[0..4].copy_from_slice(&rx.rdlr.read().bits().to_ne_bytes()); + frame.data[4..8].copy_from_slice(&rx.rdhr.read().bits().to_ne_bytes()); + + // Release the mailbox. + rfr.write(|w| w.rfom().set_bit()); + + Ok(frame) + } +} From 33e680fe6014c48f212bdda96bd00f7c8b73872a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20Kr=C3=B6ger?= Date: Sat, 9 May 2020 07:30:09 +0200 Subject: [PATCH 15/43] can: Combine both FIFOs in one receiver --- Cargo.toml | 2 +- src/can.rs | 69 +++++++++++++++++++++++++++++------------------------- 2 files changed, 38 insertions(+), 33 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 45ed515d..c56f22b9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,7 +21,6 @@ nb = "0.1.2" cortex-m-rt = "0.6.8" stm32f1 = "0.11.0" as-slice = "0.1" -typenum = "1.12.0" [dependencies.void] default-features = false @@ -99,6 +98,7 @@ codegen-units = 1 [profile.release] codegen-units = 1 debug = true +opt-level = "s" lto = true [[example]] diff --git a/src/can.rs b/src/can.rs index bebf9370..adf099a7 100644 --- a/src/can.rs +++ b/src/can.rs @@ -32,13 +32,15 @@ use crate::gpio::{ use crate::pac::CAN2; #[cfg(not(feature = "connectivity"))] use crate::pac::USB; -use crate::pac::{can1::TX, CAN1}; +use crate::pac::{ + can1::{RFR, RX, TX}, + CAN1, +}; use crate::rcc::APB1; use core::{ convert::{Infallible, TryInto}, marker::PhantomData, }; -pub use typenum::{U0, U1}; /// Identifier of a CAN message. /// @@ -276,7 +278,7 @@ impl traits::Pins for (PB6>, PB5>) { pub struct Can { _can: PhantomData, tx: Option>, - rx: Option<(Rx, Rx)>, + rx: Option>, } impl Can @@ -304,16 +306,7 @@ where Can { _can: PhantomData, tx: Some(Tx { _can: PhantomData }), - rx: Some(( - Rx { - _can: PhantomData, - _fifo: PhantomData, - }, - Rx { - _can: PhantomData, - _fifo: PhantomData, - }, - )), + rx: Some(Rx { _can: PhantomData }), } } @@ -379,7 +372,7 @@ where self.tx.take() } - /// Enables the transmit interrupt. + /// Enables the transmit interrupt CANn_TX. /// /// The interrupt flags must be cleared with `Tx::clear_interrupt_flags()`. pub fn enable_tx_interrupt(&mut self) { @@ -393,12 +386,24 @@ where can.ier.modify(|_, w| w.tmeie().clear_bit()); } - pub fn take_rx( - &mut self, - _filters: Filters, - ) -> Option<(Rx, Rx)> { + pub fn take_rx(&mut self, _filters: Filters) -> Option> { self.rx.take() } + + /// Enables the receive interrupts CANn_RX0 and CANn_RX1. + /// + /// Make sure to register interrupt handlers for both. + /// The interrupt flags are cleared by reading frames with `RX::receive()`. + pub fn enable_rx_interrupts(&mut self) { + let can = unsafe { &*Instance::REGISTERS }; + can.ier.modify(|_, w| w.fmpie1().set_bit().fmpie0().set_bit()); + } + + /// Disables the receive interrupts. + pub fn disable_rx_interrupts(&mut self) { + let can = unsafe { &*Instance::REGISTERS }; + can.ier.modify(|_, w| w.fmpie1().clear_bit().fmpie0().clear_bit()); + } } impl Can { @@ -450,17 +455,15 @@ where Instance: traits::Instance, { /// Enables a filter that accepts all messages. - pub fn accept_all(&mut self, fifo_nr: usize) { + pub fn accept_all(&mut self) { let can = unsafe { &*CAN1::ptr() }; - assert!(fifo_nr == 0 || fifo_nr == 1); - let fb_idx = Instance::FILTER_BANK_START + fifo_nr; - // For more details on the quirks of bxCAN see: // https://github.com/UAVCAN/libcanard/blob/8ee343c4edae0e0e4e1c040852aa3d8430f7bf76/drivers/stm32/canard_stm32.c#L471-L513 - can.fb[fb_idx].fr1.write(|w| unsafe { w.bits(0xFFFF_FFFE) }); - can.fb[fb_idx].fr2.write(|w| unsafe { w.bits(0) }); - bb::set(&can.fa1r, fb_idx as u8); + let idx = Instance::FILTER_BANK_START; + can.fb[idx].fr1.write(|w| unsafe { w.bits(0xFFFF_FFFE) }); + can.fb[idx].fr2.write(|w| unsafe { w.bits(0) }); + bb::set(&can.fa1r, idx as u8); } } @@ -598,21 +601,23 @@ where } /// Interface to a CAN receiver. -pub struct Rx { +pub struct Rx { _can: PhantomData, - _fifo: PhantomData, } -impl Rx +impl Rx where Instance: traits::Instance, - FifoNr: typenum::marker_traits::Unsigned, { - /// Return a received frame if available. + /// Returns a received frame if available. pub fn receive(&mut self) -> nb::Result { - let rfr = &(unsafe { &*Instance::REGISTERS }.rfr[FifoNr::to_usize()]); - let rx = &(unsafe { &*Instance::REGISTERS }.rx[FifoNr::to_usize()]); + let can = unsafe { &*Instance::REGISTERS }; + + Self::receive_fifo(&can.rfr[0], &can.rx[0]) + .or_else(|_| Self::receive_fifo(&can.rfr[1], &can.rx[1])) + } + fn receive_fifo(rfr: &RFR, rx: &RX) -> nb::Result { // Check if a frame is available in the mailbox. if rfr.read().fmp().bits() == 0 { return Err(nb::Error::WouldBlock); From ae2e3f622db5026df16cd3eb7f57036b23ec6948 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20Kr=C3=B6ger?= Date: Sat, 9 May 2020 11:06:50 +0200 Subject: [PATCH 16/43] can: Improve interrupt enableding/disabling Move the implementations to their logical entities. This is only possible with the usage of bitbanding. --- src/can.rs | 60 ++++++++++++++++++++++++++++-------------------------- 1 file changed, 31 insertions(+), 29 deletions(-) diff --git a/src/can.rs b/src/can.rs index adf099a7..ede9c265 100644 --- a/src/can.rs +++ b/src/can.rs @@ -372,38 +372,9 @@ where self.tx.take() } - /// Enables the transmit interrupt CANn_TX. - /// - /// The interrupt flags must be cleared with `Tx::clear_interrupt_flags()`. - pub fn enable_tx_interrupt(&mut self) { - let can = unsafe { &*Instance::REGISTERS }; - can.ier.modify(|_, w| w.tmeie().set_bit()); - } - - /// Disables the transmit interrupt. - pub fn disable_tx_interrupt(&mut self) { - let can = unsafe { &*Instance::REGISTERS }; - can.ier.modify(|_, w| w.tmeie().clear_bit()); - } - pub fn take_rx(&mut self, _filters: Filters) -> Option> { self.rx.take() } - - /// Enables the receive interrupts CANn_RX0 and CANn_RX1. - /// - /// Make sure to register interrupt handlers for both. - /// The interrupt flags are cleared by reading frames with `RX::receive()`. - pub fn enable_rx_interrupts(&mut self) { - let can = unsafe { &*Instance::REGISTERS }; - can.ier.modify(|_, w| w.fmpie1().set_bit().fmpie0().set_bit()); - } - - /// Disables the receive interrupts. - pub fn disable_rx_interrupts(&mut self) { - let can = unsafe { &*Instance::REGISTERS }; - can.ier.modify(|_, w| w.fmpie1().clear_bit().fmpie0().clear_bit()); - } } impl Can { @@ -592,6 +563,20 @@ where .write(|w| unsafe { w.bits(frame.id.0).txrq().set_bit() }); } + /// Enables the transmit interrupt CANn_TX. + /// + /// The interrupt flags must be cleared with `Tx::clear_interrupt_flags()`. + pub fn enable_interrupt(&mut self) { + let can = unsafe { &*Instance::REGISTERS }; + bb::set(&can.ier, 0); // TMEIE + } + + /// Disables the transmit interrupt. + pub fn disable_interrupt(&mut self) { + let can = unsafe { &*Instance::REGISTERS }; + bb::clear(&can.ier, 0); // TMEIE + } + /// Clears the request complete flag for all mailboxes. pub fn clear_interrupt_flags(&mut self) { let can = unsafe { &*Instance::REGISTERS }; @@ -637,4 +622,21 @@ where Ok(frame) } + + /// Enables the receive interrupts CANn_RX0 and CANn_RX1. + /// + /// Make sure to register interrupt handlers for both. + /// The interrupt flags are cleared by reading frames with `Rx::receive()`. + pub fn enable_interrupts(&mut self) { + let can = unsafe { &*Instance::REGISTERS }; + bb::set(&can.ier, 1); // FMPIE0 + bb::set(&can.ier, 4); // FMPIE1 + } + + /// Disables the receive interrupts. + pub fn disable_interrupts(&mut self) { + let can = unsafe { &*Instance::REGISTERS }; + bb::clear(&can.ier, 1); // FMPIE0 + bb::clear(&can.ier, 4); // FMPIE1 + } } From 4cd5caa860bfa7cd65a03a28a0fd5cd5a5d5c93f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20Kr=C3=B6ger?= Date: Sat, 9 May 2020 12:14:36 +0200 Subject: [PATCH 17/43] can: Update the examples --- Cargo.toml | 6 ++- examples/can-echo.rs | 56 +++++++++++++++++++++++++++ examples/can-rtfm.rs | 91 ++++++++++++++++++++++++++++++++------------ 3 files changed, 127 insertions(+), 26 deletions(-) create mode 100644 examples/can-echo.rs diff --git a/Cargo.toml b/Cargo.toml index c56f22b9..2856c23a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -133,6 +133,10 @@ required-features = ["rt", "medium"] name = "exti" required-features = ["rt"] +[[example]] +name = "can-echo" +required-features = ["connectivity"] + [[example]] name = "can-rtfm" -required-features = ["rt"] +required-features = ["connectivity", "rt"] diff --git a/examples/can-echo.rs b/examples/can-echo.rs new file mode 100644 index 00000000..7424cddc --- /dev/null +++ b/examples/can-echo.rs @@ -0,0 +1,56 @@ +#![no_main] +#![no_std] + +use panic_halt as _; + +use cortex_m_rt::entry; +use nb::block; +use stm32f1xx_hal::{can::Can, pac, prelude::*}; + +#[entry] +fn main() -> ! { + let dp = pac::Peripherals::take().unwrap(); + + let mut flash = dp.FLASH.constrain(); + let mut rcc = dp.RCC.constrain(); + + // To meet CAN clock accuracy requirements an external crystal or ceramic + // resonator must be used. + rcc.cfgr.use_hse(8.mhz()).freeze(&mut flash.acr); + + let mut gpiob = dp.GPIOB.split(&mut rcc.apb2); + + // Select some pins for CAN2. + let can_rx_pin = gpiob.pb5.into_floating_input(&mut gpiob.crl); + let can_tx_pin = gpiob.pb6.into_alternate_push_pull(&mut gpiob.crl); + let mut afio = dp.AFIO.constrain(&mut rcc.apb2); + + let mut can2 = Can::new(dp.CAN2, &mut rcc.apb1); + can2.assign_pins((can_tx_pin, can_rx_pin), &mut afio.mapr); + + // APB1 (PCLK1): 8MHz, Bit rate: 125kBit/s, Sample Point 87.5% + // Value was calculated with http://www.bittiming.can-wiki.info/ + unsafe { can2.set_bit_timing(0x001c_0003) }; + + // Get the transmitter part. + let mut tx = can2.take_tx().unwrap(); + + // Filters are required to use the receiver part of CAN2. + // Because the filter banks are part of CAN1 we first need to enable CAN1 + // and split the filters between the peripherals to use them for CAN2. + let mut can1 = Can::new(dp.CAN1, &mut rcc.apb1); + let (_filters1, mut filters2) = can1.split_filters().unwrap(); + filters2.accept_all(); // Receive all messages. + let mut rx = can2.take_rx(filters2).unwrap(); + + // Sync to the bus and start normal operation. + block!(can2.enable()).ok(); + + // Echo back received packages in sequence. + // See the `can-rtfm` example for an echo implementation that adheres to + // correct frame ordering based on the transfer id. + loop { + let frame = block!(rx.receive()).unwrap(); + block!(tx.transmit(&frame)).unwrap(); + } +} diff --git a/examples/can-rtfm.rs b/examples/can-rtfm.rs index e1b05cff..a52a9739 100644 --- a/examples/can-rtfm.rs +++ b/examples/can-rtfm.rs @@ -5,8 +5,13 @@ //! interrupt has to be triggered manually once. With each successful //! transmission the interrupt is reentered and more data is fetched from the //! queue. +//! Received frames are simply echoed back. In contrast to the naive `can-echo` +//! example the echo messages are also correctly prioritized by the transmit +//! queue. #![no_main] #![no_std] +// This should be inside the pool!() macro of heapless. +#![allow(non_upper_case_globals)] use heapless::{ binary_heap::{BinaryHeap, Min}, @@ -20,7 +25,7 @@ use heapless::{ use panic_halt as _; use rtfm::app; use stm32f1xx_hal::{ - can::{Can, Frame, Tx}, + can::{Can, Frame, Rx, Tx}, pac::{Interrupt, CAN2}, prelude::*, }; @@ -38,51 +43,66 @@ const APP: () = { can_tx: Tx, can_tx_queue: BinaryHeap, U8, Min>, tx_count: usize, + can_rx: Rx, } #[init] fn init(cx: init::Context) -> init::LateResources { static mut CAN_POOL_MEMORY: [u8; 256] = [0; 256]; + unsafe { cx.core.SCB.vtor.write(0x0800_0000) }; + let mut flash = cx.device.FLASH.constrain(); let mut rcc = cx.device.RCC.constrain(); - let _clocks = rcc.cfgr.use_hse(8.mhz()).freeze(&mut flash.acr); + rcc.cfgr + .use_hse(8.mhz()) + .sysclk(72.mhz()) + .hclk(72.mhz()) + .pclk1(36.mhz()) + .pclk2(72.mhz()) + .freeze(&mut flash.acr); let mut gpiob = cx.device.GPIOB.split(&mut rcc.apb2); let mut afio = cx.device.AFIO.constrain(&mut rcc.apb2); - let canrx = gpiob.pb5.into_floating_input(&mut gpiob.crl); - let cantx = gpiob.pb6.into_alternate_push_pull(&mut gpiob.crl); - let mut can = Can::new( - cx.device.CAN2, - (cantx, canrx), - &mut afio.mapr, - &mut rcc.apb1, - ); - // APB1 (PCLK1): 8MHz, Bit rate: 125kBit/s, Sample Point 87.5% + let can_rx_pin = gpiob.pb5.into_floating_input(&mut gpiob.crl); + let can_tx_pin = gpiob.pb6.into_alternate_push_pull(&mut gpiob.crl); + + let mut can2 = Can::new(cx.device.CAN2, &mut rcc.apb1); + can2.assign_pins((can_tx_pin, can_rx_pin), &mut afio.mapr); + + // APB1 (PCLK1): 36MHz, Bit rate: 125kBit/s, Sample Point 87.5% // Value was calculated with http://www.bittiming.can-wiki.info/ - unsafe { can.set_bit_timing(0x001c_0003) }; - can.enable_tx_interrupt(); - can.enable().ok(); + unsafe { can2.set_bit_timing(0x001c0011) }; + can2.enable().ok(); - let can_tx = can.take_tx().unwrap(); + let mut can_tx = can2.take_tx().unwrap(); + can_tx.enable_interrupt(); let can_tx_queue = BinaryHeap::new(); CanFramePool::grow(CAN_POOL_MEMORY); + let mut can1 = Can::new(cx.device.CAN1, &mut rcc.apb1); + let (_, mut filters) = can1.split_filters().unwrap(); + filters.accept_all(); + + let mut can_rx = can2.take_rx(filters).unwrap(); + can_rx.enable_interrupts(); + init::LateResources { can_tx, can_tx_queue, tx_count: 0, + can_rx, } } #[idle(resources = [can_tx_queue, tx_count])] fn idle(mut cx: idle::Context) -> ! { - static mut HIGH_PRIORITY_MESSAGES_PENDING: bool = true; + let mut tx_queue = cx.resources.can_tx_queue; // Enqueue some messages. Higher ID means lower priority. - cx.resources.can_tx_queue.lock(|tx_queue| { + tx_queue.lock(|tx_queue| { tx_queue.push(alloc_frame(9, &[0, 1, 2, 4])).unwrap(); tx_queue.push(alloc_frame(9, &[0, 1, 2, 4])).unwrap(); tx_queue.push(alloc_frame(8, &[0, 1, 2, 4])).unwrap(); @@ -94,20 +114,18 @@ const APP: () = { // Manually trigger the tx interrupt to start the transmission. rtfm::pend(Interrupt::CAN2_TX); + // Add some higher priority messages when 3 messages have been sent. loop { let tx_count = cx.resources.tx_count.lock(|tx_count| *tx_count); - // Add some higher priority messages when 3 messages have been sent. - if *HIGH_PRIORITY_MESSAGES_PENDING && tx_count >= 3 { - *HIGH_PRIORITY_MESSAGES_PENDING = false; - - cx.resources.can_tx_queue.lock(|tx_queue| { + if tx_count >= 3 { + tx_queue.lock(|tx_queue| { tx_queue.push(alloc_frame(3, &[0, 1, 2, 4])).unwrap(); tx_queue.push(alloc_frame(2, &[0, 1, 2, 4])).unwrap(); tx_queue.push(alloc_frame(1, &[0, 1, 2, 4])).unwrap(); }); + break; } - cortex_m::asm::wfi(); } // Expected bus traffic: @@ -122,8 +140,11 @@ const APP: () = { // 8. ID: 9 DATA: 00 01 02 04 // 9. ID: 9 DATA: 00 01 02 04 // - // The output can look different if there are other nodes on the bus that send - // higher priority messages at the same time. + // The output can look different if there are other nodes on bus the sending messages. + + loop { + cortex_m::asm::wfi(); + } } // This ISR is triggered by each finished frame transmission. @@ -157,4 +178,24 @@ const APP: () = { } } } + + #[task(binds = CAN2_RX0, resources = [can_rx, can_tx_queue])] + fn can2_rx0(cx: can2_rx0::Context) { + // Echo back received packages with correct priority ordering. + while let Ok(frame) = cx.resources.can_rx.receive() { + cx.resources + .can_tx_queue + .push(CanFramePool::alloc().unwrap().init(frame)) + .ok(); + } + + // Start transmission of the newly queue frames. + rtfm::pend(Interrupt::CAN2_TX); + } + + #[task(binds = CAN2_RX1)] + fn can2_rx1(_: can2_rx1::Context) { + // Jump to the other interrupt handler which handles both RX fifos. + rtfm::pend(Interrupt::CAN2_RX0); + } }; From c5d976627065e5a59f910e0d44ffade5890cb2c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20Kr=C3=B6ger?= Date: Sun, 10 May 2020 12:02:48 +0200 Subject: [PATCH 18/43] can: Fix priority ordering During arbitration the EID bit comes after the 11th bit. This makes it significant. In the bxCAN register the EID is in a lower bit. Explicitly handle the case to esure correct priority ordering. --- examples/can-rtfm.rs | 63 +++++++++++++++++++++++++++++--------------- src/can.rs | 32 +++++++++++++++++----- 2 files changed, 67 insertions(+), 28 deletions(-) diff --git a/examples/can-rtfm.rs b/examples/can-rtfm.rs index a52a9739..e72627ba 100644 --- a/examples/can-rtfm.rs +++ b/examples/can-rtfm.rs @@ -25,16 +25,16 @@ use heapless::{ use panic_halt as _; use rtfm::app; use stm32f1xx_hal::{ - can::{Can, Frame, Rx, Tx}, + can::{Can, Frame, Id, Rx, Tx}, pac::{Interrupt, CAN2}, prelude::*, }; pool!(CanFramePool: Frame); -fn alloc_frame(id: u32, data: &[u8]) -> Box { +fn alloc_frame(id: Id, data: &[u8]) -> Box { let frame_box = CanFramePool::alloc().unwrap(); - frame_box.init(Frame::new_standard(id, data)) + frame_box.init(Frame::new(id, data)) } #[app(device = stm32f1xx_hal::pac, peripherals = true)] @@ -103,12 +103,27 @@ const APP: () = { // Enqueue some messages. Higher ID means lower priority. tx_queue.lock(|tx_queue| { - tx_queue.push(alloc_frame(9, &[0, 1, 2, 4])).unwrap(); - tx_queue.push(alloc_frame(9, &[0, 1, 2, 4])).unwrap(); - tx_queue.push(alloc_frame(8, &[0, 1, 2, 4])).unwrap(); - tx_queue.push(alloc_frame(8, &[0, 1, 2, 4])).unwrap(); - tx_queue.push(alloc_frame(7, &[0, 1, 2, 4])).unwrap(); - tx_queue.push(alloc_frame(7, &[0, 1, 2, 4])).unwrap(); + tx_queue + .push(alloc_frame(Id::new_standard(9), &[0, 1, 2, 4])) + .unwrap(); + tx_queue + .push(alloc_frame(Id::new_standard(9), &[0, 1, 2, 4])) + .unwrap(); + tx_queue + .push(alloc_frame(Id::new_standard(8), &[0, 1, 2, 4])) + .unwrap(); + + // Extended frames have lower priority than standard frames. + tx_queue + .push(alloc_frame(Id::new_extended(8), &[0, 1, 2, 4])) + .unwrap(); + tx_queue + .push(alloc_frame(Id::new_extended(7), &[0, 1, 2, 4])) + .unwrap(); + + tx_queue + .push(alloc_frame(Id::new_standard(7), &[0, 1, 2, 4])) + .unwrap(); }); // Manually trigger the tx interrupt to start the transmission. @@ -120,9 +135,15 @@ const APP: () = { if tx_count >= 3 { tx_queue.lock(|tx_queue| { - tx_queue.push(alloc_frame(3, &[0, 1, 2, 4])).unwrap(); - tx_queue.push(alloc_frame(2, &[0, 1, 2, 4])).unwrap(); - tx_queue.push(alloc_frame(1, &[0, 1, 2, 4])).unwrap(); + tx_queue + .push(alloc_frame(Id::new_standard(3), &[0, 1, 2, 4])) + .unwrap(); + tx_queue + .push(alloc_frame(Id::new_standard(2), &[0, 1, 2, 4])) + .unwrap(); + tx_queue + .push(alloc_frame(Id::new_standard(1), &[0, 1, 2, 4])) + .unwrap(); }); break; } @@ -130,15 +151,15 @@ const APP: () = { // Expected bus traffic: // - // 1. ID: 7 DATA: 00 01 02 04 - // 2. ID: 7 DATA: 00 01 02 04 - // 3. ID: 8 DATA: 00 01 02 04 - // 4. ID: 1 DATA: 00 01 02 04 - // 5. ID: 2 DATA: 00 01 02 04 - // 6. ID: 3 DATA: 00 01 02 04 - // 7. ID: 8 DATA: 00 01 02 04 - // 8. ID: 9 DATA: 00 01 02 04 - // 9. ID: 9 DATA: 00 01 02 04 + // 1. ID: 007 DATA: 00 01 02 04 <- proper reordering happens + // 2. ID: 008 DATA: 00 01 02 04 + // 3. ID: 009 DATA: 00 01 02 04 + // 4. ID: 001 DATA: 00 01 02 04 <- higher priority messages incoming + // 5. ID: 002 DATA: 00 01 02 04 + // 6. ID: 003 DATA: 00 01 02 04 + // 7. ID: 009 DATA: 00 01 02 04 + // 8. ID: 00000007 DATA: 00 01 02 04 <- extended frames have the lowest priority + // 9. ID: 00000008 DATA: 00 01 02 04 and reach the bus last // // The output can look different if there are other nodes on bus the sending messages. diff --git a/src/can.rs b/src/can.rs index ede9c265..439df897 100644 --- a/src/can.rs +++ b/src/can.rs @@ -38,6 +38,7 @@ use crate::pac::{ }; use crate::rcc::APB1; use core::{ + cmp::{Ord, Ordering}, convert::{Infallible, TryInto}, marker::PhantomData, }; @@ -53,7 +54,7 @@ use core::{ /// Lower identifier values mean higher priority. Additionally standard frames /// have a higher priority than extended frames and data frames have a higher /// priority than remote frames. -#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct Id(u32); impl Id { @@ -115,6 +116,23 @@ impl Id { } } +impl Ord for Id { + fn cmp(&self, other: &Self) -> Ordering { + match (self.is_standard(), other.is_standard()) { + (true, false) => Ordering::Less, + (false, true) => Ordering::Greater, + // Ordering of the data/remote frames implicitly gives by the bit layout. + _ => self.0.cmp(&other.0), + } + } +} + +impl PartialOrd for Id { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + /// A CAN data or remote frame. #[derive(Clone, Debug)] pub struct Frame { @@ -185,25 +203,25 @@ impl Frame { } } -impl core::cmp::Ord for Frame { - fn cmp(&self, other: &Self) -> core::cmp::Ordering { +impl Ord for Frame { + fn cmp(&self, other: &Self) -> Ordering { self.id().cmp(&other.id()) } } -impl core::cmp::PartialOrd for Frame { - fn partial_cmp(&self, other: &Self) -> Option { +impl PartialOrd for Frame { + fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(other)) } } -impl core::cmp::PartialEq for Frame { +impl PartialEq for Frame { fn eq(&self, other: &Self) -> bool { self.id() == other.id() } } -impl core::cmp::Eq for Frame {} +impl Eq for Frame {} // Seal the traits so that they cannot be implemented outside side this crate. mod traits { From 0365b81c421f07caf0427996ffe7dcb82f46c2f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20Kr=C3=B6ger?= Date: Mon, 11 May 2020 08:04:43 +0200 Subject: [PATCH 19/43] can: Update doc comments --- src/can.rs | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/can.rs b/src/can.rs index 439df897..f92aea57 100644 --- a/src/can.rs +++ b/src/can.rs @@ -197,7 +197,7 @@ impl Frame { self.id } - // Returns the frame data. + /// Returns the frame data (0..8 bytes in length). pub fn data(&self) -> &[u8] { &self.data[0..self.dlc] } @@ -390,6 +390,10 @@ where self.tx.take() } + /// Returns the receiver interface. + /// + /// Takes ownership of filters which must be otained by `Can::split_filters()`. + /// Only the first calls returns a valid receiver. Subsequent calls return `None`. pub fn take_rx(&mut self, _filters: Filters) -> Option> { self.rx.take() } @@ -397,6 +401,8 @@ where impl Can { /// Returns the filter part of the CAN peripheral. + /// + /// Filters are required for the receiver to accept any messages at all. #[cfg(not(feature = "connectivity"))] pub fn split_filters(&mut self) -> Option> { self.split_filters_internal()?; @@ -404,6 +410,8 @@ impl Can { } /// Returns the filter part of the CAN peripheral. + /// + /// Filters are required for the receiver to accept any messages at all. #[cfg(feature = "connectivity")] pub fn split_filters(&mut self) -> Option<(Filters, Filters)> { self.split_filters_internal()?; @@ -437,6 +445,7 @@ impl Can { } } +/// Interface to the filter banks of a CAN peripheral. pub struct Filters(PhantomData); impl Filters @@ -456,7 +465,7 @@ where } } -/// Interface to the CAN transmitter. +/// Interface to the CAN transmitter part. pub struct Tx { _can: PhantomData, } @@ -603,7 +612,7 @@ where } } -/// Interface to a CAN receiver. +/// Interface to the CAN receiver part. pub struct Rx { _can: PhantomData, } From 7d15ec7cb4a515e4a3ee4cdd24e69c4e049779bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20Kr=C3=B6ger?= Date: Mon, 11 May 2020 07:59:36 +0200 Subject: [PATCH 20/43] can: Make `set_bit_timing()` safe As pointed out by Adam Greig it does not cause any memory unsafety or undefined behaviour. Worst case is the CAN peripheral not working. --- examples/can-echo.rs | 2 +- examples/can-rtfm.rs | 2 +- src/can.rs | 10 ++++------ 3 files changed, 6 insertions(+), 8 deletions(-) diff --git a/examples/can-echo.rs b/examples/can-echo.rs index 7424cddc..86ca6912 100644 --- a/examples/can-echo.rs +++ b/examples/can-echo.rs @@ -30,7 +30,7 @@ fn main() -> ! { // APB1 (PCLK1): 8MHz, Bit rate: 125kBit/s, Sample Point 87.5% // Value was calculated with http://www.bittiming.can-wiki.info/ - unsafe { can2.set_bit_timing(0x001c_0003) }; + can2.set_bit_timing(0x001c_0003); // Get the transmitter part. let mut tx = can2.take_tx().unwrap(); diff --git a/examples/can-rtfm.rs b/examples/can-rtfm.rs index e72627ba..aa4094d1 100644 --- a/examples/can-rtfm.rs +++ b/examples/can-rtfm.rs @@ -74,7 +74,7 @@ const APP: () = { // APB1 (PCLK1): 36MHz, Bit rate: 125kBit/s, Sample Point 87.5% // Value was calculated with http://www.bittiming.can-wiki.info/ - unsafe { can2.set_bit_timing(0x001c0011) }; + can2.set_bit_timing(0x001c0011); can2.enable().ok(); let mut can_tx = can2.take_tx().unwrap(); diff --git a/src/can.rs b/src/can.rs index f92aea57..a372d248 100644 --- a/src/can.rs +++ b/src/can.rs @@ -338,18 +338,16 @@ where /// Configures the bit timings. /// + /// Use http://www.bittiming.can-wiki.info/ to calculate the `btr` parameter. /// Puts the peripheral in sleep mode. `Can::enable()` must be called afterwards /// to start reception and transmission. - /// - /// # Safety - /// Use http://www.bittiming.can-wiki.info/ to calculate a safe parameter value. - pub unsafe fn set_bit_timing(&mut self, btr: u32) { - let can = &*Instance::REGISTERS; + pub fn set_bit_timing(&mut self, btr: u32) { + let can = unsafe { &*Instance::REGISTERS }; can.mcr .modify(|_, w| w.sleep().clear_bit().inrq().set_bit()); while can.msr.read().inak().bit_is_clear() {} - can.btr.write(|w| w.bits(btr)); + can.btr.write(|w| unsafe { w.bits(btr) }); self.sleep(); } From d83a0340e498791ac2b8ffa331c996a56316dd43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20Kr=C3=B6ger?= Date: Mon, 11 May 2020 11:29:41 +0200 Subject: [PATCH 21/43] can: Refactor transmitter code * Convert associate functions to methods * Extract `read_pending_frame()` * Try not to use inverse logic --- src/can.rs | 153 +++++++++++++++++++++++++++++------------------------ 1 file changed, 85 insertions(+), 68 deletions(-) diff --git a/src/can.rs b/src/can.rs index a372d248..41fdf91d 100644 --- a/src/can.rs +++ b/src/can.rs @@ -28,14 +28,11 @@ use crate::gpio::{ gpiob::{PB8, PB9}, Alternate, Floating, Input, PushPull, }; +use crate::pac::CAN1; #[cfg(feature = "connectivity")] use crate::pac::CAN2; #[cfg(not(feature = "connectivity"))] use crate::pac::USB; -use crate::pac::{ - can1::{RFR, RX, TX}, - CAN1, -}; use crate::rcc::APB1; use core::{ cmp::{Ord, Ordering}, @@ -489,78 +486,51 @@ where pub fn transmit(&mut self, frame: &Frame) -> nb::Result, Infallible> { let can = unsafe { &*Instance::REGISTERS }; - // Get the index of free mailbox or of one with the lowest priority. + // Get the index of the next free mailbox or the one with the lowest priority. let tsr = can.tsr.read(); let idx = tsr.code().bits() as usize; - let mb = unsafe { &can.tx.get_unchecked(idx) }; - let empty_flags = (tsr.bits() >> 26) & 0b111; - let pending_frame = if empty_flags == 0b111 { - // All mailboxes are available: Send frame without performing any checks. - None - } else { + let frame_is_pending = + tsr.tme0().bit_is_clear() || tsr.tme1().bit_is_clear() || tsr.tme2().bit_is_clear(); + let pending_frame = if frame_is_pending { // High priority frames are transmitted first by the mailbox system. // Frames with identical identifier shall be transmitted in FIFO order. // The controller schedules pending frames of same priority based on the // mailbox index instead. As a workaround check all pending mailboxes // and only accept higher priority frames. - Self::check_priority(&can.tx[0], frame.id)?; - Self::check_priority(&can.tx[1], frame.id)?; - Self::check_priority(&can.tx[2], frame.id)?; + self.check_priority(0, frame.id)?; + self.check_priority(1, frame.id)?; + self.check_priority(2, frame.id)?; - if empty_flags != 0b000 { - // There was a free mailbox. - None - } else { + let all_frames_are_pending = + tsr.tme0().bit_is_clear() && tsr.tme1().bit_is_clear() && tsr.tme2().bit_is_clear(); + if all_frames_are_pending { // No free mailbox is available. This can only happen when three frames with // descending priority were requested for transmission and all of them are // blocked by bus traffic with even higher priority. // To prevent a priority inversion abort and replace the lowest priority frame. - if Self::abort(idx) { - // Read back the pending frame. - let mut pending_frame = Frame { - id: Id(mb.tir.read().bits()), - dlc: mb.tdtr.read().dlc().bits() as usize, - data: [0; 8], - }; - pending_frame.data[0..4].copy_from_slice(&mb.tdlr.read().bits().to_ne_bytes()); - pending_frame.data[4..8].copy_from_slice(&mb.tdhr.read().bits().to_ne_bytes()); - - Some(pending_frame) - } else { - // Abort request failed because the frame was already sent (or being sent) on - // the bus. All mailboxes are now free. This can happen for small prescaler - // values (e.g. 1MBit/s bit timing with a source clock of 8MHz) or when an ISR - // has preemted the execution. - None - } + self.read_pending_mailbox(idx) + } else { + // There was a free mailbox. + None } + } else { + // All mailboxes are available: Send frame without performing any checks. + None }; - Self::write_tx_mailbox(mb, frame); + self.write_mailbox(idx, frame); Ok(pending_frame) } - /// Tries to abort a pending frame. Returns `true` when aborted. - fn abort(idx: usize) -> bool { - let can = unsafe { &*Instance::REGISTERS }; - - can.tsr.write(|w| unsafe { w.bits(abort_mask(idx)) }); - - // Wait for the abort request to be finished. - loop { - let tsr = can.tsr.read().bits(); - if tsr & abort_mask(idx) == 0 { - break tsr & ok_mask(idx) == 0; - } - } - } - /// Returns `Ok` when the mailbox is free or has a lower priority than /// identifier than `id`. - fn check_priority(mb: &TX, id: Id) -> nb::Result<(), Infallible> { + fn check_priority(&self, idx: usize, id: Id) -> nb::Result<(), Infallible> { + let can = unsafe { &*Instance::REGISTERS }; + // Read the pending frame's id to check its priority. - let tir = mb.tir.read(); + assert!(idx < 3); + let tir = &can.tx[idx].tir.read(); // Check the priority by comparing the identifiers. But first make sure the // frame has not finished transmission (`TXRQ` == 0) in the meantime. @@ -573,21 +543,62 @@ where Ok(()) } - fn write_tx_mailbox(tx_mb: &crate::pac::can1::TX, frame: &Frame) { - tx_mb - .tdtr - .write(|w| unsafe { w.dlc().bits(frame.dlc as u8) }); - tx_mb - .tdlr + fn write_mailbox(&mut self, idx: usize, frame: &Frame) { + let can = unsafe { &*Instance::REGISTERS }; + + debug_assert!(idx < 3); + let mb = unsafe { &can.tx.get_unchecked(idx) }; + + mb.tdtr.write(|w| unsafe { w.dlc().bits(frame.dlc as u8) }); + mb.tdlr .write(|w| unsafe { w.bits(u32::from_ne_bytes(frame.data[0..4].try_into().unwrap())) }); - tx_mb - .tdhr + mb.tdhr .write(|w| unsafe { w.bits(u32::from_ne_bytes(frame.data[4..8].try_into().unwrap())) }); - tx_mb - .tir + mb.tir .write(|w| unsafe { w.bits(frame.id.0).txrq().set_bit() }); } + fn read_pending_mailbox(&mut self, idx: usize) -> Option { + let can = unsafe { &*Instance::REGISTERS }; + + debug_assert!(idx < 3); + let mb = unsafe { &can.tx.get_unchecked(idx) }; + + if self.abort(idx) { + // Read back the pending frame. + let mut pending_frame = Frame { + id: Id(mb.tir.read().bits()), + dlc: mb.tdtr.read().dlc().bits() as usize, + data: [0; 8], + }; + pending_frame.data[0..4].copy_from_slice(&mb.tdlr.read().bits().to_ne_bytes()); + pending_frame.data[4..8].copy_from_slice(&mb.tdhr.read().bits().to_ne_bytes()); + + Some(pending_frame) + } else { + // Abort request failed because the frame was already sent (or being sent) on + // the bus. All mailboxes are now free. This can happen for small prescaler + // values (e.g. 1MBit/s bit timing with a source clock of 8MHz) or when an ISR + // has preemted the execution. + None + } + } + + /// Tries to abort a pending frame. Returns `true` when aborted. + fn abort(&mut self, idx: usize) -> bool { + let can = unsafe { &*Instance::REGISTERS }; + + can.tsr.write(|w| unsafe { w.bits(abort_mask(idx)) }); + + // Wait for the abort request to be finished. + loop { + let tsr = can.tsr.read().bits(); + if tsr & abort_mask(idx) == 0 { + break tsr & ok_mask(idx) == 0; + } + } + } + /// Enables the transmit interrupt CANn_TX. /// /// The interrupt flags must be cleared with `Tx::clear_interrupt_flags()`. @@ -621,13 +632,19 @@ where { /// Returns a received frame if available. pub fn receive(&mut self) -> nb::Result { + match self.receive_fifo(0) { + Err(nb::Error::WouldBlock) => self.receive_fifo(1), + result => result, + } + } + + fn receive_fifo(&mut self, fifo_nr: usize) -> nb::Result { let can = unsafe { &*Instance::REGISTERS }; - Self::receive_fifo(&can.rfr[0], &can.rx[0]) - .or_else(|_| Self::receive_fifo(&can.rfr[1], &can.rx[1])) - } + assert!(fifo_nr < 2); + let rfr = &can.rfr[fifo_nr]; + let rx = &can.rx[fifo_nr]; - fn receive_fifo(rfr: &RFR, rx: &RX) -> nb::Result { // Check if a frame is available in the mailbox. if rfr.read().fmp().bits() == 0 { return Err(nb::Error::WouldBlock); From c77b83727bc8d53c0fedf1ab8720d86b94cda3a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20Kr=C3=B6ger?= Date: Mon, 11 May 2020 21:53:08 +0200 Subject: [PATCH 22/43] can: Make automatic wake-up configurable --- src/can.rs | 30 +++++++++++++++++++++++++++--- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/src/can.rs b/src/can.rs index 41fdf91d..44c112b1 100644 --- a/src/can.rs +++ b/src/can.rs @@ -348,17 +348,20 @@ where self.sleep(); } + /// Configures the automatic wake-up feature. + pub fn set_automatic_wakeup(&mut self, enabled: bool) { + let can = unsafe { &*Instance::REGISTERS }; + can.mcr.modify(|_, w| w.awum().bit(enabled)); + } + /// Start reception and transmission. /// /// Waits for 11 consecutive recessive bits to sync to the CAN bus. - /// When automatic wakup functionality is not used this function must be - /// called to enable the peripheral after a wakeup interrupt. pub fn enable(&mut self) -> nb::Result<(), Infallible> { let can = unsafe { &*Instance::REGISTERS }; let msr = can.msr.read(); if msr.slak().bit_is_set() { // TODO: Make automatic bus-off management configurable. - // TODO: Make automatic wakeup configurable. can.mcr .modify(|_, w| w.abom().set_bit().sleep().clear_bit()); Err(nb::Error::WouldBlock) @@ -377,6 +380,20 @@ where while can.msr.read().slak().bit_is_clear() {} } + /// Enables the wake-up state change interrupt (CANn_SCE). + /// + /// Call `Can::enable()` in the ISR when the automatic wake-up is not enabled. + pub fn enable_wakeup_interrupt(&mut self) { + let can = unsafe { &*Instance::REGISTERS }; + bb::set(&can.ier, 16); // WKUIE + } + + /// Clears all state-change interrupt flags. + pub fn clear_interrupt_flags(&mut self) { + let can = unsafe { &*Instance::REGISTERS }; + can.msr.write(|w| w.wkui().set_bit()); + } + /// Returns the transmitter interface. /// /// Only the first calls returns a valid transmitter. Subsequent calls @@ -599,6 +616,13 @@ where } } + /// Returns `true` if no frame is pending for transmission. + pub fn is_idle(&self) -> bool { + let can = unsafe { &*Instance::REGISTERS }; + let tsr = can.tsr.read(); + tsr.tme0().bit_is_set() && tsr.tme1().bit_is_set() && tsr.tme2().bit_is_set() + } + /// Enables the transmit interrupt CANn_TX. /// /// The interrupt flags must be cleared with `Tx::clear_interrupt_flags()`. From 28dc2ecaa4e80f98c121331a096e0f2887b03380 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20Kr=C3=B6ger?= Date: Tue, 12 May 2020 18:03:44 +0200 Subject: [PATCH 23/43] can: Configurable filters --- examples/can-echo.rs | 8 +++- examples/can-rtfm.rs | 16 +++++-- src/can.rs | 112 ++++++++++++++++++++++++++++++++++++++----- 3 files changed, 119 insertions(+), 17 deletions(-) diff --git a/examples/can-echo.rs b/examples/can-echo.rs index 86ca6912..5741b421 100644 --- a/examples/can-echo.rs +++ b/examples/can-echo.rs @@ -5,7 +5,11 @@ use panic_halt as _; use cortex_m_rt::entry; use nb::block; -use stm32f1xx_hal::{can::Can, pac, prelude::*}; +use stm32f1xx_hal::{ + can::{Can, Filter}, + pac, + prelude::*, +}; #[entry] fn main() -> ! { @@ -40,7 +44,7 @@ fn main() -> ! { // and split the filters between the peripherals to use them for CAN2. let mut can1 = Can::new(dp.CAN1, &mut rcc.apb1); let (_filters1, mut filters2) = can1.split_filters().unwrap(); - filters2.accept_all(); // Receive all messages. + filters2.add(Filter::accept_all()).unwrap(); // Receive all messages. let mut rx = can2.take_rx(filters2).unwrap(); // Sync to the bus and start normal operation. diff --git a/examples/can-rtfm.rs b/examples/can-rtfm.rs index aa4094d1..58110f11 100644 --- a/examples/can-rtfm.rs +++ b/examples/can-rtfm.rs @@ -25,7 +25,7 @@ use heapless::{ use panic_halt as _; use rtfm::app; use stm32f1xx_hal::{ - can::{Can, Frame, Id, Rx, Tx}, + can::{Can, Filter, Frame, Id, Rx, Tx}, pac::{Interrupt, CAN2}, prelude::*, }; @@ -75,7 +75,6 @@ const APP: () = { // APB1 (PCLK1): 36MHz, Bit rate: 125kBit/s, Sample Point 87.5% // Value was calculated with http://www.bittiming.can-wiki.info/ can2.set_bit_timing(0x001c0011); - can2.enable().ok(); let mut can_tx = can2.take_tx().unwrap(); can_tx.enable_interrupt(); @@ -84,11 +83,22 @@ const APP: () = { let mut can1 = Can::new(cx.device.CAN1, &mut rcc.apb1); let (_, mut filters) = can1.split_filters().unwrap(); - filters.accept_all(); + + // To share load between FIFOs use one filter for standard messages and another + // for extended messages. Accept all IDs by setting the mask to 0. Only accept + // data frames (no remote frames). + filters + .add(Filter::new_standard(0).with_mask(0).with_rtr(false)) + .unwrap(); + filters + .add(Filter::new_extended(0).with_mask(0).with_rtr(false)) + .unwrap(); let mut can_rx = can2.take_rx(filters).unwrap(); can_rx.enable_interrupts(); + can2.enable().ok(); + init::LateResources { can_tx, can_tx_queue, diff --git a/src/can.rs b/src/can.rs index 44c112b1..e280d033 100644 --- a/src/can.rs +++ b/src/can.rs @@ -55,9 +55,14 @@ use core::{ pub struct Id(u32); impl Id { - const STANDARD_SHIFT: u32 = 21; // 11 valid bits. Mask: 0xFFE0_0000 - const EXTENDED_SHIFT: u32 = 3; // 29 valid bits. Mask: 0xFFFF_FFF8 + const STANDARD_SHIFT: u32 = 21; + const STANDARD_MASK: u32 = 0x7FF << Self::STANDARD_SHIFT; + + const EXTENDED_SHIFT: u32 = 3; + const EXTENDED_MASK: u32 = 0x1FFF_FFFF << Self::EXTENDED_SHIFT; + const EID_MASK: u32 = 0x0000_0004; + const RTR_MASK: u32 = 0x0000_0002; /// Creates a new standard identifier (11bit, Range: 0..0x7FF) @@ -427,7 +432,7 @@ impl Can { #[cfg(feature = "connectivity")] pub fn split_filters(&mut self) -> Option<(Filters, Filters)> { self.split_filters_internal()?; - Some((Filters(PhantomData), Filters(PhantomData))) + Some((Filters::new(), Filters::new())) } fn split_filters_internal(&mut self) -> Option<()> { @@ -457,23 +462,106 @@ impl Can { } } +/// A masked filter configuration. +pub struct Filter { + id: u32, + mask: u32, +} + +/// bxCAN filters have some quirks: +/// https://github.com/UAVCAN/libcanard/blob/8ee343c4edae0e0e4e1c040852aa3d8430f7bf76/drivers/stm32/canard_stm32.c#L471-L513 +impl Filter { + /// Creates a filter that accepts all messages. + pub fn accept_all() -> Self { + Self { + id: Id::EID_MASK | Id::RTR_MASK, + mask: 0, + } + } + + /// Creates a filter that accepts frames with the specified standard identifier. + pub fn new_standard(id: u32) -> Self { + Self { + id: id << Id::STANDARD_SHIFT | Id::RTR_MASK, + mask: Id::STANDARD_MASK | Id::EID_MASK, + } + } + + /// Creates a filter that accepts frames with the extended standard identifier. + pub fn new_extended(id: u32) -> Self { + Self { + id: id << Id::EXTENDED_SHIFT | Id::RTR_MASK | Id::EID_MASK, + mask: Id::EXTENDED_MASK | Id::EID_MASK, + } + } + + /// Only look at the bits of the indentifier which are set to 1 in the mask. + /// + /// A mask of 0 accepts all identifiers. + pub fn with_mask(mut self, mask: u32) -> Self { + if self.id & Id::EID_MASK != 0 { + self.mask = (self.mask & !Id::EXTENDED_MASK) | (mask << Id::EXTENDED_SHIFT); + } else { + self.mask = (self.mask & !Id::STANDARD_MASK) | (mask << Id::STANDARD_SHIFT); + } + self + } + + /// Select if only remote (`rtr = true`) frames or only data (`rtr = false`) shall be received. + pub fn with_rtr(mut self, rtr: bool) -> Self { + if rtr { + self.id |= Id::RTR_MASK; + } else { + self.id &= !Id::RTR_MASK; + } + self.mask |= Id::RTR_MASK; + self + } +} + /// Interface to the filter banks of a CAN peripheral. -pub struct Filters(PhantomData); +pub struct Filters { + count: usize, + _can: PhantomData, +} impl Filters where Instance: traits::Instance, { - /// Enables a filter that accepts all messages. - pub fn accept_all(&mut self) { + fn new() -> Self { + Self { + count: 0, + _can: PhantomData, + } + } + + /// Adds a filter. Returns `Err` if the maximum number of filters was reached. + pub fn add(&mut self, filter: Filter) -> Result<(), ()> { + let can = unsafe { &*CAN1::ptr() }; + + let idx = Instance::FILTER_BANK_START + self.count; + if idx < Instance::FILTER_BANK_STOP { + let filter_bank = &can.fb[idx]; + filter_bank.fr1.write(|w| unsafe { w.bits(filter.id) }); + filter_bank.fr2.write(|w| unsafe { w.bits(filter.mask) }); + bb::set(&can.fa1r, idx as u8); // Enable filter + self.count += 1; + Ok(()) + } else { + Err(()) + } + } + + /// Disables all enabled filters. + pub fn clear(&mut self) { let can = unsafe { &*CAN1::ptr() }; - // For more details on the quirks of bxCAN see: - // https://github.com/UAVCAN/libcanard/blob/8ee343c4edae0e0e4e1c040852aa3d8430f7bf76/drivers/stm32/canard_stm32.c#L471-L513 - let idx = Instance::FILTER_BANK_START; - can.fb[idx].fr1.write(|w| unsafe { w.bits(0xFFFF_FFFE) }); - can.fb[idx].fr2.write(|w| unsafe { w.bits(0) }); - bb::set(&can.fa1r, idx as u8); + // Bitbanding required because the filters are shared between CAN1 and CAN2 + for i in Instance::FILTER_BANK_START..(Instance::FILTER_BANK_START + self.count) { + bb::clear(&can.fa1r, i as u8); + } + self.count = 0; } } From 6dff5c26f9119b409c209b4ad195aea8f8ea4850 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20Kr=C3=B6ger?= Date: Tue, 12 May 2020 22:43:37 +0200 Subject: [PATCH 24/43] can: Fix stm32f103 build --- src/can.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/can.rs b/src/can.rs index e280d033..3399cecc 100644 --- a/src/can.rs +++ b/src/can.rs @@ -423,7 +423,7 @@ impl Can { #[cfg(not(feature = "connectivity"))] pub fn split_filters(&mut self) -> Option> { self.split_filters_internal()?; - Some(Filters(PhantomData)) + Some(Filters::new()) } /// Returns the filter part of the CAN peripheral. @@ -545,7 +545,7 @@ where let filter_bank = &can.fb[idx]; filter_bank.fr1.write(|w| unsafe { w.bits(filter.id) }); filter_bank.fr2.write(|w| unsafe { w.bits(filter.mask) }); - bb::set(&can.fa1r, idx as u8); // Enable filter + bb::set(&can.fa1r, idx as u8); // Enable filter self.count += 1; Ok(()) } else { From a3d629518dca16e3bdb814a13be7fd3253443145 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20Kr=C3=B6ger?= Date: Wed, 13 May 2020 16:50:13 +0200 Subject: [PATCH 25/43] can: Silent and Loop-back-mode --- examples/can-echo.rs | 8 +++-- examples/can-loopback.rs | 66 ++++++++++++++++++++++++++++++++++++++++ examples/can-rtfm.rs | 8 +++-- src/can.rs | 55 +++++++++++++++++++++++++++++---- 4 files changed, 125 insertions(+), 12 deletions(-) create mode 100644 examples/can-loopback.rs diff --git a/examples/can-echo.rs b/examples/can-echo.rs index 5741b421..e79d40c2 100644 --- a/examples/can-echo.rs +++ b/examples/can-echo.rs @@ -32,9 +32,11 @@ fn main() -> ! { let mut can2 = Can::new(dp.CAN2, &mut rcc.apb1); can2.assign_pins((can_tx_pin, can_rx_pin), &mut afio.mapr); - // APB1 (PCLK1): 8MHz, Bit rate: 125kBit/s, Sample Point 87.5% - // Value was calculated with http://www.bittiming.can-wiki.info/ - can2.set_bit_timing(0x001c_0003); + can2.configure(|config| { + // APB1 (PCLK1): 8MHz, Bit rate: 125kBit/s, Sample Point 87.5% + // Value was calculated with http://www.bittiming.can-wiki.info/ + config.set_bit_timing(0x001c_0003); + }); // Get the transmitter part. let mut tx = can2.take_tx().unwrap(); diff --git a/examples/can-loopback.rs b/examples/can-loopback.rs new file mode 100644 index 00000000..f89c0c10 --- /dev/null +++ b/examples/can-loopback.rs @@ -0,0 +1,66 @@ +#![no_main] +#![no_std] + +use panic_halt as _; + +use cortex_m_rt::entry; +use nb::block; +use stm32f1xx_hal::{ + can::{Can, Filter, Frame}, + pac, + prelude::*, +}; + +#[entry] +fn main() -> ! { + let dp = pac::Peripherals::take().unwrap(); + + let mut flash = dp.FLASH.constrain(); + let mut rcc = dp.RCC.constrain(); + + // To meet CAN clock accuracy requirements an external crystal or ceramic + // resonator must be used. + rcc.cfgr.use_hse(8.mhz()).freeze(&mut flash.acr); + + #[cfg(not(feature = "connectivity"))] + let mut can = Can::new(dp.CAN1, &mut rcc.apb1, dp.USB); + + #[cfg(feature = "connectivity")] + let mut can = Can::new(dp.CAN1, &mut rcc.apb1); + + // Use loopback mode: No pins need to be assigned to peripheral. + can.configure(|config| { + // APB1 (PCLK1): 8MHz, Bit rate: 125kBit/s, Sample Point 87.5% + // Value was calculated with http://www.bittiming.can-wiki.info/ + config.set_bit_timing(0x001c_0003); + config.set_loopback(true); + config.set_silent(true); + }); + + // Get the transmitter part. + let mut tx = can.take_tx().unwrap(); + + // Receive all messages. + #[cfg(not(feature = "connectivity"))] + let mut filters = can.split_filters().unwrap(); + #[cfg(feature = "connectivity")] + let (mut filters, _) = can.split_filters().unwrap(); + filters.add(Filter::accept_all()).unwrap(); + let mut rx = can.take_rx(filters).unwrap(); + + // Sync to the bus and start normal operation. + block!(can.enable()).ok(); + + // Send and receive messages with incrementing identifier. + for id in (0..0x7FF_u32).cycle() { + let frame_tx = Frame::new_standard(id, &[id as u8]); + block!(tx.transmit(&frame_tx)).unwrap(); + + let frame_rx = block!(rx.receive()).unwrap(); + + assert_eq!(frame_tx, frame_rx); // Only compares the identifier. + assert_eq!(frame_tx.data(), frame_rx.data()); + } + + loop {} +} diff --git a/examples/can-rtfm.rs b/examples/can-rtfm.rs index 58110f11..fe49c963 100644 --- a/examples/can-rtfm.rs +++ b/examples/can-rtfm.rs @@ -72,9 +72,11 @@ const APP: () = { let mut can2 = Can::new(cx.device.CAN2, &mut rcc.apb1); can2.assign_pins((can_tx_pin, can_rx_pin), &mut afio.mapr); - // APB1 (PCLK1): 36MHz, Bit rate: 125kBit/s, Sample Point 87.5% - // Value was calculated with http://www.bittiming.can-wiki.info/ - can2.set_bit_timing(0x001c0011); + can2.configure(|config| { + // APB1 (PCLK1): 36MHz, Bit rate: 125kBit/s, Sample Point 87.5% + // Value was calculated with http://www.bittiming.can-wiki.info/ + config.set_bit_timing(0x001c0011); + }); let mut can_tx = can2.take_tx().unwrap(); can_tx.enable_interrupt(); diff --git a/src/can.rs b/src/can.rs index 3399cecc..76db6470 100644 --- a/src/can.rs +++ b/src/can.rs @@ -294,6 +294,39 @@ impl traits::Pins for (PB6>, PB5>) { } } +/// Configuration proxy to be used with `Can::configure()`. +pub struct CanConfig { + _can: PhantomData, +} + +impl CanConfig +where + Instance: traits::Instance, +{ + /// Configures the bit timings. + /// + /// Use http://www.bittiming.can-wiki.info/ to calculate the `btr` parameter. + pub fn set_bit_timing(&mut self, btr: u32) { + let can = unsafe { &*Instance::REGISTERS }; + can.btr.modify(|r, w| unsafe { + let mode_bits = r.bits() & 0xC000_0000; + w.bits(mode_bits | btr) + }); + } + + /// Enables or disables loopback mode: Internally connects the TX to RX. + pub fn set_loopback(&mut self, enabled: bool) { + let can = unsafe { &*Instance::REGISTERS }; + can.btr.modify(|_, w| w.lbkm().bit(enabled)); + } + + /// Enables or disables silent mode: Disconnects TX from the pin. + pub fn set_silent(&mut self, enabled: bool) { + let can = unsafe { &*Instance::REGISTERS }; + can.btr.modify(|_, w| w.silm().bit(enabled)); + } +} + /// Interface to the CAN peripheral. pub struct Can { _can: PhantomData, @@ -338,18 +371,28 @@ where Pins::remap(mapr); } - /// Configures the bit timings. + /// Configure bit timings and silent/loop-back mode. /// - /// Use http://www.bittiming.can-wiki.info/ to calculate the `btr` parameter. - /// Puts the peripheral in sleep mode. `Can::enable()` must be called afterwards - /// to start reception and transmission. - pub fn set_bit_timing(&mut self, btr: u32) { + /// Acutal configuration happens on the `CanConfig` that is passed to the + /// closure. It must be done this way because those configuration bits can + /// only be set if the CAN controller is in a special init mode. + /// Puts the peripheral in sleep mode afterwards. `Can::enable()` must be + /// called to exit sleep mode and start reception and transmission. + pub fn configure(&mut self, f: F) + where + F: FnOnce(&mut CanConfig), + { let can = unsafe { &*Instance::REGISTERS }; + // Enter init mode. can.mcr .modify(|_, w| w.sleep().clear_bit().inrq().set_bit()); while can.msr.read().inak().bit_is_clear() {} - can.btr.write(|w| unsafe { w.bits(btr) }); + + let mut config = CanConfig { _can: PhantomData }; + f(&mut config); + + // Leave init mode: go back to sleep. self.sleep(); } From 901faa81b59d3440f705d67a9722392b806678b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20Kr=C3=B6ger?= Date: Thu, 14 May 2020 20:56:43 +0200 Subject: [PATCH 26/43] can: Detect RX overrun errors --- examples/can-echo.rs | 5 +++-- examples/can-rtfm.rs | 23 +++++++++++++++-------- src/can.rs | 21 ++++++++++++++++++--- 3 files changed, 36 insertions(+), 13 deletions(-) diff --git a/examples/can-echo.rs b/examples/can-echo.rs index e79d40c2..af8e71ca 100644 --- a/examples/can-echo.rs +++ b/examples/can-echo.rs @@ -56,7 +56,8 @@ fn main() -> ! { // See the `can-rtfm` example for an echo implementation that adheres to // correct frame ordering based on the transfer id. loop { - let frame = block!(rx.receive()).unwrap(); - block!(tx.transmit(&frame)).unwrap(); + if let Ok(frame) = block!(rx.receive()) { + block!(tx.transmit(&frame)).unwrap(); + } } } diff --git a/examples/can-rtfm.rs b/examples/can-rtfm.rs index fe49c963..9ebf7f1f 100644 --- a/examples/can-rtfm.rs +++ b/examples/can-rtfm.rs @@ -10,8 +10,6 @@ //! queue. #![no_main] #![no_std] -// This should be inside the pool!() macro of heapless. -#![allow(non_upper_case_globals)] use heapless::{ binary_heap::{BinaryHeap, Min}, @@ -30,7 +28,10 @@ use stm32f1xx_hal::{ prelude::*, }; -pool!(CanFramePool: Frame); +pool!( + #[allow(non_upper_case_globals)] + CanFramePool: Frame +); fn alloc_frame(id: Id, data: &[u8]) -> Box { let frame_box = CanFramePool::alloc().unwrap(); @@ -215,11 +216,17 @@ const APP: () = { #[task(binds = CAN2_RX0, resources = [can_rx, can_tx_queue])] fn can2_rx0(cx: can2_rx0::Context) { // Echo back received packages with correct priority ordering. - while let Ok(frame) = cx.resources.can_rx.receive() { - cx.resources - .can_tx_queue - .push(CanFramePool::alloc().unwrap().init(frame)) - .ok(); + loop { + match cx.resources.can_rx.receive() { + Ok(frame) => { + cx.resources + .can_tx_queue + .push(CanFramePool::alloc().unwrap().init(frame)) + .ok(); + } + Err(nb::Error::WouldBlock) => break, + Err(nb::Error::Other(_)) => {} // Ignore overrun errors. + } } // Start transmission of the newly queue frames. diff --git a/src/can.rs b/src/can.rs index 76db6470..3a299b5c 100644 --- a/src/can.rs +++ b/src/can.rs @@ -776,6 +776,12 @@ where } } + +#[derive(Debug)] +pub enum RxError { + Overrun, +} + /// Interface to the CAN receiver part. pub struct Rx { _can: PhantomData, @@ -786,14 +792,16 @@ where Instance: traits::Instance, { /// Returns a received frame if available. - pub fn receive(&mut self) -> nb::Result { + /// + /// Returns `Err` when a frame was lost due to buffer overrun. + pub fn receive(&mut self) -> nb::Result { match self.receive_fifo(0) { Err(nb::Error::WouldBlock) => self.receive_fifo(1), result => result, } } - fn receive_fifo(&mut self, fifo_nr: usize) -> nb::Result { + fn receive_fifo(&mut self, fifo_nr: usize) -> nb::Result { let can = unsafe { &*Instance::REGISTERS }; assert!(fifo_nr < 2); @@ -801,10 +809,17 @@ where let rx = &can.rx[fifo_nr]; // Check if a frame is available in the mailbox. - if rfr.read().fmp().bits() == 0 { + let rfr_read = rfr.read(); + if rfr_read.fmp().bits() == 0 { return Err(nb::Error::WouldBlock); } + // Check for RX FIFO overrun. + if rfr_read.fovr().bit_is_set() { + rfr.write(|w| w.fovr().set_bit()); + return Err(nb::Error::Other(RxError::Overrun)); + } + // Read the frame. let mut frame = Frame { id: Id(rx.rir.read().bits()), From c9ac19ef1ee4157e77bc29c4aaf31be2feaf2e02 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20Kr=C3=B6ger?= Date: Sat, 16 May 2020 10:17:47 +0200 Subject: [PATCH 27/43] can: Remove `RxError` enum --- src/can.rs | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/src/can.rs b/src/can.rs index 3a299b5c..4ea2b934 100644 --- a/src/can.rs +++ b/src/can.rs @@ -776,12 +776,6 @@ where } } - -#[derive(Debug)] -pub enum RxError { - Overrun, -} - /// Interface to the CAN receiver part. pub struct Rx { _can: PhantomData, @@ -794,14 +788,14 @@ where /// Returns a received frame if available. /// /// Returns `Err` when a frame was lost due to buffer overrun. - pub fn receive(&mut self) -> nb::Result { + pub fn receive(&mut self) -> nb::Result { match self.receive_fifo(0) { Err(nb::Error::WouldBlock) => self.receive_fifo(1), result => result, } } - fn receive_fifo(&mut self, fifo_nr: usize) -> nb::Result { + fn receive_fifo(&mut self, fifo_nr: usize) -> nb::Result { let can = unsafe { &*Instance::REGISTERS }; assert!(fifo_nr < 2); @@ -817,7 +811,7 @@ where // Check for RX FIFO overrun. if rfr_read.fovr().bit_is_set() { rfr.write(|w| w.fovr().set_bit()); - return Err(nb::Error::Other(RxError::Overrun)); + return Err(nb::Error::Other(())); } // Read the frame. From 59dfee7ec21602594c1a579cf40b2cbbdaaed5db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20Kr=C3=B6ger?= Date: Sat, 16 May 2020 10:26:00 +0200 Subject: [PATCH 28/43] can: Consistent naming for rtr bit methods --- src/can.rs | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/src/can.rs b/src/can.rs index 4ea2b934..7d9d087b 100644 --- a/src/can.rs +++ b/src/can.rs @@ -87,8 +87,12 @@ impl Id { /// Sets the remote transmission (RTR) flag. This marks the identifier as /// being part of a remote frame. - fn with_rtr(self) -> Id { - Self(self.0 | Self::RTR_MASK) + fn with_rtr(self, rtr: bool) -> Id { + if rtr { + Self(self.0 | Self::RTR_MASK) + } else { + Self(self.0 & !Self::RTR_MASK) + } } /// Returns the identifier. @@ -167,9 +171,12 @@ impl Frame { Self::new(Id::new_extended(id), data) } - /// Marks this frame as a remote frame. - pub fn set_remote(&mut self) -> &mut Self { - self.id = self.id.with_rtr(); + /// Sets the remote transmission (RTR) flag. Marks the frame as a remote frame. + /// + /// Remote frames do not contain any data, even if the frame was created with a + /// non-empty data buffer. + pub fn with_rtr(&mut self, rtr: bool) -> &mut Self { + self.id = self.id.with_rtr(rtr); self.dlc = 0; self } @@ -550,7 +557,8 @@ impl Filter { self } - /// Select if only remote (`rtr = true`) frames or only data (`rtr = false`) shall be received. + /// Select if only remote (`rtr = true`) frames or only data (`rtr = false`) + /// shall be received. pub fn with_rtr(mut self, rtr: bool) -> Self { if rtr { self.id |= Id::RTR_MASK; From 626dc40484d39538c23e0f53d439a7b6b6d84afb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20Kr=C3=B6ger?= Date: Sat, 16 May 2020 11:58:43 +0200 Subject: [PATCH 29/43] can: Fix the `PartialEq` trait for frames --- examples/can-loopback.rs | 3 +-- src/can.rs | 4 +++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/examples/can-loopback.rs b/examples/can-loopback.rs index f89c0c10..2d3170f7 100644 --- a/examples/can-loopback.rs +++ b/examples/can-loopback.rs @@ -58,8 +58,7 @@ fn main() -> ! { let frame_rx = block!(rx.receive()).unwrap(); - assert_eq!(frame_tx, frame_rx); // Only compares the identifier. - assert_eq!(frame_tx.data(), frame_rx.data()); + assert_eq!(frame_tx, frame_rx); } loop {} diff --git a/src/can.rs b/src/can.rs index 7d9d087b..d450d498 100644 --- a/src/can.rs +++ b/src/can.rs @@ -212,6 +212,7 @@ impl Frame { } } +// Ordering is based on the ID and can be used to sort frames by priority. impl Ord for Frame { fn cmp(&self, other: &Self) -> Ordering { self.id().cmp(&other.id()) @@ -224,9 +225,10 @@ impl PartialOrd for Frame { } } +// The Equality traits compare the identifier and the data. impl PartialEq for Frame { fn eq(&self, other: &Self) -> bool { - self.id() == other.id() + self.id() == other.id() && self.data[0..self.dlc] == other.data[0..other.dlc] } } From 312813948f18744d5231540af57eaa9e7dedefa2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20Kr=C3=B6ger?= Date: Sat, 16 May 2020 13:29:43 +0200 Subject: [PATCH 30/43] can: Filter bank split index for stm32f105/107 --- examples/can-echo.rs | 5 +++- examples/can-loopback.rs | 4 +-- examples/can-rtfm.rs | 2 +- src/can.rs | 55 +++++++++++++++++++++++++--------------- 4 files changed, 42 insertions(+), 24 deletions(-) diff --git a/examples/can-echo.rs b/examples/can-echo.rs index af8e71ca..e7a46c7a 100644 --- a/examples/can-echo.rs +++ b/examples/can-echo.rs @@ -45,7 +45,10 @@ fn main() -> ! { // Because the filter banks are part of CAN1 we first need to enable CAN1 // and split the filters between the peripherals to use them for CAN2. let mut can1 = Can::new(dp.CAN1, &mut rcc.apb1); - let (_filters1, mut filters2) = can1.split_filters().unwrap(); + + // Split the filters at index 0: No filters for CAN1 (unused), 28 filters + // for CAN2. + let (_filters1, mut filters2) = can1.split_filters(0).unwrap(); filters2.add(Filter::accept_all()).unwrap(); // Receive all messages. let mut rx = can2.take_rx(filters2).unwrap(); diff --git a/examples/can-loopback.rs b/examples/can-loopback.rs index 2d3170f7..8c7a938a 100644 --- a/examples/can-loopback.rs +++ b/examples/can-loopback.rs @@ -6,7 +6,7 @@ use panic_halt as _; use cortex_m_rt::entry; use nb::block; use stm32f1xx_hal::{ - can::{Can, Filter, Frame}, + can::{Can, Filter, Frame, NUM_FILTER_BANKS}, pac, prelude::*, }; @@ -44,7 +44,7 @@ fn main() -> ! { #[cfg(not(feature = "connectivity"))] let mut filters = can.split_filters().unwrap(); #[cfg(feature = "connectivity")] - let (mut filters, _) = can.split_filters().unwrap(); + let (mut filters, _) = can.split_filters(NUM_FILTER_BANKS / 2).unwrap(); filters.add(Filter::accept_all()).unwrap(); let mut rx = can.take_rx(filters).unwrap(); diff --git a/examples/can-rtfm.rs b/examples/can-rtfm.rs index 9ebf7f1f..b97d66f8 100644 --- a/examples/can-rtfm.rs +++ b/examples/can-rtfm.rs @@ -85,7 +85,7 @@ const APP: () = { CanFramePool::grow(CAN_POOL_MEMORY); let mut can1 = Can::new(cx.device.CAN1, &mut rcc.apb1); - let (_, mut filters) = can1.split_filters().unwrap(); + let (_, mut filters) = can1.split_filters(0).unwrap(); // To share load between FIFOs use one filter for standard messages and another // for extended messages. Accept all IDs by setting the mask to 0. Only accept diff --git a/src/can.rs b/src/can.rs index d450d498..0dbc5bbe 100644 --- a/src/can.rs +++ b/src/can.rs @@ -238,8 +238,6 @@ impl Eq for Frame {} mod traits { pub trait Instance: crate::rcc::Enable { const REGISTERS: *const crate::pac::can1::RegisterBlock; - const FILTER_BANK_START: usize; - const FILTER_BANK_STOP: usize; } pub trait Pins { @@ -250,8 +248,6 @@ mod traits { impl traits::Instance for CAN1 { const REGISTERS: *const crate::pac::can1::RegisterBlock = CAN1::ptr(); - const FILTER_BANK_START: usize = 0; - const FILTER_BANK_STOP: usize = 14; } #[cfg(feature = "connectivity")] @@ -259,8 +255,6 @@ impl traits::Instance for CAN2 { // Register blocks are the same except for the filter registers. // Those are only available on CAN1. const REGISTERS: *const crate::pac::can1::RegisterBlock = CAN2::ptr() as *const _; - const FILTER_BANK_START: usize = CAN1::FILTER_BANK_STOP; - const FILTER_BANK_STOP: usize = 28; } impl traits::Pins for (PA12>, PA11>) { @@ -303,6 +297,11 @@ impl traits::Pins for (PB6>, PB5>) { } } +#[cfg(not(feature = "connectivity"))] +pub const NUM_FILTER_BANKS: usize = 14; +#[cfg(feature = "connectivity")] +pub const NUM_FILTER_BANKS: usize = 28; + /// Configuration proxy to be used with `Can::configure()`. pub struct CanConfig { _can: PhantomData, @@ -474,20 +473,27 @@ impl Can { /// Filters are required for the receiver to accept any messages at all. #[cfg(not(feature = "connectivity"))] pub fn split_filters(&mut self) -> Option> { - self.split_filters_internal()?; - Some(Filters::new()) + self.split_filters_internal(NUM_FILTER_BANKS)?; + Some(Filters::new(0, NUM_FILTER_BANKS)) } /// Returns the filter part of the CAN peripheral. /// /// Filters are required for the receiver to accept any messages at all. + /// `split_idx` can be in the range `0..NUM_FILTER_BANKS` and decides the number + /// of filters assigned to each peripheral. A value of `0` means all filter + /// banks are used for CAN2 while `NUM_FILTER_BANKS` reserves all filter banks + /// for CAN2. #[cfg(feature = "connectivity")] - pub fn split_filters(&mut self) -> Option<(Filters, Filters)> { - self.split_filters_internal()?; - Some((Filters::new(), Filters::new())) + pub fn split_filters(&mut self, split_idx: usize) -> Option<(Filters, Filters)> { + self.split_filters_internal(split_idx)?; + Some(( + Filters::new(0, split_idx), + Filters::new(split_idx, NUM_FILTER_BANKS), + )) } - fn split_filters_internal(&mut self) -> Option<()> { + fn split_filters_internal(&mut self, split_idx: usize) -> Option<()> { let can = unsafe { &*CAN1::ptr() }; if can.fmr.read().finit().bit_is_clear() { @@ -498,15 +504,15 @@ impl Can { can.fm1r.write(|w| unsafe { w.bits(0x0000_0000) }); // Mask mode can.fs1r.write(|w| unsafe { w.bits(0xFFFF_FFFF) }); // 32bit scale - // Filters are alternating between between the FIFO0 and FIFO1. + // Filters are alternating between between the FIFO0 and FIFO1 to share the + // load equally. can.ffa1r.write(|w| unsafe { w.bits(0xAAAA_AAAA) }); // Init filter banks. Each used filter must still be enabled individually. #[allow(unused_unsafe)] can.fmr.modify(|_, w| unsafe { #[cfg(feature = "connectivity")] - w.can2sb() - .bits(::FILTER_BANK_STOP as u8); + w.can2sb().bits(split_idx as u8); w.finit().clear_bit() }); @@ -574,6 +580,8 @@ impl Filter { /// Interface to the filter banks of a CAN peripheral. pub struct Filters { + start_idx: usize, + stop_idx: usize, count: usize, _can: PhantomData, } @@ -582,19 +590,25 @@ impl Filters where Instance: traits::Instance, { - fn new() -> Self { + fn new(start_idx: usize, stop_idx: usize) -> Self { Self { + start_idx, + stop_idx, count: 0, _can: PhantomData, } } + pub fn num_filters(&self) -> usize { + self.stop_idx - self.start_idx + } + /// Adds a filter. Returns `Err` if the maximum number of filters was reached. pub fn add(&mut self, filter: Filter) -> Result<(), ()> { let can = unsafe { &*CAN1::ptr() }; - let idx = Instance::FILTER_BANK_START + self.count; - if idx < Instance::FILTER_BANK_STOP { + let idx = self.start_idx + self.count; + if idx < self.stop_idx { let filter_bank = &can.fb[idx]; filter_bank.fr1.write(|w| unsafe { w.bits(filter.id) }); filter_bank.fr2.write(|w| unsafe { w.bits(filter.mask) }); @@ -610,8 +624,9 @@ where pub fn clear(&mut self) { let can = unsafe { &*CAN1::ptr() }; - // Bitbanding required because the filters are shared between CAN1 and CAN2 - for i in Instance::FILTER_BANK_START..(Instance::FILTER_BANK_START + self.count) { + assert!(self.start_idx + self.count <= self.stop_idx); + for i in self.start_idx..(self.start_idx + self.count) { + // Bitbanding required because the filters are shared between CAN1 and CAN2 bb::clear(&can.fa1r, i as u8); } self.count = 0; From 6e6f1382aff08f084c3b4df29e9c4a19f69a0c53 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20Kr=C3=B6ger?= Date: Sat, 16 May 2020 15:07:44 +0200 Subject: [PATCH 31/43] can: Fix and improve filter configuration --- examples/can-echo.rs | 2 +- examples/can-loopback.rs | 2 +- examples/can-rtfm.rs | 8 +++--- src/can.rs | 59 +++++++++++++++++++++++----------------- 4 files changed, 40 insertions(+), 31 deletions(-) diff --git a/examples/can-echo.rs b/examples/can-echo.rs index e7a46c7a..3e63fe83 100644 --- a/examples/can-echo.rs +++ b/examples/can-echo.rs @@ -49,7 +49,7 @@ fn main() -> ! { // Split the filters at index 0: No filters for CAN1 (unused), 28 filters // for CAN2. let (_filters1, mut filters2) = can1.split_filters(0).unwrap(); - filters2.add(Filter::accept_all()).unwrap(); // Receive all messages. + filters2.add(&Filter::accept_all()).unwrap(); // Receive all messages. let mut rx = can2.take_rx(filters2).unwrap(); // Sync to the bus and start normal operation. diff --git a/examples/can-loopback.rs b/examples/can-loopback.rs index 8c7a938a..aeae6aaf 100644 --- a/examples/can-loopback.rs +++ b/examples/can-loopback.rs @@ -45,7 +45,7 @@ fn main() -> ! { let mut filters = can.split_filters().unwrap(); #[cfg(feature = "connectivity")] let (mut filters, _) = can.split_filters(NUM_FILTER_BANKS / 2).unwrap(); - filters.add(Filter::accept_all()).unwrap(); + filters.add(&Filter::accept_all()).unwrap(); let mut rx = can.take_rx(filters).unwrap(); // Sync to the bus and start normal operation. diff --git a/examples/can-rtfm.rs b/examples/can-rtfm.rs index b97d66f8..09d60ce3 100644 --- a/examples/can-rtfm.rs +++ b/examples/can-rtfm.rs @@ -88,13 +88,13 @@ const APP: () = { let (_, mut filters) = can1.split_filters(0).unwrap(); // To share load between FIFOs use one filter for standard messages and another - // for extended messages. Accept all IDs by setting the mask to 0. Only accept - // data frames (no remote frames). + // for extended messages. Accept all IDs by setting the mask to 0. Explicitly + // allow to receive remote frames. filters - .add(Filter::new_standard(0).with_mask(0).with_rtr(false)) + .add(&Filter::new_standard(0).with_mask(0).allow_remote()) .unwrap(); filters - .add(Filter::new_extended(0).with_mask(0).with_rtr(false)) + .add(&Filter::new_extended(0).with_mask(0).allow_remote()) .unwrap(); let mut can_rx = can2.take_rx(filters).unwrap(); diff --git a/src/can.rs b/src/can.rs index 0dbc5bbe..44f71421 100644 --- a/src/can.rs +++ b/src/can.rs @@ -61,7 +61,7 @@ impl Id { const EXTENDED_SHIFT: u32 = 3; const EXTENDED_MASK: u32 = 0x1FFF_FFFF << Self::EXTENDED_SHIFT; - const EID_MASK: u32 = 0x0000_0004; + const IDE_MASK: u32 = 0x0000_0004; const RTR_MASK: u32 = 0x0000_0002; @@ -78,7 +78,7 @@ impl Id { /// Ids outside the allowed range are silently truncated. pub fn new_extended(id: u32) -> Id { assert!(id < 0x1FFF_FFFF); - Self(id << Self::EXTENDED_SHIFT | Self::EID_MASK) + Self(id << Self::EXTENDED_SHIFT | Self::IDE_MASK) } fn from_register(reg: u32) -> Id { @@ -108,7 +108,7 @@ impl Id { /// Returns `true` if the identifier is an extended identifier. pub fn is_extended(self) -> bool { - self.0 & Self::EID_MASK != 0 + self.0 & Self::IDE_MASK != 0 } /// Returns `true` if the identifier is a standard identifier. @@ -526,38 +526,33 @@ pub struct Filter { mask: u32, } -/// bxCAN filters have some quirks: -/// https://github.com/UAVCAN/libcanard/blob/8ee343c4edae0e0e4e1c040852aa3d8430f7bf76/drivers/stm32/canard_stm32.c#L471-L513 impl Filter { /// Creates a filter that accepts all messages. pub fn accept_all() -> Self { - Self { - id: Id::EID_MASK | Id::RTR_MASK, - mask: 0, - } + Self { id: 0, mask: 0 } } /// Creates a filter that accepts frames with the specified standard identifier. pub fn new_standard(id: u32) -> Self { Self { - id: id << Id::STANDARD_SHIFT | Id::RTR_MASK, - mask: Id::STANDARD_MASK | Id::EID_MASK, + id: id << Id::STANDARD_SHIFT, + mask: Id::STANDARD_MASK | Id::IDE_MASK | Id::RTR_MASK, } } /// Creates a filter that accepts frames with the extended standard identifier. pub fn new_extended(id: u32) -> Self { Self { - id: id << Id::EXTENDED_SHIFT | Id::RTR_MASK | Id::EID_MASK, - mask: Id::EXTENDED_MASK | Id::EID_MASK, + id: id << Id::EXTENDED_SHIFT | Id::IDE_MASK, + mask: Id::EXTENDED_MASK | Id::IDE_MASK | Id::RTR_MASK, } } /// Only look at the bits of the indentifier which are set to 1 in the mask. /// /// A mask of 0 accepts all identifiers. - pub fn with_mask(mut self, mask: u32) -> Self { - if self.id & Id::EID_MASK != 0 { + pub fn with_mask(&mut self, mask: u32) -> &mut Self { + if self.is_extended() { self.mask = (self.mask & !Id::EXTENDED_MASK) | (mask << Id::EXTENDED_SHIFT); } else { self.mask = (self.mask & !Id::STANDARD_MASK) | (mask << Id::STANDARD_SHIFT); @@ -565,17 +560,25 @@ impl Filter { self } - /// Select if only remote (`rtr = true`) frames or only data (`rtr = false`) - /// shall be received. - pub fn with_rtr(mut self, rtr: bool) -> Self { - if rtr { - self.id |= Id::RTR_MASK; - } else { - self.id &= !Id::RTR_MASK; - } + /// Makes this filter accept both data and remote frames. + pub fn allow_remote(&mut self) -> &mut Self { + self.mask &= !Id::RTR_MASK; + self + } + + /// Makes this filter accept only remote frames. + pub fn only_remote(&mut self) -> &mut Self { + self.id |= Id::RTR_MASK; self.mask |= Id::RTR_MASK; self } + + fn is_extended(&self) -> bool { + self.id & Id::IDE_MASK != 0 + } + + fn matches_single_id(&self) -> bool { + } } /// Interface to the filter banks of a CAN peripheral. @@ -604,11 +607,17 @@ where } /// Adds a filter. Returns `Err` if the maximum number of filters was reached. - pub fn add(&mut self, filter: Filter) -> Result<(), ()> { + pub fn add(&mut self, filter: &Filter) -> Result<(), ()> { let can = unsafe { &*CAN1::ptr() }; let idx = self.start_idx + self.count; - if idx < self.stop_idx { + if idx >= self.stop_idx { + return Err(()); + } + + let list_mode = (can.fm1r.read().bits() & (1 << idx)) != 0; + let scale_32bit = (can.fs1r.read().bits() & (1 << idx)) != 0; + if !list_mode && scale_32bit { let filter_bank = &can.fb[idx]; filter_bank.fr1.write(|w| unsafe { w.bits(filter.id) }); filter_bank.fr2.write(|w| unsafe { w.bits(filter.mask) }); From 510daaf0e98f307295664f3982d440451a600326 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20Kr=C3=B6ger?= Date: Sat, 16 May 2020 17:31:14 +0200 Subject: [PATCH 32/43] can: Add advanced filter configuration --- examples/can-echo.rs | 3 +- examples/can-loopback.rs | 52 +++++++++-- src/can.rs | 191 ++++++++++++++++++++++++++++++++++----- 3 files changed, 212 insertions(+), 34 deletions(-) diff --git a/examples/can-echo.rs b/examples/can-echo.rs index 3e63fe83..778c7123 100644 --- a/examples/can-echo.rs +++ b/examples/can-echo.rs @@ -6,7 +6,7 @@ use panic_halt as _; use cortex_m_rt::entry; use nb::block; use stm32f1xx_hal::{ - can::{Can, Filter}, + can::{Can, Filter, NUM_FILTER_BANKS}, pac, prelude::*, }; @@ -49,6 +49,7 @@ fn main() -> ! { // Split the filters at index 0: No filters for CAN1 (unused), 28 filters // for CAN2. let (_filters1, mut filters2) = can1.split_filters(0).unwrap(); + assert_eq!(filters2.num_available(), NUM_FILTER_BANKS); filters2.add(&Filter::accept_all()).unwrap(); // Receive all messages. let mut rx = can2.take_rx(filters2).unwrap(); diff --git a/examples/can-loopback.rs b/examples/can-loopback.rs index aeae6aaf..ae89b337 100644 --- a/examples/can-loopback.rs +++ b/examples/can-loopback.rs @@ -6,7 +6,7 @@ use panic_halt as _; use cortex_m_rt::entry; use nb::block; use stm32f1xx_hal::{ - can::{Can, Filter, Frame, NUM_FILTER_BANKS}, + can::{Can, Filter, Frame}, pac, prelude::*, }; @@ -40,26 +40,60 @@ fn main() -> ! { // Get the transmitter part. let mut tx = can.take_tx().unwrap(); - // Receive all messages. + // Use advanced configurations for the first three filter banks. #[cfg(not(feature = "connectivity"))] - let mut filters = can.split_filters().unwrap(); + let mut filters = can + .split_filters_advanced(0x0000_0006, 0xFFFF_FFFA, 0x0000_0007) + .unwrap(); #[cfg(feature = "connectivity")] - let (mut filters, _) = can.split_filters(NUM_FILTER_BANKS / 2).unwrap(); - filters.add(&Filter::accept_all()).unwrap(); + let (mut filters, _) = can + .split_filters_advanced(0x0000_0006, 0xFFFF_FFFA, 0x0000_0007, 3) + .unwrap(); + + assert_eq!(filters.num_available(), 8); + + // Matches 0, 1, 2 + filters + .add_standard([ + &Filter::new_standard(0).with_mask(!0b1), + &Filter::new_standard(0).with_mask(!0b10), + ]) + .unwrap(); + + // Matches 4, 5 + filters + .add_list([&Filter::new_standard(4), &Filter::new_standard(5)]) + .unwrap(); + + // Matches 8, 9, 10, 11 + filters + .add_standard_list([ + &Filter::new_standard(8), + &Filter::new_standard(9), + &Filter::new_standard(10), + &Filter::new_standard(11), + ]) + .unwrap(); + let mut rx = can.take_rx(filters).unwrap(); // Sync to the bus and start normal operation. block!(can.enable()).ok(); - // Send and receive messages with incrementing identifier. - for id in (0..0x7FF_u32).cycle() { + // Some messages shall pass the filters. + for &id in &[0, 1, 2, 4, 5, 8, 9, 10, 11] { let frame_tx = Frame::new_standard(id, &[id as u8]); block!(tx.transmit(&frame_tx)).unwrap(); - let frame_rx = block!(rx.receive()).unwrap(); - assert_eq!(frame_tx, frame_rx); } + // Others must be filtered out. + for &id in &[3, 6, 7, 12] { + let frame_tx = Frame::new_standard(id, &[id as u8]); + block!(tx.transmit(&frame_tx)).unwrap(); + assert!(rx.receive().is_err()); + } + loop {} } diff --git a/src/can.rs b/src/can.rs index 44f71421..8768e595 100644 --- a/src/can.rs +++ b/src/can.rs @@ -473,7 +473,10 @@ impl Can { /// Filters are required for the receiver to accept any messages at all. #[cfg(not(feature = "connectivity"))] pub fn split_filters(&mut self) -> Option> { - self.split_filters_internal(NUM_FILTER_BANKS)?; + // Set all filter banks to 32bit scale and mask mode. + // Filters are alternating between between the FIFO0 and FIFO1 to share the + // load equally. + self.split_filters_internal(0x0000_0000, 0xFFFF_FFFF, 0xAAAA_AAAA, NUM_FILTER_BANKS)?; Some(Filters::new(0, NUM_FILTER_BANKS)) } @@ -486,33 +489,80 @@ impl Can { /// for CAN2. #[cfg(feature = "connectivity")] pub fn split_filters(&mut self, split_idx: usize) -> Option<(Filters, Filters)> { - self.split_filters_internal(split_idx)?; + // Set all filter banks to 32bit scale and mask mode. + // Filters are alternating between between the FIFO0 and FIFO1 to share the + // load equally. + self.split_filters_internal(0x0000_0000, 0xFFFF_FFFF, 0xAAAA_AAAA, split_idx)?; Some(( Filters::new(0, split_idx), Filters::new(split_idx, NUM_FILTER_BANKS), )) } - fn split_filters_internal(&mut self, split_idx: usize) -> Option<()> { + /// Advanced version of `Can::split_filters()`. + /// + /// The additional parameters are bitmasks configure the filter banks. + /// Bit 0 for filter bank 0, bit 1 for filter bank 1 and so on. + /// `fm1r` in combination with `fs1r` sets the filter bank layout. The correct + /// `Filters::add_*()` function must be used. + /// `ffa1r` selects the FIFO the filter uses to store accepted messages. + /// More details can be found in the reference manual of the device. + #[cfg(not(feature = "connectivity"))] + pub fn split_filters_advanced( + &mut self, + fm1r: u32, + fs1r: u32, + ffa1r: u32, + ) -> Option> { + self.split_filters_internal(fm1r, fs1r, ffa1r, NUM_FILTER_BANKS)?; + Some(Filters::new(0, NUM_FILTER_BANKS)) + } + + /// Advanced version of `Can::split_filters()`. + /// + /// The additional parameters are bitmasks to configure the filter banks. + /// Bit 0 for filter bank 0, bit 1 for filter bank 1 and so on. + /// `fm1r` in combination with `fs1r` sets the filter bank layout. The correct + /// `Filters::add_*()` function must be used. + /// `ffa1r` selects the FIFO the filter uses to store accepted messages. + /// More details can be found in the reference manual of the device. + #[cfg(feature = "connectivity")] + pub fn split_filters_advanced( + &mut self, + fm1r: u32, + fs1r: u32, + ffa1r: u32, + split_idx: usize, + ) -> Option<(Filters, Filters)> { + self.split_filters_internal(fm1r, fs1r, ffa1r, split_idx)?; + Some(( + Filters::new(0, split_idx), + Filters::new(split_idx, NUM_FILTER_BANKS), + )) + } + + fn split_filters_internal( + &mut self, + fm1r: u32, + fs1r: u32, + ffa1r: u32, + _split_idx: usize, + ) -> Option<()> { let can = unsafe { &*CAN1::ptr() }; if can.fmr.read().finit().bit_is_clear() { return None; } - // Same configuration for all filters banks. - can.fm1r.write(|w| unsafe { w.bits(0x0000_0000) }); // Mask mode - can.fs1r.write(|w| unsafe { w.bits(0xFFFF_FFFF) }); // 32bit scale - - // Filters are alternating between between the FIFO0 and FIFO1 to share the - // load equally. - can.ffa1r.write(|w| unsafe { w.bits(0xAAAA_AAAA) }); + can.fm1r.write(|w| unsafe { w.bits(fm1r) }); + can.fs1r.write(|w| unsafe { w.bits(fs1r) }); + can.ffa1r.write(|w| unsafe { w.bits(ffa1r) }); // Init filter banks. Each used filter must still be enabled individually. #[allow(unused_unsafe)] can.fmr.modify(|_, w| unsafe { #[cfg(feature = "connectivity")] - w.can2sb().bits(split_idx as u8); + w.can2sb().bits(_split_idx as u8); w.finit().clear_bit() }); @@ -578,6 +628,16 @@ impl Filter { } fn matches_single_id(&self) -> bool { + ((self.mask & (Id::IDE_MASK | Id::RTR_MASK)) == (Id::IDE_MASK | Id::RTR_MASK)) + && if self.is_extended() { + (self.mask & Id::EXTENDED_MASK) == Id::EXTENDED_MASK + } else { + (self.mask & Id::STANDARD_MASK) == Id::STANDARD_MASK + } + } + + fn as_16bit_filter(reg: u32) -> u32 { + (reg & Id::STANDARD_MASK) >> 16 | (reg & Id::IDE_MASK) << 1 | (reg & Id::RTR_MASK) << 3 } } @@ -602,33 +662,116 @@ where } } - pub fn num_filters(&self) -> usize { - self.stop_idx - self.start_idx + /// Returns the number of available filters. + pub fn num_available(&self) -> usize { + let can = unsafe { &*CAN1::ptr() }; + + let mut filter_count = self.stop_idx - self.start_idx; + + let owned_bits = ((1 << filter_count) - 1) << self.start_idx; + let mode_list = can.fm1r.read().bits() & owned_bits; + let scale_16bit = !can.fs1r.read().bits() & owned_bits; + + filter_count += mode_list.count_ones() as usize; + filter_count += scale_16bit.count_ones() as usize; + filter_count += (mode_list & scale_16bit).count_ones() as usize; + filter_count } /// Adds a filter. Returns `Err` if the maximum number of filters was reached. pub fn add(&mut self, filter: &Filter) -> Result<(), ()> { - let can = unsafe { &*CAN1::ptr() }; + let idx = self.next_idx()?; + self.check_config(idx, false, true)?; + self.write_filter_bank(idx, filter.id, filter.mask); + Ok(()) + } - let idx = self.start_idx + self.count; - if idx >= self.stop_idx { + /// Adds two filters, each filtering for a single ID. + /// + /// The filter bank must to be configured to `fm1r.fbm = 1` and `fs1r.fsc = 1` by + /// `Can::split_filters_advanced()`. + pub fn add_list(&mut self, filters: [&Filter; 2]) -> Result<(), ()> { + if !filters[0].matches_single_id() || !filters[1].matches_single_id() { + return Err(()); + } + + let idx = self.next_idx()?; + self.check_config(idx, true, true)?; + self.write_filter_bank(idx, filters[0].id, filters[1].id); + Ok(()) + } + + /// Adds two standard ID filters with mask support. + /// + /// The filter bank must to be configured to `fm1r.fbm = 0` and `fs1r.fsc = 0` by + /// `Can::split_filters_advanced()`. + pub fn add_standard(&mut self, filters: [&Filter; 2]) -> Result<(), ()> { + if filters[0].is_extended() || filters[1].is_extended() { return Err(()); } - let list_mode = (can.fm1r.read().bits() & (1 << idx)) != 0; - let scale_32bit = (can.fs1r.read().bits() & (1 << idx)) != 0; - if !list_mode && scale_32bit { - let filter_bank = &can.fb[idx]; - filter_bank.fr1.write(|w| unsafe { w.bits(filter.id) }); - filter_bank.fr2.write(|w| unsafe { w.bits(filter.mask) }); - bb::set(&can.fa1r, idx as u8); // Enable filter - self.count += 1; + let idx = self.next_idx()?; + self.check_config(idx, false, false)?; + self.write_filter_bank( + idx, + Filter::as_16bit_filter(filters[0].mask) << 16 | Filter::as_16bit_filter(filters[0].id), + Filter::as_16bit_filter(filters[1].mask) << 16 | Filter::as_16bit_filter(filters[1].id), + ); + Ok(()) + } + + /// Adds four filters, each filtering for a single standard ID. + /// + /// The filter bank must to be configured to `fm1r.fbm = 1` and `fs1r.fsc = 0` by + /// `Can::split_filters_advanced()`. + pub fn add_standard_list(&mut self, filters: [&Filter; 4]) -> Result<(), ()> { + for filter in &filters { + if !filter.matches_single_id() { + return Err(()); + } + } + + let idx = self.next_idx()?; + self.check_config(idx, true, false)?; + self.write_filter_bank( + idx, + Filter::as_16bit_filter(filters[1].id) << 16 | Filter::as_16bit_filter(filters[0].id), + Filter::as_16bit_filter(filters[3].id) << 16 | Filter::as_16bit_filter(filters[2].id), + ); + Ok(()) + } + + fn next_idx(&self) -> Result { + let idx = self.start_idx + self.count; + if idx < self.stop_idx { + Ok(idx) + } else { + Err(()) + } + } + + fn check_config(&self, idx: usize, mode_list: bool, scale_32bit: bool) -> Result<(), ()> { + let can = unsafe { &*CAN1::ptr() }; + + if mode_list == ((can.fm1r.read().bits() & (1 << idx)) != 0) + && scale_32bit == ((can.fs1r.read().bits() & (1 << idx)) != 0) + { Ok(()) } else { Err(()) } } + fn write_filter_bank(&mut self, idx: usize, fr1: u32, fr2: u32) { + let can = unsafe { &*CAN1::ptr() }; + + let filter_bank = &can.fb[idx]; + filter_bank.fr1.write(|w| unsafe { w.bits(fr1) }); + filter_bank.fr2.write(|w| unsafe { w.bits(fr2) }); + bb::set(&can.fa1r, idx as u8); // Enable filter + self.count += 1; + } + /// Disables all enabled filters. pub fn clear(&mut self) { let can = unsafe { &*CAN1::ptr() }; From baf7f0f232add8547416c1b391e9f08b4d694fa4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20Kr=C3=B6ger?= Date: Sun, 17 May 2020 20:53:08 +0200 Subject: [PATCH 33/43] can: Documentation and example cleanup --- Cargo.toml | 1 - examples/can-echo.rs | 15 ++++----- examples/can-loopback.rs | 17 ++++++++-- examples/can-rtfm.rs | 29 ++++++++++------- src/can.rs | 67 ++++++++++++++++++++++++---------------- 5 files changed, 79 insertions(+), 50 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 2856c23a..799c7bff 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -98,7 +98,6 @@ codegen-units = 1 [profile.release] codegen-units = 1 debug = true -opt-level = "s" lto = true [[example]] diff --git a/examples/can-echo.rs b/examples/can-echo.rs index 778c7123..2e1fc8a6 100644 --- a/examples/can-echo.rs +++ b/examples/can-echo.rs @@ -1,3 +1,5 @@ +//! Simple CAN example. Requires a transceiver connected to PB5 and PB6. + #![no_main] #![no_std] @@ -22,14 +24,13 @@ fn main() -> ! { // resonator must be used. rcc.cfgr.use_hse(8.mhz()).freeze(&mut flash.acr); - let mut gpiob = dp.GPIOB.split(&mut rcc.apb2); + let mut can2 = Can::new(dp.CAN2, &mut rcc.apb1); - // Select some pins for CAN2. + // Select pins for CAN2. + let mut gpiob = dp.GPIOB.split(&mut rcc.apb2); let can_rx_pin = gpiob.pb5.into_floating_input(&mut gpiob.crl); let can_tx_pin = gpiob.pb6.into_alternate_push_pull(&mut gpiob.crl); let mut afio = dp.AFIO.constrain(&mut rcc.apb2); - - let mut can2 = Can::new(dp.CAN2, &mut rcc.apb1); can2.assign_pins((can_tx_pin, can_rx_pin), &mut afio.mapr); can2.configure(|config| { @@ -38,9 +39,6 @@ fn main() -> ! { config.set_bit_timing(0x001c_0003); }); - // Get the transmitter part. - let mut tx = can2.take_tx().unwrap(); - // Filters are required to use the receiver part of CAN2. // Because the filter banks are part of CAN1 we first need to enable CAN1 // and split the filters between the peripherals to use them for CAN2. @@ -51,7 +49,10 @@ fn main() -> ! { let (_filters1, mut filters2) = can1.split_filters(0).unwrap(); assert_eq!(filters2.num_available(), NUM_FILTER_BANKS); filters2.add(&Filter::accept_all()).unwrap(); // Receive all messages. + + // Split the peripheral into transmitter and receiver parts. let mut rx = can2.take_rx(filters2).unwrap(); + let mut tx = can2.take_tx().unwrap(); // Sync to the bus and start normal operation. block!(can2.enable()).ok(); diff --git a/examples/can-loopback.rs b/examples/can-loopback.rs index ae89b337..659001b8 100644 --- a/examples/can-loopback.rs +++ b/examples/can-loopback.rs @@ -1,9 +1,13 @@ +//! Showcases advanced CAN filter capabilities. +//! Does not require additional transceiver hardware. + #![no_main] #![no_std] use panic_halt as _; use cortex_m_rt::entry; +use embedded_hal::digital::v2::OutputPin; use nb::block; use stm32f1xx_hal::{ can::{Can, Filter, Frame}, @@ -37,10 +41,8 @@ fn main() -> ! { config.set_silent(true); }); - // Get the transmitter part. - let mut tx = can.take_tx().unwrap(); - // Use advanced configurations for the first three filter banks. + // More details can be found in the reference manual of the device. #[cfg(not(feature = "connectivity"))] let mut filters = can .split_filters_advanced(0x0000_0006, 0xFFFF_FFFA, 0x0000_0007) @@ -52,6 +54,9 @@ fn main() -> ! { assert_eq!(filters.num_available(), 8); + // The order of the added filters is important: it must match configuration + // of the `split_filters_advanced()` method. + // Matches 0, 1, 2 filters .add_standard([ @@ -75,7 +80,9 @@ fn main() -> ! { ]) .unwrap(); + // Split the peripheral into transmitter and receiver parts. let mut rx = can.take_rx(filters).unwrap(); + let mut tx = can.take_tx().unwrap(); // Sync to the bus and start normal operation. block!(can.enable()).ok(); @@ -95,5 +102,9 @@ fn main() -> ! { assert!(rx.receive().is_err()); } + let mut gpiob = dp.GPIOB.split(&mut rcc.apb2); + let mut led = gpiob.pb9.into_push_pull_output(&mut gpiob.crh); + led.set_high().unwrap(); + loop {} } diff --git a/examples/can-rtfm.rs b/examples/can-rtfm.rs index 09d60ce3..d5e11704 100644 --- a/examples/can-rtfm.rs +++ b/examples/can-rtfm.rs @@ -6,8 +6,8 @@ //! transmission the interrupt is reentered and more data is fetched from the //! queue. //! Received frames are simply echoed back. In contrast to the naive `can-echo` -//! example the echo messages are also correctly prioritized by the transmit -//! queue. +//! example all messages are also correctly prioritized by the transmit queue. + #![no_main] #![no_std] @@ -64,26 +64,24 @@ const APP: () = { .pclk2(72.mhz()) .freeze(&mut flash.acr); - let mut gpiob = cx.device.GPIOB.split(&mut rcc.apb2); - let mut afio = cx.device.AFIO.constrain(&mut rcc.apb2); + let mut can2 = Can::new(cx.device.CAN2, &mut rcc.apb1); + // Select pins for CAN2. + let mut gpiob = cx.device.GPIOB.split(&mut rcc.apb2); let can_rx_pin = gpiob.pb5.into_floating_input(&mut gpiob.crl); let can_tx_pin = gpiob.pb6.into_alternate_push_pull(&mut gpiob.crl); - - let mut can2 = Can::new(cx.device.CAN2, &mut rcc.apb1); + let mut afio = cx.device.AFIO.constrain(&mut rcc.apb2); can2.assign_pins((can_tx_pin, can_rx_pin), &mut afio.mapr); can2.configure(|config| { // APB1 (PCLK1): 36MHz, Bit rate: 125kBit/s, Sample Point 87.5% // Value was calculated with http://www.bittiming.can-wiki.info/ - config.set_bit_timing(0x001c0011); + config.set_bit_timing(0x001c_0011); }); - let mut can_tx = can2.take_tx().unwrap(); - can_tx.enable_interrupt(); - let can_tx_queue = BinaryHeap::new(); - CanFramePool::grow(CAN_POOL_MEMORY); - + // Filters are required to use the receiver part of CAN2. + // Because the filter banks are part of CAN1 we first need to enable CAN1 + // and split the filters between the peripherals to use them for CAN2. let mut can1 = Can::new(cx.device.CAN1, &mut rcc.apb1); let (_, mut filters) = can1.split_filters(0).unwrap(); @@ -100,6 +98,13 @@ const APP: () = { let mut can_rx = can2.take_rx(filters).unwrap(); can_rx.enable_interrupts(); + let mut can_tx = can2.take_tx().unwrap(); + can_tx.enable_interrupt(); + + let can_tx_queue = BinaryHeap::new(); + CanFramePool::grow(CAN_POOL_MEMORY); + + // Sync to the bus and start normal operation. can2.enable().ok(); init::LateResources { diff --git a/src/can.rs b/src/can.rs index 8768e595..baf1128b 100644 --- a/src/can.rs +++ b/src/can.rs @@ -45,10 +45,9 @@ use core::{ /// Can be either a standard identifier (11bit, Range: 0..0x3FF) or a /// extendended identifier (29bit , Range: 0..0x1FFFFFFF). /// -/// The `Ord` trait is can be used to determine the frame’s priority this ID -/// belongs to. This works because the EID and RTR flags are included in the -/// underlying integer representaiton. -/// Lower identifier values mean higher priority. Additionally standard frames +/// The `Ord` trait can be used to determine the frame’s priority this ID +/// belongs to. +/// Lower identifier values have a higher priority. Additionally standard frames /// have a higher priority than extended frames and data frames have a higher /// priority than remote frames. #[derive(Clone, Copy, Debug, PartialEq, Eq)] @@ -67,7 +66,7 @@ impl Id { /// Creates a new standard identifier (11bit, Range: 0..0x7FF) /// - /// Ids outside the allowed range are silently truncated. + /// IDs outside the allowed range are silently truncated. pub fn new_standard(id: u32) -> Self { assert!(id < 0x7FF); Self(id << Self::STANDARD_SHIFT) @@ -75,7 +74,7 @@ impl Id { /// Creates a new extendended identifier (29bit , Range: 0..0x1FFFFFFF). /// - /// Ids outside the allowed range are silently truncated. + /// IDs outside the allowed range are silently truncated. pub fn new_extended(id: u32) -> Id { assert!(id < 0x1FFF_FFFF); Self(id << Self::EXTENDED_SHIFT | Self::IDE_MASK) @@ -161,12 +160,12 @@ impl Frame { frame } - /// Creates ane new frame with a standard identifier. + /// Creates a new frame with a standard identifier. pub fn new_standard(id: u32, data: &[u8]) -> Self { Self::new(Id::new_standard(id), data) } - /// Creates ane new frame with an extended identifier. + /// Creates a new frame with an extended identifier. pub fn new_extended(id: u32, data: &[u8]) -> Self { Self::new(Id::new_extended(id), data) } @@ -181,22 +180,22 @@ impl Frame { self } - /// Returns true if this `Frame` is a extended frame + /// Returns true if this frame is an extended frame pub fn is_extended(&self) -> bool { self.id.is_extended() } - /// Returns true if this `Frame` is a standard frame + /// Returns true if this frame is a standard frame pub fn is_standard(&self) -> bool { self.id.is_standard() } - /// Returns true if this `Frame` is a remote frame + /// Returns true if this frame is a remote frame pub fn is_remote_frame(&self) -> bool { self.id.rtr() } - /// Returns true if this `Frame` is a data frame + /// Returns true if this frame is a data frame pub fn is_data_frame(&self) -> bool { !self.is_remote_frame() } @@ -297,8 +296,11 @@ impl traits::Pins for (PB6>, PB5>) { } } +/// Number of supported filter banks. #[cfg(not(feature = "connectivity"))] pub const NUM_FILTER_BANKS: usize = 14; + +/// Number of supported filter banks. #[cfg(feature = "connectivity")] pub const NUM_FILTER_BANKS: usize = 28; @@ -309,7 +311,7 @@ pub struct CanConfig { impl CanConfig where - Instance: traits::Instance, + Instance: traits::Instance, { /// Configures the bit timings. /// @@ -322,13 +324,14 @@ where }); } - /// Enables or disables loopback mode: Internally connects the TX to RX. + /// Enables or disables loopback mode: Internally connects the TX and RX + /// signals together. pub fn set_loopback(&mut self, enabled: bool) { let can = unsafe { &*Instance::REGISTERS }; can.btr.modify(|_, w| w.lbkm().bit(enabled)); } - /// Enables or disables silent mode: Disconnects TX from the pin. + /// Enables or disables silent mode: Disconnects the TX signal from the pin. pub fn set_silent(&mut self, enabled: bool) { let can = unsafe { &*Instance::REGISTERS }; can.btr.modify(|_, w| w.silm().bit(enabled)); @@ -417,7 +420,6 @@ where let can = unsafe { &*Instance::REGISTERS }; let msr = can.msr.read(); if msr.slak().bit_is_set() { - // TODO: Make automatic bus-off management configurable. can.mcr .modify(|_, w| w.abom().set_bit().sleep().clear_bit()); Err(nb::Error::WouldBlock) @@ -486,7 +488,7 @@ impl Can { /// `split_idx` can be in the range `0..NUM_FILTER_BANKS` and decides the number /// of filters assigned to each peripheral. A value of `0` means all filter /// banks are used for CAN2 while `NUM_FILTER_BANKS` reserves all filter banks - /// for CAN2. + /// for CAN1. #[cfg(feature = "connectivity")] pub fn split_filters(&mut self, split_idx: usize) -> Option<(Filters, Filters)> { // Set all filter banks to 32bit scale and mask mode. @@ -570,7 +572,7 @@ impl Can { } } -/// A masked filter configuration. +/// Filter with an optional mask. pub struct Filter { id: u32, mask: u32, @@ -636,9 +638,17 @@ impl Filter { } } - fn as_16bit_filter(reg: u32) -> u32 { + fn reg_to_16bit(reg: u32) -> u32 { (reg & Id::STANDARD_MASK) >> 16 | (reg & Id::IDE_MASK) << 1 | (reg & Id::RTR_MASK) << 3 } + + fn id_to_16bit(&self) -> u32 { + Self::reg_to_16bit(self.id) + } + + fn mask_to_16bit(&self) -> u32 { + Self::reg_to_16bit(self.mask) + } } /// Interface to the filter banks of a CAN peripheral. @@ -663,6 +673,9 @@ where } /// Returns the number of available filters. + /// + /// This can number can be larger than the number of filter banks if + /// `Can::split_filters_advanced()` was used. pub fn num_available(&self) -> usize { let can = unsafe { &*CAN1::ptr() }; @@ -714,8 +727,8 @@ where self.check_config(idx, false, false)?; self.write_filter_bank( idx, - Filter::as_16bit_filter(filters[0].mask) << 16 | Filter::as_16bit_filter(filters[0].id), - Filter::as_16bit_filter(filters[1].mask) << 16 | Filter::as_16bit_filter(filters[1].id), + filters[0].mask_to_16bit() << 16 | filters[0].id_to_16bit(), + filters[1].mask_to_16bit() << 16 | filters[1].id_to_16bit(), ); Ok(()) } @@ -735,8 +748,8 @@ where self.check_config(idx, true, false)?; self.write_filter_bank( idx, - Filter::as_16bit_filter(filters[1].id) << 16 | Filter::as_16bit_filter(filters[0].id), - Filter::as_16bit_filter(filters[3].id) << 16 | Filter::as_16bit_filter(filters[2].id), + filters[1].id_to_16bit() << 16 | filters[0].id_to_16bit(), + filters[3].id_to_16bit() << 16 | filters[2].id_to_16bit(), ); Ok(()) } @@ -772,7 +785,7 @@ where self.count += 1; } - /// Disables all enabled filters. + /// Disables all enabled filter banks. pub fn clear(&mut self) { let can = unsafe { &*CAN1::ptr() }; @@ -805,9 +818,9 @@ where /// Puts a CAN frame in a free transmit mailbox for transmission on the bus. /// /// Frames are transmitted to the bus based on their priority (identifier). - /// Transmit order is preserved for frames with identical identifiers. - /// If all transmit mailboxes are full a higher priority frame replaces the - /// lowest priority frame which is returned as `Ok(Some(frame))`. + /// Transmit order is preserved for frames with of identifiers. + /// If all transmit mailboxes are full, a higher priority frame replaces the + /// lowest priority frame, which is returned as `Ok(Some(frame))`. pub fn transmit(&mut self, frame: &Frame) -> nb::Result, Infallible> { let can = unsafe { &*Instance::REGISTERS }; From e3a17fefe6ea7dc7d2aae682f5aea831dca82fba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20Kr=C3=B6ger?= Date: Thu, 21 May 2020 12:38:46 +0200 Subject: [PATCH 34/43] can: RTR frames can have a DLC > 0 --- src/can.rs | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/src/can.rs b/src/can.rs index baf1128b..53605113 100644 --- a/src/can.rs +++ b/src/can.rs @@ -170,13 +170,13 @@ impl Frame { Self::new(Id::new_extended(id), data) } - /// Sets the remote transmission (RTR) flag. Marks the frame as a remote frame. + /// Marks the frame as a remote frame with configurable data length code (DLC). /// /// Remote frames do not contain any data, even if the frame was created with a /// non-empty data buffer. - pub fn with_rtr(&mut self, rtr: bool) -> &mut Self { - self.id = self.id.with_rtr(rtr); - self.dlc = 0; + pub fn with_rtr(&mut self, dlc: usize) -> &mut Self { + self.id = self.id.with_rtr(true); + self.dlc = dlc; self } @@ -205,9 +205,21 @@ impl Frame { self.id } + /// Returns the data length code (DLC) which is in the range 0..8. + /// + /// For data frames the DLC value always matches the lenght of the data. + /// Remote frames no not carry any data, yet the DLC can be greater than 0. + pub fn dlc(&self) -> usize { + self.dlc + } + /// Returns the frame data (0..8 bytes in length). pub fn data(&self) -> &[u8] { - &self.data[0..self.dlc] + if self.is_data_frame() { + &self.data[0..self.dlc] + } else { + &[] + } } } From ee348dff1d52fc676e8b6c26fa9f93794c0537b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20Kr=C3=B6ger?= Date: Thu, 6 Aug 2020 09:52:23 +0200 Subject: [PATCH 35/43] Do not build `can-loopback` when unsupported The example only works on devices with CAN peripheral --- Cargo.toml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Cargo.toml b/Cargo.toml index 799c7bff..7fa905e1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -136,6 +136,10 @@ required-features = ["rt"] name = "can-echo" required-features = ["connectivity"] +[[example]] +name = "can-loopback" +required-features = ["stm32f103", "stm32f105", "stm32f107"] + [[example]] name = "can-rtfm" required-features = ["connectivity", "rt"] From e86efb03f6cb99a45f81965e56f73fd6d25f95f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20Kr=C3=B6ger?= Date: Sat, 23 May 2020 11:12:11 +0200 Subject: [PATCH 36/43] can: Improve advanced filter configuration --- examples/can-loopback.rs | 29 ++++----- src/can.rs | 135 +++++++++++++++++---------------------- 2 files changed, 70 insertions(+), 94 deletions(-) diff --git a/examples/can-loopback.rs b/examples/can-loopback.rs index 659001b8..b7c70857 100644 --- a/examples/can-loopback.rs +++ b/examples/can-loopback.rs @@ -57,28 +57,23 @@ fn main() -> ! { // The order of the added filters is important: it must match configuration // of the `split_filters_advanced()` method. - // Matches 0, 1, 2 + // 2x 11bit id + mask filter bank: Matches 0, 1, 2 filters - .add_standard([ - &Filter::new_standard(0).with_mask(!0b1), - &Filter::new_standard(0).with_mask(!0b10), - ]) + .add(&Filter::new_standard(0).with_mask(!0b1)) .unwrap(); - - // Matches 4, 5 filters - .add_list([&Filter::new_standard(4), &Filter::new_standard(5)]) + .add(&Filter::new_standard(0).with_mask(!0b10)) .unwrap(); - // Matches 8, 9, 10, 11 - filters - .add_standard_list([ - &Filter::new_standard(8), - &Filter::new_standard(9), - &Filter::new_standard(10), - &Filter::new_standard(11), - ]) - .unwrap(); + // 2x 29bit id filter bank: Matches 4, 5 + filters.add(&Filter::new_standard(4)).unwrap(); + filters.add(&Filter::new_standard(5)).unwrap(); + + // 4x 11bit id filter bank: Matches 8, 9, 10, 11 + filters.add(&Filter::new_standard(8)).unwrap(); + filters.add(&Filter::new_standard(9)).unwrap(); + filters.add(&Filter::new_standard(10)).unwrap(); + filters.add(&Filter::new_standard(11)).unwrap(); // Split the peripheral into transmitter and receiver parts. let mut rx = can.take_rx(filters).unwrap(); diff --git a/src/can.rs b/src/can.rs index 53605113..bdccfc74 100644 --- a/src/can.rs +++ b/src/can.rs @@ -705,96 +705,77 @@ where /// Adds a filter. Returns `Err` if the maximum number of filters was reached. pub fn add(&mut self, filter: &Filter) -> Result<(), ()> { - let idx = self.next_idx()?; - self.check_config(idx, false, true)?; - self.write_filter_bank(idx, filter.id, filter.mask); - Ok(()) - } + let can = unsafe { &*CAN1::ptr() }; - /// Adds two filters, each filtering for a single ID. - /// - /// The filter bank must to be configured to `fm1r.fbm = 1` and `fs1r.fsc = 1` by - /// `Can::split_filters_advanced()`. - pub fn add_list(&mut self, filters: [&Filter; 2]) -> Result<(), ()> { - if !filters[0].matches_single_id() || !filters[1].matches_single_id() { + let idx = self.start_idx + self.count; + if idx >= self.stop_idx { return Err(()); } - let idx = self.next_idx()?; - self.check_config(idx, true, true)?; - self.write_filter_bank(idx, filters[0].id, filters[1].id); - Ok(()) - } + let mode_list = (can.fm1r.read().bits() & (1 << idx)) != 0; + let scale_16bit = (can.fs1r.read().bits() & (1 << idx)) == 0; + let bank_enabled = (can.fa1r.read().bits() & (1 << idx)) != 0; - /// Adds two standard ID filters with mask support. - /// - /// The filter bank must to be configured to `fm1r.fbm = 0` and `fs1r.fsc = 0` by - /// `Can::split_filters_advanced()`. - pub fn add_standard(&mut self, filters: [&Filter; 2]) -> Result<(), ()> { - if filters[0].is_extended() || filters[1].is_extended() { + // Make sure the filter is supported by the filter bank configuration. + if (mode_list && !filter.matches_single_id()) || (scale_16bit && filter.is_extended()) { return Err(()); } - let idx = self.next_idx()?; - self.check_config(idx, false, false)?; - self.write_filter_bank( - idx, - filters[0].mask_to_16bit() << 16 | filters[0].id_to_16bit(), - filters[1].mask_to_16bit() << 16 | filters[1].id_to_16bit(), - ); - Ok(()) - } + // Disable the filter bank so it can be modified. + bb::clear(&can.fa1r, idx as u8); - /// Adds four filters, each filtering for a single standard ID. - /// - /// The filter bank must to be configured to `fm1r.fbm = 1` and `fs1r.fsc = 0` by - /// `Can::split_filters_advanced()`. - pub fn add_standard_list(&mut self, filters: [&Filter; 4]) -> Result<(), ()> { - for filter in &filters { - if !filter.matches_single_id() { - return Err(()); + let filter_bank = &can.fb[idx]; + let fr1 = filter_bank.fr1.read().bits(); + let fr2 = filter_bank.fr2.read().bits(); + let (fr1, fr2) = match (mode_list, scale_16bit, bank_enabled) { + // 29bit id + mask + (false, false, _) => { + self.count += 1; + (filter.id, filter.mask) } - } - - let idx = self.next_idx()?; - self.check_config(idx, true, false)?; - self.write_filter_bank( - idx, - filters[1].id_to_16bit() << 16 | filters[0].id_to_16bit(), - filters[3].id_to_16bit() << 16 | filters[2].id_to_16bit(), - ); - Ok(()) - } - - fn next_idx(&self) -> Result { - let idx = self.start_idx + self.count; - if idx < self.stop_idx { - Ok(idx) - } else { - Err(()) - } - } - - fn check_config(&self, idx: usize, mode_list: bool, scale_32bit: bool) -> Result<(), ()> { - let can = unsafe { &*CAN1::ptr() }; - - if mode_list == ((can.fm1r.read().bits() & (1 << idx)) != 0) - && scale_32bit == ((can.fs1r.read().bits() & (1 << idx)) != 0) - { - Ok(()) - } else { - Err(()) - } - } - - fn write_filter_bank(&mut self, idx: usize, fr1: u32, fr2: u32) { - let can = unsafe { &*CAN1::ptr() }; + // 2x 29bit id + (true, false, false) => (filter.id, filter.id), + (true, false, true) => { + self.count += 1; + (fr1, filter.id) + } + // 2x 11bit id + mask + (false, true, false) => ( + filter.mask_to_16bit() << 16 | filter.id_to_16bit(), + filter.mask_to_16bit() << 16 | filter.id_to_16bit(), + ), + (false, true, true) => { + self.count += 1; + (fr1, filter.mask_to_16bit() << 16 | filter.id_to_16bit()) + } + // 4x 11bit id + (true, true, false) => ( + filter.id_to_16bit() << 16 | filter.id_to_16bit(), + filter.id_to_16bit() << 16 | filter.id_to_16bit(), + ), + (true, true, true) => { + let f = [fr1 & 0xFFFF, fr1 >> 16, fr2 & 0xFFFF, fr2 >> 16]; + + if f[0] == f[1] { + // One filter available, add the second. + (filter.id_to_16bit() << 16 | f[0], fr2) + } else if f[0] == f[2] { + // Two filters available, add the third. + (fr1, f[0] << 16 | filter.id_to_16bit()) + } else if f[0] == f[3] { + // Three filters available, add the fourth. + self.count += 1; + (fr1, filter.id_to_16bit() << 16 | f[2]) + } else { + unreachable!() + } + } + }; - let filter_bank = &can.fb[idx]; filter_bank.fr1.write(|w| unsafe { w.bits(fr1) }); filter_bank.fr2.write(|w| unsafe { w.bits(fr2) }); - bb::set(&can.fa1r, idx as u8); // Enable filter - self.count += 1; + bb::set(&can.fa1r, idx as u8); // Enable the filter bank + Ok(()) } /// Disables all enabled filter banks. From a2e8f0df09b6d03515a1554964a036ab5ab69622 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20Kr=C3=B6ger?= Date: Sun, 30 Aug 2020 13:12:59 +0200 Subject: [PATCH 37/43] can: Improve feature usage for examples Previously the `can-loopback` example could not be built at all --- Cargo.toml | 8 +++++--- src/lib.rs | 5 +---- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 7fa905e1..57452c6e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -78,7 +78,7 @@ doc = [] rt = ["stm32f1/rt"] stm32f100 = ["stm32f1/stm32f100", "device-selected"] stm32f101 = ["stm32f1/stm32f101", "device-selected"] -stm32f103 = ["stm32f1/stm32f103", "device-selected"] +stm32f103 = ["stm32f1/stm32f103", "device-selected", "has-can"] stm32f105 = ["stm32f1/stm32f107", "device-selected", "connectivity"] stm32f107 = ["stm32f1/stm32f107", "device-selected", "connectivity"] @@ -89,7 +89,9 @@ high = ["medium"] # Devices with 768 Kb ROM or more xl = ["high"] # Connectivity line devices (`stm32f105xx` and `stm32f107xx`) -connectivity = ["medium"] +connectivity = ["medium", "has-can"] +# Devices with CAN interface +has-can = [] [profile.dev] incremental = false @@ -138,7 +140,7 @@ required-features = ["connectivity"] [[example]] name = "can-loopback" -required-features = ["stm32f103", "stm32f105", "stm32f107"] +required-features = ["has-can"] [[example]] name = "can-rtfm" diff --git a/src/lib.rs b/src/lib.rs index 55847c2b..c3a22e91 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -144,10 +144,7 @@ pub mod afio; pub mod backup_domain; #[cfg(feature = "device-selected")] pub mod bb; -#[cfg(all( - feature = "device-selected", - any(feature = "stm32f103", feature = "connectivity") -))] +#[cfg(all(feature = "device-selected", feature = "has-can"))] pub mod can; #[cfg(feature = "device-selected")] pub mod crc; From cb801eb6e993051ee402e3fbc6f709d2be849bd4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20Kr=C3=B6ger?= Date: Sun, 6 Sep 2020 07:34:31 +0200 Subject: [PATCH 38/43] can: Make `can-rtfm` example work with stm32f103 Based on @arrowcircle example on #215 --- Cargo.toml | 2 +- examples/can-rtfm.rs | 73 +++++++++++++++++++++++--------------------- 2 files changed, 39 insertions(+), 36 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 57452c6e..c9030db2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -144,4 +144,4 @@ required-features = ["has-can"] [[example]] name = "can-rtfm" -required-features = ["connectivity", "rt"] +required-features = ["has-can", "rt"] diff --git a/examples/can-rtfm.rs b/examples/can-rtfm.rs index d5e11704..59c5558a 100644 --- a/examples/can-rtfm.rs +++ b/examples/can-rtfm.rs @@ -24,7 +24,7 @@ use panic_halt as _; use rtfm::app; use stm32f1xx_hal::{ can::{Can, Filter, Frame, Id, Rx, Tx}, - pac::{Interrupt, CAN2}, + pac::{Interrupt, CAN1}, prelude::*, }; @@ -41,10 +41,10 @@ fn alloc_frame(id: Id, data: &[u8]) -> Box { #[app(device = stm32f1xx_hal::pac, peripherals = true)] const APP: () = { struct Resources { - can_tx: Tx, + can_tx: Tx, can_tx_queue: BinaryHeap, U8, Min>, tx_count: usize, - can_rx: Rx, + can_rx: Rx, } #[init] @@ -56,38 +56,41 @@ const APP: () = { let mut flash = cx.device.FLASH.constrain(); let mut rcc = cx.device.RCC.constrain(); - rcc.cfgr + let _clocks = rcc + .cfgr .use_hse(8.mhz()) - .sysclk(72.mhz()) - .hclk(72.mhz()) - .pclk1(36.mhz()) - .pclk2(72.mhz()) + .sysclk(64.mhz()) + .hclk(64.mhz()) + .pclk1(16.mhz()) + .pclk2(64.mhz()) .freeze(&mut flash.acr); - let mut can2 = Can::new(cx.device.CAN2, &mut rcc.apb1); + #[cfg(not(feature = "connectivity"))] + let mut can = Can::new(cx.device.CAN1, &mut rcc.apb1, cx.device.USB); - // Select pins for CAN2. - let mut gpiob = cx.device.GPIOB.split(&mut rcc.apb2); - let can_rx_pin = gpiob.pb5.into_floating_input(&mut gpiob.crl); - let can_tx_pin = gpiob.pb6.into_alternate_push_pull(&mut gpiob.crl); + #[cfg(feature = "connectivity")] + let mut can = Can::new(cx.device.CAN1, &mut rcc.apb1); + + // Select pins for CAN1. + let mut gpioa = cx.device.GPIOA.split(&mut rcc.apb2); + let can_rx_pin = gpioa.pa11.into_floating_input(&mut gpioa.crh); + let can_tx_pin = gpioa.pa12.into_alternate_push_pull(&mut gpioa.crh); let mut afio = cx.device.AFIO.constrain(&mut rcc.apb2); - can2.assign_pins((can_tx_pin, can_rx_pin), &mut afio.mapr); + can.assign_pins((can_tx_pin, can_rx_pin), &mut afio.mapr); - can2.configure(|config| { - // APB1 (PCLK1): 36MHz, Bit rate: 125kBit/s, Sample Point 87.5% + can.configure(|config| { + // APB1 (PCLK1): 16MHz, Bit rate: 1000kBit/s, Sample Point 87.5% // Value was calculated with http://www.bittiming.can-wiki.info/ - config.set_bit_timing(0x001c_0011); + config.set_bit_timing(0x001c_0000); }); - // Filters are required to use the receiver part of CAN2. - // Because the filter banks are part of CAN1 we first need to enable CAN1 - // and split the filters between the peripherals to use them for CAN2. - let mut can1 = Can::new(cx.device.CAN1, &mut rcc.apb1); - let (_, mut filters) = can1.split_filters(0).unwrap(); - // To share load between FIFOs use one filter for standard messages and another // for extended messages. Accept all IDs by setting the mask to 0. Explicitly // allow to receive remote frames. + #[cfg(not(feature = "connectivity"))] + let mut filters = can.split_filters().unwrap(); + #[cfg(feature = "connectivity")] + let (mut filters, _) = can.split_filters(0).unwrap(); filters .add(&Filter::new_standard(0).with_mask(0).allow_remote()) .unwrap(); @@ -95,17 +98,17 @@ const APP: () = { .add(&Filter::new_extended(0).with_mask(0).allow_remote()) .unwrap(); - let mut can_rx = can2.take_rx(filters).unwrap(); + let mut can_rx = can.take_rx(filters).unwrap(); can_rx.enable_interrupts(); - let mut can_tx = can2.take_tx().unwrap(); + let mut can_tx = can.take_tx().unwrap(); can_tx.enable_interrupt(); let can_tx_queue = BinaryHeap::new(); CanFramePool::grow(CAN_POOL_MEMORY); // Sync to the bus and start normal operation. - can2.enable().ok(); + can.enable().ok(); init::LateResources { can_tx, @@ -145,7 +148,7 @@ const APP: () = { }); // Manually trigger the tx interrupt to start the transmission. - rtfm::pend(Interrupt::CAN2_TX); + rtfm::pend(Interrupt::USB_HP_CAN_TX); // Add some higher priority messages when 3 messages have been sent. loop { @@ -187,8 +190,8 @@ const APP: () = { } // This ISR is triggered by each finished frame transmission. - #[task(binds = CAN2_TX, resources = [can_tx, can_tx_queue, tx_count])] - fn can2_tx(cx: can2_tx::Context) { + #[task(binds = USB_HP_CAN_TX, resources = [can_tx, can_tx_queue, tx_count])] + fn can_tx(cx: can_tx::Context) { let tx = cx.resources.can_tx; let tx_queue = cx.resources.can_tx_queue; @@ -218,8 +221,8 @@ const APP: () = { } } - #[task(binds = CAN2_RX0, resources = [can_rx, can_tx_queue])] - fn can2_rx0(cx: can2_rx0::Context) { + #[task(binds = USB_LP_CAN_RX0, resources = [can_rx, can_tx_queue])] + fn can_rx0(cx: can_rx0::Context) { // Echo back received packages with correct priority ordering. loop { match cx.resources.can_rx.receive() { @@ -235,12 +238,12 @@ const APP: () = { } // Start transmission of the newly queue frames. - rtfm::pend(Interrupt::CAN2_TX); + rtfm::pend(Interrupt::USB_HP_CAN_TX); } - #[task(binds = CAN2_RX1)] - fn can2_rx1(_: can2_rx1::Context) { + #[task(binds = CAN_RX1)] + fn can_rx1(_: can_rx1::Context) { // Jump to the other interrupt handler which handles both RX fifos. - rtfm::pend(Interrupt::CAN2_RX0); + rtfm::pend(Interrupt::USB_LP_CAN_RX0); } }; From f5b6d73ae8731690719fcc509aebd02f2746ca19 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20Kr=C3=B6ger?= Date: Thu, 1 Oct 2020 17:24:13 +0200 Subject: [PATCH 39/43] can: Fix PB8 PB9 pin remapping --- src/can.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/can.rs b/src/can.rs index bdccfc74..e54f3522 100644 --- a/src/can.rs +++ b/src/can.rs @@ -284,9 +284,9 @@ impl traits::Pins for (PB9>, PB8>) { fn remap(mapr: &mut MAPR) { #[cfg(not(feature = "connectivity"))] - mapr.modify_mapr(|_, w| unsafe { w.can_remap().bits(0x10) }); + mapr.modify_mapr(|_, w| unsafe { w.can_remap().bits(0b10) }); #[cfg(feature = "connectivity")] - mapr.modify_mapr(|_, w| unsafe { w.can1_remap().bits(0x10) }); + mapr.modify_mapr(|_, w| unsafe { w.can1_remap().bits(0b10) }); } } From 916b96c7f2438431f5f09b8e1bf2a13b366d00dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20Kr=C3=B6ger?= Date: Fri, 2 Oct 2020 07:46:57 +0200 Subject: [PATCH 40/43] can: Use `unsafe` for bit-banding --- src/can.rs | 40 +++++++++++++++++++++++++--------------- 1 file changed, 25 insertions(+), 15 deletions(-) diff --git a/src/can.rs b/src/can.rs index e54f3522..3841f4de 100644 --- a/src/can.rs +++ b/src/can.rs @@ -454,8 +454,10 @@ where /// /// Call `Can::enable()` in the ISR when the automatic wake-up is not enabled. pub fn enable_wakeup_interrupt(&mut self) { - let can = unsafe { &*Instance::REGISTERS }; - bb::set(&can.ier, 16); // WKUIE + unsafe { + let can = &*Instance::REGISTERS; + bb::set(&can.ier, 16); // WKUIE + } } /// Clears all state-change interrupt flags. @@ -722,7 +724,7 @@ where } // Disable the filter bank so it can be modified. - bb::clear(&can.fa1r, idx as u8); + unsafe { bb::clear(&can.fa1r, idx as u8) }; let filter_bank = &can.fb[idx]; let fr1 = filter_bank.fr1.read().bits(); @@ -774,7 +776,7 @@ where filter_bank.fr1.write(|w| unsafe { w.bits(fr1) }); filter_bank.fr2.write(|w| unsafe { w.bits(fr2) }); - bb::set(&can.fa1r, idx as u8); // Enable the filter bank + unsafe { bb::set(&can.fa1r, idx as u8) }; // Enable the filter bank Ok(()) } @@ -785,7 +787,7 @@ where assert!(self.start_idx + self.count <= self.stop_idx); for i in self.start_idx..(self.start_idx + self.count) { // Bitbanding required because the filters are shared between CAN1 and CAN2 - bb::clear(&can.fa1r, i as u8); + unsafe { bb::clear(&can.fa1r, i as u8) }; } self.count = 0; } @@ -941,14 +943,18 @@ where /// /// The interrupt flags must be cleared with `Tx::clear_interrupt_flags()`. pub fn enable_interrupt(&mut self) { - let can = unsafe { &*Instance::REGISTERS }; - bb::set(&can.ier, 0); // TMEIE + unsafe { + let can = &*Instance::REGISTERS; + bb::set(&can.ier, 0); // TMEIE + } } /// Disables the transmit interrupt. pub fn disable_interrupt(&mut self) { - let can = unsafe { &*Instance::REGISTERS }; - bb::clear(&can.ier, 0); // TMEIE + unsafe { + let can = &*Instance::REGISTERS; + bb::clear(&can.ier, 0); // TMEIE + } } /// Clears the request complete flag for all mailboxes. @@ -1017,15 +1023,19 @@ where /// Make sure to register interrupt handlers for both. /// The interrupt flags are cleared by reading frames with `Rx::receive()`. pub fn enable_interrupts(&mut self) { - let can = unsafe { &*Instance::REGISTERS }; - bb::set(&can.ier, 1); // FMPIE0 - bb::set(&can.ier, 4); // FMPIE1 + unsafe { + let can = &*Instance::REGISTERS; + bb::set(&can.ier, 1); // FMPIE0 + bb::set(&can.ier, 4); // FMPIE1 + } } /// Disables the receive interrupts. pub fn disable_interrupts(&mut self) { - let can = unsafe { &*Instance::REGISTERS }; - bb::clear(&can.ier, 1); // FMPIE0 - bb::clear(&can.ier, 4); // FMPIE1 + unsafe { + let can = &*Instance::REGISTERS; + bb::clear(&can.ier, 1); // FMPIE0 + bb::clear(&can.ier, 4); // FMPIE1 + } } } From e1b217d052e09bc294fd25d68468640162c922ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20Kr=C3=B6ger?= Date: Mon, 5 Oct 2020 18:02:43 +0200 Subject: [PATCH 41/43] Fix typos and review comments Big thanks to @TheZoq2 --- examples/can-echo.rs | 3 ++- examples/can-loopback.rs | 2 +- examples/can-rtfm.rs | 2 -- src/can.rs | 11 ++++++----- src/rcc.rs | 1 - 5 files changed, 9 insertions(+), 10 deletions(-) diff --git a/examples/can-echo.rs b/examples/can-echo.rs index 2e1fc8a6..e9d057e6 100644 --- a/examples/can-echo.rs +++ b/examples/can-echo.rs @@ -21,7 +21,8 @@ fn main() -> ! { let mut rcc = dp.RCC.constrain(); // To meet CAN clock accuracy requirements an external crystal or ceramic - // resonator must be used. + // resonator must be used. The blue pill has a 8MHz external crystal. + // Other boards might have a crystal with another frequency or none at all. rcc.cfgr.use_hse(8.mhz()).freeze(&mut flash.acr); let mut can2 = Can::new(dp.CAN2, &mut rcc.apb1); diff --git a/examples/can-loopback.rs b/examples/can-loopback.rs index b7c70857..bc5ee737 100644 --- a/examples/can-loopback.rs +++ b/examples/can-loopback.rs @@ -22,7 +22,7 @@ fn main() -> ! { let mut flash = dp.FLASH.constrain(); let mut rcc = dp.RCC.constrain(); - // To meet CAN clock accuracy requirements an external crystal or ceramic + // To meet CAN clock accuracy requirements, an external crystal or ceramic // resonator must be used. rcc.cfgr.use_hse(8.mhz()).freeze(&mut flash.acr); diff --git a/examples/can-rtfm.rs b/examples/can-rtfm.rs index 59c5558a..bba01655 100644 --- a/examples/can-rtfm.rs +++ b/examples/can-rtfm.rs @@ -51,8 +51,6 @@ const APP: () = { fn init(cx: init::Context) -> init::LateResources { static mut CAN_POOL_MEMORY: [u8; 256] = [0; 256]; - unsafe { cx.core.SCB.vtor.write(0x0800_0000) }; - let mut flash = cx.device.FLASH.constrain(); let mut rcc = cx.device.RCC.constrain(); diff --git a/src/can.rs b/src/can.rs index 3841f4de..68fd061c 100644 --- a/src/can.rs +++ b/src/can.rs @@ -66,7 +66,7 @@ impl Id { /// Creates a new standard identifier (11bit, Range: 0..0x7FF) /// - /// IDs outside the allowed range are silently truncated. + /// Panics for IDs outside the allowed range. pub fn new_standard(id: u32) -> Self { assert!(id < 0x7FF); Self(id << Self::STANDARD_SHIFT) @@ -74,7 +74,7 @@ impl Id { /// Creates a new extendended identifier (29bit , Range: 0..0x1FFFFFFF). /// - /// IDs outside the allowed range are silently truncated. + /// Panics for IDs outside the allowed range. pub fn new_extended(id: u32) -> Id { assert!(id < 0x1FFF_FFFF); Self(id << Self::EXTENDED_SHIFT | Self::IDE_MASK) @@ -207,7 +207,7 @@ impl Frame { /// Returns the data length code (DLC) which is in the range 0..8. /// - /// For data frames the DLC value always matches the lenght of the data. + /// For data frames the DLC value always matches the length of the data. /// Remote frames no not carry any data, yet the DLC can be greater than 0. pub fn dlc(&self) -> usize { self.dlc @@ -522,7 +522,8 @@ impl Can { /// `fm1r` in combination with `fs1r` sets the filter bank layout. The correct /// `Filters::add_*()` function must be used. /// `ffa1r` selects the FIFO the filter uses to store accepted messages. - /// More details can be found in the reference manual of the device. + /// More details can be found in the reference manual (Section 24.7.4 + /// Identifier filtering, Filter bank scale and mode configuration). #[cfg(not(feature = "connectivity"))] pub fn split_filters_advanced( &mut self, @@ -813,7 +814,7 @@ where /// Puts a CAN frame in a free transmit mailbox for transmission on the bus. /// /// Frames are transmitted to the bus based on their priority (identifier). - /// Transmit order is preserved for frames with of identifiers. + /// Transmit order is preserved for frames with identical identifiers. /// If all transmit mailboxes are full, a higher priority frame replaces the /// lowest priority frame, which is returned as `Ok(Some(frame))`. pub fn transmit(&mut self, frame: &Frame) -> nb::Result, Infallible> { diff --git a/src/rcc.rs b/src/rcc.rs index edb31030..3eef7afe 100644 --- a/src/rcc.rs +++ b/src/rcc.rs @@ -650,7 +650,6 @@ bus! { } #[cfg(feature = "connectivity")] bus! { - ADC2 => (APB2, adc2en, adc2rst), CAN1 => (APB1, can1en, can1rst), CAN2 => (APB1, can2en, can2rst), } From 8d1732aa3bed9113fe09846a6b3640af18419d3f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20Kr=C3=B6ger?= Date: Tue, 6 Oct 2020 22:25:54 +0200 Subject: [PATCH 42/43] can: Add `Id` enum With this addition the frame constructors are now fallible and return `Error` when the identifier is out of range. --- examples/can-loopback.rs | 22 +++--- examples/can-rtfm.rs | 24 +++---- src/can.rs | 148 ++++++++++++++++++++++----------------- 3 files changed, 106 insertions(+), 88 deletions(-) diff --git a/examples/can-loopback.rs b/examples/can-loopback.rs index bc5ee737..d5d9c7c0 100644 --- a/examples/can-loopback.rs +++ b/examples/can-loopback.rs @@ -10,7 +10,7 @@ use cortex_m_rt::entry; use embedded_hal::digital::v2::OutputPin; use nb::block; use stm32f1xx_hal::{ - can::{Can, Filter, Frame}, + can::{Can, Filter, Frame, Id}, pac, prelude::*, }; @@ -59,21 +59,21 @@ fn main() -> ! { // 2x 11bit id + mask filter bank: Matches 0, 1, 2 filters - .add(&Filter::new_standard(0).with_mask(!0b1)) + .add(&Filter::new(Id::Standard(0)).with_mask(!0b1)) .unwrap(); filters - .add(&Filter::new_standard(0).with_mask(!0b10)) + .add(&Filter::new(Id::Standard(0)).with_mask(!0b10)) .unwrap(); // 2x 29bit id filter bank: Matches 4, 5 - filters.add(&Filter::new_standard(4)).unwrap(); - filters.add(&Filter::new_standard(5)).unwrap(); + filters.add(&Filter::new(Id::Standard(4))).unwrap(); + filters.add(&Filter::new(Id::Standard(5))).unwrap(); // 4x 11bit id filter bank: Matches 8, 9, 10, 11 - filters.add(&Filter::new_standard(8)).unwrap(); - filters.add(&Filter::new_standard(9)).unwrap(); - filters.add(&Filter::new_standard(10)).unwrap(); - filters.add(&Filter::new_standard(11)).unwrap(); + filters.add(&Filter::new(Id::Standard(8))).unwrap(); + filters.add(&Filter::new(Id::Standard(9))).unwrap(); + filters.add(&Filter::new(Id::Standard(10))).unwrap(); + filters.add(&Filter::new(Id::Standard(11))).unwrap(); // Split the peripheral into transmitter and receiver parts. let mut rx = can.take_rx(filters).unwrap(); @@ -84,7 +84,7 @@ fn main() -> ! { // Some messages shall pass the filters. for &id in &[0, 1, 2, 4, 5, 8, 9, 10, 11] { - let frame_tx = Frame::new_standard(id, &[id as u8]); + let frame_tx = Frame::new(Id::Standard(id), &[id as u8]).unwrap(); block!(tx.transmit(&frame_tx)).unwrap(); let frame_rx = block!(rx.receive()).unwrap(); assert_eq!(frame_tx, frame_rx); @@ -92,7 +92,7 @@ fn main() -> ! { // Others must be filtered out. for &id in &[3, 6, 7, 12] { - let frame_tx = Frame::new_standard(id, &[id as u8]); + let frame_tx = Frame::new(Id::Standard(id), &[id as u8]).unwrap(); block!(tx.transmit(&frame_tx)).unwrap(); assert!(rx.receive().is_err()); } diff --git a/examples/can-rtfm.rs b/examples/can-rtfm.rs index bba01655..9045307c 100644 --- a/examples/can-rtfm.rs +++ b/examples/can-rtfm.rs @@ -35,7 +35,7 @@ pool!( fn alloc_frame(id: Id, data: &[u8]) -> Box { let frame_box = CanFramePool::alloc().unwrap(); - frame_box.init(Frame::new(id, data)) + frame_box.init(Frame::new(id, data).unwrap()) } #[app(device = stm32f1xx_hal::pac, peripherals = true)] @@ -90,10 +90,10 @@ const APP: () = { #[cfg(feature = "connectivity")] let (mut filters, _) = can.split_filters(0).unwrap(); filters - .add(&Filter::new_standard(0).with_mask(0).allow_remote()) + .add(&Filter::new(Id::Standard(0)).with_mask(0).allow_remote()) .unwrap(); filters - .add(&Filter::new_extended(0).with_mask(0).allow_remote()) + .add(&Filter::new(Id::Extended(0)).with_mask(0).allow_remote()) .unwrap(); let mut can_rx = can.take_rx(filters).unwrap(); @@ -123,25 +123,25 @@ const APP: () = { // Enqueue some messages. Higher ID means lower priority. tx_queue.lock(|tx_queue| { tx_queue - .push(alloc_frame(Id::new_standard(9), &[0, 1, 2, 4])) + .push(alloc_frame(Id::Standard(9), &[0, 1, 2, 4])) .unwrap(); tx_queue - .push(alloc_frame(Id::new_standard(9), &[0, 1, 2, 4])) + .push(alloc_frame(Id::Standard(9), &[0, 1, 2, 4])) .unwrap(); tx_queue - .push(alloc_frame(Id::new_standard(8), &[0, 1, 2, 4])) + .push(alloc_frame(Id::Standard(8), &[0, 1, 2, 4])) .unwrap(); // Extended frames have lower priority than standard frames. tx_queue - .push(alloc_frame(Id::new_extended(8), &[0, 1, 2, 4])) + .push(alloc_frame(Id::Extended(8), &[0, 1, 2, 4])) .unwrap(); tx_queue - .push(alloc_frame(Id::new_extended(7), &[0, 1, 2, 4])) + .push(alloc_frame(Id::Extended(7), &[0, 1, 2, 4])) .unwrap(); tx_queue - .push(alloc_frame(Id::new_standard(7), &[0, 1, 2, 4])) + .push(alloc_frame(Id::Standard(7), &[0, 1, 2, 4])) .unwrap(); }); @@ -155,13 +155,13 @@ const APP: () = { if tx_count >= 3 { tx_queue.lock(|tx_queue| { tx_queue - .push(alloc_frame(Id::new_standard(3), &[0, 1, 2, 4])) + .push(alloc_frame(Id::Standard(3), &[0, 1, 2, 4])) .unwrap(); tx_queue - .push(alloc_frame(Id::new_standard(2), &[0, 1, 2, 4])) + .push(alloc_frame(Id::Standard(2), &[0, 1, 2, 4])) .unwrap(); tx_queue - .push(alloc_frame(Id::new_standard(1), &[0, 1, 2, 4])) + .push(alloc_frame(Id::Standard(1), &[0, 1, 2, 4])) .unwrap(); }); break; diff --git a/src/can.rs b/src/can.rs index 68fd061c..e749041e 100644 --- a/src/can.rs +++ b/src/can.rs @@ -40,6 +40,27 @@ use core::{ marker::PhantomData, }; +/// 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, + } + } +} + /// Identifier of a CAN message. /// /// Can be either a standard identifier (11bit, Range: 0..0x3FF) or a @@ -51,9 +72,9 @@ use core::{ /// have a higher priority than extended frames and data frames have a higher /// priority than remote frames. #[derive(Clone, Copy, Debug, PartialEq, Eq)] -pub struct Id(u32); +struct IdReg(u32); -impl Id { +impl IdReg { const STANDARD_SHIFT: u32 = 21; const STANDARD_MASK: u32 = 0x7FF << Self::STANDARD_SHIFT; @@ -75,18 +96,18 @@ impl Id { /// Creates a new extendended identifier (29bit , Range: 0..0x1FFFFFFF). /// /// Panics for IDs outside the allowed range. - pub fn new_extended(id: u32) -> Id { + fn new_extended(id: u32) -> IdReg { assert!(id < 0x1FFF_FFFF); Self(id << Self::EXTENDED_SHIFT | Self::IDE_MASK) } - fn from_register(reg: u32) -> Id { + fn from_register(reg: u32) -> IdReg { Self(reg & 0xFFFF_FFFE) } /// Sets the remote transmission (RTR) flag. This marks the identifier as /// being part of a remote frame. - fn with_rtr(self, rtr: bool) -> Id { + fn with_rtr(self, rtr: bool) -> IdReg { if rtr { Self(self.0 | Self::RTR_MASK) } else { @@ -95,23 +116,21 @@ impl Id { } /// Returns the identifier. - /// - /// It is up to the user to check if it is an standard or extended id. - pub fn as_u32(self) -> u32 { + fn to_id(self) -> Id { if self.is_extended() { - self.0 >> Self::EXTENDED_SHIFT + Id::Extended(self.0 >> Self::EXTENDED_SHIFT) } else { - self.0 >> Self::STANDARD_SHIFT + Id::Standard(self.0 >> Self::STANDARD_SHIFT) } } /// Returns `true` if the identifier is an extended identifier. - pub fn is_extended(self) -> bool { + fn is_extended(self) -> bool { self.0 & Self::IDE_MASK != 0 } /// Returns `true` if the identifier is a standard identifier. - pub fn is_standard(self) -> bool { + fn is_standard(self) -> bool { !self.is_extended() } @@ -121,7 +140,7 @@ impl Id { } } -impl Ord for Id { +impl Ord for IdReg { fn cmp(&self, other: &Self) -> Ordering { match (self.is_standard(), other.is_standard()) { (true, false) => Ordering::Less, @@ -132,7 +151,7 @@ impl Ord for Id { } } -impl PartialOrd for Id { +impl PartialOrd for IdReg { fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(other)) } @@ -141,15 +160,22 @@ impl PartialOrd for Id { /// A CAN data or remote frame. #[derive(Clone, Debug)] pub struct Frame { - id: Id, + id: IdReg, dlc: usize, data: [u8; 8], } impl Frame { - /// Creates a new data frame. - pub fn new(id: Id, data: &[u8]) -> Self { - assert!(!id.rtr()); + /// Creates a new frame. + pub fn new(id: Id, data: &[u8]) -> Result { + if !id.valid() || data.len() > 8 { + return Err(()); + } + + let id = match id { + Id::Standard(id) => IdReg::new_standard(id), + Id::Extended(id) => IdReg::new_extended(id), + }; let mut frame = Self { id, @@ -157,27 +183,19 @@ impl Frame { data: [0; 8], }; frame.data[0..data.len()].copy_from_slice(data); - frame - } - - /// Creates a new frame with a standard identifier. - pub fn new_standard(id: u32, data: &[u8]) -> Self { - Self::new(Id::new_standard(id), data) + Ok(frame) } - /// Creates a new frame with an extended identifier. - pub fn new_extended(id: u32, data: &[u8]) -> Self { - Self::new(Id::new_extended(id), data) - } + /// Creates a new remote frame with configurable data length code (DLC). + pub fn new_remote(id: Id, dlc: usize) -> Result { + if dlc >= 8 { + return Err(()); + } - /// Marks the frame as a remote frame with configurable data length code (DLC). - /// - /// Remote frames do not contain any data, even if the frame was created with a - /// non-empty data buffer. - pub fn with_rtr(&mut self, dlc: usize) -> &mut Self { - self.id = self.id.with_rtr(true); - self.dlc = dlc; - self + let mut frame = Self::new(id, &[])?; + frame.dlc = dlc; + frame.id.with_rtr(true); + Ok(frame) } /// Returns true if this frame is an extended frame @@ -202,7 +220,7 @@ impl Frame { /// Returns the frame identifier. pub fn id(&self) -> Id { - self.id + self.id.to_id() } /// Returns the data length code (DLC) which is in the range 0..8. @@ -226,7 +244,7 @@ impl Frame { // Ordering is based on the ID and can be used to sort frames by priority. impl Ord for Frame { fn cmp(&self, other: &Self) -> Ordering { - self.id().cmp(&other.id()) + self.id.cmp(&other.id) } } @@ -599,19 +617,17 @@ impl Filter { Self { id: 0, mask: 0 } } - /// Creates a filter that accepts frames with the specified standard identifier. - pub fn new_standard(id: u32) -> Self { - Self { - id: id << Id::STANDARD_SHIFT, - mask: Id::STANDARD_MASK | Id::IDE_MASK | Id::RTR_MASK, - } - } - - /// Creates a filter that accepts frames with the extended standard identifier. - pub fn new_extended(id: u32) -> Self { - Self { - id: id << Id::EXTENDED_SHIFT | Id::IDE_MASK, - mask: Id::EXTENDED_MASK | Id::IDE_MASK | Id::RTR_MASK, + /// Creates a filter that accepts frames with the specified identifier. + pub fn new(id: Id) -> Self { + match id { + Id::Standard(id) => Filter { + id: id << IdReg::STANDARD_SHIFT, + mask: IdReg::STANDARD_MASK | IdReg::IDE_MASK | IdReg::RTR_MASK, + }, + Id::Extended(id) => Filter { + id: id << IdReg::EXTENDED_SHIFT | IdReg::IDE_MASK, + mask: IdReg::EXTENDED_MASK | IdReg::IDE_MASK | IdReg::RTR_MASK, + }, } } @@ -620,41 +636,43 @@ impl Filter { /// A mask of 0 accepts all identifiers. pub fn with_mask(&mut self, mask: u32) -> &mut Self { if self.is_extended() { - self.mask = (self.mask & !Id::EXTENDED_MASK) | (mask << Id::EXTENDED_SHIFT); + self.mask = (self.mask & !IdReg::EXTENDED_MASK) | (mask << IdReg::EXTENDED_SHIFT); } else { - self.mask = (self.mask & !Id::STANDARD_MASK) | (mask << Id::STANDARD_SHIFT); + self.mask = (self.mask & !IdReg::STANDARD_MASK) | (mask << IdReg::STANDARD_SHIFT); } self } /// Makes this filter accept both data and remote frames. pub fn allow_remote(&mut self) -> &mut Self { - self.mask &= !Id::RTR_MASK; + self.mask &= !IdReg::RTR_MASK; self } /// Makes this filter accept only remote frames. pub fn only_remote(&mut self) -> &mut Self { - self.id |= Id::RTR_MASK; - self.mask |= Id::RTR_MASK; + self.id |= IdReg::RTR_MASK; + self.mask |= IdReg::RTR_MASK; self } fn is_extended(&self) -> bool { - self.id & Id::IDE_MASK != 0 + self.id & IdReg::IDE_MASK != 0 } fn matches_single_id(&self) -> bool { - ((self.mask & (Id::IDE_MASK | Id::RTR_MASK)) == (Id::IDE_MASK | Id::RTR_MASK)) + ((self.mask & (IdReg::IDE_MASK | IdReg::RTR_MASK)) == (IdReg::IDE_MASK | IdReg::RTR_MASK)) && if self.is_extended() { - (self.mask & Id::EXTENDED_MASK) == Id::EXTENDED_MASK + (self.mask & IdReg::EXTENDED_MASK) == IdReg::EXTENDED_MASK } else { - (self.mask & Id::STANDARD_MASK) == Id::STANDARD_MASK + (self.mask & IdReg::STANDARD_MASK) == IdReg::STANDARD_MASK } } fn reg_to_16bit(reg: u32) -> u32 { - (reg & Id::STANDARD_MASK) >> 16 | (reg & Id::IDE_MASK) << 1 | (reg & Id::RTR_MASK) << 3 + (reg & IdReg::STANDARD_MASK) >> 16 + | (reg & IdReg::IDE_MASK) << 1 + | (reg & IdReg::RTR_MASK) << 3 } fn id_to_16bit(&self) -> u32 { @@ -859,7 +877,7 @@ where /// Returns `Ok` when the mailbox is free or has a lower priority than /// identifier than `id`. - fn check_priority(&self, idx: usize, id: Id) -> nb::Result<(), Infallible> { + fn check_priority(&self, idx: usize, id: IdReg) -> nb::Result<(), Infallible> { let can = unsafe { &*Instance::REGISTERS }; // Read the pending frame's id to check its priority. @@ -868,7 +886,7 @@ where // Check the priority by comparing the identifiers. But first make sure the // frame has not finished transmission (`TXRQ` == 0) in the meantime. - if tir.txrq().bit_is_set() && id >= Id::from_register(tir.bits()) { + if tir.txrq().bit_is_set() && id >= IdReg::from_register(tir.bits()) { // There's a mailbox whose priority is higher or equal // the priority of the new frame. return Err(nb::Error::WouldBlock); @@ -901,7 +919,7 @@ where if self.abort(idx) { // Read back the pending frame. let mut pending_frame = Frame { - id: Id(mb.tir.read().bits()), + id: IdReg(mb.tir.read().bits()), dlc: mb.tdtr.read().dlc().bits() as usize, data: [0; 8], }; @@ -1006,7 +1024,7 @@ where // Read the frame. let mut frame = Frame { - id: Id(rx.rir.read().bits()), + id: IdReg(rx.rir.read().bits()), dlc: rx.rdtr.read().dlc().bits() as usize, data: [0; 8], }; From c6a095ef1db3ef42c3390d509aeb0d85d4a7a785 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20Kr=C3=B6ger?= Date: Sun, 11 Oct 2020 22:52:58 +0200 Subject: [PATCH 43/43] can: Improve RTFM example * Fix typos and improve comments * Flatting return code checking * Use `nop` instead `wfi` so that debugging still works --- examples/can-rtfm.rs | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/examples/can-rtfm.rs b/examples/can-rtfm.rs index 9045307c..9768d69a 100644 --- a/examples/can-rtfm.rs +++ b/examples/can-rtfm.rs @@ -183,7 +183,7 @@ const APP: () = { // The output can look different if there are other nodes on bus the sending messages. loop { - cortex_m::asm::wfi(); + cortex_m::asm::nop(); } } @@ -195,26 +195,25 @@ const APP: () = { tx.clear_interrupt_flags(); - // There is now a free mailbox. Send the next frame if there is still someting - // in the queue. + // There is now a free mailbox. Try to transmit pending frames until either + // the queue is empty or transmission would block the execution of this ISR. while let Some(frame) = tx_queue.peek() { match tx.transmit(&frame) { Ok(None) => { + // Frame was successfully placed into a transmit buffer. tx_queue.pop(); *cx.resources.tx_count += 1; } - Ok(pending_frame) => { + Ok(Some(pending_frame)) => { // A lower priority frame was replaced with our high priority frame. // Put the low priority frame back in the transmit queue. tx_queue.pop(); - if let Some(frame) = pending_frame { - tx_queue - .push(CanFramePool::alloc().unwrap().init(frame)) - .unwrap(); - } + tx_queue + .push(CanFramePool::alloc().unwrap().init(pending_frame)) + .unwrap(); } Err(nb::Error::WouldBlock) => break, - _ => unreachable!(), + Err(_) => unreachable!(), } } }