|
|
|
@ -58,91 +58,6 @@ pub const fn calculate_sample_point(tseg1: u8, tseg2: u8) -> f32 {
|
|
|
|
|
(tseg1_val + 1.0) / (1.0 + tseg1_val + tseg2 as f32)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Calculate all viable clock configurations for the given input clock, the target bitrate and
|
|
|
|
|
/// for a sample point between 0.5 and 1.0.
|
|
|
|
|
///
|
|
|
|
|
/// There are various recommendations for the sample point when using the CAN bus. The value
|
|
|
|
|
/// depends on different parameters like the bus length and propagation time, as well as
|
|
|
|
|
/// the information processing time of the nodes. It should always be at least 50 %.
|
|
|
|
|
/// In doubt, select a value like 0.75.
|
|
|
|
|
///
|
|
|
|
|
/// - The [Python CAN library](https://python-can.readthedocs.io/en/stable/bit_timing.html)
|
|
|
|
|
/// assumes a default value of 69 % as the sample point if none is specified.
|
|
|
|
|
/// - CiA-301 recommends 87.5 %
|
|
|
|
|
/// - For simpler setups like laboratory setups, smaller values should work as well.
|
|
|
|
|
///
|
|
|
|
|
/// A clock configuration is consideres viable when
|
|
|
|
|
///
|
|
|
|
|
/// - The sample point deviation is less than 5 %.
|
|
|
|
|
/// - The bitrate error is less than +-0.5 %.
|
|
|
|
|
///
|
|
|
|
|
/// SJW will be set to either TSEG2 or 4, whichever is smaller.
|
|
|
|
|
#[cfg(feature = "alloc")]
|
|
|
|
|
pub fn calculate_all_viable_clock_configs(
|
|
|
|
|
apb1_clock: Hertz,
|
|
|
|
|
bitrate: Hertz,
|
|
|
|
|
sample_point: f32,
|
|
|
|
|
) -> Result<alloc::vec::Vec<ClockConfig>, InvalidSamplePointError> {
|
|
|
|
|
if sample_point < 0.5 || sample_point > 1.0 {
|
|
|
|
|
return Err(InvalidSamplePointError { sample_point });
|
|
|
|
|
}
|
|
|
|
|
let mut configs = alloc::vec::Vec::new();
|
|
|
|
|
for prescaler in PRESCALER_MIN..PRESCALER_MAX {
|
|
|
|
|
let nom_bit_time = apb1_clock / (bitrate * prescaler as u32);
|
|
|
|
|
// This is taken from the Python CAN library. NBT should not be too small.
|
|
|
|
|
if nom_bit_time < 8 {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
let actual_bitrate = apb1_clock / (prescaler as u32 * nom_bit_time);
|
|
|
|
|
let bitrate_deviation = ((actual_bitrate.raw() as i32 - bitrate.raw() as i32).abs() as f32)
|
|
|
|
|
/ bitrate.raw() as f32;
|
|
|
|
|
if bitrate_deviation > 0.05 {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
let tseg1 = roundf(sample_point * nom_bit_time as f32) as u32 - 1;
|
|
|
|
|
if tseg1 > TSEG1_MAX as u32 || tseg1 < TSEG1_MIN as u32 {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
// limit tseg1, so tseg2 is at least 1 TQ
|
|
|
|
|
let tseg1 = core::cmp::min(tseg1, nom_bit_time - 2) as u8;
|
|
|
|
|
let tseg2 = nom_bit_time - tseg1 as u32 - 1;
|
|
|
|
|
if tseg2 > TSEG2_MAX as u32 {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
let tseg2 = tseg2 as u8;
|
|
|
|
|
let sjw = core::cmp::min(tseg2, 4) as u8;
|
|
|
|
|
// Use percent to have a higher resolution for the sample point deviation.
|
|
|
|
|
let sample_point_actual = roundf(calculate_sample_point(tseg1, tseg2) * 100.0) as u32;
|
|
|
|
|
let sample_point = roundf(sample_point * 100.0) as u32;
|
|
|
|
|
let deviation = (sample_point_actual as i32 - sample_point as i32).abs();
|
|
|
|
|
if deviation > 5 {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
configs.push(ClockConfig {
|
|
|
|
|
prescaler,
|
|
|
|
|
tseg1,
|
|
|
|
|
tseg2,
|
|
|
|
|
sjw,
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
Ok(configs)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub trait CanMarker {
|
|
|
|
|
const ID: CanId;
|
|
|
|
|
const PERIPH_SEL: PeripheralSelect;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl CanMarker for va416xx::Can0 {
|
|
|
|
|
const ID: CanId = CanId::Can0;
|
|
|
|
|
const PERIPH_SEL: PeripheralSelect = PeripheralSelect::Can0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl CanMarker for va416xx::Can1 {
|
|
|
|
|
const ID: CanId = CanId::Can1;
|
|
|
|
|
const PERIPH_SEL: PeripheralSelect = PeripheralSelect::Can1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, Clone, Copy)]
|
|
|
|
|
pub struct ClockConfig {
|
|
|
|
|
prescaler: u8,
|
|
|
|
@ -151,41 +66,6 @@ pub struct ClockConfig {
|
|
|
|
|
sjw: u8,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, thiserror::Error)]
|
|
|
|
|
#[error("invalid buffer index {0}")]
|
|
|
|
|
pub struct InvalidBufferIndexError(usize);
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, thiserror::Error)]
|
|
|
|
|
#[error("sjw must be less than or equal to the smaller tseg value")]
|
|
|
|
|
pub struct InvalidSjwError(u8);
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, thiserror::Error)]
|
|
|
|
|
#[error("invalid sample point {sample_point}")]
|
|
|
|
|
pub struct InvalidSamplePointError {
|
|
|
|
|
/// Sample point, should be larger than 0.5 (50 %) but was not.
|
|
|
|
|
sample_point: f32,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, thiserror::Error)]
|
|
|
|
|
pub enum ClockConfigError {
|
|
|
|
|
#[error("invalid sjw: {0}")]
|
|
|
|
|
InvalidSjw(#[from] InvalidSjwError),
|
|
|
|
|
#[error("TSEG is zero which is not allowed")]
|
|
|
|
|
TsegIsZero,
|
|
|
|
|
#[error("TSEG1 is larger than 16")]
|
|
|
|
|
InvalidTseg1,
|
|
|
|
|
#[error("TSEG1 is larger than 8")]
|
|
|
|
|
InvalidTseg2,
|
|
|
|
|
#[error("invalid sample point: {0}")]
|
|
|
|
|
InvalidSamplePoint(#[from] InvalidSamplePointError),
|
|
|
|
|
#[error("bitrate is zero")]
|
|
|
|
|
BitrateIsZero,
|
|
|
|
|
#[error("bitrate error larger than +-0.5 %")]
|
|
|
|
|
BitrateErrorTooLarge,
|
|
|
|
|
#[error("maximum or minimum allowed prescaler is not sufficient for target bitrate clock")]
|
|
|
|
|
CanNotFindPrescaler,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl ClockConfig {
|
|
|
|
|
/// New clock configuration from the raw configuration values.
|
|
|
|
|
///
|
|
|
|
@ -245,17 +125,20 @@ impl ClockConfig {
|
|
|
|
|
if bitrate.raw() == 0 {
|
|
|
|
|
return Err(ClockConfigError::BitrateIsZero);
|
|
|
|
|
}
|
|
|
|
|
let prescaler = roundf(
|
|
|
|
|
let mut prescaler = roundf(
|
|
|
|
|
clocks.apb1().raw() as f32
|
|
|
|
|
/ (bitrate.raw() as f32 * (1.0 + tseg1.as_u32() as f32 + tseg2.as_u32() as f32)),
|
|
|
|
|
) as u32;
|
|
|
|
|
// defmt::info!("calc prescaler: {}", prescaler);
|
|
|
|
|
if !(PRESCALER_MIN as u32..=PRESCALER_MAX as u32).contains(&prescaler) {
|
|
|
|
|
return Err(ClockConfigError::CanNotFindPrescaler);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let actual_bitrate = clocks.apb1() / (prescaler * (1 + tseg1.as_u32() + tseg2.as_u32()));
|
|
|
|
|
let bitrate_deviation = ((actual_bitrate.raw() as i32 - bitrate.raw() as i32).abs() as f32)
|
|
|
|
|
/ bitrate.raw() as f32;
|
|
|
|
|
let actual_bitrate = (clocks.apb1().raw() as f32)
|
|
|
|
|
/ (prescaler * (1 + tseg1.as_u32() + tseg2.as_u32())) as f32;
|
|
|
|
|
let bitrate_deviation =
|
|
|
|
|
((actual_bitrate as i32 - bitrate.raw() as i32).abs() as f32) / bitrate.raw() as f32;
|
|
|
|
|
//defmt::info!("actual bitrate: {}, target {}", actual_bitrate, bitrate);
|
|
|
|
|
if bitrate_deviation > MAX_BITRATE_DEVIATION {
|
|
|
|
|
return Err(ClockConfigError::BitrateErrorTooLarge);
|
|
|
|
|
}
|
|
|
|
@ -280,6 +163,144 @@ impl ClockConfig {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Calculate all viable clock configurations for the given input clock, the target bitrate and
|
|
|
|
|
/// for a sample point between 0.5 and 1.0.
|
|
|
|
|
///
|
|
|
|
|
/// There are various recommendations for the sample point when using the CAN bus. The value
|
|
|
|
|
/// depends on different parameters like the bus length and propagation time, as well as
|
|
|
|
|
/// the information processing time of the nodes. It should always be at least 50 %.
|
|
|
|
|
/// In doubt, select a value like 0.75.
|
|
|
|
|
///
|
|
|
|
|
/// - The [Python CAN library](https://python-can.readthedocs.io/en/stable/bit_timing.html)
|
|
|
|
|
/// assumes a default value of 69 % as the sample point if none is specified.
|
|
|
|
|
/// - CiA-301 recommends 87.5 %
|
|
|
|
|
/// - For simpler setups like laboratory setups, smaller values should work as well.
|
|
|
|
|
///
|
|
|
|
|
/// A clock configuration is consideres viable when
|
|
|
|
|
///
|
|
|
|
|
/// - The sample point deviation is less than 5 %.
|
|
|
|
|
/// - The bitrate error is less than +-0.5 %.
|
|
|
|
|
///
|
|
|
|
|
/// SJW will be set to either TSEG2 or 4, whichever is smaller.
|
|
|
|
|
#[cfg(feature = "alloc")]
|
|
|
|
|
pub fn calculate_all_viable_clock_configs(
|
|
|
|
|
apb1_clock: Hertz,
|
|
|
|
|
bitrate: Hertz,
|
|
|
|
|
sample_point: f32,
|
|
|
|
|
) -> Result<alloc::vec::Vec<(ClockConfig, f32)>, InvalidSamplePointError> {
|
|
|
|
|
if sample_point < 0.5 || sample_point > 1.0 {
|
|
|
|
|
return Err(InvalidSamplePointError { sample_point });
|
|
|
|
|
}
|
|
|
|
|
let mut configs = alloc::vec::Vec::new();
|
|
|
|
|
for prescaler in PRESCALER_MIN..PRESCALER_MAX {
|
|
|
|
|
let nom_bit_time = calculate_nominal_bit_time(apb1_clock, bitrate, prescaler);
|
|
|
|
|
// This is taken from the Python CAN library. NBT should not be too small.
|
|
|
|
|
if nom_bit_time < 8 {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
let actual_bitrate = calculate_actual_bitrate(apb1_clock, prescaler, nom_bit_time);
|
|
|
|
|
let bitrate_deviation = calculate_bitrate_deviation(actual_bitrate, bitrate);
|
|
|
|
|
if bitrate_deviation > 0.05 {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
let tseg1 = roundf(sample_point * nom_bit_time as f32) as u32 - 1;
|
|
|
|
|
if tseg1 > TSEG1_MAX as u32 || tseg1 < TSEG1_MIN as u32 {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
// limit tseg1, so tseg2 is at least 1 TQ
|
|
|
|
|
let tseg1 = core::cmp::min(tseg1, nom_bit_time - 2) as u8;
|
|
|
|
|
let tseg2 = nom_bit_time - tseg1 as u32 - 1;
|
|
|
|
|
if tseg2 > TSEG2_MAX as u32 {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
let tseg2 = tseg2 as u8;
|
|
|
|
|
let sjw = core::cmp::min(tseg2, 4) as u8;
|
|
|
|
|
// Use percent to have a higher resolution for the sample point deviation.
|
|
|
|
|
let sample_point_actual = roundf(calculate_sample_point(tseg1, tseg2) * 100.0) as u32;
|
|
|
|
|
let sample_point = roundf(sample_point * 100.0) as u32;
|
|
|
|
|
let deviation = (sample_point_actual as i32 - sample_point as i32).abs();
|
|
|
|
|
if deviation > 5 {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
configs.push((
|
|
|
|
|
ClockConfig {
|
|
|
|
|
prescaler,
|
|
|
|
|
tseg1,
|
|
|
|
|
tseg2,
|
|
|
|
|
sjw,
|
|
|
|
|
},
|
|
|
|
|
bitrate_deviation,
|
|
|
|
|
));
|
|
|
|
|
}
|
|
|
|
|
Ok(configs)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub const fn calculate_nominal_bit_time(
|
|
|
|
|
apb1_clock: Hertz,
|
|
|
|
|
target_bitrate: Hertz,
|
|
|
|
|
prescaler: u8,
|
|
|
|
|
) -> u32 {
|
|
|
|
|
apb1_clock.raw() / (target_bitrate.raw() * prescaler as u32)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub const fn calculate_actual_bitrate(apb1_clock: Hertz, prescaler: u8, nom_bit_time: u32) -> f32 {
|
|
|
|
|
apb1_clock.raw() as f32 / (prescaler as u32 * nom_bit_time) as f32
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub const fn calculate_bitrate_deviation(actual_bitrate: f32, target_bitrate: Hertz) -> f32 {
|
|
|
|
|
(actual_bitrate - target_bitrate.raw() as f32).abs() / target_bitrate.raw() as f32
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub trait CanMarker {
|
|
|
|
|
const ID: CanId;
|
|
|
|
|
const PERIPH_SEL: PeripheralSelect;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl CanMarker for va416xx::Can0 {
|
|
|
|
|
const ID: CanId = CanId::Can0;
|
|
|
|
|
const PERIPH_SEL: PeripheralSelect = PeripheralSelect::Can0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl CanMarker for va416xx::Can1 {
|
|
|
|
|
const ID: CanId = CanId::Can1;
|
|
|
|
|
const PERIPH_SEL: PeripheralSelect = PeripheralSelect::Can1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, thiserror::Error)]
|
|
|
|
|
#[error("invalid buffer index {0}")]
|
|
|
|
|
pub struct InvalidBufferIndexError(usize);
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, thiserror::Error)]
|
|
|
|
|
#[error("sjw must be less than or equal to the smaller tseg value")]
|
|
|
|
|
pub struct InvalidSjwError(u8);
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, thiserror::Error)]
|
|
|
|
|
#[error("invalid sample point {sample_point}")]
|
|
|
|
|
pub struct InvalidSamplePointError {
|
|
|
|
|
/// Sample point, should be larger than 0.5 (50 %) but was not.
|
|
|
|
|
sample_point: f32,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, thiserror::Error)]
|
|
|
|
|
pub enum ClockConfigError {
|
|
|
|
|
#[error("invalid sjw: {0}")]
|
|
|
|
|
InvalidSjw(#[from] InvalidSjwError),
|
|
|
|
|
#[error("TSEG is zero which is not allowed")]
|
|
|
|
|
TsegIsZero,
|
|
|
|
|
#[error("TSEG1 is larger than 16")]
|
|
|
|
|
InvalidTseg1,
|
|
|
|
|
#[error("TSEG1 is larger than 8")]
|
|
|
|
|
InvalidTseg2,
|
|
|
|
|
#[error("invalid sample point: {0}")]
|
|
|
|
|
InvalidSamplePoint(#[from] InvalidSamplePointError),
|
|
|
|
|
#[error("bitrate is zero")]
|
|
|
|
|
BitrateIsZero,
|
|
|
|
|
#[error("bitrate error larger than +-0.5 %")]
|
|
|
|
|
BitrateErrorTooLarge,
|
|
|
|
|
#[error("maximum or minimum allowed prescaler is not sufficient for target bitrate clock")]
|
|
|
|
|
CanNotFindPrescaler,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub struct Can {
|
|
|
|
|
regs: regs::MmioCan<'static>,
|
|
|
|
|
id: CanId,
|
|
|
|
@ -314,7 +335,8 @@ impl Can {
|
|
|
|
|
/// with the ID in the receive message buffers. This is the default reset configuration for
|
|
|
|
|
/// the global mask as well.
|
|
|
|
|
pub fn set_global_mask_for_exact_id_match(&mut self) {
|
|
|
|
|
self.regs.write_gmskx(regs::ExtendedId::new_with_raw_value(0));
|
|
|
|
|
self.regs
|
|
|
|
|
.write_gmskx(regs::ExtendedId::new_with_raw_value(0));
|
|
|
|
|
self.regs.write_gmskb(BaseId::new_with_raw_value(0));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -352,7 +374,8 @@ impl Can {
|
|
|
|
|
/// exact match with the ID in the receive message buffers. This is the default reset
|
|
|
|
|
/// configuration for the global mask as well.
|
|
|
|
|
pub fn set_base_mask_for_exact_id_match(&mut self) {
|
|
|
|
|
self.regs.write_bmskx(regs::ExtendedId::new_with_raw_value(0));
|
|
|
|
|
self.regs
|
|
|
|
|
.write_bmskx(regs::ExtendedId::new_with_raw_value(0));
|
|
|
|
|
self.regs.write_bmskb(BaseId::new_with_raw_value(0));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|