diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index 948ce3aff..414723573 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs @@ -449,7 +449,7 @@ fn main() { // ======== // Generate RccPeripheral impls - let refcounted_peripherals = HashSet::from(["usart", "adc"]); + let refcounted_peripherals = HashSet::from(["usart", "adc", "can"]); let mut refcount_statics = BTreeSet::new(); for p in METADATA.peripherals { diff --git a/embassy-stm32/src/can/bxcan.rs b/embassy-stm32/src/can/bxcan.rs index cc87b2565..7e00eca6f 100644 --- a/embassy-stm32/src/can/bxcan.rs +++ b/embassy-stm32/src/can/bxcan.rs @@ -13,9 +13,12 @@ use crate::gpio::sealed::AFType; use crate::interrupt::typelevel::Interrupt; use crate::pac::can::vals::{Ide, Lec}; use crate::rcc::RccPeripheral; -use crate::time::Hertz; use crate::{interrupt, peripherals, Peripheral}; +pub mod enums; +use enums::*; +pub mod util; + /// Contains CAN frame and additional metadata. /// /// Timestamp is available if `time` feature is enabled. @@ -93,23 +96,6 @@ pub struct Can<'d, T: Instance> { can: bxcan::Can>, } -/// CAN bus error -#[allow(missing_docs)] -#[derive(Debug)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum BusError { - Stuff, - Form, - Acknowledge, - BitRecessive, - BitDominant, - Crc, - Software, - BusOff, - BusPassive, - BusWarning, -} - /// Error returned by `try_read` #[derive(Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] @@ -186,8 +172,15 @@ impl<'d, T: Instance> Can<'d, T> { /// Set CAN bit rate. 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(); + let bit_timing = util::calc_can_timings(T::frequency(), bitrate).unwrap(); + let sjw = u8::from(bit_timing.sync_jump_width) as u32; + let seg1 = u8::from(bit_timing.seg1) as u32; + let seg2 = u8::from(bit_timing.seg2) as u32; + let prescaler = u16::from(bit_timing.prescaler) as u32; + self.can + .modify_config() + .set_bit_timing((sjw - 1) << 24 | (seg1 - 1) << 16 | (seg2 - 1) << 20 | (prescaler - 1)) + .leave_disabled(); } /// Enables the peripheral and synchronizes with the bus. @@ -302,97 +295,6 @@ impl<'d, T: Instance> Can<'d, T> { } } - const fn calc_bxcan_timings(periph_clock: Hertz, can_bitrate: u32) -> Option { - const BS1_MAX: u8 = 16; - const BS2_MAX: u8 = 8; - const MAX_SAMPLE_POINT_PERMILL: u16 = 900; - - let periph_clock = periph_clock.0; - - if can_bitrate < 1000 { - return None; - } - - // Ref. "Automatic Baudrate Detection in CANopen Networks", U. Koppe, MicroControl GmbH & Co. KG - // CAN in Automation, 2003 - // - // According to the source, optimal quanta per bit are: - // Bitrate Optimal Maximum - // 1000 kbps 8 10 - // 500 kbps 16 17 - // 250 kbps 16 17 - // 125 kbps 16 17 - let max_quanta_per_bit: u8 = if can_bitrate >= 1_000_000 { 10 } else { 17 }; - - // Computing (prescaler * BS): - // BITRATE = 1 / (PRESCALER * (1 / PCLK) * (1 + BS1 + BS2)) -- See the Reference Manual - // BITRATE = PCLK / (PRESCALER * (1 + BS1 + BS2)) -- Simplified - // let: - // BS = 1 + BS1 + BS2 -- Number of time quanta per bit - // PRESCALER_BS = PRESCALER * BS - // ==> - // PRESCALER_BS = PCLK / BITRATE - let prescaler_bs = periph_clock / can_bitrate; - - // Searching for such prescaler value so that the number of quanta per bit is highest. - let mut bs1_bs2_sum = max_quanta_per_bit - 1; - while (prescaler_bs % (1 + bs1_bs2_sum) as u32) != 0 { - if bs1_bs2_sum <= 2 { - return None; // No solution - } - bs1_bs2_sum -= 1; - } - - let prescaler = prescaler_bs / (1 + bs1_bs2_sum) as u32; - if (prescaler < 1) || (prescaler > 1024) { - return None; // No solution - } - - // Now we have a constraint: (BS1 + BS2) == bs1_bs2_sum. - // We need to find such values so that the sample point is as close as possible to the optimal value, - // which is 87.5%, which is 7/8. - // - // Solve[(1 + bs1)/(1 + bs1 + bs2) == 7/8, bs2] (* Where 7/8 is 0.875, the recommended sample point location *) - // {{bs2 -> (1 + bs1)/7}} - // - // Hence: - // bs2 = (1 + bs1) / 7 - // bs1 = (7 * bs1_bs2_sum - 1) / 8 - // - // Sample point location can be computed as follows: - // Sample point location = (1 + bs1) / (1 + bs1 + bs2) - // - // Since the optimal solution is so close to the maximum, we prepare two solutions, and then pick the best one: - // - With rounding to nearest - // - With rounding to zero - let mut bs1 = ((7 * bs1_bs2_sum - 1) + 4) / 8; // Trying rounding to nearest first - let mut bs2 = bs1_bs2_sum - bs1; - core::assert!(bs1_bs2_sum > bs1); - - let sample_point_permill = 1000 * ((1 + bs1) / (1 + bs1 + bs2)) as u16; - if sample_point_permill > MAX_SAMPLE_POINT_PERMILL { - // Nope, too far; now rounding to zero - bs1 = (7 * bs1_bs2_sum - 1) / 8; - bs2 = bs1_bs2_sum - bs1; - } - - // Check is BS1 and BS2 are in range - if (bs1 < 1) || (bs1 > BS1_MAX) || (bs2 < 1) || (bs2 > BS2_MAX) { - return None; - } - - // Check if final bitrate matches the requested - if can_bitrate != (periph_clock / (prescaler * (1 + bs1 + bs2) as u32)) { - return None; - } - - // One is recommended by DS-015, CANOpen, and DeviceNet - let sjw = 1; - - // Pack into BTR register values - Some((sjw - 1) << 24 | (bs1 as u32 - 1) << 16 | (bs2 as u32 - 1) << 20 | (prescaler - 1)) - } - /// Split the CAN driver into transmit and receive halves. /// /// Useful for doing separate transmit/receive tasks. diff --git a/embassy-stm32/src/can/enums.rs b/embassy-stm32/src/can/enums.rs new file mode 100644 index 000000000..36139a45c --- /dev/null +++ b/embassy-stm32/src/can/enums.rs @@ -0,0 +1,30 @@ +//! Enums shared between CAN controller types. + +/// Bus error +#[derive(Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum BusError { + /// Bit stuffing error - more than 5 equal bits + Stuff, + /// Form error - A fixed format part of a received message has wrong format + Form, + /// The message transmitted by the FDCAN was not acknowledged by another node. + Acknowledge, + /// Bit0Error: During the transmission of a message the device wanted to send a dominant level + /// but the monitored bus value was recessive. + BitRecessive, + /// Bit1Error: During the transmission of a message the device wanted to send a recessive level + /// but the monitored bus value was dominant. + BitDominant, + /// The CRC check sum of a received message was incorrect. The CRC of an + /// incoming message does not match with the CRC calculated from the received data. + Crc, + /// A software error occured + Software, + /// The FDCAN is in Bus_Off state. + BusOff, + /// The FDCAN is in the Error_Passive state. + BusPassive, + /// At least one of error counter has reached the Error_Warning limit of 96. + BusWarning, +} diff --git a/embassy-stm32/src/can/util.rs b/embassy-stm32/src/can/util.rs new file mode 100644 index 000000000..fcdbbad62 --- /dev/null +++ b/embassy-stm32/src/can/util.rs @@ -0,0 +1,117 @@ +//! Utility functions shared between CAN controller types. + +use core::num::{NonZeroU16, NonZeroU8}; + +/// Shared struct to represent bit timings used by calc_can_timings. +#[derive(Clone, Copy, Debug)] +pub struct NominalBitTiming { + /// Value by which the oscillator frequency is divided for generating the bit time quanta. The bit + /// time is built up from a multiple of this quanta. Valid values are 1 to 512. + pub prescaler: NonZeroU16, + /// Valid values are 1 to 128. + pub seg1: NonZeroU8, + /// Valid values are 1 to 255. + pub seg2: NonZeroU8, + /// Valid values are 1 to 128. + pub sync_jump_width: NonZeroU8, +} + +/// Calculate nominal CAN bit timing based on CAN bitrate and periphial clock frequency +pub fn calc_can_timings(periph_clock: crate::time::Hertz, can_bitrate: u32) -> Option { + const BS1_MAX: u8 = 16; + const BS2_MAX: u8 = 8; + const MAX_SAMPLE_POINT_PERMILL: u16 = 900; + + let periph_clock = periph_clock.0; + + if can_bitrate < 1000 { + return None; + } + + // Ref. "Automatic Baudrate Detection in CANopen Networks", U. Koppe, MicroControl GmbH & Co. KG + // CAN in Automation, 2003 + // + // According to the source, optimal quanta per bit are: + // Bitrate Optimal Maximum + // 1000 kbps 8 10 + // 500 kbps 16 17 + // 250 kbps 16 17 + // 125 kbps 16 17 + let max_quanta_per_bit: u8 = if can_bitrate >= 1_000_000 { 10 } else { 17 }; + + // Computing (prescaler * BS): + // BITRATE = 1 / (PRESCALER * (1 / PCLK) * (1 + BS1 + BS2)) -- See the Reference Manual + // BITRATE = PCLK / (PRESCALER * (1 + BS1 + BS2)) -- Simplified + // let: + // BS = 1 + BS1 + BS2 -- Number of time quanta per bit + // PRESCALER_BS = PRESCALER * BS + // ==> + // PRESCALER_BS = PCLK / BITRATE + let prescaler_bs = periph_clock / can_bitrate; + + // Searching for such prescaler value so that the number of quanta per bit is highest. + let mut bs1_bs2_sum = max_quanta_per_bit - 1; + while (prescaler_bs % (1 + bs1_bs2_sum) as u32) != 0 { + if bs1_bs2_sum <= 2 { + return None; // No solution + } + bs1_bs2_sum -= 1; + } + + let prescaler = prescaler_bs / (1 + bs1_bs2_sum) as u32; + if (prescaler < 1) || (prescaler > 1024) { + return None; // No solution + } + + // Now we have a constraint: (BS1 + BS2) == bs1_bs2_sum. + // We need to find such values so that the sample point is as close as possible to the optimal value, + // which is 87.5%, which is 7/8. + // + // Solve[(1 + bs1)/(1 + bs1 + bs2) == 7/8, bs2] (* Where 7/8 is 0.875, the recommended sample point location *) + // {{bs2 -> (1 + bs1)/7}} + // + // Hence: + // bs2 = (1 + bs1) / 7 + // bs1 = (7 * bs1_bs2_sum - 1) / 8 + // + // Sample point location can be computed as follows: + // Sample point location = (1 + bs1) / (1 + bs1 + bs2) + // + // Since the optimal solution is so close to the maximum, we prepare two solutions, and then pick the best one: + // - With rounding to nearest + // - With rounding to zero + let mut bs1 = ((7 * bs1_bs2_sum - 1) + 4) / 8; // Trying rounding to nearest first + let mut bs2 = bs1_bs2_sum - bs1; + core::assert!(bs1_bs2_sum > bs1); + + let sample_point_permill = 1000 * ((1 + bs1) / (1 + bs1 + bs2)) as u16; + if sample_point_permill > MAX_SAMPLE_POINT_PERMILL { + // Nope, too far; now rounding to zero + bs1 = (7 * bs1_bs2_sum - 1) / 8; + bs2 = bs1_bs2_sum - bs1; + } + + // Check is BS1 and BS2 are in range + if (bs1 < 1) || (bs1 > BS1_MAX) || (bs2 < 1) || (bs2 > BS2_MAX) { + return None; + } + + // Check if final bitrate matches the requested + if can_bitrate != (periph_clock / (prescaler * (1 + bs1 + bs2) as u32)) { + return None; + } + + // One is recommended by DS-015, CANOpen, and DeviceNet + let sync_jump_width = core::num::NonZeroU8::new(1)?; + + let seg1 = core::num::NonZeroU8::new(bs1)?; + let seg2 = core::num::NonZeroU8::new(bs2)?; + let nz_prescaler = core::num::NonZeroU16::new(prescaler as u16)?; + + Some(NominalBitTiming { + sync_jump_width, + prescaler: nz_prescaler, + seg1, + seg2, + }) +} diff --git a/embassy-stm32/src/rcc/g4.rs b/embassy-stm32/src/rcc/g4.rs index fca364c21..891f0490b 100644 --- a/embassy-stm32/src/rcc/g4.rs +++ b/embassy-stm32/src/rcc/g4.rs @@ -3,8 +3,8 @@ use stm32_metapac::rcc::vals::{Adcsel, Pllsrc, Sw}; use stm32_metapac::FLASH; pub use crate::pac::rcc::vals::{ - Adcsel as AdcClockSource, Hpre as AHBPrescaler, Pllm as PllM, Plln as PllN, Pllp as PllP, Pllq as PllQ, - Pllr as PllR, Ppre as APBPrescaler, + Adcsel as AdcClockSource, Fdcansel as FdCanClockSource, Hpre as AHBPrescaler, Pllm as PllM, Plln as PllN, + Pllp as PllP, Pllq as PllQ, Pllr as PllR, Ppre as APBPrescaler, }; use crate::pac::{PWR, RCC}; use crate::rcc::{set_freqs, Clocks}; @@ -87,6 +87,7 @@ pub struct Config { pub clock_48mhz_src: Option, pub adc12_clock_source: AdcClockSource, pub adc345_clock_source: AdcClockSource, + pub fdcan_clock_source: FdCanClockSource, pub ls: super::LsConfig, } @@ -104,6 +105,7 @@ impl Default for Config { clock_48mhz_src: Some(Clock48MhzSrc::Hsi48(Default::default())), adc12_clock_source: Adcsel::DISABLE, adc345_clock_source: Adcsel::DISABLE, + fdcan_clock_source: FdCanClockSource::PCLK1, ls: Default::default(), } } @@ -282,6 +284,7 @@ pub(crate) unsafe fn init(config: Config) { RCC.ccipr().modify(|w| w.set_adc12sel(config.adc12_clock_source)); RCC.ccipr().modify(|w| w.set_adc345sel(config.adc345_clock_source)); + RCC.ccipr().modify(|w| w.set_fdcansel(config.fdcan_clock_source)); let adc12_ck = match config.adc12_clock_source { AdcClockSource::DISABLE => None, diff --git a/embassy-stm32/src/rcc/h.rs b/embassy-stm32/src/rcc/h.rs index 15b51a398..85e12637e 100644 --- a/embassy-stm32/src/rcc/h.rs +++ b/embassy-stm32/src/rcc/h.rs @@ -6,6 +6,7 @@ use crate::pac::pwr::vals::Vos; pub use crate::pac::rcc::vals::Adcdacsel as AdcClockSource; #[cfg(stm32h7)] pub use crate::pac::rcc::vals::Adcsel as AdcClockSource; +pub use crate::pac::rcc::vals::Fdcansel as FdCanClockSource; pub use crate::pac::rcc::vals::{ Ckpersel as PerClockSource, Hsidiv as HSIPrescaler, Plldiv as PllDiv, Pllm as PllPreDiv, Plln as PllMul, Pllsrc as PllSource, Sw as Sysclk, @@ -212,6 +213,8 @@ pub struct Config { pub per_clock_source: PerClockSource, pub adc_clock_source: AdcClockSource, + pub fdcan_clock_source: FdCanClockSource, + pub timer_prescaler: TimerPrescaler, pub voltage_scale: VoltageScale, pub ls: super::LsConfig, @@ -248,6 +251,8 @@ impl Default for Config { #[cfg(stm32h7)] adc_clock_source: AdcClockSource::PER, + fdcan_clock_source: FdCanClockSource::from_bits(0), // HSE + timer_prescaler: TimerPrescaler::DefaultX2, voltage_scale: VoltageScale::Scale0, ls: Default::default(), @@ -585,7 +590,8 @@ pub(crate) unsafe fn init(config: Config) { RCC.ccipr5().modify(|w| { w.set_ckpersel(config.per_clock_source); - w.set_adcdacsel(config.adc_clock_source) + w.set_adcdacsel(config.adc_clock_source); + w.set_fdcan1sel(config.fdcan_clock_source) }); }