Add FDCAN clock registers to G4 RCC.
Author: Adam Morgan <adam@luci.com> Break definitions out of bxcan that can be used innm fdcan. Typo
This commit is contained in:
parent
a91a7a8557
commit
03ba45065e
6 changed files with 173 additions and 115 deletions
|
@ -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 {
|
||||
|
|
|
@ -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<BxcanInstance<'d, T>>,
|
||||
}
|
||||
|
||||
/// 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<u32> {
|
||||
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.
|
||||
|
|
30
embassy-stm32/src/can/enums.rs
Normal file
30
embassy-stm32/src/can/enums.rs
Normal file
|
@ -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,
|
||||
}
|
117
embassy-stm32/src/can/util.rs
Normal file
117
embassy-stm32/src/can/util.rs
Normal file
|
@ -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<NominalBitTiming> {
|
||||
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,
|
||||
})
|
||||
}
|
|
@ -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<Clock48MhzSrc>,
|
||||
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,
|
||||
|
|
|
@ -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)
|
||||
});
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue