diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index fd121f6d..e0c16b46 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -26,6 +26,7 @@ bare-metal = "1.0.0" atomic-polyfill = "0.1.3" stm32-metapac = { version = "0.1.0", path = "../stm32-metapac", features = ["rt"] } vcell = { version = "0.1.3", optional = true } +bxcan = { version = "0.5.1" } cfg-if = "1.0.0" diff --git a/embassy-stm32/src/can/bxcan.rs b/embassy-stm32/src/can/bxcan.rs new file mode 100644 index 00000000..906978e8 --- /dev/null +++ b/embassy-stm32/src/can/bxcan.rs @@ -0,0 +1,144 @@ +use core::marker::PhantomData; +use core::ops::{Deref, DerefMut}; + +use embassy::util::Unborrow; +use embassy_hal_common::unborrow; + +use crate::gpio::Pin; +use crate::{peripherals, rcc::RccPeripheral}; + +pub use bxcan::*; + +pub struct Can<'d, T: Instance + bxcan::Instance> { + phantom: PhantomData<&'d mut T>, + can: bxcan::Can, +} + +impl<'d, T: Instance + bxcan::Instance> Can<'d, T> { + pub fn new( + peri: impl Unborrow + 'd, + rx: impl Unborrow> + 'd, + tx: impl Unborrow> + 'd, + ) -> Self { + unborrow!(peri, rx, tx); + + unsafe { + rx.set_as_af(rx.af_num()); + tx.set_as_af(tx.af_num()); + } + + T::enable(); + T::reset(); + + Self { + phantom: PhantomData, + can: bxcan::Can::new(peri), + } + } +} + +impl<'d, T: Instance + bxcan::Instance> Drop for Can<'d, T> { + fn drop(&mut self) { + // Cannot call `free()` because it moves the instance. + // Manually reset the peripheral. + unsafe { + T::regs().mcr().write(|w| w.set_reset(true)); + } + T::disable(); + } +} + +impl<'d, T: Instance + bxcan::Instance> Deref for Can<'d, T> { + type Target = bxcan::Can; + + fn deref(&self) -> &Self::Target { + &self.can + } +} + +impl<'d, T: Instance + bxcan::Instance> DerefMut for Can<'d, T> { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.can + } +} + +pub(crate) mod sealed { + use super::*; + + pub trait Instance { + fn regs() -> &'static crate::pac::can::Can; + } + + pub trait RxPin: Pin { + fn af_num(&self) -> u8; + } + + pub trait TxPin: Pin { + fn af_num(&self) -> u8; + } +} + +pub trait Instance: sealed::Instance + RccPeripheral {} +pub trait RxPin: sealed::RxPin {} +pub trait TxPin: sealed::TxPin {} + +crate::pac::peripherals!( + (can, $inst:ident) => { + impl sealed::Instance for peripherals::$inst { + fn regs() -> &'static crate::pac::can::Can { + &crate::pac::$inst + } + } + + impl Instance for peripherals::$inst {} + + unsafe impl bxcan::Instance for peripherals::$inst { + const REGISTERS: *mut bxcan::RegisterBlock = crate::pac::$inst.0 as *mut _; + } + }; +); + +crate::pac::peripherals!( + (can, CAN) => { + unsafe impl bxcan::FilterOwner for peripherals::$inst { + const NUM_FILTER_BANKS: u8 = 14; + } + }; + // Only correct when CAN2 also exists… Fix on yaml level? + // There are only 14 filter banks when CAN2 is not available. + (can, CAN1) => { + unsafe impl bxcan::FilterOwner for peripherals::CAN1 { + const NUM_FILTER_BANKS: u8 = 28; + } + }; + (can, CAN2) => { + // CAN2 is always a slave instance where CAN1 is the master instance + unsafe impl bxcan::MasterInstance for peripherals::CAN1 {} + }; + (can, CAN3) => { + unsafe impl bxcan::FilterOwner for peripherals::$inst { + const NUM_FILTER_BANKS: u8 = 14; + } + }; +); + +macro_rules! impl_pin { + ($inst:ident, $pin:ident, $signal:ident, $af:expr) => { + impl $signal for peripherals::$pin {} + + impl sealed::$signal for peripherals::$pin { + fn af_num(&self) -> u8 { + $af + } + } + }; +} + +crate::pac::peripheral_pins!( + ($inst:ident, can, CAN, $pin:ident, TX, $af:expr) => { + impl_pin!($inst, $pin, TxPin, $af); + }; + ($inst:ident, can, CAN, $pin:ident, RX, $af:expr) => { + impl_pin!($inst, $pin, RxPin, $af); + }; +); diff --git a/embassy-stm32/src/can/mod.rs b/embassy-stm32/src/can/mod.rs new file mode 100644 index 00000000..c7e2e620 --- /dev/null +++ b/embassy-stm32/src/can/mod.rs @@ -0,0 +1,5 @@ +#![macro_use] + +#[cfg_attr(can_bxcan, path = "bxcan.rs")] +mod _version; +pub use _version::*; diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs index 8a213b68..af4ab95f 100644 --- a/embassy-stm32/src/lib.rs +++ b/embassy-stm32/src/lib.rs @@ -27,6 +27,8 @@ mod time_driver; #[cfg(adc)] pub mod adc; +#[cfg(can)] +pub mod can; #[cfg(dac)] pub mod dac; #[cfg(dbgmcu)] diff --git a/examples/stm32f4/Cargo.toml b/examples/stm32f4/Cargo.toml index c254573f..95c8441d 100644 --- a/examples/stm32f4/Cargo.toml +++ b/examples/stm32f4/Cargo.toml @@ -32,3 +32,4 @@ panic-probe = { version = "0.2.0", features= ["print-defmt"] } futures = { version = "0.3.8", default-features = false, features = ["async-await"] } rtt-target = { version = "0.3", features = ["cortex-m"] } heapless = { version = "0.7.1", default-features = false } +nb = { version = "1.0" } diff --git a/examples/stm32f4/src/bin/can.rs b/examples/stm32f4/src/bin/can.rs new file mode 100644 index 00000000..2bb24f04 --- /dev/null +++ b/examples/stm32f4/src/bin/can.rs @@ -0,0 +1,52 @@ +#![no_std] +#![no_main] +#![feature(trait_alias)] +#![feature(type_alias_impl_trait)] +#![allow(incomplete_features)] + +#[path = "../example_common.rs"] +mod example_common; + +use cortex_m_rt::entry; +use embassy_stm32::can::filter::Mask32; +use embassy_stm32::can::{Can, Frame, StandardId}; +use embassy_stm32::dbgmcu::Dbgmcu; +use embassy_stm32::gpio::{Input, Pull}; +use example_common::*; + +#[entry] +fn main() -> ! { + info!("Hello World!"); + + unsafe { + Dbgmcu::enable_all(); + } + + 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.PA11, Pull::Up); + core::mem::forget(rx_pin); + + let mut can = Can::new(p.CAN1, p.PA11, p.PA12); + + can.modify_config() + .set_bit_timing(0x001c0003) // http://www.bittiming.can-wiki.info/ + .set_loopback(true) // Receive own frames + .set_silent(true); + can.modify_filters().enable_bank(0, Mask32::accept_all()); + unwrap!(nb::block!(can.enable())); + + 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())); + info!("loopback frame {=u8}", unwrap!(rx_frame.data())[0]); + i += 1; + } +}