work on high level driver

This commit is contained in:
Hans Josephsen 2024-09-25 18:51:41 +02:00
parent 1e7af3e966
commit 819ff1b7e3
14 changed files with 682 additions and 625 deletions

View File

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

View File

@ -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 <http://www.bittiming.can-wiki.info/> 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<crate::pac::can::vals::Tfqm> 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(),
}
}
}

View File

@ -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<M: CanMode>(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,

View File

@ -57,32 +57,35 @@ pub struct IT0InterruptHandler<T: Instance> {
impl<T: Instance> interrupt::typelevel::Handler<T::IT0Interrupt> for IT0InterruptHandler<T> {
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::<T>(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::<T>(1);
}
// Bus_Off
if ir.bo() {
regs.ir().write(|w| w.set_bo(true));
if regs.psr().read().bo() {

View File

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

View File

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

View File

@ -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<H: Sized> Elements<HeaderElement<H>> {
}
}
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<usize>,
/// 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<E: ?Sized>(&self, base_ptr: *mut RW<u32>) -> Elements<E> {
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<usize>,
}
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<u32>) -> 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<u32>;
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) }
}
}

View File

@ -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<F: embedded_can::Frame + CanHeader>(&self, frame: &F) -> nb::Result<bool, Infallible> {
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<RxFifoElementHeader>) -> 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<RxElementData> {
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<RxElementData> {
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)
}
}

View File

@ -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<u32>], 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<TxBufferElementHeader>) -> Self {
// todo!()
//}
#[inline]
pub fn put(&self, element: &mut HeaderElement<TxBufferElementHeader>) {
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<u32>], len: usize) {
for (i, register) in mailbox_data.iter().enumerate() {
let register_value = register.read();
let register_bytes = unsafe { slice::from_raw_parts(&register_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(&register_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<u8>,
}
impl RxElementData {
#[inline]
pub fn extract(element: &HeaderElement<RxFifoElementHeader>) -> 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<u32>], len: usize) {
for (i, register) in mailbox_data.iter().enumerate() {
let register_value = register.read();
let register_bytes = unsafe { slice::from_raw_parts(&register_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(&register_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<u32>],
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))
}

View File

@ -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<Result<BaseEnvelope<M>, 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<M>) -> Option<BaseFrame<M>> {
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<M>) -> 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<M>) -> Option<BaseFrame<M>> {
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<M>) {
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,
}

View File

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

View File

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

View File

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

View File

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