use core::cmp::Ordering; use core::convert::Infallible; pub use embedded_can::{ExtendedId, Id, StandardId}; use stm32_metapac::can::vals::Lec; use super::{Mailbox, TransmitStatus}; use crate::can::enums::BusError; use crate::can::frame::{Envelope, Frame, Header}; pub(crate) struct Registers(pub crate::pac::can::Can); impl Registers { pub fn enter_init_mode(&mut self) { self.0.mcr().modify(|reg| { reg.set_sleep(false); reg.set_inrq(true); }); loop { let msr = self.0.msr().read(); if !msr.slak() && msr.inak() { break; } } } // Leaves initialization mode, enters sleep mode. pub fn leave_init_mode(&mut self) { self.0.mcr().modify(|reg| { reg.set_sleep(true); reg.set_inrq(false); }); loop { let msr = self.0.msr().read(); if msr.slak() && !msr.inak() { break; } } } pub fn set_bit_timing(&mut self, bt: crate::can::util::NominalBitTiming) { let prescaler = u16::from(bt.prescaler) & 0x1FF; let seg1 = u8::from(bt.seg1); let seg2 = u8::from(bt.seg2) & 0x7F; let sync_jump_width = u8::from(bt.sync_jump_width) & 0x7F; self.0.btr().modify(|reg| { reg.set_brp(prescaler - 1); reg.set_ts(0, seg1 - 1); reg.set_ts(1, seg2 - 1); reg.set_sjw(sync_jump_width - 1); }); } /// Enables or disables silent mode: Disconnects the TX signal from the pin. pub fn set_silent(&self, enabled: bool) { let mode = match enabled { false => stm32_metapac::can::vals::Silm::NORMAL, true => stm32_metapac::can::vals::Silm::SILENT, }; self.0.btr().modify(|reg| reg.set_silm(mode)); } /// Enables or disables automatic retransmission of messages. /// /// If this is enabled, the CAN peripheral will automatically try to retransmit each frame /// until it can be sent. Otherwise, it will try only once to send each frame. /// /// Automatic retransmission is enabled by default. pub fn set_automatic_retransmit(&self, enabled: bool) { self.0.mcr().modify(|reg| reg.set_nart(enabled)); } /// Enables or disables loopback mode: Internally connects the TX and RX /// signals together. pub fn set_loopback(&self, enabled: bool) { self.0.btr().modify(|reg| reg.set_lbkm(enabled)); } /// Configures the automatic wake-up feature. /// /// This is turned off by default. /// /// When turned on, an incoming frame will cause the peripheral to wake up from sleep and /// receive the frame. If enabled, [`Interrupt::Wakeup`] will also be triggered by the incoming /// frame. #[allow(dead_code)] pub fn set_automatic_wakeup(&mut self, enabled: bool) { self.0.mcr().modify(|reg| reg.set_awum(enabled)); } /// Leaves initialization mode and enables the peripheral (non-blocking version). /// /// Usually, it is recommended to call [`CanConfig::enable`] instead. This method is only needed /// if you want non-blocking initialization. /// /// If this returns [`WouldBlock`][nb::Error::WouldBlock], the peripheral will enable itself /// in the background. The peripheral is enabled and ready to use when this method returns /// successfully. pub fn enable_non_blocking(&mut self) -> nb::Result<(), Infallible> { let msr = self.0.msr().read(); if msr.slak() { self.0.mcr().modify(|reg| { reg.set_abom(true); reg.set_sleep(false); }); Err(nb::Error::WouldBlock) } else { Ok(()) } } /// Puts the peripheral in a sleep mode to save power. /// /// While in sleep mode, an incoming CAN frame will trigger [`Interrupt::Wakeup`] if enabled. #[allow(dead_code)] pub fn sleep(&mut self) { self.0.mcr().modify(|reg| { reg.set_sleep(true); reg.set_inrq(false); }); loop { let msr = self.0.msr().read(); if msr.slak() && !msr.inak() { break; } } } /// Wakes up from sleep mode. /// /// Note that this will not trigger [`Interrupt::Wakeup`], only reception of an incoming CAN /// frame will cause that interrupt. #[allow(dead_code)] pub fn wakeup(&mut self) { self.0.mcr().modify(|reg| { reg.set_sleep(false); reg.set_inrq(false); }); loop { let msr = self.0.msr().read(); if !msr.slak() && !msr.inak() { break; } } } pub fn curr_error(&self) -> Option<BusError> { let err = { self.0.esr().read() }; if err.boff() { return Some(BusError::BusOff); } else if err.epvf() { return Some(BusError::BusPassive); } else if err.ewgf() { return Some(BusError::BusWarning); } else if err.lec() != Lec::NOERROR { return Some(match err.lec() { Lec::STUFF => BusError::Stuff, Lec::FORM => BusError::Form, Lec::ACK => BusError::Acknowledge, Lec::BITRECESSIVE => BusError::BitRecessive, Lec::BITDOMINANT => BusError::BitDominant, Lec::CRC => BusError::Crc, Lec::CUSTOM => BusError::Software, Lec::NOERROR => unreachable!(), }); } None } /// Puts a CAN frame in a transmit mailbox for transmission on the bus. /// /// Frames are transmitted to the bus based on their priority (see [`FramePriority`]). /// Transmit order is preserved for frames with identical priority. /// /// If all transmit mailboxes are full, and `frame` has a higher priority than the /// lowest-priority message in the transmit mailboxes, transmission of the enqueued frame is /// cancelled and `frame` is enqueued instead. The frame that was replaced is returned as /// [`TransmitStatus::dequeued_frame`]. pub fn transmit(&mut self, frame: &Frame) -> nb::Result<TransmitStatus, Infallible> { // Get the index of the next free mailbox or the one with the lowest priority. let tsr = self.0.tsr().read(); let idx = tsr.code() as usize; let frame_is_pending = !tsr.tme(0) || !tsr.tme(1) || !tsr.tme(2); let pending_frame = if frame_is_pending { // High priority frames are transmitted first by the mailbox system. // Frames with identical identifier shall be transmitted in FIFO order. // The controller schedules pending frames of same priority based on the // mailbox index instead. As a workaround check all pending mailboxes // and only accept higher priority frames. self.check_priority(0, frame.id().into())?; self.check_priority(1, frame.id().into())?; self.check_priority(2, frame.id().into())?; let all_frames_are_pending = !tsr.tme(0) && !tsr.tme(1) && !tsr.tme(2); if all_frames_are_pending { // No free mailbox is available. This can only happen when three frames with // ascending priority (descending IDs) were requested for transmission and all // of them are blocked by bus traffic with even higher priority. // To prevent a priority inversion abort and replace the lowest priority frame. self.read_pending_mailbox(idx) } else { // There was a free mailbox. None } } else { // All mailboxes are available: Send frame without performing any checks. None }; self.write_mailbox(idx, frame); let mailbox = match idx { 0 => Mailbox::Mailbox0, 1 => Mailbox::Mailbox1, 2 => Mailbox::Mailbox2, _ => unreachable!(), }; Ok(TransmitStatus { dequeued_frame: pending_frame, mailbox, }) } /// Returns `Ok` when the mailbox is free or if it contains pending frame with a /// lower priority (higher ID) than the identifier `id`. fn check_priority(&self, idx: usize, id: IdReg) -> nb::Result<(), Infallible> { // Read the pending frame's id to check its priority. assert!(idx < 3); let tir = &self.0.tx(idx).tir().read(); //let tir = &can.tx[idx].tir.read(); // Check the priority by comparing the identifiers. But first make sure the // frame has not finished the transmission (`TXRQ` == 0) in the meantime. if tir.txrq() && id <= IdReg::from_register(tir.0) { // There's a mailbox whose priority is higher or equal // the priority of the new frame. return Err(nb::Error::WouldBlock); } Ok(()) } fn write_mailbox(&mut self, idx: usize, frame: &Frame) { debug_assert!(idx < 3); let mb = self.0.tx(idx); mb.tdtr().write(|w| w.set_dlc(frame.header().len() as u8)); mb.tdlr() .write(|w| w.0 = u32::from_ne_bytes(frame.data()[0..4].try_into().unwrap())); mb.tdhr() .write(|w| w.0 = u32::from_ne_bytes(frame.data()[4..8].try_into().unwrap())); let id: IdReg = frame.id().into(); mb.tir().write(|w| { w.0 = id.0; w.set_txrq(true); }); } fn read_pending_mailbox(&mut self, idx: usize) -> Option<Frame> { if self.abort_by_index(idx) { debug_assert!(idx < 3); let mb = self.0.tx(idx); let id = IdReg(mb.tir().read().0); let mut data = [0xff; 8]; data[0..4].copy_from_slice(&mb.tdlr().read().0.to_ne_bytes()); data[4..8].copy_from_slice(&mb.tdhr().read().0.to_ne_bytes()); let len = mb.tdtr().read().dlc(); Some(Frame::new(Header::new(id.id(), len, id.rtr()), &data).unwrap()) } else { // Abort request failed because the frame was already sent (or being sent) on // the bus. All mailboxes are now free. This can happen for small prescaler // values (e.g. 1MBit/s bit timing with a source clock of 8MHz) or when an ISR // has preempted the execution. None } } /// Tries to abort a pending frame. Returns `true` when aborted. fn abort_by_index(&mut self, idx: usize) -> bool { self.0.tsr().write(|reg| reg.set_abrq(idx, true)); // Wait for the abort request to be finished. loop { let tsr = self.0.tsr().read(); if false == tsr.abrq(idx) { break tsr.txok(idx) == false; } } } /// Attempts to abort the sending of a frame that is pending in a mailbox. /// /// If there is no frame in the provided mailbox, or its transmission succeeds before it can be /// aborted, this function has no effect and returns `false`. /// /// If there is a frame in the provided mailbox, and it is canceled successfully, this function /// returns `true`. pub fn abort(&mut self, mailbox: Mailbox) -> bool { // If the mailbox is empty, the value of TXOKx depends on what happened with the previous // frame in that mailbox. Only call abort_by_index() if the mailbox is not empty. let tsr = self.0.tsr().read(); let mailbox_empty = match mailbox { Mailbox::Mailbox0 => tsr.tme(0), Mailbox::Mailbox1 => tsr.tme(1), Mailbox::Mailbox2 => tsr.tme(2), }; if mailbox_empty { false } else { self.abort_by_index(mailbox as usize) } } /// Returns `true` if no frame is pending for transmission. pub fn is_idle(&self) -> bool { let tsr = self.0.tsr().read(); tsr.tme(0) && tsr.tme(1) && tsr.tme(2) } pub fn receive_frame_available(&self) -> bool { if self.0.rfr(0).read().fmp() != 0 { true } else if self.0.rfr(1).read().fmp() != 0 { true } else { false } } pub fn receive_fifo(&self, fifo: RxFifo) -> Option<Envelope> { // Generate timestamp as early as possible #[cfg(feature = "time")] let ts = embassy_time::Instant::now(); use crate::pac::can::vals::Ide; let fifo_idx = match fifo { RxFifo::Fifo0 => 0usize, RxFifo::Fifo1 => 1usize, }; let rfr = self.0.rfr(fifo_idx); let fifo = self.0.rx(fifo_idx); // If there are no pending messages, there is nothing to do if rfr.read().fmp() == 0 { return None; } let rir = fifo.rir().read(); let id: embedded_can::Id = if rir.ide() == Ide::STANDARD { embedded_can::StandardId::new(rir.stid()).unwrap().into() } else { let stid = (rir.stid() & 0x7FF) as u32; let exid = rir.exid() & 0x3FFFF; let id = (stid << 18) | (exid); embedded_can::ExtendedId::new(id).unwrap().into() }; let rdtr = fifo.rdtr().read(); let data_len = rdtr.dlc(); let rtr = rir.rtr() == stm32_metapac::can::vals::Rtr::REMOTE; #[cfg(not(feature = "time"))] let ts = rdtr.time(); let mut data: [u8; 8] = [0; 8]; data[0..4].copy_from_slice(&fifo.rdlr().read().0.to_ne_bytes()); data[4..8].copy_from_slice(&fifo.rdhr().read().0.to_ne_bytes()); let frame = Frame::new(Header::new(id, data_len, rtr), &data).unwrap(); let envelope = Envelope { ts, frame }; rfr.modify(|v| v.set_rfom(true)); Some(envelope) } } /// Identifier of a CAN message. /// /// Can be either a standard identifier (11bit, Range: 0..0x3FF) or a /// extendended identifier (29bit , Range: 0..0x1FFFFFFF). /// /// The `Ord` trait can be used to determine the frame’s priority this ID /// belongs to. /// Lower identifier values have a higher priority. Additionally standard frames /// have a higher priority than extended frames and data frames have a higher /// priority than remote frames. #[derive(Clone, Copy, Debug, PartialEq, Eq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub(crate) struct IdReg(u32); impl IdReg { const STANDARD_SHIFT: u32 = 21; const EXTENDED_SHIFT: u32 = 3; const IDE_MASK: u32 = 0x0000_0004; const RTR_MASK: u32 = 0x0000_0002; /// Creates a new standard identifier (11bit, Range: 0..0x7FF) /// /// Panics for IDs outside the allowed range. fn new_standard(id: StandardId) -> Self { Self(u32::from(id.as_raw()) << Self::STANDARD_SHIFT) } /// Creates a new extendended identifier (29bit , Range: 0..0x1FFFFFFF). /// /// Panics for IDs outside the allowed range. fn new_extended(id: ExtendedId) -> IdReg { Self(id.as_raw() << Self::EXTENDED_SHIFT | Self::IDE_MASK) } fn from_register(reg: u32) -> IdReg { Self(reg & 0xFFFF_FFFE) } /// Returns the identifier. fn to_id(self) -> Id { if self.is_extended() { Id::Extended(unsafe { ExtendedId::new_unchecked(self.0 >> Self::EXTENDED_SHIFT) }) } else { Id::Standard(unsafe { StandardId::new_unchecked((self.0 >> Self::STANDARD_SHIFT) as u16) }) } } /// Returns the identifier. fn id(self) -> embedded_can::Id { if self.is_extended() { embedded_can::ExtendedId::new(self.0 >> Self::EXTENDED_SHIFT) .unwrap() .into() } else { embedded_can::StandardId::new((self.0 >> Self::STANDARD_SHIFT) as u16) .unwrap() .into() } } /// Returns `true` if the identifier is an extended identifier. fn is_extended(self) -> bool { self.0 & Self::IDE_MASK != 0 } /// Returns `true` if the identifer is part of a remote frame (RTR bit set). fn rtr(self) -> bool { self.0 & Self::RTR_MASK != 0 } } impl From<&embedded_can::Id> for IdReg { fn from(eid: &embedded_can::Id) -> Self { match eid { embedded_can::Id::Standard(id) => IdReg::new_standard(StandardId::new(id.as_raw()).unwrap()), embedded_can::Id::Extended(id) => IdReg::new_extended(ExtendedId::new(id.as_raw()).unwrap()), } } } impl From<IdReg> for embedded_can::Id { fn from(idr: IdReg) -> Self { idr.id() } } /// `IdReg` is ordered by priority. impl Ord for IdReg { fn cmp(&self, other: &Self) -> Ordering { // When the IDs match, data frames have priority over remote frames. let rtr = self.rtr().cmp(&other.rtr()).reverse(); let id_a = self.to_id(); let id_b = other.to_id(); match (id_a, id_b) { (Id::Standard(a), Id::Standard(b)) => { // Lower IDs have priority over higher IDs. a.as_raw().cmp(&b.as_raw()).reverse().then(rtr) } (Id::Extended(a), Id::Extended(b)) => a.as_raw().cmp(&b.as_raw()).reverse().then(rtr), (Id::Standard(a), Id::Extended(b)) => { // Standard frames have priority over extended frames if their Base IDs match. a.as_raw() .cmp(&b.standard_id().as_raw()) .reverse() .then(Ordering::Greater) } (Id::Extended(a), Id::Standard(b)) => { a.standard_id().as_raw().cmp(&b.as_raw()).reverse().then(Ordering::Less) } } } } impl PartialOrd for IdReg { fn partial_cmp(&self, other: &Self) -> Option<Ordering> { Some(self.cmp(other)) } } #[derive(Debug, Copy, Clone, Eq, PartialEq)] pub(crate) enum RxFifo { Fifo0, Fifo1, }