mirror of
https://github.com/embassy-rs/embassy.git
synced 2024-11-25 16:23:10 +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};
|
use core::num::{NonZeroU16, NonZeroU8};
|
||||||
|
|
||||||
|
#[cfg(can_fdcan_h7)]
|
||||||
|
mod message_ram;
|
||||||
|
#[cfg(can_fdcan_h7)]
|
||||||
|
pub use message_ram::*;
|
||||||
|
|
||||||
/// Configures the bit timings.
|
/// Configures the bit timings.
|
||||||
///
|
///
|
||||||
/// You can use <http://www.bittiming.can-wiki.info/> to calculate the `btr` parameter. Enter
|
/// 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)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
pub enum ClockDivider {
|
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
|
/// FdCan Config Struct
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
pub struct FdCanConfig {
|
pub struct FdCanConfig {
|
||||||
@ -334,12 +344,6 @@ pub struct FdCanConfig {
|
|||||||
/// "babbling idiot" scenarios where the application program erroneously requests too many
|
/// "babbling idiot" scenarios where the application program erroneously requests too many
|
||||||
/// transmissions.
|
/// transmissions.
|
||||||
pub transmit_pause: bool,
|
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
|
/// Edge Filtering: Two consecutive dominant tq required to detect an edge for hard synchronization
|
||||||
pub edge_filtering: bool,
|
pub edge_filtering: bool,
|
||||||
/// Enables protocol exception handling
|
/// Enables protocol exception handling
|
||||||
@ -353,9 +357,17 @@ pub struct FdCanConfig {
|
|||||||
/// TX buffer mode (FIFO or priority queue)
|
/// TX buffer mode (FIFO or priority queue)
|
||||||
pub tx_buffer_mode: TxBufferMode,
|
pub tx_buffer_mode: TxBufferMode,
|
||||||
|
|
||||||
/// If this is set, the FDCAN uses the CAN FD frame format as specified by the Bosch CAN
|
/// Configures whether the peripheral uses CAN FD as specified by the
|
||||||
/// FD Specification V1.0.
|
/// Bosch CAN FD Specification V1.0 or if it is limited to regular classic
|
||||||
pub can_fd_enabled: bool,
|
/// 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 {
|
impl FdCanConfig {
|
||||||
@ -396,11 +408,12 @@ impl FdCanConfig {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// If this is set, the FDCAN uses the CAN FD frame format as specified by the Bosch CAN
|
/// Configures whether the peripheral uses CAN FD as specified by the
|
||||||
/// FD Specification V1.0.
|
/// Bosch CAN FD Specification V1.0 or if it is limited to regular classic
|
||||||
|
/// ISO CAN.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub const fn set_can_fd_enabled(mut self, enabled: bool) -> Self {
|
pub const fn set_can_fd_mode(mut self, mode: CanFdMode) -> Self {
|
||||||
self.can_fd_enabled = enabled;
|
self.can_fd_mode = mode;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -411,13 +424,6 @@ impl FdCanConfig {
|
|||||||
self
|
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
|
/// Enables protocol exception handling
|
||||||
#[inline]
|
#[inline]
|
||||||
pub const fn set_protocol_exception_handling(mut self, peh: bool) -> Self {
|
pub const fn set_protocol_exception_handling(mut self, peh: bool) -> Self {
|
||||||
@ -452,6 +458,12 @@ impl FdCanConfig {
|
|||||||
self.tx_buffer_mode = txbm;
|
self.tx_buffer_mode = txbm;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub const fn set_message_ram_config(mut self, config: MessageRamConfig) -> Self {
|
||||||
|
self.message_ram_config = config;
|
||||||
|
self
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for FdCanConfig {
|
impl Default for FdCanConfig {
|
||||||
@ -462,14 +474,14 @@ impl Default for FdCanConfig {
|
|||||||
dbtr: DataBitTiming::default(),
|
dbtr: DataBitTiming::default(),
|
||||||
automatic_retransmit: true,
|
automatic_retransmit: true,
|
||||||
transmit_pause: false,
|
transmit_pause: false,
|
||||||
frame_transmit: FrameTransmissionConfig::ClassicCanOnly,
|
|
||||||
non_iso_mode: false,
|
|
||||||
edge_filtering: false,
|
edge_filtering: false,
|
||||||
protocol_exception_handling: true,
|
protocol_exception_handling: true,
|
||||||
clock_divider: ClockDivider::_1,
|
clock_divider: ClockDivider::_1,
|
||||||
timestamp_source: TimestampSource::None,
|
timestamp_source: TimestampSource::None,
|
||||||
global_filter: GlobalFilter::default(),
|
global_filter: GlobalFilter::default(),
|
||||||
tx_buffer_mode: TxBufferMode::Priority,
|
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::{
|
use crate::{
|
||||||
can::{
|
can::{
|
||||||
common::CanMode,
|
common::CanMode,
|
||||||
config::{FrameTransmissionConfig, TimestampPrescaler, TimestampSource},
|
config::{TimestampPrescaler, TimestampSource},
|
||||||
filter::{ExtendedFilter, StandardFilter},
|
filter::{ExtendedFilter, StandardFilter},
|
||||||
util, Can, Instance, OperatingMode, RxPin, TxPin,
|
util, Can, Instance, OperatingMode, RxPin, TxPin,
|
||||||
},
|
},
|
||||||
@ -15,6 +15,7 @@ use crate::{
|
|||||||
interrupt, rcc, Peripheral,
|
interrupt, rcc, Peripheral,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use super::config::CanFdMode;
|
||||||
use super::{calc_ns_per_timer_tick, IT0InterruptHandler, IT1InterruptHandler, Info, State};
|
use super::{calc_ns_per_timer_tick, IT0InterruptHandler, IT1InterruptHandler, Info, State};
|
||||||
|
|
||||||
/// FDCAN Configuration instance instance
|
/// FDCAN Configuration instance instance
|
||||||
@ -56,9 +57,7 @@ impl<'d> CanConfigurator<'d> {
|
|||||||
info.low.apply_config(&config);
|
info.low.apply_config(&config);
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
//unsafe { T::mut_info() }.low.apply_message_ram_config();
|
||||||
unsafe { T::mut_info() }.low.apply_dummy_msg_ram_config();
|
|
||||||
}
|
|
||||||
|
|
||||||
rx.set_as_af(rx.af_num(), AfType::input(Pull::None));
|
rx.set_as_af(rx.af_num(), AfType::input(Pull::None));
|
||||||
tx.set_as_af(tx.af_num(), AfType::output(OutputType::PushPull, Speed::VeryHigh));
|
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,
|
seg1: bit_timing.seg1,
|
||||||
seg2: bit_timing.seg2,
|
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);
|
self.config = self.config.set_data_bit_timing(nbtr);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Start in mode.
|
/// Start in mode.
|
||||||
pub fn start<M: CanMode>(self, mode: OperatingMode) -> Can<'d, M> {
|
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(|_| {
|
critical_section::with(|_| {
|
||||||
let state = self.state as *const State;
|
let state = self.state as *const State;
|
||||||
unsafe {
|
unsafe {
|
||||||
let mut_state = state as *mut State;
|
let mut_state = state as *mut State;
|
||||||
(*mut_state).ns_per_timer_tick = ns_per_timer_tick;
|
(*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.apply_config(&self.config);
|
||||||
self.info.low.into_mode(mode);
|
self.info.low.into_mode(mode);
|
||||||
|
|
||||||
Can {
|
Can {
|
||||||
_phantom: PhantomData,
|
_phantom: PhantomData,
|
||||||
config: self.config,
|
config: self.config,
|
||||||
|
@ -57,32 +57,35 @@ pub struct IT0InterruptHandler<T: Instance> {
|
|||||||
impl<T: Instance> interrupt::typelevel::Handler<T::IT0Interrupt> for IT0InterruptHandler<T> {
|
impl<T: Instance> interrupt::typelevel::Handler<T::IT0Interrupt> for IT0InterruptHandler<T> {
|
||||||
unsafe fn on_interrupt() {
|
unsafe fn on_interrupt() {
|
||||||
let regs = T::info().low.regs;
|
let regs = T::info().low.regs;
|
||||||
|
|
||||||
let ir = regs.ir().read();
|
let ir = regs.ir().read();
|
||||||
|
|
||||||
|
// TX transfer complete
|
||||||
if ir.tc() {
|
if ir.tc() {
|
||||||
regs.ir().write(|w| w.set_tc(true));
|
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() {
|
if ir.tefn() {
|
||||||
regs.ir().write(|w| w.set_tefn(true));
|
regs.ir().write(|w| w.set_tefn(true));
|
||||||
}
|
}
|
||||||
|
|
||||||
T::state().tx_waker.wake();
|
T::state().tx_waker.wake();
|
||||||
//match &T::state().tx_mode {
|
|
||||||
// TxMode::NonBuffered(waker) => waker.wake(),
|
|
||||||
//}
|
|
||||||
|
|
||||||
|
// RX FIFO new element
|
||||||
if ir.rfn(0) {
|
if ir.rfn(0) {
|
||||||
T::info().low.regs.ir().write(|w| w.set_rfn(0, true));
|
T::info().low.regs.ir().write(|w| w.set_rfn(0, true));
|
||||||
T::state().rx_waker.wake();
|
T::state().rx_waker.wake();
|
||||||
//T::state().rx_mode.on_interrupt::<T>(0);
|
|
||||||
}
|
}
|
||||||
if ir.rfn(1) {
|
if ir.rfn(1) {
|
||||||
T::info().low.regs.ir().write(|w| w.set_rfn(1, true));
|
T::info().low.regs.ir().write(|w| w.set_rfn(1, true));
|
||||||
T::state().rx_waker.wake();
|
T::state().rx_waker.wake();
|
||||||
//T::state().rx_mode.on_interrupt::<T>(1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Bus_Off
|
||||||
if ir.bo() {
|
if ir.bo() {
|
||||||
regs.ir().write(|w| w.set_bo(true));
|
regs.ir().write(|w| w.set_bo(true));
|
||||||
if regs.psr().read().bo() {
|
if regs.psr().read().bo() {
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
use super::{
|
use super::{
|
||||||
message_ram::{self, MessageRam},
|
message_ram::{MessageRam, MessageRamSegment},
|
||||||
CanLowLevel, LoopbackMode, TimestampSource,
|
CanLowLevel, LoopbackMode, TimestampSource,
|
||||||
};
|
};
|
||||||
use crate::can::config::{DataBitTiming, FdCanConfig, FrameTransmissionConfig, GlobalFilter, NominalBitTiming};
|
use crate::can::config::{CanFdMode, DataBitTiming, FdCanConfig, GlobalFilter, MessageRamConfig, NominalBitTiming};
|
||||||
|
|
||||||
/// Configuration.
|
/// Configuration.
|
||||||
/// Can only be called when in config mode (CCCR.INIT=1, CCCR.CCE=1).
|
/// 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) {
|
pub fn apply_message_ram_config(&mut self, config: MessageRamConfig) {
|
||||||
let message_ram = config.apply_config(&self.regs, &self.msgram);
|
let segment = MessageRamSegment {
|
||||||
self.message_ram = message_ram;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn apply_dummy_msg_ram_config(&mut self) {
|
|
||||||
self.apply_message_ram_config(message_ram::MessageRamConfig {
|
|
||||||
base_offset: self.msg_ram_offset,
|
base_offset: self.msg_ram_offset,
|
||||||
available_space: Some(self.msg_ram_size),
|
available_space: Some(self.msg_ram_size),
|
||||||
standard_id_filter_size: 28,
|
};
|
||||||
extended_id_filter_size: 8,
|
let message_ram = unsafe { config.apply_config(&segment, &self.regs, &self.msgram) };
|
||||||
rx_fifo_0: message_ram::RxFifoConfig {
|
self.message_ram = message_ram;
|
||||||
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,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Applies the settings of a new FdCanConfig See [`FdCanConfig`]
|
/// Applies the settings of a new FdCanConfig See [`FdCanConfig`]
|
||||||
@ -75,8 +56,7 @@ impl CanLowLevel {
|
|||||||
self.set_nominal_bit_timing(config.nbtr);
|
self.set_nominal_bit_timing(config.nbtr);
|
||||||
self.set_automatic_retransmit(config.automatic_retransmit);
|
self.set_automatic_retransmit(config.automatic_retransmit);
|
||||||
self.set_transmit_pause(config.transmit_pause);
|
self.set_transmit_pause(config.transmit_pause);
|
||||||
self.set_frame_transmit(config.frame_transmit);
|
self.set_can_fd_mode(config.can_fd_mode);
|
||||||
self.set_non_iso_mode(config.can_fd_enabled);
|
|
||||||
self.set_edge_filtering(config.edge_filtering);
|
self.set_edge_filtering(config.edge_filtering);
|
||||||
self.set_protocol_exception_handling(config.protocol_exception_handling);
|
self.set_protocol_exception_handling(config.protocol_exception_handling);
|
||||||
self.set_global_filter(config.global_filter);
|
self.set_global_filter(config.global_filter);
|
||||||
@ -163,23 +143,18 @@ impl CanLowLevel {
|
|||||||
self.regs.cccr().modify(|w| w.set_efbi(enabled));
|
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
|
/// Configures frame transmission mode. See
|
||||||
/// [`FdCanConfig::set_frame_transmit`]
|
/// [`FdCanConfig::set_frame_transmit`]
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn set_frame_transmit(&self, fts: FrameTransmissionConfig) {
|
pub fn set_can_fd_mode(&self, fts: CanFdMode) {
|
||||||
let (fdoe, brse) = match fts {
|
let (niso, fdoe, brse) = match fts {
|
||||||
FrameTransmissionConfig::ClassicCanOnly => (false, false),
|
CanFdMode::ClassicCanOnly => (false, false, false),
|
||||||
FrameTransmissionConfig::AllowFdCan => (true, false),
|
CanFdMode::AllowFdCan => (true, true, false),
|
||||||
FrameTransmissionConfig::AllowFdCanAndBRS => (true, true),
|
CanFdMode::AllowFdCanAndBRS => (true, true, true),
|
||||||
};
|
};
|
||||||
|
|
||||||
self.regs.cccr().modify(|w| {
|
self.regs.cccr().modify(|w| {
|
||||||
|
w.set_niso(niso);
|
||||||
w.set_fdoe(fdoe);
|
w.set_fdoe(fdoe);
|
||||||
#[cfg(can_fdcan_h7)]
|
#[cfg(can_fdcan_h7)]
|
||||||
w.set_bse(brse);
|
w.set_bse(brse);
|
||||||
|
@ -153,7 +153,7 @@ impl<'a> ID_W<'a> {
|
|||||||
#[doc = r"Writes raw bits to the field"]
|
#[doc = r"Writes raw bits to the field"]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[allow(dead_code)]
|
#[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.bits[0] = (self.w.bits[0] & !(0x1FFFFFFF)) | ((value as u32) & 0x1FFFFFFF);
|
||||||
self.w
|
self.w
|
||||||
}
|
}
|
||||||
@ -304,13 +304,14 @@ pub(crate) struct MM_W<'a> {
|
|||||||
impl<'a> MM_W<'a> {
|
impl<'a> MM_W<'a> {
|
||||||
#[doc = r"Writes raw bits to the field"]
|
#[doc = r"Writes raw bits to the field"]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub unsafe fn bits(self, value: u8) -> &'a mut W {
|
pub fn bits(self, value: u8) -> &'a mut W {
|
||||||
self.w.bits[1] = (self.w.bits[1] & !(0x7F << 24)) | (((value as u32) & 0x7F) << 24);
|
self.w.bits[1] = (self.w.bits[1] & !(0xff << 24)) | (((value as u32) & 0xff) << 24);
|
||||||
self.w
|
self.w
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_message_marker(self, mm: Marker) -> &'a mut W {
|
#[inline(always)]
|
||||||
unsafe { self.bits(mm.0) }
|
pub fn set_message_marker(self, mm: Marker) -> &'a mut W {
|
||||||
|
self.bits(mm.0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
use core::marker::PhantomData;
|
use core::marker::PhantomData;
|
||||||
|
|
||||||
use element::tx_event::TxEventElement;
|
use element::tx_event::TxEventElement;
|
||||||
use stm32_metapac::can::vals::Tfqm;
|
|
||||||
use volatile_register::RW;
|
use volatile_register::RW;
|
||||||
|
|
||||||
mod element;
|
mod element;
|
||||||
@ -11,6 +10,8 @@ pub(crate) use element::{
|
|||||||
tx_buffer::TxBufferElementHeader,
|
tx_buffer::TxBufferElementHeader,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use crate::can::config::MessageRamConfig;
|
||||||
|
|
||||||
/// Configuration for MessageRam layout.
|
/// Configuration for MessageRam layout.
|
||||||
pub struct MessageRam {
|
pub struct MessageRam {
|
||||||
// 32 bit words
|
// 32 bit words
|
||||||
@ -70,24 +71,6 @@ impl MessageRam {
|
|||||||
#[cfg(can_fdcan_h7)]
|
#[cfg(can_fdcan_h7)]
|
||||||
trigger_memory: Elements::EMPTY,
|
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)]
|
#[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);
|
struct ElementAllocator(usize);
|
||||||
impl ElementAllocator {
|
impl ElementAllocator {
|
||||||
fn new(offset: usize) -> Self {
|
fn new(offset: usize) -> Self {
|
||||||
@ -363,6 +191,9 @@ impl ElementSizing {
|
|||||||
fn end_offset_words(&self) -> usize {
|
fn end_offset_words(&self) -> usize {
|
||||||
self.offset + self.total_size_words()
|
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 {
|
struct ElementsSizing {
|
||||||
@ -381,7 +212,20 @@ struct ElementsSizing {
|
|||||||
trigger: ElementSizing,
|
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 {
|
impl ElementsSizing {
|
||||||
|
/// Calculates the sizing used for the simplified variant of the M_CAN peripheral.
|
||||||
fn calculate_element_sizing_simplified() -> ElementsSizing {
|
fn calculate_element_sizing_simplified() -> ElementsSizing {
|
||||||
let mut a = ElementAllocator::new(0);
|
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!(
|
assert!(
|
||||||
config.standard_id_filter_size <= 128,
|
config.standard_id_filter_size <= 128,
|
||||||
"more than 128 standard id filters not supported"
|
"more than 128 standard id filters not supported"
|
||||||
@ -427,7 +273,7 @@ impl ElementsSizing {
|
|||||||
"total TX elements can not be larger than 32"
|
"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 base_offset_words = base_offset >> 2;
|
||||||
let mut a = ElementAllocator::new(base_offset_words);
|
let mut a = ElementAllocator::new(base_offset_words);
|
||||||
|
|
||||||
@ -447,148 +293,97 @@ impl ElementsSizing {
|
|||||||
2 + config.rx_buffer.data_field_size.word_size(),
|
2 + config.rx_buffer.data_field_size.word_size(),
|
||||||
config.rx_buffer.size as usize,
|
config.rx_buffer.size as usize,
|
||||||
),
|
),
|
||||||
// TODO implement TX events
|
// Hard code size 16 for TX Event FIFO.
|
||||||
tx_event: a.next(2, 0),
|
// Closely coupled to the driver implementation, no reason to
|
||||||
|
// allow for customization for now.
|
||||||
|
tx_event: a.next(2, 16),
|
||||||
tx_elements: a.next(
|
tx_elements: a.next(
|
||||||
2 + config.tx.data_field_size.word_size(),
|
2 + config.tx.data_field_size.word_size(),
|
||||||
(config.tx.dedicated_size + config.tx.queue_size) as usize,
|
(config.tx.dedicated_size + config.tx.queue_size) as usize,
|
||||||
),
|
),
|
||||||
dedicated_len: config.tx.dedicated_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),
|
trigger: a.next(2, 0),
|
||||||
end_offset: a.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)]
|
#[cfg(can_fdcan_h7)]
|
||||||
impl MessageRamConfig {
|
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
|
/// Configures message ram for the peripheral according to the supplied
|
||||||
/// config and returns a struct which can be used to interact with the
|
/// config and returns a struct which can be used to interact with the
|
||||||
/// message RAM.
|
/// message RAM.
|
||||||
#[cfg(can_fdcan_h7)]
|
pub(crate) unsafe fn apply_config(
|
||||||
pub fn apply_config(&self, regs: &crate::pac::can::Fdcan, ram: &crate::pac::fdcanram::Fdcanram) -> MessageRam {
|
&self,
|
||||||
let base_offset = self.base_offset;
|
segment: &MessageRamSegment,
|
||||||
let base_offset_words = base_offset >> 2;
|
regs: &crate::pac::can::Fdcan,
|
||||||
|
ram: &crate::pac::fdcanram::Fdcanram,
|
||||||
|
) -> MessageRam {
|
||||||
|
let sizing = ElementsSizing::calculate_element_sizing(self, segment);
|
||||||
|
|
||||||
// Abbreviations:
|
let total_size_words = sizing.end_offset - sizing.start_offset;
|
||||||
// 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_bytes = total_size_words << 2;
|
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!");
|
assert!(total_size_bytes <= avail, "CAN RAM config exceeded available space!");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Standard ID filter config
|
// Standard ID filter config
|
||||||
// Fully managed
|
// Fully managed
|
||||||
regs.sidfc().modify(|v| {
|
regs.sidfc().modify(|v| {
|
||||||
v.set_flssa(sid_sa as u16);
|
v.set_flssa(sizing.standard_id.offset as u16);
|
||||||
v.set_lss(sid_en as u8);
|
v.set_lss(sizing.standard_id.num_elements as u8);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Extended ID filter config
|
// Extended ID filter config
|
||||||
// Fully managed
|
// Fully managed
|
||||||
regs.xidfc().modify(|v| {
|
regs.xidfc().modify(|v| {
|
||||||
v.set_flesa(xid_sa as u16);
|
v.set_flesa(sizing.extended_id.offset as u16);
|
||||||
v.set_lse(xid_en as u8);
|
v.set_lse(sizing.extended_id.num_elements as u8);
|
||||||
});
|
});
|
||||||
|
|
||||||
// RX FIFO 0 config
|
// RX FIFO 0 config
|
||||||
regs.rxfc(0).modify(|v| {
|
regs.rxfc(0).modify(|v| {
|
||||||
// F0OM - RX FIFO Operating Mode
|
// F0OM - RX FIFO Operating Mode
|
||||||
// F0WM - RX FIFO Water Mark
|
// F0WM - RX FIFO Water Mark
|
||||||
v.set_fsa(rx0_sa as u16);
|
v.set_fsa(sizing.rx_fifo_0.offset as u16);
|
||||||
v.set_fs(rx0_en as u8);
|
v.set_fs(sizing.rx_fifo_0.num_elements as u8);
|
||||||
});
|
});
|
||||||
|
|
||||||
// RX FIFO 1 config
|
// RX FIFO 1 config
|
||||||
regs.rxfc(1).modify(|v| {
|
regs.rxfc(1).modify(|v| {
|
||||||
// F1OM - RX FIFO Operating Mode
|
// F1OM - RX FIFO Operating Mode
|
||||||
// F1WM - RX FIFO Water Mark
|
// F1WM - RX FIFO Water Mark
|
||||||
v.set_fsa(rx1_sa as u16);
|
v.set_fsa(sizing.rx_fifo_1.offset as u16);
|
||||||
v.set_fs(rx1_en as u8);
|
v.set_fs(sizing.rx_fifo_1.num_elements as u8);
|
||||||
});
|
});
|
||||||
|
|
||||||
// RX buffer config
|
// RX buffer config
|
||||||
// Fully managed
|
// Fully managed
|
||||||
regs.rxbc().modify(|v| {
|
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
|
// Rx buffer / queue element size config
|
||||||
@ -602,8 +397,8 @@ impl MessageRamConfig {
|
|||||||
// TX event FIFO config
|
// TX event FIFO config
|
||||||
regs.txefc().modify(|v| {
|
regs.txefc().modify(|v| {
|
||||||
// EFWM - Event FIFO Water Mark
|
// EFWM - Event FIFO Water Mark
|
||||||
v.set_efsa(txe_sa as u16);
|
v.set_efsa(sizing.tx_event.offset as u16);
|
||||||
v.set_efs(txe_en as u8);
|
v.set_efs(sizing.tx_event.num_elements as u8);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Tx buffer / queue element size config
|
// Tx buffer / queue element size config
|
||||||
@ -615,57 +410,25 @@ impl MessageRamConfig {
|
|||||||
// TX queue configuration
|
// TX queue configuration
|
||||||
// Fully managed
|
// Fully managed
|
||||||
regs.txbc().modify(|v| {
|
regs.txbc().modify(|v| {
|
||||||
v.set_tfqm(self.tx.queue_operation_mode.reg_value());
|
//v.set_tfqm(self.tx.queue_operation_mode.reg_value());
|
||||||
v.set_tbsa(txq_sa as u16);
|
v.set_tbsa(sizing.tx_elements.offset as u16);
|
||||||
v.set_ndtb(txd_en as u8);
|
v.set_ndtb(sizing.dedicated_len as u8);
|
||||||
v.set_tfqs(txq_en as u8);
|
v.set_tfqs((sizing.tx_elements.num_elements - sizing.dedicated_len) as u8);
|
||||||
});
|
});
|
||||||
|
|
||||||
// TT Trigger memory config
|
// TT Trigger memory config
|
||||||
// Fully managed
|
// Fully managed
|
||||||
regs.tttmc().modify(|v| {
|
regs.tttmc().modify(|v| {
|
||||||
v.set_tmsa(tmc_sa as u16);
|
v.set_tmsa(sizing.trigger.offset as u16);
|
||||||
v.set_tme(tmc_en as u8);
|
v.set_tme(sizing.trigger.num_elements as u8);
|
||||||
});
|
});
|
||||||
|
|
||||||
let ram_ptr = ram.as_ptr() as *mut RW<u32>;
|
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 {
|
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 {
|
unsafe { sizing.make(ram_ptr) }
|
||||||
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),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@ use core::convert::Infallible;
|
|||||||
use cfg_if::cfg_if;
|
use cfg_if::cfg_if;
|
||||||
use message_ram::{HeaderElement, RxFifoElementHeader};
|
use message_ram::{HeaderElement, RxFifoElementHeader};
|
||||||
use stm32_metapac::can::regs::{Ndat1, Ndat2, Txbcr};
|
use stm32_metapac::can::regs::{Ndat1, Ndat2, Txbcr};
|
||||||
|
use util::{RxElementData, TxElementData};
|
||||||
|
|
||||||
use crate::can::{
|
use crate::can::{
|
||||||
enums::BusError,
|
enums::BusError,
|
||||||
@ -15,8 +16,6 @@ mod filter;
|
|||||||
pub(crate) mod message_ram;
|
pub(crate) mod message_ram;
|
||||||
mod util;
|
mod util;
|
||||||
|
|
||||||
use util::{extract_frame, put_tx_data, put_tx_header};
|
|
||||||
|
|
||||||
/// Loopback Mode
|
/// Loopback Mode
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
pub(crate) enum LoopbackMode {
|
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
|
/// Tx
|
||||||
impl CanLowLevel {
|
impl CanLowLevel {
|
||||||
fn tx_element_set(&self, idx: u8, header: &Header, data: &[u8]) {
|
fn tx_element_set(&self, idx: u8, header: &Header, data: &[u8]) {
|
||||||
let element = self.message_ram.tx_elements.get_mut(idx as usize);
|
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]) {
|
fn tx_element_get(&self, idx: u8) -> (Header, [u8; 64]) {
|
||||||
@ -181,16 +166,7 @@ impl CanLowLevel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl CanLowLevel {
|
impl CanLowLevel {
|
||||||
pub fn rx_element_get(&self, element: &HeaderElement<RxFifoElementHeader>) -> Option<(Header, u16, [u8; 64])> {
|
pub fn rx_buffer_read(&self, buffer_idx: u8) -> Option<RxElementData> {
|
||||||
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])> {
|
|
||||||
let bit_idx = buffer_idx & 0b11111;
|
let bit_idx = buffer_idx & 0b11111;
|
||||||
let bit = 1 << bit_idx;
|
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 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 {
|
match buffer_idx {
|
||||||
idx if idx < 32 => self.regs.ndat1().write_value(Ndat1(bit)),
|
idx if idx < 32 => self.regs.ndat1().write_value(Ndat1(bit)),
|
||||||
@ -214,10 +190,10 @@ impl CanLowLevel {
|
|||||||
_ => panic!(),
|
_ => 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 status = self.regs.rxfs(fifo_num as usize).read();
|
||||||
|
|
||||||
let fill_level = status.ffl();
|
let fill_level = status.ffl();
|
||||||
@ -227,11 +203,11 @@ impl CanLowLevel {
|
|||||||
|
|
||||||
let get_index = self.regs.rxfs(fifo_num as usize).read().fgi();
|
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 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));
|
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 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 {
|
fn make_id(id: u32, extended: bool) -> embedded_can::Id {
|
||||||
if extended {
|
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) {
|
pub(crate) struct TxElementData {
|
||||||
let (id, id_type) = match header.id() {
|
pub header: Header,
|
||||||
// A standard identifier has to be written to ID[28:18].
|
pub data: [u8; 64],
|
||||||
embedded_can::Id::Standard(id) => ((id.as_raw() as u32) << 18, IdType::StandardId),
|
pub marker: u8,
|
||||||
embedded_can::Id::Extended(id) => (id.as_raw() as u32, IdType::ExtendedId),
|
pub tx_event: bool,
|
||||||
};
|
|
||||||
|
|
||||||
// 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) fn put_tx_data(mailbox_data: &mut [RW<u32>], buffer: &[u8]) {
|
impl TxElementData {
|
||||||
let mut lbuffer = [0_u32; 16];
|
//#[inline]
|
||||||
let len = buffer.len();
|
//pub(crate) fn extract(element: &HeaderElement<TxBufferElementHeader>) -> Self {
|
||||||
let data = unsafe { slice::from_raw_parts_mut(lbuffer.as_mut_ptr() as *mut u8, len) };
|
// todo!()
|
||||||
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()) {
|
#[inline]
|
||||||
unsafe { register.write(*byte) };
|
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) {
|
pub(crate) struct RxElementData {
|
||||||
for (i, register) in mailbox_data.iter().enumerate() {
|
pub header: Header,
|
||||||
let register_value = register.read();
|
pub data: [u8; 64],
|
||||||
let register_bytes = unsafe { slice::from_raw_parts(®ister_value as *const u32 as *const u8, 4) };
|
/// If `dlc` is above what can be represented in the configured element,
|
||||||
let num_bytes = (len) - i * 4;
|
/// data can be clipped.
|
||||||
if num_bytes <= 4 {
|
/// This indicates what the real data word size (4 byte words) was.
|
||||||
buffer[i * 4..i * 4 + num_bytes].copy_from_slice(®ister_bytes[..num_bytes]);
|
pub data_words: u8,
|
||||||
break;
|
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
|
//! 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 configurator::Properties;
|
||||||
use embassy_sync::waitqueue::AtomicWaker;
|
use embassy_sync::waitqueue::AtomicWaker;
|
||||||
@ -36,6 +41,12 @@ pub struct Can<'d, M> {
|
|||||||
properties: Properties,
|
properties: Properties,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
pub enum RxQueue {
|
||||||
|
Q0 = 0,
|
||||||
|
Q1 = 1,
|
||||||
|
}
|
||||||
|
|
||||||
impl<'d, M: CanMode> Can<'d, M> {
|
impl<'d, M: CanMode> Can<'d, M> {
|
||||||
/// Get driver properties
|
/// Get driver properties
|
||||||
pub fn properties(&self) -> &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>> {
|
fn internal_try_read(&self) -> Option<Result<BaseEnvelope<M>, BusError>> {
|
||||||
if let Some((header, ts, data)) = self.info.low.rx_fifo_read(0) {
|
if let Some(element) = self.info.low.rx_fifo_read(0) {
|
||||||
let ts = self.info.calc_timestamp(self.state.ns_per_timer_tick, ts);
|
let ts = self
|
||||||
let data = &data[0..header.len() as usize];
|
.info
|
||||||
let frame = match BaseFrame::from_header(header, data) {
|
.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,
|
Ok(frame) => frame,
|
||||||
Err(_) => panic!(),
|
Err(_) => panic!(),
|
||||||
};
|
};
|
||||||
@ -102,57 +115,110 @@ impl<'d, M: CanMode> Can<'d, M> {
|
|||||||
//self.state.rx_mode.try_read(self.info, self.state)
|
//self.state.rx_mode.try_read(self.info, self.state)
|
||||||
self.internal_try_read()
|
self.internal_try_read()
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Queues the message to be sent but exerts backpressure. If a lower-priority
|
impl<'d, M: CanMode> Can<'d, M> {
|
||||||
/// frame is dropped from the mailbox, it is returned. If no lower-priority frames
|
/// Queues the message to be sent but exerts backpressure.
|
||||||
/// can be replaced, this call asynchronously waits for a frame to be successfully
|
/// If the given queue is full, this call will wait asynchronously
|
||||||
/// transmitted, then tries again.
|
/// until it can be added to the queue.
|
||||||
async fn internal_write(&self, frame: &BaseFrame<M>) -> Option<BaseFrame<M>> {
|
/// 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| {
|
poll_fn(|cx| {
|
||||||
self.state.tx_waker.register(cx.waker());
|
self.state.tx_waker.register(cx.waker());
|
||||||
|
|
||||||
match self.info.low.tx_fifo_add(frame.header(), frame.data()) {
|
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,
|
None => Poll::Pending,
|
||||||
}
|
}
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//if !info.low.bad_write(frame) {
|
// == TX
|
||||||
// return Poll::Ready(*frame);
|
// Buffer add req -> TXBAR
|
||||||
//}
|
// Buffer pending, TXBRP AS, TXBTO DE, TXBCF DE
|
||||||
//if let Ok(dropped) = info.low.bad_write(frame) {
|
//
|
||||||
// return Poll::Ready(dropped);
|
// 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
|
/// Reference to a frame which is in the process of being transmitted.
|
||||||
// to clear.
|
pub struct TxRef {
|
||||||
//Poll::Pending
|
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
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Queues the message to be sent but exerts backpressure. If a lower-priority
|
/// Polls the transmission, returns true if it is finished.
|
||||||
/// frame is dropped from the mailbox, it is returned. If no lower-priority frames
|
pub fn poll_done(&mut self) -> bool {
|
||||||
/// can be replaced, this call asynchronously waits for a frame to be successfully
|
// If there is a result stored in self, we are finished.
|
||||||
/// transmitted, then tries again.
|
if self.completed {
|
||||||
pub async fn write(&mut self, frame: &BaseFrame<M>) -> Option<BaseFrame<M>> {
|
return true;
|
||||||
self.internal_write(frame).await
|
}
|
||||||
|
|
||||||
|
// 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
|
/// Begins cancellation of the transmission.
|
||||||
/// if one was removed from the mailbox.
|
/// Cancellation may take some time, and may race with
|
||||||
pub async fn write_blocking(&mut self, frame: &BaseFrame<M>) {
|
/// successful transmission.
|
||||||
let mut frame_opt = self.internal_write(frame).await;
|
/// You can call `wait` to wait for transmission completion
|
||||||
while let Some(frame) = frame_opt {
|
/// or call `poll` to check the state at any point.
|
||||||
frame_opt = self.internal_write(&frame).await;
|
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(self) struct State {
|
||||||
|
pub info: &'static Info,
|
||||||
|
|
||||||
pub rx_waker: AtomicWaker,
|
pub rx_waker: AtomicWaker,
|
||||||
pub tx_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,
|
pub err_waker: AtomicWaker,
|
||||||
|
|
||||||
@ -160,11 +226,50 @@ pub(self) struct State {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl State {
|
impl State {
|
||||||
const fn new() -> Self {
|
const fn new(info: &'static Info) -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
info,
|
||||||
|
|
||||||
rx_waker: AtomicWaker::new(),
|
rx_waker: AtomicWaker::new(),
|
||||||
tx_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(),
|
err_waker: AtomicWaker::new(),
|
||||||
ns_per_timer_tick: 0,
|
ns_per_timer_tick: 0,
|
||||||
}
|
}
|
||||||
|
@ -64,52 +64,55 @@ macro_rules! impl_fdcan {
|
|||||||
($inst:ident,
|
($inst:ident,
|
||||||
//$irq0:ident, $irq1:ident,
|
//$irq0:ident, $irq1:ident,
|
||||||
$msg_ram_inst:ident, $msg_ram_offset:literal, $msg_ram_size:literal) => {
|
$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)]
|
#[allow(non_snake_case)]
|
||||||
pub(crate) mod $inst {
|
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!(
|
foreach_interrupt!(
|
||||||
($inst,can,FDCAN,IT0,$irq:ident) => {
|
($inst,can,FDCAN,IT0,$irq:ident) => {
|
||||||
@ -119,10 +122,11 @@ macro_rules! impl_fdcan {
|
|||||||
pub type Interrupt1 = crate::interrupt::typelevel::$irq;
|
pub type Interrupt1 = crate::interrupt::typelevel::$irq;
|
||||||
};
|
};
|
||||||
);
|
);
|
||||||
}
|
|
||||||
impl Instance for peripherals::$inst {
|
impl Instance for peripherals::$inst {
|
||||||
type IT0Interrupt = $inst::Interrupt0;
|
type IT0Interrupt = $inst::Interrupt0;
|
||||||
type IT1Interrupt = $inst::Interrupt1;
|
type IT1Interrupt = $inst::Interrupt1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -3,11 +3,11 @@ use super::Info;
|
|||||||
pub fn calc_ns_per_timer_tick(
|
pub fn calc_ns_per_timer_tick(
|
||||||
info: &'static Info,
|
info: &'static Info,
|
||||||
freq: crate::time::Hertz,
|
freq: crate::time::Hertz,
|
||||||
mode: crate::can::fd::config::FrameTransmissionConfig,
|
mode: crate::can::fd::config::CanFdMode,
|
||||||
) -> u64 {
|
) -> u64 {
|
||||||
match mode {
|
match mode {
|
||||||
// Use timestamp from Rx FIFO to adjust timestamp reported to user
|
// 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
|
let prescale: u64 = ({ info.low.regs.nbtp().read().nbrp() } + 1) as u64
|
||||||
* ({ info.low.regs.tscc().read().tcp() } + 1) as u64;
|
* ({ info.low.regs.tscc().read().tcp() } + 1) as u64;
|
||||||
1_000_000_000 as u64 / (freq.0 as u64 * prescale)
|
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 self::fd::{config, filter};
|
||||||
pub use super::common::{BufferedCanReceiver, BufferedCanSender};
|
pub use super::common::{BufferedCanReceiver, BufferedCanSender};
|
||||||
pub use fd::configurator::CanConfigurator;
|
pub use fd::configurator::CanConfigurator;
|
||||||
|
pub use fd::interrupt::{IT0InterruptHandler, IT1InterruptHandler};
|
||||||
pub use fd::peripheral::*;
|
pub use fd::peripheral::*;
|
||||||
pub use fd::Can;
|
pub use fd::Can;
|
||||||
|
|
||||||
|
@ -38,9 +38,17 @@ impl defmt::Format for Header {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Header {
|
impl Header {
|
||||||
const FLAG_RTR: usize = 0; // Remote
|
/// RTR (remote transmit request) flag in CAN frame
|
||||||
const FLAG_FDCAN: usize = 1; // FDCan vs Classic CAN
|
const FLAG_RTR: usize = 0;
|
||||||
const FLAG_BRS: usize = 2; // Bit-rate switching, ignored for Classic CAN
|
/// 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
|
/// Create new CAN Header
|
||||||
pub fn new(id: embedded_can::Id, len: u8, rtr: bool) -> Header {
|
pub fn new(id: embedded_can::Id, len: u8, rtr: bool) -> Header {
|
||||||
@ -49,13 +57,15 @@ impl Header {
|
|||||||
Header { id, len, flags }
|
Header { id, len, flags }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create new CAN FD Header
|
pub fn set_can_fd(mut self, flag: bool, brs: bool) -> Header {
|
||||||
pub fn new_fd(id: embedded_can::Id, len: u8, rtr: bool, brs: bool) -> Header {
|
self.flags.set_bit(Self::FLAG_FDCAN, flag);
|
||||||
let mut flags = 0u8;
|
self.flags.set_bit(Self::FLAG_BRS, brs);
|
||||||
flags.set_bit(Self::FLAG_RTR, rtr);
|
self
|
||||||
flags.set_bit(Self::FLAG_FDCAN, true);
|
}
|
||||||
flags.set_bit(Self::FLAG_BRS, brs);
|
|
||||||
Header { id, len, flags }
|
pub fn set_esi(mut self, flag: bool) -> Header {
|
||||||
|
self.flags.set_bit(Self::FLAG_ESI, flag);
|
||||||
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return ID
|
/// Return ID
|
||||||
@ -73,6 +83,10 @@ impl Header {
|
|||||||
self.flags.get_bit(Self::FLAG_RTR)
|
self.flags.get_bit(Self::FLAG_RTR)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn esi(&self) -> bool {
|
||||||
|
self.flags.get_bit(Self::FLAG_ESI)
|
||||||
|
}
|
||||||
|
|
||||||
/// Request/is FDCAN frame
|
/// Request/is FDCAN frame
|
||||||
pub fn fdcan(&self) -> bool {
|
pub fn fdcan(&self) -> bool {
|
||||||
self.flags.get_bit(Self::FLAG_FDCAN)
|
self.flags.get_bit(Self::FLAG_FDCAN)
|
||||||
|
Loading…
Reference in New Issue
Block a user