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:
Tomasz bla Fortuna 2024-01-14 09:47:26 +01:00 committed by Corey Schuhen
parent a91a7a8557
commit 03ba45065e
6 changed files with 173 additions and 115 deletions

View file

@ -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 {

View file

@ -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.

View 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,
}

View 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,
})
}

View file

@ -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,

View file

@ -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)
});
}