diff --git a/embassy-stm32/src/can/fdcan.rs b/embassy-stm32/src/can/fdcan.rs index 0cc2559cf..faf4af73f 100644 --- a/embassy-stm32/src/can/fdcan.rs +++ b/embassy-stm32/src/can/fdcan.rs @@ -1,14 +1,577 @@ -use crate::peripherals; +use core::future::poll_fn; +use core::marker::PhantomData; +use core::ops::{Deref, DerefMut}; +use core::task::Poll; + +use cfg_if::cfg_if; +use embassy_hal_internal::{into_ref, PeripheralRef}; +pub use fdcan::frame::{FrameFormat, RxFrameInfo, TxFrameHeader}; +pub use fdcan::id::{ExtendedId, Id, StandardId}; +use fdcan::message_ram::RegisterBlock; +use fdcan::{self, LastErrorCode}; +pub use fdcan::{config, filter}; + +use crate::gpio::sealed::AFType; +use crate::interrupt::typelevel::Interrupt; +use crate::rcc::RccPeripheral; +use crate::{interrupt, peripherals, Peripheral}; + +pub mod enums; +use enums::*; +pub mod util; + +/// CAN Frame returned by read +pub struct RxFrame { + /// CAN Header info: frame ID, data length and other meta + pub header: RxFrameInfo, + /// CAN(0-8 bytes) or FDCAN(0-64 bytes) Frame data + pub data: Data, + /// Reception time. + #[cfg(feature = "time")] + pub timestamp: embassy_time::Instant, +} + +/// CAN frame used for write +pub struct TxFrame { + /// CAN Header info: frame ID, data length and other meta + pub header: TxFrameHeader, + /// CAN(0-8 bytes) or FDCAN(0-64 bytes) Frame data + pub data: Data, +} + +impl TxFrame { + /// Create new TX frame from header and data + pub fn new(header: TxFrameHeader, data: &[u8]) -> Option<Self> { + if data.len() < header.len as usize { + return None; + } + + let Some(data) = Data::new(data) else { return None }; + + Some(TxFrame { header, data }) + } + + fn from_preserved(header: TxFrameHeader, data32: &[u32]) -> Option<Self> { + let mut data = [0u8; 64]; + + for i in 0..data32.len() { + data[4 * i..][..4].copy_from_slice(&data32[i].to_le_bytes()); + } + + let Some(data) = Data::new(&data) else { return None }; + + Some(TxFrame { header, data }) + } + + /// Access frame data. Slice length will match header. + pub fn data(&self) -> &[u8] { + &self.data.bytes[..(self.header.len as usize)] + } +} + +impl RxFrame { + pub(crate) fn new( + header: RxFrameInfo, + data: &[u8], + #[cfg(feature = "time")] timestamp: embassy_time::Instant, + ) -> Self { + let data = Data::new(&data).unwrap_or_else(|| Data::empty()); + + RxFrame { + header, + data, + #[cfg(feature = "time")] + timestamp, + } + } + + /// Access frame data. Slice length will match header. + pub fn data(&self) -> &[u8] { + &self.data.bytes[..(self.header.len as usize)] + } +} + +/// Payload of a (FD)CAN data frame. +/// +/// Contains 0 to 64 Bytes of data. +#[derive(Debug, Copy, Clone)] +pub struct Data { + pub(crate) bytes: [u8; 64], +} + +impl Data { + /// Creates a data payload from a raw byte slice. + /// + /// Returns `None` if `data` is more than 64 bytes (which is the maximum) or + /// cannot be represented with an FDCAN DLC. + pub fn new(data: &[u8]) -> Option<Self> { + if !Data::is_valid_len(data.len()) { + return None; + } + + let mut bytes = [0; 64]; + bytes[..data.len()].copy_from_slice(data); + + Some(Self { bytes }) + } + + /// Raw read access to data. + pub fn raw(&self) -> &[u8] { + &self.bytes + } + + /// Checks if the length can be encoded in FDCAN DLC field. + pub const fn is_valid_len(len: usize) -> bool { + match len { + 0..=8 => true, + 12 => true, + 16 => true, + 20 => true, + 24 => true, + 32 => true, + 48 => true, + 64 => true, + _ => false, + } + } + + /// Creates an empty data payload containing 0 bytes. + #[inline] + pub const fn empty() -> Self { + Self { bytes: [0; 64] } + } +} + +/// Interrupt handler channel 0. +pub struct IT0InterruptHandler<T: Instance> { + _phantom: PhantomData<T>, +} + +// We use IT0 for everything currently +impl<T: Instance> interrupt::typelevel::Handler<T::IT0Interrupt> for IT0InterruptHandler<T> { + unsafe fn on_interrupt() { + let regs = T::regs(); + + let ir = regs.ir().read(); + + if ir.tc() { + regs.ir().write(|w| w.set_tc(true)); + T::state().tx_waker.wake(); + } + + if ir.tefn() { + regs.ir().write(|w| w.set_tefn(true)); + T::state().tx_waker.wake(); + } + + if ir.ped() || ir.pea() { + regs.ir().write(|w| { + w.set_ped(true); + w.set_pea(true); + }); + } + + if ir.rfn(0) { + regs.ir().write(|w| w.set_rfn(0, true)); + T::state().rx_waker.wake(); + } + + if ir.rfn(1) { + regs.ir().write(|w| w.set_rfn(1, true)); + T::state().rx_waker.wake(); + } + } +} + +/// Interrupt handler channel 1. +pub struct IT1InterruptHandler<T: Instance> { + _phantom: PhantomData<T>, +} + +impl<T: Instance> interrupt::typelevel::Handler<T::IT1Interrupt> for IT1InterruptHandler<T> { + unsafe fn on_interrupt() {} +} + +impl BusError { + fn try_from(lec: LastErrorCode) -> Option<BusError> { + match lec { + LastErrorCode::AckError => Some(BusError::Acknowledge), + // `0` data bit encodes a dominant state. `1` data bit is recessive. + // Bit0Error: During transmit, the node wanted to send a 0 but monitored a 1 + LastErrorCode::Bit0Error => Some(BusError::BitRecessive), + LastErrorCode::Bit1Error => Some(BusError::BitDominant), + LastErrorCode::CRCError => Some(BusError::Crc), + LastErrorCode::FormError => Some(BusError::Form), + LastErrorCode::StuffError => Some(BusError::Stuff), + _ => None, + } + } +} + +/// Operating modes trait +pub trait FdcanOperatingMode {} +impl FdcanOperatingMode for fdcan::PoweredDownMode {} +impl FdcanOperatingMode for fdcan::ConfigMode {} +impl FdcanOperatingMode for fdcan::InternalLoopbackMode {} +impl FdcanOperatingMode for fdcan::ExternalLoopbackMode {} +impl FdcanOperatingMode for fdcan::NormalOperationMode {} +impl FdcanOperatingMode for fdcan::RestrictedOperationMode {} +impl FdcanOperatingMode for fdcan::BusMonitoringMode {} +impl FdcanOperatingMode for fdcan::TestMode {} + +/// FDCAN Instance +pub struct Fdcan<'d, T: Instance, M: FdcanOperatingMode> { + /// Reference to internals. + pub can: fdcan::FdCan<FdcanInstance<'d, T>, M>, + ns_per_timer_tick: u64, // For FDCAN internal timer +} + +fn calc_ns_per_timer_tick<T: Instance>(mode: config::FrameTransmissionConfig) -> u64 { + match mode { + // Use timestamp from Rx FIFO to adjust timestamp reported to user + config::FrameTransmissionConfig::ClassicCanOnly => { + let freq = T::frequency(); + let prescale: u64 = + ({ T::regs().nbtp().read().nbrp() } + 1) as u64 * ({ T::regs().tscc().read().tcp() } + 1) as u64; + 1_000_000_000 as u64 / (freq.0 as u64 * prescale) + } + // For VBR this is too hard because the FDCAN timer switches clock rate you need to configure to use + // timer3 instead which is too hard to do from this module. + _ => 0, + } +} + +#[cfg(feature = "time")] +fn calc_timestamp<T: Instance>(ns_per_timer_tick: u64, ts_val: u16) -> embassy_time::Instant { + let now_embassy = embassy_time::Instant::now(); + if ns_per_timer_tick == 0 { + return now_embassy; + } + let now_can = { T::regs().tscv().read().tsc() }; + let delta = now_can.overflowing_sub(ts_val).0 as u64; + let ns = ns_per_timer_tick * delta as u64; + now_embassy - embassy_time::Duration::from_nanos(ns) +} + +fn curr_error<T: Instance>() -> Option<BusError> { + let err = { T::regs().psr().read() }; + if err.bo() { + return Some(BusError::BusOff); + } else if err.ep() { + return Some(BusError::BusPassive); + } else if err.ew() { + return Some(BusError::BusWarning); + } else { + cfg_if! { + if #[cfg(stm32h7)] { + let lec = err.lec(); + } else { + let lec = err.lec().to_bits(); + } + } + if let Ok(err) = LastErrorCode::try_from(lec) { + return BusError::try_from(err); + } + } + None +} + +impl<'d, T: Instance> Fdcan<'d, T, fdcan::ConfigMode> { + /// Creates a new Fdcan instance, keeping the peripheral in sleep mode. + /// You must call [Fdcan::enable_non_blocking] to use the peripheral. + pub fn new( + peri: impl Peripheral<P = T> + 'd, + rx: impl Peripheral<P = impl RxPin<T>> + 'd, + tx: impl Peripheral<P = impl TxPin<T>> + 'd, + _irqs: impl interrupt::typelevel::Binding<T::IT0Interrupt, IT0InterruptHandler<T>> + + interrupt::typelevel::Binding<T::IT1Interrupt, IT1InterruptHandler<T>> + + 'd, + ) -> Fdcan<'d, T, fdcan::ConfigMode> { + into_ref!(peri, rx, tx); + + rx.set_as_af(rx.af_num(), AFType::Input); + tx.set_as_af(tx.af_num(), AFType::OutputPushPull); + + T::enable_and_reset(); + + rx.set_as_af(rx.af_num(), AFType::Input); + tx.set_as_af(tx.af_num(), AFType::OutputPushPull); + + let mut can = fdcan::FdCan::new(FdcanInstance(peri)).into_config_mode(); + + T::configure_msg_ram(); + unsafe { + // Enable timestamping + #[cfg(not(stm32h7))] + T::regs() + .tscc() + .write(|w| w.set_tss(stm32_metapac::can::vals::Tss::INCREMENT)); + #[cfg(stm32h7)] + T::regs().tscc().write(|w| w.set_tss(0x01)); + + T::IT0Interrupt::unpend(); // Not unsafe + T::IT0Interrupt::enable(); + + T::IT1Interrupt::unpend(); // Not unsafe + T::IT1Interrupt::enable(); + + // this isn't really documented in the reference manual + // but corresponding txbtie bit has to be set for the TC (TxComplete) interrupt to fire + T::regs().txbtie().write(|w| w.0 = 0xffff_ffff); + } + + can.enable_interrupt(fdcan::interrupt::Interrupt::RxFifo0NewMsg); + can.enable_interrupt(fdcan::interrupt::Interrupt::RxFifo1NewMsg); + can.enable_interrupt(fdcan::interrupt::Interrupt::TxComplete); + can.enable_interrupt_line(fdcan::interrupt::InterruptLine::_0, true); + can.enable_interrupt_line(fdcan::interrupt::InterruptLine::_1, true); + + let ns_per_timer_tick = calc_ns_per_timer_tick::<T>(can.get_config().frame_transmit); + Self { can, ns_per_timer_tick } + } + + /// Configures the bit timings calculated from supplied bitrate. + pub fn set_bitrate(&mut self, bitrate: u32) { + let bit_timing = util::calc_can_timings(T::frequency(), bitrate).unwrap(); + self.can.set_nominal_bit_timing(config::NominalBitTiming { + sync_jump_width: bit_timing.sync_jump_width, + prescaler: bit_timing.prescaler, + seg1: bit_timing.seg1, + seg2: bit_timing.seg2, + }); + } +} + +macro_rules! impl_transition { + ($from_mode:ident, $to_mode:ident, $name:ident, $func: ident) => { + impl<'d, T: Instance> Fdcan<'d, T, fdcan::$from_mode> { + /// Transition from $from_mode:ident mode to $to_mode:ident mode + pub fn $name(self) -> Fdcan<'d, T, fdcan::$to_mode> { + let ns_per_timer_tick = calc_ns_per_timer_tick::<T>(self.can.get_config().frame_transmit); + Fdcan { + can: self.can.$func(), + ns_per_timer_tick, + } + } + } + }; +} + +impl_transition!(PoweredDownMode, ConfigMode, into_config_mode, into_config_mode); +impl_transition!(InternalLoopbackMode, ConfigMode, into_config_mode, into_config_mode); + +impl_transition!(ConfigMode, NormalOperationMode, into_normal_mode, into_normal); +impl_transition!( + ConfigMode, + ExternalLoopbackMode, + into_external_loopback_mode, + into_external_loopback +); +impl_transition!( + ConfigMode, + InternalLoopbackMode, + into_internal_loopback_mode, + into_internal_loopback +); + +impl<'d, T: Instance, M: FdcanOperatingMode> Fdcan<'d, T, M> +where + M: fdcan::Transmit, + M: fdcan::Receive, +{ + /// 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: &TxFrame) -> Option<TxFrame> { + poll_fn(|cx| { + T::state().tx_waker.register(cx.waker()); + if let Ok(dropped) = self + .can + .transmit_preserve(frame.header, &frame.data.bytes, &mut |_, hdr, data32| { + TxFrame::from_preserved(hdr, data32) + }) + { + return Poll::Ready(dropped.flatten()); + } + + // Couldn't replace any lower priority frames. Need to wait for some mailboxes + // to clear. + Poll::Pending + }) + .await + } + + /// Flush one of the TX mailboxes. + pub async fn flush(&self, mb: fdcan::Mailbox) { + poll_fn(|cx| { + T::state().tx_waker.register(cx.waker()); + + let idx: u8 = mb.into(); + let idx = 1 << idx; + if !T::regs().txbrp().read().trp(idx) { + return Poll::Ready(()); + } + + Poll::Pending + }) + .await; + } + + /// Returns the next received message frame + pub async fn read(&mut self) -> Result<RxFrame, BusError> { + poll_fn(|cx| { + T::state().err_waker.register(cx.waker()); + T::state().rx_waker.register(cx.waker()); + + let mut buffer: [u8; 64] = [0; 64]; + if let Ok(rx) = self.can.receive0(&mut buffer) { + // rx: fdcan::ReceiveOverrun<RxFrameInfo> + // TODO: report overrun? + // for now we just drop it + + let frame: RxFrame = RxFrame::new( + rx.unwrap(), + &buffer, + #[cfg(feature = "time")] + calc_timestamp::<T>(self.ns_per_timer_tick, rx.unwrap().time_stamp), + ); + return Poll::Ready(Ok(frame)); + } else if let Ok(rx) = self.can.receive1(&mut buffer) { + // rx: fdcan::ReceiveOverrun<RxFrameInfo> + // TODO: report overrun? + // for now we just drop it + + let frame: RxFrame = RxFrame::new( + rx.unwrap(), + &buffer, + #[cfg(feature = "time")] + calc_timestamp::<T>(self.ns_per_timer_tick, rx.unwrap().time_stamp), + ); + return Poll::Ready(Ok(frame)); + } else if let Some(err) = curr_error::<T>() { + // TODO: this is probably wrong + return Poll::Ready(Err(err)); + } + Poll::Pending + }) + .await + } + + /// Split instance into separate Tx(write) and Rx(read) portions + pub fn split<'c>(&'c mut self) -> (FdcanTx<'c, 'd, T, M>, FdcanRx<'c, 'd, T, M>) { + let (mut _control, tx, rx0, rx1) = self.can.split_by_ref(); + ( + FdcanTx { _control, tx }, + FdcanRx { + rx0, + rx1, + ns_per_timer_tick: self.ns_per_timer_tick, + }, + ) + } +} + +/// FDCAN Tx only Instance +pub struct FdcanTx<'c, 'd, T: Instance, M: fdcan::Transmit> { + _control: &'c mut fdcan::FdCanControl<FdcanInstance<'d, T>, M>, + tx: &'c mut fdcan::Tx<FdcanInstance<'d, T>, M>, +} + +impl<'c, 'd, T: Instance, M: fdcan::Transmit> FdcanTx<'c, 'd, T, M> { + /// 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: &TxFrame) -> Option<TxFrame> { + poll_fn(|cx| { + T::state().tx_waker.register(cx.waker()); + if let Ok(dropped) = self + .tx + .transmit_preserve(frame.header, &frame.data.bytes, &mut |_, hdr, data32| { + TxFrame::from_preserved(hdr, data32) + }) + { + return Poll::Ready(dropped.flatten()); + } + + // Couldn't replace any lower priority frames. Need to wait for some mailboxes + // to clear. + Poll::Pending + }) + .await + } +} + +/// FDCAN Rx only Instance +#[allow(dead_code)] +pub struct FdcanRx<'c, 'd, T: Instance, M: fdcan::Receive> { + rx0: &'c mut fdcan::Rx<FdcanInstance<'d, T>, M, fdcan::Fifo0>, + rx1: &'c mut fdcan::Rx<FdcanInstance<'d, T>, M, fdcan::Fifo1>, + ns_per_timer_tick: u64, // For FDCAN internal timer +} + +impl<'c, 'd, T: Instance, M: fdcan::Receive> FdcanRx<'c, 'd, T, M> { + /// Returns the next received message frame + pub async fn read(&mut self) -> Result<RxFrame, BusError> { + poll_fn(|cx| { + T::state().err_waker.register(cx.waker()); + T::state().rx_waker.register(cx.waker()); + + let mut buffer: [u8; 64] = [0; 64]; + if let Ok(rx) = self.rx0.receive(&mut buffer) { + // rx: fdcan::ReceiveOverrun<RxFrameInfo> + // TODO: report overrun? + // for now we just drop it + let frame: RxFrame = RxFrame::new( + rx.unwrap(), + &buffer, + #[cfg(feature = "time")] + calc_timestamp::<T>(self.ns_per_timer_tick, rx.unwrap().time_stamp), + ); + return Poll::Ready(Ok(frame)); + } else if let Ok(rx) = self.rx1.receive(&mut buffer) { + // rx: fdcan::ReceiveOverrun<RxFrameInfo> + // TODO: report overrun? + // for now we just drop it + let frame: RxFrame = RxFrame::new( + rx.unwrap(), + &buffer, + #[cfg(feature = "time")] + calc_timestamp::<T>(self.ns_per_timer_tick, rx.unwrap().time_stamp), + ); + return Poll::Ready(Ok(frame)); + } else if let Some(err) = curr_error::<T>() { + // TODO: this is probably wrong + return Poll::Ready(Err(err)); + } + + Poll::Pending + }) + .await + } +} +impl<'d, T: Instance, M: FdcanOperatingMode> Deref for Fdcan<'d, T, M> { + type Target = fdcan::FdCan<FdcanInstance<'d, T>, M>; + + fn deref(&self) -> &Self::Target { + &self.can + } +} + +impl<'d, T: Instance, M: FdcanOperatingMode> DerefMut for Fdcan<'d, T, M> { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.can + } +} pub(crate) mod sealed { - use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; - use embassy_sync::channel::Channel; use embassy_sync::waitqueue::AtomicWaker; pub struct State { pub tx_waker: AtomicWaker, pub err_waker: AtomicWaker, - pub rx_queue: Channel<CriticalSectionRawMutex, (u16, bxcan::Frame), 32>, + pub rx_waker: AtomicWaker, } impl State { @@ -16,25 +579,122 @@ pub(crate) mod sealed { Self { tx_waker: AtomicWaker::new(), err_waker: AtomicWaker::new(), - rx_queue: Channel::new(), + rx_waker: AtomicWaker::new(), } } } pub trait Instance { + const REGISTERS: *mut fdcan::RegisterBlock; + const MSG_RAM: *mut fdcan::message_ram::RegisterBlock; + const MSG_RAM_OFFSET: usize; + fn regs() -> &'static crate::pac::can::Fdcan; fn state() -> &'static State; + + #[cfg(not(stm32h7))] + fn configure_msg_ram() {} + + #[cfg(stm32h7)] + fn configure_msg_ram() { + let r = Self::regs(); + + use fdcan::message_ram::*; + let mut offset_words = Self::MSG_RAM_OFFSET as u16; + + // 11-bit filter + r.sidfc().modify(|w| w.set_flssa(offset_words)); + offset_words += STANDARD_FILTER_MAX as u16; + + // 29-bit filter + r.xidfc().modify(|w| w.set_flesa(offset_words)); + offset_words += 2 * EXTENDED_FILTER_MAX as u16; + + // Rx FIFO 0 and 1 + for i in 0..=1 { + r.rxfc(i).modify(|w| { + w.set_fsa(offset_words); + w.set_fs(RX_FIFO_MAX); + w.set_fwm(RX_FIFO_MAX); + }); + offset_words += 18 * RX_FIFO_MAX as u16; + } + + // Rx buffer - see below + // Tx event FIFO + r.txefc().modify(|w| { + w.set_efsa(offset_words); + w.set_efs(TX_EVENT_MAX); + w.set_efwm(TX_EVENT_MAX); + }); + offset_words += 2 * TX_EVENT_MAX as u16; + + // Tx buffers + r.txbc().modify(|w| { + w.set_tbsa(offset_words); + w.set_tfqs(TX_FIFO_MAX); + }); + offset_words += 18 * TX_FIFO_MAX as u16; + + // Rx Buffer - not used + r.rxbc().modify(|w| { + w.set_rbsa(offset_words); + }); + + // TX event FIFO? + // Trigger memory? + + // Set the element sizes to 16 bytes + r.rxesc().modify(|w| { + w.set_rbds(0b111); + for i in 0..=1 { + w.set_fds(i, 0b111); + } + }); + r.txesc().modify(|w| { + w.set_tbds(0b111); + }) + } } } -/// Interruptable FDCAN instance. -pub trait InterruptableInstance {} -/// FDCAN instance. -pub trait Instance: sealed::Instance + InterruptableInstance + 'static {} +/// Trait for FDCAN interrupt channel 0 +pub trait IT0Instance { + /// Type for FDCAN interrupt channel 0 + type IT0Interrupt: crate::interrupt::typelevel::Interrupt; +} -foreach_peripheral!( - (can, $inst:ident) => { +/// Trait for FDCAN interrupt channel 1 +pub trait IT1Instance { + /// Type for FDCAN interrupt channel 1 + type IT1Interrupt: crate::interrupt::typelevel::Interrupt; +} + +/// InterruptableInstance trait +pub trait InterruptableInstance: IT0Instance + IT1Instance {} +/// Instance trait +pub trait Instance: sealed::Instance + RccPeripheral + InterruptableInstance + 'static {} +/// Fdcan Instance struct +pub struct FdcanInstance<'a, T>(PeripheralRef<'a, T>); + +unsafe impl<'d, T: Instance> fdcan::message_ram::Instance for FdcanInstance<'d, T> { + const MSG_RAM: *mut RegisterBlock = T::MSG_RAM; +} + +unsafe impl<'d, T: Instance> fdcan::Instance for FdcanInstance<'d, T> +where + FdcanInstance<'d, T>: fdcan::message_ram::Instance, +{ + const REGISTERS: *mut fdcan::RegisterBlock = T::REGISTERS; +} + +macro_rules! impl_fdcan { + ($inst:ident, $msg_ram_inst:ident, $msg_ram_offset:literal) => { impl sealed::Instance for peripherals::$inst { + const REGISTERS: *mut fdcan::RegisterBlock = crate::pac::$inst.as_ptr() as *mut _; + const MSG_RAM: *mut fdcan::message_ram::RegisterBlock = crate::pac::$msg_ram_inst.as_ptr() as *mut _; + const MSG_RAM_OFFSET: usize = $msg_ram_offset; + fn regs() -> &'static crate::pac::can::Fdcan { &crate::pac::$inst } @@ -47,8 +707,40 @@ foreach_peripheral!( impl Instance for peripherals::$inst {} + foreach_interrupt!( + ($inst,can,FDCAN,IT0,$irq:ident) => { + impl IT0Instance for peripherals::$inst { + type IT0Interrupt = crate::interrupt::typelevel::$irq; + } + }; + ($inst,can,FDCAN,IT1,$irq:ident) => { + impl IT1Instance for peripherals::$inst { + type IT1Interrupt = crate::interrupt::typelevel::$irq; + } + }; + ); + impl InterruptableInstance for peripherals::$inst {} }; + + ($inst:ident, $msg_ram_inst:ident) => { + impl_fdcan!($inst, $msg_ram_inst, 0); + }; +} + +#[cfg(not(stm32h7))] +foreach_peripheral!( + (can, FDCAN) => { impl_fdcan!(FDCAN, FDCANRAM); }; + (can, FDCAN1) => { impl_fdcan!(FDCAN1, FDCANRAM1); }; + (can, FDCAN2) => { impl_fdcan!(FDCAN2, FDCANRAM2); }; + (can, FDCAN3) => { impl_fdcan!(FDCAN3, FDCANRAM3); }; +); + +#[cfg(stm32h7)] +foreach_peripheral!( + (can, FDCAN1) => { impl_fdcan!(FDCAN1, FDCANRAM, 0x0000); }; + (can, FDCAN2) => { impl_fdcan!(FDCAN2, FDCANRAM, 0x0C00); }; + (can, FDCAN3) => { impl_fdcan!(FDCAN3, FDCANRAM, 0x1800); }; ); pin_trait!(RxPin, Instance); diff --git a/embassy-stm32/src/rcc/h.rs b/embassy-stm32/src/rcc/h.rs index 85e12637e..dcaf2dced 100644 --- a/embassy-stm32/src/rcc/h.rs +++ b/embassy-stm32/src/rcc/h.rs @@ -6,10 +6,9 @@ use crate::pac::pwr::vals::Vos; pub use crate::pac::rcc::vals::Adcdacsel as AdcClockSource; #[cfg(stm32h7)] pub use crate::pac::rcc::vals::Adcsel as AdcClockSource; -pub use crate::pac::rcc::vals::Fdcansel as FdCanClockSource; pub use crate::pac::rcc::vals::{ - Ckpersel as PerClockSource, Hsidiv as HSIPrescaler, Plldiv as PllDiv, Pllm as PllPreDiv, Plln as PllMul, - Pllsrc as PllSource, Sw as Sysclk, + Ckpersel as PerClockSource, Fdcansel as FdCanClockSource, Hsidiv as HSIPrescaler, Plldiv as PllDiv, + Pllm as PllPreDiv, Plln as PllMul, Pllsrc as PllSource, Sw as Sysclk, }; use crate::pac::rcc::vals::{Ckpersel, Pllrge, Pllvcosel, Timpre}; use crate::pac::{FLASH, PWR, RCC}; @@ -591,7 +590,7 @@ pub(crate) unsafe fn init(config: Config) { RCC.ccipr5().modify(|w| { w.set_ckpersel(config.per_clock_source); w.set_adcdacsel(config.adc_clock_source); - w.set_fdcan1sel(config.fdcan_clock_source) + w.set_fdcan12sel(config.fdcan_clock_source) }); }