diff --git a/embassy-stm32/src/can/fd/config/message_ram.rs b/embassy-stm32/src/can/fd/config/message_ram.rs
new file mode 100644
index 000000000..af5846c0e
--- /dev/null
+++ b/embassy-stm32/src/can/fd/config/message_ram.rs
@@ -0,0 +1,169 @@
+/// For RX:
+/// Excess data is IGNORED, only the number of bytes which fit
+/// into the element are stored.
+///
+/// For TX:
+/// If DLC is higher than the data field size, excess bytes are
+/// transmitted as 0xCC (padding bytes).
+#[derive(Clone, Copy, Debug)]
+pub enum DataFieldSize {
+ /// Supports storing DLC up to 8 bytes.
+ B8 = 0b000,
+ /// Supports storing DLC up to 12 bytes.
+ B12 = 0b001,
+ /// Supports storing DLC up to 16 bytes.
+ B16 = 0b010,
+ /// Supports storing DLC up to 20 bytes.
+ B20 = 0b011,
+ /// Supports storing DLC up to 24 bytes.
+ B24 = 0b100,
+ /// Supports storing DLC up to 32 bytes.
+ B32 = 0b101,
+ /// Supports storing DLC up to 48 bytes.
+ B48 = 0b110,
+ /// Supports storing DLC up to 64 bytes.
+ B64 = 0b111,
+}
+
+impl DataFieldSize {
+ pub(crate) fn reg_value(self) -> u8 {
+ self as u8
+ }
+
+ /// Returns the max byte size for this setting.
+ pub fn byte_size(self) -> usize {
+ match self {
+ DataFieldSize::B8 => 8,
+ DataFieldSize::B12 => 12,
+ DataFieldSize::B16 => 16,
+ DataFieldSize::B20 => 20,
+ DataFieldSize::B24 => 24,
+ DataFieldSize::B32 => 32,
+ DataFieldSize::B48 => 48,
+ DataFieldSize::B64 => 64,
+ }
+ }
+
+ pub(crate) fn word_size(self) -> usize {
+ self.byte_size() / 4
+ }
+}
+
+/// Configuration for an Rx FIFO
+#[derive(Clone, Copy, Debug)]
+pub struct RxFifoConfig {
+ /// 0: Disabled
+ /// 1-64: Watermark interrupt level
+ /// >64: Disabled
+ pub watermark_interrupt_level: u8,
+ /// 0-64: Number of RX FIFO elements
+ pub fifo_size: u8,
+
+ /// The data size for each Tx buffer. This will indicate the max
+ /// data length you will be able to send.
+ ///
+ /// If you are using Classic CAN only, there is no reason to set
+ /// this to any value above B8.
+ ///
+ /// If you receive a frame with data that doesn't fit within the
+ /// configured data field size, the data will be truncated.
+ pub data_field_size: DataFieldSize,
+}
+
+impl RxFifoConfig {
+ /// Configuration which disables the FIFO.
+ pub const DISABLED: Self = RxFifoConfig {
+ watermark_interrupt_level: 0,
+ fifo_size: 0,
+ data_field_size: DataFieldSize::B8,
+ };
+}
+
+/// Configuration for an RX Buffer
+#[derive(Clone, Copy, Debug)]
+pub struct RxBufferConfig {
+ /// 0-64: Number of RX Buffer elements
+ pub size: u8,
+
+ /// The data size for each Tx buffer. This will indicate the max
+ /// data length you will be able to send.
+ ///
+ /// If you are using Classic CAN only, there is no reason to set
+ /// this to any value above B8.
+ ///
+ /// If you receive a frame with data that doesn't fit within the
+ /// configured data field size, the data will be truncated.
+ pub data_field_size: DataFieldSize,
+}
+
+impl RxBufferConfig {
+ /// Configuration which disables the buffer.
+ pub const DISABLED: Self = RxBufferConfig {
+ size: 0,
+ data_field_size: DataFieldSize::B8,
+ };
+}
+
+/// Configuration for TX buffers
+#[derive(Clone, Copy, Debug)]
+pub struct TxConfig {
+ /// Number of elements reserved for TX Queue.
+ /// NOTE: queue_size + dedicated_size may not be greater than 32.
+ ///
+ /// 0-32: Number of TX buffers used for TX FIFO/Priority queue
+ pub queue_size: u8,
+ /// Number of elements reserved for Dedicated TX buffers.
+ /// NOTE: queue_size + dedicated_size may not be greater than 32.
+ ///
+ /// 0-32: Number of TX buffers used for TX dedicated buffers
+ pub dedicated_size: u8,
+ /// The data size for each Tx buffer. This will indicate the max
+ /// data length you will be able to send.
+ ///
+ /// If you are using Classic CAN only, there is no reason to set
+ /// this to any value above B8.
+ pub data_field_size: DataFieldSize,
+}
+
+/// Configuration for Message RAM layout
+#[derive(Clone, Copy, Debug)]
+pub struct MessageRamConfig {
+ /// 0-128: Number of standard Message ID filter elements
+ /// >128: Interpreted as 128
+ pub standard_id_filter_size: u8,
+ /// 0-64: Number of extended Message ID filter elements
+ /// >64: Interpreted as 64
+ pub extended_id_filter_size: u8,
+
+ /// Configuration for Rx FIFO 0
+ pub rx_fifo_0: RxFifoConfig,
+ /// Configuration for Rx FIFO 1
+ pub rx_fifo_1: RxFifoConfig,
+ /// Configuration for Rx Buffers
+ pub rx_buffer: RxBufferConfig,
+
+ /// Configuration for Tx FIFO/Queue and dedicated buffers
+ pub tx: TxConfig,
+}
+
+impl Default for MessageRamConfig {
+ fn default() -> Self {
+ // TODO make better default config.
+ MessageRamConfig {
+ standard_id_filter_size: 28,
+ extended_id_filter_size: 8,
+ rx_fifo_0: RxFifoConfig {
+ watermark_interrupt_level: 3,
+ fifo_size: 3,
+ data_field_size: DataFieldSize::B64,
+ },
+ rx_fifo_1: RxFifoConfig::DISABLED,
+ rx_buffer: RxBufferConfig::DISABLED,
+ tx: TxConfig {
+ queue_size: 3,
+ dedicated_size: 0,
+ data_field_size: DataFieldSize::B64,
+ },
+ }
+ }
+}
diff --git a/embassy-stm32/src/can/fd/config.rs b/embassy-stm32/src/can/fd/config/mod.rs
similarity index 90%
rename from embassy-stm32/src/can/fd/config.rs
rename to embassy-stm32/src/can/fd/config/mod.rs
index 856f2fb3a..eaf0d5e5a 100644
--- a/embassy-stm32/src/can/fd/config.rs
+++ b/embassy-stm32/src/can/fd/config/mod.rs
@@ -3,6 +3,11 @@
use core::num::{NonZeroU16, NonZeroU8};
+#[cfg(can_fdcan_h7)]
+mod message_ram;
+#[cfg(can_fdcan_h7)]
+pub use message_ram::*;
+
/// Configures the bit timings.
///
/// You can use to calculate the `btr` parameter. Enter
@@ -117,20 +122,6 @@ impl Default for DataBitTiming {
}
}
-/// 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 {
@@ -314,6 +305,25 @@ impl From for TxBufferMode {
}
}
+/// 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, PartialEq, Eq, Debug)]
+pub enum CanFdMode {
+ /// Only allow Classic CAN message Frames.
+ /// Will behave as a Classic CAN node on the bus, will reject
+ /// CAN FD frames.
+ ClassicCanOnly,
+ /// Allow (non-BRS) FdCAN Message Frames.
+ /// This will allow the peripheral to send and receive frames
+ /// with data length up to 64 bytes, but will never perform bit
+ /// rate switching.
+ AllowFdCan,
+ /// Allow FdCAN Message Frames and allow Bit Rate Switching.
+ AllowFdCanAndBRS,
+}
+
/// FdCan Config Struct
#[derive(Clone, Copy, Debug)]
pub struct FdCanConfig {
@@ -334,12 +344,6 @@ pub struct FdCanConfig {
/// "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,
/// Edge Filtering: Two consecutive dominant tq required to detect an edge for hard synchronization
pub edge_filtering: bool,
/// Enables protocol exception handling
@@ -353,9 +357,17 @@ pub struct FdCanConfig {
/// TX buffer mode (FIFO or priority queue)
pub tx_buffer_mode: TxBufferMode,
- /// If this is set, the FDCAN uses the CAN FD frame format as specified by the Bosch CAN
- /// FD Specification V1.0.
- pub can_fd_enabled: bool,
+ /// Configures whether the peripheral uses CAN FD as specified by the
+ /// Bosch CAN FD Specification V1.0 or if it is limited to regular classic
+ /// ISO CAN.
+ pub can_fd_mode: CanFdMode,
+
+ /// This peripheral stores TX/RX buffers and more in a memory region called
+ /// Message RAM.
+ ///
+ /// The size of the different buffers are reconfigurable.
+ #[cfg(can_fdcan_h7)]
+ pub message_ram_config: MessageRamConfig,
}
impl FdCanConfig {
@@ -396,11 +408,12 @@ impl FdCanConfig {
self
}
- /// If this is set, the FDCAN uses the CAN FD frame format as specified by the Bosch CAN
- /// FD Specification V1.0.
+ /// Configures whether the peripheral uses CAN FD as specified by the
+ /// Bosch CAN FD Specification V1.0 or if it is limited to regular classic
+ /// ISO CAN.
#[inline]
- pub const fn set_can_fd_enabled(mut self, enabled: bool) -> Self {
- self.can_fd_enabled = enabled;
+ pub const fn set_can_fd_mode(mut self, mode: CanFdMode) -> Self {
+ self.can_fd_mode = mode;
self
}
@@ -411,13 +424,6 @@ impl FdCanConfig {
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 {
@@ -452,6 +458,12 @@ impl FdCanConfig {
self.tx_buffer_mode = txbm;
self
}
+
+ #[inline]
+ pub const fn set_message_ram_config(mut self, config: MessageRamConfig) -> Self {
+ self.message_ram_config = config;
+ self
+ }
}
impl Default for FdCanConfig {
@@ -462,14 +474,14 @@ impl Default for FdCanConfig {
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,
+ can_fd_mode: CanFdMode::ClassicCanOnly,
+ message_ram_config: MessageRamConfig::default(),
}
}
}
diff --git a/embassy-stm32/src/can/fd/configurator.rs b/embassy-stm32/src/can/fd/configurator.rs
index 710f90572..eebe75ec4 100644
--- a/embassy-stm32/src/can/fd/configurator.rs
+++ b/embassy-stm32/src/can/fd/configurator.rs
@@ -7,7 +7,7 @@ use crate::interrupt::typelevel::Interrupt;
use crate::{
can::{
common::CanMode,
- config::{FrameTransmissionConfig, TimestampPrescaler, TimestampSource},
+ config::{TimestampPrescaler, TimestampSource},
filter::{ExtendedFilter, StandardFilter},
util, Can, Instance, OperatingMode, RxPin, TxPin,
},
@@ -15,6 +15,7 @@ use crate::{
interrupt, rcc, Peripheral,
};
+use super::config::CanFdMode;
use super::{calc_ns_per_timer_tick, IT0InterruptHandler, IT1InterruptHandler, Info, State};
/// FDCAN Configuration instance instance
@@ -56,9 +57,7 @@ impl<'d> CanConfigurator<'d> {
info.low.apply_config(&config);
}
- {
- unsafe { T::mut_info() }.low.apply_dummy_msg_ram_config();
- }
+ //unsafe { T::mut_info() }.low.apply_message_ram_config();
rx.set_as_af(rx.af_num(), AfType::input(Pull::None));
tx.set_as_af(tx.af_num(), AfType::output(OutputType::PushPull, Speed::VeryHigh));
@@ -119,22 +118,47 @@ impl<'d> CanConfigurator<'d> {
seg1: bit_timing.seg1,
seg2: bit_timing.seg2,
};
- self.config.frame_transmit = FrameTransmissionConfig::AllowFdCanAndBRS;
+ self.config.can_fd_mode = CanFdMode::AllowFdCanAndBRS;
self.config = self.config.set_data_bit_timing(nbtr);
}
/// Start in mode.
pub fn start(self, mode: OperatingMode) -> Can<'d, M> {
- let ns_per_timer_tick = calc_ns_per_timer_tick(self.info, self.periph_clock, self.config.frame_transmit);
+ use crate::can::common::DynCanMode;
+
+ match M::dyn_can_mode() {
+ // Creating a FD parametrized CAN instance for Classic hardware config
+ // is supported. Can be used to be able to switch back and fourth without
+ // refactoring all your code or parametrizing it.
+ DynCanMode::Fd => (),
+ // Creating a Classic parametrized CAN instance for FD hardware config
+ // could lead to unexpected behaviour, and is not supported.
+ DynCanMode::Classic => assert!(
+ self.config.can_fd_mode == CanFdMode::ClassicCanOnly,
+ "Short frame types are not supported for FD hardware configuration"
+ ),
+ }
+
+ let ns_per_timer_tick = calc_ns_per_timer_tick(self.info, self.periph_clock, self.config.can_fd_mode);
+
+ // TODO: I really don't like this..
critical_section::with(|_| {
let state = self.state as *const State;
unsafe {
let mut_state = state as *mut State;
(*mut_state).ns_per_timer_tick = ns_per_timer_tick;
}
+
+ let info = self.info as *const Info;
+ unsafe {
+ let mut_info = info as *mut Info;
+ (*mut_info).low.apply_message_ram_config(self.config.message_ram_config);
+ }
});
+
self.info.low.apply_config(&self.config);
self.info.low.into_mode(mode);
+
Can {
_phantom: PhantomData,
config: self.config,
diff --git a/embassy-stm32/src/can/fd/interrupt.rs b/embassy-stm32/src/can/fd/interrupt.rs
index 1be498d49..025413d8f 100644
--- a/embassy-stm32/src/can/fd/interrupt.rs
+++ b/embassy-stm32/src/can/fd/interrupt.rs
@@ -57,32 +57,35 @@ pub struct IT0InterruptHandler {
impl interrupt::typelevel::Handler for IT0InterruptHandler {
unsafe fn on_interrupt() {
let regs = T::info().low.regs;
-
let ir = regs.ir().read();
+ // TX transfer complete
if ir.tc() {
regs.ir().write(|w| w.set_tc(true));
}
+ // TX cancel request finished
+ if ir.tcf() {
+ regs.ir().write(|w| w.set_tc(true));
+ }
+
+ // TX event FIFO new element
if ir.tefn() {
regs.ir().write(|w| w.set_tefn(true));
}
T::state().tx_waker.wake();
- //match &T::state().tx_mode {
- // TxMode::NonBuffered(waker) => waker.wake(),
- //}
+ // RX FIFO new element
if ir.rfn(0) {
T::info().low.regs.ir().write(|w| w.set_rfn(0, true));
T::state().rx_waker.wake();
- //T::state().rx_mode.on_interrupt::(0);
}
if ir.rfn(1) {
T::info().low.regs.ir().write(|w| w.set_rfn(1, true));
T::state().rx_waker.wake();
- //T::state().rx_mode.on_interrupt::(1);
}
+ // Bus_Off
if ir.bo() {
regs.ir().write(|w| w.set_bo(true));
if regs.psr().read().bo() {
diff --git a/embassy-stm32/src/can/fd/low_level/configuration.rs b/embassy-stm32/src/can/fd/low_level/configuration.rs
index d1b8d9fa2..f2ab9515b 100644
--- a/embassy-stm32/src/can/fd/low_level/configuration.rs
+++ b/embassy-stm32/src/can/fd/low_level/configuration.rs
@@ -1,8 +1,8 @@
use super::{
- message_ram::{self, MessageRam},
+ message_ram::{MessageRam, MessageRamSegment},
CanLowLevel, LoopbackMode, TimestampSource,
};
-use crate::can::config::{DataBitTiming, FdCanConfig, FrameTransmissionConfig, GlobalFilter, NominalBitTiming};
+use crate::can::config::{CanFdMode, DataBitTiming, FdCanConfig, GlobalFilter, MessageRamConfig, NominalBitTiming};
/// Configuration.
/// Can only be called when in config mode (CCCR.INIT=1, CCCR.CCE=1).
@@ -22,32 +22,13 @@ impl CanLowLevel {
}
}
- pub fn apply_message_ram_config(&mut self, config: message_ram::MessageRamConfig) {
- let message_ram = config.apply_config(&self.regs, &self.msgram);
- self.message_ram = message_ram;
- }
-
- pub fn apply_dummy_msg_ram_config(&mut self) {
- self.apply_message_ram_config(message_ram::MessageRamConfig {
+ pub fn apply_message_ram_config(&mut self, config: MessageRamConfig) {
+ let segment = MessageRamSegment {
base_offset: self.msg_ram_offset,
available_space: Some(self.msg_ram_size),
- standard_id_filter_size: 28,
- extended_id_filter_size: 8,
- rx_fifo_0: message_ram::RxFifoConfig {
- operation_mode: message_ram::RxFifoOperationMode::Blocking,
- watermark_interrupt_level: 3,
- fifo_size: 3,
- data_field_size: message_ram::DataFieldSize::B64,
- },
- rx_fifo_1: message_ram::RxFifoConfig::DISABLED,
- rx_buffer: message_ram::RxBufferConfig::DISABLED,
- tx: message_ram::TxConfig {
- queue_operation_mode: message_ram::TxQueueOperationMode::FIFO,
- queue_size: 3,
- dedicated_size: 0,
- data_field_size: message_ram::DataFieldSize::B64,
- },
- })
+ };
+ let message_ram = unsafe { config.apply_config(&segment, &self.regs, &self.msgram) };
+ self.message_ram = message_ram;
}
/// Applies the settings of a new FdCanConfig See [`FdCanConfig`]
@@ -75,8 +56,7 @@ impl CanLowLevel {
self.set_nominal_bit_timing(config.nbtr);
self.set_automatic_retransmit(config.automatic_retransmit);
self.set_transmit_pause(config.transmit_pause);
- self.set_frame_transmit(config.frame_transmit);
- self.set_non_iso_mode(config.can_fd_enabled);
+ self.set_can_fd_mode(config.can_fd_mode);
self.set_edge_filtering(config.edge_filtering);
self.set_protocol_exception_handling(config.protocol_exception_handling);
self.set_global_filter(config.global_filter);
@@ -163,23 +143,18 @@ impl CanLowLevel {
self.regs.cccr().modify(|w| w.set_efbi(enabled));
}
- /// Configures non-iso mode. See [`FdCanConfig::set_non_iso_mode`]
- #[inline]
- pub fn set_non_iso_mode(&self, enabled: bool) {
- self.regs.cccr().modify(|w| w.set_niso(enabled));
- }
-
/// Configures frame transmission mode. See
/// [`FdCanConfig::set_frame_transmit`]
#[inline]
- pub fn set_frame_transmit(&self, fts: FrameTransmissionConfig) {
- let (fdoe, brse) = match fts {
- FrameTransmissionConfig::ClassicCanOnly => (false, false),
- FrameTransmissionConfig::AllowFdCan => (true, false),
- FrameTransmissionConfig::AllowFdCanAndBRS => (true, true),
+ pub fn set_can_fd_mode(&self, fts: CanFdMode) {
+ let (niso, fdoe, brse) = match fts {
+ CanFdMode::ClassicCanOnly => (false, false, false),
+ CanFdMode::AllowFdCan => (true, true, false),
+ CanFdMode::AllowFdCanAndBRS => (true, true, true),
};
self.regs.cccr().modify(|w| {
+ w.set_niso(niso);
w.set_fdoe(fdoe);
#[cfg(can_fdcan_h7)]
w.set_bse(brse);
diff --git a/embassy-stm32/src/can/fd/low_level/message_ram/element/tx_buffer.rs b/embassy-stm32/src/can/fd/low_level/message_ram/element/tx_buffer.rs
index 8656c9268..b2d71c5db 100644
--- a/embassy-stm32/src/can/fd/low_level/message_ram/element/tx_buffer.rs
+++ b/embassy-stm32/src/can/fd/low_level/message_ram/element/tx_buffer.rs
@@ -153,7 +153,7 @@ impl<'a> ID_W<'a> {
#[doc = r"Writes raw bits to the field"]
#[inline(always)]
#[allow(dead_code)]
- pub unsafe fn bits(self, value: u32) -> &'a mut W {
+ pub fn bits(self, value: u32) -> &'a mut W {
self.w.bits[0] = (self.w.bits[0] & !(0x1FFFFFFF)) | ((value as u32) & 0x1FFFFFFF);
self.w
}
@@ -304,13 +304,14 @@ pub(crate) struct MM_W<'a> {
impl<'a> MM_W<'a> {
#[doc = r"Writes raw bits to the field"]
#[inline(always)]
- pub unsafe fn bits(self, value: u8) -> &'a mut W {
- self.w.bits[1] = (self.w.bits[1] & !(0x7F << 24)) | (((value as u32) & 0x7F) << 24);
+ pub fn bits(self, value: u8) -> &'a mut W {
+ self.w.bits[1] = (self.w.bits[1] & !(0xff << 24)) | (((value as u32) & 0xff) << 24);
self.w
}
- fn set_message_marker(self, mm: Marker) -> &'a mut W {
- unsafe { self.bits(mm.0) }
+ #[inline(always)]
+ pub fn set_message_marker(self, mm: Marker) -> &'a mut W {
+ self.bits(mm.0)
}
}
diff --git a/embassy-stm32/src/can/fd/low_level/message_ram/mod.rs b/embassy-stm32/src/can/fd/low_level/message_ram/mod.rs
index 212cde849..b25fc99b3 100644
--- a/embassy-stm32/src/can/fd/low_level/message_ram/mod.rs
+++ b/embassy-stm32/src/can/fd/low_level/message_ram/mod.rs
@@ -1,7 +1,6 @@
use core::marker::PhantomData;
use element::tx_event::TxEventElement;
-use stm32_metapac::can::vals::Tfqm;
use volatile_register::RW;
mod element;
@@ -11,6 +10,8 @@ pub(crate) use element::{
tx_buffer::TxBufferElementHeader,
};
+use crate::can::config::MessageRamConfig;
+
/// Configuration for MessageRam layout.
pub struct MessageRam {
// 32 bit words
@@ -70,24 +71,6 @@ impl MessageRam {
#[cfg(can_fdcan_h7)]
trigger_memory: Elements::EMPTY,
};
-
- pub fn debug_print(&self, regs: &crate::pac::can::Fdcan) {
- defmt::info!("msgram seg base: {}", self.base_ptr);
- defmt::info!("msgram tx base: {}", self.tx_elements.base);
- //defmt::info!(
- // "msgram tx calc offet: {}",
- // ((self.tx_elements.base as usize) - (self.base_ptr as usize)) >> 2
- //);
- defmt::info!("msgram tx periph offset: {}", regs.txbc().read().tbsa());
- let ram_ptr = crate::pac::FDCANRAM.as_ptr();
- defmt::info!("msgram tx periph ptr: {}", unsafe {
- ram_ptr.byte_add(regs.txbc().read().tbsa() as usize * 4)
- });
- //defmt::info!("elem 0 start {}", self.tx_elements.get_mut(0) as *mut _);
- //defmt::info!("elem 1 start {}", self.tx_elements.get_mut(1) as *mut _);
- //defmt::info!("elem 0 data start {}", (&self.tx_elements.get_mut(0).data).as_ptr());
- //defmt::info!("elem 1 data start {}", (&self.tx_elements.get_mut(1).data).as_ptr());
- }
}
#[repr(C)]
@@ -167,161 +150,6 @@ impl Elements> {
}
}
-impl MessageRam {}
-
-#[derive(Debug, Clone, Copy)]
-pub enum RxFifoOperationMode {
- Blocking = 0,
- Overwrite = 1,
-}
-
-/// For RX:
-/// Excess data is IGNORED, only the number of bytes which fit
-/// into the element are stored.
-///
-/// For TX:
-/// If DLC is higher than the data field size, excess bytes are
-/// transmitted as 0xCC (padding bytes).
-#[derive(Debug, Clone, Copy)]
-pub enum DataFieldSize {
- B8 = 0b000,
- B12 = 0b001,
- B16 = 0b010,
- B20 = 0b011,
- B24 = 0b100,
- B32 = 0b101,
- B48 = 0b110,
- B64 = 0b111,
-}
-
-impl DataFieldSize {
- fn reg_value(self) -> u8 {
- self as u8
- }
-
- fn byte_size(self) -> usize {
- match self {
- DataFieldSize::B8 => 8,
- DataFieldSize::B12 => 12,
- DataFieldSize::B16 => 16,
- DataFieldSize::B20 => 20,
- DataFieldSize::B24 => 24,
- DataFieldSize::B32 => 32,
- DataFieldSize::B48 => 48,
- DataFieldSize::B64 => 64,
- }
- }
-
- fn word_size(self) -> usize {
- self.byte_size() / 4
- }
-}
-
-#[derive(Debug, Clone, Copy)]
-pub struct RxFifoConfig {
- pub(crate) operation_mode: RxFifoOperationMode,
- /// 0: Disabled
- /// 1-64: Watermark interrupt level
- /// >64: Disabled
- pub(crate) watermark_interrupt_level: u8,
- /// 0: RX FIFO disabled
- /// 1-64: Number of RX FIFO elements
- /// >64: Interpreted as 64
- pub(crate) fifo_size: u8,
- pub(crate) data_field_size: DataFieldSize,
-}
-
-impl RxFifoConfig {
- pub const DISABLED: Self = RxFifoConfig {
- operation_mode: RxFifoOperationMode::Blocking,
- watermark_interrupt_level: 0,
- fifo_size: 0,
- data_field_size: DataFieldSize::B8,
- };
-}
-
-pub struct RxBufferConfig {
- /// 0-64
- pub(crate) size: u8,
- pub(crate) data_field_size: DataFieldSize,
-}
-
-impl RxBufferConfig {
- pub const DISABLED: Self = RxBufferConfig {
- size: 0,
- data_field_size: DataFieldSize::B8,
- };
-}
-
-#[derive(Debug, Clone, Copy)]
-pub enum TxQueueOperationMode {
- /// Operates as a strict FIFO.
- /// First element entered into the FIFO is always the first element sent.
- FIFO = 0,
- /// Operates as a priority queue.
- /// Element with highest priority (lowest ID) is always the first element sent.
- Priority = 1,
-}
-
-impl TxQueueOperationMode {
- fn reg_value(self) -> Tfqm {
- match self {
- TxQueueOperationMode::FIFO => Tfqm::FIFO,
- TxQueueOperationMode::Priority => Tfqm::QUEUE,
- }
- }
-}
-
-#[cfg(can_fdcan_h7)]
-pub struct TxConfig {
- pub(crate) queue_operation_mode: TxQueueOperationMode,
- /// Number of elements reserved for TX Queue.
- /// NOTE: queue_size + dedicated_size may not be greater than 32.
- ///
- /// 0: No TX FIFO/Priority queue
- /// 1-32: Number of TX buffers used for TX FIFO/Priority queue
- /// >32: Interpreted as 32
- pub(crate) queue_size: u8,
- /// Number of elements reserved for Dedicated TX buffers.
- /// NOTE: queue_size + dedicated_size may not be greater than 32.
- ///
- /// 0: No TX dedicated buffers
- /// 1-32: Number of TX buffers used for TX dedicated buffers
- /// >32: Interpreted as 32
- pub(crate) dedicated_size: u8,
- pub(crate) data_field_size: DataFieldSize,
-}
-
-#[cfg(can_fdcan_h7)]
-pub struct MessageRamConfig {
- /// Base offset of the Message RAM region allocated to this
- /// peripheral instance.
- /// In bytes.
- pub(crate) base_offset: usize,
- /// Available space allocated for this peripheral instance in bytes.
- /// If present, it will be validated that everything fits into the
- /// allocated space.
- /// In bytes.
- pub(crate) available_space: Option,
-
- /// 0: No standard Message ID filter
- /// 1-128: Number of standard Message ID filter elements
- /// >128: Interpreted as 128
- pub(crate) standard_id_filter_size: u8,
- /// 0: No extended Message ID filter
- /// 1-64: Number of extended Message ID filter elements
- /// >64: Interpreted as 64
- pub(crate) extended_id_filter_size: u8,
-
- pub(crate) rx_fifo_0: RxFifoConfig,
- pub(crate) rx_fifo_1: RxFifoConfig,
- pub(crate) rx_buffer: RxBufferConfig,
- pub(crate) tx: TxConfig,
-}
-
-#[cfg(can_fdcan_h7)]
-const MSG_RAM_SIZE: usize = 0x2800;
-
struct ElementAllocator(usize);
impl ElementAllocator {
fn new(offset: usize) -> Self {
@@ -363,6 +191,9 @@ impl ElementSizing {
fn end_offset_words(&self) -> usize {
self.offset + self.total_size_words()
}
+ unsafe fn make(&self, base_ptr: *mut RW) -> Elements {
+ Elements::new(base_ptr.add(self.offset), self.num_elements, self.element_size)
+ }
}
struct ElementsSizing {
@@ -381,7 +212,20 @@ struct ElementsSizing {
trigger: ElementSizing,
}
+pub(crate) struct MessageRamSegment {
+ /// Base offset of the Message RAM region allocated to this
+ /// peripheral instance.
+ /// In bytes.
+ pub base_offset: usize,
+ /// Available space allocated for this peripheral instance in bytes.
+ /// If present, it will be validated that everything fits into the
+ /// allocated space.
+ /// In bytes.
+ pub available_space: Option,
+}
+
impl ElementsSizing {
+ /// Calculates the sizing used for the simplified variant of the M_CAN peripheral.
fn calculate_element_sizing_simplified() -> ElementsSizing {
let mut a = ElementAllocator::new(0);
@@ -401,7 +245,9 @@ impl ElementsSizing {
}
}
- fn calculate_element_sizing(config: &MessageRamConfig) -> ElementsSizing {
+ /// Calculates the sizing used for the full variant of the M_CAN peripheral.
+ /// This mapping is customizable, a configuration is needed to instantiate it.
+ fn calculate_element_sizing(config: &MessageRamConfig, segment: &MessageRamSegment) -> ElementsSizing {
assert!(
config.standard_id_filter_size <= 128,
"more than 128 standard id filters not supported"
@@ -427,7 +273,7 @@ impl ElementsSizing {
"total TX elements can not be larger than 32"
);
- let base_offset = config.base_offset;
+ let base_offset = segment.base_offset;
let base_offset_words = base_offset >> 2;
let mut a = ElementAllocator::new(base_offset_words);
@@ -447,148 +293,97 @@ impl ElementsSizing {
2 + config.rx_buffer.data_field_size.word_size(),
config.rx_buffer.size as usize,
),
- // TODO implement TX events
- tx_event: a.next(2, 0),
+ // Hard code size 16 for TX Event FIFO.
+ // Closely coupled to the driver implementation, no reason to
+ // allow for customization for now.
+ tx_event: a.next(2, 16),
tx_elements: a.next(
2 + config.tx.data_field_size.word_size(),
(config.tx.dedicated_size + config.tx.queue_size) as usize,
),
dedicated_len: config.tx.dedicated_size as usize,
- // TODO implement triggers
+ // Driver does not support TTCAN for now, zero triggers.
trigger: a.next(2, 0),
end_offset: a.0,
}
}
+
+ unsafe fn make(&self, ram_base: *mut RW) -> MessageRam {
+ MessageRam {
+ base_ptr: ram_base.add(self.start_offset),
+ standard_filter: self.standard_id.make(ram_base),
+ extended_filter: self.extended_id.make(ram_base),
+ rx_fifos: [self.rx_fifo_0.make(ram_base), self.rx_fifo_1.make(ram_base)],
+ #[cfg(can_fdcan_h7)]
+ rx_buffer: self.rx_buffer.make(ram_base),
+ tx_event_fifo: self.tx_event.make(ram_base),
+ tx_elements: self.tx_elements.make(ram_base),
+ #[cfg(can_fdcan_h7)]
+ tx_buffer_len: self.dedicated_len,
+ tx_queue_len: self.tx_elements.num_elements - self.dedicated_len,
+ #[cfg(can_fdcan_h7)]
+ trigger_memory: self.trigger.make(ram_base),
+ }
+ }
}
#[cfg(can_fdcan_h7)]
impl MessageRamConfig {
+ // This constant is a H7 thing, not a limitation of M_CAN.
+ const H7_MSG_RAM_SIZE: usize = 0x2800;
+
/// Configures message ram for the peripheral according to the supplied
/// config and returns a struct which can be used to interact with the
/// message RAM.
- #[cfg(can_fdcan_h7)]
- pub fn apply_config(&self, regs: &crate::pac::can::Fdcan, ram: &crate::pac::fdcanram::Fdcanram) -> MessageRam {
- let base_offset = self.base_offset;
- let base_offset_words = base_offset >> 2;
+ pub(crate) unsafe fn apply_config(
+ &self,
+ segment: &MessageRamSegment,
+ regs: &crate::pac::can::Fdcan,
+ ram: &crate::pac::fdcanram::Fdcanram,
+ ) -> MessageRam {
+ let sizing = ElementsSizing::calculate_element_sizing(self, segment);
- // Abbreviations:
- // sa: start address
- // es: element size
- // en: element num
- // ts: total size
-
- assert!(
- self.standard_id_filter_size <= 128,
- "more than 128 standard id filters not supported"
- );
- let sid_sa = base_offset_words;
- let sid_es = 1;
- let sid_en = self.standard_id_filter_size as usize;
- let sid_ts = sid_es * sid_en;
-
- assert!(
- self.extended_id_filter_size <= 64,
- "more than 64 extended id filters not supported"
- );
- let xid_sa = sid_sa + sid_ts;
- let xid_es = 2;
- let xid_en = self.extended_id_filter_size as usize;
- let xid_ts = xid_es * xid_en;
-
- assert!(
- self.rx_fifo_0.fifo_size <= 64,
- "more than 64 rx fifo 0 elements not supported"
- );
- let rx0_sa = xid_sa + xid_ts;
- let rx0_es = 2 + self.rx_fifo_0.data_field_size.word_size();
- let rx0_en = self.rx_fifo_0.fifo_size as usize;
- let rx0_ts = rx0_es * rx0_en;
-
- assert!(
- self.rx_fifo_1.fifo_size <= 64,
- "more than 64 rx fifo 1 elements not supported"
- );
- let rx1_sa = rx0_sa + rx0_ts;
- let rx1_es = 2 + self.rx_fifo_1.data_field_size.word_size();
- let rx1_en = self.rx_fifo_1.fifo_size as usize;
- let rx1_ts = rx1_es * rx1_en;
-
- assert!(
- self.rx_buffer.size <= 64,
- "more than 64 rx buffer elements not supported"
- );
- let rxb_sa = rx1_sa + rx1_ts;
- let rxb_es = 2 + self.rx_buffer.data_field_size.word_size();
- let rxb_en = self.rx_buffer.size as usize;
- let rxb_ts = rxb_es * rxb_en;
-
- let txe_sa = rxb_sa + rxb_ts;
- let txe_es = 2;
- let txe_en = 0; // TODO implement TX events
- let txe_ts = txe_es * txe_en;
-
- assert!(
- self.tx.dedicated_size + self.tx.queue_size <= 32,
- "total TX elements can not be larger than 32"
- );
- let txx_es = 2 + self.tx.data_field_size.word_size();
-
- let txq_sa = txe_sa + txe_ts;
- let txq_en = self.tx.queue_size as usize;
- let txq_ts = txx_es * txq_en;
-
- let txd_sa = txq_sa + txq_ts;
- let txd_en = self.tx.dedicated_size as usize;
- let txd_ts = txx_es * txd_en;
-
- let tmc_sa = txd_sa + txd_ts;
- let tmc_es = 2;
- let tmc_en = 0; // TODO implement trigger stuff
- let tmc_ts = tmc_es * tmc_en;
-
- let end_offset_words = tmc_sa + tmc_ts;
- let total_size_words = end_offset_words - base_offset_words;
+ let total_size_words = sizing.end_offset - sizing.start_offset;
let total_size_bytes = total_size_words << 2;
- //defmt::info!("total calculated word size: {:#x}", total_size_words);
- if let Some(avail) = self.available_space {
+ if let Some(avail) = segment.available_space {
assert!(total_size_bytes <= avail, "CAN RAM config exceeded available space!");
}
// Standard ID filter config
// Fully managed
regs.sidfc().modify(|v| {
- v.set_flssa(sid_sa as u16);
- v.set_lss(sid_en as u8);
+ v.set_flssa(sizing.standard_id.offset as u16);
+ v.set_lss(sizing.standard_id.num_elements as u8);
});
// Extended ID filter config
// Fully managed
regs.xidfc().modify(|v| {
- v.set_flesa(xid_sa as u16);
- v.set_lse(xid_en as u8);
+ v.set_flesa(sizing.extended_id.offset as u16);
+ v.set_lse(sizing.extended_id.num_elements as u8);
});
// RX FIFO 0 config
regs.rxfc(0).modify(|v| {
// F0OM - RX FIFO Operating Mode
// F0WM - RX FIFO Water Mark
- v.set_fsa(rx0_sa as u16);
- v.set_fs(rx0_en as u8);
+ v.set_fsa(sizing.rx_fifo_0.offset as u16);
+ v.set_fs(sizing.rx_fifo_0.num_elements as u8);
});
// RX FIFO 1 config
regs.rxfc(1).modify(|v| {
// F1OM - RX FIFO Operating Mode
// F1WM - RX FIFO Water Mark
- v.set_fsa(rx1_sa as u16);
- v.set_fs(rx1_en as u8);
+ v.set_fsa(sizing.rx_fifo_1.offset as u16);
+ v.set_fs(sizing.rx_fifo_1.num_elements as u8);
});
// RX buffer config
// Fully managed
regs.rxbc().modify(|v| {
- v.set_rbsa(rxb_sa as u16);
+ v.set_rbsa(sizing.rx_buffer.offset as u16);
});
// Rx buffer / queue element size config
@@ -602,8 +397,8 @@ impl MessageRamConfig {
// TX event FIFO config
regs.txefc().modify(|v| {
// EFWM - Event FIFO Water Mark
- v.set_efsa(txe_sa as u16);
- v.set_efs(txe_en as u8);
+ v.set_efsa(sizing.tx_event.offset as u16);
+ v.set_efs(sizing.tx_event.num_elements as u8);
});
// Tx buffer / queue element size config
@@ -615,57 +410,25 @@ impl MessageRamConfig {
// TX queue configuration
// Fully managed
regs.txbc().modify(|v| {
- v.set_tfqm(self.tx.queue_operation_mode.reg_value());
- v.set_tbsa(txq_sa as u16);
- v.set_ndtb(txd_en as u8);
- v.set_tfqs(txq_en as u8);
+ //v.set_tfqm(self.tx.queue_operation_mode.reg_value());
+ v.set_tbsa(sizing.tx_elements.offset as u16);
+ v.set_ndtb(sizing.dedicated_len as u8);
+ v.set_tfqs((sizing.tx_elements.num_elements - sizing.dedicated_len) as u8);
});
// TT Trigger memory config
// Fully managed
regs.tttmc().modify(|v| {
- v.set_tmsa(tmc_sa as u16);
- v.set_tme(tmc_en as u8);
+ v.set_tmsa(sizing.trigger.offset as u16);
+ v.set_tme(sizing.trigger.num_elements as u8);
});
let ram_ptr = ram.as_ptr() as *mut RW;
- let base_ptr = unsafe { ram_ptr.add(base_offset_words) };
-
- // let mut test_ptr = base_ptr as *const u32;
- // while test_ptr < unsafe { base_ptr.byte_add(self.available_space.unwrap()) as *const u32 } {
- // let val = unsafe { test_ptr.read() };
- // if val == 0xaa95fd71 || val == 0x71fd95aa {
- // defmt::info!("FOUND BYTE MATCH: {}", test_ptr);
- // }
- // test_ptr = unsafe { test_ptr.byte_add(4) };
- // }
-
- // if let Some(avail) = self.available_space {
- // unsafe { core::ptr::write_bytes(ram_ptr as *mut u8, 0, avail) };
- // }
-
- // assert!(unsafe { base_ptr as usize == (ram_ptr as *mut u8).add(self.base_offset) as usize });
assert!(unsafe {
- (ram_ptr.add(end_offset_words) as usize) < ((ram_ptr as *mut u8).add(MSG_RAM_SIZE) as usize)
+ (ram_ptr.add(sizing.end_offset) as usize) < ((ram_ptr as *mut u8).add(Self::H7_MSG_RAM_SIZE) as usize)
});
- unsafe {
- MessageRam {
- base_ptr,
- standard_filter: Elements::new(ram_ptr.add(sid_sa), sid_en, sid_es),
- extended_filter: Elements::new(ram_ptr.add(xid_sa), xid_en, xid_es),
- rx_fifos: [
- Elements::new(ram_ptr.add(rx0_sa), rx0_en, rx0_es),
- Elements::new(ram_ptr.add(rx1_sa), rx1_en, rx1_es),
- ],
- rx_buffer: Elements::new(ram_ptr.add(rxb_sa), rxb_en, rxb_es),
- tx_event_fifo: Elements::new(ram_ptr.add(txe_sa), txe_en, txe_es),
- tx_elements: Elements::new(ram_ptr.add(txq_sa), txd_en + txq_en, txx_es),
- tx_buffer_len: txd_en,
- tx_queue_len: txq_en,
- trigger_memory: Elements::new(ram_ptr.add(tmc_sa), tmc_en, tmc_es),
- }
- }
+ unsafe { sizing.make(ram_ptr) }
}
}
diff --git a/embassy-stm32/src/can/fd/low_level/mod.rs b/embassy-stm32/src/can/fd/low_level/mod.rs
index b61670035..c6f24c1ad 100644
--- a/embassy-stm32/src/can/fd/low_level/mod.rs
+++ b/embassy-stm32/src/can/fd/low_level/mod.rs
@@ -3,6 +3,7 @@ use core::convert::Infallible;
use cfg_if::cfg_if;
use message_ram::{HeaderElement, RxFifoElementHeader};
use stm32_metapac::can::regs::{Ndat1, Ndat2, Txbcr};
+use util::{RxElementData, TxElementData};
use crate::can::{
enums::BusError,
@@ -15,8 +16,6 @@ mod filter;
pub(crate) mod message_ram;
mod util;
-use util::{extract_frame, put_tx_data, put_tx_header};
-
/// Loopback Mode
#[derive(Clone, Copy, Debug)]
pub(crate) enum LoopbackMode {
@@ -73,35 +72,21 @@ impl CanLowLevel {
}
}
-/// Legacy and bad
-impl CanLowLevel {
- pub fn bad_write(&self, frame: &F) -> nb::Result {
- let is_full = self.regs.txfqs().read().tfqf();
-
- // TODO this does not dequeue lower priority frame!
- // Temporary!
-
- if is_full {
- // TODO only for FIFO!
- return Err(nb::Error::WouldBlock);
- }
-
- let result = self.tx_fifo_add(frame.header(), frame.data());
-
- if let Some(_idx) = result {
- Ok(true)
- } else {
- Ok(false)
- }
- }
-}
-
/// Tx
impl CanLowLevel {
fn tx_element_set(&self, idx: u8, header: &Header, data: &[u8]) {
let element = self.message_ram.tx_elements.get_mut(idx as usize);
- put_tx_header(&mut element.header, &header);
- put_tx_data(&mut element.data, data);
+
+ let mut n_data = [0u8; 64];
+ n_data.iter_mut().zip(data.iter()).for_each(|(to, from)| *to = *from);
+
+ TxElementData {
+ header: *header,
+ data: n_data,
+ marker: 0,
+ tx_event: false,
+ }
+ .put(element);
}
fn tx_element_get(&self, idx: u8) -> (Header, [u8; 64]) {
@@ -181,16 +166,7 @@ impl CanLowLevel {
}
impl CanLowLevel {
- pub fn rx_element_get(&self, element: &HeaderElement) -> Option<(Header, u16, [u8; 64])> {
- let mut buffer = [0u8; 64];
- // TODO allow read len to be lower than drl?
- let maybe_header = extract_frame(&element.header, &element.data, &mut buffer);
-
- let (header, ts) = maybe_header.unwrap();
- Some((header, ts, buffer))
- }
-
- pub fn rx_buffer_read(&self, buffer_idx: u8) -> Option<(Header, u16, [u8; 64])> {
+ pub fn rx_buffer_read(&self, buffer_idx: u8) -> Option {
let bit_idx = buffer_idx & 0b11111;
let bit = 1 << bit_idx;
@@ -206,7 +182,7 @@ impl CanLowLevel {
}
let element = self.message_ram.rx_buffer.get_mut(buffer_idx as usize);
- let ret = self.rx_element_get(element);
+ let ret = RxElementData::extract(element);
match buffer_idx {
idx if idx < 32 => self.regs.ndat1().write_value(Ndat1(bit)),
@@ -214,10 +190,10 @@ impl CanLowLevel {
_ => panic!(),
};
- ret
+ Some(ret)
}
- pub fn rx_fifo_read(&self, fifo_num: u8) -> Option<(Header, u16, [u8; 64])> {
+ pub fn rx_fifo_read(&self, fifo_num: u8) -> Option {
let status = self.regs.rxfs(fifo_num as usize).read();
let fill_level = status.ffl();
@@ -227,11 +203,11 @@ impl CanLowLevel {
let get_index = self.regs.rxfs(fifo_num as usize).read().fgi();
let element = self.message_ram.rx_fifos[fifo_num as usize].get_mut(get_index as usize);
- let ret = self.rx_element_get(element);
+ let ret = RxElementData::extract(element);
self.regs.rxfa(fifo_num as usize).write(|v| v.set_fai(get_index));
- ret
+ Some(ret)
}
}
diff --git a/embassy-stm32/src/can/fd/low_level/util.rs b/embassy-stm32/src/can/fd/low_level/util.rs
index 0b8eee71c..edb76d395 100644
--- a/embassy-stm32/src/can/fd/low_level/util.rs
+++ b/embassy-stm32/src/can/fd/low_level/util.rs
@@ -4,7 +4,9 @@ use volatile_register::RW;
use crate::can::frame::Header;
-use super::message_ram::{DataLength, Event, FrameFormat, IdType, RxFifoElementHeader, TxBufferElementHeader};
+use super::message_ram::{
+ DataLength, Event, FrameFormat, HeaderElement, IdType, RxFifoElementHeader, TxBufferElementHeader,
+};
fn make_id(id: u32, extended: bool) -> embedded_can::Id {
if extended {
@@ -15,93 +17,101 @@ fn make_id(id: u32, extended: bool) -> embedded_can::Id {
}
}
-pub(crate) fn put_tx_header(reg: &mut TxBufferElementHeader, header: &Header) {
- let (id, id_type) = match header.id() {
- // A standard identifier has to be written to ID[28:18].
- embedded_can::Id::Standard(id) => ((id.as_raw() as u32) << 18, IdType::StandardId),
- embedded_can::Id::Extended(id) => (id.as_raw() as u32, IdType::ExtendedId),
- };
-
- // Use FDCAN only for DLC > 8. FDCAN users can revise this if required.
- let frame_format = if header.len() > 8 || header.fdcan() {
- FrameFormat::Fdcan
- } else {
- FrameFormat::Classic
- };
- let brs = (frame_format == FrameFormat::Fdcan) && header.bit_rate_switching();
-
- reg.write(|w| {
- unsafe { w.id().bits(id) }
- .rtr()
- .bit(header.len() == 0 && header.rtr())
- .xtd()
- .set_id_type(id_type)
- .set_len(DataLength::new(header.len(), frame_format))
- .set_event(Event::NoEvent)
- .fdf()
- .set_format(frame_format)
- .brs()
- .bit(brs)
- //esi.set_error_indicator(//TODO//)
- });
+pub(crate) struct TxElementData {
+ pub header: Header,
+ pub data: [u8; 64],
+ pub marker: u8,
+ pub tx_event: bool,
}
-pub(crate) fn put_tx_data(mailbox_data: &mut [RW], buffer: &[u8]) {
- let mut lbuffer = [0_u32; 16];
- let len = buffer.len();
- let data = unsafe { slice::from_raw_parts_mut(lbuffer.as_mut_ptr() as *mut u8, len) };
- data[..len].copy_from_slice(&buffer[..len]);
- let data_len = ((len) + 3) / 4;
- for (register, byte) in mailbox_data.iter_mut().zip(lbuffer[..data_len].iter()) {
- unsafe { register.write(*byte) };
+impl TxElementData {
+ //#[inline]
+ //pub(crate) fn extract(element: &HeaderElement) -> Self {
+ // todo!()
+ //}
+
+ #[inline]
+ pub fn put(&self, element: &mut HeaderElement) {
+ let (id, id_type) = match self.header.id() {
+ // A standard identifier has to be written to ID[28:18].
+ embedded_can::Id::Standard(id) => ((id.as_raw() as u32) << 18, IdType::StandardId),
+ embedded_can::Id::Extended(id) => (id.as_raw() as u32, IdType::ExtendedId),
+ };
+
+ let frame_format = if self.header.fdcan() {
+ FrameFormat::Fdcan
+ } else {
+ FrameFormat::Classic
+ };
+
+ element.header.write(|w| {
+ w.esi().bit(self.header.esi());
+ w.xtd().set_id_type(id_type);
+ w.rtr().bit(self.header.rtr());
+ w.id().bits(id);
+ w.mm().bits(self.marker);
+ w.efc().bit(self.tx_event);
+ w.fdf().bit(self.header.fdcan());
+ w.brs().bit(self.header.bit_rate_switching());
+ w.set_len(DataLength::new(self.header.len(), frame_format));
+ w
+ });
+
+ // TODO validate endianness
+ let u32_data: &[u32; 16] = unsafe { core::mem::transmute(&self.data) };
+ element.data.iter_mut().zip(u32_data.iter()).for_each(|(reg, data)| {
+ unsafe { reg.write(*data) };
+ });
}
}
-pub(crate) fn data_from_fifo(buffer: &mut [u8], mailbox_data: &[RW], len: usize) {
- for (i, register) in mailbox_data.iter().enumerate() {
- let register_value = register.read();
- let register_bytes = unsafe { slice::from_raw_parts(®ister_value as *const u32 as *const u8, 4) };
- let num_bytes = (len) - i * 4;
- if num_bytes <= 4 {
- buffer[i * 4..i * 4 + num_bytes].copy_from_slice(®ister_bytes[..num_bytes]);
- break;
+pub(crate) struct RxElementData {
+ pub header: Header,
+ pub data: [u8; 64],
+ /// If `dlc` is above what can be represented in the configured element,
+ /// data can be clipped.
+ /// This indicates what the real data word size (4 byte words) was.
+ pub data_words: u8,
+ pub timestamp: u16,
+ /// If None, the frame was accepted without a filter as enabled by
+ /// `GFC.ANFS` or `GFC.ANFE`.
+ /// If Some, contains the index of the filter that was matched.
+ pub filter_index: Option,
+}
+
+impl RxElementData {
+ #[inline]
+ pub fn extract(element: &HeaderElement) -> Self {
+ let reg = element.header.read();
+
+ let dlc = reg.to_data_length().len();
+
+ let id = make_id(reg.id().bits(), reg.xtd().bits());
+ let timestamp = reg.txts().bits;
+
+ let rtr = reg.rtr().bits;
+ let can_fd = reg.fdf().bits;
+ let brs = reg.brs().bits;
+ let esi = reg.esi().bits;
+
+ let filter_index = if reg.anmf().bits { Some(reg.fidx().bits) } else { None };
+
+ let mut data_u32 = [0u32; 16];
+ data_u32
+ .iter_mut()
+ .zip(element.data.iter())
+ .for_each(|(val, reg)| *val = reg.read());
+ // TODO validate endianness
+ let data: [u8; 64] = unsafe { core::mem::transmute(data_u32) };
+
+ let data_words = element.data.len() as u8;
+
+ Self {
+ header: Header::new(id, dlc, rtr).set_can_fd(can_fd, brs).set_esi(esi),
+ data_words,
+ data,
+ timestamp,
+ filter_index,
}
- buffer[i * 4..(i + 1) * 4].copy_from_slice(register_bytes);
}
}
-
-pub(crate) fn data_from_tx_buffer(buffer: &mut [u8], mailbox_data: &[RW], len: usize) {
- for (i, register) in mailbox_data.iter().enumerate() {
- let register_value = register.read();
- let register_bytes = unsafe { slice::from_raw_parts(®ister_value as *const u32 as *const u8, 4) };
- let num_bytes = (len) - i * 4;
- if num_bytes <= 4 {
- buffer[i * 4..i * 4 + num_bytes].copy_from_slice(®ister_bytes[..num_bytes]);
- break;
- }
- buffer[i * 4..(i + 1) * 4].copy_from_slice(register_bytes);
- }
-}
-
-pub(crate) fn extract_frame(
- mailbox_header: &RxFifoElementHeader,
- mailbox_data: &[RW],
- buffer: &mut [u8],
-) -> Option<(Header, u16)> {
- let header_reg = mailbox_header.read();
-
- let id = make_id(header_reg.id().bits(), header_reg.xtd().bits());
- let dlc = header_reg.to_data_length().len();
- let len = dlc as usize;
- let timestamp = header_reg.txts().bits;
- if len > buffer.len() {
- return None;
- }
- data_from_fifo(buffer, mailbox_data, len);
- let header = if header_reg.fdf().bits {
- Header::new_fd(id, dlc, header_reg.rtr().bits(), header_reg.brs().bits())
- } else {
- Header::new(id, dlc, header_reg.rtr().bits())
- };
- Some((header, timestamp))
-}
diff --git a/embassy-stm32/src/can/fd/mod.rs b/embassy-stm32/src/can/fd/mod.rs
index e2b4c34bc..26872b7a6 100644
--- a/embassy-stm32/src/can/fd/mod.rs
+++ b/embassy-stm32/src/can/fd/mod.rs
@@ -1,6 +1,11 @@
//! Module containing that which is specific to fdcan hardware variant
-use core::{future::poll_fn, marker::PhantomData, sync::atomic::AtomicU32, task::Poll};
+use core::{
+ future::poll_fn,
+ marker::PhantomData,
+ sync::atomic::{AtomicU32, Ordering},
+ task::Poll,
+};
use configurator::Properties;
use embassy_sync::waitqueue::AtomicWaker;
@@ -36,6 +41,12 @@ pub struct Can<'d, M> {
properties: Properties,
}
+#[derive(Debug, Clone, Copy)]
+pub enum RxQueue {
+ Q0 = 0,
+ Q1 = 1,
+}
+
impl<'d, M: CanMode> Can<'d, M> {
/// Get driver properties
pub fn properties(&self) -> &Properties {
@@ -62,10 +73,12 @@ impl<'d, M: CanMode> Can<'d, M> {
}
fn internal_try_read(&self) -> Option, BusError>> {
- if let Some((header, ts, data)) = self.info.low.rx_fifo_read(0) {
- let ts = self.info.calc_timestamp(self.state.ns_per_timer_tick, ts);
- let data = &data[0..header.len() as usize];
- let frame = match BaseFrame::from_header(header, data) {
+ if let Some(element) = self.info.low.rx_fifo_read(0) {
+ let ts = self
+ .info
+ .calc_timestamp(self.state.ns_per_timer_tick, element.timestamp);
+ let data = &element.data[0..element.header.len() as usize];
+ let frame = match BaseFrame::from_header(element.header, data) {
Ok(frame) => frame,
Err(_) => panic!(),
};
@@ -102,57 +115,110 @@ impl<'d, M: CanMode> Can<'d, M> {
//self.state.rx_mode.try_read(self.info, self.state)
self.internal_try_read()
}
+}
- /// Queues the message to be sent but exerts backpressure. If a lower-priority
- /// frame is dropped from the mailbox, it is returned. If no lower-priority frames
- /// can be replaced, this call asynchronously waits for a frame to be successfully
- /// transmitted, then tries again.
- async fn internal_write(&self, frame: &BaseFrame) -> Option> {
+impl<'d, M: CanMode> Can<'d, M> {
+ /// Queues the message to be sent but exerts backpressure.
+ /// If the given queue is full, this call will wait asynchronously
+ /// until it can be added to the queue.
+ /// Returns a `TxRef` which can be used to monitor the completion
+ /// of the transmission.
+ pub async fn tx_queue(&self, frame: &BaseFrame) -> TxRef {
poll_fn(|cx| {
self.state.tx_waker.register(cx.waker());
match self.info.low.tx_fifo_add(frame.header(), frame.data()) {
- Some(_idx) => Poll::Ready(None),
+ // Frame was successfully added to slot.
+ Some(slot_idx) => {
+ let generation = self.state.tx_generations[slot_idx as usize].fetch_add(1, Ordering::Relaxed);
+
+ Poll::Ready(TxRef {
+ slot_idx,
+ generation,
+ state: self.state,
+ completed: false,
+ })
+ }
+ // No free slot, retry later.
None => Poll::Pending,
}
+ })
+ .await
+ }
+}
- //if !info.low.bad_write(frame) {
- // return Poll::Ready(*frame);
- //}
- //if let Ok(dropped) = info.low.bad_write(frame) {
- // return Poll::Ready(dropped);
- //}
+// == TX
+// Buffer add req -> TXBAR
+// Buffer pending, TXBRP AS, TXBTO DE, TXBCF DE
+//
+// Buffer transmitted, TXBTO AS, TXBRP DE
+//
+// Buffer cancel req -> TXBCR
+// Buffer failed, TXBCF AS, TXBRP DE
- // Couldn't replace any lower priority frames. Need to wait for some mailboxes
- // to clear.
- //Poll::Pending
+/// Reference to a frame which is in the process of being transmitted.
+pub struct TxRef {
+ slot_idx: u8,
+ generation: u32,
+ state: &'static State,
+ completed: bool,
+}
+
+impl TxRef {
+ /// Waits for transmission to finish.
+ pub async fn wait(&mut self) {
+ poll_fn(|cx| {
+ if self.poll_done() {
+ Poll::Ready(())
+ } else {
+ self.state.tx_done_waker.register(cx.waker());
+ Poll::Pending
+ }
})
.await
}
- /// Queues the message to be sent but exerts backpressure. If a lower-priority
- /// frame is dropped from the mailbox, it is returned. If no lower-priority frames
- /// can be replaced, this call asynchronously waits for a frame to be successfully
- /// transmitted, then tries again.
- pub async fn write(&mut self, frame: &BaseFrame) -> Option> {
- self.internal_write(frame).await
+ /// Polls the transmission, returns true if it is finished.
+ pub fn poll_done(&mut self) -> bool {
+ // If there is a result stored in self, we are finished.
+ if self.completed {
+ return true;
+ }
+
+ // Check the generation for the current slot, if it is different
+ // the slot finished.
+ let current_generation = self.state.tx_generations[self.slot_idx as usize].load(Ordering::Relaxed);
+ if current_generation != self.generation {
+ // Store completion state to handle subsequent calls.
+ self.completed = true;
+ return true;
+ }
+
+ return false;
}
- /// Queues the message to be sent. Handles rescheduling lower priority message
- /// if one was removed from the mailbox.
- pub async fn write_blocking(&mut self, frame: &BaseFrame) {
- let mut frame_opt = self.internal_write(frame).await;
- while let Some(frame) = frame_opt {
- frame_opt = self.internal_write(&frame).await;
- }
+ /// Begins cancellation of the transmission.
+ /// Cancellation may take some time, and may race with
+ /// successful transmission.
+ /// You can call `wait` to wait for transmission completion
+ /// or call `poll` to check the state at any point.
+ pub fn cancel(&mut self) -> &mut Self {
+ // Set the tx busy flag.
+ // This prevents any other transmission from taking our slot
+ self.state.info.low.tx_cancel(1u32 << self.slot_idx);
+ self
}
}
pub(self) struct State {
+ pub info: &'static Info,
+
pub rx_waker: AtomicWaker,
pub tx_waker: AtomicWaker,
- pub refcount: AtomicU32,
+ pub tx_done_waker: AtomicWaker,
+ pub tx_busy: AtomicU32,
+ pub tx_generations: [AtomicU32; 32],
pub err_waker: AtomicWaker,
@@ -160,11 +226,50 @@ pub(self) struct State {
}
impl State {
- const fn new() -> Self {
+ const fn new(info: &'static Info) -> Self {
Self {
+ info,
+
rx_waker: AtomicWaker::new(),
tx_waker: AtomicWaker::new(),
- refcount: AtomicU32::new(0),
+
+ tx_done_waker: AtomicWaker::new(),
+ tx_busy: AtomicU32::new(0),
+ tx_generations: [
+ AtomicU32::new(0),
+ AtomicU32::new(0),
+ AtomicU32::new(0),
+ AtomicU32::new(0),
+ AtomicU32::new(0),
+ AtomicU32::new(0),
+ AtomicU32::new(0),
+ AtomicU32::new(0),
+ AtomicU32::new(0),
+ AtomicU32::new(0),
+ AtomicU32::new(0),
+ AtomicU32::new(0),
+ AtomicU32::new(0),
+ AtomicU32::new(0),
+ AtomicU32::new(0),
+ AtomicU32::new(0),
+ AtomicU32::new(0),
+ AtomicU32::new(0),
+ AtomicU32::new(0),
+ AtomicU32::new(0),
+ AtomicU32::new(0),
+ AtomicU32::new(0),
+ AtomicU32::new(0),
+ AtomicU32::new(0),
+ AtomicU32::new(0),
+ AtomicU32::new(0),
+ AtomicU32::new(0),
+ AtomicU32::new(0),
+ AtomicU32::new(0),
+ AtomicU32::new(0),
+ AtomicU32::new(0),
+ AtomicU32::new(0),
+ ],
+
err_waker: AtomicWaker::new(),
ns_per_timer_tick: 0,
}
diff --git a/embassy-stm32/src/can/fd/peripheral.rs b/embassy-stm32/src/can/fd/peripheral.rs
index 3840b4460..cce8e5814 100644
--- a/embassy-stm32/src/can/fd/peripheral.rs
+++ b/embassy-stm32/src/can/fd/peripheral.rs
@@ -64,52 +64,55 @@ macro_rules! impl_fdcan {
($inst:ident,
//$irq0:ident, $irq1:ident,
$msg_ram_inst:ident, $msg_ram_offset:literal, $msg_ram_size:literal) => {
- impl SealedInstance for peripherals::$inst {
- const MSG_RAM_OFFSET: usize = $msg_ram_offset;
-
- unsafe fn mut_info() -> &'static mut Info {
- static mut INFO: Info = Info {
- low: unsafe { CanLowLevel::new(crate::pac::$inst, crate::pac::$msg_ram_inst, $msg_ram_offset, $msg_ram_size) },
- interrupt0: crate::_generated::peripheral_interrupts::$inst::IT0::IRQ,
- _interrupt1: crate::_generated::peripheral_interrupts::$inst::IT1::IRQ,
- tx_waker: crate::_generated::peripheral_interrupts::$inst::IT0::pend,
- };
- &mut *core::ptr::addr_of_mut!(INFO)
- }
-
- fn info() -> &'static Info {
- unsafe { peripherals::$inst::mut_info() }
- }
-
- unsafe fn mut_state() -> &'static mut State {
- static mut STATE: State = State::new();
- &mut *core::ptr::addr_of_mut!(STATE)
- }
- fn state() -> &'static State {
- unsafe { peripherals::$inst::mut_state() }
- }
-
- #[cfg(feature = "time")]
- fn calc_timestamp(ns_per_timer_tick: u64, ts_val: u16) -> Timestamp {
- let now_embassy = embassy_time::Instant::now();
- if ns_per_timer_tick == 0 {
- return now_embassy;
- }
- let cantime = { Self::info().low.regs.tscv().read().tsc() };
- let delta = cantime.overflowing_sub(ts_val).0 as u64;
- let ns = ns_per_timer_tick * delta as u64;
- now_embassy - embassy_time::Duration::from_nanos(ns)
- }
-
- #[cfg(not(feature = "time"))]
- fn calc_timestamp(_ns_per_timer_tick: u64, ts_val: u16) -> Timestamp {
- ts_val
- }
-
- }
#[allow(non_snake_case)]
pub(crate) mod $inst {
+ use super::*;
+
+ static mut INFO: Info = Info {
+ low: unsafe { CanLowLevel::new(crate::pac::$inst, crate::pac::$msg_ram_inst, $msg_ram_offset, $msg_ram_size) },
+ interrupt0: crate::_generated::peripheral_interrupts::$inst::IT0::IRQ,
+ _interrupt1: crate::_generated::peripheral_interrupts::$inst::IT1::IRQ,
+ tx_waker: crate::_generated::peripheral_interrupts::$inst::IT0::pend,
+ };
+
+ impl SealedInstance for peripherals::$inst {
+ const MSG_RAM_OFFSET: usize = $msg_ram_offset;
+
+ unsafe fn mut_info() -> &'static mut Info {
+ &mut *core::ptr::addr_of_mut!(INFO)
+ }
+
+ fn info() -> &'static Info {
+ unsafe { peripherals::$inst::mut_info() }
+ }
+
+ unsafe fn mut_state() -> &'static mut State {
+ static mut STATE: State = State::new(unsafe { &*core::ptr::addr_of!(INFO) });
+ &mut *core::ptr::addr_of_mut!(STATE)
+ }
+ fn state() -> &'static State {
+ unsafe { peripherals::$inst::mut_state() }
+ }
+
+ #[cfg(feature = "time")]
+ fn calc_timestamp(ns_per_timer_tick: u64, ts_val: u16) -> Timestamp {
+ let now_embassy = embassy_time::Instant::now();
+ if ns_per_timer_tick == 0 {
+ return now_embassy;
+ }
+ let cantime = { Self::info().low.regs.tscv().read().tsc() };
+ let delta = cantime.overflowing_sub(ts_val).0 as u64;
+ let ns = ns_per_timer_tick * delta as u64;
+ now_embassy - embassy_time::Duration::from_nanos(ns)
+ }
+
+ #[cfg(not(feature = "time"))]
+ fn calc_timestamp(_ns_per_timer_tick: u64, ts_val: u16) -> Timestamp {
+ ts_val
+ }
+
+ }
foreach_interrupt!(
($inst,can,FDCAN,IT0,$irq:ident) => {
@@ -119,10 +122,11 @@ macro_rules! impl_fdcan {
pub type Interrupt1 = crate::interrupt::typelevel::$irq;
};
);
- }
- impl Instance for peripherals::$inst {
- type IT0Interrupt = $inst::Interrupt0;
- type IT1Interrupt = $inst::Interrupt1;
+
+ impl Instance for peripherals::$inst {
+ type IT0Interrupt = $inst::Interrupt0;
+ type IT1Interrupt = $inst::Interrupt1;
+ }
}
};
}
diff --git a/embassy-stm32/src/can/fd/util.rs b/embassy-stm32/src/can/fd/util.rs
index a740cc84c..f68dc6607 100644
--- a/embassy-stm32/src/can/fd/util.rs
+++ b/embassy-stm32/src/can/fd/util.rs
@@ -3,11 +3,11 @@ use super::Info;
pub fn calc_ns_per_timer_tick(
info: &'static Info,
freq: crate::time::Hertz,
- mode: crate::can::fd::config::FrameTransmissionConfig,
+ mode: crate::can::fd::config::CanFdMode,
) -> u64 {
match mode {
// Use timestamp from Rx FIFO to adjust timestamp reported to user
- crate::can::fd::config::FrameTransmissionConfig::ClassicCanOnly => {
+ crate::can::fd::config::CanFdMode::ClassicCanOnly => {
let prescale: u64 = ({ info.low.regs.nbtp().read().nbrp() } + 1) as u64
* ({ info.low.regs.tscc().read().tcp() } + 1) as u64;
1_000_000_000 as u64 / (freq.0 as u64 * prescale)
diff --git a/embassy-stm32/src/can/fdcan.rs b/embassy-stm32/src/can/fdcan.rs
index c51788138..455e0eb6a 100644
--- a/embassy-stm32/src/can/fdcan.rs
+++ b/embassy-stm32/src/can/fdcan.rs
@@ -12,6 +12,7 @@ use super::frame::*;
pub use self::fd::{config, filter};
pub use super::common::{BufferedCanReceiver, BufferedCanSender};
pub use fd::configurator::CanConfigurator;
+pub use fd::interrupt::{IT0InterruptHandler, IT1InterruptHandler};
pub use fd::peripheral::*;
pub use fd::Can;
diff --git a/embassy-stm32/src/can/frame.rs b/embassy-stm32/src/can/frame.rs
index e8171d110..4cd9a27e7 100644
--- a/embassy-stm32/src/can/frame.rs
+++ b/embassy-stm32/src/can/frame.rs
@@ -38,9 +38,17 @@ impl defmt::Format for Header {
}
impl Header {
- const FLAG_RTR: usize = 0; // Remote
- const FLAG_FDCAN: usize = 1; // FDCan vs Classic CAN
- const FLAG_BRS: usize = 2; // Bit-rate switching, ignored for Classic CAN
+ /// RTR (remote transmit request) flag in CAN frame
+ const FLAG_RTR: usize = 0;
+ /// FDCan vs Classic CAN
+ const FLAG_FDCAN: usize = 1;
+ /// Bit-rate switching, ignored for Classic CAN
+ const FLAG_BRS: usize = 2;
+ /// ESI recessive in CAN FD message.
+ /// ORed with error passive flag before transmission.
+ /// By spec, an error active mode may transmit ESI resessive,
+ /// but an error passive node will always transmit ESI resessive.
+ const FLAG_ESI: usize = 3;
/// Create new CAN Header
pub fn new(id: embedded_can::Id, len: u8, rtr: bool) -> Header {
@@ -49,13 +57,15 @@ impl Header {
Header { id, len, flags }
}
- /// Create new CAN FD Header
- pub fn new_fd(id: embedded_can::Id, len: u8, rtr: bool, brs: bool) -> Header {
- let mut flags = 0u8;
- flags.set_bit(Self::FLAG_RTR, rtr);
- flags.set_bit(Self::FLAG_FDCAN, true);
- flags.set_bit(Self::FLAG_BRS, brs);
- Header { id, len, flags }
+ pub fn set_can_fd(mut self, flag: bool, brs: bool) -> Header {
+ self.flags.set_bit(Self::FLAG_FDCAN, flag);
+ self.flags.set_bit(Self::FLAG_BRS, brs);
+ self
+ }
+
+ pub fn set_esi(mut self, flag: bool) -> Header {
+ self.flags.set_bit(Self::FLAG_ESI, flag);
+ self
}
/// Return ID
@@ -73,6 +83,10 @@ impl Header {
self.flags.get_bit(Self::FLAG_RTR)
}
+ pub fn esi(&self) -> bool {
+ self.flags.get_bit(Self::FLAG_ESI)
+ }
+
/// Request/is FDCAN frame
pub fn fdcan(&self) -> bool {
self.flags.get_bit(Self::FLAG_FDCAN)