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.