From 7c405250a77c08740524529757f8203b5b7e0a60 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20Kr=C3=B6ger?= Date: Fri, 6 Aug 2021 00:08:24 +0200 Subject: [PATCH 1/6] CAN support with bxcan crate --- embassy-stm32/Cargo.toml | 1 + embassy-stm32/src/bxcan.rs | 128 ++++++++++++++++++++++++++++++++ embassy-stm32/src/lib.rs | 2 + examples/stm32f4/Cargo.toml | 1 + examples/stm32f4/src/bin/can.rs | 39 ++++++++++ 5 files changed, 171 insertions(+) create mode 100644 embassy-stm32/src/bxcan.rs create mode 100644 examples/stm32f4/src/bin/can.rs diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index fd121f6d..dc09b8e8 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 00000000..59dad20c --- /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 8a213b68..ca5c4d77 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 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..d35a81d1 --- /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; + } +} From dc6b7f3cbaa291d2a9b83bda88fbef08cf464bc5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20Kr=C3=B6ger?= Date: Fri, 6 Aug 2021 10:37:32 +0200 Subject: [PATCH 2/6] bxcan: Disable on drop --- embassy-stm32/src/bxcan.rs | 29 +++++++++++++++++++++++------ 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/embassy-stm32/src/bxcan.rs b/embassy-stm32/src/bxcan.rs index 59dad20c..8dd7459b 100644 --- a/embassy-stm32/src/bxcan.rs +++ b/embassy-stm32/src/bxcan.rs @@ -26,12 +26,12 @@ impl<'d, T: Instance + bxcan::Instance> Can<'d, T> { 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 } + T::enable(); + T::reset(); + // TODO: CAN2 also required CAN1 clock + Self { phantom: PhantomData, can: bxcan::Can::new(peri), @@ -39,6 +39,17 @@ impl<'d, T: Instance + bxcan::Instance> Can<'d, T> { } } +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; @@ -56,7 +67,9 @@ impl<'d, T: Instance + bxcan::Instance> DerefMut for Can<'d, T> { pub(crate) mod sealed { use super::*; - pub trait Instance {} + pub trait Instance { + fn regs() -> &'static crate::pac::bxcan::Can; + } pub trait RxPin: Pin { fn af_num(&self) -> u8; @@ -73,7 +86,11 @@ pub trait TxPin: sealed::TxPin {} crate::pac::peripherals!( (bxcan, $inst:ident) => { - impl sealed::Instance for peripherals::$inst {} + impl sealed::Instance for peripherals::$inst { + fn regs() -> &'static crate::pac::bxcan::Can { + &crate::pac::$inst + } + } impl Instance for peripherals::$inst {} From 191a589820159a69fbc72f996c4be91093dc2b44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20Kr=C3=B6ger?= Date: Fri, 6 Aug 2021 11:59:16 +0200 Subject: [PATCH 3/6] bxcan: namechange "bxcan_v1" -> "can_bxcan" --- embassy-stm32/src/{ => can}/bxcan.rs | 21 ++++++++------------- embassy-stm32/src/can/mod.rs | 5 +++++ embassy-stm32/src/lib.rs | 4 ++-- examples/stm32f4/src/bin/can.rs | 2 +- 4 files changed, 16 insertions(+), 16 deletions(-) rename embassy-stm32/src/{ => can}/bxcan.rs (86%) create mode 100644 embassy-stm32/src/can/mod.rs diff --git a/embassy-stm32/src/bxcan.rs b/embassy-stm32/src/can/bxcan.rs similarity index 86% rename from embassy-stm32/src/bxcan.rs rename to embassy-stm32/src/can/bxcan.rs index 8dd7459b..33f3cebf 100644 --- a/embassy-stm32/src/bxcan.rs +++ b/embassy-stm32/src/can/bxcan.rs @@ -68,7 +68,7 @@ pub(crate) mod sealed { use super::*; pub trait Instance { - fn regs() -> &'static crate::pac::bxcan::Can; + fn regs() -> &'static crate::pac::can::Can; } pub trait RxPin: Pin { @@ -85,9 +85,9 @@ pub trait RxPin: sealed::RxPin {} pub trait TxPin: sealed::TxPin {} crate::pac::peripherals!( - (bxcan, $inst:ident) => { + (can, $inst:ident) => { impl sealed::Instance for peripherals::$inst { - fn regs() -> &'static crate::pac::bxcan::Can { + fn regs() -> &'static crate::pac::can::Can { &crate::pac::$inst } } @@ -98,26 +98,21 @@ crate::pac::peripherals!( 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) => { + (can, CAN) => { unsafe impl bxcan::FilterOwner for peripherals::CAN { const NUM_FILTER_BANKS: u8 = 14; } }; - (bxcan, CAN1) => { + (can, CAN1) => { unsafe impl bxcan::FilterOwner for peripherals::CAN1 { const NUM_FILTER_BANKS: u8 = 14; } }; - (bxcan, CAN2) => { + (can, CAN2) => { // TODO: when CAN2 existis, we have 28 filter banks unsafe impl bxcan::MasterInstance for peripherals::CAN1 {} }; @@ -136,10 +131,10 @@ macro_rules! impl_pin { } crate::pac::peripheral_pins!( - ($inst:ident, bxcan, CAN, $pin:ident, TX, $af:expr) => { + ($inst:ident, can, CAN, $pin:ident, TX, $af:expr) => { impl_pin!($inst, $pin, TxPin, $af); }; - ($inst:ident, bxcan, CAN, $pin:ident, RX, $af:expr) => { + ($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 ca5c4d77..af4ab95f 100644 --- a/embassy-stm32/src/lib.rs +++ b/embassy-stm32/src/lib.rs @@ -27,8 +27,8 @@ mod time_driver; #[cfg(adc)] pub mod adc; -#[cfg(bxcan)] -pub mod bxcan; +#[cfg(can)] +pub mod can; #[cfg(dac)] pub mod dac; #[cfg(dbgmcu)] diff --git a/examples/stm32f4/src/bin/can.rs b/examples/stm32f4/src/bin/can.rs index d35a81d1..04f3417c 100644 --- a/examples/stm32f4/src/bin/can.rs +++ b/examples/stm32f4/src/bin/can.rs @@ -8,7 +8,7 @@ mod example_common; use cortex_m_rt::entry; -use embassy_stm32::bxcan::{Can, Frame, StandardId}; +use embassy_stm32::can::{Can, Frame, StandardId}; use embassy_stm32::dbgmcu::Dbgmcu; use example_common::*; From dacf75d911058dd09d2a825a5b23d758f1f7a92d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20Kr=C3=B6ger?= Date: Mon, 9 Aug 2021 15:59:05 +0200 Subject: [PATCH 4/6] bxcan: Fix the flaky CAN example --- examples/stm32f4/src/bin/can.rs | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/examples/stm32f4/src/bin/can.rs b/examples/stm32f4/src/bin/can.rs index 04f3417c..2bb24f04 100644 --- a/examples/stm32f4/src/bin/can.rs +++ b/examples/stm32f4/src/bin/can.rs @@ -8,8 +8,10 @@ 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] @@ -20,11 +22,22 @@ fn main() -> ! { Dbgmcu::enable_all(); } - let p = embassy_stm32::init(Default::default()); + 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_loopback(true); + 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; From 0c3bede64f990daafb87ae8841261b754c885ddb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20Kr=C3=B6ger?= Date: Mon, 9 Aug 2021 16:19:00 +0200 Subject: [PATCH 5/6] bxcan: Make `bxcan` a hard dependency There seems no way to enable a optional dependency from build.rs or features passed through the command line. --- embassy-stm32/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index dc09b8e8..e0c16b46 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -26,7 +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 } +bxcan = { version = "0.5.1" } cfg-if = "1.0.0" From f141b987418ca55500b4f137555a64ea2bbb18d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20Kr=C3=B6ger?= Date: Sat, 14 Aug 2021 23:13:50 +0200 Subject: [PATCH 6/6] bxcan: Cleanup Older families like F1 and F4 have a consistent naming for the CAN peripherals: CAN when there is only one instance, CAN1/CAN2/CAN2 if there are multiple instances. Newer families like L4 and F7 use the name CAN1 even if there is only one instance. The number of filter banks is incorrect for those. Affected chips: * STM32F722 * STM32F723 * STM32F730 * STM32F732 * STM32F733 * STM32L4P5 * STM32L4Q5 * STM32L4R5 * STM32L4R7 * STM32L4R9 * STM32L4S5 * STM32L4S7 * STM32L4S9 * STM32L431 * STM32L432 * STM32L433 * STM32L442 * STM32L443 * STM32L451 * STM32L452 * STM32L462 * STM32L471 * STM32L475 * STM32L476 * STM32L485 * STM32L486 --- embassy-stm32/src/can/bxcan.rs | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/embassy-stm32/src/can/bxcan.rs b/embassy-stm32/src/can/bxcan.rs index 33f3cebf..906978e8 100644 --- a/embassy-stm32/src/can/bxcan.rs +++ b/embassy-stm32/src/can/bxcan.rs @@ -17,7 +17,6 @@ pub struct Can<'d, T: Instance + bxcan::Instance> { 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 { @@ -30,7 +29,6 @@ impl<'d, T: Instance + bxcan::Instance> Can<'d, T> { T::enable(); T::reset(); - // TODO: CAN2 also required CAN1 clock Self { phantom: PhantomData, @@ -101,21 +99,27 @@ crate::pac::peripherals!( ); crate::pac::peripherals!( - // TODO: rename CAN to CAN1 on yaml level?? (can, CAN) => { - unsafe impl bxcan::FilterOwner for peripherals::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 = 14; + const NUM_FILTER_BANKS: u8 = 28; } }; (can, CAN2) => { - // TODO: when CAN2 existis, we have 28 filter banks + // 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 {