mirror of
https://github.com/embassy-rs/embassy.git
synced 2024-11-22 06:42:32 +00:00
Merge pull request #2760 from embassy-rs/stm32-bxcan-flatten
stm32/can: simplify bxcan api, merging bx::* into the main structs.
This commit is contained in:
commit
016d552d1a
File diff suppressed because it is too large
Load Diff
@ -2,7 +2,7 @@
|
||||
|
||||
use core::marker::PhantomData;
|
||||
|
||||
use crate::can::bx::{ExtendedId, Fifo, FilterOwner, Id, Instance, MasterInstance, StandardId};
|
||||
use super::{ExtendedId, Fifo, FilterOwner, Id, Instance, MasterInstance, StandardId};
|
||||
|
||||
const F32_RTR: u32 = 0b010; // set the RTR bit to match remote frames
|
||||
const F32_IDE: u32 = 0b100; // set the IDE bit to match extended identifiers
|
@ -1,29 +1,27 @@
|
||||
pub mod filter;
|
||||
mod registers;
|
||||
|
||||
use core::future::poll_fn;
|
||||
use core::marker::PhantomData;
|
||||
use core::ops::{Deref, DerefMut};
|
||||
use core::task::Poll;
|
||||
|
||||
pub mod bx;
|
||||
|
||||
pub use bx::{filter, Data, ExtendedId, Fifo, Frame, Header, Id, StandardId};
|
||||
use embassy_hal_internal::{into_ref, PeripheralRef};
|
||||
use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
|
||||
use embassy_sync::channel::Channel;
|
||||
use embassy_sync::waitqueue::AtomicWaker;
|
||||
pub use embedded_can::{ExtendedId, Id, StandardId};
|
||||
|
||||
use self::filter::MasterFilters;
|
||||
use self::registers::{Registers, RxFifo};
|
||||
pub use super::common::{BufferedCanReceiver, BufferedCanSender};
|
||||
use super::frame::{Envelope, Frame};
|
||||
use super::util;
|
||||
use crate::can::enums::{BusError, TryReadError};
|
||||
use crate::gpio::AFType;
|
||||
use crate::interrupt::typelevel::Interrupt;
|
||||
use crate::rcc::RccPeripheral;
|
||||
use crate::{interrupt, peripherals, Peripheral};
|
||||
|
||||
pub mod enums;
|
||||
pub mod frame;
|
||||
pub mod util;
|
||||
pub use frame::Envelope;
|
||||
|
||||
mod common;
|
||||
pub use self::common::{BufferedCanReceiver, BufferedCanSender};
|
||||
|
||||
/// Interrupt handler.
|
||||
pub struct TxInterruptHandler<T: Instance> {
|
||||
_phantom: PhantomData<T>,
|
||||
@ -80,9 +78,72 @@ impl<T: Instance> interrupt::typelevel::Handler<T::SCEInterrupt> for SceInterrup
|
||||
}
|
||||
}
|
||||
|
||||
/// Configuration proxy returned by [`Can::modify_config`].
|
||||
pub struct CanConfig<'a, T: Instance> {
|
||||
can: PhantomData<&'a mut T>,
|
||||
}
|
||||
|
||||
impl<T: Instance> CanConfig<'_, T> {
|
||||
/// Configures the bit timings.
|
||||
///
|
||||
/// You can use <http://www.bittiming.can-wiki.info/> to calculate the `btr` parameter. Enter
|
||||
/// parameters as follows:
|
||||
///
|
||||
/// - *Clock Rate*: The input clock speed to the CAN peripheral (*not* the CPU clock speed).
|
||||
/// This is the clock rate of the peripheral bus the CAN peripheral is attached to (eg. APB1).
|
||||
/// - *Sample Point*: Should normally be left at the default value of 87.5%.
|
||||
/// - *SJW*: Should normally be left at the default value of 1.
|
||||
///
|
||||
/// Then copy the `CAN_BUS_TIME` register value from the table and pass it as the `btr`
|
||||
/// parameter to this method.
|
||||
pub fn set_bit_timing(self, bt: crate::can::util::NominalBitTiming) -> Self {
|
||||
Registers(T::regs()).set_bit_timing(bt);
|
||||
self
|
||||
}
|
||||
|
||||
/// Configure the CAN bit rate.
|
||||
///
|
||||
/// This is a helper that internally calls `set_bit_timing()`[Self::set_bit_timing].
|
||||
pub fn set_bitrate(self, bitrate: u32) -> Self {
|
||||
let bit_timing = util::calc_can_timings(T::frequency(), bitrate).unwrap();
|
||||
self.set_bit_timing(bit_timing)
|
||||
}
|
||||
|
||||
/// Enables or disables loopback mode: Internally connects the TX and RX
|
||||
/// signals together.
|
||||
pub fn set_loopback(self, enabled: bool) -> Self {
|
||||
Registers(T::regs()).set_loopback(enabled);
|
||||
self
|
||||
}
|
||||
|
||||
/// Enables or disables silent mode: Disconnects the TX signal from the pin.
|
||||
pub fn set_silent(self, enabled: bool) -> Self {
|
||||
Registers(T::regs()).set_silent(enabled);
|
||||
self
|
||||
}
|
||||
|
||||
/// 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 {
|
||||
Registers(T::regs()).set_automatic_retransmit(enabled);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Instance> Drop for CanConfig<'_, T> {
|
||||
#[inline]
|
||||
fn drop(&mut self) {
|
||||
Registers(T::regs()).leave_init_mode();
|
||||
}
|
||||
}
|
||||
|
||||
/// CAN driver
|
||||
pub struct Can<'d, T: Instance> {
|
||||
can: crate::can::bx::Can<BxcanInstance<'d, T>>,
|
||||
peri: PeripheralRef<'d, T>,
|
||||
}
|
||||
|
||||
/// Error returned by `try_write`
|
||||
@ -145,14 +206,25 @@ impl<'d, T: Instance> Can<'d, T> {
|
||||
rx.set_as_af(rx.af_num(), AFType::Input);
|
||||
tx.set_as_af(tx.af_num(), AFType::OutputPushPull);
|
||||
|
||||
let can = crate::can::bx::Can::builder(BxcanInstance(peri), T::regs()).leave_disabled();
|
||||
Self { can }
|
||||
Registers(T::regs()).leave_init_mode();
|
||||
|
||||
Self { peri }
|
||||
}
|
||||
|
||||
/// Set CAN bit rate.
|
||||
pub fn set_bitrate(&mut self, bitrate: u32) {
|
||||
let bit_timing = util::calc_can_timings(T::frequency(), bitrate).unwrap();
|
||||
self.can.modify_config().set_bit_timing(bit_timing).leave_disabled();
|
||||
self.modify_config().set_bit_timing(bit_timing);
|
||||
}
|
||||
|
||||
/// Configure bit timings and silent/loop-back mode.
|
||||
///
|
||||
/// Calling this method will enter initialization mode. You must enable the peripheral
|
||||
/// again afterwards with [`enable`](Self::enable).
|
||||
pub fn modify_config(&mut self) -> CanConfig<'_, T> {
|
||||
Registers(T::regs()).enter_init_mode();
|
||||
|
||||
CanConfig { can: PhantomData }
|
||||
}
|
||||
|
||||
/// Enables the peripheral and synchronizes with the bus.
|
||||
@ -160,7 +232,7 @@ impl<'d, T: Instance> Can<'d, T> {
|
||||
/// This will wait for 11 consecutive recessive bits (bus idle state).
|
||||
/// Contrary to enable method from bxcan library, this will not freeze the executor while waiting.
|
||||
pub async fn enable(&mut self) {
|
||||
while self.registers.enable_non_blocking().is_err() {
|
||||
while Registers(T::regs()).enable_non_blocking().is_err() {
|
||||
// SCE interrupt is only generated for entering sleep mode, but not leaving.
|
||||
// Yield to allow other tasks to execute while can bus is initializing.
|
||||
embassy_futures::yield_now().await;
|
||||
@ -170,19 +242,19 @@ impl<'d, T: Instance> Can<'d, T> {
|
||||
/// Queues the message to be sent.
|
||||
///
|
||||
/// If the TX queue is full, this will wait until there is space, therefore exerting backpressure.
|
||||
pub async fn write(&mut self, frame: &Frame) -> crate::can::bx::TransmitStatus {
|
||||
pub async fn write(&mut self, frame: &Frame) -> TransmitStatus {
|
||||
self.split().0.write(frame).await
|
||||
}
|
||||
|
||||
/// Attempts to transmit a frame without blocking.
|
||||
///
|
||||
/// Returns [Err(TryWriteError::Full)] if all transmit mailboxes are full.
|
||||
pub fn try_write(&mut self, frame: &Frame) -> Result<crate::can::bx::TransmitStatus, TryWriteError> {
|
||||
pub fn try_write(&mut self, frame: &Frame) -> Result<TransmitStatus, TryWriteError> {
|
||||
self.split().0.try_write(frame)
|
||||
}
|
||||
|
||||
/// Waits for a specific transmit mailbox to become empty
|
||||
pub async fn flush(&self, mb: crate::can::bx::Mailbox) {
|
||||
pub async fn flush(&self, mb: Mailbox) {
|
||||
CanTx::<T>::flush_inner(mb).await
|
||||
}
|
||||
|
||||
@ -196,6 +268,22 @@ impl<'d, T: Instance> Can<'d, T> {
|
||||
CanTx::<T>::flush_all_inner().await
|
||||
}
|
||||
|
||||
/// 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 {
|
||||
Registers(T::regs()).abort(mailbox)
|
||||
}
|
||||
|
||||
/// Returns `true` if no frame is pending for transmission.
|
||||
pub fn is_transmitter_idle(&self) -> bool {
|
||||
Registers(T::regs()).is_idle()
|
||||
}
|
||||
|
||||
/// Read a CAN frame.
|
||||
///
|
||||
/// If no CAN frame is in the RX buffer, this will wait until there is one.
|
||||
@ -221,8 +309,14 @@ impl<'d, T: Instance> Can<'d, T> {
|
||||
///
|
||||
/// Useful for doing separate transmit/receive tasks.
|
||||
pub fn split<'c>(&'c mut self) -> (CanTx<'d, T>, CanRx<'d, T>) {
|
||||
let (tx, rx) = self.can.split_by_ref();
|
||||
(CanTx { tx }, CanRx { rx })
|
||||
(
|
||||
CanTx {
|
||||
_peri: unsafe { self.peri.clone_unchecked() },
|
||||
},
|
||||
CanRx {
|
||||
peri: unsafe { self.peri.clone_unchecked() },
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
/// Return a buffered instance of driver. User must supply Buffers
|
||||
@ -239,10 +333,13 @@ impl<'d, T: Instance> Can<'d, T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> AsMut<crate::can::bx::Can<BxcanInstance<'d, T>>> for Can<'d, T> {
|
||||
/// Get mutable access to the lower-level driver from the `bxcan` crate.
|
||||
fn as_mut(&mut self) -> &mut crate::can::bx::Can<BxcanInstance<'d, T>> {
|
||||
&mut self.can
|
||||
impl<'d, T: FilterOwner> Can<'d, T> {
|
||||
/// Accesses the filter banks owned by this CAN peripheral.
|
||||
///
|
||||
/// To modify filters of a slave peripheral, `modify_filters` has to be called on the master
|
||||
/// peripheral instead.
|
||||
pub fn modify_filters(&mut self) -> MasterFilters<'_, T> {
|
||||
unsafe { MasterFilters::new(T::regs()) }
|
||||
}
|
||||
}
|
||||
|
||||
@ -288,17 +385,17 @@ impl<'d, T: Instance, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize> Buffer
|
||||
|
||||
/// CAN driver, transmit half.
|
||||
pub struct CanTx<'d, T: Instance> {
|
||||
tx: crate::can::bx::Tx<BxcanInstance<'d, T>>,
|
||||
_peri: PeripheralRef<'d, T>,
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> CanTx<'d, T> {
|
||||
/// Queues the message to be sent.
|
||||
///
|
||||
/// If the TX queue is full, this will wait until there is space, therefore exerting backpressure.
|
||||
pub async fn write(&mut self, frame: &Frame) -> crate::can::bx::TransmitStatus {
|
||||
pub async fn write(&mut self, frame: &Frame) -> TransmitStatus {
|
||||
poll_fn(|cx| {
|
||||
T::state().tx_mode.register(cx.waker());
|
||||
if let Ok(status) = self.tx.transmit(frame) {
|
||||
if let Ok(status) = Registers(T::regs()).transmit(frame) {
|
||||
return Poll::Ready(status);
|
||||
}
|
||||
|
||||
@ -310,11 +407,11 @@ impl<'d, T: Instance> CanTx<'d, T> {
|
||||
/// Attempts to transmit a frame without blocking.
|
||||
///
|
||||
/// Returns [Err(TryWriteError::Full)] if all transmit mailboxes are full.
|
||||
pub fn try_write(&mut self, frame: &Frame) -> Result<crate::can::bx::TransmitStatus, TryWriteError> {
|
||||
self.tx.transmit(frame).map_err(|_| TryWriteError::Full)
|
||||
pub fn try_write(&mut self, frame: &Frame) -> Result<TransmitStatus, TryWriteError> {
|
||||
Registers(T::regs()).transmit(frame).map_err(|_| TryWriteError::Full)
|
||||
}
|
||||
|
||||
async fn flush_inner(mb: crate::can::bx::Mailbox) {
|
||||
async fn flush_inner(mb: Mailbox) {
|
||||
poll_fn(|cx| {
|
||||
T::state().tx_mode.register(cx.waker());
|
||||
if T::regs().tsr().read().tme(mb.index()) {
|
||||
@ -327,7 +424,7 @@ impl<'d, T: Instance> CanTx<'d, T> {
|
||||
}
|
||||
|
||||
/// Waits for a specific transmit mailbox to become empty
|
||||
pub async fn flush(&self, mb: crate::can::bx::Mailbox) {
|
||||
pub async fn flush(&self, mb: Mailbox) {
|
||||
Self::flush_inner(mb).await
|
||||
}
|
||||
|
||||
@ -336,9 +433,9 @@ impl<'d, T: Instance> CanTx<'d, T> {
|
||||
T::state().tx_mode.register(cx.waker());
|
||||
|
||||
let tsr = T::regs().tsr().read();
|
||||
if tsr.tme(crate::can::bx::Mailbox::Mailbox0.index())
|
||||
|| tsr.tme(crate::can::bx::Mailbox::Mailbox1.index())
|
||||
|| tsr.tme(crate::can::bx::Mailbox::Mailbox2.index())
|
||||
if tsr.tme(Mailbox::Mailbox0.index())
|
||||
|| tsr.tme(Mailbox::Mailbox1.index())
|
||||
|| tsr.tme(Mailbox::Mailbox2.index())
|
||||
{
|
||||
return Poll::Ready(());
|
||||
}
|
||||
@ -358,9 +455,9 @@ impl<'d, T: Instance> CanTx<'d, T> {
|
||||
T::state().tx_mode.register(cx.waker());
|
||||
|
||||
let tsr = T::regs().tsr().read();
|
||||
if tsr.tme(crate::can::bx::Mailbox::Mailbox0.index())
|
||||
&& tsr.tme(crate::can::bx::Mailbox::Mailbox1.index())
|
||||
&& tsr.tme(crate::can::bx::Mailbox::Mailbox2.index())
|
||||
if tsr.tme(Mailbox::Mailbox0.index())
|
||||
&& tsr.tme(Mailbox::Mailbox1.index())
|
||||
&& tsr.tme(Mailbox::Mailbox2.index())
|
||||
{
|
||||
return Poll::Ready(());
|
||||
}
|
||||
@ -375,12 +472,28 @@ impl<'d, T: Instance> CanTx<'d, T> {
|
||||
Self::flush_all_inner().await
|
||||
}
|
||||
|
||||
/// 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 {
|
||||
Registers(T::regs()).abort(mailbox)
|
||||
}
|
||||
|
||||
/// Returns `true` if no frame is pending for transmission.
|
||||
pub fn is_idle(&self) -> bool {
|
||||
Registers(T::regs()).is_idle()
|
||||
}
|
||||
|
||||
/// Return a buffered instance of driver. User must supply Buffers
|
||||
pub fn buffered<const TX_BUF_SIZE: usize>(
|
||||
self,
|
||||
txb: &'static mut TxBuf<TX_BUF_SIZE>,
|
||||
) -> BufferedCanTx<'d, T, TX_BUF_SIZE> {
|
||||
BufferedCanTx::new(self.tx, txb)
|
||||
BufferedCanTx::new(self, txb)
|
||||
}
|
||||
}
|
||||
|
||||
@ -389,19 +502,19 @@ pub type TxBuf<const BUF_SIZE: usize> = Channel<CriticalSectionRawMutex, Frame,
|
||||
|
||||
/// Buffered CAN driver, transmit half.
|
||||
pub struct BufferedCanTx<'d, T: Instance, const TX_BUF_SIZE: usize> {
|
||||
_tx: crate::can::bx::Tx<BxcanInstance<'d, T>>,
|
||||
_tx: CanTx<'d, T>,
|
||||
tx_buf: &'static TxBuf<TX_BUF_SIZE>,
|
||||
}
|
||||
|
||||
impl<'d, T: Instance, const TX_BUF_SIZE: usize> BufferedCanTx<'d, T, TX_BUF_SIZE> {
|
||||
fn new(_tx: crate::can::bx::Tx<BxcanInstance<'d, T>>, tx_buf: &'static TxBuf<TX_BUF_SIZE>) -> Self {
|
||||
fn new(_tx: CanTx<'d, T>, tx_buf: &'static TxBuf<TX_BUF_SIZE>) -> Self {
|
||||
Self { _tx, tx_buf }.setup()
|
||||
}
|
||||
|
||||
fn setup(self) -> Self {
|
||||
// We don't want interrupts being processed while we change modes.
|
||||
critical_section::with(|_| unsafe {
|
||||
let tx_inner = self::common::ClassicBufferedTxInner {
|
||||
let tx_inner = super::common::ClassicBufferedTxInner {
|
||||
tx_receiver: self.tx_buf.receiver().into(),
|
||||
};
|
||||
T::mut_state().tx_mode = TxMode::Buffered(tx_inner);
|
||||
@ -435,7 +548,7 @@ impl<'d, T: Instance, const TX_BUF_SIZE: usize> Drop for BufferedCanTx<'d, T, TX
|
||||
/// CAN driver, receive half.
|
||||
#[allow(dead_code)]
|
||||
pub struct CanRx<'d, T: Instance> {
|
||||
rx: crate::can::bx::Rx<BxcanInstance<'d, T>>,
|
||||
peri: PeripheralRef<'d, T>,
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> CanRx<'d, T> {
|
||||
@ -465,7 +578,7 @@ impl<'d, T: Instance> CanRx<'d, T> {
|
||||
self,
|
||||
rxb: &'static mut RxBuf<RX_BUF_SIZE>,
|
||||
) -> BufferedCanRx<'d, T, RX_BUF_SIZE> {
|
||||
BufferedCanRx::new(self.rx, rxb)
|
||||
BufferedCanRx::new(self, rxb)
|
||||
}
|
||||
}
|
||||
|
||||
@ -474,19 +587,19 @@ pub type RxBuf<const BUF_SIZE: usize> = Channel<CriticalSectionRawMutex, Result<
|
||||
|
||||
/// CAN driver, receive half in Buffered mode.
|
||||
pub struct BufferedCanRx<'d, T: Instance, const RX_BUF_SIZE: usize> {
|
||||
_rx: crate::can::bx::Rx<BxcanInstance<'d, T>>,
|
||||
_rx: CanRx<'d, T>,
|
||||
rx_buf: &'static RxBuf<RX_BUF_SIZE>,
|
||||
}
|
||||
|
||||
impl<'d, T: Instance, const RX_BUF_SIZE: usize> BufferedCanRx<'d, T, RX_BUF_SIZE> {
|
||||
fn new(_rx: crate::can::bx::Rx<BxcanInstance<'d, T>>, rx_buf: &'static RxBuf<RX_BUF_SIZE>) -> Self {
|
||||
fn new(_rx: CanRx<'d, T>, rx_buf: &'static RxBuf<RX_BUF_SIZE>) -> Self {
|
||||
BufferedCanRx { _rx, rx_buf }.setup()
|
||||
}
|
||||
|
||||
fn setup(self) -> Self {
|
||||
// We don't want interrupts being processed while we change modes.
|
||||
critical_section::with(|_| unsafe {
|
||||
let rx_inner = self::common::ClassicBufferedRxInner {
|
||||
let rx_inner = super::common::ClassicBufferedRxInner {
|
||||
rx_sender: self.rx_buf.sender().into(),
|
||||
};
|
||||
T::mut_state().rx_mode = RxMode::Buffered(rx_inner);
|
||||
@ -511,8 +624,7 @@ impl<'d, T: Instance, const RX_BUF_SIZE: usize> BufferedCanRx<'d, T, RX_BUF_SIZE
|
||||
Err(e) => Err(TryReadError::BusError(e)),
|
||||
}
|
||||
} else {
|
||||
let registers = crate::can::bx::Registers { canregs: T::regs() };
|
||||
if let Some(err) = registers.curr_error() {
|
||||
if let Some(err) = Registers(T::regs()).curr_error() {
|
||||
return Err(TryReadError::BusError(err));
|
||||
} else {
|
||||
Err(TryReadError::Empty)
|
||||
@ -544,8 +656,6 @@ impl<'d, T: Instance, const RX_BUF_SIZE: usize> Drop for BufferedCanRx<'d, T, RX
|
||||
}
|
||||
}
|
||||
|
||||
use crate::can::bx::RxFifo;
|
||||
|
||||
impl<'d, T: Instance> Drop for Can<'d, T> {
|
||||
fn drop(&mut self) {
|
||||
// Cannot call `free()` because it moves the instance.
|
||||
@ -555,35 +665,62 @@ impl<'d, T: Instance> Drop for Can<'d, T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> Deref for Can<'d, T> {
|
||||
type Target = crate::can::bx::Can<BxcanInstance<'d, T>>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.can
|
||||
}
|
||||
/// Identifies one of the two receive FIFOs.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum Fifo {
|
||||
/// First receive FIFO
|
||||
Fifo0 = 0,
|
||||
/// Second receive FIFO
|
||||
Fifo1 = 1,
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> DerefMut for Can<'d, T> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.can
|
||||
}
|
||||
/// Identifies one of the three transmit mailboxes.
|
||||
#[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum Mailbox {
|
||||
/// Transmit mailbox 0
|
||||
Mailbox0 = 0,
|
||||
/// Transmit mailbox 1
|
||||
Mailbox1 = 1,
|
||||
/// Transmit mailbox 2
|
||||
Mailbox2 = 2,
|
||||
}
|
||||
|
||||
use crate::can::enums::{BusError, TryReadError};
|
||||
/// Contains information about a frame enqueued for transmission via [`Can::transmit`] or
|
||||
/// [`Tx::transmit`].
|
||||
pub struct TransmitStatus {
|
||||
dequeued_frame: Option<Frame>,
|
||||
mailbox: Mailbox,
|
||||
}
|
||||
|
||||
impl TransmitStatus {
|
||||
/// Returns the lower-priority frame that was dequeued to make space for the new frame.
|
||||
#[inline]
|
||||
pub fn dequeued_frame(&self) -> Option<&Frame> {
|
||||
self.dequeued_frame.as_ref()
|
||||
}
|
||||
|
||||
/// Returns the [`Mailbox`] the frame was enqueued in.
|
||||
#[inline]
|
||||
pub fn mailbox(&self) -> Mailbox {
|
||||
self.mailbox
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) enum RxMode {
|
||||
NonBuffered(AtomicWaker),
|
||||
Buffered(crate::can::_version::common::ClassicBufferedRxInner),
|
||||
Buffered(super::common::ClassicBufferedRxInner),
|
||||
}
|
||||
|
||||
impl RxMode {
|
||||
pub fn on_interrupt<T: Instance>(&self, fifo: crate::can::_version::bx::RxFifo) {
|
||||
pub fn on_interrupt<T: Instance>(&self, fifo: RxFifo) {
|
||||
match self {
|
||||
Self::NonBuffered(waker) => {
|
||||
// Disable interrupts until read
|
||||
let fifo_idx = match fifo {
|
||||
crate::can::_version::bx::RxFifo::Fifo0 => 0usize,
|
||||
crate::can::_version::bx::RxFifo::Fifo1 => 1usize,
|
||||
RxFifo::Fifo0 => 0usize,
|
||||
RxFifo::Fifo1 => 1usize,
|
||||
};
|
||||
T::regs().ier().write(|w| {
|
||||
w.set_fmpie(fifo_idx, false);
|
||||
@ -591,10 +728,8 @@ impl RxMode {
|
||||
waker.wake();
|
||||
}
|
||||
Self::Buffered(buf) => {
|
||||
let regsisters = crate::can::bx::Registers { canregs: T::regs() };
|
||||
|
||||
loop {
|
||||
match regsisters.receive_fifo(fifo) {
|
||||
match Registers(T::regs()).receive_fifo(fifo) {
|
||||
Some(envelope) => {
|
||||
// NOTE: consensus was reached that if rx_queue is full, packets should be dropped
|
||||
let _ = buf.rx_sender.try_send(Ok(envelope));
|
||||
@ -628,13 +763,13 @@ impl RxMode {
|
||||
pub fn try_read<T: Instance>(&self) -> Result<Envelope, TryReadError> {
|
||||
match self {
|
||||
Self::NonBuffered(_) => {
|
||||
let registers = crate::can::bx::Registers { canregs: T::regs() };
|
||||
if let Some(msg) = registers.receive_fifo(super::bx::RxFifo::Fifo0) {
|
||||
let registers = Registers(T::regs());
|
||||
if let Some(msg) = registers.receive_fifo(RxFifo::Fifo0) {
|
||||
T::regs().ier().write(|w| {
|
||||
w.set_fmpie(0, true);
|
||||
});
|
||||
Ok(msg)
|
||||
} else if let Some(msg) = registers.receive_fifo(super::bx::RxFifo::Fifo1) {
|
||||
} else if let Some(msg) = registers.receive_fifo(RxFifo::Fifo1) {
|
||||
T::regs().ier().write(|w| {
|
||||
w.set_fmpie(1, true);
|
||||
});
|
||||
@ -655,8 +790,7 @@ impl RxMode {
|
||||
Self::NonBuffered(waker) => {
|
||||
poll_fn(|cx| {
|
||||
waker.register(cx.waker());
|
||||
let registers = crate::can::bx::Registers { canregs: T::regs() };
|
||||
if registers.receive_frame_available() {
|
||||
if Registers(T::regs()).receive_frame_available() {
|
||||
Poll::Ready(())
|
||||
} else {
|
||||
Poll::Pending
|
||||
@ -673,15 +807,13 @@ impl RxMode {
|
||||
|
||||
enum TxMode {
|
||||
NonBuffered(AtomicWaker),
|
||||
Buffered(self::common::ClassicBufferedTxInner),
|
||||
Buffered(super::common::ClassicBufferedTxInner),
|
||||
}
|
||||
|
||||
impl TxMode {
|
||||
pub fn buffer_free<T: Instance>(&self) -> bool {
|
||||
let tsr = T::regs().tsr().read();
|
||||
tsr.tme(crate::can::bx::Mailbox::Mailbox0.index())
|
||||
|| tsr.tme(crate::can::bx::Mailbox::Mailbox1.index())
|
||||
|| tsr.tme(crate::can::bx::Mailbox::Mailbox2.index())
|
||||
tsr.tme(Mailbox::Mailbox0.index()) || tsr.tme(Mailbox::Mailbox1.index()) || tsr.tme(Mailbox::Mailbox2.index())
|
||||
}
|
||||
pub fn on_interrupt<T: Instance>(&self) {
|
||||
match &T::state().tx_mode {
|
||||
@ -690,8 +822,7 @@ impl TxMode {
|
||||
while self.buffer_free::<T>() {
|
||||
match buf.tx_receiver.try_receive() {
|
||||
Ok(frame) => {
|
||||
let mut registers = crate::can::bx::Registers { canregs: T::regs() };
|
||||
_ = registers.transmit(&frame);
|
||||
_ = Registers(T::regs()).transmit(&frame);
|
||||
}
|
||||
Err(_) => {
|
||||
break;
|
||||
@ -738,7 +869,7 @@ trait SealedInstance {
|
||||
|
||||
/// CAN instance trait.
|
||||
#[allow(private_bounds)]
|
||||
pub trait Instance: SealedInstance + RccPeripheral + 'static {
|
||||
pub trait Instance: Peripheral<P = Self> + SealedInstance + RccPeripheral + 'static {
|
||||
/// TX interrupt for this instance.
|
||||
type TXInterrupt: crate::interrupt::typelevel::Interrupt;
|
||||
/// RX0 interrupt for this instance.
|
||||
@ -749,10 +880,35 @@ pub trait Instance: SealedInstance + RccPeripheral + 'static {
|
||||
type SCEInterrupt: crate::interrupt::typelevel::Interrupt;
|
||||
}
|
||||
|
||||
/// BXCAN instance newtype.
|
||||
pub struct BxcanInstance<'a, T>(PeripheralRef<'a, T>);
|
||||
/// A bxCAN instance that owns filter banks.
|
||||
///
|
||||
/// In master-slave-instance setups, only the master instance owns the filter banks, and needs to
|
||||
/// split some of them off for use by the slave instance. In that case, the master instance should
|
||||
/// implement [`FilterOwner`] and [`MasterInstance`], while the slave instance should only implement
|
||||
/// [`Instance`].
|
||||
///
|
||||
/// In single-instance configurations, the instance owns all filter banks and they can not be split
|
||||
/// off. In that case, the instance should implement [`Instance`] and [`FilterOwner`].
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// This trait must only be implemented if the instance does, in fact, own its associated filter
|
||||
/// banks, and `NUM_FILTER_BANKS` must be correct.
|
||||
pub unsafe trait FilterOwner: Instance {
|
||||
/// The total number of filter banks available to the instance.
|
||||
///
|
||||
/// This is usually either 14 or 28, and should be specified in the chip's reference manual or datasheet.
|
||||
const NUM_FILTER_BANKS: u8;
|
||||
}
|
||||
|
||||
unsafe impl<'d, T: Instance> crate::can::bx::Instance for BxcanInstance<'d, T> {}
|
||||
/// A bxCAN master instance that shares filter banks with a slave instance.
|
||||
///
|
||||
/// In master-slave-instance setups, this trait should be implemented for the master instance.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// This trait must only be implemented when there is actually an associated slave instance.
|
||||
pub unsafe trait MasterInstance: FilterOwner {}
|
||||
|
||||
foreach_peripheral!(
|
||||
(can, $inst:ident) => {
|
||||
@ -782,7 +938,7 @@ foreach_peripheral!(
|
||||
|
||||
foreach_peripheral!(
|
||||
(can, CAN) => {
|
||||
unsafe impl<'d> crate::can::bx::FilterOwner for BxcanInstance<'d, peripherals::CAN> {
|
||||
unsafe impl FilterOwner for peripherals::CAN {
|
||||
const NUM_FILTER_BANKS: u8 = 14;
|
||||
}
|
||||
};
|
||||
@ -797,19 +953,19 @@ foreach_peripheral!(
|
||||
))] {
|
||||
// Most L4 devices and some F7 devices use the name "CAN1"
|
||||
// even if there is no "CAN2" peripheral.
|
||||
unsafe impl<'d> crate::can::bx::FilterOwner for BxcanInstance<'d, peripherals::CAN1> {
|
||||
unsafe impl FilterOwner for peripherals::CAN1 {
|
||||
const NUM_FILTER_BANKS: u8 = 14;
|
||||
}
|
||||
} else {
|
||||
unsafe impl<'d> crate::can::bx::FilterOwner for BxcanInstance<'d, peripherals::CAN1> {
|
||||
unsafe impl FilterOwner for peripherals::CAN1 {
|
||||
const NUM_FILTER_BANKS: u8 = 28;
|
||||
}
|
||||
unsafe impl<'d> crate::can::bx::MasterInstance for BxcanInstance<'d, peripherals::CAN1> {}
|
||||
unsafe impl MasterInstance for peripherals::CAN1 {}
|
||||
}
|
||||
}
|
||||
};
|
||||
(can, CAN3) => {
|
||||
unsafe impl<'d> crate::can::bx::FilterOwner for BxcanInstance<'d, peripherals::CAN3> {
|
||||
unsafe impl FilterOwner for peripherals::CAN3 {
|
||||
const NUM_FILTER_BANKS: u8 = 14;
|
||||
}
|
||||
};
|
||||
@ -822,12 +978,12 @@ trait Index {
|
||||
fn index(&self) -> usize;
|
||||
}
|
||||
|
||||
impl Index for crate::can::bx::Mailbox {
|
||||
impl Index for Mailbox {
|
||||
fn index(&self) -> usize {
|
||||
match self {
|
||||
crate::can::bx::Mailbox::Mailbox0 => 0,
|
||||
crate::can::bx::Mailbox::Mailbox1 => 1,
|
||||
crate::can::bx::Mailbox::Mailbox2 => 2,
|
||||
Mailbox::Mailbox0 => 0,
|
||||
Mailbox::Mailbox1 => 1,
|
||||
Mailbox::Mailbox2 => 2,
|
||||
}
|
||||
}
|
||||
}
|
510
embassy-stm32/src/can/bxcan/registers.rs
Normal file
510
embassy-stm32/src/can/bxcan/registers.rs
Normal file
@ -0,0 +1,510 @@
|
||||
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,
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
use embassy_sync::channel::{DynamicReceiver, DynamicSender};
|
||||
|
||||
use crate::can::_version::enums::*;
|
||||
use crate::can::_version::frame::*;
|
||||
use super::enums::*;
|
||||
use super::frame::*;
|
||||
|
||||
pub(crate) struct ClassicBufferedRxInner {
|
||||
pub rx_sender: DynamicSender<'static, Result<Envelope, BusError>>,
|
||||
|
@ -14,19 +14,15 @@ use crate::interrupt::typelevel::Interrupt;
|
||||
use crate::rcc::RccPeripheral;
|
||||
use crate::{interrupt, peripherals, Peripheral};
|
||||
|
||||
mod common;
|
||||
pub mod enums;
|
||||
pub(crate) mod fd;
|
||||
pub mod frame;
|
||||
mod util;
|
||||
|
||||
use enums::*;
|
||||
use fd::config::*;
|
||||
use fd::filter::*;
|
||||
pub use fd::{config, filter};
|
||||
use frame::*;
|
||||
|
||||
pub use self::common::{BufferedCanReceiver, BufferedCanSender};
|
||||
use self::fd::config::*;
|
||||
use self::fd::filter::*;
|
||||
pub use self::fd::{config, filter};
|
||||
pub use super::common::{BufferedCanReceiver, BufferedCanSender};
|
||||
use super::enums::*;
|
||||
use super::frame::*;
|
||||
use super::util;
|
||||
|
||||
/// Timestamp for incoming packets. Use Embassy time when enabled.
|
||||
#[cfg(feature = "time")]
|
||||
@ -439,10 +435,10 @@ impl<'c, 'd, T: Instance, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize>
|
||||
fn setup(self) -> Self {
|
||||
// We don't want interrupts being processed while we change modes.
|
||||
critical_section::with(|_| unsafe {
|
||||
let rx_inner = self::common::ClassicBufferedRxInner {
|
||||
let rx_inner = super::common::ClassicBufferedRxInner {
|
||||
rx_sender: self.rx_buf.sender().into(),
|
||||
};
|
||||
let tx_inner = self::common::ClassicBufferedTxInner {
|
||||
let tx_inner = super::common::ClassicBufferedTxInner {
|
||||
tx_receiver: self.tx_buf.receiver().into(),
|
||||
};
|
||||
T::mut_state().rx_mode = RxMode::ClassicBuffered(rx_inner);
|
||||
@ -555,10 +551,10 @@ impl<'c, 'd, T: Instance, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize>
|
||||
fn setup(self) -> Self {
|
||||
// We don't want interrupts being processed while we change modes.
|
||||
critical_section::with(|_| unsafe {
|
||||
let rx_inner = self::common::FdBufferedRxInner {
|
||||
let rx_inner = super::common::FdBufferedRxInner {
|
||||
rx_sender: self.rx_buf.sender().into(),
|
||||
};
|
||||
let tx_inner = self::common::FdBufferedTxInner {
|
||||
let tx_inner = super::common::FdBufferedTxInner {
|
||||
tx_receiver: self.tx_buf.receiver().into(),
|
||||
};
|
||||
T::mut_state().rx_mode = RxMode::FdBuffered(rx_inner);
|
||||
@ -649,8 +645,8 @@ impl<'c, 'd, T: Instance> CanRx<'d, T> {
|
||||
|
||||
enum RxMode {
|
||||
NonBuffered(AtomicWaker),
|
||||
ClassicBuffered(self::common::ClassicBufferedRxInner),
|
||||
FdBuffered(self::common::FdBufferedRxInner),
|
||||
ClassicBuffered(super::common::ClassicBufferedRxInner),
|
||||
FdBuffered(super::common::FdBufferedRxInner),
|
||||
}
|
||||
|
||||
impl RxMode {
|
||||
@ -758,8 +754,8 @@ impl RxMode {
|
||||
|
||||
enum TxMode {
|
||||
NonBuffered(AtomicWaker),
|
||||
ClassicBuffered(self::common::ClassicBufferedTxInner),
|
||||
FdBuffered(self::common::FdBufferedTxInner),
|
||||
ClassicBuffered(super::common::ClassicBufferedTxInner),
|
||||
FdBuffered(super::common::FdBufferedTxInner),
|
||||
}
|
||||
|
||||
impl TxMode {
|
||||
|
@ -1,7 +1,14 @@
|
||||
//! Controller Area Network (CAN)
|
||||
#![macro_use]
|
||||
|
||||
#[cfg_attr(can_bxcan, path = "bxcan.rs")]
|
||||
#[cfg_attr(can_bxcan, path = "bxcan/mod.rs")]
|
||||
#[cfg_attr(any(can_fdcan_v1, can_fdcan_h7), path = "fdcan.rs")]
|
||||
mod _version;
|
||||
pub use _version::*;
|
||||
|
||||
mod common;
|
||||
pub mod enums;
|
||||
pub mod frame;
|
||||
pub mod util;
|
||||
|
||||
pub use frame::Frame;
|
||||
|
@ -3,8 +3,9 @@
|
||||
|
||||
use defmt::*;
|
||||
use embassy_executor::Spawner;
|
||||
use embassy_stm32::can::frame::Envelope;
|
||||
use embassy_stm32::can::{
|
||||
filter, Can, Envelope, Fifo, Frame, Id, Rx0InterruptHandler, Rx1InterruptHandler, SceInterruptHandler, StandardId,
|
||||
filter, Can, Fifo, Frame, Id, Rx0InterruptHandler, Rx1InterruptHandler, SceInterruptHandler, StandardId,
|
||||
TxInterruptHandler,
|
||||
};
|
||||
use embassy_stm32::peripherals::CAN;
|
||||
@ -55,17 +56,13 @@ async fn main(_spawner: Spawner) {
|
||||
|
||||
let mut can = Can::new(p.CAN, p.PB8, p.PB9, Irqs);
|
||||
|
||||
can.as_mut()
|
||||
.modify_filters()
|
||||
can.modify_filters()
|
||||
.enable_bank(0, Fifo::Fifo0, filter::Mask32::accept_all());
|
||||
|
||||
can.as_mut()
|
||||
.modify_config()
|
||||
can.modify_config()
|
||||
.set_loopback(false)
|
||||
.set_silent(false)
|
||||
.leave_disabled();
|
||||
|
||||
can.set_bitrate(250_000);
|
||||
.set_bitrate(250_000);
|
||||
|
||||
can.enable().await;
|
||||
let mut i: u8 = 0;
|
||||
|
@ -35,17 +35,12 @@ async fn main(_spawner: Spawner) {
|
||||
|
||||
let mut can = Can::new(p.CAN1, p.PA11, p.PA12, Irqs);
|
||||
|
||||
can.as_mut()
|
||||
.modify_filters()
|
||||
.enable_bank(0, Fifo::Fifo0, Mask32::accept_all());
|
||||
can.modify_filters().enable_bank(0, Fifo::Fifo0, Mask32::accept_all());
|
||||
|
||||
can.as_mut()
|
||||
.modify_config()
|
||||
can.modify_config()
|
||||
.set_loopback(true) // Receive own frames
|
||||
.set_silent(true)
|
||||
.leave_disabled();
|
||||
|
||||
can.set_bitrate(1_000_000);
|
||||
.set_bitrate(1_000_000);
|
||||
|
||||
can.enable().await;
|
||||
|
||||
|
@ -47,20 +47,18 @@ async fn main(spawner: Spawner) {
|
||||
|
||||
static CAN: StaticCell<Can<'static, CAN3>> = StaticCell::new();
|
||||
let can = CAN.init(Can::new(p.CAN3, p.PA8, p.PA15, Irqs));
|
||||
can.as_mut()
|
||||
.modify_filters()
|
||||
.enable_bank(0, Fifo::Fifo0, Mask32::accept_all());
|
||||
can.modify_filters().enable_bank(0, Fifo::Fifo0, Mask32::accept_all());
|
||||
|
||||
can.as_mut()
|
||||
.modify_config()
|
||||
can.modify_config()
|
||||
.set_bit_timing(can::util::NominalBitTiming {
|
||||
prescaler: NonZeroU16::new(2).unwrap(),
|
||||
seg1: NonZeroU8::new(13).unwrap(),
|
||||
seg2: NonZeroU8::new(2).unwrap(),
|
||||
sync_jump_width: NonZeroU8::new(1).unwrap(),
|
||||
}) // http://www.bittiming.can-wiki.info/
|
||||
.set_loopback(true)
|
||||
.enable();
|
||||
.set_loopback(true);
|
||||
|
||||
can.enable().await;
|
||||
|
||||
let (tx, mut rx) = can.split();
|
||||
|
||||
|
@ -8,9 +8,10 @@ mod common;
|
||||
use common::*;
|
||||
use embassy_executor::Spawner;
|
||||
use embassy_stm32::bind_interrupts;
|
||||
use embassy_stm32::can::bx::filter::Mask32;
|
||||
use embassy_stm32::can::bx::Fifo;
|
||||
use embassy_stm32::can::{Can, Rx0InterruptHandler, Rx1InterruptHandler, SceInterruptHandler, TxInterruptHandler};
|
||||
use embassy_stm32::can::filter::Mask32;
|
||||
use embassy_stm32::can::{
|
||||
Can, Fifo, Rx0InterruptHandler, Rx1InterruptHandler, SceInterruptHandler, TxInterruptHandler,
|
||||
};
|
||||
use embassy_stm32::gpio::{Input, Pull};
|
||||
use embassy_stm32::peripherals::CAN1;
|
||||
use embassy_time::Duration;
|
||||
@ -51,17 +52,15 @@ async fn main(_spawner: Spawner) {
|
||||
|
||||
info!("Configuring can...");
|
||||
|
||||
can.as_mut()
|
||||
.modify_filters()
|
||||
.enable_bank(0, Fifo::Fifo0, Mask32::accept_all());
|
||||
can.modify_filters().enable_bank(0, Fifo::Fifo0, Mask32::accept_all());
|
||||
|
||||
can.set_bitrate(1_000_000);
|
||||
can.as_mut()
|
||||
.modify_config()
|
||||
can.modify_config()
|
||||
.set_loopback(true) // Receive own frames
|
||||
.set_silent(true)
|
||||
// .set_bit_timing(0x001c0003)
|
||||
.enable();
|
||||
.set_bitrate(1_000_000);
|
||||
|
||||
can.enable().await;
|
||||
|
||||
info!("Can configured");
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user