diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index fd121f6db..dc09b8e87 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" } # TODO: , optional = true } cfg-if = "1.0.0" diff --git a/embassy-stm32/src/bxcan.rs b/embassy-stm32/src/bxcan.rs new file mode 100644 index 000000000..59dad20c2 --- /dev/null +++ b/embassy-stm32/src/bxcan.rs @@ -0,0 +1,128 @@ +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, + // irq: 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(); + // TODO: CAN2 also required CAN1 clock + } + + Self { + phantom: PhantomData, + can: bxcan::Can::new(peri), + } + } +} + +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 {} + + 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!( + (bxcan, $inst:ident) => { + impl sealed::Instance for peripherals::$inst {} + + impl Instance for peripherals::$inst {} + + unsafe impl bxcan::Instance for peripherals::$inst { + const REGISTERS: *mut bxcan::RegisterBlock = crate::pac::$inst.0 as *mut _; + } + }; + // (bxcan, CAN) => { + // unsafe impl bxcan::FilterOwner for Can { + // const NUM_FILTER_BANKS: u8 = 14; + // } + // }; +); + +crate::pac::peripherals!( + // TODO: rename CAN to CAN1 on yaml level?? + (bxcan, CAN) => { + unsafe impl bxcan::FilterOwner for peripherals::CAN { + const NUM_FILTER_BANKS: u8 = 14; + } + }; + (bxcan, CAN1) => { + unsafe impl bxcan::FilterOwner for peripherals::CAN1 { + const NUM_FILTER_BANKS: u8 = 14; + } + }; + (bxcan, CAN2) => { + // TODO: when CAN2 existis, we have 28 filter banks + unsafe impl bxcan::MasterInstance for peripherals::CAN1 {} + }; +); + +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, bxcan, CAN, $pin:ident, TX, $af:expr) => { + impl_pin!($inst, $pin, TxPin, $af); + }; + ($inst:ident, bxcan, CAN, $pin:ident, RX, $af:expr) => { + impl_pin!($inst, $pin, RxPin, $af); + }; +); diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs index 8a213b680..ca5c4d77a 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(bxcan)] +pub mod bxcan; #[cfg(dac)] pub mod dac; #[cfg(dbgmcu)] diff --git a/examples/stm32f4/Cargo.toml b/examples/stm32f4/Cargo.toml index c254573f8..95c8441d9 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 000000000..d35a81d1d --- /dev/null +++ b/examples/stm32f4/src/bin/can.rs @@ -0,0 +1,39 @@ +#![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::bxcan::{Can, Frame, StandardId}; +use embassy_stm32::dbgmcu::Dbgmcu; +use example_common::*; + +#[entry] +fn main() -> ! { + info!("Hello World!"); + + unsafe { + Dbgmcu::enable_all(); + } + + let p = embassy_stm32::init(Default::default()); + + let mut can = Can::new(p.CAN1, p.PA11, p.PA12); + + can.modify_config().set_loopback(true); + 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; + } +}