diff --git a/embassy-stm32/src/can/bxcan.rs b/embassy-stm32/src/can/bxcan.rs index 73861776..5a015346 100644 --- a/embassy-stm32/src/can/bxcan.rs +++ b/embassy-stm32/src/can/bxcan.rs @@ -1,3 +1,4 @@ +use core::cell::{RefCell, RefMut}; use core::future::poll_fn; use core::marker::PhantomData; use core::ops::{Deref, DerefMut}; @@ -72,7 +73,7 @@ impl interrupt::typelevel::Handler for SceInterrup } pub struct Can<'d, T: Instance> { - can: bxcan::Can>, + pub can: RefCell>>, } #[derive(Debug)] @@ -147,19 +148,24 @@ impl<'d, T: Instance> Can<'d, T> { tx.set_as_af(tx.af_num(), AFType::OutputPushPull); let can = bxcan::Can::builder(BxcanInstance(peri)).leave_disabled(); - Self { can } + let can_ref_cell = RefCell::new(can); + Self { can: can_ref_cell } } pub fn set_bitrate(&mut self, bitrate: u32) { let bit_timing = Self::calc_bxcan_timings(T::frequency(), bitrate).unwrap(); - self.can.modify_config().set_bit_timing(bit_timing).leave_disabled(); + self.can + .borrow_mut() + .modify_config() + .set_bit_timing(bit_timing) + .leave_disabled(); } /// Queues the message to be sent but exerts backpressure pub async fn write(&mut self, frame: &Frame) -> bxcan::TransmitStatus { poll_fn(|cx| { T::state().tx_waker.register(cx.waker()); - if let Ok(status) = self.can.transmit(frame) { + if let Ok(status) = self.can.borrow_mut().transmit(frame) { return Poll::Ready(status); } @@ -341,6 +347,79 @@ impl<'d, T: Instance> Can<'d, T> { // Pack into BTR register values Some((sjw - 1) << 24 | (bs1 as u32 - 1) << 16 | (bs2 as u32 - 1) << 20 | (prescaler as u32 - 1)) } + + pub fn split<'c>(&'c self) -> (CanTx<'c, 'd, T>, CanRx<'c, 'd, T>) { + (CanTx { can: &self.can }, CanRx { can: &self.can }) + } + + pub fn as_mut(&self) -> RefMut<'_, bxcan::Can>> { + self.can.borrow_mut() + } +} + +pub struct CanTx<'c, 'd, T: Instance> { + can: &'c RefCell>>, +} + +impl<'c, 'd, T: Instance> CanTx<'c, 'd, T> { + pub async fn write(&mut self, frame: &Frame) -> bxcan::TransmitStatus { + poll_fn(|cx| { + T::state().tx_waker.register(cx.waker()); + if let Ok(status) = self.can.borrow_mut().transmit(frame) { + return Poll::Ready(status); + } + + Poll::Pending + }) + .await + } + + pub async fn flush(&self, mb: bxcan::Mailbox) { + poll_fn(|cx| { + T::state().tx_waker.register(cx.waker()); + if T::regs().tsr().read().tme(mb.index()) { + return Poll::Ready(()); + } + + Poll::Pending + }) + .await; + } +} + +#[allow(dead_code)] +pub struct CanRx<'c, 'd, T: Instance> { + can: &'c RefCell>>, +} + +impl<'c, 'd, T: Instance> CanRx<'c, 'd, T> { + pub async fn read(&mut self) -> Result<(u16, bxcan::Frame), BusError> { + poll_fn(|cx| { + T::state().err_waker.register(cx.waker()); + if let Poll::Ready((time, frame)) = T::state().rx_queue.recv().poll_unpin(cx) { + return Poll::Ready(Ok((time, frame))); + } else if let Some(err) = self.curr_error() { + return Poll::Ready(Err(err)); + } + + Poll::Pending + }) + .await + } + + fn curr_error(&self) -> Option { + 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); + } + None + } } enum RxFifo { @@ -358,7 +437,7 @@ impl<'d, T: Instance> Drop for Can<'d, T> { } impl<'d, T: Instance> Deref for Can<'d, T> { - type Target = bxcan::Can>; + type Target = RefCell>>; fn deref(&self) -> &Self::Target { &self.can diff --git a/examples/stm32f4/src/bin/can.rs b/examples/stm32f4/src/bin/can.rs index da895505..08bed88d 100644 --- a/examples/stm32f4/src/bin/can.rs +++ b/examples/stm32f4/src/bin/can.rs @@ -2,8 +2,8 @@ #![no_main] #![feature(type_alias_impl_trait)] -use cortex_m_rt::entry; use defmt::*; +use embassy_executor::Spawner; use embassy_stm32::bind_interrupts; use embassy_stm32::can::bxcan::filter::Mask32; use embassy_stm32::can::bxcan::{Fifo, Frame, StandardId}; @@ -19,8 +19,8 @@ bind_interrupts!(struct Irqs { CAN1_TX => TxInterruptHandler; }); -#[entry] -fn main() -> ! { +#[embassy_executor::main] +async fn main(_spawner: Spawner) { info!("Hello World!"); let mut p = embassy_stm32::init(Default::default()); @@ -34,9 +34,12 @@ fn main() -> ! { let mut can = Can::new(p.CAN1, p.PA11, p.PA12, Irqs); - can.modify_filters().enable_bank(0, Fifo::Fifo0, Mask32::accept_all()); + can.as_mut() + .modify_filters() + .enable_bank(0, Fifo::Fifo0, Mask32::accept_all()); - can.modify_config() + can.as_mut() + .modify_config() .set_bit_timing(0x001c0003) // http://www.bittiming.can-wiki.info/ .set_loopback(true) // Receive own frames .set_silent(true) @@ -45,9 +48,8 @@ fn main() -> ! { let mut i: u8 = 0; loop { let tx_frame = Frame::new_data(unwrap!(StandardId::new(i as _)), [i]); - unwrap!(nb::block!(can.transmit(&tx_frame))); - while !can.is_transmitter_idle() {} - let rx_frame = unwrap!(nb::block!(can.receive())); + can.write(&tx_frame).await; + let (_, rx_frame) = can.read().await.unwrap(); info!("loopback frame {=u8}", unwrap!(rx_frame.data())[0]); i += 1; } diff --git a/examples/stm32f7/src/bin/can.rs b/examples/stm32f7/src/bin/can.rs new file mode 100644 index 00000000..1b5b377e --- /dev/null +++ b/examples/stm32f7/src/bin/can.rs @@ -0,0 +1,66 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_stm32::bind_interrupts; +use embassy_stm32::can::bxcan::filter::Mask32; +use embassy_stm32::can::bxcan::{Fifo, Frame, StandardId}; +use embassy_stm32::can::{ + Can, CanTx, Rx0InterruptHandler, Rx1InterruptHandler, SceInterruptHandler, TxInterruptHandler, +}; +use embassy_stm32::gpio::{Input, Pull}; +use embassy_stm32::peripherals::CAN3; +use {defmt_rtt as _, panic_probe as _}; + +bind_interrupts!(struct Irqs { + CAN3_RX0 => Rx0InterruptHandler; + CAN3_RX1 => Rx1InterruptHandler; + CAN3_SCE => SceInterruptHandler; + CAN3_TX => TxInterruptHandler; +}); + +#[embassy_executor::task] +pub async fn send_can_message(tx: &'static mut CanTx<'static, 'static, CAN3>) { + loop { + let frame = Frame::new_data(unwrap!(StandardId::new(0 as _)), [0]); + tx.write(&frame).await; + embassy_time::Timer::after(embassy_time::Duration::from_secs(1)).await; + } +} + +#[embassy_executor::main] +async fn main(spawner: Spawner) { + info!("Hello World!"); + + let mut p = embassy_stm32::init(Default::default()); + + // The next two lines are a workaround for testing without transceiver. + // To synchronise to the bus the RX input needs to see a high level. + // Use `mem::forget()` to release the borrow on the pin but keep the + // pull-up resistor enabled. + let rx_pin = Input::new(&mut p.PA15, Pull::Up); + core::mem::forget(rx_pin); + + let can: &'static mut Can<'static, CAN3> = static_cell::make_static!(Can::new(p.CAN3, p.PA8, p.PA15, Irqs)); + can.as_mut() + .modify_filters() + .enable_bank(0, Fifo::Fifo0, Mask32::accept_all()); + + can.as_mut() + .modify_config() + .set_bit_timing(0x001c0001) // http://www.bittiming.can-wiki.info/ + .set_loopback(true) + .enable(); + + let (tx, mut rx) = can.split(); + + let tx: &'static mut CanTx<'static, 'static, CAN3> = static_cell::make_static!(tx); + spawner.spawn(send_can_message(tx)).unwrap(); + + loop { + let frame = rx.read().await.unwrap(); + println!("Received: {:?}", frame); + } +} diff --git a/tests/stm32/src/bin/can.rs b/tests/stm32/src/bin/can.rs index 33d63d54..8bdd3c24 100644 --- a/tests/stm32/src/bin/can.rs +++ b/tests/stm32/src/bin/can.rs @@ -43,10 +43,13 @@ async fn main(_spawner: Spawner) { info!("Configuring can..."); - can.modify_filters().enable_bank(0, Fifo::Fifo0, Mask32::accept_all()); + can.as_mut() + .modify_filters() + .enable_bank(0, Fifo::Fifo0, Mask32::accept_all()); can.set_bitrate(1_000_000); - can.modify_config() + can.as_mut() + .modify_config() .set_loopback(true) // Receive own frames .set_silent(true) // .set_bit_timing(0x001c0003)