475 lines
14 KiB
Rust
475 lines
14 KiB
Rust
//! Configuration for FDCAN Module
|
|
// Note: This file is copied and modified from fdcan crate by Richard Meadows
|
|
|
|
use core::num::{NonZeroU16, NonZeroU8};
|
|
|
|
/// Configures the bit timings.
|
|
///
|
|
/// You can use <http://www.bittiming.can-wiki.info/> to calculate the `btr` parameter. Enter
|
|
/// parameters as follows:
|
|
///
|
|
/// - *Clock Rate*: The input clock speed to the CAN peripheral (*not* the CPU clock speed).
|
|
/// This is the clock rate of the peripheral bus the CAN peripheral is attached to (eg. APB1).
|
|
/// - *Sample Point*: Should normally be left at the default value of 87.5%.
|
|
/// - *SJW*: Should normally be left at the default value of 1.
|
|
///
|
|
/// Then copy the `CAN_BUS_TIME` register value from the table and pass it as the `btr`
|
|
/// parameter to this method.
|
|
#[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,
|
|
}
|
|
impl NominalBitTiming {
|
|
#[inline]
|
|
pub(crate) fn nbrp(&self) -> u16 {
|
|
u16::from(self.prescaler) & 0x1FF
|
|
}
|
|
#[inline]
|
|
pub(crate) fn ntseg1(&self) -> u8 {
|
|
u8::from(self.seg1)
|
|
}
|
|
#[inline]
|
|
pub(crate) fn ntseg2(&self) -> u8 {
|
|
u8::from(self.seg2) & 0x7F
|
|
}
|
|
#[inline]
|
|
pub(crate) fn nsjw(&self) -> u8 {
|
|
u8::from(self.sync_jump_width) & 0x7F
|
|
}
|
|
}
|
|
|
|
impl Default for NominalBitTiming {
|
|
#[inline]
|
|
fn default() -> Self {
|
|
// Kernel Clock 8MHz, Bit rate: 500kbit/s. Corresponds to a NBTP
|
|
// register value of 0x0600_0A03
|
|
Self {
|
|
prescaler: NonZeroU16::new(1).unwrap(),
|
|
seg1: NonZeroU8::new(11).unwrap(),
|
|
seg2: NonZeroU8::new(4).unwrap(),
|
|
sync_jump_width: NonZeroU8::new(4).unwrap(),
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Configures the data bit timings for the FdCan Variable Bitrates.
|
|
/// This is not used when frame_transmit is set to anything other than AllowFdCanAndBRS.
|
|
#[derive(Clone, Copy, Debug)]
|
|
pub struct DataBitTiming {
|
|
/// Tranceiver Delay Compensation
|
|
pub transceiver_delay_compensation: bool,
|
|
/// The value by which the oscillator frequency is divided to generate the bit time quanta. The bit
|
|
/// time is built up from a multiple of this quanta. Valid values for the Baud Rate Prescaler are 1
|
|
/// to 31.
|
|
pub prescaler: NonZeroU16,
|
|
/// Valid values are 1 to 31.
|
|
pub seg1: NonZeroU8,
|
|
/// Valid values are 1 to 15.
|
|
pub seg2: NonZeroU8,
|
|
/// Must always be smaller than DTSEG2, valid values are 1 to 15.
|
|
pub sync_jump_width: NonZeroU8,
|
|
}
|
|
impl DataBitTiming {
|
|
// #[inline]
|
|
// fn tdc(&self) -> u8 {
|
|
// let tsd = self.transceiver_delay_compensation as u8;
|
|
// //TODO: stm32g4 does not export the TDC field
|
|
// todo!()
|
|
// }
|
|
#[inline]
|
|
pub(crate) fn dbrp(&self) -> u8 {
|
|
(u16::from(self.prescaler) & 0x001F) as u8
|
|
}
|
|
#[inline]
|
|
pub(crate) fn dtseg1(&self) -> u8 {
|
|
u8::from(self.seg1) & 0x1F
|
|
}
|
|
#[inline]
|
|
pub(crate) fn dtseg2(&self) -> u8 {
|
|
u8::from(self.seg2) & 0x0F
|
|
}
|
|
#[inline]
|
|
pub(crate) fn dsjw(&self) -> u8 {
|
|
u8::from(self.sync_jump_width) & 0x0F
|
|
}
|
|
}
|
|
|
|
impl Default for DataBitTiming {
|
|
#[inline]
|
|
fn default() -> Self {
|
|
// Kernel Clock 8MHz, Bit rate: 500kbit/s. Corresponds to a DBTP
|
|
// register value of 0x0000_0A33
|
|
Self {
|
|
transceiver_delay_compensation: false,
|
|
prescaler: NonZeroU16::new(1).unwrap(),
|
|
seg1: NonZeroU8::new(11).unwrap(),
|
|
seg2: NonZeroU8::new(4).unwrap(),
|
|
sync_jump_width: NonZeroU8::new(4).unwrap(),
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Configures which modes to use
|
|
/// Individual headers can contain a desire to be send via FdCan
|
|
/// or use Bit rate switching. But if this general setting does not allow
|
|
/// that, only classic CAN is used instead.
|
|
#[derive(Clone, Copy, Debug)]
|
|
pub enum FrameTransmissionConfig {
|
|
/// Only allow Classic CAN message Frames
|
|
ClassicCanOnly,
|
|
/// Allow (non-brs) FdCAN Message Frames
|
|
AllowFdCan,
|
|
/// Allow FdCAN Message Frames and allow Bit Rate Switching
|
|
AllowFdCanAndBRS,
|
|
}
|
|
|
|
///
|
|
#[derive(Clone, Copy, Debug)]
|
|
pub enum ClockDivider {
|
|
/// Divide by 1
|
|
_1 = 0b0000,
|
|
/// Divide by 2
|
|
_2 = 0b0001,
|
|
/// Divide by 4
|
|
_4 = 0b0010,
|
|
/// Divide by 6
|
|
_6 = 0b0011,
|
|
/// Divide by 8
|
|
_8 = 0b0100,
|
|
/// Divide by 10
|
|
_10 = 0b0101,
|
|
/// Divide by 12
|
|
_12 = 0b0110,
|
|
/// Divide by 14
|
|
_14 = 0b0111,
|
|
/// Divide by 16
|
|
_16 = 0b1000,
|
|
/// Divide by 18
|
|
_18 = 0b1001,
|
|
/// Divide by 20
|
|
_20 = 0b1010,
|
|
/// Divide by 22
|
|
_22 = 0b1011,
|
|
/// Divide by 24
|
|
_24 = 0b1100,
|
|
/// Divide by 26
|
|
_26 = 0b1101,
|
|
/// Divide by 28
|
|
_28 = 0b1110,
|
|
/// Divide by 30
|
|
_30 = 0b1111,
|
|
}
|
|
|
|
/// Prescaler of the Timestamp counter
|
|
#[derive(Clone, Copy, Debug)]
|
|
pub enum TimestampPrescaler {
|
|
/// 1
|
|
_1 = 1,
|
|
/// 2
|
|
_2 = 2,
|
|
/// 3
|
|
_3 = 3,
|
|
/// 4
|
|
_4 = 4,
|
|
/// 5
|
|
_5 = 5,
|
|
/// 6
|
|
_6 = 6,
|
|
/// 7
|
|
_7 = 7,
|
|
/// 8
|
|
_8 = 8,
|
|
/// 9
|
|
_9 = 9,
|
|
/// 10
|
|
_10 = 10,
|
|
/// 11
|
|
_11 = 11,
|
|
/// 12
|
|
_12 = 12,
|
|
/// 13
|
|
_13 = 13,
|
|
/// 14
|
|
_14 = 14,
|
|
/// 15
|
|
_15 = 15,
|
|
/// 16
|
|
_16 = 16,
|
|
}
|
|
|
|
/// Selects the source of the Timestamp counter
|
|
#[derive(Clone, Copy, Debug)]
|
|
pub enum TimestampSource {
|
|
/// The Timestamp counter is disabled
|
|
None,
|
|
/// Using the FdCan input clock as the Timstamp counter's source,
|
|
/// and using a specific prescaler
|
|
Prescaler(TimestampPrescaler),
|
|
/// Using TIM3 as a source
|
|
FromTIM3,
|
|
}
|
|
|
|
/// How to handle frames in the global filter
|
|
#[derive(Clone, Copy, Debug)]
|
|
pub enum NonMatchingFilter {
|
|
/// Frames will go to Fifo0 when they do no match any specific filter
|
|
IntoRxFifo0 = 0b00,
|
|
/// Frames will go to Fifo1 when they do no match any specific filter
|
|
IntoRxFifo1 = 0b01,
|
|
/// Frames will be rejected when they do not match any specific filter
|
|
Reject = 0b11,
|
|
}
|
|
|
|
/// How to handle frames which do not match a specific filter
|
|
#[derive(Clone, Copy, Debug)]
|
|
pub struct GlobalFilter {
|
|
/// How to handle non-matching standard frames
|
|
pub handle_standard_frames: NonMatchingFilter,
|
|
|
|
/// How to handle non-matching extended frames
|
|
pub handle_extended_frames: NonMatchingFilter,
|
|
|
|
/// How to handle remote standard frames
|
|
pub reject_remote_standard_frames: bool,
|
|
|
|
/// How to handle remote extended frames
|
|
pub reject_remote_extended_frames: bool,
|
|
}
|
|
impl GlobalFilter {
|
|
/// Reject all non-matching and remote frames
|
|
pub const fn reject_all() -> Self {
|
|
Self {
|
|
handle_standard_frames: NonMatchingFilter::Reject,
|
|
handle_extended_frames: NonMatchingFilter::Reject,
|
|
reject_remote_standard_frames: true,
|
|
reject_remote_extended_frames: true,
|
|
}
|
|
}
|
|
|
|
/// How to handle non-matching standard frames
|
|
pub const fn set_handle_standard_frames(mut self, filter: NonMatchingFilter) -> Self {
|
|
self.handle_standard_frames = filter;
|
|
self
|
|
}
|
|
/// How to handle non-matching exteded frames
|
|
pub const fn set_handle_extended_frames(mut self, filter: NonMatchingFilter) -> Self {
|
|
self.handle_extended_frames = filter;
|
|
self
|
|
}
|
|
/// How to handle remote standard frames
|
|
pub const fn set_reject_remote_standard_frames(mut self, filter: bool) -> Self {
|
|
self.reject_remote_standard_frames = filter;
|
|
self
|
|
}
|
|
/// How to handle remote extended frames
|
|
pub const fn set_reject_remote_extended_frames(mut self, filter: bool) -> Self {
|
|
self.reject_remote_extended_frames = filter;
|
|
self
|
|
}
|
|
}
|
|
impl Default for GlobalFilter {
|
|
#[inline]
|
|
fn default() -> Self {
|
|
Self {
|
|
handle_standard_frames: NonMatchingFilter::IntoRxFifo0,
|
|
handle_extended_frames: NonMatchingFilter::IntoRxFifo0,
|
|
reject_remote_standard_frames: false,
|
|
reject_remote_extended_frames: false,
|
|
}
|
|
}
|
|
}
|
|
|
|
/// TX buffer operation mode
|
|
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
|
|
pub enum TxBufferMode {
|
|
/// TX FIFO operation - In this mode CAN frames are trasmitted strictly in write order.
|
|
Fifo,
|
|
/// TX priority queue operation - In this mode CAN frames are transmitted according to CAN priority.
|
|
Priority,
|
|
}
|
|
|
|
impl From<TxBufferMode> for crate::pac::can::vals::Tfqm {
|
|
fn from(value: TxBufferMode) -> Self {
|
|
match value {
|
|
TxBufferMode::Priority => Self::QUEUE,
|
|
TxBufferMode::Fifo => Self::FIFO,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<crate::pac::can::vals::Tfqm> for TxBufferMode {
|
|
fn from(value: crate::pac::can::vals::Tfqm) -> Self {
|
|
match value {
|
|
crate::pac::can::vals::Tfqm::QUEUE => Self::Priority,
|
|
crate::pac::can::vals::Tfqm::FIFO => Self::Fifo,
|
|
}
|
|
}
|
|
}
|
|
|
|
/// FdCan Config Struct
|
|
#[derive(Clone, Copy, Debug)]
|
|
pub struct FdCanConfig {
|
|
/// Nominal Bit Timings
|
|
pub nbtr: NominalBitTiming,
|
|
/// (Variable) Data Bit Timings
|
|
pub dbtr: DataBitTiming,
|
|
/// Enables or disables automatic retransmission of messages
|
|
///
|
|
/// If this is enabled, the CAN peripheral will automatically try to retransmit each frame
|
|
/// util it can be sent. Otherwise, it will try only once to send each frame.
|
|
///
|
|
/// Automatic retransmission is enabled by default.
|
|
pub automatic_retransmit: bool,
|
|
/// Enabled or disables the pausing between transmissions
|
|
///
|
|
/// This feature looses up burst transmissions coming from a single node and it protects against
|
|
/// "babbling idiot" scenarios where the application program erroneously requests too many
|
|
/// transmissions.
|
|
pub transmit_pause: bool,
|
|
/// Enabled or disables the pausing between transmissions
|
|
///
|
|
/// This feature looses up burst transmissions coming from a single node and it protects against
|
|
/// "babbling idiot" scenarios where the application program erroneously requests too many
|
|
/// transmissions.
|
|
pub frame_transmit: FrameTransmissionConfig,
|
|
/// Non Isoe Mode
|
|
/// If this is set, the FDCAN uses the CAN FD frame format as specified by the Bosch CAN
|
|
/// FD Specification V1.0.
|
|
pub non_iso_mode: bool,
|
|
/// Edge Filtering: Two consecutive dominant tq required to detect an edge for hard synchronization
|
|
pub edge_filtering: bool,
|
|
/// Enables protocol exception handling
|
|
pub protocol_exception_handling: bool,
|
|
/// Sets the general clock divider for this FdCAN instance
|
|
pub clock_divider: ClockDivider,
|
|
/// Sets the timestamp source
|
|
pub timestamp_source: TimestampSource,
|
|
/// Configures the Global Filter
|
|
pub global_filter: GlobalFilter,
|
|
/// TX buffer mode (FIFO or priority queue)
|
|
pub tx_buffer_mode: TxBufferMode,
|
|
}
|
|
|
|
impl FdCanConfig {
|
|
/// Configures the bit timings.
|
|
#[inline]
|
|
pub const fn set_nominal_bit_timing(mut self, btr: NominalBitTiming) -> Self {
|
|
self.nbtr = btr;
|
|
self
|
|
}
|
|
|
|
/// Configures the bit timings.
|
|
#[inline]
|
|
pub const fn set_data_bit_timing(mut self, btr: DataBitTiming) -> Self {
|
|
self.dbtr = btr;
|
|
self
|
|
}
|
|
|
|
/// Enables or disables automatic retransmission of messages
|
|
///
|
|
/// If this is enabled, the CAN peripheral will automatically try to retransmit each frame
|
|
/// util it can be sent. Otherwise, it will try only once to send each frame.
|
|
///
|
|
/// Automatic retransmission is enabled by default.
|
|
#[inline]
|
|
pub const fn set_automatic_retransmit(mut self, enabled: bool) -> Self {
|
|
self.automatic_retransmit = enabled;
|
|
self
|
|
}
|
|
|
|
/// Enabled or disables the pausing between transmissions
|
|
///
|
|
/// This feature looses up burst transmissions coming from a single node and it protects against
|
|
/// "babbling idiot" scenarios where the application program erroneously requests too many
|
|
/// transmissions.
|
|
#[inline]
|
|
pub const fn set_transmit_pause(mut self, enabled: bool) -> Self {
|
|
self.transmit_pause = enabled;
|
|
self
|
|
}
|
|
|
|
/// If this is set, the FDCAN uses the CAN FD frame format as specified by the Bosch CAN
|
|
/// FD Specification V1.0.
|
|
#[inline]
|
|
pub const fn set_non_iso_mode(mut self, enabled: bool) -> Self {
|
|
self.non_iso_mode = enabled;
|
|
self
|
|
}
|
|
|
|
/// Two consecutive dominant tq required to detect an edge for hard synchronization
|
|
#[inline]
|
|
pub const fn set_edge_filtering(mut self, enabled: bool) -> Self {
|
|
self.edge_filtering = enabled;
|
|
self
|
|
}
|
|
|
|
/// Sets the allowed transmission types for messages.
|
|
#[inline]
|
|
pub const fn set_frame_transmit(mut self, fts: FrameTransmissionConfig) -> Self {
|
|
self.frame_transmit = fts;
|
|
self
|
|
}
|
|
|
|
/// Enables protocol exception handling
|
|
#[inline]
|
|
pub const fn set_protocol_exception_handling(mut self, peh: bool) -> Self {
|
|
self.protocol_exception_handling = peh;
|
|
self
|
|
}
|
|
|
|
/// Sets the general clock divider for this FdCAN instance
|
|
#[inline]
|
|
pub const fn set_clock_divider(mut self, div: ClockDivider) -> Self {
|
|
self.clock_divider = div;
|
|
self
|
|
}
|
|
|
|
/// Sets the timestamp source
|
|
#[inline]
|
|
pub const fn set_timestamp_source(mut self, tss: TimestampSource) -> Self {
|
|
self.timestamp_source = tss;
|
|
self
|
|
}
|
|
|
|
/// Sets the global filter settings
|
|
#[inline]
|
|
pub const fn set_global_filter(mut self, filter: GlobalFilter) -> Self {
|
|
self.global_filter = filter;
|
|
self
|
|
}
|
|
|
|
/// Sets the TX buffer mode (FIFO or priority queue)
|
|
#[inline]
|
|
pub const fn set_tx_buffer_mode(mut self, txbm: TxBufferMode) -> Self {
|
|
self.tx_buffer_mode = txbm;
|
|
self
|
|
}
|
|
}
|
|
|
|
impl Default for FdCanConfig {
|
|
#[inline]
|
|
fn default() -> Self {
|
|
Self {
|
|
nbtr: NominalBitTiming::default(),
|
|
dbtr: DataBitTiming::default(),
|
|
automatic_retransmit: true,
|
|
transmit_pause: false,
|
|
frame_transmit: FrameTransmissionConfig::ClassicCanOnly,
|
|
non_iso_mode: false,
|
|
edge_filtering: false,
|
|
protocol_exception_handling: true,
|
|
clock_divider: ClockDivider::_1,
|
|
timestamp_source: TimestampSource::None,
|
|
global_filter: GlobalFilter::default(),
|
|
tx_buffer_mode: TxBufferMode::Priority,
|
|
}
|
|
}
|
|
}
|