aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--embassy-stm32/Cargo.toml2
-rw-r--r--embassy-stm32/src/can/bxcan.rs39
-rw-r--r--examples/stm32f4/src/bin/can.rs18
-rw-r--r--examples/stm32f7/src/bin/can.rs4
-rw-r--r--tests/stm32/src/bin/can.rs27
5 files changed, 71 insertions, 19 deletions
diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml
index a1323e85b..8c7dd38c2 100644
--- a/embassy-stm32/Cargo.toml
+++ b/embassy-stm32/Cargo.toml
@@ -84,7 +84,7 @@ default = ["rt"]
84rt = ["stm32-metapac/rt"] 84rt = ["stm32-metapac/rt"]
85 85
86## Use [`defmt`](https://docs.rs/defmt/latest/defmt/) for logging 86## Use [`defmt`](https://docs.rs/defmt/latest/defmt/) for logging
87defmt = ["dep:defmt", "bxcan/unstable-defmt", "embassy-sync/defmt", "embassy-embedded-hal/defmt", "embassy-hal-internal/defmt", "embedded-io?/defmt", "embassy-usb-driver?/defmt", "embassy-net-driver/defmt"] 87defmt = ["dep:defmt", "bxcan/unstable-defmt", "embassy-sync/defmt", "embassy-embedded-hal/defmt", "embassy-hal-internal/defmt", "embedded-io?/defmt", "embassy-usb-driver?/defmt", "embassy-net-driver/defmt", "embassy-time?/defmt"]
88 88
89exti = [] 89exti = []
90 90
diff --git a/embassy-stm32/src/can/bxcan.rs b/embassy-stm32/src/can/bxcan.rs
index 1d0fdd532..a3e3ec860 100644
--- a/embassy-stm32/src/can/bxcan.rs
+++ b/embassy-stm32/src/can/bxcan.rs
@@ -16,6 +16,17 @@ use crate::rcc::RccPeripheral;
16use crate::time::Hertz; 16use crate::time::Hertz;
17use crate::{interrupt, peripherals, Peripheral}; 17use crate::{interrupt, peripherals, Peripheral};
18 18
19/// Contains CAN frame and additional metadata.
20///
21/// Timestamp is available if `time` feature is enabled.
22#[derive(Debug, Clone, PartialEq, Eq)]
23#[cfg_attr(feature = "defmt", derive(defmt::Format))]
24pub struct Envelope {
25 #[cfg(feature = "time")]
26 pub ts: embassy_time::Instant,
27 pub frame: bxcan::Frame,
28}
29
19/// Interrupt handler. 30/// Interrupt handler.
20pub struct TxInterruptHandler<T: Instance> { 31pub struct TxInterruptHandler<T: Instance> {
21 _phantom: PhantomData<T>, 32 _phantom: PhantomData<T>,
@@ -218,14 +229,14 @@ impl<'d, T: Instance> Can<'d, T> {
218 } 229 }
219 230
220 /// Returns a tuple of the time the message was received and the message frame 231 /// Returns a tuple of the time the message was received and the message frame
221 pub async fn read(&mut self) -> Result<(u16, bxcan::Frame), BusError> { 232 pub async fn read(&mut self) -> Result<Envelope, BusError> {
222 CanRx { can: &self.can }.read().await 233 CanRx { can: &self.can }.read().await
223 } 234 }
224 235
225 /// Attempts to read a can frame without blocking. 236 /// Attempts to read a can frame without blocking.
226 /// 237 ///
227 /// Returns [Err(TryReadError::Empty)] if there are no frames in the rx queue. 238 /// Returns [Err(TryReadError::Empty)] if there are no frames in the rx queue.
228 pub fn try_read(&mut self) -> Result<(u16, bxcan::Frame), TryReadError> { 239 pub fn try_read(&mut self) -> Result<Envelope, TryReadError> {
229 CanRx { can: &self.can }.try_read() 240 CanRx { can: &self.can }.try_read()
230 } 241 }
231 242
@@ -235,6 +246,10 @@ impl<'d, T: Instance> Can<'d, T> {
235 } 246 }
236 247
237 unsafe fn receive_fifo(fifo: RxFifo) { 248 unsafe fn receive_fifo(fifo: RxFifo) {
249 // Generate timestamp as early as possible
250 #[cfg(feature = "time")]
251 let ts = embassy_time::Instant::now();
252
238 let state = T::state(); 253 let state = T::state();
239 let regs = T::regs(); 254 let regs = T::regs();
240 let fifo_idx = match fifo { 255 let fifo_idx = match fifo {
@@ -264,15 +279,19 @@ impl<'d, T: Instance> Can<'d, T> {
264 data[0..4].copy_from_slice(&fifo.rdlr().read().0.to_ne_bytes()); 279 data[0..4].copy_from_slice(&fifo.rdlr().read().0.to_ne_bytes());
265 data[4..8].copy_from_slice(&fifo.rdhr().read().0.to_ne_bytes()); 280 data[4..8].copy_from_slice(&fifo.rdhr().read().0.to_ne_bytes());
266 281
267 let time = fifo.rdtr().read().time();
268 let frame = Frame::new_data(id, Data::new(&data[0..data_len]).unwrap()); 282 let frame = Frame::new_data(id, Data::new(&data[0..data_len]).unwrap());
283 let envelope = Envelope {
284 #[cfg(feature = "time")]
285 ts,
286 frame,
287 };
269 288
270 rfr.modify(|v| v.set_rfom(true)); 289 rfr.modify(|v| v.set_rfom(true));
271 290
272 /* 291 /*
273 NOTE: consensus was reached that if rx_queue is full, packets should be dropped 292 NOTE: consensus was reached that if rx_queue is full, packets should be dropped
274 */ 293 */
275 let _ = state.rx_queue.try_send((time, frame)); 294 let _ = state.rx_queue.try_send(envelope);
276 } 295 }
277 } 296 }
278 297
@@ -456,11 +475,11 @@ pub struct CanRx<'c, 'd, T: Instance> {
456} 475}
457 476
458impl<'c, 'd, T: Instance> CanRx<'c, 'd, T> { 477impl<'c, 'd, T: Instance> CanRx<'c, 'd, T> {
459 pub async fn read(&mut self) -> Result<(u16, bxcan::Frame), BusError> { 478 pub async fn read(&mut self) -> Result<Envelope, BusError> {
460 poll_fn(|cx| { 479 poll_fn(|cx| {
461 T::state().err_waker.register(cx.waker()); 480 T::state().err_waker.register(cx.waker());
462 if let Poll::Ready((time, frame)) = T::state().rx_queue.recv().poll_unpin(cx) { 481 if let Poll::Ready(envelope) = T::state().rx_queue.recv().poll_unpin(cx) {
463 return Poll::Ready(Ok((time, frame))); 482 return Poll::Ready(Ok(envelope));
464 } else if let Some(err) = self.curr_error() { 483 } else if let Some(err) = self.curr_error() {
465 return Poll::Ready(Err(err)); 484 return Poll::Ready(Err(err));
466 } 485 }
@@ -473,7 +492,7 @@ impl<'c, 'd, T: Instance> CanRx<'c, 'd, T> {
473 /// Attempts to read a CAN frame without blocking. 492 /// Attempts to read a CAN frame without blocking.
474 /// 493 ///
475 /// Returns [Err(TryReadError::Empty)] if there are no frames in the rx queue. 494 /// Returns [Err(TryReadError::Empty)] if there are no frames in the rx queue.
476 pub fn try_read(&mut self) -> Result<(u16, bxcan::Frame), TryReadError> { 495 pub fn try_read(&mut self) -> Result<Envelope, TryReadError> {
477 if let Ok(envelope) = T::state().rx_queue.try_recv() { 496 if let Ok(envelope) = T::state().rx_queue.try_recv() {
478 return Ok(envelope); 497 return Ok(envelope);
479 } 498 }
@@ -545,10 +564,12 @@ pub(crate) mod sealed {
545 use embassy_sync::channel::Channel; 564 use embassy_sync::channel::Channel;
546 use embassy_sync::waitqueue::AtomicWaker; 565 use embassy_sync::waitqueue::AtomicWaker;
547 566
567 use super::Envelope;
568
548 pub struct State { 569 pub struct State {
549 pub tx_waker: AtomicWaker, 570 pub tx_waker: AtomicWaker,
550 pub err_waker: AtomicWaker, 571 pub err_waker: AtomicWaker,
551 pub rx_queue: Channel<CriticalSectionRawMutex, (u16, bxcan::Frame), 32>, 572 pub rx_queue: Channel<CriticalSectionRawMutex, Envelope, 32>,
552 } 573 }
553 574
554 impl State { 575 impl State {
diff --git a/examples/stm32f4/src/bin/can.rs b/examples/stm32f4/src/bin/can.rs
index f84f74d30..20ce4edce 100644
--- a/examples/stm32f4/src/bin/can.rs
+++ b/examples/stm32f4/src/bin/can.rs
@@ -10,6 +10,7 @@ use embassy_stm32::can::bxcan::{Fifo, Frame, StandardId};
10use embassy_stm32::can::{Can, Rx0InterruptHandler, Rx1InterruptHandler, SceInterruptHandler, TxInterruptHandler}; 10use embassy_stm32::can::{Can, Rx0InterruptHandler, Rx1InterruptHandler, SceInterruptHandler, TxInterruptHandler};
11use embassy_stm32::gpio::{Input, Pull}; 11use embassy_stm32::gpio::{Input, Pull};
12use embassy_stm32::peripherals::CAN1; 12use embassy_stm32::peripherals::CAN1;
13use embassy_time::Instant;
13use {defmt_rtt as _, panic_probe as _}; 14use {defmt_rtt as _, panic_probe as _};
14 15
15bind_interrupts!(struct Irqs { 16bind_interrupts!(struct Irqs {
@@ -51,9 +52,22 @@ async fn main(_spawner: Spawner) {
51 let mut i: u8 = 0; 52 let mut i: u8 = 0;
52 loop { 53 loop {
53 let tx_frame = Frame::new_data(unwrap!(StandardId::new(i as _)), [i]); 54 let tx_frame = Frame::new_data(unwrap!(StandardId::new(i as _)), [i]);
55 let tx_ts = Instant::now();
54 can.write(&tx_frame).await; 56 can.write(&tx_frame).await;
55 let (_, rx_frame) = can.read().await.unwrap(); 57
56 info!("loopback frame {=u8}", unwrap!(rx_frame.data())[0]); 58 let envelope = can.read().await.unwrap();
59
60 // We can measure loopback latency by using receive timestamp in the `Envelope`.
61 // Our frame is ~55 bits long (exlcuding bit stuffing), so at 1mbps loopback delay is at least 55 us.
62 // When measured with `tick-hz-1_000_000` actual latency is 80~83 us, giving a combined hardware and software
63 // overhead of ~25 us. Note that CPU frequency can greatly affect the result.
64 let latency = envelope.ts.saturating_duration_since(tx_ts);
65
66 info!(
67 "loopback frame {=u8}, latency: {} us",
68 unwrap!(envelope.frame.data())[0],
69 latency.as_micros()
70 );
57 i += 1; 71 i += 1;
58 } 72 }
59} 73}
diff --git a/examples/stm32f7/src/bin/can.rs b/examples/stm32f7/src/bin/can.rs
index 1b5b377ea..e9650f23a 100644
--- a/examples/stm32f7/src/bin/can.rs
+++ b/examples/stm32f7/src/bin/can.rs
@@ -60,7 +60,7 @@ async fn main(spawner: Spawner) {
60 spawner.spawn(send_can_message(tx)).unwrap(); 60 spawner.spawn(send_can_message(tx)).unwrap();
61 61
62 loop { 62 loop {
63 let frame = rx.read().await.unwrap(); 63 let envelope = rx.read().await.unwrap();
64 println!("Received: {:?}", frame); 64 println!("Received: {:?}", envelope);
65 } 65 }
66} 66}
diff --git a/tests/stm32/src/bin/can.rs b/tests/stm32/src/bin/can.rs
index 8bdd3c24f..8737ca8ee 100644
--- a/tests/stm32/src/bin/can.rs
+++ b/tests/stm32/src/bin/can.rs
@@ -7,6 +7,7 @@
7#[path = "../common.rs"] 7#[path = "../common.rs"]
8mod common; 8mod common;
9use common::*; 9use common::*;
10use defmt::assert;
10use embassy_executor::Spawner; 11use embassy_executor::Spawner;
11use embassy_stm32::bind_interrupts; 12use embassy_stm32::bind_interrupts;
12use embassy_stm32::can::bxcan::filter::Mask32; 13use embassy_stm32::can::bxcan::filter::Mask32;
@@ -14,6 +15,7 @@ use embassy_stm32::can::bxcan::{Fifo, Frame, StandardId};
14use embassy_stm32::can::{Can, Rx0InterruptHandler, Rx1InterruptHandler, SceInterruptHandler, TxInterruptHandler}; 15use embassy_stm32::can::{Can, Rx0InterruptHandler, Rx1InterruptHandler, SceInterruptHandler, TxInterruptHandler};
15use embassy_stm32::gpio::{Input, Pull}; 16use embassy_stm32::gpio::{Input, Pull};
16use embassy_stm32::peripherals::CAN1; 17use embassy_stm32::peripherals::CAN1;
18use embassy_time::{Duration, Instant};
17use {defmt_rtt as _, panic_probe as _}; 19use {defmt_rtt as _, panic_probe as _};
18 20
19bind_interrupts!(struct Irqs { 21bind_interrupts!(struct Irqs {
@@ -62,13 +64,28 @@ async fn main(_spawner: Spawner) {
62 let tx_frame = Frame::new_data(unwrap!(StandardId::new(i as _)), [i]); 64 let tx_frame = Frame::new_data(unwrap!(StandardId::new(i as _)), [i]);
63 65
64 info!("Transmitting frame..."); 66 info!("Transmitting frame...");
67 let tx_ts = Instant::now();
65 can.write(&tx_frame).await; 68 can.write(&tx_frame).await;
66 69
67 info!("Receiving frame..."); 70 let envelope = can.read().await.unwrap();
68 let (time, rx_frame) = can.read().await.unwrap(); 71 info!("Frame received!");
69 72
70 info!("loopback time {}", time); 73 info!("loopback time {}", envelope.ts);
71 info!("loopback frame {=u8}", rx_frame.data().unwrap()[0]); 74 info!("loopback frame {=u8}", envelope.frame.data().unwrap()[0]);
75
76 let latency = envelope.ts.saturating_duration_since(tx_ts);
77 info!("loopback latency {} us", latency.as_micros());
78
79 // Theoretical minimum latency is 55us, actual is usually ~80us
80 const MIN_LATENCY: Duration = Duration::from_micros(50);
81 const MAX_LATENCY: Duration = Duration::from_micros(150);
82 assert!(
83 MIN_LATENCY < latency && latency < MAX_LATENCY,
84 "{} < {} < {}",
85 MIN_LATENCY,
86 latency,
87 MAX_LATENCY
88 );
72 89
73 i += 1; 90 i += 1;
74 if i > 10 { 91 if i > 10 {