diff --git a/embassy-stm32f4-examples/Cargo.toml b/embassy-stm32f4-examples/Cargo.toml index 94f24c75..60ba4455 100644 --- a/embassy-stm32f4-examples/Cargo.toml +++ b/embassy-stm32f4-examples/Cargo.toml @@ -31,3 +31,4 @@ panic-probe = "0.1.0" stm32f4xx-hal = { version = "0.8.3", features = ["rt", "stm32f405"], git = "https://github.com/stm32-rs/stm32f4xx-hal.git"} futures = { version = "0.3.8", default-features = false, features = ["async-await"] } rtt-target = { version = "0.3", features = ["cortex-m"] } +bxcan = "0.5.0" \ No newline at end of file diff --git a/embassy-stm32f4-examples/src/bin/can.rs b/embassy-stm32f4-examples/src/bin/can.rs new file mode 100644 index 00000000..c9144762 --- /dev/null +++ b/embassy-stm32f4-examples/src/bin/can.rs @@ -0,0 +1,64 @@ +#![no_std] +#![no_main] +#![feature(trait_alias)] +#![feature(type_alias_impl_trait)] + +#[path = "../example_common.rs"] +mod example_common; +use example_common::{panic, *}; + +use cortex_m_rt::entry; +use embassy::executor::{task, Executor}; +use embassy::traits::gpio::*; +use embassy::util::Forever; +use embassy_stm32f4::can; +use embassy_stm32f4::exti; +use embassy_stm32f4::interrupt; +use futures::pin_mut; +use stm32f4xx_hal::prelude::*; +use stm32f4xx_hal::stm32; + +static EXTI: Forever = Forever::new(); + +#[task] +async fn run(dp: stm32::Peripherals, _cp: cortex_m::Peripherals) { + let rx = gpioa.pa11.into_alternate_af9(); + let tx = gpioa.pa12.into_alternate_af9(); + let mut can = bxcan::Can::new(Can::new(dp.CAN1, (tx, rx))); + + // APB1 (PCLK1): 24MHz, Bit rate: 20kBit/s, Sample Point 87.5% + // Value was calculated with http://www.bittiming.can-wiki.info/ + can.modify_config().set_bit_timing(0x001c_004a); + // Configure filters so that can frames can be received. + can.modify_filters().enable_bank(0, Mask32::accept_all()); + + block!(can.enable()).unwrap(); + + let can = can::Can::new( + can, + interrupt::take!(CAN1_TX), + interrupt::take!(CAN1_RX0), + )); + + let frame = can.receive().await; +} + +static EXECUTOR: Forever = Forever::new(); + +#[entry] +fn main() -> ! { + let dp = stm32::Peripherals::take().unwrap(); + let cp = cortex_m::peripheral::Peripherals::take().unwrap(); + + dp.DBGMCU.cr.modify(|_, w| { + w.dbg_sleep().set_bit(); + w.dbg_standby().set_bit(); + w.dbg_stop().set_bit() + }); + dp.RCC.ahb1enr.modify(|_, w| w.dma1en().enabled()); + + let executor = EXECUTOR.put(Executor::new()); + executor.run(|spawner| { + unwrap!(spawner.spawn(run(dp, cp))); + }); +} diff --git a/embassy-stm32f4/Cargo.toml b/embassy-stm32f4/Cargo.toml index f9a931b7..afe771eb 100644 --- a/embassy-stm32f4/Cargo.toml +++ b/embassy-stm32f4/Cargo.toml @@ -39,3 +39,5 @@ cortex-m = "0.7.1" embedded-hal = { version = "0.2.4" } embedded-dma = { version = "0.1.2" } stm32f4xx-hal = { version = "0.8.3", features = ["rt"], git = "https://github.com/stm32-rs/stm32f4xx-hal.git"} +bxcan = "0.5.0" +nb = "*" diff --git a/embassy-stm32f4/src/can.rs b/embassy-stm32f4/src/can.rs new file mode 100644 index 00000000..eb1699c5 --- /dev/null +++ b/embassy-stm32f4/src/can.rs @@ -0,0 +1,103 @@ +//! Async low power Serial. +//! +//! The peripheral is autmatically enabled and disabled as required to save power. +//! Lowest power consumption can only be guaranteed if the send receive futures +//! are dropped correctly (e.g. not using `mem::forget()`). + +use bxcan; +use bxcan::Interrupts; +use core::future::Future; +use embassy::interrupt::Interrupt; +use embassy::util::InterruptFuture; +use embassy::util::Signal; +use nb; +use nb::block; + +use crate::hal::prelude::*; +use crate::interrupt; + +/// Interface to the Serial peripheral +pub struct Can { + can: bxcan::Can, + tx_int: T::TInterrupt, + rx_int: T::RInterrupt, +} + +impl Can { + pub fn new(mut can: bxcan::Can, tx_int: T::TInterrupt, rx_int: T::RInterrupt) -> Self { + // Sync to the bus and start normal operation. + can.enable_interrupts( + Interrupts::TRANSMIT_MAILBOX_EMPTY | Interrupts::FIFO0_MESSAGE_PENDING, + ); + block!(can.enable()).unwrap(); + + Can { + can: can, + tx_int: tx_int, + rx_int: rx_int, + } + } + + /// Sends can frame. + /// + /// This method async-blocks until the frame is transmitted. + pub fn transmit<'a>(&'a mut self, frame: &'a bxcan::Frame) -> impl Future + 'a { + async move { + let fut = InterruptFuture::new(&mut self.tx_int); + self.can.transmit(frame); + + fut.await; + } + } + + /// Receive can frame. + /// + /// This method async-blocks until the frame is received. + pub fn receive<'a>(&'a mut self) -> impl Future + 'a { + async move { + let mut frame: Option = None; + + loop { + let fut = InterruptFuture::new(&mut self.rx_int); + frame = match self.can.receive() { + Ok(frame) => Some(frame), + Err(nb::Error::WouldBlock) => None, + Err(nb::Error::Other(_)) => None, // Ignore overrun errors. + }; + if frame.is_some() { + break; + } + fut.await; + } + + frame.unwrap() + } + } +} + +mod private { + pub trait Sealed {} +} + +pub trait Instance: bxcan::Instance + private::Sealed { + type TInterrupt: Interrupt; + type RInterrupt: Interrupt; +} + +macro_rules! can { + ($($can:ident => ($tint:ident, $rint:ident),)+) => { + $( + impl private::Sealed for crate::hal::can::Can {} + impl Instance for crate::hal::can::Can { + type TInterrupt = interrupt::$tint; + type RInterrupt = interrupt::$rint; + } + )+ + } +} + +#[cfg(any(feature = "stm32f405",))] +can! { + CAN1 => (CAN1_TX, CAN1_RX0), + CAN2 => (CAN2_TX, CAN2_RX0), +} diff --git a/embassy-stm32f4/src/lib.rs b/embassy-stm32f4/src/lib.rs index aa99068c..5d4354bc 100644 --- a/embassy-stm32f4/src/lib.rs +++ b/embassy-stm32f4/src/lib.rs @@ -311,6 +311,7 @@ pub use stm32f4xx_hal::stm32 as pac; // This mod MUST go first, so that the others see its macros. pub(crate) mod fmt; +pub mod can; pub mod exti; pub mod interrupt; pub mod qei;