mirror of
https://github.com/embassy-rs/embassy.git
synced 2024-11-21 22:32:29 +00:00
work on high level driver
This commit is contained in:
parent
1e7af3e966
commit
819ff1b7e3
169
embassy-stm32/src/can/fd/config/message_ram.rs
Normal file
169
embassy-stm32/src/can/fd/config/message_ram.rs
Normal 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,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
@ -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(),
|
||||
}
|
||||
}
|
||||
}
|
@ -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,
|
||||
|
@ -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() {
|
||||
|
@ -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);
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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) }
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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(®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<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(®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<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))
|
||||
}
|
||||
|
@ -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,
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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)
|
||||
|
Loading…
Reference in New Issue
Block a user