From 663fa2addd30463328f6186e14f98680b4a35c9b Mon Sep 17 00:00:00 2001 From: Frostie314159 <iam.an.programmer@gmail.com> Date: Mon, 11 Dec 2023 13:27:55 +0100 Subject: [PATCH 01/14] Introduce reset_{at|after} functions for Ticker. --- embassy-time/src/timer.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/embassy-time/src/timer.rs b/embassy-time/src/timer.rs index 574d715da..3444d3e24 100644 --- a/embassy-time/src/timer.rs +++ b/embassy-time/src/timer.rs @@ -184,6 +184,16 @@ impl Ticker { self.expires_at = Instant::now() + self.duration; } + /// Reset the ticker to fire for the next time on the deadline. + pub fn reset_at(&mut self, deadline: Instant) { + self.expires_at = deadline; + } + + /// Resets the ticker, after the specified duration has passed. + pub fn reset_after(&mut self, after: Duration) { + self.expires_at = Instant::now() + after; + } + /// Waits for the next tick. pub fn next(&mut self) -> impl Future<Output = ()> + '_ { poll_fn(|cx| { From 8707462ec23807782796fbac4295bc5bce9ff136 Mon Sep 17 00:00:00 2001 From: Frostie314159 <iam.an.programmer@gmail.com> Date: Mon, 11 Dec 2023 16:11:57 +0100 Subject: [PATCH 02/14] Adjusted documentation and reset_after behaviour. --- embassy-time/src/timer.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/embassy-time/src/timer.rs b/embassy-time/src/timer.rs index 3444d3e24..fe0e93951 100644 --- a/embassy-time/src/timer.rs +++ b/embassy-time/src/timer.rs @@ -185,13 +185,15 @@ impl Ticker { } /// Reset the ticker to fire for the next time on the deadline. + /// If the deadline is in the past, the ticker will fire instantly. pub fn reset_at(&mut self, deadline: Instant) { self.expires_at = deadline; } /// Resets the ticker, after the specified duration has passed. + /// If the specified duration is zero, the next tick will be after the duration of the ticker. pub fn reset_after(&mut self, after: Duration) { - self.expires_at = Instant::now() + after; + self.expires_at = Instant::now() + after + self.duration; } /// Waits for the next tick. From c0590626273f020893a8fedf7bffa1cb9a9a6d77 Mon Sep 17 00:00:00 2001 From: Maarten de Vries <maarten@de-vri.es> Date: Wed, 27 Mar 2024 15:39:44 +0100 Subject: [PATCH 03/14] embassy_stm32: Preseve the RTR flag in messages. --- embassy-stm32/src/can/bx/mod.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/embassy-stm32/src/can/bx/mod.rs b/embassy-stm32/src/can/bx/mod.rs index a369ae6fd..121da1bb2 100644 --- a/embassy-stm32/src/can/bx/mod.rs +++ b/embassy-stm32/src/can/bx/mod.rs @@ -776,13 +776,13 @@ where let mb = self.canregs.tx(idx); - let id = IdReg(mb.tir().read().0).id(); + let id = IdReg(mb.tir().read().0); let mut data = [0xff; 8]; data[0..4].copy_from_slice(&mb.tdlr().read().0.to_ne_bytes()); data[4..8].copy_from_slice(&mb.tdhr().read().0.to_ne_bytes()); let len = mb.tdtr().read().dlc(); - Some(Frame::new(Header::new(id, len, false), &data).unwrap()) + Some(Frame::new(Header::new(id.id(), len, id.rtr()), &data).unwrap()) } 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 @@ -915,7 +915,7 @@ fn receive_fifo(canregs: crate::pac::can::Can, fifo_nr: usize) -> nb::Result<Fra } // Read the frame. - let id = IdReg(rx.rir().read().0).id(); + let id = IdReg(rx.rir().read().0); let mut data = [0xff; 8]; data[0..4].copy_from_slice(&rx.rdlr().read().0.to_ne_bytes()); data[4..8].copy_from_slice(&rx.rdhr().read().0.to_ne_bytes()); @@ -924,7 +924,7 @@ fn receive_fifo(canregs: crate::pac::can::Can, fifo_nr: usize) -> nb::Result<Fra // Release the mailbox. rfr.write(|w| w.set_rfom(true)); - Ok(Frame::new(Header::new(id, len, false), &data).unwrap()) + Ok(Frame::new(Header::new(id.id(), len, id.rtr()), &data).unwrap()) } /// Identifies one of the two receive FIFOs. From fcfcfce4007768578c3a6a1b744a5efd97f14376 Mon Sep 17 00:00:00 2001 From: Corey Schuhen <cschuhen@gmail.com> Date: Sun, 24 Mar 2024 07:19:00 +1000 Subject: [PATCH 04/14] CAN: Move some FDCAN definitions into a module to share with BXCAN. --- embassy-stm32/src/can/common.rs | 51 +++++++++++++++++++++++++++ embassy-stm32/src/can/fdcan.rs | 62 ++++++--------------------------- 2 files changed, 61 insertions(+), 52 deletions(-) create mode 100644 embassy-stm32/src/can/common.rs diff --git a/embassy-stm32/src/can/common.rs b/embassy-stm32/src/can/common.rs new file mode 100644 index 000000000..1de54e5a1 --- /dev/null +++ b/embassy-stm32/src/can/common.rs @@ -0,0 +1,51 @@ +use embassy_sync::channel::{DynamicReceiver, DynamicSender}; + +use crate::can::_version::frame::*; +use crate::can::_version::Timestamp; +use crate::can::_version::enums::*; + +pub(crate) struct ClassicBufferedRxInner { + pub rx_sender: DynamicSender<'static, Result<(ClassicFrame, Timestamp), BusError>>, +} +pub(crate) struct ClassicBufferedTxInner { + pub tx_receiver: DynamicReceiver<'static, ClassicFrame>, +} + +pub(crate) struct FdBufferedRxInner { + pub rx_sender: DynamicSender<'static, Result<(FdFrame, Timestamp), BusError>>, +} +pub(crate) struct FdBufferedTxInner { + pub tx_receiver: DynamicReceiver<'static, FdFrame>, +} + +/// Sender that can be used for sending CAN frames. +#[derive(Copy, Clone)] +pub struct BufferedCanSender { + pub(crate) tx_buf: embassy_sync::channel::DynamicSender<'static, ClassicFrame>, + pub(crate) waker: fn(), +} + +impl BufferedCanSender { + /// Async write frame to TX buffer. + pub fn try_write(&mut self, frame: ClassicFrame) -> Result<(), embassy_sync::channel::TrySendError<ClassicFrame>> { + self.tx_buf.try_send(frame)?; + (self.waker)(); + Ok(()) + } + + /// Async write frame to TX buffer. + pub async fn write(&mut self, frame: ClassicFrame) { + self.tx_buf.send(frame).await; + (self.waker)(); + } + + /// Allows a poll_fn to poll until the channel is ready to write + pub fn poll_ready_to_send(&self, cx: &mut core::task::Context<'_>) -> core::task::Poll<()> { + self.tx_buf.poll_ready_to_send(cx) + } +} + +/// Receiver that can be used for receiving CAN frames. Note, each CAN frame will only be received by one receiver. +pub type BufferedCanReceiver = + embassy_sync::channel::DynamicReceiver<'static, Result<(ClassicFrame, Timestamp), BusError>>; + diff --git a/embassy-stm32/src/can/fdcan.rs b/embassy-stm32/src/can/fdcan.rs index 4ea036ab4..c42c42853 100644 --- a/embassy-stm32/src/can/fdcan.rs +++ b/embassy-stm32/src/can/fdcan.rs @@ -14,6 +14,7 @@ use crate::interrupt::typelevel::Interrupt; use crate::rcc::RccPeripheral; use crate::{interrupt, peripherals, Peripheral}; +mod common; pub mod enums; pub(crate) mod fd; pub mod frame; @@ -24,6 +25,7 @@ use fd::config::*; use fd::filter::*; pub use fd::{config, filter}; use frame::*; +pub use self::common::{BufferedCanSender, BufferedCanReceiver}; /// Timestamp for incoming packets. Use Embassy time when enabled. #[cfg(feature = "time")] @@ -414,36 +416,6 @@ pub struct BufferedCan<'d, T: Instance, const TX_BUF_SIZE: usize, const RX_BUF_S rx_buf: &'static RxBuf<RX_BUF_SIZE>, } -/// Sender that can be used for sending CAN frames. -#[derive(Copy, Clone)] -pub struct BufferedCanSender { - tx_buf: embassy_sync::channel::DynamicSender<'static, ClassicFrame>, - waker: fn(), -} - -impl BufferedCanSender { - /// Async write frame to TX buffer. - pub fn try_write(&mut self, frame: ClassicFrame) -> Result<(), embassy_sync::channel::TrySendError<ClassicFrame>> { - self.tx_buf.try_send(frame)?; - (self.waker)(); - Ok(()) - } - - /// Async write frame to TX buffer. - pub async fn write(&mut self, frame: ClassicFrame) { - self.tx_buf.send(frame).await; - (self.waker)(); - } - - /// Allows a poll_fn to poll until the channel is ready to write - pub fn poll_ready_to_send(&self, cx: &mut core::task::Context<'_>) -> core::task::Poll<()> { - self.tx_buf.poll_ready_to_send(cx) - } -} - -/// Receiver that can be used for receiving CAN frames. Note, each CAN frame will only be received by one receiver. -pub type BufferedCanReceiver = - embassy_sync::channel::DynamicReceiver<'static, Result<(ClassicFrame, Timestamp), BusError>>; impl<'c, 'd, T: Instance, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize> BufferedCan<'d, T, TX_BUF_SIZE, RX_BUF_SIZE> @@ -468,10 +440,10 @@ impl<'c, 'd, T: Instance, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize> fn setup(self) -> Self { // We don't want interrupts being processed while we change modes. critical_section::with(|_| unsafe { - let rx_inner = ClassicBufferedRxInner { + let rx_inner = self::common::ClassicBufferedRxInner { rx_sender: self.rx_buf.sender().into(), }; - let tx_inner = ClassicBufferedTxInner { + let tx_inner = self::common::ClassicBufferedTxInner { tx_receiver: self.tx_buf.receiver().into(), }; T::mut_state().rx_mode = RxMode::ClassicBuffered(rx_inner); @@ -586,10 +558,10 @@ impl<'c, 'd, T: Instance, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize> fn setup(self) -> Self { // We don't want interrupts being processed while we change modes. critical_section::with(|_| unsafe { - let rx_inner = FdBufferedRxInner { + let rx_inner = self::common::FdBufferedRxInner { rx_sender: self.rx_buf.sender().into(), }; - let tx_inner = FdBufferedTxInner { + let tx_inner = self::common::FdBufferedTxInner { tx_receiver: self.tx_buf.receiver().into(), }; T::mut_state().rx_mode = RxMode::FdBuffered(rx_inner); @@ -678,24 +650,10 @@ impl<'c, 'd, T: Instance> FdcanRx<'d, T> { } } -struct ClassicBufferedRxInner { - rx_sender: DynamicSender<'static, Result<(ClassicFrame, Timestamp), BusError>>, -} -struct ClassicBufferedTxInner { - tx_receiver: DynamicReceiver<'static, ClassicFrame>, -} - -struct FdBufferedRxInner { - rx_sender: DynamicSender<'static, Result<(FdFrame, Timestamp), BusError>>, -} -struct FdBufferedTxInner { - tx_receiver: DynamicReceiver<'static, FdFrame>, -} - enum RxMode { NonBuffered(AtomicWaker), - ClassicBuffered(ClassicBufferedRxInner), - FdBuffered(FdBufferedRxInner), + ClassicBuffered(self::common::ClassicBufferedRxInner), + FdBuffered(self::common::FdBufferedRxInner), } impl RxMode { @@ -765,8 +723,8 @@ impl RxMode { enum TxMode { NonBuffered(AtomicWaker), - ClassicBuffered(ClassicBufferedTxInner), - FdBuffered(FdBufferedTxInner), + ClassicBuffered(self::common::ClassicBufferedTxInner), + FdBuffered(self::common::FdBufferedTxInner), } impl TxMode { From 32065d7719e8dd2f5da7787d4b7edf3109c632ba Mon Sep 17 00:00:00 2001 From: Corey Schuhen <cschuhen@gmail.com> Date: Thu, 14 Mar 2024 21:13:19 +1000 Subject: [PATCH 05/14] BXCAN: Cut out more that wasn't required from BXCAN crate. --- embassy-stm32/src/can/bx/mod.rs | 123 +++----------------------------- embassy-stm32/src/can/bxcan.rs | 7 +- 2 files changed, 11 insertions(+), 119 deletions(-) diff --git a/embassy-stm32/src/can/bx/mod.rs b/embassy-stm32/src/can/bx/mod.rs index 121da1bb2..5ea0471c9 100644 --- a/embassy-stm32/src/can/bx/mod.rs +++ b/embassy-stm32/src/can/bx/mod.rs @@ -602,54 +602,14 @@ where unsafe { Tx::<I>::conjure(self.canregs).abort(mailbox) } } - /// Returns a received frame if available. - /// - /// This will first check FIFO 0 for a message or error. If none are available, FIFO 1 is - /// checked. - /// - /// Returns `Err` when a frame was lost due to buffer overrun. - pub fn receive(&mut self) -> nb::Result<Frame, OverrunError> { - // Safety: We have a `&mut self` and have unique access to the peripheral. - let mut rx0 = unsafe { Rx0::<I>::conjure(self.canregs) }; - let mut rx1 = unsafe { Rx1::<I>::conjure(self.canregs) }; - match rx0.receive() { - Err(nb::Error::WouldBlock) => rx1.receive(), - result => result, - } - } - - /// Returns a reference to the RX FIFO 0. - pub fn rx0(&mut self) -> Rx0<I> { - // Safety: We take `&mut self` and the return value lifetimes are tied to `self`'s lifetime. - unsafe { Rx0::conjure(self.canregs) } - } - - /// Returns a reference to the RX FIFO 1. - pub fn rx1(&mut self) -> Rx1<I> { - // Safety: We take `&mut self` and the return value lifetimes are tied to `self`'s lifetime. - unsafe { Rx1::conjure(self.canregs) } - } - - pub(crate) fn split_by_ref(&mut self) -> (Tx<I>, Rx0<I>, Rx1<I>) { + pub(crate) fn split_by_ref(&mut self) -> (Tx<I>, Rx<I>) { // Safety: We take `&mut self` and the return value lifetimes are tied to `self`'s lifetime. let tx = unsafe { Tx::conjure(self.canregs) }; - let rx0 = unsafe { Rx0::conjure(self.canregs) }; - let rx1 = unsafe { Rx1::conjure(self.canregs) }; - (tx, rx0, rx1) + let rx0 = unsafe { Rx::conjure() }; + (tx, rx0) } - /// Consumes this `Can` instance and splits it into transmitting and receiving halves. - pub fn split(self) -> (Tx<I>, Rx0<I>, Rx1<I>) { - // Safety: `Self` is not `Copy` and is destroyed by moving it into this method. - unsafe { - ( - Tx::conjure(self.canregs), - Rx0::conjure(self.canregs), - Rx1::conjure(self.canregs), - ) - } - } } impl<I: FilterOwner> Can<I> { @@ -662,7 +622,7 @@ impl<I: FilterOwner> Can<I> { } } -/// Interface to the CAN transmitter part. +/// Marker for Tx half pub struct Tx<I> { _can: PhantomData<I>, canregs: crate::pac::can::Can, @@ -844,87 +804,20 @@ where } } -/// Interface to receiver FIFO 0. -pub struct Rx0<I> { +/// Marker for Rx half +pub struct Rx<I> { _can: PhantomData<I>, - canregs: crate::pac::can::Can, } -impl<I> Rx0<I> +impl<I> Rx<I> where I: Instance, { - unsafe fn conjure(canregs: crate::pac::can::Can) -> Self { + unsafe fn conjure() -> Self { Self { _can: PhantomData, - canregs, } } - - /// Returns a received frame if available. - /// - /// Returns `Err` when a frame was lost due to buffer overrun. - pub fn receive(&mut self) -> nb::Result<Frame, OverrunError> { - receive_fifo(self.canregs, 0) - } -} - -/// Interface to receiver FIFO 1. -pub struct Rx1<I> { - _can: PhantomData<I>, - canregs: crate::pac::can::Can, -} - -impl<I> Rx1<I> -where - I: Instance, -{ - unsafe fn conjure(canregs: crate::pac::can::Can) -> Self { - Self { - _can: PhantomData, - canregs, - } - } - - /// Returns a received frame if available. - /// - /// Returns `Err` when a frame was lost due to buffer overrun. - pub fn receive(&mut self) -> nb::Result<Frame, OverrunError> { - receive_fifo(self.canregs, 1) - } -} - -fn receive_fifo(canregs: crate::pac::can::Can, fifo_nr: usize) -> nb::Result<Frame, OverrunError> { - assert!(fifo_nr < 2); - let rfr = canregs.rfr(fifo_nr); - let rx = canregs.rx(fifo_nr); - - //let rfr = &can.rfr[fifo_nr]; - //let rx = &can.rx[fifo_nr]; - - // Check if a frame is available in the mailbox. - let rfr_read = rfr.read(); - if rfr_read.fmp() == 0 { - return Err(nb::Error::WouldBlock); - } - - // Check for RX FIFO overrun. - if rfr_read.fovr() { - rfr.write(|w| w.set_fovr(true)); - return Err(nb::Error::Other(OverrunError { _priv: () })); - } - - // Read the frame. - let id = IdReg(rx.rir().read().0); - let mut data = [0xff; 8]; - data[0..4].copy_from_slice(&rx.rdlr().read().0.to_ne_bytes()); - data[4..8].copy_from_slice(&rx.rdhr().read().0.to_ne_bytes()); - let len = rx.rdtr().read().dlc(); - - // Release the mailbox. - rfr.write(|w| w.set_rfom(true)); - - Ok(Frame::new(Header::new(id.id(), len, id.rtr()), &data).unwrap()) } /// Identifies one of the two receive FIFOs. diff --git a/embassy-stm32/src/can/bxcan.rs b/embassy-stm32/src/can/bxcan.rs index 017c5110d..9d2b8797e 100644 --- a/embassy-stm32/src/can/bxcan.rs +++ b/embassy-stm32/src/can/bxcan.rs @@ -296,8 +296,8 @@ impl<'d, T: Instance> Can<'d, T> { /// /// Useful for doing separate transmit/receive tasks. pub fn split<'c>(&'c mut self) -> (CanTx<'d, T>, CanRx<'d, T>) { - let (tx, rx0, rx1) = self.can.split_by_ref(); - (CanTx { tx }, CanRx { rx0, rx1 }) + let (tx, rx) = self.can.split_by_ref(); + (CanTx { tx }, CanRx { rx}) } } @@ -401,8 +401,7 @@ impl<'d, T: Instance> CanTx<'d, T> { /// CAN driver, receive half. #[allow(dead_code)] pub struct CanRx<'d, T: Instance> { - rx0: crate::can::bx::Rx0<BxcanInstance<'d, T>>, - rx1: crate::can::bx::Rx1<BxcanInstance<'d, T>>, + rx: crate::can::bx::Rx<BxcanInstance<'d, T>>, } impl<'d, T: Instance> CanRx<'d, T> { From 3bdaad39e8955fe52e55c65a834dfc42dc54d676 Mon Sep 17 00:00:00 2001 From: Corey Schuhen <cschuhen@gmail.com> Date: Sun, 24 Mar 2024 07:20:33 +1000 Subject: [PATCH 06/14] BXCAN: Register access into new Registers struct. --- embassy-stm32/src/can/bx/mod.rs | 753 +++++++++++++++++++------------- embassy-stm32/src/can/bxcan.rs | 76 +--- embassy-stm32/src/can/common.rs | 12 +- embassy-stm32/src/can/fdcan.rs | 4 +- 4 files changed, 465 insertions(+), 380 deletions(-) diff --git a/embassy-stm32/src/can/bx/mod.rs b/embassy-stm32/src/can/bx/mod.rs index 5ea0471c9..9b6ebf5a4 100644 --- a/embassy-stm32/src/can/bx/mod.rs +++ b/embassy-stm32/src/can/bx/mod.rs @@ -43,7 +43,35 @@ pub type Data = crate::can::frame::ClassicData; /// CAN Frame pub type Frame = crate::can::frame::ClassicFrame; +use crate::can::_version::Envelope; use crate::can::bx::filter::MasterFilters; +use crate::can::enums::BusError; +use crate::pac::can::vals::Lec; + +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +pub(crate) enum RxFifo { + Fifo0, + Fifo1, +} + +trait IntoBusError { + fn into_bus_err(self) -> Option<BusError>; +} + +impl IntoBusError for Lec { + fn into_bus_err(self) -> Option<BusError> { + match self { + Lec::STUFF => Some(BusError::Stuff), + Lec::FORM => Some(BusError::Form), + Lec::ACK => Some(BusError::Acknowledge), + Lec::BITRECESSIVE => Some(BusError::BitRecessive), + Lec::BITDOMINANT => Some(BusError::BitDominant), + Lec::CRC => Some(BusError::Crc), + Lec::CUSTOM => Some(BusError::Software), + _ => None, + } + } +} /// A bxCAN peripheral instance. /// @@ -233,229 +261,36 @@ impl PartialOrd for IdReg { } } -/// Configuration proxy returned by [`Can::modify_config`]. -#[must_use = "`CanConfig` leaves the peripheral in uninitialized state, call `CanConfig::enable` or explicitly drop the value"] -pub struct CanConfig<'a, I: Instance> { - can: &'a mut Can<I>, +pub(crate) struct Registers { + pub canregs: crate::pac::can::Can, } -impl<I: Instance> CanConfig<'_, I> { - /// Configures the bit timings. - /// - /// You can use <http://www.bittiming.can-wiki.info/> to calculate the `btr` parameter. Enter - /// parameters as follows: - /// - /// - *Clock Rate*: The input clock speed to the CAN peripheral (*not* the CPU clock speed). - /// This is the clock rate of the peripheral bus the CAN peripheral is attached to (eg. APB1). - /// - *Sample Point*: Should normally be left at the default value of 87.5%. - /// - *SJW*: Should normally be left at the default value of 1. - /// - /// Then copy the `CAN_BUS_TIME` register value from the table and pass it as the `btr` - /// parameter to this method. - pub fn set_bit_timing(self, bt: crate::can::util::NominalBitTiming) -> Self { - self.can.set_bit_timing(bt); - self - } - - /// Enables or disables loopback mode: Internally connects the TX and RX - /// signals together. - pub fn set_loopback(self, enabled: bool) -> Self { - self.can.canregs.btr().modify(|reg| reg.set_lbkm(enabled)); - self - } - - /// Enables or disables silent mode: Disconnects the TX signal from the pin. - pub fn set_silent(self, enabled: bool) -> Self { - let mode = match enabled { - false => stm32_metapac::can::vals::Silm::NORMAL, - true => stm32_metapac::can::vals::Silm::SILENT, - }; - self.can.canregs.btr().modify(|reg| reg.set_silm(mode)); - self - } - - /// Enables or disables automatic retransmission of messages. - /// - /// If this is enabled, the CAN peripheral will automatically try to retransmit each frame - /// until it can be sent. Otherwise, it will try only once to send each frame. - /// - /// Automatic retransmission is enabled by default. - pub fn set_automatic_retransmit(self, enabled: bool) -> Self { - self.can.canregs.mcr().modify(|reg| reg.set_nart(enabled)); - self - } - - /// Leaves initialization mode and enables the peripheral. - /// - /// To sync with the CAN bus, this will block until 11 consecutive recessive bits are detected - /// on the bus. - /// - /// If you want to finish configuration without enabling the peripheral, you can call - /// [`CanConfig::leave_disabled`] or [`drop`] the [`CanConfig`] instead. - pub fn enable(mut self) { - self.leave_init_mode(); - - match nb::block!(self.can.enable_non_blocking()) { - Ok(()) => {} - Err(void) => match void {}, - } - - // Don't run the destructor. - mem::forget(self); - } - - /// Leaves initialization mode, but keeps the peripheral in sleep mode. - /// - /// Before the [`Can`] instance can be used, you have to enable it by calling - /// [`Can::enable_non_blocking`]. - pub fn leave_disabled(mut self) { - self.leave_init_mode(); - } - - /// Leaves initialization mode, enters sleep mode. - fn leave_init_mode(&mut self) { - self.can.canregs.mcr().modify(|reg| { - reg.set_sleep(true); - reg.set_inrq(false); - }); - loop { - let msr = self.can.canregs.msr().read(); - if msr.slak() && !msr.inak() { - break; - } - } - } -} - -impl<I: Instance> Drop for CanConfig<'_, I> { - #[inline] - fn drop(&mut self) { - self.leave_init_mode(); - } -} - -/// Builder returned by [`Can::builder`]. -#[must_use = "`CanBuilder` leaves the peripheral in uninitialized state, call `CanBuilder::enable` or `CanBuilder::leave_disabled`"] -pub struct CanBuilder<I: Instance> { - can: Can<I>, -} - -impl<I: Instance> CanBuilder<I> { - /// Configures the bit timings. - /// - /// You can use <http://www.bittiming.can-wiki.info/> to calculate the `btr` parameter. Enter - /// parameters as follows: - /// - /// - *Clock Rate*: The input clock speed to the CAN peripheral (*not* the CPU clock speed). - /// This is the clock rate of the peripheral bus the CAN peripheral is attached to (eg. APB1). - /// - *Sample Point*: Should normally be left at the default value of 87.5%. - /// - *SJW*: Should normally be left at the default value of 1. - /// - /// Then copy the `CAN_BUS_TIME` register value from the table and pass it as the `btr` - /// parameter to this method. - pub fn set_bit_timing(mut self, bt: crate::can::util::NominalBitTiming) -> Self { - self.can.set_bit_timing(bt); - self - } - /// Enables or disables loopback mode: Internally connects the TX and RX - /// signals together. - pub fn set_loopback(self, enabled: bool) -> Self { - self.can.canregs.btr().modify(|reg| reg.set_lbkm(enabled)); - self - } - - /// Enables or disables silent mode: Disconnects the TX signal from the pin. - pub fn set_silent(self, enabled: bool) -> Self { - let mode = match enabled { - false => stm32_metapac::can::vals::Silm::NORMAL, - true => stm32_metapac::can::vals::Silm::SILENT, - }; - self.can.canregs.btr().modify(|reg| reg.set_silm(mode)); - self - } - - /// Enables or disables automatic retransmission of messages. - /// - /// If this is enabled, the CAN peripheral will automatically try to retransmit each frame - /// until it can be sent. Otherwise, it will try only once to send each frame. - /// - /// Automatic retransmission is enabled by default. - pub fn set_automatic_retransmit(self, enabled: bool) -> Self { - self.can.canregs.mcr().modify(|reg| reg.set_nart(enabled)); - self - } - - /// Leaves initialization mode and enables the peripheral. - /// - /// To sync with the CAN bus, this will block until 11 consecutive recessive bits are detected - /// on the bus. - /// - /// If you want to finish configuration without enabling the peripheral, you can call - /// [`CanBuilder::leave_disabled`] instead. - pub fn enable(mut self) -> Can<I> { - self.leave_init_mode(); - - match nb::block!(self.can.enable_non_blocking()) { - Ok(()) => self.can, - Err(void) => match void {}, - } - } - - /// Returns the [`Can`] interface without enabling it. - /// - /// This leaves initialization mode, but keeps the peripheral in sleep mode instead of enabling - /// it. - /// - /// Before the [`Can`] instance can be used, you have to enable it by calling - /// [`Can::enable_non_blocking`]. - pub fn leave_disabled(mut self) -> Can<I> { - self.leave_init_mode(); - self.can - } - - /// Leaves initialization mode, enters sleep mode. - fn leave_init_mode(&mut self) { - self.can.canregs.mcr().modify(|reg| { - reg.set_sleep(true); - reg.set_inrq(false); - }); - loop { - let msr = self.can.canregs.msr().read(); - if msr.slak() && !msr.inak() { - break; - } - } - } -} - -/// Interface to a bxCAN peripheral. -pub struct Can<I: Instance> { - instance: I, - canregs: crate::pac::can::Can, -} - -impl<I> Can<I> -where - I: Instance, -{ - /// Creates a [`CanBuilder`] for constructing a CAN interface. - pub fn builder(instance: I, canregs: crate::pac::can::Can) -> CanBuilder<I> { - let can_builder = CanBuilder { - can: Can { instance, canregs }, - }; - - canregs.mcr().modify(|reg| { +impl Registers { + fn enter_init_mode(&mut self) { + self.canregs.mcr().modify(|reg| { reg.set_sleep(false); reg.set_inrq(true); }); loop { - let msr = canregs.msr().read(); + let msr = self.canregs.msr().read(); if !msr.slak() && msr.inak() { break; } } + } - can_builder + // Leaves initialization mode, enters sleep mode. + fn leave_init_mode(&mut self) { + self.canregs.mcr().modify(|reg| { + reg.set_sleep(true); + reg.set_inrq(false); + }); + loop { + let msr = self.canregs.msr().read(); + if msr.slak() && !msr.inak() { + break; + } + } } fn set_bit_timing(&mut self, bt: crate::can::util::NominalBitTiming) { @@ -471,38 +306,29 @@ where }); } - /// Returns a reference to the peripheral instance. - /// - /// This allows accessing HAL-specific data stored in the instance type. - pub fn instance(&mut self) -> &mut I { - &mut self.instance + /// Enables or disables silent mode: Disconnects the TX signal from the pin. + pub fn set_silent(&self, enabled: bool) { + let mode = match enabled { + false => stm32_metapac::can::vals::Silm::NORMAL, + true => stm32_metapac::can::vals::Silm::SILENT, + }; + self.canregs.btr().modify(|reg| reg.set_silm(mode)); } - /// Disables the CAN interface and returns back the raw peripheral it was created from. + /// Enables or disables automatic retransmission of messages. /// - /// The peripheral is disabled by setting `RESET` in `CAN_MCR`, which causes the peripheral to - /// enter sleep mode. - pub fn free(self) -> I { - self.canregs.mcr().write(|reg| reg.set_reset(true)); - self.instance + /// If this is enabled, the CAN peripheral will automatically try to retransmit each frame + /// until it can be sent. Otherwise, it will try only once to send each frame. + /// + /// Automatic retransmission is enabled by default. + pub fn set_automatic_retransmit(&self, enabled: bool) { + self.canregs.mcr().modify(|reg| reg.set_nart(enabled)); } - /// Configure bit timings and silent/loop-back mode. - /// - /// Calling this method will enter initialization mode. - pub fn modify_config(&mut self) -> CanConfig<'_, I> { - self.canregs.mcr().modify(|reg| { - reg.set_sleep(false); - reg.set_inrq(true); - }); - loop { - let msr = self.canregs.msr().read(); - if !msr.slak() && msr.inak() { - break; - } - } - - CanConfig { can: self } + /// Enables or disables loopback mode: Internally connects the TX and RX + /// signals together. + pub fn set_loopback(&self, enabled: bool) { + self.canregs.btr().modify(|reg| reg.set_lbkm(enabled)); } /// Configures the automatic wake-up feature. @@ -512,6 +338,7 @@ where /// When turned on, an incoming frame will cause the peripheral to wake up from sleep and /// receive the frame. If enabled, [`Interrupt::Wakeup`] will also be triggered by the incoming /// frame. + #[allow(dead_code)] pub fn set_automatic_wakeup(&mut self, enabled: bool) { self.canregs.mcr().modify(|reg| reg.set_awum(enabled)); } @@ -540,6 +367,7 @@ where /// Puts the peripheral in a sleep mode to save power. /// /// While in sleep mode, an incoming CAN frame will trigger [`Interrupt::Wakeup`] if enabled. + #[allow(dead_code)] pub fn sleep(&mut self) { self.canregs.mcr().modify(|reg| { reg.set_sleep(true); @@ -553,10 +381,19 @@ where } } + /// Disables the CAN interface. + /// + /// The peripheral is disabled by setting `RESET` in `CAN_MCR`, which causes the peripheral to + /// enter sleep mode. + pub fn reset(&self) { + self.canregs.mcr().write(|reg| reg.set_reset(true)); + } + /// Wakes up from sleep mode. /// /// Note that this will not trigger [`Interrupt::Wakeup`], only reception of an incoming CAN /// frame will cause that interrupt. + #[allow(dead_code)] pub fn wakeup(&mut self) { self.canregs.mcr().modify(|reg| { reg.set_sleep(false); @@ -569,74 +406,19 @@ 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 (see [`FramePriority`]). - /// Transmit order is preserved for frames with identical priority. - /// - /// If all transmit mailboxes are full, and `frame` has a higher priority than the - /// lowest-priority message in the transmit mailboxes, transmission of the enqueued frame is - /// cancelled and `frame` is enqueued instead. The frame that was replaced is returned as - /// [`TransmitStatus::dequeued_frame`]. - pub fn transmit(&mut self, frame: &Frame) -> nb::Result<TransmitStatus, Infallible> { - // Safety: We have a `&mut self` and have unique access to the peripheral. - unsafe { Tx::<I>::conjure(self.canregs).transmit(frame) } - } - - /// Returns `true` if no frame is pending for transmission. - pub fn is_transmitter_idle(&self) -> bool { - // Safety: Read-only operation. - unsafe { Tx::<I>::conjure(self.canregs).is_idle() } - } - - /// Attempts to abort the sending of a frame that is pending in a mailbox. - /// - /// If there is no frame in the provided mailbox, or its transmission succeeds before it can be - /// aborted, this function has no effect and returns `false`. - /// - /// If there is a frame in the provided mailbox, and it is canceled successfully, this function - /// returns `true`. - pub fn abort(&mut self, mailbox: Mailbox) -> bool { - // Safety: We have a `&mut self` and have unique access to the peripheral. - unsafe { Tx::<I>::conjure(self.canregs).abort(mailbox) } - } - - - pub(crate) fn split_by_ref(&mut self) -> (Tx<I>, Rx<I>) { - // Safety: We take `&mut self` and the return value lifetimes are tied to `self`'s lifetime. - let tx = unsafe { Tx::conjure(self.canregs) }; - let rx0 = unsafe { Rx::conjure() }; - (tx, rx0) - } - -} - -impl<I: FilterOwner> Can<I> { - /// Accesses the filter banks owned by this CAN peripheral. - /// - /// To modify filters of a slave peripheral, `modify_filters` has to be called on the master - /// peripheral instead. - pub fn modify_filters(&mut self) -> MasterFilters<'_, I> { - unsafe { MasterFilters::new(self.canregs) } - } -} - -/// Marker for Tx half -pub struct Tx<I> { - _can: PhantomData<I>, - canregs: crate::pac::can::Can, -} - -impl<I> Tx<I> -where - I: Instance, -{ - unsafe fn conjure(canregs: crate::pac::can::Can) -> Self { - Self { - _can: PhantomData, - canregs, + + pub fn curr_error(&self) -> Option<BusError> { + let err = { self.canregs.esr().read() }; + if err.boff() { + return Some(BusError::BusOff); + } else if err.epvf() { + return Some(BusError::BusPassive); + } else if err.ewgf() { + return Some(BusError::BusWarning); + } else if let Some(err) = err.lec().into_bus_err() { + return Some(err); } + None } /// Puts a CAN frame in a transmit mailbox for transmission on the bus. @@ -802,6 +584,361 @@ where reg.set_rqcp(2, true); }); } + + pub fn receive_fifo(&self, fifo: crate::can::_version::bx::RxFifo) -> Option<Envelope> { + // Generate timestamp as early as possible + #[cfg(feature = "time")] + let ts = embassy_time::Instant::now(); + + use crate::pac::can::vals::Ide; + + let fifo_idx = match fifo { + crate::can::_version::bx::RxFifo::Fifo0 => 0usize, + crate::can::_version::bx::RxFifo::Fifo1 => 1usize, + }; + let rfr = self.canregs.rfr(fifo_idx); + let fifo = self.canregs.rx(fifo_idx); + + // If there are no pending messages, there is nothing to do + if rfr.read().fmp() == 0 { + return None; + } + + let rir = fifo.rir().read(); + let id: embedded_can::Id = if rir.ide() == Ide::STANDARD { + embedded_can::StandardId::new(rir.stid()).unwrap().into() + } else { + let stid = (rir.stid() & 0x7FF) as u32; + let exid = rir.exid() & 0x3FFFF; + let id = (stid << 18) | (exid); + embedded_can::ExtendedId::new(id).unwrap().into() + }; + let data_len = fifo.rdtr().read().dlc(); + let mut data: [u8; 8] = [0; 8]; + data[0..4].copy_from_slice(&fifo.rdlr().read().0.to_ne_bytes()); + data[4..8].copy_from_slice(&fifo.rdhr().read().0.to_ne_bytes()); + + let frame = Frame::new(Header::new(id, data_len, false), &data).unwrap(); + let envelope = Envelope { + #[cfg(feature = "time")] + ts, + frame, + }; + + rfr.modify(|v| v.set_rfom(true)); + + Some(envelope) + } +} + +/// Configuration proxy returned by [`Can::modify_config`]. +#[must_use = "`CanConfig` leaves the peripheral in uninitialized state, call `CanConfig::enable` or explicitly drop the value"] +pub struct CanConfig<'a, I: Instance> { + can: &'a mut Can<I>, +} + +impl<I: Instance> CanConfig<'_, I> { + /// Configures the bit timings. + /// + /// You can use <http://www.bittiming.can-wiki.info/> to calculate the `btr` parameter. Enter + /// parameters as follows: + /// + /// - *Clock Rate*: The input clock speed to the CAN peripheral (*not* the CPU clock speed). + /// This is the clock rate of the peripheral bus the CAN peripheral is attached to (eg. APB1). + /// - *Sample Point*: Should normally be left at the default value of 87.5%. + /// - *SJW*: Should normally be left at the default value of 1. + /// + /// Then copy the `CAN_BUS_TIME` register value from the table and pass it as the `btr` + /// parameter to this method. + pub fn set_bit_timing(self, bt: crate::can::util::NominalBitTiming) -> Self { + self.can.registers.set_bit_timing(bt); + self + } + + /// Enables or disables loopback mode: Internally connects the TX and RX + /// signals together. + pub fn set_loopback(self, enabled: bool) -> Self { + self.can.registers.set_loopback(enabled); + self + } + + /// Enables or disables silent mode: Disconnects the TX signal from the pin. + pub fn set_silent(self, enabled: bool) -> Self { + self.can.registers.set_silent(enabled); + self + } + + /// Enables or disables automatic retransmission of messages. + /// + /// If this is enabled, the CAN peripheral will automatically try to retransmit each frame + /// until it can be sent. Otherwise, it will try only once to send each frame. + /// + /// Automatic retransmission is enabled by default. + pub fn set_automatic_retransmit(self, enabled: bool) -> Self { + self.can.registers.set_automatic_retransmit(enabled); + self + } + + /// Leaves initialization mode and enables the peripheral. + /// + /// To sync with the CAN bus, this will block until 11 consecutive recessive bits are detected + /// on the bus. + /// + /// If you want to finish configuration without enabling the peripheral, you can call + /// [`CanConfig::leave_disabled`] or [`drop`] the [`CanConfig`] instead. + pub fn enable(self) { + self.can.registers.leave_init_mode(); + + match nb::block!(self.can.registers.enable_non_blocking()) { + Ok(()) => {} + Err(void) => match void {}, + } + + // Don't run the destructor. + mem::forget(self); + } + + /// Leaves initialization mode, but keeps the peripheral in sleep mode. + /// + /// Before the [`Can`] instance can be used, you have to enable it by calling + /// [`Can::enable_non_blocking`]. + pub fn leave_disabled(self) { + self.can.registers.leave_init_mode(); + } +} + +impl<I: Instance> Drop for CanConfig<'_, I> { + #[inline] + fn drop(&mut self) { + self.can.registers.leave_init_mode(); + } +} + +/// Builder returned by [`Can::builder`]. +#[must_use = "`CanBuilder` leaves the peripheral in uninitialized state, call `CanBuilder::enable` or `CanBuilder::leave_disabled`"] +pub struct CanBuilder<I: Instance> { + can: Can<I>, +} + +impl<I: Instance> CanBuilder<I> { + /// Configures the bit timings. + /// + /// You can use <http://www.bittiming.can-wiki.info/> to calculate the `btr` parameter. Enter + /// parameters as follows: + /// + /// - *Clock Rate*: The input clock speed to the CAN peripheral (*not* the CPU clock speed). + /// This is the clock rate of the peripheral bus the CAN peripheral is attached to (eg. APB1). + /// - *Sample Point*: Should normally be left at the default value of 87.5%. + /// - *SJW*: Should normally be left at the default value of 1. + /// + /// Then copy the `CAN_BUS_TIME` register value from the table and pass it as the `btr` + /// parameter to this method. + pub fn set_bit_timing(mut self, bt: crate::can::util::NominalBitTiming) -> Self { + self.can.registers.set_bit_timing(bt); + self + } + /// Enables or disables loopback mode: Internally connects the TX and RX + /// signals together. + pub fn set_loopback(self, enabled: bool) -> Self { + self.can.registers.set_loopback(enabled); + self + } + + /// Enables or disables silent mode: Disconnects the TX signal from the pin. + pub fn set_silent(self, enabled: bool) -> Self { + self.can.registers.set_silent(enabled); + self + } + + /// Enables or disables automatic retransmission of messages. + /// + /// If this is enabled, the CAN peripheral will automatically try to retransmit each frame + /// until it can be sent. Otherwise, it will try only once to send each frame. + /// + /// Automatic retransmission is enabled by default. + pub fn set_automatic_retransmit(self, enabled: bool) -> Self { + self.can.registers.set_automatic_retransmit(enabled); + self + } + + /// Leaves initialization mode and enables the peripheral. + /// + /// To sync with the CAN bus, this will block until 11 consecutive recessive bits are detected + /// on the bus. + /// + /// If you want to finish configuration without enabling the peripheral, you can call + /// [`CanBuilder::leave_disabled`] instead. + pub fn enable(mut self) -> Can<I> { + self.leave_init_mode(); + + match nb::block!(self.can.registers.enable_non_blocking()) { + Ok(()) => self.can, + Err(void) => match void {}, + } + } + + /// Returns the [`Can`] interface without enabling it. + /// + /// This leaves initialization mode, but keeps the peripheral in sleep mode instead of enabling + /// it. + /// + /// Before the [`Can`] instance can be used, you have to enable it by calling + /// [`Can::enable_non_blocking`]. + pub fn leave_disabled(mut self) -> Can<I> { + self.leave_init_mode(); + self.can + } + + /// Leaves initialization mode, enters sleep mode. + fn leave_init_mode(&mut self) { + self.can.registers.leave_init_mode(); + } +} + +/// Interface to a bxCAN peripheral. +pub struct Can<I: Instance> { + instance: I, + canregs: crate::pac::can::Can, + pub(crate) registers: Registers, +} + +impl<I> Can<I> +where + I: Instance, +{ + /// Creates a [`CanBuilder`] for constructing a CAN interface. + pub fn builder(instance: I, canregs: crate::pac::can::Can) -> CanBuilder<I> { + let mut can_builder = CanBuilder { + can: Can { + instance, + canregs, + registers: Registers { canregs }, + }, + }; + + can_builder.can.registers.enter_init_mode(); + + can_builder + } + + /// Disables the CAN interface and returns back the raw peripheral it was created from. + /// + /// The peripheral is disabled by setting `RESET` in `CAN_MCR`, which causes the peripheral to + /// enter sleep mode. + pub fn free(self) -> I { + self.registers.reset(); + self.instance + } + + /// Configure bit timings and silent/loop-back mode. + /// + /// Calling this method will enter initialization mode. + pub fn modify_config(&mut self) -> CanConfig<'_, I> { + self.registers.enter_init_mode(); + + CanConfig { can: self } + } + + /// Puts a CAN frame in a free transmit mailbox for transmission on the bus. + /// + /// Frames are transmitted to the bus based on their priority (see [`FramePriority`]). + /// Transmit order is preserved for frames with identical priority. + /// + /// If all transmit mailboxes are full, and `frame` has a higher priority than the + /// lowest-priority message in the transmit mailboxes, transmission of the enqueued frame is + /// cancelled and `frame` is enqueued instead. The frame that was replaced is returned as + /// [`TransmitStatus::dequeued_frame`]. + pub fn transmit(&mut self, frame: &Frame) -> nb::Result<TransmitStatus, Infallible> { + // Safety: We have a `&mut self` and have unique access to the peripheral. + unsafe { Tx::<I>::conjure(self.canregs).transmit(frame) } + } + + /// Returns `true` if no frame is pending for transmission. + pub fn is_transmitter_idle(&self) -> bool { + // Safety: Read-only operation. + unsafe { Tx::<I>::conjure(self.canregs).is_idle() } + } + + /// Attempts to abort the sending of a frame that is pending in a mailbox. + /// + /// If there is no frame in the provided mailbox, or its transmission succeeds before it can be + /// aborted, this function has no effect and returns `false`. + /// + /// If there is a frame in the provided mailbox, and it is canceled successfully, this function + /// returns `true`. + pub fn abort(&mut self, mailbox: Mailbox) -> bool { + // Safety: We have a `&mut self` and have unique access to the peripheral. + unsafe { Tx::<I>::conjure(self.canregs).abort(mailbox) } + } + + pub(crate) fn split_by_ref(&mut self) -> (Tx<I>, Rx<I>) { + // Safety: We take `&mut self` and the return value lifetimes are tied to `self`'s lifetime. + let tx = unsafe { Tx::conjure(self.canregs) }; + let rx0 = unsafe { Rx::conjure() }; + (tx, rx0) + } +} + +impl<I: FilterOwner> Can<I> { + /// Accesses the filter banks owned by this CAN peripheral. + /// + /// To modify filters of a slave peripheral, `modify_filters` has to be called on the master + /// peripheral instead. + pub fn modify_filters(&mut self) -> MasterFilters<'_, I> { + unsafe { MasterFilters::new(self.canregs) } + } +} + +/// Marker for Tx half +pub struct Tx<I> { + _can: PhantomData<I>, + pub(crate) registers: Registers, +} + +impl<I> Tx<I> +where + I: Instance, +{ + unsafe fn conjure(canregs: crate::pac::can::Can) -> Self { + Self { + _can: PhantomData, + registers: Registers { canregs }, //canregs, + } + } + + /// Puts a CAN frame in a transmit mailbox for transmission on the bus. + /// + /// Frames are transmitted to the bus based on their priority (see [`FramePriority`]). + /// Transmit order is preserved for frames with identical priority. + /// + /// If all transmit mailboxes are full, and `frame` has a higher priority than the + /// lowest-priority message in the transmit mailboxes, transmission of the enqueued frame is + /// cancelled and `frame` is enqueued instead. The frame that was replaced is returned as + /// [`TransmitStatus::dequeued_frame`]. + pub fn transmit(&mut self, frame: &Frame) -> nb::Result<TransmitStatus, Infallible> { + self.registers.transmit(frame) + } + + /// Attempts to abort the sending of a frame that is pending in a mailbox. + /// + /// If there is no frame in the provided mailbox, or its transmission succeeds before it can be + /// aborted, this function has no effect and returns `false`. + /// + /// If there is a frame in the provided mailbox, and it is canceled successfully, this function + /// returns `true`. + pub fn abort(&mut self, mailbox: Mailbox) -> bool { + self.registers.abort(mailbox) + } + + /// Returns `true` if no frame is pending for transmission. + pub fn is_idle(&self) -> bool { + self.registers.is_idle() + } + + /// Clears the request complete flag for all mailboxes. + pub fn clear_interrupt_flags(&mut self) { + self.registers.clear_interrupt_flags() + } } /// Marker for Rx half @@ -814,9 +951,7 @@ where I: Instance, { unsafe fn conjure() -> Self { - Self { - _can: PhantomData, - } + Self { _can: PhantomData } } } diff --git a/embassy-stm32/src/can/bxcan.rs b/embassy-stm32/src/can/bxcan.rs index 9d2b8797e..d865bbe7d 100644 --- a/embassy-stm32/src/can/bxcan.rs +++ b/embassy-stm32/src/can/bxcan.rs @@ -14,7 +14,6 @@ use futures::FutureExt; use crate::gpio::AFType; use crate::interrupt::typelevel::Interrupt; -use crate::pac::can::vals::{Ide, Lec}; use crate::rcc::RccPeripheral; use crate::{interrupt, peripherals, Peripheral}; @@ -185,7 +184,7 @@ impl<'d, T: Instance> Can<'d, T> { /// This will wait for 11 consecutive recessive bits (bus idle state). /// Contrary to enable method from bxcan library, this will not freeze the executor while waiting. pub async fn enable(&mut self) { - while self.enable_non_blocking().is_err() { + while self.registers.enable_non_blocking().is_err() { // SCE interrupt is only generated for entering sleep mode, but not leaving. // Yield to allow other tasks to execute while can bus is initializing. embassy_futures::yield_now().await; @@ -243,52 +242,17 @@ impl<'d, T: Instance> Can<'d, T> { } unsafe fn receive_fifo(fifo: RxFifo) { - // Generate timestamp as early as possible - #[cfg(feature = "time")] - let ts = embassy_time::Instant::now(); - let state = T::state(); - let regs = T::regs(); - let fifo_idx = match fifo { - RxFifo::Fifo0 => 0usize, - RxFifo::Fifo1 => 1usize, - }; - let rfr = regs.rfr(fifo_idx); - let fifo = regs.rx(fifo_idx); + let regsisters = crate::can::bx::Registers { canregs: T::regs() }; loop { - // If there are no pending messages, there is nothing to do - if rfr.read().fmp() == 0 { - return; - } - - let rir = fifo.rir().read(); - let id: embedded_can::Id = if rir.ide() == Ide::STANDARD { - embedded_can::StandardId::new(rir.stid()).unwrap().into() - } else { - let stid = (rir.stid() & 0x7FF) as u32; - let exid = rir.exid() & 0x3FFFF; - let id = (stid << 18) | (exid); - embedded_can::ExtendedId::new(id).unwrap().into() + match regsisters.receive_fifo(fifo) { + Some(envelope) => { + // NOTE: consensus was reached that if rx_queue is full, packets should be dropped + let _ = state.rx_queue.try_send(envelope); + } + None => return, }; - let data_len = fifo.rdtr().read().dlc(); - let mut data: [u8; 8] = [0; 8]; - data[0..4].copy_from_slice(&fifo.rdlr().read().0.to_ne_bytes()); - data[4..8].copy_from_slice(&fifo.rdhr().read().0.to_ne_bytes()); - - let frame = Frame::new(Header::new(id, data_len, false), &data).unwrap(); - let envelope = Envelope { - #[cfg(feature = "time")] - ts, - frame, - }; - - rfr.modify(|v| v.set_rfom(true)); - - /* - NOTE: consensus was reached that if rx_queue is full, packets should be dropped - */ - let _ = state.rx_queue.try_send(envelope); } } @@ -297,7 +261,7 @@ impl<'d, T: Instance> Can<'d, T> { /// Useful for doing separate transmit/receive tasks. pub fn split<'c>(&'c mut self) -> (CanTx<'d, T>, CanRx<'d, T>) { let (tx, rx) = self.can.split_by_ref(); - (CanTx { tx }, CanRx { rx}) + (CanTx { tx }, CanRx { rx }) } } @@ -459,10 +423,7 @@ impl<'d, T: Instance> CanRx<'d, T> { } } -enum RxFifo { - Fifo0, - Fifo1, -} +use crate::can::bx::RxFifo; impl<'d, T: Instance> Drop for Can<'d, T> { fn drop(&mut self) { @@ -601,21 +562,4 @@ impl Index for crate::can::bx::Mailbox { } } -trait IntoBusError { - fn into_bus_err(self) -> Option<BusError>; -} -impl IntoBusError for Lec { - fn into_bus_err(self) -> Option<BusError> { - match self { - Lec::STUFF => Some(BusError::Stuff), - Lec::FORM => Some(BusError::Form), - Lec::ACK => Some(BusError::Acknowledge), - Lec::BITRECESSIVE => Some(BusError::BitRecessive), - Lec::BITDOMINANT => Some(BusError::BitDominant), - Lec::CRC => Some(BusError::Crc), - Lec::CUSTOM => Some(BusError::Software), - _ => None, - } - } -} diff --git a/embassy-stm32/src/can/common.rs b/embassy-stm32/src/can/common.rs index 1de54e5a1..8c9990798 100644 --- a/embassy-stm32/src/can/common.rs +++ b/embassy-stm32/src/can/common.rs @@ -1,8 +1,15 @@ use embassy_sync::channel::{DynamicReceiver, DynamicSender}; -use crate::can::_version::frame::*; -use crate::can::_version::Timestamp; use crate::can::_version::enums::*; +use crate::can::_version::frame::*; + +/// Timestamp for incoming packets. Use Embassy time when enabled. +#[cfg(feature = "time")] +pub type Timestamp = embassy_time::Instant; + +/// Timestamp for incoming packets. +#[cfg(not(feature = "time"))] +pub type Timestamp = u16; pub(crate) struct ClassicBufferedRxInner { pub rx_sender: DynamicSender<'static, Result<(ClassicFrame, Timestamp), BusError>>, @@ -48,4 +55,3 @@ impl BufferedCanSender { /// Receiver that can be used for receiving CAN frames. Note, each CAN frame will only be received by one receiver. pub type BufferedCanReceiver = embassy_sync::channel::DynamicReceiver<'static, Result<(ClassicFrame, Timestamp), BusError>>; - diff --git a/embassy-stm32/src/can/fdcan.rs b/embassy-stm32/src/can/fdcan.rs index c42c42853..42c9bd9f6 100644 --- a/embassy-stm32/src/can/fdcan.rs +++ b/embassy-stm32/src/can/fdcan.rs @@ -25,7 +25,8 @@ use fd::config::*; use fd::filter::*; pub use fd::{config, filter}; use frame::*; -pub use self::common::{BufferedCanSender, BufferedCanReceiver}; + +pub use self::common::{BufferedCanReceiver, BufferedCanSender}; /// Timestamp for incoming packets. Use Embassy time when enabled. #[cfg(feature = "time")] @@ -416,7 +417,6 @@ pub struct BufferedCan<'d, T: Instance, const TX_BUF_SIZE: usize, const RX_BUF_S rx_buf: &'static RxBuf<RX_BUF_SIZE>, } - impl<'c, 'd, T: Instance, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize> BufferedCan<'d, T, TX_BUF_SIZE, RX_BUF_SIZE> { From 26c739c2f93252e9dc476d69f591a84d44491787 Mon Sep 17 00:00:00 2001 From: Corey Schuhen <cschuhen@gmail.com> Date: Sun, 24 Mar 2024 07:27:29 +1000 Subject: [PATCH 07/14] BXCAN: Create RxMode enum and move reader methods into it, laying foundations for different Rx buffering modes. --- embassy-stm32/src/can/bx/mod.rs | 12 +- embassy-stm32/src/can/bxcan.rs | 264 +++++++++++++++++++++++--------- embassy-stm32/src/can/common.rs | 4 + embassy-stm32/src/can/enums.rs | 10 ++ 4 files changed, 218 insertions(+), 72 deletions(-) diff --git a/embassy-stm32/src/can/bx/mod.rs b/embassy-stm32/src/can/bx/mod.rs index 9b6ebf5a4..c508ef2fe 100644 --- a/embassy-stm32/src/can/bx/mod.rs +++ b/embassy-stm32/src/can/bx/mod.rs @@ -406,7 +406,7 @@ impl Registers { } } } - + pub fn curr_error(&self) -> Option<BusError> { let err = { self.canregs.esr().read() }; if err.boff() { @@ -585,6 +585,16 @@ impl Registers { }); } + pub fn receive_frame_available(&self) -> bool { + if self.canregs.rfr(0).read().fmp() != 0 { + true + } else if self.canregs.rfr(1).read().fmp() != 0 { + true + } else { + false + } + } + pub fn receive_fifo(&self, fifo: crate::can::_version::bx::RxFifo) -> Option<Envelope> { // Generate timestamp as early as possible #[cfg(feature = "time")] diff --git a/embassy-stm32/src/can/bxcan.rs b/embassy-stm32/src/can/bxcan.rs index d865bbe7d..66f6e7067 100644 --- a/embassy-stm32/src/can/bxcan.rs +++ b/embassy-stm32/src/can/bxcan.rs @@ -10,7 +10,6 @@ use embassy_hal_internal::{into_ref, PeripheralRef}; use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; use embassy_sync::channel::Channel; use embassy_sync::waitqueue::AtomicWaker; -use futures::FutureExt; use crate::gpio::AFType; use crate::interrupt::typelevel::Interrupt; @@ -22,6 +21,9 @@ use enums::*; pub mod frame; pub mod util; +mod common; +pub use self::common::{BufferedCanReceiver, BufferedCanSender, Timestamp}; + /// Contains CAN frame and additional metadata. /// /// Timestamp is available if `time` feature is enabled. @@ -59,8 +61,7 @@ pub struct Rx0InterruptHandler<T: Instance> { impl<T: Instance> interrupt::typelevel::Handler<T::RX0Interrupt> for Rx0InterruptHandler<T> { unsafe fn on_interrupt() { - // info!("rx0 irq"); - Can::<T>::receive_fifo(RxFifo::Fifo0); + T::state().rx_mode.on_interrupt::<T>(RxFifo::Fifo0); } } @@ -71,8 +72,7 @@ pub struct Rx1InterruptHandler<T: Instance> { impl<T: Instance> interrupt::typelevel::Handler<T::RX1Interrupt> for Rx1InterruptHandler<T> { unsafe fn on_interrupt() { - // info!("rx1 irq"); - Can::<T>::receive_fifo(RxFifo::Fifo1); + T::state().rx_mode.on_interrupt::<T>(RxFifo::Fifo1); } } @@ -99,16 +99,6 @@ pub struct Can<'d, T: Instance> { can: crate::can::bx::Can<BxcanInstance<'d, T>>, } -/// Error returned by `try_read` -#[derive(Debug)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum TryReadError { - /// Bus error - BusError(BusError), - /// Receive buffer is empty - Empty, -} - /// Error returned by `try_write` #[derive(Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] @@ -226,34 +216,19 @@ impl<'d, T: Instance> Can<'d, T> { /// /// Returns a tuple of the time the message was received and the message frame pub async fn read(&mut self) -> Result<Envelope, BusError> { - self.split().1.read().await + T::state().rx_mode.read::<T>().await } /// Attempts to read a CAN frame without blocking. /// /// Returns [Err(TryReadError::Empty)] if there are no frames in the rx queue. pub fn try_read(&mut self) -> Result<Envelope, TryReadError> { - self.split().1.try_read() + T::state().rx_mode.try_read::<T>() } /// Waits while receive queue is empty. pub async fn wait_not_empty(&mut self) { - self.split().1.wait_not_empty().await - } - - unsafe fn receive_fifo(fifo: RxFifo) { - let state = T::state(); - let regsisters = crate::can::bx::Registers { canregs: T::regs() }; - - loop { - match regsisters.receive_fifo(fifo) { - Some(envelope) => { - // NOTE: consensus was reached that if rx_queue is full, packets should be dropped - let _ = state.rx_queue.try_send(envelope); - } - None => return, - }; - } + T::state().rx_mode.wait_not_empty::<T>().await } /// Split the CAN driver into transmit and receive halves. @@ -375,51 +350,95 @@ impl<'d, T: Instance> CanRx<'d, T> { /// /// Returns a tuple of the time the message was received and the message frame pub async fn read(&mut self) -> Result<Envelope, BusError> { - poll_fn(|cx| { - T::state().err_waker.register(cx.waker()); - if let Poll::Ready(envelope) = T::state().rx_queue.receive().poll_unpin(cx) { - return Poll::Ready(Ok(envelope)); - } else if let Some(err) = self.curr_error() { - return Poll::Ready(Err(err)); - } - - Poll::Pending - }) - .await + T::state().rx_mode.read::<T>().await } /// Attempts to read a CAN frame without blocking. /// /// Returns [Err(TryReadError::Empty)] if there are no frames in the rx queue. pub fn try_read(&mut self) -> Result<Envelope, TryReadError> { - if let Ok(envelope) = T::state().rx_queue.try_receive() { - return Ok(envelope); - } - - if let Some(err) = self.curr_error() { - return Err(TryReadError::BusError(err)); - } - - Err(TryReadError::Empty) + T::state().rx_mode.try_read::<T>() } /// Waits while receive queue is empty. pub async fn wait_not_empty(&mut self) { - poll_fn(|cx| T::state().rx_queue.poll_ready_to_receive(cx)).await + T::state().rx_mode.wait_not_empty::<T>().await } - fn curr_error(&self) -> Option<BusError> { - let err = { T::regs().esr().read() }; - if err.boff() { - return Some(BusError::BusOff); - } else if err.epvf() { - return Some(BusError::BusPassive); - } else if err.ewgf() { - return Some(BusError::BusWarning); - } else if let Some(err) = err.lec().into_bus_err() { - return Some(err); + /// Return a buffered instance of driver without CAN FD support. User must supply Buffers + pub fn buffered<const RX_BUF_SIZE: usize>( + self, + rxb: &'static mut RxBuf<RX_BUF_SIZE>, + ) -> BufferedCanRx<'d, T, RX_BUF_SIZE> { + BufferedCanRx::new(self.rx, rxb) + } +} + +/// User supplied buffer for RX Buffering +pub type RxBuf<const BUF_SIZE: usize> = + Channel<CriticalSectionRawMutex, Result<(Frame, Timestamp), BusError>, BUF_SIZE>; + +/// CAN driver, receive half in Buffered mode. +pub struct BufferedCanRx<'d, T: Instance, const RX_BUF_SIZE: usize> { + _rx: crate::can::bx::Rx<BxcanInstance<'d, T>>, + rx_buf: &'static RxBuf<RX_BUF_SIZE>, +} + +impl<'d, T: Instance, const RX_BUF_SIZE: usize> BufferedCanRx<'d, T, RX_BUF_SIZE> { + fn new(_rx: crate::can::bx::Rx<BxcanInstance<'d, T>>, rx_buf: &'static RxBuf<RX_BUF_SIZE>) -> Self { + BufferedCanRx { _rx, rx_buf }.setup() + } + + fn setup(self) -> Self { + // We don't want interrupts being processed while we change modes. + critical_section::with(|_| unsafe { + let rx_inner = self::common::ClassicBufferedRxInner { + rx_sender: self.rx_buf.sender().into(), + }; + T::mut_state().rx_mode = RxMode::Buffered(rx_inner); + }); + self + } + + /// Async read frame from RX buffer. + pub async fn read(&mut self) -> Result<(Frame, Timestamp), BusError> { + self.rx_buf.receive().await + } + + /// Attempts to read a CAN frame without blocking. + /// + /// Returns [Err(TryReadError::Empty)] if there are no frames in the rx queue. + pub fn try_read(&mut self) -> Result<Envelope, TryReadError> { + match &T::state().rx_mode { + RxMode::Buffered(_) => { + if let Ok(result) = self.rx_buf.try_receive() { + match result { + Ok((frame, ts)) => Ok(Envelope { ts, frame }), + Err(e) => Err(TryReadError::BusError(e)), + } + } else { + let registers = crate::can::bx::Registers { canregs: T::regs() }; + if let Some(err) = registers.curr_error() { + return Err(TryReadError::BusError(err)); + } else { + Err(TryReadError::Empty) + } + } + } + _ => { + panic!("Bad Mode") + } } - None + } + + /// Waits while receive queue is empty. + pub async fn wait_not_empty(&mut self) { + poll_fn(|cx| self.rx_buf.poll_ready_to_receive(cx)).await + } + + /// Returns a receiver that can be used for receiving CAN frames. Note, each CAN frame will only be received by one receiver. + pub fn reader(&self) -> BufferedCanReceiver { + self.rx_buf.receiver().into() } } @@ -448,10 +467,111 @@ impl<'d, T: Instance> DerefMut for Can<'d, T> { } } +use crate::can::enums::{BusError, TryReadError}; + +pub(crate) enum RxMode { + NonBuffered(AtomicWaker), + Buffered(crate::can::_version::common::ClassicBufferedRxInner), +} + +impl RxMode { + pub fn on_interrupt<T: Instance>(&self, fifo: crate::can::_version::bx::RxFifo) { + match self { + Self::NonBuffered(waker) => { + // Disable interrupts until read + let fifo_idx = match fifo { + crate::can::_version::bx::RxFifo::Fifo0 => 0usize, + crate::can::_version::bx::RxFifo::Fifo1 => 1usize, + }; + T::regs().ier().write(|w| { + w.set_fmpie(fifo_idx, false); + }); + waker.wake(); + } + Self::Buffered(buf) => { + let regsisters = crate::can::bx::Registers { canregs: T::regs() }; + + loop { + match regsisters.receive_fifo(fifo) { + Some(envelope) => { + // NOTE: consensus was reached that if rx_queue is full, packets should be dropped + let _ = buf.rx_sender.try_send(Ok((envelope.frame, envelope.ts))); + } + None => return, + }; + } + } + } + } + + pub async fn read<T: Instance>(&self) -> Result<Envelope, BusError> { + match self { + Self::NonBuffered(waker) => { + poll_fn(|cx| { + T::state().err_waker.register(cx.waker()); + waker.register(cx.waker()); + match self.try_read::<T>() { + Ok(result) => Poll::Ready(Ok(result)), + Err(TryReadError::Empty) => Poll::Pending, + Err(TryReadError::BusError(be)) => Poll::Ready(Err(be)), + } + }) + .await + } + _ => { + panic!("Bad Mode") + } + } + } + pub fn try_read<T: Instance>(&self) -> Result<Envelope, TryReadError> { + match self { + Self::NonBuffered(_) => { + let registers = crate::can::bx::Registers { canregs: T::regs() }; + if let Some(msg) = registers.receive_fifo(super::bx::RxFifo::Fifo0) { + T::regs().ier().write(|w| { + w.set_fmpie(0, true); + }); + Ok(msg) + } else if let Some(msg) = registers.receive_fifo(super::bx::RxFifo::Fifo1) { + T::regs().ier().write(|w| { + w.set_fmpie(1, true); + }); + Ok(msg) + } else if let Some(err) = registers.curr_error() { + Err(TryReadError::BusError(err)) + } else { + Err(TryReadError::Empty) + } + } + _ => { + panic!("Bad Mode") + } + } + } + pub async fn wait_not_empty<T: Instance>(&self) { + match &T::state().rx_mode { + Self::NonBuffered(waker) => { + poll_fn(|cx| { + waker.register(cx.waker()); + let registers = crate::can::bx::Registers { canregs: T::regs() }; + if registers.receive_frame_available() { + Poll::Ready(()) + } else { + Poll::Pending + } + }) + .await + } + _ => { + panic!("Bad Mode") + } + } + } +} struct State { pub tx_waker: AtomicWaker, pub err_waker: AtomicWaker, - pub rx_queue: Channel<CriticalSectionRawMutex, Envelope, 32>, + pub(crate) rx_mode: RxMode, } impl State { @@ -459,7 +579,7 @@ impl State { Self { tx_waker: AtomicWaker::new(), err_waker: AtomicWaker::new(), - rx_queue: Channel::new(), + rx_mode: RxMode::NonBuffered(AtomicWaker::new()), } } } @@ -467,6 +587,7 @@ impl State { trait SealedInstance { fn regs() -> crate::pac::can::Can; fn state() -> &'static State; + unsafe fn mut_state() -> &'static mut State; } /// CAN instance trait. @@ -495,9 +616,12 @@ foreach_peripheral!( crate::pac::$inst } + unsafe fn mut_state() -> & 'static mut State { + static mut STATE: State = State::new(); + &mut *core::ptr::addr_of_mut!(STATE) + } fn state() -> &'static State { - static STATE: State = State::new(); - &STATE + unsafe { peripherals::$inst::mut_state() } } } @@ -561,5 +685,3 @@ impl Index for crate::can::bx::Mailbox { } } } - - diff --git a/embassy-stm32/src/can/common.rs b/embassy-stm32/src/can/common.rs index 8c9990798..b0d177a6d 100644 --- a/embassy-stm32/src/can/common.rs +++ b/embassy-stm32/src/can/common.rs @@ -18,9 +18,13 @@ pub(crate) struct ClassicBufferedTxInner { pub tx_receiver: DynamicReceiver<'static, ClassicFrame>, } +#[cfg(any(can_fdcan_v1, can_fdcan_h7))] + pub(crate) struct FdBufferedRxInner { pub rx_sender: DynamicSender<'static, Result<(FdFrame, Timestamp), BusError>>, } + +#[cfg(any(can_fdcan_v1, can_fdcan_h7))] pub(crate) struct FdBufferedTxInner { pub tx_receiver: DynamicReceiver<'static, FdFrame>, } diff --git a/embassy-stm32/src/can/enums.rs b/embassy-stm32/src/can/enums.rs index 651de9194..4d89c84d1 100644 --- a/embassy-stm32/src/can/enums.rs +++ b/embassy-stm32/src/can/enums.rs @@ -40,3 +40,13 @@ pub enum FrameCreateError { /// Invalid ID. InvalidCanId, } + +/// Error returned by `try_read` +#[derive(Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum TryReadError { + /// Bus error + BusError(BusError), + /// Receive buffer is empty + Empty, +} From 41b7e4a434c0d1fe1fc5d93afe76f8058aa411db Mon Sep 17 00:00:00 2001 From: Corey Schuhen <cschuhen@gmail.com> Date: Sun, 24 Mar 2024 08:08:12 +1000 Subject: [PATCH 08/14] BXCAN: Create TxMode in order to support buffered TX. --- embassy-stm32/src/can/bxcan.rs | 130 ++++++++++++++++++++++++++++++--- embassy-stm32/src/can/fdcan.rs | 5 +- 2 files changed, 120 insertions(+), 15 deletions(-) diff --git a/embassy-stm32/src/can/bxcan.rs b/embassy-stm32/src/can/bxcan.rs index 66f6e7067..45a3836c0 100644 --- a/embassy-stm32/src/can/bxcan.rs +++ b/embassy-stm32/src/can/bxcan.rs @@ -17,7 +17,6 @@ use crate::rcc::RccPeripheral; use crate::{interrupt, peripherals, Peripheral}; pub mod enums; -use enums::*; pub mod frame; pub mod util; @@ -49,8 +48,7 @@ impl<T: Instance> interrupt::typelevel::Handler<T::TXInterrupt> for TxInterruptH v.set_rqcp(1, true); v.set_rqcp(2, true); }); - - T::state().tx_waker.wake(); + T::state().tx_mode.on_interrupt::<T>(); } } @@ -258,7 +256,7 @@ impl<'d, T: Instance> CanTx<'d, T> { /// If the TX queue is full, this will wait until there is space, therefore exerting backpressure. pub async fn write(&mut self, frame: &Frame) -> crate::can::bx::TransmitStatus { poll_fn(|cx| { - T::state().tx_waker.register(cx.waker()); + T::state().tx_mode.register(cx.waker()); if let Ok(status) = self.tx.transmit(frame) { return Poll::Ready(status); } @@ -277,7 +275,7 @@ impl<'d, T: Instance> CanTx<'d, T> { async fn flush_inner(mb: crate::can::bx::Mailbox) { poll_fn(|cx| { - T::state().tx_waker.register(cx.waker()); + T::state().tx_mode.register(cx.waker()); if T::regs().tsr().read().tme(mb.index()) { return Poll::Ready(()); } @@ -294,7 +292,7 @@ impl<'d, T: Instance> CanTx<'d, T> { async fn flush_any_inner() { poll_fn(|cx| { - T::state().tx_waker.register(cx.waker()); + T::state().tx_mode.register(cx.waker()); let tsr = T::regs().tsr().read(); if tsr.tme(crate::can::bx::Mailbox::Mailbox0.index()) @@ -316,7 +314,7 @@ impl<'d, T: Instance> CanTx<'d, T> { async fn flush_all_inner() { poll_fn(|cx| { - T::state().tx_waker.register(cx.waker()); + T::state().tx_mode.register(cx.waker()); let tsr = T::regs().tsr().read(); if tsr.tme(crate::can::bx::Mailbox::Mailbox0.index()) @@ -335,6 +333,62 @@ impl<'d, T: Instance> CanTx<'d, T> { pub async fn flush_all(&self) { Self::flush_all_inner().await } + + /// Return a buffered instance of driver. User must supply Buffers + pub fn buffered<const TX_BUF_SIZE: usize>( + self, + txb: &'static mut TxBuf<TX_BUF_SIZE>, + ) -> BufferedCanTx<'d, T, TX_BUF_SIZE> { + BufferedCanTx::new(self.tx, txb) + } +} + +/// User supplied buffer for TX buffering +pub type TxBuf<const BUF_SIZE: usize> = Channel<CriticalSectionRawMutex, Frame, BUF_SIZE>; + +/// CAN driver, transmit half. +pub struct BufferedCanTx<'d, T: Instance, const TX_BUF_SIZE: usize> { + _tx: crate::can::bx::Tx<BxcanInstance<'d, T>>, + tx_buf: &'static TxBuf<TX_BUF_SIZE>, +} + +impl<'d, T: Instance, const TX_BUF_SIZE: usize> BufferedCanTx<'d, T, TX_BUF_SIZE> { + fn new(_tx: crate::can::bx::Tx<BxcanInstance<'d, T>>, tx_buf: &'static TxBuf<TX_BUF_SIZE>) -> Self { + Self { _tx, tx_buf }.setup() + } + + fn setup(self) -> Self { + // We don't want interrupts being processed while we change modes. + critical_section::with(|_| unsafe { + let tx_inner = self::common::ClassicBufferedTxInner { + tx_receiver: self.tx_buf.receiver().into(), + }; + T::mut_state().tx_mode = TxMode::Buffered(tx_inner); + }); + self + } + + /// Async write frame to TX buffer. + pub async fn write(&mut self, frame: &Frame) { + self.tx_buf.send(*frame).await; + T::TXInterrupt::pend(); // Wake for Tx + } + + /// Returns a sender that can be used for sending CAN frames. + pub fn writer(&self) -> BufferedCanSender { + BufferedCanSender { + tx_buf: self.tx_buf.sender().into(), + waker: T::TXInterrupt::pend, + } + } +} + +impl<'d, T: Instance, const TX_BUF_SIZE: usize> Drop for BufferedCanTx<'d, T, TX_BUF_SIZE> { + fn drop(&mut self) { + critical_section::with(|_| unsafe { + T::mut_state().tx_mode = TxMode::NonBuffered(embassy_sync::waitqueue::AtomicWaker::new()); + }); + } } /// CAN driver, receive half. @@ -365,7 +419,7 @@ impl<'d, T: Instance> CanRx<'d, T> { T::state().rx_mode.wait_not_empty::<T>().await } - /// Return a buffered instance of driver without CAN FD support. User must supply Buffers + /// Return a buffered instance of driver. User must supply Buffers pub fn buffered<const RX_BUF_SIZE: usize>( self, rxb: &'static mut RxBuf<RX_BUF_SIZE>, @@ -442,6 +496,14 @@ impl<'d, T: Instance, const RX_BUF_SIZE: usize> BufferedCanRx<'d, T, RX_BUF_SIZE } } +impl<'d, T: Instance, const RX_BUF_SIZE: usize> Drop for BufferedCanRx<'d, T, RX_BUF_SIZE> { + fn drop(&mut self) { + critical_section::with(|_| unsafe { + T::mut_state().rx_mode = RxMode::NonBuffered(embassy_sync::waitqueue::AtomicWaker::new()); + }); + } +} + use crate::can::bx::RxFifo; impl<'d, T: Instance> Drop for Can<'d, T> { @@ -568,18 +630,62 @@ impl RxMode { } } } + +enum TxMode { + NonBuffered(AtomicWaker), + Buffered(self::common::ClassicBufferedTxInner), +} + +impl TxMode { + pub fn buffer_free<T: Instance>(&self) -> bool { + let tsr = T::regs().tsr().read(); + tsr.tme(crate::can::bx::Mailbox::Mailbox0.index()) + || tsr.tme(crate::can::bx::Mailbox::Mailbox1.index()) + || tsr.tme(crate::can::bx::Mailbox::Mailbox2.index()) + } + pub fn on_interrupt<T: Instance>(&self) { + match &T::state().tx_mode { + TxMode::NonBuffered(waker) => waker.wake(), + TxMode::Buffered(buf) => { + while self.buffer_free::<T>() { + match buf.tx_receiver.try_receive() { + Ok(frame) => { + let mut registers = crate::can::bx::Registers { canregs: T::regs() }; + _ = registers.transmit(&frame); + } + Err(_) => { + break; + } + } + } + } + } + } + + fn register(&self, arg: &core::task::Waker) { + match self { + TxMode::NonBuffered(waker) => { + waker.register(arg); + } + _ => { + panic!("Bad mode"); + } + } + } +} + struct State { - pub tx_waker: AtomicWaker, - pub err_waker: AtomicWaker, pub(crate) rx_mode: RxMode, + pub(crate) tx_mode: TxMode, + pub err_waker: AtomicWaker, } impl State { pub const fn new() -> Self { Self { - tx_waker: AtomicWaker::new(), - err_waker: AtomicWaker::new(), rx_mode: RxMode::NonBuffered(AtomicWaker::new()), + tx_mode: TxMode::NonBuffered(AtomicWaker::new()), + err_waker: AtomicWaker::new(), } } } diff --git a/embassy-stm32/src/can/fdcan.rs b/embassy-stm32/src/can/fdcan.rs index 42c9bd9f6..e58d8c0ec 100644 --- a/embassy-stm32/src/can/fdcan.rs +++ b/embassy-stm32/src/can/fdcan.rs @@ -507,7 +507,7 @@ pub struct BufferedCanFd<'d, T: Instance, const TX_BUF_SIZE: usize, const RX_BUF /// Sender that can be used for sending CAN frames. #[derive(Copy, Clone)] pub struct BufferedFdCanSender { - tx_buf: embassy_sync::channel::DynamicSender<'static, FdFrame>, + tx_buf: DynamicSender<'static, FdFrame>, waker: fn(), } @@ -532,8 +532,7 @@ impl BufferedFdCanSender { } /// Receiver that can be used for receiving CAN frames. Note, each CAN frame will only be received by one receiver. -pub type BufferedFdCanReceiver = - embassy_sync::channel::DynamicReceiver<'static, Result<(FdFrame, Timestamp), BusError>>; +pub type BufferedFdCanReceiver = DynamicReceiver<'static, Result<(FdFrame, Timestamp), BusError>>; impl<'c, 'd, T: Instance, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize> BufferedCanFd<'d, T, TX_BUF_SIZE, RX_BUF_SIZE> From f5daa50a7baceb44f2aad44bf6ce055bccb08433 Mon Sep 17 00:00:00 2001 From: Corey Schuhen <cschuhen@gmail.com> Date: Sun, 24 Mar 2024 14:15:46 +1000 Subject: [PATCH 09/14] BXCAN: Add struct that combines Buffered RX and Buffered TX. --- embassy-stm32/src/can/bxcan.rs | 55 +++++++++++++++++++++++++++++++++- 1 file changed, 54 insertions(+), 1 deletion(-) diff --git a/embassy-stm32/src/can/bxcan.rs b/embassy-stm32/src/can/bxcan.rs index 45a3836c0..be2e34963 100644 --- a/embassy-stm32/src/can/bxcan.rs +++ b/embassy-stm32/src/can/bxcan.rs @@ -236,6 +236,19 @@ impl<'d, T: Instance> Can<'d, T> { let (tx, rx) = self.can.split_by_ref(); (CanTx { tx }, CanRx { rx }) } + + /// Return a buffered instance of driver. User must supply Buffers + pub fn buffered<'c, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize>( + &'c mut self, + txb: &'static mut TxBuf<TX_BUF_SIZE>, + rxb: &'static mut RxBuf<RX_BUF_SIZE>, + ) -> BufferedCan<'d, T, TX_BUF_SIZE, RX_BUF_SIZE> { + let (tx, rx) = self.split(); + BufferedCan { + tx: tx.buffered(txb), + rx: rx.buffered(rxb), + } + } } impl<'d, T: Instance> AsMut<crate::can::bx::Can<BxcanInstance<'d, T>>> for Can<'d, T> { @@ -245,6 +258,46 @@ impl<'d, T: Instance> AsMut<crate::can::bx::Can<BxcanInstance<'d, T>>> for Can<' } } +/// Buffered CAN driver. +pub struct BufferedCan<'d, T: Instance, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize> { + tx: BufferedCanTx<'d, T, TX_BUF_SIZE>, + rx: BufferedCanRx<'d, T, RX_BUF_SIZE>, +} + +impl<'d, T: Instance, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize> BufferedCan<'d, T, TX_BUF_SIZE, RX_BUF_SIZE> { + /// Async write frame to TX buffer. + pub async fn write(&mut self, frame: &Frame) { + self.tx.write(frame).await + } + + /// Returns a sender that can be used for sending CAN frames. + pub fn writer(&self) -> BufferedCanSender { + self.tx.writer() + } + + /// Async read frame from RX buffer. + pub async fn read(&mut self) -> Result<(Frame, Timestamp), BusError> { + self.rx.read().await + } + + /// Attempts to read a CAN frame without blocking. + /// + /// Returns [Err(TryReadError::Empty)] if there are no frames in the rx queue. + pub fn try_read(&mut self) -> Result<Envelope, TryReadError> { + self.rx.try_read() + } + + /// Waits while receive queue is empty. + pub async fn wait_not_empty(&mut self) { + self.rx.wait_not_empty().await + } + + /// Returns a receiver that can be used for receiving CAN frames. Note, each CAN frame will only be received by one receiver. + pub fn reader(&self) -> BufferedCanReceiver { + self.rx.reader() + } +} + /// CAN driver, transmit half. pub struct CanTx<'d, T: Instance> { tx: crate::can::bx::Tx<BxcanInstance<'d, T>>, @@ -346,7 +399,7 @@ impl<'d, T: Instance> CanTx<'d, T> { /// User supplied buffer for TX buffering pub type TxBuf<const BUF_SIZE: usize> = Channel<CriticalSectionRawMutex, Frame, BUF_SIZE>; -/// CAN driver, transmit half. +/// Buffered CAN driver, transmit half. pub struct BufferedCanTx<'d, T: Instance, const TX_BUF_SIZE: usize> { _tx: crate::can::bx::Tx<BxcanInstance<'d, T>>, tx_buf: &'static TxBuf<TX_BUF_SIZE>, From 2217b802781b5f2188b4da659aca47f9d89ee032 Mon Sep 17 00:00:00 2001 From: Corey Schuhen <cschuhen@gmail.com> Date: Sun, 24 Mar 2024 15:13:55 +1000 Subject: [PATCH 10/14] CAN: Unify API's between BXCAN and FDCAN. Use Envelope for all read methods instead of a tuple sometimes. --- embassy-stm32/src/can/bx/mod.rs | 5 +- embassy-stm32/src/can/bxcan.rs | 27 ++--- embassy-stm32/src/can/common.rs | 23 ++-- embassy-stm32/src/can/fd/peripheral.rs | 12 +-- embassy-stm32/src/can/fdcan.rs | 140 ++++++++++++++++--------- embassy-stm32/src/can/frame.rs | 60 +++++++++-- examples/stm32f1/Cargo.toml | 1 + examples/stm32f1/src/bin/can.rs | 100 +++++++++++++++--- examples/stm32g4/src/bin/can.rs | 29 ++--- examples/stm32h5/src/bin/can.rs | 12 ++- examples/stm32h7/src/bin/can.rs | 12 ++- tests/stm32/src/bin/fdcan.rs | 24 ++--- 12 files changed, 294 insertions(+), 151 deletions(-) diff --git a/embassy-stm32/src/can/bx/mod.rs b/embassy-stm32/src/can/bx/mod.rs index c508ef2fe..253bcee13 100644 --- a/embassy-stm32/src/can/bx/mod.rs +++ b/embassy-stm32/src/can/bx/mod.rs @@ -40,12 +40,11 @@ pub type Header = crate::can::frame::Header; /// Data for a CAN Frame pub type Data = crate::can::frame::ClassicData; -/// CAN Frame -pub type Frame = crate::can::frame::ClassicFrame; - use crate::can::_version::Envelope; use crate::can::bx::filter::MasterFilters; use crate::can::enums::BusError; +/// CAN Frame +pub use crate::can::frame::Frame; use crate::pac::can::vals::Lec; #[derive(Debug, Copy, Clone, Eq, PartialEq)] diff --git a/embassy-stm32/src/can/bxcan.rs b/embassy-stm32/src/can/bxcan.rs index be2e34963..fd6a79092 100644 --- a/embassy-stm32/src/can/bxcan.rs +++ b/embassy-stm32/src/can/bxcan.rs @@ -19,22 +19,10 @@ use crate::{interrupt, peripherals, Peripheral}; pub mod enums; pub mod frame; pub mod util; +pub use frame::Envelope; mod common; -pub use self::common::{BufferedCanReceiver, BufferedCanSender, Timestamp}; - -/// Contains CAN frame and additional metadata. -/// -/// Timestamp is available if `time` feature is enabled. -#[derive(Debug, Clone)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct Envelope { - /// Reception time. - #[cfg(feature = "time")] - pub ts: embassy_time::Instant, - /// The actual CAN frame. - pub frame: Frame, -} +pub use self::common::{BufferedCanReceiver, BufferedCanSender}; /// Interrupt handler. pub struct TxInterruptHandler<T: Instance> { @@ -276,7 +264,7 @@ impl<'d, T: Instance, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize> Buffer } /// Async read frame from RX buffer. - pub async fn read(&mut self) -> Result<(Frame, Timestamp), BusError> { + pub async fn read(&mut self) -> Result<Envelope, BusError> { self.rx.read().await } @@ -482,8 +470,7 @@ impl<'d, T: Instance> CanRx<'d, T> { } /// User supplied buffer for RX Buffering -pub type RxBuf<const BUF_SIZE: usize> = - Channel<CriticalSectionRawMutex, Result<(Frame, Timestamp), BusError>, BUF_SIZE>; +pub type RxBuf<const BUF_SIZE: usize> = Channel<CriticalSectionRawMutex, Result<Envelope, BusError>, BUF_SIZE>; /// CAN driver, receive half in Buffered mode. pub struct BufferedCanRx<'d, T: Instance, const RX_BUF_SIZE: usize> { @@ -508,7 +495,7 @@ impl<'d, T: Instance, const RX_BUF_SIZE: usize> BufferedCanRx<'d, T, RX_BUF_SIZE } /// Async read frame from RX buffer. - pub async fn read(&mut self) -> Result<(Frame, Timestamp), BusError> { + pub async fn read(&mut self) -> Result<Envelope, BusError> { self.rx_buf.receive().await } @@ -520,7 +507,7 @@ impl<'d, T: Instance, const RX_BUF_SIZE: usize> BufferedCanRx<'d, T, RX_BUF_SIZE RxMode::Buffered(_) => { if let Ok(result) = self.rx_buf.try_receive() { match result { - Ok((frame, ts)) => Ok(Envelope { ts, frame }), + Ok(envelope) => Ok(envelope), Err(e) => Err(TryReadError::BusError(e)), } } else { @@ -610,7 +597,7 @@ impl RxMode { match regsisters.receive_fifo(fifo) { Some(envelope) => { // NOTE: consensus was reached that if rx_queue is full, packets should be dropped - let _ = buf.rx_sender.try_send(Ok((envelope.frame, envelope.ts))); + let _ = buf.rx_sender.try_send(Ok(envelope)); } None => return, }; diff --git a/embassy-stm32/src/can/common.rs b/embassy-stm32/src/can/common.rs index b0d177a6d..570761b19 100644 --- a/embassy-stm32/src/can/common.rs +++ b/embassy-stm32/src/can/common.rs @@ -3,25 +3,17 @@ use embassy_sync::channel::{DynamicReceiver, DynamicSender}; use crate::can::_version::enums::*; use crate::can::_version::frame::*; -/// Timestamp for incoming packets. Use Embassy time when enabled. -#[cfg(feature = "time")] -pub type Timestamp = embassy_time::Instant; - -/// Timestamp for incoming packets. -#[cfg(not(feature = "time"))] -pub type Timestamp = u16; - pub(crate) struct ClassicBufferedRxInner { - pub rx_sender: DynamicSender<'static, Result<(ClassicFrame, Timestamp), BusError>>, + pub rx_sender: DynamicSender<'static, Result<Envelope, BusError>>, } pub(crate) struct ClassicBufferedTxInner { - pub tx_receiver: DynamicReceiver<'static, ClassicFrame>, + pub tx_receiver: DynamicReceiver<'static, Frame>, } #[cfg(any(can_fdcan_v1, can_fdcan_h7))] pub(crate) struct FdBufferedRxInner { - pub rx_sender: DynamicSender<'static, Result<(FdFrame, Timestamp), BusError>>, + pub rx_sender: DynamicSender<'static, Result<FdEnvelope, BusError>>, } #[cfg(any(can_fdcan_v1, can_fdcan_h7))] @@ -32,20 +24,20 @@ pub(crate) struct FdBufferedTxInner { /// Sender that can be used for sending CAN frames. #[derive(Copy, Clone)] pub struct BufferedCanSender { - pub(crate) tx_buf: embassy_sync::channel::DynamicSender<'static, ClassicFrame>, + pub(crate) tx_buf: embassy_sync::channel::DynamicSender<'static, Frame>, pub(crate) waker: fn(), } impl BufferedCanSender { /// Async write frame to TX buffer. - pub fn try_write(&mut self, frame: ClassicFrame) -> Result<(), embassy_sync::channel::TrySendError<ClassicFrame>> { + pub fn try_write(&mut self, frame: Frame) -> Result<(), embassy_sync::channel::TrySendError<Frame>> { self.tx_buf.try_send(frame)?; (self.waker)(); Ok(()) } /// Async write frame to TX buffer. - pub async fn write(&mut self, frame: ClassicFrame) { + pub async fn write(&mut self, frame: Frame) { self.tx_buf.send(frame).await; (self.waker)(); } @@ -57,5 +49,4 @@ impl BufferedCanSender { } /// Receiver that can be used for receiving CAN frames. Note, each CAN frame will only be received by one receiver. -pub type BufferedCanReceiver = - embassy_sync::channel::DynamicReceiver<'static, Result<(ClassicFrame, Timestamp), BusError>>; +pub type BufferedCanReceiver = embassy_sync::channel::DynamicReceiver<'static, Result<Envelope, BusError>>; diff --git a/embassy-stm32/src/can/fd/peripheral.rs b/embassy-stm32/src/can/fd/peripheral.rs index 76b76afe1..e32f19d91 100644 --- a/embassy-stm32/src/can/fd/peripheral.rs +++ b/embassy-stm32/src/can/fd/peripheral.rs @@ -397,13 +397,13 @@ impl Registers { /// Moves out of ConfigMode and into specified mode #[inline] - pub fn into_mode(mut self, config: FdCanConfig, mode: crate::can::_version::FdcanOperatingMode) { + pub fn into_mode(mut self, config: FdCanConfig, mode: crate::can::_version::OperatingMode) { match mode { - crate::can::FdcanOperatingMode::InternalLoopbackMode => self.set_loopback_mode(LoopbackMode::Internal), - crate::can::FdcanOperatingMode::ExternalLoopbackMode => self.set_loopback_mode(LoopbackMode::External), - crate::can::FdcanOperatingMode::NormalOperationMode => self.set_normal_operations(true), - crate::can::FdcanOperatingMode::RestrictedOperationMode => self.set_restricted_operations(true), - crate::can::FdcanOperatingMode::BusMonitoringMode => self.set_bus_monitoring_mode(true), + crate::can::OperatingMode::InternalLoopbackMode => self.set_loopback_mode(LoopbackMode::Internal), + crate::can::OperatingMode::ExternalLoopbackMode => self.set_loopback_mode(LoopbackMode::External), + crate::can::OperatingMode::NormalOperationMode => self.set_normal_operations(true), + crate::can::OperatingMode::RestrictedOperationMode => self.set_restricted_operations(true), + crate::can::OperatingMode::BusMonitoringMode => self.set_bus_monitoring_mode(true), } self.leave_init_mode(config); } diff --git a/embassy-stm32/src/can/fdcan.rs b/embassy-stm32/src/can/fdcan.rs index e58d8c0ec..2ccf4b093 100644 --- a/embassy-stm32/src/can/fdcan.rs +++ b/embassy-stm32/src/can/fdcan.rs @@ -110,7 +110,7 @@ impl<T: Instance> interrupt::typelevel::Handler<T::IT1Interrupt> for IT1Interrup #[derive(Debug, Copy, Clone, Eq, PartialEq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] /// Different operating modes -pub enum FdcanOperatingMode { +pub enum OperatingMode { //PoweredDownMode, //ConfigMode, /// This mode can be used for a “Hot Selftest”, meaning the FDCAN can be tested without @@ -148,7 +148,7 @@ pub enum FdcanOperatingMode { /// FDCAN Configuration instance instance /// Create instance of this first -pub struct FdcanConfigurator<'d, T: Instance> { +pub struct CanConfigurator<'d, T: Instance> { config: crate::can::fd::config::FdCanConfig, /// Reference to internals. instance: FdcanInstance<'d, T>, @@ -169,7 +169,7 @@ fn calc_ns_per_timer_tick<T: Instance>(mode: crate::can::fd::config::FrameTransm } } -impl<'d, T: Instance> FdcanConfigurator<'d, T> { +impl<'d, T: Instance> CanConfigurator<'d, T> { /// Creates a new Fdcan instance, keeping the peripheral in sleep mode. /// You must call [Fdcan::enable_non_blocking] to use the peripheral. pub fn new( @@ -179,7 +179,7 @@ impl<'d, T: Instance> FdcanConfigurator<'d, T> { _irqs: impl interrupt::typelevel::Binding<T::IT0Interrupt, IT0InterruptHandler<T>> + interrupt::typelevel::Binding<T::IT1Interrupt, IT1InterruptHandler<T>> + 'd, - ) -> FdcanConfigurator<'d, T> { + ) -> CanConfigurator<'d, T> { into_ref!(peri, rx, tx); rx.set_as_af(rx.af_num(), AFType::Input); @@ -273,13 +273,13 @@ impl<'d, T: Instance> FdcanConfigurator<'d, T> { } /// Start in mode. - pub fn start(self, mode: FdcanOperatingMode) -> Fdcan<'d, T> { + pub fn start(self, mode: OperatingMode) -> Can<'d, T> { let ns_per_timer_tick = calc_ns_per_timer_tick::<T>(self.config.frame_transmit); critical_section::with(|_| unsafe { T::mut_state().ns_per_timer_tick = ns_per_timer_tick; }); T::registers().into_mode(self.config, mode); - let ret = Fdcan { + let ret = Can { config: self.config, instance: self.instance, _mode: mode, @@ -288,30 +288,30 @@ impl<'d, T: Instance> FdcanConfigurator<'d, T> { } /// Start, entering mode. Does same as start(mode) - pub fn into_normal_mode(self) -> Fdcan<'d, T> { - self.start(FdcanOperatingMode::NormalOperationMode) + pub fn into_normal_mode(self) -> Can<'d, T> { + self.start(OperatingMode::NormalOperationMode) } /// Start, entering mode. Does same as start(mode) - pub fn into_internal_loopback_mode(self) -> Fdcan<'d, T> { - self.start(FdcanOperatingMode::InternalLoopbackMode) + pub fn into_internal_loopback_mode(self) -> Can<'d, T> { + self.start(OperatingMode::InternalLoopbackMode) } /// Start, entering mode. Does same as start(mode) - pub fn into_external_loopback_mode(self) -> Fdcan<'d, T> { - self.start(FdcanOperatingMode::ExternalLoopbackMode) + pub fn into_external_loopback_mode(self) -> Can<'d, T> { + self.start(OperatingMode::ExternalLoopbackMode) } } /// FDCAN Instance -pub struct Fdcan<'d, T: Instance> { +pub struct Can<'d, T: Instance> { config: crate::can::fd::config::FdCanConfig, /// Reference to internals. instance: FdcanInstance<'d, T>, - _mode: FdcanOperatingMode, + _mode: OperatingMode, } -impl<'d, T: Instance> Fdcan<'d, T> { +impl<'d, T: Instance> Can<'d, T> { /// Flush one of the TX mailboxes. pub async fn flush(&self, idx: usize) { poll_fn(|cx| { @@ -334,12 +334,12 @@ impl<'d, T: Instance> Fdcan<'d, T> { /// frame is dropped from the mailbox, it is returned. If no lower-priority frames /// can be replaced, this call asynchronously waits for a frame to be successfully /// transmitted, then tries again. - pub async fn write(&mut self, frame: &ClassicFrame) -> Option<ClassicFrame> { + pub async fn write(&mut self, frame: &Frame) -> Option<Frame> { T::state().tx_mode.write::<T>(frame).await } /// Returns the next received message frame - pub async fn read(&mut self) -> Result<(ClassicFrame, Timestamp), BusError> { + pub async fn read(&mut self) -> Result<Envelope, BusError> { T::state().rx_mode.read_classic::<T>().await } @@ -352,19 +352,19 @@ impl<'d, T: Instance> Fdcan<'d, T> { } /// Returns the next received message frame - pub async fn read_fd(&mut self) -> Result<(FdFrame, Timestamp), BusError> { + pub async fn read_fd(&mut self) -> Result<FdEnvelope, BusError> { T::state().rx_mode.read_fd::<T>().await } /// Split instance into separate Tx(write) and Rx(read) portions - pub fn split(self) -> (FdcanTx<'d, T>, FdcanRx<'d, T>) { + pub fn split(self) -> (CanTx<'d, T>, CanRx<'d, T>) { ( - FdcanTx { + CanTx { config: self.config, _instance: self.instance, _mode: self._mode, }, - FdcanRx { + CanRx { _instance1: PhantomData::<T>, _instance2: T::regs(), _mode: self._mode, @@ -373,8 +373,8 @@ impl<'d, T: Instance> Fdcan<'d, T> { } /// Join split rx and tx portions back together - pub fn join(tx: FdcanTx<'d, T>, rx: FdcanRx<'d, T>) -> Self { - Fdcan { + pub fn join(tx: CanTx<'d, T>, rx: CanRx<'d, T>) -> Self { + Can { config: tx.config, //_instance2: T::regs(), instance: tx._instance, @@ -402,17 +402,16 @@ impl<'d, T: Instance> Fdcan<'d, T> { } /// User supplied buffer for RX Buffering -pub type RxBuf<const BUF_SIZE: usize> = - Channel<CriticalSectionRawMutex, Result<(ClassicFrame, Timestamp), BusError>, BUF_SIZE>; +pub type RxBuf<const BUF_SIZE: usize> = Channel<CriticalSectionRawMutex, Result<Envelope, BusError>, BUF_SIZE>; /// User supplied buffer for TX buffering -pub type TxBuf<const BUF_SIZE: usize> = Channel<CriticalSectionRawMutex, ClassicFrame, BUF_SIZE>; +pub type TxBuf<const BUF_SIZE: usize> = Channel<CriticalSectionRawMutex, Frame, BUF_SIZE>; /// Buffered FDCAN Instance pub struct BufferedCan<'d, T: Instance, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize> { _instance1: PhantomData<T>, _instance2: &'d crate::pac::can::Fdcan, - _mode: FdcanOperatingMode, + _mode: OperatingMode, tx_buf: &'static TxBuf<TX_BUF_SIZE>, rx_buf: &'static RxBuf<RX_BUF_SIZE>, } @@ -423,7 +422,7 @@ impl<'c, 'd, T: Instance, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize> fn new( _instance1: PhantomData<T>, _instance2: &'d crate::pac::can::Fdcan, - _mode: FdcanOperatingMode, + _mode: OperatingMode, tx_buf: &'static TxBuf<TX_BUF_SIZE>, rx_buf: &'static RxBuf<RX_BUF_SIZE>, ) -> Self { @@ -453,13 +452,13 @@ impl<'c, 'd, T: Instance, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize> } /// Async write frame to TX buffer. - pub async fn write(&mut self, frame: ClassicFrame) { + pub async fn write(&mut self, frame: Frame) { self.tx_buf.send(frame).await; T::IT0Interrupt::pend(); // Wake for Tx } /// Async read frame from RX buffer. - pub async fn read(&mut self) -> Result<(ClassicFrame, Timestamp), BusError> { + pub async fn read(&mut self) -> Result<Envelope, BusError> { self.rx_buf.receive().await } @@ -489,8 +488,7 @@ impl<'c, 'd, T: Instance, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize> Dr } /// User supplied buffer for RX Buffering -pub type RxFdBuf<const BUF_SIZE: usize> = - Channel<CriticalSectionRawMutex, Result<(FdFrame, Timestamp), BusError>, BUF_SIZE>; +pub type RxFdBuf<const BUF_SIZE: usize> = Channel<CriticalSectionRawMutex, Result<FdEnvelope, BusError>, BUF_SIZE>; /// User supplied buffer for TX buffering pub type TxFdBuf<const BUF_SIZE: usize> = Channel<CriticalSectionRawMutex, FdFrame, BUF_SIZE>; @@ -499,7 +497,7 @@ pub type TxFdBuf<const BUF_SIZE: usize> = Channel<CriticalSectionRawMutex, FdFra pub struct BufferedCanFd<'d, T: Instance, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize> { _instance1: PhantomData<T>, _instance2: &'d crate::pac::can::Fdcan, - _mode: FdcanOperatingMode, + _mode: OperatingMode, tx_buf: &'static TxFdBuf<TX_BUF_SIZE>, rx_buf: &'static RxFdBuf<RX_BUF_SIZE>, } @@ -532,7 +530,7 @@ impl BufferedFdCanSender { } /// Receiver that can be used for receiving CAN frames. Note, each CAN frame will only be received by one receiver. -pub type BufferedFdCanReceiver = DynamicReceiver<'static, Result<(FdFrame, Timestamp), BusError>>; +pub type BufferedFdCanReceiver = DynamicReceiver<'static, Result<FdEnvelope, BusError>>; impl<'c, 'd, T: Instance, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize> BufferedCanFd<'d, T, TX_BUF_SIZE, RX_BUF_SIZE> @@ -540,7 +538,7 @@ impl<'c, 'd, T: Instance, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize> fn new( _instance1: PhantomData<T>, _instance2: &'d crate::pac::can::Fdcan, - _mode: FdcanOperatingMode, + _mode: OperatingMode, tx_buf: &'static TxFdBuf<TX_BUF_SIZE>, rx_buf: &'static RxFdBuf<RX_BUF_SIZE>, ) -> Self { @@ -576,7 +574,7 @@ impl<'c, 'd, T: Instance, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize> } /// Async read frame from RX buffer. - pub async fn read(&mut self) -> Result<(FdFrame, Timestamp), BusError> { + pub async fn read(&mut self) -> Result<FdEnvelope, BusError> { self.rx_buf.receive().await } @@ -606,25 +604,25 @@ impl<'c, 'd, T: Instance, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize> Dr } /// FDCAN Rx only Instance -pub struct FdcanRx<'d, T: Instance> { +pub struct CanRx<'d, T: Instance> { _instance1: PhantomData<T>, _instance2: &'d crate::pac::can::Fdcan, - _mode: FdcanOperatingMode, + _mode: OperatingMode, } /// FDCAN Tx only Instance -pub struct FdcanTx<'d, T: Instance> { +pub struct CanTx<'d, T: Instance> { config: crate::can::fd::config::FdCanConfig, _instance: FdcanInstance<'d, T>, //(PeripheralRef<'a, T>); - _mode: FdcanOperatingMode, + _mode: OperatingMode, } -impl<'c, 'd, T: Instance> FdcanTx<'d, T> { +impl<'c, 'd, T: Instance> CanTx<'d, T> { /// Queues the message to be sent but exerts backpressure. If a lower-priority /// frame is dropped from the mailbox, it is returned. If no lower-priority frames /// can be replaced, this call asynchronously waits for a frame to be successfully /// transmitted, then tries again. - pub async fn write(&mut self, frame: &ClassicFrame) -> Option<ClassicFrame> { + pub async fn write(&mut self, frame: &Frame) -> Option<Frame> { T::state().tx_mode.write::<T>(frame).await } @@ -637,14 +635,14 @@ impl<'c, 'd, T: Instance> FdcanTx<'d, T> { } } -impl<'c, 'd, T: Instance> FdcanRx<'d, T> { +impl<'c, 'd, T: Instance> CanRx<'d, T> { /// Returns the next received message frame - pub async fn read(&mut self) -> Result<(ClassicFrame, Timestamp), BusError> { + pub async fn read(&mut self) -> Result<Envelope, BusError> { T::state().rx_mode.read_classic::<T>().await } /// Returns the next received message frame - pub async fn read_fd(&mut self) -> Result<(FdFrame, Timestamp), BusError> { + pub async fn read_fd(&mut self) -> Result<FdEnvelope, BusError> { T::state().rx_mode.read_fd::<T>().await } } @@ -672,18 +670,50 @@ impl RxMode { waker.wake(); } RxMode::ClassicBuffered(buf) => { - if let Some(result) = self.read::<T, _>() { + if let Some(result) = self.try_read::<T>() { let _ = buf.rx_sender.try_send(result); } } RxMode::FdBuffered(buf) => { - if let Some(result) = self.read::<T, _>() { + if let Some(result) = self.try_read_fd::<T>() { let _ = buf.rx_sender.try_send(result); } } } } + //async fn read_classic<T: Instance>(&self) -> Result<Envelope, BusError> { + fn try_read<T: Instance>(&self) -> Option<Result<Envelope, BusError>> { + if let Some((frame, ts)) = T::registers().read(0) { + let ts = T::calc_timestamp(T::state().ns_per_timer_tick, ts); + Some(Ok(Envelope { ts, frame })) + } else if let Some((frame, ts)) = T::registers().read(1) { + let ts = T::calc_timestamp(T::state().ns_per_timer_tick, ts); + Some(Ok(Envelope { ts, frame })) + } else if let Some(err) = T::registers().curr_error() { + // TODO: this is probably wrong + Some(Err(err)) + } else { + None + } + } + + //async fn read_classic<T: Instance>(&self) -> Result<Envelope, BusError> { + fn try_read_fd<T: Instance>(&self) -> Option<Result<FdEnvelope, BusError>> { + if let Some((frame, ts)) = T::registers().read(0) { + let ts = T::calc_timestamp(T::state().ns_per_timer_tick, ts); + Some(Ok(FdEnvelope { ts, frame })) + } else if let Some((frame, ts)) = T::registers().read(1) { + let ts = T::calc_timestamp(T::state().ns_per_timer_tick, ts); + Some(Ok(FdEnvelope { ts, frame })) + } else if let Some(err) = T::registers().curr_error() { + // TODO: this is probably wrong + Some(Err(err)) + } else { + None + } + } + fn read<T: Instance, F: CanHeader>(&self) -> Option<Result<(F, Timestamp), BusError>> { if let Some((msg, ts)) = T::registers().read(0) { let ts = T::calc_timestamp(T::state().ns_per_timer_tick, ts); @@ -711,12 +741,18 @@ impl RxMode { .await } - async fn read_classic<T: Instance>(&self) -> Result<(ClassicFrame, Timestamp), BusError> { - self.read_async::<T, _>().await + async fn read_classic<T: Instance>(&self) -> Result<Envelope, BusError> { + match self.read_async::<T, _>().await { + Ok((frame, ts)) => Ok(Envelope { ts, frame }), + Err(e) => Err(e), + } } - async fn read_fd<T: Instance>(&self) -> Result<(FdFrame, Timestamp), BusError> { - self.read_async::<T, _>().await + async fn read_fd<T: Instance>(&self) -> Result<FdEnvelope, BusError> { + match self.read_async::<T, _>().await { + Ok((frame, ts)) => Ok(FdEnvelope { ts, frame }), + Err(e) => Err(e), + } } } @@ -761,7 +797,7 @@ impl TxMode { /// frame is dropped from the mailbox, it is returned. If no lower-priority frames /// can be replaced, this call asynchronously waits for a frame to be successfully /// transmitted, then tries again. - async fn write<T: Instance>(&self, frame: &ClassicFrame) -> Option<ClassicFrame> { + async fn write<T: Instance>(&self, frame: &Frame) -> Option<Frame> { self.write_generic::<T, _>(frame).await } diff --git a/embassy-stm32/src/can/frame.rs b/embassy-stm32/src/can/frame.rs index 14fc32c51..fb032aee2 100644 --- a/embassy-stm32/src/can/frame.rs +++ b/embassy-stm32/src/can/frame.rs @@ -136,19 +136,20 @@ impl ClassicData { } } -/// Frame with up to 8 bytes of data payload as per Classic CAN +/// Frame with up to 8 bytes of data payload as per Classic(non-FD) CAN +/// For CAN-FD support use FdFrame #[derive(Debug, Copy, Clone)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct ClassicFrame { +pub struct Frame { can_header: Header, data: ClassicData, } -impl ClassicFrame { +impl Frame { /// Create a new CAN classic Frame pub fn new(can_header: Header, raw_data: &[u8]) -> Result<Self, FrameCreateError> { let data = ClassicData::new(raw_data)?; - Ok(ClassicFrame { can_header, data: data }) + Ok(Frame { can_header, data: data }) } /// Creates a new data frame. @@ -206,9 +207,9 @@ impl ClassicFrame { } } -impl embedded_can::Frame for ClassicFrame { +impl embedded_can::Frame for Frame { fn new(id: impl Into<embedded_can::Id>, raw_data: &[u8]) -> Option<Self> { - let frameopt = ClassicFrame::new(Header::new(id.into(), raw_data.len() as u8, false), raw_data); + let frameopt = Frame::new(Header::new(id.into(), raw_data.len() as u8, false), raw_data); match frameopt { Ok(frame) => Some(frame), Err(_) => None, @@ -216,7 +217,7 @@ impl embedded_can::Frame for ClassicFrame { } fn new_remote(id: impl Into<embedded_can::Id>, len: usize) -> Option<Self> { if len <= 8 { - let frameopt = ClassicFrame::new(Header::new(id.into(), len as u8, true), &[0; 8]); + let frameopt = Frame::new(Header::new(id.into(), len as u8, true), &[0; 8]); match frameopt { Ok(frame) => Some(frame), Err(_) => None, @@ -245,7 +246,7 @@ impl embedded_can::Frame for ClassicFrame { } } -impl CanHeader for ClassicFrame { +impl CanHeader for Frame { fn from_header(header: Header, data: &[u8]) -> Result<Self, FrameCreateError> { Self::new(header, data) } @@ -255,10 +256,32 @@ impl CanHeader for ClassicFrame { } } +/// Contains CAN frame and additional metadata. +/// +/// Timestamp is available if `time` feature is enabled. +/// For CAN-FD support use FdEnvelope +#[derive(Debug, Clone)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub struct Envelope { + /// Reception time. + #[cfg(feature = "time")] + pub ts: embassy_time::Instant, + /// The actual CAN frame. + pub frame: Frame, +} + +impl Envelope { + /// Convert into a tuple + pub fn parts(self) -> (Frame, embassy_time::Instant) { + (self.frame, self.ts) + } +} + /// Payload of a (FD)CAN data frame. /// /// Contains 0 to 64 Bytes of data. #[derive(Debug, Copy, Clone)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct FdData { pub(crate) bytes: [u8; 64], } @@ -308,6 +331,7 @@ impl FdData { /// Frame with up to 8 bytes of data payload as per Fd CAN #[derive(Debug, Copy, Clone)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct FdFrame { can_header: Header, data: FdData, @@ -410,3 +434,23 @@ impl CanHeader for FdFrame { self.header() } } + +/// Contains CAN FD frame and additional metadata. +/// +/// Timestamp is available if `time` feature is enabled. +#[derive(Debug, Clone)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub struct FdEnvelope { + /// Reception time. + #[cfg(feature = "time")] + pub ts: embassy_time::Instant, + /// The actual CAN frame. + pub frame: FdFrame, +} + +impl FdEnvelope { + /// Convert into a tuple + pub fn parts(self) -> (FdFrame, embassy_time::Instant) { + (self.frame, self.ts) + } +} diff --git a/examples/stm32f1/Cargo.toml b/examples/stm32f1/Cargo.toml index df5d32f70..4f282f326 100644 --- a/examples/stm32f1/Cargo.toml +++ b/examples/stm32f1/Cargo.toml @@ -23,6 +23,7 @@ panic-probe = { version = "0.3", features = ["print-defmt"] } futures = { version = "0.3.17", default-features = false, features = ["async-await"] } heapless = { version = "0.8", default-features = false } nb = "1.0.0" +static_cell = "2.0.0" [profile.dev] opt-level = "s" diff --git a/examples/stm32f1/src/bin/can.rs b/examples/stm32f1/src/bin/can.rs index ac337e8a0..90cb9e46b 100644 --- a/examples/stm32f1/src/bin/can.rs +++ b/examples/stm32f1/src/bin/can.rs @@ -4,11 +4,12 @@ use defmt::*; use embassy_executor::Spawner; use embassy_stm32::can::{ - filter, Can, Fifo, Frame, Id, Rx0InterruptHandler, Rx1InterruptHandler, SceInterruptHandler, StandardId, + filter, Can, Envelope, Fifo, Frame, Id, Rx0InterruptHandler, Rx1InterruptHandler, SceInterruptHandler, StandardId, TxInterruptHandler, }; use embassy_stm32::peripherals::CAN; use embassy_stm32::{bind_interrupts, Config}; +use static_cell::StaticCell; use {defmt_rtt as _, panic_probe as _}; bind_interrupts!(struct Irqs { @@ -21,6 +22,27 @@ bind_interrupts!(struct Irqs { // This example is configured to work with real CAN transceivers on B8/B9. // See other examples for loopback. +fn handle_frame(env: Envelope, read_mode: &str) { + match env.frame.id() { + Id::Extended(id) => { + defmt::println!( + "{} Extended Frame id={:x} {:02x}", + read_mode, + id.as_raw(), + env.frame.data() + ); + } + Id::Standard(id) => { + defmt::println!( + "{} Standard Frame id={:x} {:02x}", + read_mode, + id.as_raw(), + env.frame.data() + ); + } + } +} + #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Config::default()); @@ -28,6 +50,9 @@ async fn main(_spawner: Spawner) { // Set alternate pin mapping to B8/B9 embassy_stm32::pac::AFIO.mapr().modify(|w| w.set_can1_remap(2)); + static RX_BUF: StaticCell<embassy_stm32::can::RxBuf<10>> = StaticCell::new(); + static TX_BUF: StaticCell<embassy_stm32::can::TxBuf<10>> = StaticCell::new(); + let mut can = Can::new(p.CAN, p.PB8, p.PB9, Irqs); can.as_mut() @@ -43,21 +68,72 @@ async fn main(_spawner: Spawner) { can.set_bitrate(250_000); can.enable().await; - let mut i: u8 = 0; + + /* + // Example for using buffered Tx and Rx without needing to + // split first as is done below. + let mut can = can.buffered( + TX_BUF.init(embassy_stm32::can::TxBuf::<10>::new()), + RX_BUF.init(embassy_stm32::can::RxBuf::<10>::new())); + loop { + let tx_frame = Frame::new_data(unwrap!(StandardId::new(i as _)), &[i, 0, 1, 2, 3, 4, 5, 6]).unwrap(); + can.write(&tx_frame).await; + + match can.read().await { + Ok((frame, ts)) => { + handle_frame(Envelope { ts, frame }, "Buf"); + } + Err(err) => { + defmt::println!("Error {}", err); + } + } + i += 1; + } + + */ + let (mut tx, mut rx) = can.split(); + + // This example shows using the wait_not_empty API before try read + while i < 3 { + let tx_frame = Frame::new_data(unwrap!(StandardId::new(i as _)), &[i, 0, 1, 2, 3, 4, 5, 6]).unwrap(); + tx.write(&tx_frame).await; + + rx.wait_not_empty().await; + let env = rx.try_read().unwrap(); + handle_frame(env, "Wait"); + i += 1; + } + + // This example shows using the full async non-buffered API + while i < 6 { + let tx_frame = Frame::new_data(unwrap!(StandardId::new(i as _)), &[i, 0, 1, 2, 3, 4, 5, 6]).unwrap(); + tx.write(&tx_frame).await; + + match rx.read().await { + Ok(env) => { + handle_frame(env, "NoBuf"); + } + Err(err) => { + defmt::println!("Error {}", err); + } + } + i += 1; + } + + // This example shows using buffered RX and TX. User passes in desired buffer (size) + // It's possible this way to have just RX or TX buffered. + let mut rx = rx.buffered(RX_BUF.init(embassy_stm32::can::RxBuf::<10>::new())); + let mut tx = tx.buffered(TX_BUF.init(embassy_stm32::can::TxBuf::<10>::new())); + loop { let tx_frame = Frame::new_data(unwrap!(StandardId::new(i as _)), &[i, 0, 1, 2, 3, 4, 5, 6]).unwrap(); - can.write(&tx_frame).await; + tx.write(&tx_frame).await; - match can.read().await { - Ok(env) => match env.frame.id() { - Id::Extended(id) => { - defmt::println!("Extended Frame id={:x} {:02x}", id.as_raw(), env.frame.data()); - } - Id::Standard(id) => { - defmt::println!("Standard Frame id={:x} {:02x}", id.as_raw(), env.frame.data()); - } - }, + match rx.read().await { + Ok(envelope) => { + handle_frame(envelope, "Buf"); + } Err(err) => { defmt::println!("Error {}", err); } diff --git a/examples/stm32g4/src/bin/can.rs b/examples/stm32g4/src/bin/can.rs index 4373a89a8..2ed632a93 100644 --- a/examples/stm32g4/src/bin/can.rs +++ b/examples/stm32g4/src/bin/can.rs @@ -36,7 +36,7 @@ async fn main(_spawner: Spawner) { } let peripherals = embassy_stm32::init(config); - let mut can = can::FdcanConfigurator::new(peripherals.FDCAN1, peripherals.PA11, peripherals.PA12, Irqs); + let mut can = can::CanConfigurator::new(peripherals.FDCAN1, peripherals.PA11, peripherals.PA12, Irqs); can.set_extended_filter( can::filter::ExtendedFilterSlot::_0, @@ -56,21 +56,22 @@ async fn main(_spawner: Spawner) { info!("Configured"); let mut can = can.start(match use_fd { - true => can::FdcanOperatingMode::InternalLoopbackMode, - false => can::FdcanOperatingMode::NormalOperationMode, + true => can::OperatingMode::InternalLoopbackMode, + false => can::OperatingMode::NormalOperationMode, }); let mut i = 0; let mut last_read_ts = embassy_time::Instant::now(); loop { - let frame = can::frame::ClassicFrame::new_extended(0x123456F, &[i; 8]).unwrap(); + let frame = can::frame::Frame::new_extended(0x123456F, &[i; 8]).unwrap(); info!("Writing frame"); _ = can.write(&frame).await; match can.read().await { - Ok((rx_frame, ts)) => { + Ok(envelope) => { + let (ts, rx_frame) = (envelope.ts, envelope.frame); let delta = (ts - last_read_ts).as_millis(); last_read_ts = ts; info!( @@ -105,7 +106,8 @@ async fn main(_spawner: Spawner) { } match can.read_fd().await { - Ok((rx_frame, ts)) => { + Ok(envelope) => { + let (ts, rx_frame) = (envelope.ts, envelope.frame); let delta = (ts - last_read_ts).as_millis(); last_read_ts = ts; info!( @@ -129,12 +131,13 @@ async fn main(_spawner: Spawner) { let (mut tx, mut rx) = can.split(); // With split loop { - let frame = can::frame::ClassicFrame::new_extended(0x123456F, &[i; 8]).unwrap(); + let frame = can::frame::Frame::new_extended(0x123456F, &[i; 8]).unwrap(); info!("Writing frame"); _ = tx.write(&frame).await; match rx.read().await { - Ok((rx_frame, ts)) => { + Ok(envelope) => { + let (ts, rx_frame) = (envelope.ts, envelope.frame); let delta = (ts - last_read_ts).as_millis(); last_read_ts = ts; info!( @@ -156,7 +159,7 @@ async fn main(_spawner: Spawner) { } } - let can = can::Fdcan::join(tx, rx); + let can = can::Can::join(tx, rx); info!("\n\n\nBuffered\n"); if use_fd { @@ -173,7 +176,8 @@ async fn main(_spawner: Spawner) { _ = can.write(frame).await; match can.read().await { - Ok((rx_frame, ts)) => { + Ok(envelope) => { + let (ts, rx_frame) = (envelope.ts, envelope.frame); let delta = (ts - last_read_ts).as_millis(); last_read_ts = ts; info!( @@ -198,7 +202,7 @@ async fn main(_spawner: Spawner) { RX_BUF.init(can::RxBuf::<10>::new()), ); loop { - let frame = can::frame::ClassicFrame::new_extended(0x123456F, &[i; 8]).unwrap(); + let frame = can::frame::Frame::new_extended(0x123456F, &[i; 8]).unwrap(); info!("Writing frame"); // You can use any of these approaches to send. The writer makes it @@ -208,7 +212,8 @@ async fn main(_spawner: Spawner) { can.writer().write(frame).await; match can.read().await { - Ok((rx_frame, ts)) => { + Ok(envelope) => { + let (ts, rx_frame) = (envelope.ts, envelope.frame); let delta = (ts - last_read_ts).as_millis(); last_read_ts = ts; info!( diff --git a/examples/stm32h5/src/bin/can.rs b/examples/stm32h5/src/bin/can.rs index 643df27f9..dd625c90a 100644 --- a/examples/stm32h5/src/bin/can.rs +++ b/examples/stm32h5/src/bin/can.rs @@ -24,7 +24,7 @@ async fn main(_spawner: Spawner) { let peripherals = embassy_stm32::init(config); - let mut can = can::FdcanConfigurator::new(peripherals.FDCAN1, peripherals.PA11, peripherals.PA12, Irqs); + let mut can = can::CanConfigurator::new(peripherals.FDCAN1, peripherals.PA11, peripherals.PA12, Irqs); // 250k bps can.set_bitrate(250_000); @@ -38,12 +38,13 @@ async fn main(_spawner: Spawner) { let mut last_read_ts = embassy_time::Instant::now(); loop { - let frame = can::frame::ClassicFrame::new_extended(0x123456F, &[i; 8]).unwrap(); + let frame = can::frame::Frame::new_extended(0x123456F, &[i; 8]).unwrap(); info!("Writing frame"); _ = can.write(&frame).await; match can.read().await { - Ok((rx_frame, ts)) => { + Ok(envelope) => { + let (rx_frame, ts) = envelope.parts(); let delta = (ts - last_read_ts).as_millis(); last_read_ts = ts; info!( @@ -69,12 +70,13 @@ async fn main(_spawner: Spawner) { let (mut tx, mut rx) = can.split(); // With split loop { - let frame = can::frame::ClassicFrame::new_extended(0x123456F, &[i; 8]).unwrap(); + let frame = can::frame::Frame::new_extended(0x123456F, &[i; 8]).unwrap(); info!("Writing frame"); _ = tx.write(&frame).await; match rx.read().await { - Ok((rx_frame, ts)) => { + Ok(envelope) => { + let (rx_frame, ts) = envelope.parts(); let delta = (ts - last_read_ts).as_millis(); last_read_ts = ts; info!( diff --git a/examples/stm32h7/src/bin/can.rs b/examples/stm32h7/src/bin/can.rs index 13a6a5051..22cb27481 100644 --- a/examples/stm32h7/src/bin/can.rs +++ b/examples/stm32h7/src/bin/can.rs @@ -24,7 +24,7 @@ async fn main(_spawner: Spawner) { let peripherals = embassy_stm32::init(config); - let mut can = can::FdcanConfigurator::new(peripherals.FDCAN1, peripherals.PA11, peripherals.PA12, Irqs); + let mut can = can::CanConfigurator::new(peripherals.FDCAN1, peripherals.PA11, peripherals.PA12, Irqs); // 250k bps can.set_bitrate(250_000); @@ -38,12 +38,13 @@ async fn main(_spawner: Spawner) { let mut last_read_ts = embassy_time::Instant::now(); loop { - let frame = can::frame::ClassicFrame::new_extended(0x123456F, &[i; 8]).unwrap(); + let frame = can::frame::Frame::new_extended(0x123456F, &[i; 8]).unwrap(); info!("Writing frame"); _ = can.write(&frame).await; match can.read().await { - Ok((rx_frame, ts)) => { + Ok(envelope) => { + let (rx_frame, ts) = envelope.parts(); let delta = (ts - last_read_ts).as_millis(); last_read_ts = ts; info!( @@ -69,12 +70,13 @@ async fn main(_spawner: Spawner) { let (mut tx, mut rx) = can.split(); // With split loop { - let frame = can::frame::ClassicFrame::new_extended(0x123456F, &[i; 8]).unwrap(); + let frame = can::frame::Frame::new_extended(0x123456F, &[i; 8]).unwrap(); info!("Writing frame"); _ = tx.write(&frame).await; match rx.read().await { - Ok((rx_frame, ts)) => { + Ok(envelope) => { + let (rx_frame, ts) = envelope.parts(); let delta = (ts - last_read_ts).as_millis(); last_read_ts = ts; info!( diff --git a/tests/stm32/src/bin/fdcan.rs b/tests/stm32/src/bin/fdcan.rs index c7373e294..bddfa7684 100644 --- a/tests/stm32/src/bin/fdcan.rs +++ b/tests/stm32/src/bin/fdcan.rs @@ -79,8 +79,8 @@ async fn main(_spawner: Spawner) { let options = options(); let peripherals = embassy_stm32::init(options.config); - let mut can = can::FdcanConfigurator::new(peripherals.FDCAN1, peripherals.PB8, peripherals.PB9, Irqs1); - let mut can2 = can::FdcanConfigurator::new(peripherals.FDCAN2, peripherals.PB12, peripherals.PB13, Irqs2); + let mut can = can::CanConfigurator::new(peripherals.FDCAN1, peripherals.PB8, peripherals.PB9, Irqs1); + let mut can2 = can::CanConfigurator::new(peripherals.FDCAN2, peripherals.PB12, peripherals.PB13, Irqs2); // 250k bps can.set_bitrate(250_000); @@ -102,13 +102,13 @@ async fn main(_spawner: Spawner) { let mut i: u8 = 0; loop { - let tx_frame = can::frame::ClassicFrame::new_standard(0x123, &[i; 1]).unwrap(); + let tx_frame = can::frame::Frame::new_standard(0x123, &[i; 1]).unwrap(); info!("Transmitting frame..."); let tx_ts = Instant::now(); can.write(&tx_frame).await; - let (frame, timestamp) = can.read().await.unwrap(); + let (frame, timestamp) = can.read().await.unwrap().parts(); info!("Frame received!"); // Check data. @@ -139,13 +139,13 @@ async fn main(_spawner: Spawner) { let mut i: u8 = 0; loop { - let tx_frame = can::frame::ClassicFrame::new_standard(0x123, &[i; 1]).unwrap(); + let tx_frame = can::frame::Frame::new_standard(0x123, &[i; 1]).unwrap(); info!("Transmitting frame..."); let tx_ts = Instant::now(); can2.write(&tx_frame).await; - let (frame, timestamp) = can2.read().await.unwrap(); + let (frame, timestamp) = can2.read().await.unwrap().parts(); info!("Frame received!"); //print_regs().await; @@ -182,20 +182,20 @@ async fn main(_spawner: Spawner) { // in each FIFO so make sure we write enough to fill them both up before reading. for i in 0..3 { // Try filling up the RX FIFO0 buffers with standard packets - let tx_frame = can::frame::ClassicFrame::new_standard(0x123, &[i; 1]).unwrap(); + let tx_frame = can::frame::Frame::new_standard(0x123, &[i; 1]).unwrap(); info!("Transmitting frame {}", i); can.write(&tx_frame).await; } for i in 3..max_buffered { // Try filling up the RX FIFO0 buffers with extended packets - let tx_frame = can::frame::ClassicFrame::new_extended(0x1232344, &[i; 1]).unwrap(); + let tx_frame = can::frame::Frame::new_extended(0x1232344, &[i; 1]).unwrap(); info!("Transmitting frame {}", i); can.write(&tx_frame).await; } // Try and receive all 6 packets for i in 0..max_buffered { - let (frame, _ts) = can.read().await.unwrap(); + let (frame, _ts) = can.read().await.unwrap().parts(); match frame.id() { embedded_can::Id::Extended(id) => { info!("Extended received! {:x} {} {}", id.as_raw(), frame.data()[0], i); @@ -210,20 +210,20 @@ async fn main(_spawner: Spawner) { let (mut tx, mut rx) = can.split(); for i in 0..3 { // Try filling up the RX FIFO0 buffers with standard packets - let tx_frame = can::frame::ClassicFrame::new_standard(0x123, &[i; 1]).unwrap(); + let tx_frame = can::frame::Frame::new_standard(0x123, &[i; 1]).unwrap(); info!("Transmitting frame {}", i); tx.write(&tx_frame).await; } for i in 3..max_buffered { // Try filling up the RX FIFO0 buffers with extended packets - let tx_frame = can::frame::ClassicFrame::new_extended(0x1232344, &[i; 1]).unwrap(); + let tx_frame = can::frame::Frame::new_extended(0x1232344, &[i; 1]).unwrap(); info!("Transmitting frame {}", i); tx.write(&tx_frame).await; } // Try and receive all 6 packets for i in 0..max_buffered { - let (frame, _ts) = rx.read().await.unwrap(); + let (frame, _ts) = rx.read().await.unwrap().parts(); match frame.id() { embedded_can::Id::Extended(id) => { info!("Extended received! {:x} {} {}", id.as_raw(), frame.data()[0], i); From 8d43fb4da4712dd9bb5a2ae343168e536c2b3129 Mon Sep 17 00:00:00 2001 From: Corey Schuhen <cschuhen@gmail.com> Date: Wed, 27 Mar 2024 19:43:19 +1000 Subject: [PATCH 11/14] CAN: Use the same testing code for BXCAN and FDCAN h/w. --- tests/stm32/src/bin/can.rs | 50 +++----- tests/stm32/src/bin/can_common.rs | 112 +++++++++++++++++ tests/stm32/src/bin/fdcan.rs | 195 ++++++------------------------ 3 files changed, 163 insertions(+), 194 deletions(-) create mode 100644 tests/stm32/src/bin/can_common.rs diff --git a/tests/stm32/src/bin/can.rs b/tests/stm32/src/bin/can.rs index c08c69a3b..74d84c42f 100644 --- a/tests/stm32/src/bin/can.rs +++ b/tests/stm32/src/bin/can.rs @@ -6,17 +6,19 @@ #[path = "../common.rs"] mod common; use common::*; -use defmt::assert; use embassy_executor::Spawner; use embassy_stm32::bind_interrupts; use embassy_stm32::can::bx::filter::Mask32; -use embassy_stm32::can::bx::{Fifo, Frame, StandardId}; +use embassy_stm32::can::bx::Fifo; use embassy_stm32::can::{Can, Rx0InterruptHandler, Rx1InterruptHandler, SceInterruptHandler, TxInterruptHandler}; use embassy_stm32::gpio::{Input, Pull}; use embassy_stm32::peripherals::CAN1; -use embassy_time::{Duration, Instant}; +use embassy_time::Duration; use {defmt_rtt as _, panic_probe as _}; +mod can_common; +use can_common::*; + bind_interrupts!(struct Irqs { CAN1_RX0 => Rx0InterruptHandler<CAN1>; CAN1_RX1 => Rx1InterruptHandler<CAN1>; @@ -29,6 +31,11 @@ async fn main(_spawner: Spawner) { let p = embassy_stm32::init(config()); info!("Hello World!"); + let options = TestOptions { + max_latency: Duration::from_micros(1200), + max_buffered: 2, + }; + let can = peri!(p, CAN); let tx = peri!(p, CAN_TX); let mut rx = peri!(p, CAN_RX); @@ -58,40 +65,13 @@ async fn main(_spawner: Spawner) { info!("Can configured"); - let mut i: u8 = 0; - loop { - let tx_frame = Frame::new_data(unwrap!(StandardId::new(i as _)), &[i]).unwrap(); + run_can_tests(&mut can, &options).await; - info!("Transmitting frame..."); - let tx_ts = Instant::now(); - can.write(&tx_frame).await; - - let envelope = can.read().await.unwrap(); - info!("Frame received!"); - - info!("loopback time {}", envelope.ts); - info!("loopback frame {=u8}", envelope.frame.data()[0]); - - let latency = envelope.ts.saturating_duration_since(tx_ts); - info!("loopback latency {} us", latency.as_micros()); - - // Theoretical minimum latency is 55us, actual is usually ~80us - const MIN_LATENCY: Duration = Duration::from_micros(50); - const MAX_LATENCY: Duration = Duration::from_micros(150); - assert!( - MIN_LATENCY <= latency && latency <= MAX_LATENCY, - "{} <= {} <= {}", - MIN_LATENCY, - latency, - MAX_LATENCY - ); - - i += 1; - if i > 10 { - break; - } - } + // Test again with a split + let (mut tx, mut rx) = can.split(); + run_split_can_tests(&mut tx, &mut rx, &options).await; info!("Test OK"); + cortex_m::asm::bkpt(); } diff --git a/tests/stm32/src/bin/can_common.rs b/tests/stm32/src/bin/can_common.rs new file mode 100644 index 000000000..4b39269cc --- /dev/null +++ b/tests/stm32/src/bin/can_common.rs @@ -0,0 +1,112 @@ +use defmt::{assert, *}; +use embassy_stm32::can; +use embassy_time::{Duration, Instant}; + +#[derive(Clone, Copy, Debug)] +pub struct TestOptions { + pub max_latency: Duration, + pub max_buffered: u8, +} + +pub async fn run_can_tests<'d, T: can::Instance>(can: &mut can::Can<'d, T>, options: &TestOptions) { + let mut i: u8 = 0; + loop { + //let tx_frame = can::frame::Frame::new_standard(0x123, &[i, 0x12 as u8, 0x34 as u8, 0x56 as u8, 0x78 as u8, 0x9A as u8, 0xBC as u8 ]).unwrap(); + let tx_frame = can::frame::Frame::new_standard(0x123, &[i; 1]).unwrap(); + + //info!("Transmitting frame..."); + let tx_ts = Instant::now(); + can.write(&tx_frame).await; + + let (frame, timestamp) = can.read().await.unwrap().parts(); + //info!("Frame received!"); + + // Check data. + assert!(i == frame.data()[0], "{} == {}", i, frame.data()[0]); + + //info!("loopback time {}", timestamp); + //info!("loopback frame {=u8}", frame.data()[0]); + let latency = timestamp.saturating_duration_since(tx_ts); + info!("loopback latency {} us", latency.as_micros()); + + // Theoretical minimum latency is 55us, actual is usually ~80us + const MIN_LATENCY: Duration = Duration::from_micros(50); + // Was failing at 150 but we are not getting a real time stamp. I'm not + // sure if there are other delays + assert!( + MIN_LATENCY <= latency && latency <= options.max_latency, + "{} <= {} <= {}", + MIN_LATENCY, + latency, + options.max_latency + ); + + i += 1; + if i > 5 { + break; + } + } + + // Below here, check that we can receive from both FIFO0 and FIFO1 + // Above we configured FIFO1 for extended ID packets. There are only 3 slots + // in each FIFO so make sure we write enough to fill them both up before reading. + for i in 0..options.max_buffered { + // Try filling up the RX FIFO0 buffers + //let tx_frame = if 0 != (i & 0x01) { + let tx_frame = if i < options.max_buffered / 2 { + info!("Transmitting standard frame {}", i); + can::frame::Frame::new_standard(0x123, &[i; 1]).unwrap() + } else { + info!("Transmitting extended frame {}", i); + can::frame::Frame::new_extended(0x1232344, &[i; 1]).unwrap() + }; + can.write(&tx_frame).await; + } + + // Try and receive all 6 packets + for _i in 0..options.max_buffered { + let (frame, _ts) = can.read().await.unwrap().parts(); + match frame.id() { + embedded_can::Id::Extended(_id) => { + info!("Extended received! {}", frame.data()[0]); + //info!("Extended received! {:x} {} {}", id.as_raw(), frame.data()[0], i); + } + embedded_can::Id::Standard(_id) => { + info!("Standard received! {}", frame.data()[0]); + //info!("Standard received! {:x} {} {}", id.as_raw(), frame.data()[0], i); + } + } + } +} + +pub async fn run_split_can_tests<'d, T: can::Instance>( + tx: &mut can::CanTx<'d, T>, + rx: &mut can::CanRx<'d, T>, + options: &TestOptions, +) { + for i in 0..options.max_buffered { + // Try filling up the RX FIFO0 buffers + //let tx_frame = if 0 != (i & 0x01) { + let tx_frame = if i < options.max_buffered / 2 { + info!("Transmitting standard frame {}", i); + can::frame::Frame::new_standard(0x123, &[i; 1]).unwrap() + } else { + info!("Transmitting extended frame {}", i); + can::frame::Frame::new_extended(0x1232344, &[i; 1]).unwrap() + }; + tx.write(&tx_frame).await; + } + + // Try and receive all 6 packets + for _i in 0..options.max_buffered { + let (frame, _ts) = rx.read().await.unwrap().parts(); + match frame.id() { + embedded_can::Id::Extended(_id) => { + info!("Extended received! {}", frame.data()[0]); + } + embedded_can::Id::Standard(_id) => { + info!("Standard received! {}", frame.data()[0]); + } + } + } +} diff --git a/tests/stm32/src/bin/fdcan.rs b/tests/stm32/src/bin/fdcan.rs index bddfa7684..27bdd038a 100644 --- a/tests/stm32/src/bin/fdcan.rs +++ b/tests/stm32/src/bin/fdcan.rs @@ -6,13 +6,15 @@ #[path = "../common.rs"] mod common; use common::*; -use defmt::assert; use embassy_executor::Spawner; use embassy_stm32::peripherals::*; use embassy_stm32::{bind_interrupts, can, Config}; -use embassy_time::{Duration, Instant}; +use embassy_time::Duration; use {defmt_rtt as _, panic_probe as _}; +mod can_common; +use can_common::*; + bind_interrupts!(struct Irqs2 { FDCAN2_IT0 => can::IT0InterruptHandler<FDCAN2>; FDCAN2_IT1 => can::IT1InterruptHandler<FDCAN2>; @@ -22,14 +24,8 @@ bind_interrupts!(struct Irqs1 { FDCAN1_IT1 => can::IT1InterruptHandler<FDCAN1>; }); -struct TestOptions { - config: Config, - max_latency: Duration, - second_fifo_working: bool, -} - #[cfg(any(feature = "stm32h755zi", feature = "stm32h753zi", feature = "stm32h563zi"))] -fn options() -> TestOptions { +fn options() -> (Config, TestOptions) { use embassy_stm32::rcc; info!("H75 config"); let mut c = config(); @@ -38,15 +34,17 @@ fn options() -> TestOptions { mode: rcc::HseMode::Oscillator, }); c.rcc.mux.fdcansel = rcc::mux::Fdcansel::HSE; - TestOptions { - config: c, - max_latency: Duration::from_micros(1200), - second_fifo_working: false, - } + ( + c, + TestOptions { + max_latency: Duration::from_micros(1200), + max_buffered: 3, + }, + ) } #[cfg(any(feature = "stm32h7a3zi"))] -fn options() -> TestOptions { +fn options() -> (Config, TestOptions) { use embassy_stm32::rcc; info!("H7a config"); let mut c = config(); @@ -55,29 +53,33 @@ fn options() -> TestOptions { mode: rcc::HseMode::Oscillator, }); c.rcc.mux.fdcansel = rcc::mux::Fdcansel::HSE; - TestOptions { - config: c, - max_latency: Duration::from_micros(1200), - second_fifo_working: false, - } + ( + c, + TestOptions { + max_latency: Duration::from_micros(1200), + max_buffered: 3, + }, + ) } #[cfg(any(feature = "stm32g491re", feature = "stm32g431cb"))] -fn options() -> TestOptions { +fn options() -> (Config, TestOptions) { info!("G4 config"); - TestOptions { - config: config(), - max_latency: Duration::from_micros(500), - second_fifo_working: true, - } + ( + config(), + TestOptions { + max_latency: Duration::from_micros(500), + max_buffered: 6, + }, + ) } #[embassy_executor::main] async fn main(_spawner: Spawner) { //let peripherals = embassy_stm32::init(config()); - let options = options(); - let peripherals = embassy_stm32::init(options.config); + let (config, options) = options(); + let peripherals = embassy_stm32::init(config); let mut can = can::CanConfigurator::new(peripherals.FDCAN1, peripherals.PB8, peripherals.PB9, Irqs1); let mut can2 = can::CanConfigurator::new(peripherals.FDCAN2, peripherals.PB12, peripherals.PB13, Irqs2); @@ -98,141 +100,16 @@ async fn main(_spawner: Spawner) { let mut can = can.into_internal_loopback_mode(); let mut can2 = can2.into_internal_loopback_mode(); + run_can_tests(&mut can, &options).await; + run_can_tests(&mut can2, &options).await; + info!("CAN Configured"); - let mut i: u8 = 0; - loop { - let tx_frame = can::frame::Frame::new_standard(0x123, &[i; 1]).unwrap(); - - info!("Transmitting frame..."); - let tx_ts = Instant::now(); - can.write(&tx_frame).await; - - let (frame, timestamp) = can.read().await.unwrap().parts(); - info!("Frame received!"); - - // Check data. - assert!(i == frame.data()[0], "{} == {}", i, frame.data()[0]); - - info!("loopback time {}", timestamp); - info!("loopback frame {=u8}", frame.data()[0]); - let latency = timestamp.saturating_duration_since(tx_ts); - info!("loopback latency {} us", latency.as_micros()); - - // Theoretical minimum latency is 55us, actual is usually ~80us - const MIN_LATENCY: Duration = Duration::from_micros(50); - // Was failing at 150 but we are not getting a real time stamp. I'm not - // sure if there are other delays - assert!( - MIN_LATENCY <= latency && latency <= options.max_latency, - "{} <= {} <= {}", - MIN_LATENCY, - latency, - options.max_latency - ); - - i += 1; - if i > 10 { - break; - } - } - - let mut i: u8 = 0; - loop { - let tx_frame = can::frame::Frame::new_standard(0x123, &[i; 1]).unwrap(); - - info!("Transmitting frame..."); - let tx_ts = Instant::now(); - can2.write(&tx_frame).await; - - let (frame, timestamp) = can2.read().await.unwrap().parts(); - info!("Frame received!"); - - //print_regs().await; - // Check data. - assert!(i == frame.data()[0], "{} == {}", i, frame.data()[0]); - - info!("loopback time {}", timestamp); - info!("loopback frame {=u8}", frame.data()[0]); - let latency = timestamp.saturating_duration_since(tx_ts); - info!("loopback latency {} us", latency.as_micros()); - - // Theoretical minimum latency is 55us, actual is usually ~80us - const MIN_LATENCY: Duration = Duration::from_micros(50); - // Was failing at 150 but we are not getting a real time stamp. I'm not - // sure if there are other delays - assert!( - MIN_LATENCY <= latency && latency <= options.max_latency, - "{} <= {} <= {}", - MIN_LATENCY, - latency, - options.max_latency - ); - - i += 1; - if i > 10 { - break; - } - } - - let max_buffered = if options.second_fifo_working { 6 } else { 3 }; - - // Below here, check that we can receive from both FIFO0 and FIFO0 - // Above we configured FIFO1 for extended ID packets. There are only 3 slots - // in each FIFO so make sure we write enough to fill them both up before reading. - for i in 0..3 { - // Try filling up the RX FIFO0 buffers with standard packets - let tx_frame = can::frame::Frame::new_standard(0x123, &[i; 1]).unwrap(); - info!("Transmitting frame {}", i); - can.write(&tx_frame).await; - } - for i in 3..max_buffered { - // Try filling up the RX FIFO0 buffers with extended packets - let tx_frame = can::frame::Frame::new_extended(0x1232344, &[i; 1]).unwrap(); - info!("Transmitting frame {}", i); - can.write(&tx_frame).await; - } - - // Try and receive all 6 packets - for i in 0..max_buffered { - let (frame, _ts) = can.read().await.unwrap().parts(); - match frame.id() { - embedded_can::Id::Extended(id) => { - info!("Extended received! {:x} {} {}", id.as_raw(), frame.data()[0], i); - } - embedded_can::Id::Standard(id) => { - info!("Standard received! {:x} {} {}", id.as_raw(), frame.data()[0], i); - } - } - } - // Test again with a split let (mut tx, mut rx) = can.split(); - for i in 0..3 { - // Try filling up the RX FIFO0 buffers with standard packets - let tx_frame = can::frame::Frame::new_standard(0x123, &[i; 1]).unwrap(); - info!("Transmitting frame {}", i); - tx.write(&tx_frame).await; - } - for i in 3..max_buffered { - // Try filling up the RX FIFO0 buffers with extended packets - let tx_frame = can::frame::Frame::new_extended(0x1232344, &[i; 1]).unwrap(); - info!("Transmitting frame {}", i); - tx.write(&tx_frame).await; - } - - // Try and receive all 6 packets - for i in 0..max_buffered { - let (frame, _ts) = rx.read().await.unwrap().parts(); - match frame.id() { - embedded_can::Id::Extended(id) => { - info!("Extended received! {:x} {} {}", id.as_raw(), frame.data()[0], i); - } - embedded_can::Id::Standard(id) => { - info!("Standard received! {:x} {} {}", id.as_raw(), frame.data()[0], i); - } - } - } + let (mut tx2, mut rx2) = can2.split(); + run_split_can_tests(&mut tx, &mut rx, &options).await; + run_split_can_tests(&mut tx2, &mut rx2, &options).await; info!("Test OK"); cortex_m::asm::bkpt(); From a9f0c8c3a941684c1a610a72dfb2b925535fc8cd Mon Sep 17 00:00:00 2001 From: Corey Schuhen <cschuhen@gmail.com> Date: Wed, 27 Mar 2024 20:29:28 +1000 Subject: [PATCH 12/14] Fixes for no-time. --- embassy-stm32/src/can/bx/mod.rs | 13 +++++++------ embassy-stm32/src/can/frame.rs | 19 +++++++++++++------ 2 files changed, 20 insertions(+), 12 deletions(-) diff --git a/embassy-stm32/src/can/bx/mod.rs b/embassy-stm32/src/can/bx/mod.rs index 253bcee13..cb83799d3 100644 --- a/embassy-stm32/src/can/bx/mod.rs +++ b/embassy-stm32/src/can/bx/mod.rs @@ -622,17 +622,18 @@ impl Registers { let id = (stid << 18) | (exid); embedded_can::ExtendedId::new(id).unwrap().into() }; - let data_len = fifo.rdtr().read().dlc(); + let rdtr = fifo.rdtr().read(); + let data_len = rdtr.dlc(); + + #[cfg(not(feature = "time"))] + let ts = rdtr.time(); + let mut data: [u8; 8] = [0; 8]; data[0..4].copy_from_slice(&fifo.rdlr().read().0.to_ne_bytes()); data[4..8].copy_from_slice(&fifo.rdhr().read().0.to_ne_bytes()); let frame = Frame::new(Header::new(id, data_len, false), &data).unwrap(); - let envelope = Envelope { - #[cfg(feature = "time")] - ts, - frame, - }; + let envelope = Envelope { ts, frame }; rfr.modify(|v| v.set_rfom(true)); diff --git a/embassy-stm32/src/can/frame.rs b/embassy-stm32/src/can/frame.rs index fb032aee2..d2d1f7aa6 100644 --- a/embassy-stm32/src/can/frame.rs +++ b/embassy-stm32/src/can/frame.rs @@ -3,6 +3,14 @@ use bit_field::BitField; use crate::can::enums::FrameCreateError; +/// Calculate proper timestamp when available. +#[cfg(feature = "time")] +pub type Timestamp = embassy_time::Instant; + +/// Raw register timestamp +#[cfg(not(feature = "time"))] +pub type Timestamp = u16; + /// CAN Header, without meta data #[derive(Debug, Copy, Clone)] pub struct Header { @@ -264,15 +272,14 @@ impl CanHeader for Frame { #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct Envelope { /// Reception time. - #[cfg(feature = "time")] - pub ts: embassy_time::Instant, + pub ts: Timestamp, /// The actual CAN frame. pub frame: Frame, } impl Envelope { /// Convert into a tuple - pub fn parts(self) -> (Frame, embassy_time::Instant) { + pub fn parts(self) -> (Frame, Timestamp) { (self.frame, self.ts) } } @@ -442,15 +449,15 @@ impl CanHeader for FdFrame { #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct FdEnvelope { /// Reception time. - #[cfg(feature = "time")] - pub ts: embassy_time::Instant, + pub ts: Timestamp, + /// The actual CAN frame. pub frame: FdFrame, } impl FdEnvelope { /// Convert into a tuple - pub fn parts(self) -> (FdFrame, embassy_time::Instant) { + pub fn parts(self) -> (FdFrame, Timestamp) { (self.frame, self.ts) } } From 25618cd93d5209d864fa250b2077a59532e0bedf Mon Sep 17 00:00:00 2001 From: Corey Schuhen <cschuhen@gmail.com> Date: Thu, 28 Mar 2024 09:47:16 +1000 Subject: [PATCH 13/14] RTR fix. --- embassy-stm32/src/can/bx/mod.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/embassy-stm32/src/can/bx/mod.rs b/embassy-stm32/src/can/bx/mod.rs index cb83799d3..cd82148ba 100644 --- a/embassy-stm32/src/can/bx/mod.rs +++ b/embassy-stm32/src/can/bx/mod.rs @@ -624,6 +624,7 @@ impl Registers { }; let rdtr = fifo.rdtr().read(); let data_len = rdtr.dlc(); + let rtr = rir.rtr() == stm32_metapac::can::vals::Rtr::REMOTE; #[cfg(not(feature = "time"))] let ts = rdtr.time(); @@ -632,7 +633,7 @@ impl Registers { data[0..4].copy_from_slice(&fifo.rdlr().read().0.to_ne_bytes()); data[4..8].copy_from_slice(&fifo.rdhr().read().0.to_ne_bytes()); - let frame = Frame::new(Header::new(id, data_len, false), &data).unwrap(); + let frame = Frame::new(Header::new(id, data_len, rtr), &data).unwrap(); let envelope = Envelope { ts, frame }; rfr.modify(|v| v.set_rfom(true)); From 2ea1040e073a54d2a97f75c7d68d0d9a230d15c8 Mon Sep 17 00:00:00 2001 From: Frostie314159 <iam.an.programmer@gmail.com> Date: Fri, 22 Mar 2024 08:50:56 +0100 Subject: [PATCH 14/14] Adjusted behavior. --- embassy-time/src/timer.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/embassy-time/src/timer.rs b/embassy-time/src/timer.rs index 763bfdeeb..d6d0a46e2 100644 --- a/embassy-time/src/timer.rs +++ b/embassy-time/src/timer.rs @@ -190,10 +190,10 @@ impl Ticker { self.expires_at = Instant::now() + self.duration; } - /// Reset the ticker to fire for the next time on the deadline. + /// Reset the ticker at the deadline. /// If the deadline is in the past, the ticker will fire instantly. pub fn reset_at(&mut self, deadline: Instant) { - self.expires_at = deadline; + self.expires_at = deadline + self.duration; } /// Resets the ticker, after the specified duration has passed.