mirror of
https://github.com/embassy-rs/embassy.git
synced 2024-11-22 06:42:32 +00:00
Merge pull request #2744 from cschuhen/feature/bxcan_use_fdcan_api
Synchronise the feature sets and API between BXCAN and FDCAN.
This commit is contained in:
commit
393de9e19f
@ -40,10 +40,37 @@ pub type Header = crate::can::frame::Header;
|
||||
/// Data for a CAN Frame
|
||||
pub type Data = crate::can::frame::ClassicData;
|
||||
|
||||
/// CAN Frame
|
||||
pub type Frame = crate::can::frame::ClassicFrame;
|
||||
|
||||
use crate::can::_version::Envelope;
|
||||
use crate::can::bx::filter::MasterFilters;
|
||||
use crate::can::enums::BusError;
|
||||
/// CAN Frame
|
||||
pub use crate::can::frame::Frame;
|
||||
use crate::pac::can::vals::Lec;
|
||||
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
pub(crate) enum RxFifo {
|
||||
Fifo0,
|
||||
Fifo1,
|
||||
}
|
||||
|
||||
trait IntoBusError {
|
||||
fn into_bus_err(self) -> Option<BusError>;
|
||||
}
|
||||
|
||||
impl IntoBusError for Lec {
|
||||
fn into_bus_err(self) -> Option<BusError> {
|
||||
match self {
|
||||
Lec::STUFF => Some(BusError::Stuff),
|
||||
Lec::FORM => Some(BusError::Form),
|
||||
Lec::ACK => Some(BusError::Acknowledge),
|
||||
Lec::BITRECESSIVE => Some(BusError::BitRecessive),
|
||||
Lec::BITDOMINANT => Some(BusError::BitDominant),
|
||||
Lec::CRC => Some(BusError::Crc),
|
||||
Lec::CUSTOM => Some(BusError::Software),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A bxCAN peripheral instance.
|
||||
///
|
||||
@ -233,229 +260,36 @@ impl PartialOrd for IdReg {
|
||||
}
|
||||
}
|
||||
|
||||
/// Configuration proxy returned by [`Can::modify_config`].
|
||||
#[must_use = "`CanConfig` leaves the peripheral in uninitialized state, call `CanConfig::enable` or explicitly drop the value"]
|
||||
pub struct CanConfig<'a, I: Instance> {
|
||||
can: &'a mut Can<I>,
|
||||
pub(crate) struct Registers {
|
||||
pub canregs: crate::pac::can::Can,
|
||||
}
|
||||
|
||||
impl<I: Instance> CanConfig<'_, I> {
|
||||
/// 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 {
|
||||
self.can.set_bit_timing(bt);
|
||||
self
|
||||
}
|
||||
|
||||
/// Enables or disables loopback mode: Internally connects the TX and RX
|
||||
/// signals together.
|
||||
pub fn set_loopback(self, enabled: bool) -> Self {
|
||||
self.can.canregs.btr().modify(|reg| reg.set_lbkm(enabled));
|
||||
self
|
||||
}
|
||||
|
||||
/// Enables or disables silent mode: Disconnects the TX signal from the pin.
|
||||
pub fn set_silent(self, enabled: bool) -> Self {
|
||||
let mode = match enabled {
|
||||
false => stm32_metapac::can::vals::Silm::NORMAL,
|
||||
true => stm32_metapac::can::vals::Silm::SILENT,
|
||||
};
|
||||
self.can.canregs.btr().modify(|reg| reg.set_silm(mode));
|
||||
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 {
|
||||
self.can.canregs.mcr().modify(|reg| reg.set_nart(enabled));
|
||||
self
|
||||
}
|
||||
|
||||
/// Leaves initialization mode and enables the peripheral.
|
||||
///
|
||||
/// To sync with the CAN bus, this will block until 11 consecutive recessive bits are detected
|
||||
/// on the bus.
|
||||
///
|
||||
/// If you want to finish configuration without enabling the peripheral, you can call
|
||||
/// [`CanConfig::leave_disabled`] or [`drop`] the [`CanConfig`] instead.
|
||||
pub fn enable(mut self) {
|
||||
self.leave_init_mode();
|
||||
|
||||
match nb::block!(self.can.enable_non_blocking()) {
|
||||
Ok(()) => {}
|
||||
Err(void) => match void {},
|
||||
}
|
||||
|
||||
// Don't run the destructor.
|
||||
mem::forget(self);
|
||||
}
|
||||
|
||||
/// Leaves initialization mode, but keeps the peripheral in sleep mode.
|
||||
///
|
||||
/// Before the [`Can`] instance can be used, you have to enable it by calling
|
||||
/// [`Can::enable_non_blocking`].
|
||||
pub fn leave_disabled(mut self) {
|
||||
self.leave_init_mode();
|
||||
}
|
||||
|
||||
/// Leaves initialization mode, enters sleep mode.
|
||||
fn leave_init_mode(&mut self) {
|
||||
self.can.canregs.mcr().modify(|reg| {
|
||||
reg.set_sleep(true);
|
||||
reg.set_inrq(false);
|
||||
});
|
||||
loop {
|
||||
let msr = self.can.canregs.msr().read();
|
||||
if msr.slak() && !msr.inak() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<I: Instance> Drop for CanConfig<'_, I> {
|
||||
#[inline]
|
||||
fn drop(&mut self) {
|
||||
self.leave_init_mode();
|
||||
}
|
||||
}
|
||||
|
||||
/// Builder returned by [`Can::builder`].
|
||||
#[must_use = "`CanBuilder` leaves the peripheral in uninitialized state, call `CanBuilder::enable` or `CanBuilder::leave_disabled`"]
|
||||
pub struct CanBuilder<I: Instance> {
|
||||
can: Can<I>,
|
||||
}
|
||||
|
||||
impl<I: Instance> CanBuilder<I> {
|
||||
/// 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(mut self, bt: crate::can::util::NominalBitTiming) -> Self {
|
||||
self.can.set_bit_timing(bt);
|
||||
self
|
||||
}
|
||||
/// Enables or disables loopback mode: Internally connects the TX and RX
|
||||
/// signals together.
|
||||
pub fn set_loopback(self, enabled: bool) -> Self {
|
||||
self.can.canregs.btr().modify(|reg| reg.set_lbkm(enabled));
|
||||
self
|
||||
}
|
||||
|
||||
/// Enables or disables silent mode: Disconnects the TX signal from the pin.
|
||||
pub fn set_silent(self, enabled: bool) -> Self {
|
||||
let mode = match enabled {
|
||||
false => stm32_metapac::can::vals::Silm::NORMAL,
|
||||
true => stm32_metapac::can::vals::Silm::SILENT,
|
||||
};
|
||||
self.can.canregs.btr().modify(|reg| reg.set_silm(mode));
|
||||
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 {
|
||||
self.can.canregs.mcr().modify(|reg| reg.set_nart(enabled));
|
||||
self
|
||||
}
|
||||
|
||||
/// Leaves initialization mode and enables the peripheral.
|
||||
///
|
||||
/// To sync with the CAN bus, this will block until 11 consecutive recessive bits are detected
|
||||
/// on the bus.
|
||||
///
|
||||
/// If you want to finish configuration without enabling the peripheral, you can call
|
||||
/// [`CanBuilder::leave_disabled`] instead.
|
||||
pub fn enable(mut self) -> Can<I> {
|
||||
self.leave_init_mode();
|
||||
|
||||
match nb::block!(self.can.enable_non_blocking()) {
|
||||
Ok(()) => self.can,
|
||||
Err(void) => match void {},
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the [`Can`] interface without enabling it.
|
||||
///
|
||||
/// This leaves initialization mode, but keeps the peripheral in sleep mode instead of enabling
|
||||
/// it.
|
||||
///
|
||||
/// Before the [`Can`] instance can be used, you have to enable it by calling
|
||||
/// [`Can::enable_non_blocking`].
|
||||
pub fn leave_disabled(mut self) -> Can<I> {
|
||||
self.leave_init_mode();
|
||||
self.can
|
||||
}
|
||||
|
||||
/// Leaves initialization mode, enters sleep mode.
|
||||
fn leave_init_mode(&mut self) {
|
||||
self.can.canregs.mcr().modify(|reg| {
|
||||
reg.set_sleep(true);
|
||||
reg.set_inrq(false);
|
||||
});
|
||||
loop {
|
||||
let msr = self.can.canregs.msr().read();
|
||||
if msr.slak() && !msr.inak() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Interface to a bxCAN peripheral.
|
||||
pub struct Can<I: Instance> {
|
||||
instance: I,
|
||||
canregs: crate::pac::can::Can,
|
||||
}
|
||||
|
||||
impl<I> Can<I>
|
||||
where
|
||||
I: Instance,
|
||||
{
|
||||
/// Creates a [`CanBuilder`] for constructing a CAN interface.
|
||||
pub fn builder(instance: I, canregs: crate::pac::can::Can) -> CanBuilder<I> {
|
||||
let can_builder = CanBuilder {
|
||||
can: Can { instance, canregs },
|
||||
};
|
||||
|
||||
canregs.mcr().modify(|reg| {
|
||||
impl Registers {
|
||||
fn enter_init_mode(&mut self) {
|
||||
self.canregs.mcr().modify(|reg| {
|
||||
reg.set_sleep(false);
|
||||
reg.set_inrq(true);
|
||||
});
|
||||
loop {
|
||||
let msr = canregs.msr().read();
|
||||
let msr = self.canregs.msr().read();
|
||||
if !msr.slak() && msr.inak() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
can_builder
|
||||
// Leaves initialization mode, enters sleep mode.
|
||||
fn leave_init_mode(&mut self) {
|
||||
self.canregs.mcr().modify(|reg| {
|
||||
reg.set_sleep(true);
|
||||
reg.set_inrq(false);
|
||||
});
|
||||
loop {
|
||||
let msr = self.canregs.msr().read();
|
||||
if msr.slak() && !msr.inak() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn set_bit_timing(&mut self, bt: crate::can::util::NominalBitTiming) {
|
||||
@ -471,38 +305,29 @@ where
|
||||
});
|
||||
}
|
||||
|
||||
/// Returns a reference to the peripheral instance.
|
||||
///
|
||||
/// This allows accessing HAL-specific data stored in the instance type.
|
||||
pub fn instance(&mut self) -> &mut I {
|
||||
&mut self.instance
|
||||
/// 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.canregs.btr().modify(|reg| reg.set_silm(mode));
|
||||
}
|
||||
|
||||
/// Disables the CAN interface and returns back the raw peripheral it was created from.
|
||||
/// Enables or disables automatic retransmission of messages.
|
||||
///
|
||||
/// The peripheral is disabled by setting `RESET` in `CAN_MCR`, which causes the peripheral to
|
||||
/// enter sleep mode.
|
||||
pub fn free(self) -> I {
|
||||
self.canregs.mcr().write(|reg| reg.set_reset(true));
|
||||
self.instance
|
||||
/// 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.canregs.mcr().modify(|reg| reg.set_nart(enabled));
|
||||
}
|
||||
|
||||
/// Configure bit timings and silent/loop-back mode.
|
||||
///
|
||||
/// Calling this method will enter initialization mode.
|
||||
pub fn modify_config(&mut self) -> CanConfig<'_, I> {
|
||||
self.canregs.mcr().modify(|reg| {
|
||||
reg.set_sleep(false);
|
||||
reg.set_inrq(true);
|
||||
});
|
||||
loop {
|
||||
let msr = self.canregs.msr().read();
|
||||
if !msr.slak() && msr.inak() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
CanConfig { can: self }
|
||||
/// Enables or disables loopback mode: Internally connects the TX and RX
|
||||
/// signals together.
|
||||
pub fn set_loopback(&self, enabled: bool) {
|
||||
self.canregs.btr().modify(|reg| reg.set_lbkm(enabled));
|
||||
}
|
||||
|
||||
/// Configures the automatic wake-up feature.
|
||||
@ -512,6 +337,7 @@ where
|
||||
/// 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.canregs.mcr().modify(|reg| reg.set_awum(enabled));
|
||||
}
|
||||
@ -540,6 +366,7 @@ where
|
||||
/// 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.canregs.mcr().modify(|reg| {
|
||||
reg.set_sleep(true);
|
||||
@ -553,10 +380,19 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
/// Disables the CAN interface.
|
||||
///
|
||||
/// The peripheral is disabled by setting `RESET` in `CAN_MCR`, which causes the peripheral to
|
||||
/// enter sleep mode.
|
||||
pub fn reset(&self) {
|
||||
self.canregs.mcr().write(|reg| reg.set_reset(true));
|
||||
}
|
||||
|
||||
/// 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.canregs.mcr().modify(|reg| {
|
||||
reg.set_sleep(false);
|
||||
@ -570,113 +406,18 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
/// Puts a CAN frame in a free 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> {
|
||||
// Safety: We have a `&mut self` and have unique access to the peripheral.
|
||||
unsafe { Tx::<I>::conjure(self.canregs).transmit(frame) }
|
||||
}
|
||||
|
||||
/// Returns `true` if no frame is pending for transmission.
|
||||
pub fn is_transmitter_idle(&self) -> bool {
|
||||
// Safety: Read-only operation.
|
||||
unsafe { Tx::<I>::conjure(self.canregs).is_idle() }
|
||||
}
|
||||
|
||||
/// 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 {
|
||||
// Safety: We have a `&mut self` and have unique access to the peripheral.
|
||||
unsafe { Tx::<I>::conjure(self.canregs).abort(mailbox) }
|
||||
}
|
||||
|
||||
/// Returns a received frame if available.
|
||||
///
|
||||
/// This will first check FIFO 0 for a message or error. If none are available, FIFO 1 is
|
||||
/// checked.
|
||||
///
|
||||
/// Returns `Err` when a frame was lost due to buffer overrun.
|
||||
pub fn receive(&mut self) -> nb::Result<Frame, OverrunError> {
|
||||
// Safety: We have a `&mut self` and have unique access to the peripheral.
|
||||
let mut rx0 = unsafe { Rx0::<I>::conjure(self.canregs) };
|
||||
let mut rx1 = unsafe { Rx1::<I>::conjure(self.canregs) };
|
||||
|
||||
match rx0.receive() {
|
||||
Err(nb::Error::WouldBlock) => rx1.receive(),
|
||||
result => result,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a reference to the RX FIFO 0.
|
||||
pub fn rx0(&mut self) -> Rx0<I> {
|
||||
// Safety: We take `&mut self` and the return value lifetimes are tied to `self`'s lifetime.
|
||||
unsafe { Rx0::conjure(self.canregs) }
|
||||
}
|
||||
|
||||
/// Returns a reference to the RX FIFO 1.
|
||||
pub fn rx1(&mut self) -> Rx1<I> {
|
||||
// Safety: We take `&mut self` and the return value lifetimes are tied to `self`'s lifetime.
|
||||
unsafe { Rx1::conjure(self.canregs) }
|
||||
}
|
||||
|
||||
pub(crate) fn split_by_ref(&mut self) -> (Tx<I>, Rx0<I>, Rx1<I>) {
|
||||
// Safety: We take `&mut self` and the return value lifetimes are tied to `self`'s lifetime.
|
||||
let tx = unsafe { Tx::conjure(self.canregs) };
|
||||
let rx0 = unsafe { Rx0::conjure(self.canregs) };
|
||||
let rx1 = unsafe { Rx1::conjure(self.canregs) };
|
||||
(tx, rx0, rx1)
|
||||
}
|
||||
|
||||
/// Consumes this `Can` instance and splits it into transmitting and receiving halves.
|
||||
pub fn split(self) -> (Tx<I>, Rx0<I>, Rx1<I>) {
|
||||
// Safety: `Self` is not `Copy` and is destroyed by moving it into this method.
|
||||
unsafe {
|
||||
(
|
||||
Tx::conjure(self.canregs),
|
||||
Rx0::conjure(self.canregs),
|
||||
Rx1::conjure(self.canregs),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<I: FilterOwner> Can<I> {
|
||||
/// 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<'_, I> {
|
||||
unsafe { MasterFilters::new(self.canregs) }
|
||||
}
|
||||
}
|
||||
|
||||
/// Interface to the CAN transmitter part.
|
||||
pub struct Tx<I> {
|
||||
_can: PhantomData<I>,
|
||||
canregs: crate::pac::can::Can,
|
||||
}
|
||||
|
||||
impl<I> Tx<I>
|
||||
where
|
||||
I: Instance,
|
||||
{
|
||||
unsafe fn conjure(canregs: crate::pac::can::Can) -> Self {
|
||||
Self {
|
||||
_can: PhantomData,
|
||||
canregs,
|
||||
pub fn curr_error(&self) -> Option<BusError> {
|
||||
let err = { self.canregs.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 let Some(err) = err.lec().into_bus_err() {
|
||||
return Some(err);
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
/// Puts a CAN frame in a transmit mailbox for transmission on the bus.
|
||||
@ -842,89 +583,387 @@ where
|
||||
reg.set_rqcp(2, true);
|
||||
});
|
||||
}
|
||||
|
||||
pub fn receive_frame_available(&self) -> bool {
|
||||
if self.canregs.rfr(0).read().fmp() != 0 {
|
||||
true
|
||||
} else if self.canregs.rfr(1).read().fmp() != 0 {
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
pub fn receive_fifo(&self, fifo: crate::can::_version::bx::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 {
|
||||
crate::can::_version::bx::RxFifo::Fifo0 => 0usize,
|
||||
crate::can::_version::bx::RxFifo::Fifo1 => 1usize,
|
||||
};
|
||||
let rfr = self.canregs.rfr(fifo_idx);
|
||||
let fifo = self.canregs.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)
|
||||
}
|
||||
}
|
||||
|
||||
/// Interface to receiver FIFO 0.
|
||||
pub struct Rx0<I> {
|
||||
_can: PhantomData<I>,
|
||||
/// Configuration proxy returned by [`Can::modify_config`].
|
||||
#[must_use = "`CanConfig` leaves the peripheral in uninitialized state, call `CanConfig::enable` or explicitly drop the value"]
|
||||
pub struct CanConfig<'a, I: Instance> {
|
||||
can: &'a mut Can<I>,
|
||||
}
|
||||
|
||||
impl<I: Instance> CanConfig<'_, I> {
|
||||
/// 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 {
|
||||
self.can.registers.set_bit_timing(bt);
|
||||
self
|
||||
}
|
||||
|
||||
/// Enables or disables loopback mode: Internally connects the TX and RX
|
||||
/// signals together.
|
||||
pub fn set_loopback(self, enabled: bool) -> Self {
|
||||
self.can.registers.set_loopback(enabled);
|
||||
self
|
||||
}
|
||||
|
||||
/// Enables or disables silent mode: Disconnects the TX signal from the pin.
|
||||
pub fn set_silent(self, enabled: bool) -> Self {
|
||||
self.can.registers.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 {
|
||||
self.can.registers.set_automatic_retransmit(enabled);
|
||||
self
|
||||
}
|
||||
|
||||
/// Leaves initialization mode and enables the peripheral.
|
||||
///
|
||||
/// To sync with the CAN bus, this will block until 11 consecutive recessive bits are detected
|
||||
/// on the bus.
|
||||
///
|
||||
/// If you want to finish configuration without enabling the peripheral, you can call
|
||||
/// [`CanConfig::leave_disabled`] or [`drop`] the [`CanConfig`] instead.
|
||||
pub fn enable(self) {
|
||||
self.can.registers.leave_init_mode();
|
||||
|
||||
match nb::block!(self.can.registers.enable_non_blocking()) {
|
||||
Ok(()) => {}
|
||||
Err(void) => match void {},
|
||||
}
|
||||
|
||||
// Don't run the destructor.
|
||||
mem::forget(self);
|
||||
}
|
||||
|
||||
/// Leaves initialization mode, but keeps the peripheral in sleep mode.
|
||||
///
|
||||
/// Before the [`Can`] instance can be used, you have to enable it by calling
|
||||
/// [`Can::enable_non_blocking`].
|
||||
pub fn leave_disabled(self) {
|
||||
self.can.registers.leave_init_mode();
|
||||
}
|
||||
}
|
||||
|
||||
impl<I: Instance> Drop for CanConfig<'_, I> {
|
||||
#[inline]
|
||||
fn drop(&mut self) {
|
||||
self.can.registers.leave_init_mode();
|
||||
}
|
||||
}
|
||||
|
||||
/// Builder returned by [`Can::builder`].
|
||||
#[must_use = "`CanBuilder` leaves the peripheral in uninitialized state, call `CanBuilder::enable` or `CanBuilder::leave_disabled`"]
|
||||
pub struct CanBuilder<I: Instance> {
|
||||
can: Can<I>,
|
||||
}
|
||||
|
||||
impl<I: Instance> CanBuilder<I> {
|
||||
/// 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(mut self, bt: crate::can::util::NominalBitTiming) -> Self {
|
||||
self.can.registers.set_bit_timing(bt);
|
||||
self
|
||||
}
|
||||
/// Enables or disables loopback mode: Internally connects the TX and RX
|
||||
/// signals together.
|
||||
pub fn set_loopback(self, enabled: bool) -> Self {
|
||||
self.can.registers.set_loopback(enabled);
|
||||
self
|
||||
}
|
||||
|
||||
/// Enables or disables silent mode: Disconnects the TX signal from the pin.
|
||||
pub fn set_silent(self, enabled: bool) -> Self {
|
||||
self.can.registers.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 {
|
||||
self.can.registers.set_automatic_retransmit(enabled);
|
||||
self
|
||||
}
|
||||
|
||||
/// Leaves initialization mode and enables the peripheral.
|
||||
///
|
||||
/// To sync with the CAN bus, this will block until 11 consecutive recessive bits are detected
|
||||
/// on the bus.
|
||||
///
|
||||
/// If you want to finish configuration without enabling the peripheral, you can call
|
||||
/// [`CanBuilder::leave_disabled`] instead.
|
||||
pub fn enable(mut self) -> Can<I> {
|
||||
self.leave_init_mode();
|
||||
|
||||
match nb::block!(self.can.registers.enable_non_blocking()) {
|
||||
Ok(()) => self.can,
|
||||
Err(void) => match void {},
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the [`Can`] interface without enabling it.
|
||||
///
|
||||
/// This leaves initialization mode, but keeps the peripheral in sleep mode instead of enabling
|
||||
/// it.
|
||||
///
|
||||
/// Before the [`Can`] instance can be used, you have to enable it by calling
|
||||
/// [`Can::enable_non_blocking`].
|
||||
pub fn leave_disabled(mut self) -> Can<I> {
|
||||
self.leave_init_mode();
|
||||
self.can
|
||||
}
|
||||
|
||||
/// Leaves initialization mode, enters sleep mode.
|
||||
fn leave_init_mode(&mut self) {
|
||||
self.can.registers.leave_init_mode();
|
||||
}
|
||||
}
|
||||
|
||||
/// Interface to a bxCAN peripheral.
|
||||
pub struct Can<I: Instance> {
|
||||
instance: I,
|
||||
canregs: crate::pac::can::Can,
|
||||
pub(crate) registers: Registers,
|
||||
}
|
||||
|
||||
impl<I> Rx0<I>
|
||||
impl<I> Can<I>
|
||||
where
|
||||
I: Instance,
|
||||
{
|
||||
/// Creates a [`CanBuilder`] for constructing a CAN interface.
|
||||
pub fn builder(instance: I, canregs: crate::pac::can::Can) -> CanBuilder<I> {
|
||||
let mut can_builder = CanBuilder {
|
||||
can: Can {
|
||||
instance,
|
||||
canregs,
|
||||
registers: Registers { canregs },
|
||||
},
|
||||
};
|
||||
|
||||
can_builder.can.registers.enter_init_mode();
|
||||
|
||||
can_builder
|
||||
}
|
||||
|
||||
/// Disables the CAN interface and returns back the raw peripheral it was created from.
|
||||
///
|
||||
/// The peripheral is disabled by setting `RESET` in `CAN_MCR`, which causes the peripheral to
|
||||
/// enter sleep mode.
|
||||
pub fn free(self) -> I {
|
||||
self.registers.reset();
|
||||
self.instance
|
||||
}
|
||||
|
||||
/// Configure bit timings and silent/loop-back mode.
|
||||
///
|
||||
/// Calling this method will enter initialization mode.
|
||||
pub fn modify_config(&mut self) -> CanConfig<'_, I> {
|
||||
self.registers.enter_init_mode();
|
||||
|
||||
CanConfig { can: self }
|
||||
}
|
||||
|
||||
/// Puts a CAN frame in a free 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> {
|
||||
// Safety: We have a `&mut self` and have unique access to the peripheral.
|
||||
unsafe { Tx::<I>::conjure(self.canregs).transmit(frame) }
|
||||
}
|
||||
|
||||
/// Returns `true` if no frame is pending for transmission.
|
||||
pub fn is_transmitter_idle(&self) -> bool {
|
||||
// Safety: Read-only operation.
|
||||
unsafe { Tx::<I>::conjure(self.canregs).is_idle() }
|
||||
}
|
||||
|
||||
/// 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 {
|
||||
// Safety: We have a `&mut self` and have unique access to the peripheral.
|
||||
unsafe { Tx::<I>::conjure(self.canregs).abort(mailbox) }
|
||||
}
|
||||
|
||||
pub(crate) fn split_by_ref(&mut self) -> (Tx<I>, Rx<I>) {
|
||||
// Safety: We take `&mut self` and the return value lifetimes are tied to `self`'s lifetime.
|
||||
let tx = unsafe { Tx::conjure(self.canregs) };
|
||||
let rx0 = unsafe { Rx::conjure() };
|
||||
(tx, rx0)
|
||||
}
|
||||
}
|
||||
|
||||
impl<I: FilterOwner> Can<I> {
|
||||
/// 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<'_, I> {
|
||||
unsafe { MasterFilters::new(self.canregs) }
|
||||
}
|
||||
}
|
||||
|
||||
/// Marker for Tx half
|
||||
pub struct Tx<I> {
|
||||
_can: PhantomData<I>,
|
||||
pub(crate) registers: Registers,
|
||||
}
|
||||
|
||||
impl<I> Tx<I>
|
||||
where
|
||||
I: Instance,
|
||||
{
|
||||
unsafe fn conjure(canregs: crate::pac::can::Can) -> Self {
|
||||
Self {
|
||||
_can: PhantomData,
|
||||
canregs,
|
||||
registers: Registers { canregs }, //canregs,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a received frame if available.
|
||||
/// Puts a CAN frame in a transmit mailbox for transmission on the bus.
|
||||
///
|
||||
/// Returns `Err` when a frame was lost due to buffer overrun.
|
||||
pub fn receive(&mut self) -> nb::Result<Frame, OverrunError> {
|
||||
receive_fifo(self.canregs, 0)
|
||||
/// 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> {
|
||||
self.registers.transmit(frame)
|
||||
}
|
||||
|
||||
/// 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 {
|
||||
self.registers.abort(mailbox)
|
||||
}
|
||||
|
||||
/// Returns `true` if no frame is pending for transmission.
|
||||
pub fn is_idle(&self) -> bool {
|
||||
self.registers.is_idle()
|
||||
}
|
||||
|
||||
/// Clears the request complete flag for all mailboxes.
|
||||
pub fn clear_interrupt_flags(&mut self) {
|
||||
self.registers.clear_interrupt_flags()
|
||||
}
|
||||
}
|
||||
|
||||
/// Interface to receiver FIFO 1.
|
||||
pub struct Rx1<I> {
|
||||
/// Marker for Rx half
|
||||
pub struct Rx<I> {
|
||||
_can: PhantomData<I>,
|
||||
canregs: crate::pac::can::Can,
|
||||
}
|
||||
|
||||
impl<I> Rx1<I>
|
||||
impl<I> Rx<I>
|
||||
where
|
||||
I: Instance,
|
||||
{
|
||||
unsafe fn conjure(canregs: crate::pac::can::Can) -> Self {
|
||||
Self {
|
||||
_can: PhantomData,
|
||||
canregs,
|
||||
unsafe fn conjure() -> Self {
|
||||
Self { _can: PhantomData }
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a received frame if available.
|
||||
///
|
||||
/// Returns `Err` when a frame was lost due to buffer overrun.
|
||||
pub fn receive(&mut self) -> nb::Result<Frame, OverrunError> {
|
||||
receive_fifo(self.canregs, 1)
|
||||
}
|
||||
}
|
||||
|
||||
fn receive_fifo(canregs: crate::pac::can::Can, fifo_nr: usize) -> nb::Result<Frame, OverrunError> {
|
||||
assert!(fifo_nr < 2);
|
||||
let rfr = canregs.rfr(fifo_nr);
|
||||
let rx = canregs.rx(fifo_nr);
|
||||
|
||||
//let rfr = &can.rfr[fifo_nr];
|
||||
//let rx = &can.rx[fifo_nr];
|
||||
|
||||
// Check if a frame is available in the mailbox.
|
||||
let rfr_read = rfr.read();
|
||||
if rfr_read.fmp() == 0 {
|
||||
return Err(nb::Error::WouldBlock);
|
||||
}
|
||||
|
||||
// Check for RX FIFO overrun.
|
||||
if rfr_read.fovr() {
|
||||
rfr.write(|w| w.set_fovr(true));
|
||||
return Err(nb::Error::Other(OverrunError { _priv: () }));
|
||||
}
|
||||
|
||||
// Read the frame.
|
||||
let id = IdReg(rx.rir().read().0);
|
||||
let mut data = [0xff; 8];
|
||||
data[0..4].copy_from_slice(&rx.rdlr().read().0.to_ne_bytes());
|
||||
data[4..8].copy_from_slice(&rx.rdhr().read().0.to_ne_bytes());
|
||||
let len = rx.rdtr().read().dlc();
|
||||
|
||||
// Release the mailbox.
|
||||
rfr.write(|w| w.set_rfom(true));
|
||||
|
||||
Ok(Frame::new(Header::new(id.id(), len, id.rtr()), &data).unwrap())
|
||||
}
|
||||
|
||||
/// Identifies one of the two receive FIFOs.
|
||||
|
@ -10,31 +10,19 @@ use embassy_hal_internal::{into_ref, PeripheralRef};
|
||||
use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
|
||||
use embassy_sync::channel::Channel;
|
||||
use embassy_sync::waitqueue::AtomicWaker;
|
||||
use futures::FutureExt;
|
||||
|
||||
use crate::gpio::AFType;
|
||||
use crate::interrupt::typelevel::Interrupt;
|
||||
use crate::pac::can::vals::{Ide, Lec};
|
||||
use crate::rcc::RccPeripheral;
|
||||
use crate::{interrupt, peripherals, Peripheral};
|
||||
|
||||
pub mod enums;
|
||||
use enums::*;
|
||||
pub mod frame;
|
||||
pub mod util;
|
||||
pub use frame::Envelope;
|
||||
|
||||
/// Contains CAN frame and additional metadata.
|
||||
///
|
||||
/// Timestamp is available if `time` feature is enabled.
|
||||
#[derive(Debug, Clone)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub struct Envelope {
|
||||
/// Reception time.
|
||||
#[cfg(feature = "time")]
|
||||
pub ts: embassy_time::Instant,
|
||||
/// The actual CAN frame.
|
||||
pub frame: Frame,
|
||||
}
|
||||
mod common;
|
||||
pub use self::common::{BufferedCanReceiver, BufferedCanSender};
|
||||
|
||||
/// Interrupt handler.
|
||||
pub struct TxInterruptHandler<T: Instance> {
|
||||
@ -48,8 +36,7 @@ impl<T: Instance> interrupt::typelevel::Handler<T::TXInterrupt> for TxInterruptH
|
||||
v.set_rqcp(1, true);
|
||||
v.set_rqcp(2, true);
|
||||
});
|
||||
|
||||
T::state().tx_waker.wake();
|
||||
T::state().tx_mode.on_interrupt::<T>();
|
||||
}
|
||||
}
|
||||
|
||||
@ -60,8 +47,7 @@ pub struct Rx0InterruptHandler<T: Instance> {
|
||||
|
||||
impl<T: Instance> interrupt::typelevel::Handler<T::RX0Interrupt> for Rx0InterruptHandler<T> {
|
||||
unsafe fn on_interrupt() {
|
||||
// info!("rx0 irq");
|
||||
Can::<T>::receive_fifo(RxFifo::Fifo0);
|
||||
T::state().rx_mode.on_interrupt::<T>(RxFifo::Fifo0);
|
||||
}
|
||||
}
|
||||
|
||||
@ -72,8 +58,7 @@ pub struct Rx1InterruptHandler<T: Instance> {
|
||||
|
||||
impl<T: Instance> interrupt::typelevel::Handler<T::RX1Interrupt> for Rx1InterruptHandler<T> {
|
||||
unsafe fn on_interrupt() {
|
||||
// info!("rx1 irq");
|
||||
Can::<T>::receive_fifo(RxFifo::Fifo1);
|
||||
T::state().rx_mode.on_interrupt::<T>(RxFifo::Fifo1);
|
||||
}
|
||||
}
|
||||
|
||||
@ -100,16 +85,6 @@ pub struct Can<'d, T: Instance> {
|
||||
can: crate::can::bx::Can<BxcanInstance<'d, T>>,
|
||||
}
|
||||
|
||||
/// Error returned by `try_read`
|
||||
#[derive(Debug)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum TryReadError {
|
||||
/// Bus error
|
||||
BusError(BusError),
|
||||
/// Receive buffer is empty
|
||||
Empty,
|
||||
}
|
||||
|
||||
/// Error returned by `try_write`
|
||||
#[derive(Debug)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
@ -185,7 +160,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.enable_non_blocking().is_err() {
|
||||
while self.registers.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;
|
||||
@ -227,77 +202,40 @@ impl<'d, T: Instance> Can<'d, T> {
|
||||
///
|
||||
/// Returns a tuple of the time the message was received and the message frame
|
||||
pub async fn read(&mut self) -> Result<Envelope, BusError> {
|
||||
self.split().1.read().await
|
||||
T::state().rx_mode.read::<T>().await
|
||||
}
|
||||
|
||||
/// Attempts to read a CAN frame without blocking.
|
||||
///
|
||||
/// Returns [Err(TryReadError::Empty)] if there are no frames in the rx queue.
|
||||
pub fn try_read(&mut self) -> Result<Envelope, TryReadError> {
|
||||
self.split().1.try_read()
|
||||
T::state().rx_mode.try_read::<T>()
|
||||
}
|
||||
|
||||
/// Waits while receive queue is empty.
|
||||
pub async fn wait_not_empty(&mut self) {
|
||||
self.split().1.wait_not_empty().await
|
||||
}
|
||||
|
||||
unsafe fn receive_fifo(fifo: RxFifo) {
|
||||
// Generate timestamp as early as possible
|
||||
#[cfg(feature = "time")]
|
||||
let ts = embassy_time::Instant::now();
|
||||
|
||||
let state = T::state();
|
||||
let regs = T::regs();
|
||||
let fifo_idx = match fifo {
|
||||
RxFifo::Fifo0 => 0usize,
|
||||
RxFifo::Fifo1 => 1usize,
|
||||
};
|
||||
let rfr = regs.rfr(fifo_idx);
|
||||
let fifo = regs.rx(fifo_idx);
|
||||
|
||||
loop {
|
||||
// If there are no pending messages, there is nothing to do
|
||||
if rfr.read().fmp() == 0 {
|
||||
return;
|
||||
}
|
||||
|
||||
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 data_len = fifo.rdtr().read().dlc();
|
||||
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, false), &data).unwrap();
|
||||
let envelope = Envelope {
|
||||
#[cfg(feature = "time")]
|
||||
ts,
|
||||
frame,
|
||||
};
|
||||
|
||||
rfr.modify(|v| v.set_rfom(true));
|
||||
|
||||
/*
|
||||
NOTE: consensus was reached that if rx_queue is full, packets should be dropped
|
||||
*/
|
||||
let _ = state.rx_queue.try_send(envelope);
|
||||
}
|
||||
T::state().rx_mode.wait_not_empty::<T>().await
|
||||
}
|
||||
|
||||
/// Split the CAN driver into transmit and receive halves.
|
||||
///
|
||||
/// Useful for doing separate transmit/receive tasks.
|
||||
pub fn split<'c>(&'c mut self) -> (CanTx<'d, T>, CanRx<'d, T>) {
|
||||
let (tx, rx0, rx1) = self.can.split_by_ref();
|
||||
(CanTx { tx }, CanRx { rx0, rx1 })
|
||||
let (tx, rx) = self.can.split_by_ref();
|
||||
(CanTx { tx }, CanRx { rx })
|
||||
}
|
||||
|
||||
/// Return a buffered instance of driver. User must supply Buffers
|
||||
pub fn buffered<'c, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize>(
|
||||
&'c mut self,
|
||||
txb: &'static mut TxBuf<TX_BUF_SIZE>,
|
||||
rxb: &'static mut RxBuf<RX_BUF_SIZE>,
|
||||
) -> BufferedCan<'d, T, TX_BUF_SIZE, RX_BUF_SIZE> {
|
||||
let (tx, rx) = self.split();
|
||||
BufferedCan {
|
||||
tx: tx.buffered(txb),
|
||||
rx: rx.buffered(rxb),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -308,6 +246,46 @@ impl<'d, T: Instance> AsMut<crate::can::bx::Can<BxcanInstance<'d, T>>> for Can<'
|
||||
}
|
||||
}
|
||||
|
||||
/// Buffered CAN driver.
|
||||
pub struct BufferedCan<'d, T: Instance, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize> {
|
||||
tx: BufferedCanTx<'d, T, TX_BUF_SIZE>,
|
||||
rx: BufferedCanRx<'d, T, RX_BUF_SIZE>,
|
||||
}
|
||||
|
||||
impl<'d, T: Instance, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize> BufferedCan<'d, T, TX_BUF_SIZE, RX_BUF_SIZE> {
|
||||
/// Async write frame to TX buffer.
|
||||
pub async fn write(&mut self, frame: &Frame) {
|
||||
self.tx.write(frame).await
|
||||
}
|
||||
|
||||
/// Returns a sender that can be used for sending CAN frames.
|
||||
pub fn writer(&self) -> BufferedCanSender {
|
||||
self.tx.writer()
|
||||
}
|
||||
|
||||
/// Async read frame from RX buffer.
|
||||
pub async fn read(&mut self) -> Result<Envelope, BusError> {
|
||||
self.rx.read().await
|
||||
}
|
||||
|
||||
/// Attempts to read a CAN frame without blocking.
|
||||
///
|
||||
/// Returns [Err(TryReadError::Empty)] if there are no frames in the rx queue.
|
||||
pub fn try_read(&mut self) -> Result<Envelope, TryReadError> {
|
||||
self.rx.try_read()
|
||||
}
|
||||
|
||||
/// Waits while receive queue is empty.
|
||||
pub async fn wait_not_empty(&mut self) {
|
||||
self.rx.wait_not_empty().await
|
||||
}
|
||||
|
||||
/// Returns a receiver that can be used for receiving CAN frames. Note, each CAN frame will only be received by one receiver.
|
||||
pub fn reader(&self) -> BufferedCanReceiver {
|
||||
self.rx.reader()
|
||||
}
|
||||
}
|
||||
|
||||
/// CAN driver, transmit half.
|
||||
pub struct CanTx<'d, T: Instance> {
|
||||
tx: crate::can::bx::Tx<BxcanInstance<'d, T>>,
|
||||
@ -319,7 +297,7 @@ impl<'d, T: Instance> CanTx<'d, T> {
|
||||
/// 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 {
|
||||
poll_fn(|cx| {
|
||||
T::state().tx_waker.register(cx.waker());
|
||||
T::state().tx_mode.register(cx.waker());
|
||||
if let Ok(status) = self.tx.transmit(frame) {
|
||||
return Poll::Ready(status);
|
||||
}
|
||||
@ -338,7 +316,7 @@ impl<'d, T: Instance> CanTx<'d, T> {
|
||||
|
||||
async fn flush_inner(mb: crate::can::bx::Mailbox) {
|
||||
poll_fn(|cx| {
|
||||
T::state().tx_waker.register(cx.waker());
|
||||
T::state().tx_mode.register(cx.waker());
|
||||
if T::regs().tsr().read().tme(mb.index()) {
|
||||
return Poll::Ready(());
|
||||
}
|
||||
@ -355,7 +333,7 @@ impl<'d, T: Instance> CanTx<'d, T> {
|
||||
|
||||
async fn flush_any_inner() {
|
||||
poll_fn(|cx| {
|
||||
T::state().tx_waker.register(cx.waker());
|
||||
T::state().tx_mode.register(cx.waker());
|
||||
|
||||
let tsr = T::regs().tsr().read();
|
||||
if tsr.tme(crate::can::bx::Mailbox::Mailbox0.index())
|
||||
@ -377,7 +355,7 @@ impl<'d, T: Instance> CanTx<'d, T> {
|
||||
|
||||
async fn flush_all_inner() {
|
||||
poll_fn(|cx| {
|
||||
T::state().tx_waker.register(cx.waker());
|
||||
T::state().tx_mode.register(cx.waker());
|
||||
|
||||
let tsr = T::regs().tsr().read();
|
||||
if tsr.tme(crate::can::bx::Mailbox::Mailbox0.index())
|
||||
@ -396,13 +374,68 @@ impl<'d, T: Instance> CanTx<'d, T> {
|
||||
pub async fn flush_all(&self) {
|
||||
Self::flush_all_inner().await
|
||||
}
|
||||
|
||||
/// 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)
|
||||
}
|
||||
}
|
||||
|
||||
/// User supplied buffer for TX buffering
|
||||
pub type TxBuf<const BUF_SIZE: usize> = Channel<CriticalSectionRawMutex, Frame, BUF_SIZE>;
|
||||
|
||||
/// 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_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 {
|
||||
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 {
|
||||
tx_receiver: self.tx_buf.receiver().into(),
|
||||
};
|
||||
T::mut_state().tx_mode = TxMode::Buffered(tx_inner);
|
||||
});
|
||||
self
|
||||
}
|
||||
|
||||
/// Async write frame to TX buffer.
|
||||
pub async fn write(&mut self, frame: &Frame) {
|
||||
self.tx_buf.send(*frame).await;
|
||||
T::TXInterrupt::pend(); // Wake for Tx
|
||||
}
|
||||
|
||||
/// Returns a sender that can be used for sending CAN frames.
|
||||
pub fn writer(&self) -> BufferedCanSender {
|
||||
BufferedCanSender {
|
||||
tx_buf: self.tx_buf.sender().into(),
|
||||
waker: T::TXInterrupt::pend,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance, const TX_BUF_SIZE: usize> Drop for BufferedCanTx<'d, T, TX_BUF_SIZE> {
|
||||
fn drop(&mut self) {
|
||||
critical_section::with(|_| unsafe {
|
||||
T::mut_state().tx_mode = TxMode::NonBuffered(embassy_sync::waitqueue::AtomicWaker::new());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// CAN driver, receive half.
|
||||
#[allow(dead_code)]
|
||||
pub struct CanRx<'d, T: Instance> {
|
||||
rx0: crate::can::bx::Rx0<BxcanInstance<'d, T>>,
|
||||
rx1: crate::can::bx::Rx1<BxcanInstance<'d, T>>,
|
||||
rx: crate::can::bx::Rx<BxcanInstance<'d, T>>,
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> CanRx<'d, T> {
|
||||
@ -412,59 +445,107 @@ impl<'d, T: Instance> CanRx<'d, T> {
|
||||
///
|
||||
/// Returns a tuple of the time the message was received and the message frame
|
||||
pub async fn read(&mut self) -> Result<Envelope, BusError> {
|
||||
poll_fn(|cx| {
|
||||
T::state().err_waker.register(cx.waker());
|
||||
if let Poll::Ready(envelope) = T::state().rx_queue.receive().poll_unpin(cx) {
|
||||
return Poll::Ready(Ok(envelope));
|
||||
} else if let Some(err) = self.curr_error() {
|
||||
return Poll::Ready(Err(err));
|
||||
}
|
||||
|
||||
Poll::Pending
|
||||
})
|
||||
.await
|
||||
T::state().rx_mode.read::<T>().await
|
||||
}
|
||||
|
||||
/// Attempts to read a CAN frame without blocking.
|
||||
///
|
||||
/// Returns [Err(TryReadError::Empty)] if there are no frames in the rx queue.
|
||||
pub fn try_read(&mut self) -> Result<Envelope, TryReadError> {
|
||||
if let Ok(envelope) = T::state().rx_queue.try_receive() {
|
||||
return Ok(envelope);
|
||||
}
|
||||
|
||||
if let Some(err) = self.curr_error() {
|
||||
return Err(TryReadError::BusError(err));
|
||||
}
|
||||
|
||||
Err(TryReadError::Empty)
|
||||
T::state().rx_mode.try_read::<T>()
|
||||
}
|
||||
|
||||
/// Waits while receive queue is empty.
|
||||
pub async fn wait_not_empty(&mut self) {
|
||||
poll_fn(|cx| T::state().rx_queue.poll_ready_to_receive(cx)).await
|
||||
T::state().rx_mode.wait_not_empty::<T>().await
|
||||
}
|
||||
|
||||
fn curr_error(&self) -> Option<BusError> {
|
||||
let err = { T::regs().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 let Some(err) = err.lec().into_bus_err() {
|
||||
return Some(err);
|
||||
}
|
||||
None
|
||||
/// Return a buffered instance of driver. User must supply Buffers
|
||||
pub fn buffered<const RX_BUF_SIZE: usize>(
|
||||
self,
|
||||
rxb: &'static mut RxBuf<RX_BUF_SIZE>,
|
||||
) -> BufferedCanRx<'d, T, RX_BUF_SIZE> {
|
||||
BufferedCanRx::new(self.rx, rxb)
|
||||
}
|
||||
}
|
||||
|
||||
enum RxFifo {
|
||||
Fifo0,
|
||||
Fifo1,
|
||||
/// User supplied buffer for RX Buffering
|
||||
pub type RxBuf<const BUF_SIZE: usize> = Channel<CriticalSectionRawMutex, Result<Envelope, BusError>, BUF_SIZE>;
|
||||
|
||||
/// 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_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 {
|
||||
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 {
|
||||
rx_sender: self.rx_buf.sender().into(),
|
||||
};
|
||||
T::mut_state().rx_mode = RxMode::Buffered(rx_inner);
|
||||
});
|
||||
self
|
||||
}
|
||||
|
||||
/// Async read frame from RX buffer.
|
||||
pub async fn read(&mut self) -> Result<Envelope, BusError> {
|
||||
self.rx_buf.receive().await
|
||||
}
|
||||
|
||||
/// Attempts to read a CAN frame without blocking.
|
||||
///
|
||||
/// Returns [Err(TryReadError::Empty)] if there are no frames in the rx queue.
|
||||
pub fn try_read(&mut self) -> Result<Envelope, TryReadError> {
|
||||
match &T::state().rx_mode {
|
||||
RxMode::Buffered(_) => {
|
||||
if let Ok(result) = self.rx_buf.try_receive() {
|
||||
match result {
|
||||
Ok(envelope) => Ok(envelope),
|
||||
Err(e) => Err(TryReadError::BusError(e)),
|
||||
}
|
||||
} else {
|
||||
let registers = crate::can::bx::Registers { canregs: T::regs() };
|
||||
if let Some(err) = registers.curr_error() {
|
||||
return Err(TryReadError::BusError(err));
|
||||
} else {
|
||||
Err(TryReadError::Empty)
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
panic!("Bad Mode")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Waits while receive queue is empty.
|
||||
pub async fn wait_not_empty(&mut self) {
|
||||
poll_fn(|cx| self.rx_buf.poll_ready_to_receive(cx)).await
|
||||
}
|
||||
|
||||
/// Returns a receiver that can be used for receiving CAN frames. Note, each CAN frame will only be received by one receiver.
|
||||
pub fn reader(&self) -> BufferedCanReceiver {
|
||||
self.rx_buf.receiver().into()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance, const RX_BUF_SIZE: usize> Drop for BufferedCanRx<'d, T, RX_BUF_SIZE> {
|
||||
fn drop(&mut self) {
|
||||
critical_section::with(|_| unsafe {
|
||||
T::mut_state().rx_mode = RxMode::NonBuffered(embassy_sync::waitqueue::AtomicWaker::new());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
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.
|
||||
@ -488,18 +569,163 @@ impl<'d, T: Instance> DerefMut for Can<'d, T> {
|
||||
}
|
||||
}
|
||||
|
||||
use crate::can::enums::{BusError, TryReadError};
|
||||
|
||||
pub(crate) enum RxMode {
|
||||
NonBuffered(AtomicWaker),
|
||||
Buffered(crate::can::_version::common::ClassicBufferedRxInner),
|
||||
}
|
||||
|
||||
impl RxMode {
|
||||
pub fn on_interrupt<T: Instance>(&self, fifo: crate::can::_version::bx::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,
|
||||
};
|
||||
T::regs().ier().write(|w| {
|
||||
w.set_fmpie(fifo_idx, false);
|
||||
});
|
||||
waker.wake();
|
||||
}
|
||||
Self::Buffered(buf) => {
|
||||
let regsisters = crate::can::bx::Registers { canregs: T::regs() };
|
||||
|
||||
loop {
|
||||
match regsisters.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));
|
||||
}
|
||||
None => return,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn read<T: Instance>(&self) -> Result<Envelope, BusError> {
|
||||
match self {
|
||||
Self::NonBuffered(waker) => {
|
||||
poll_fn(|cx| {
|
||||
T::state().err_waker.register(cx.waker());
|
||||
waker.register(cx.waker());
|
||||
match self.try_read::<T>() {
|
||||
Ok(result) => Poll::Ready(Ok(result)),
|
||||
Err(TryReadError::Empty) => Poll::Pending,
|
||||
Err(TryReadError::BusError(be)) => Poll::Ready(Err(be)),
|
||||
}
|
||||
})
|
||||
.await
|
||||
}
|
||||
_ => {
|
||||
panic!("Bad Mode")
|
||||
}
|
||||
}
|
||||
}
|
||||
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) {
|
||||
T::regs().ier().write(|w| {
|
||||
w.set_fmpie(0, true);
|
||||
});
|
||||
Ok(msg)
|
||||
} else if let Some(msg) = registers.receive_fifo(super::bx::RxFifo::Fifo1) {
|
||||
T::regs().ier().write(|w| {
|
||||
w.set_fmpie(1, true);
|
||||
});
|
||||
Ok(msg)
|
||||
} else if let Some(err) = registers.curr_error() {
|
||||
Err(TryReadError::BusError(err))
|
||||
} else {
|
||||
Err(TryReadError::Empty)
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
panic!("Bad Mode")
|
||||
}
|
||||
}
|
||||
}
|
||||
pub async fn wait_not_empty<T: Instance>(&self) {
|
||||
match &T::state().rx_mode {
|
||||
Self::NonBuffered(waker) => {
|
||||
poll_fn(|cx| {
|
||||
waker.register(cx.waker());
|
||||
let registers = crate::can::bx::Registers { canregs: T::regs() };
|
||||
if registers.receive_frame_available() {
|
||||
Poll::Ready(())
|
||||
} else {
|
||||
Poll::Pending
|
||||
}
|
||||
})
|
||||
.await
|
||||
}
|
||||
_ => {
|
||||
panic!("Bad Mode")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum TxMode {
|
||||
NonBuffered(AtomicWaker),
|
||||
Buffered(self::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())
|
||||
}
|
||||
pub fn on_interrupt<T: Instance>(&self) {
|
||||
match &T::state().tx_mode {
|
||||
TxMode::NonBuffered(waker) => waker.wake(),
|
||||
TxMode::Buffered(buf) => {
|
||||
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);
|
||||
}
|
||||
Err(_) => {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn register(&self, arg: &core::task::Waker) {
|
||||
match self {
|
||||
TxMode::NonBuffered(waker) => {
|
||||
waker.register(arg);
|
||||
}
|
||||
_ => {
|
||||
panic!("Bad mode");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct State {
|
||||
pub tx_waker: AtomicWaker,
|
||||
pub(crate) rx_mode: RxMode,
|
||||
pub(crate) tx_mode: TxMode,
|
||||
pub err_waker: AtomicWaker,
|
||||
pub rx_queue: Channel<CriticalSectionRawMutex, Envelope, 32>,
|
||||
}
|
||||
|
||||
impl State {
|
||||
pub const fn new() -> Self {
|
||||
Self {
|
||||
tx_waker: AtomicWaker::new(),
|
||||
rx_mode: RxMode::NonBuffered(AtomicWaker::new()),
|
||||
tx_mode: TxMode::NonBuffered(AtomicWaker::new()),
|
||||
err_waker: AtomicWaker::new(),
|
||||
rx_queue: Channel::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -507,6 +733,7 @@ impl State {
|
||||
trait SealedInstance {
|
||||
fn regs() -> crate::pac::can::Can;
|
||||
fn state() -> &'static State;
|
||||
unsafe fn mut_state() -> &'static mut State;
|
||||
}
|
||||
|
||||
/// CAN instance trait.
|
||||
@ -535,9 +762,12 @@ foreach_peripheral!(
|
||||
crate::pac::$inst
|
||||
}
|
||||
|
||||
unsafe fn mut_state() -> & 'static mut State {
|
||||
static mut STATE: State = State::new();
|
||||
&mut *core::ptr::addr_of_mut!(STATE)
|
||||
}
|
||||
fn state() -> &'static State {
|
||||
static STATE: State = State::new();
|
||||
&STATE
|
||||
unsafe { peripherals::$inst::mut_state() }
|
||||
}
|
||||
}
|
||||
|
||||
@ -601,22 +831,3 @@ impl Index for crate::can::bx::Mailbox {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
trait IntoBusError {
|
||||
fn into_bus_err(self) -> Option<BusError>;
|
||||
}
|
||||
|
||||
impl IntoBusError for Lec {
|
||||
fn into_bus_err(self) -> Option<BusError> {
|
||||
match self {
|
||||
Lec::STUFF => Some(BusError::Stuff),
|
||||
Lec::FORM => Some(BusError::Form),
|
||||
Lec::ACK => Some(BusError::Acknowledge),
|
||||
Lec::BITRECESSIVE => Some(BusError::BitRecessive),
|
||||
Lec::BITDOMINANT => Some(BusError::BitDominant),
|
||||
Lec::CRC => Some(BusError::Crc),
|
||||
Lec::CUSTOM => Some(BusError::Software),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
52
embassy-stm32/src/can/common.rs
Normal file
52
embassy-stm32/src/can/common.rs
Normal file
@ -0,0 +1,52 @@
|
||||
use embassy_sync::channel::{DynamicReceiver, DynamicSender};
|
||||
|
||||
use crate::can::_version::enums::*;
|
||||
use crate::can::_version::frame::*;
|
||||
|
||||
pub(crate) struct ClassicBufferedRxInner {
|
||||
pub rx_sender: DynamicSender<'static, Result<Envelope, BusError>>,
|
||||
}
|
||||
pub(crate) struct ClassicBufferedTxInner {
|
||||
pub tx_receiver: DynamicReceiver<'static, Frame>,
|
||||
}
|
||||
|
||||
#[cfg(any(can_fdcan_v1, can_fdcan_h7))]
|
||||
|
||||
pub(crate) struct FdBufferedRxInner {
|
||||
pub rx_sender: DynamicSender<'static, Result<FdEnvelope, BusError>>,
|
||||
}
|
||||
|
||||
#[cfg(any(can_fdcan_v1, can_fdcan_h7))]
|
||||
pub(crate) struct FdBufferedTxInner {
|
||||
pub tx_receiver: DynamicReceiver<'static, FdFrame>,
|
||||
}
|
||||
|
||||
/// Sender that can be used for sending CAN frames.
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct BufferedCanSender {
|
||||
pub(crate) tx_buf: embassy_sync::channel::DynamicSender<'static, Frame>,
|
||||
pub(crate) waker: fn(),
|
||||
}
|
||||
|
||||
impl BufferedCanSender {
|
||||
/// Async write frame to TX buffer.
|
||||
pub fn try_write(&mut self, frame: Frame) -> Result<(), embassy_sync::channel::TrySendError<Frame>> {
|
||||
self.tx_buf.try_send(frame)?;
|
||||
(self.waker)();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Async write frame to TX buffer.
|
||||
pub async fn write(&mut self, frame: Frame) {
|
||||
self.tx_buf.send(frame).await;
|
||||
(self.waker)();
|
||||
}
|
||||
|
||||
/// Allows a poll_fn to poll until the channel is ready to write
|
||||
pub fn poll_ready_to_send(&self, cx: &mut core::task::Context<'_>) -> core::task::Poll<()> {
|
||||
self.tx_buf.poll_ready_to_send(cx)
|
||||
}
|
||||
}
|
||||
|
||||
/// Receiver that can be used for receiving CAN frames. Note, each CAN frame will only be received by one receiver.
|
||||
pub type BufferedCanReceiver = embassy_sync::channel::DynamicReceiver<'static, Result<Envelope, BusError>>;
|
@ -40,3 +40,13 @@ pub enum FrameCreateError {
|
||||
/// Invalid ID.
|
||||
InvalidCanId,
|
||||
}
|
||||
|
||||
/// Error returned by `try_read`
|
||||
#[derive(Debug)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum TryReadError {
|
||||
/// Bus error
|
||||
BusError(BusError),
|
||||
/// Receive buffer is empty
|
||||
Empty,
|
||||
}
|
||||
|
@ -397,13 +397,13 @@ impl Registers {
|
||||
|
||||
/// Moves out of ConfigMode and into specified mode
|
||||
#[inline]
|
||||
pub fn into_mode(mut self, config: FdCanConfig, mode: crate::can::_version::FdcanOperatingMode) {
|
||||
pub fn into_mode(mut self, config: FdCanConfig, mode: crate::can::_version::OperatingMode) {
|
||||
match mode {
|
||||
crate::can::FdcanOperatingMode::InternalLoopbackMode => self.set_loopback_mode(LoopbackMode::Internal),
|
||||
crate::can::FdcanOperatingMode::ExternalLoopbackMode => self.set_loopback_mode(LoopbackMode::External),
|
||||
crate::can::FdcanOperatingMode::NormalOperationMode => self.set_normal_operations(true),
|
||||
crate::can::FdcanOperatingMode::RestrictedOperationMode => self.set_restricted_operations(true),
|
||||
crate::can::FdcanOperatingMode::BusMonitoringMode => self.set_bus_monitoring_mode(true),
|
||||
crate::can::OperatingMode::InternalLoopbackMode => self.set_loopback_mode(LoopbackMode::Internal),
|
||||
crate::can::OperatingMode::ExternalLoopbackMode => self.set_loopback_mode(LoopbackMode::External),
|
||||
crate::can::OperatingMode::NormalOperationMode => self.set_normal_operations(true),
|
||||
crate::can::OperatingMode::RestrictedOperationMode => self.set_restricted_operations(true),
|
||||
crate::can::OperatingMode::BusMonitoringMode => self.set_bus_monitoring_mode(true),
|
||||
}
|
||||
self.leave_init_mode(config);
|
||||
}
|
||||
|
@ -14,6 +14,7 @@ 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;
|
||||
@ -25,6 +26,8 @@ use fd::filter::*;
|
||||
pub use fd::{config, filter};
|
||||
use frame::*;
|
||||
|
||||
pub use self::common::{BufferedCanReceiver, BufferedCanSender};
|
||||
|
||||
/// Timestamp for incoming packets. Use Embassy time when enabled.
|
||||
#[cfg(feature = "time")]
|
||||
pub type Timestamp = embassy_time::Instant;
|
||||
@ -107,7 +110,7 @@ impl<T: Instance> interrupt::typelevel::Handler<T::IT1Interrupt> for IT1Interrup
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
/// Different operating modes
|
||||
pub enum FdcanOperatingMode {
|
||||
pub enum OperatingMode {
|
||||
//PoweredDownMode,
|
||||
//ConfigMode,
|
||||
/// This mode can be used for a “Hot Selftest”, meaning the FDCAN can be tested without
|
||||
@ -145,7 +148,7 @@ pub enum FdcanOperatingMode {
|
||||
|
||||
/// FDCAN Configuration instance instance
|
||||
/// Create instance of this first
|
||||
pub struct FdcanConfigurator<'d, T: Instance> {
|
||||
pub struct CanConfigurator<'d, T: Instance> {
|
||||
config: crate::can::fd::config::FdCanConfig,
|
||||
/// Reference to internals.
|
||||
instance: FdcanInstance<'d, T>,
|
||||
@ -166,7 +169,7 @@ fn calc_ns_per_timer_tick<T: Instance>(mode: crate::can::fd::config::FrameTransm
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> FdcanConfigurator<'d, T> {
|
||||
impl<'d, T: Instance> CanConfigurator<'d, T> {
|
||||
/// 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(
|
||||
@ -176,7 +179,7 @@ impl<'d, T: Instance> FdcanConfigurator<'d, T> {
|
||||
_irqs: impl interrupt::typelevel::Binding<T::IT0Interrupt, IT0InterruptHandler<T>>
|
||||
+ interrupt::typelevel::Binding<T::IT1Interrupt, IT1InterruptHandler<T>>
|
||||
+ 'd,
|
||||
) -> FdcanConfigurator<'d, T> {
|
||||
) -> CanConfigurator<'d, T> {
|
||||
into_ref!(peri, rx, tx);
|
||||
|
||||
rx.set_as_af(rx.af_num(), AFType::Input);
|
||||
@ -270,13 +273,13 @@ impl<'d, T: Instance> FdcanConfigurator<'d, T> {
|
||||
}
|
||||
|
||||
/// Start in mode.
|
||||
pub fn start(self, mode: FdcanOperatingMode) -> Fdcan<'d, T> {
|
||||
pub fn start(self, mode: OperatingMode) -> Can<'d, T> {
|
||||
let ns_per_timer_tick = calc_ns_per_timer_tick::<T>(self.config.frame_transmit);
|
||||
critical_section::with(|_| unsafe {
|
||||
T::mut_state().ns_per_timer_tick = ns_per_timer_tick;
|
||||
});
|
||||
T::registers().into_mode(self.config, mode);
|
||||
let ret = Fdcan {
|
||||
let ret = Can {
|
||||
config: self.config,
|
||||
instance: self.instance,
|
||||
_mode: mode,
|
||||
@ -285,30 +288,30 @@ impl<'d, T: Instance> FdcanConfigurator<'d, T> {
|
||||
}
|
||||
|
||||
/// Start, entering mode. Does same as start(mode)
|
||||
pub fn into_normal_mode(self) -> Fdcan<'d, T> {
|
||||
self.start(FdcanOperatingMode::NormalOperationMode)
|
||||
pub fn into_normal_mode(self) -> Can<'d, T> {
|
||||
self.start(OperatingMode::NormalOperationMode)
|
||||
}
|
||||
|
||||
/// Start, entering mode. Does same as start(mode)
|
||||
pub fn into_internal_loopback_mode(self) -> Fdcan<'d, T> {
|
||||
self.start(FdcanOperatingMode::InternalLoopbackMode)
|
||||
pub fn into_internal_loopback_mode(self) -> Can<'d, T> {
|
||||
self.start(OperatingMode::InternalLoopbackMode)
|
||||
}
|
||||
|
||||
/// Start, entering mode. Does same as start(mode)
|
||||
pub fn into_external_loopback_mode(self) -> Fdcan<'d, T> {
|
||||
self.start(FdcanOperatingMode::ExternalLoopbackMode)
|
||||
pub fn into_external_loopback_mode(self) -> Can<'d, T> {
|
||||
self.start(OperatingMode::ExternalLoopbackMode)
|
||||
}
|
||||
}
|
||||
|
||||
/// FDCAN Instance
|
||||
pub struct Fdcan<'d, T: Instance> {
|
||||
pub struct Can<'d, T: Instance> {
|
||||
config: crate::can::fd::config::FdCanConfig,
|
||||
/// Reference to internals.
|
||||
instance: FdcanInstance<'d, T>,
|
||||
_mode: FdcanOperatingMode,
|
||||
_mode: OperatingMode,
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> Fdcan<'d, T> {
|
||||
impl<'d, T: Instance> Can<'d, T> {
|
||||
/// Flush one of the TX mailboxes.
|
||||
pub async fn flush(&self, idx: usize) {
|
||||
poll_fn(|cx| {
|
||||
@ -331,12 +334,12 @@ impl<'d, T: Instance> Fdcan<'d, T> {
|
||||
/// 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: &ClassicFrame) -> Option<ClassicFrame> {
|
||||
pub async fn write(&mut self, frame: &Frame) -> Option<Frame> {
|
||||
T::state().tx_mode.write::<T>(frame).await
|
||||
}
|
||||
|
||||
/// Returns the next received message frame
|
||||
pub async fn read(&mut self) -> Result<(ClassicFrame, Timestamp), BusError> {
|
||||
pub async fn read(&mut self) -> Result<Envelope, BusError> {
|
||||
T::state().rx_mode.read_classic::<T>().await
|
||||
}
|
||||
|
||||
@ -349,19 +352,19 @@ impl<'d, T: Instance> Fdcan<'d, T> {
|
||||
}
|
||||
|
||||
/// Returns the next received message frame
|
||||
pub async fn read_fd(&mut self) -> Result<(FdFrame, Timestamp), BusError> {
|
||||
pub async fn read_fd(&mut self) -> Result<FdEnvelope, BusError> {
|
||||
T::state().rx_mode.read_fd::<T>().await
|
||||
}
|
||||
|
||||
/// Split instance into separate Tx(write) and Rx(read) portions
|
||||
pub fn split(self) -> (FdcanTx<'d, T>, FdcanRx<'d, T>) {
|
||||
pub fn split(self) -> (CanTx<'d, T>, CanRx<'d, T>) {
|
||||
(
|
||||
FdcanTx {
|
||||
CanTx {
|
||||
config: self.config,
|
||||
_instance: self.instance,
|
||||
_mode: self._mode,
|
||||
},
|
||||
FdcanRx {
|
||||
CanRx {
|
||||
_instance1: PhantomData::<T>,
|
||||
_instance2: T::regs(),
|
||||
_mode: self._mode,
|
||||
@ -370,8 +373,8 @@ impl<'d, T: Instance> Fdcan<'d, T> {
|
||||
}
|
||||
|
||||
/// Join split rx and tx portions back together
|
||||
pub fn join(tx: FdcanTx<'d, T>, rx: FdcanRx<'d, T>) -> Self {
|
||||
Fdcan {
|
||||
pub fn join(tx: CanTx<'d, T>, rx: CanRx<'d, T>) -> Self {
|
||||
Can {
|
||||
config: tx.config,
|
||||
//_instance2: T::regs(),
|
||||
instance: tx._instance,
|
||||
@ -399,59 +402,27 @@ impl<'d, T: Instance> Fdcan<'d, T> {
|
||||
}
|
||||
|
||||
/// User supplied buffer for RX Buffering
|
||||
pub type RxBuf<const BUF_SIZE: usize> =
|
||||
Channel<CriticalSectionRawMutex, Result<(ClassicFrame, Timestamp), BusError>, BUF_SIZE>;
|
||||
pub type RxBuf<const BUF_SIZE: usize> = Channel<CriticalSectionRawMutex, Result<Envelope, BusError>, BUF_SIZE>;
|
||||
|
||||
/// User supplied buffer for TX buffering
|
||||
pub type TxBuf<const BUF_SIZE: usize> = Channel<CriticalSectionRawMutex, ClassicFrame, BUF_SIZE>;
|
||||
pub type TxBuf<const BUF_SIZE: usize> = Channel<CriticalSectionRawMutex, Frame, BUF_SIZE>;
|
||||
|
||||
/// Buffered FDCAN Instance
|
||||
pub struct BufferedCan<'d, T: Instance, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize> {
|
||||
_instance1: PhantomData<T>,
|
||||
_instance2: &'d crate::pac::can::Fdcan,
|
||||
_mode: FdcanOperatingMode,
|
||||
_mode: OperatingMode,
|
||||
tx_buf: &'static TxBuf<TX_BUF_SIZE>,
|
||||
rx_buf: &'static RxBuf<RX_BUF_SIZE>,
|
||||
}
|
||||
|
||||
/// Sender that can be used for sending CAN frames.
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct BufferedCanSender {
|
||||
tx_buf: embassy_sync::channel::DynamicSender<'static, ClassicFrame>,
|
||||
waker: fn(),
|
||||
}
|
||||
|
||||
impl BufferedCanSender {
|
||||
/// Async write frame to TX buffer.
|
||||
pub fn try_write(&mut self, frame: ClassicFrame) -> Result<(), embassy_sync::channel::TrySendError<ClassicFrame>> {
|
||||
self.tx_buf.try_send(frame)?;
|
||||
(self.waker)();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Async write frame to TX buffer.
|
||||
pub async fn write(&mut self, frame: ClassicFrame) {
|
||||
self.tx_buf.send(frame).await;
|
||||
(self.waker)();
|
||||
}
|
||||
|
||||
/// Allows a poll_fn to poll until the channel is ready to write
|
||||
pub fn poll_ready_to_send(&self, cx: &mut core::task::Context<'_>) -> core::task::Poll<()> {
|
||||
self.tx_buf.poll_ready_to_send(cx)
|
||||
}
|
||||
}
|
||||
|
||||
/// Receiver that can be used for receiving CAN frames. Note, each CAN frame will only be received by one receiver.
|
||||
pub type BufferedCanReceiver =
|
||||
embassy_sync::channel::DynamicReceiver<'static, Result<(ClassicFrame, Timestamp), BusError>>;
|
||||
|
||||
impl<'c, 'd, T: Instance, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize>
|
||||
BufferedCan<'d, T, TX_BUF_SIZE, RX_BUF_SIZE>
|
||||
{
|
||||
fn new(
|
||||
_instance1: PhantomData<T>,
|
||||
_instance2: &'d crate::pac::can::Fdcan,
|
||||
_mode: FdcanOperatingMode,
|
||||
_mode: OperatingMode,
|
||||
tx_buf: &'static TxBuf<TX_BUF_SIZE>,
|
||||
rx_buf: &'static RxBuf<RX_BUF_SIZE>,
|
||||
) -> Self {
|
||||
@ -468,10 +439,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 = ClassicBufferedRxInner {
|
||||
let rx_inner = self::common::ClassicBufferedRxInner {
|
||||
rx_sender: self.rx_buf.sender().into(),
|
||||
};
|
||||
let tx_inner = ClassicBufferedTxInner {
|
||||
let tx_inner = self::common::ClassicBufferedTxInner {
|
||||
tx_receiver: self.tx_buf.receiver().into(),
|
||||
};
|
||||
T::mut_state().rx_mode = RxMode::ClassicBuffered(rx_inner);
|
||||
@ -481,13 +452,13 @@ impl<'c, 'd, T: Instance, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize>
|
||||
}
|
||||
|
||||
/// Async write frame to TX buffer.
|
||||
pub async fn write(&mut self, frame: ClassicFrame) {
|
||||
pub async fn write(&mut self, frame: Frame) {
|
||||
self.tx_buf.send(frame).await;
|
||||
T::IT0Interrupt::pend(); // Wake for Tx
|
||||
}
|
||||
|
||||
/// Async read frame from RX buffer.
|
||||
pub async fn read(&mut self) -> Result<(ClassicFrame, Timestamp), BusError> {
|
||||
pub async fn read(&mut self) -> Result<Envelope, BusError> {
|
||||
self.rx_buf.receive().await
|
||||
}
|
||||
|
||||
@ -517,8 +488,7 @@ impl<'c, 'd, T: Instance, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize> Dr
|
||||
}
|
||||
|
||||
/// User supplied buffer for RX Buffering
|
||||
pub type RxFdBuf<const BUF_SIZE: usize> =
|
||||
Channel<CriticalSectionRawMutex, Result<(FdFrame, Timestamp), BusError>, BUF_SIZE>;
|
||||
pub type RxFdBuf<const BUF_SIZE: usize> = Channel<CriticalSectionRawMutex, Result<FdEnvelope, BusError>, BUF_SIZE>;
|
||||
|
||||
/// User supplied buffer for TX buffering
|
||||
pub type TxFdBuf<const BUF_SIZE: usize> = Channel<CriticalSectionRawMutex, FdFrame, BUF_SIZE>;
|
||||
@ -527,7 +497,7 @@ pub type TxFdBuf<const BUF_SIZE: usize> = Channel<CriticalSectionRawMutex, FdFra
|
||||
pub struct BufferedCanFd<'d, T: Instance, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize> {
|
||||
_instance1: PhantomData<T>,
|
||||
_instance2: &'d crate::pac::can::Fdcan,
|
||||
_mode: FdcanOperatingMode,
|
||||
_mode: OperatingMode,
|
||||
tx_buf: &'static TxFdBuf<TX_BUF_SIZE>,
|
||||
rx_buf: &'static RxFdBuf<RX_BUF_SIZE>,
|
||||
}
|
||||
@ -535,7 +505,7 @@ pub struct BufferedCanFd<'d, T: Instance, const TX_BUF_SIZE: usize, const RX_BUF
|
||||
/// Sender that can be used for sending CAN frames.
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct BufferedFdCanSender {
|
||||
tx_buf: embassy_sync::channel::DynamicSender<'static, FdFrame>,
|
||||
tx_buf: DynamicSender<'static, FdFrame>,
|
||||
waker: fn(),
|
||||
}
|
||||
|
||||
@ -560,8 +530,7 @@ impl BufferedFdCanSender {
|
||||
}
|
||||
|
||||
/// Receiver that can be used for receiving CAN frames. Note, each CAN frame will only be received by one receiver.
|
||||
pub type BufferedFdCanReceiver =
|
||||
embassy_sync::channel::DynamicReceiver<'static, Result<(FdFrame, Timestamp), BusError>>;
|
||||
pub type BufferedFdCanReceiver = DynamicReceiver<'static, Result<FdEnvelope, BusError>>;
|
||||
|
||||
impl<'c, 'd, T: Instance, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize>
|
||||
BufferedCanFd<'d, T, TX_BUF_SIZE, RX_BUF_SIZE>
|
||||
@ -569,7 +538,7 @@ impl<'c, 'd, T: Instance, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize>
|
||||
fn new(
|
||||
_instance1: PhantomData<T>,
|
||||
_instance2: &'d crate::pac::can::Fdcan,
|
||||
_mode: FdcanOperatingMode,
|
||||
_mode: OperatingMode,
|
||||
tx_buf: &'static TxFdBuf<TX_BUF_SIZE>,
|
||||
rx_buf: &'static RxFdBuf<RX_BUF_SIZE>,
|
||||
) -> Self {
|
||||
@ -586,10 +555,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 = FdBufferedRxInner {
|
||||
let rx_inner = self::common::FdBufferedRxInner {
|
||||
rx_sender: self.rx_buf.sender().into(),
|
||||
};
|
||||
let tx_inner = FdBufferedTxInner {
|
||||
let tx_inner = self::common::FdBufferedTxInner {
|
||||
tx_receiver: self.tx_buf.receiver().into(),
|
||||
};
|
||||
T::mut_state().rx_mode = RxMode::FdBuffered(rx_inner);
|
||||
@ -605,7 +574,7 @@ impl<'c, 'd, T: Instance, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize>
|
||||
}
|
||||
|
||||
/// Async read frame from RX buffer.
|
||||
pub async fn read(&mut self) -> Result<(FdFrame, Timestamp), BusError> {
|
||||
pub async fn read(&mut self) -> Result<FdEnvelope, BusError> {
|
||||
self.rx_buf.receive().await
|
||||
}
|
||||
|
||||
@ -635,25 +604,25 @@ impl<'c, 'd, T: Instance, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize> Dr
|
||||
}
|
||||
|
||||
/// FDCAN Rx only Instance
|
||||
pub struct FdcanRx<'d, T: Instance> {
|
||||
pub struct CanRx<'d, T: Instance> {
|
||||
_instance1: PhantomData<T>,
|
||||
_instance2: &'d crate::pac::can::Fdcan,
|
||||
_mode: FdcanOperatingMode,
|
||||
_mode: OperatingMode,
|
||||
}
|
||||
|
||||
/// FDCAN Tx only Instance
|
||||
pub struct FdcanTx<'d, T: Instance> {
|
||||
pub struct CanTx<'d, T: Instance> {
|
||||
config: crate::can::fd::config::FdCanConfig,
|
||||
_instance: FdcanInstance<'d, T>, //(PeripheralRef<'a, T>);
|
||||
_mode: FdcanOperatingMode,
|
||||
_mode: OperatingMode,
|
||||
}
|
||||
|
||||
impl<'c, 'd, T: Instance> FdcanTx<'d, T> {
|
||||
impl<'c, 'd, T: Instance> CanTx<'d, T> {
|
||||
/// 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: &ClassicFrame) -> Option<ClassicFrame> {
|
||||
pub async fn write(&mut self, frame: &Frame) -> Option<Frame> {
|
||||
T::state().tx_mode.write::<T>(frame).await
|
||||
}
|
||||
|
||||
@ -666,36 +635,22 @@ impl<'c, 'd, T: Instance> FdcanTx<'d, T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'c, 'd, T: Instance> FdcanRx<'d, T> {
|
||||
impl<'c, 'd, T: Instance> CanRx<'d, T> {
|
||||
/// Returns the next received message frame
|
||||
pub async fn read(&mut self) -> Result<(ClassicFrame, Timestamp), BusError> {
|
||||
pub async fn read(&mut self) -> Result<Envelope, BusError> {
|
||||
T::state().rx_mode.read_classic::<T>().await
|
||||
}
|
||||
|
||||
/// Returns the next received message frame
|
||||
pub async fn read_fd(&mut self) -> Result<(FdFrame, Timestamp), BusError> {
|
||||
pub async fn read_fd(&mut self) -> Result<FdEnvelope, BusError> {
|
||||
T::state().rx_mode.read_fd::<T>().await
|
||||
}
|
||||
}
|
||||
|
||||
struct ClassicBufferedRxInner {
|
||||
rx_sender: DynamicSender<'static, Result<(ClassicFrame, Timestamp), BusError>>,
|
||||
}
|
||||
struct ClassicBufferedTxInner {
|
||||
tx_receiver: DynamicReceiver<'static, ClassicFrame>,
|
||||
}
|
||||
|
||||
struct FdBufferedRxInner {
|
||||
rx_sender: DynamicSender<'static, Result<(FdFrame, Timestamp), BusError>>,
|
||||
}
|
||||
struct FdBufferedTxInner {
|
||||
tx_receiver: DynamicReceiver<'static, FdFrame>,
|
||||
}
|
||||
|
||||
enum RxMode {
|
||||
NonBuffered(AtomicWaker),
|
||||
ClassicBuffered(ClassicBufferedRxInner),
|
||||
FdBuffered(FdBufferedRxInner),
|
||||
ClassicBuffered(self::common::ClassicBufferedRxInner),
|
||||
FdBuffered(self::common::FdBufferedRxInner),
|
||||
}
|
||||
|
||||
impl RxMode {
|
||||
@ -715,18 +670,50 @@ impl RxMode {
|
||||
waker.wake();
|
||||
}
|
||||
RxMode::ClassicBuffered(buf) => {
|
||||
if let Some(result) = self.read::<T, _>() {
|
||||
if let Some(result) = self.try_read::<T>() {
|
||||
let _ = buf.rx_sender.try_send(result);
|
||||
}
|
||||
}
|
||||
RxMode::FdBuffered(buf) => {
|
||||
if let Some(result) = self.read::<T, _>() {
|
||||
if let Some(result) = self.try_read_fd::<T>() {
|
||||
let _ = buf.rx_sender.try_send(result);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//async fn read_classic<T: Instance>(&self) -> Result<Envelope, BusError> {
|
||||
fn try_read<T: Instance>(&self) -> Option<Result<Envelope, BusError>> {
|
||||
if let Some((frame, ts)) = T::registers().read(0) {
|
||||
let ts = T::calc_timestamp(T::state().ns_per_timer_tick, ts);
|
||||
Some(Ok(Envelope { ts, frame }))
|
||||
} else if let Some((frame, ts)) = T::registers().read(1) {
|
||||
let ts = T::calc_timestamp(T::state().ns_per_timer_tick, ts);
|
||||
Some(Ok(Envelope { ts, frame }))
|
||||
} else if let Some(err) = T::registers().curr_error() {
|
||||
// TODO: this is probably wrong
|
||||
Some(Err(err))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
//async fn read_classic<T: Instance>(&self) -> Result<Envelope, BusError> {
|
||||
fn try_read_fd<T: Instance>(&self) -> Option<Result<FdEnvelope, BusError>> {
|
||||
if let Some((frame, ts)) = T::registers().read(0) {
|
||||
let ts = T::calc_timestamp(T::state().ns_per_timer_tick, ts);
|
||||
Some(Ok(FdEnvelope { ts, frame }))
|
||||
} else if let Some((frame, ts)) = T::registers().read(1) {
|
||||
let ts = T::calc_timestamp(T::state().ns_per_timer_tick, ts);
|
||||
Some(Ok(FdEnvelope { ts, frame }))
|
||||
} else if let Some(err) = T::registers().curr_error() {
|
||||
// TODO: this is probably wrong
|
||||
Some(Err(err))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn read<T: Instance, F: CanHeader>(&self) -> Option<Result<(F, Timestamp), BusError>> {
|
||||
if let Some((msg, ts)) = T::registers().read(0) {
|
||||
let ts = T::calc_timestamp(T::state().ns_per_timer_tick, ts);
|
||||
@ -754,19 +741,25 @@ impl RxMode {
|
||||
.await
|
||||
}
|
||||
|
||||
async fn read_classic<T: Instance>(&self) -> Result<(ClassicFrame, Timestamp), BusError> {
|
||||
self.read_async::<T, _>().await
|
||||
async fn read_classic<T: Instance>(&self) -> Result<Envelope, BusError> {
|
||||
match self.read_async::<T, _>().await {
|
||||
Ok((frame, ts)) => Ok(Envelope { ts, frame }),
|
||||
Err(e) => Err(e),
|
||||
}
|
||||
}
|
||||
|
||||
async fn read_fd<T: Instance>(&self) -> Result<(FdFrame, Timestamp), BusError> {
|
||||
self.read_async::<T, _>().await
|
||||
async fn read_fd<T: Instance>(&self) -> Result<FdEnvelope, BusError> {
|
||||
match self.read_async::<T, _>().await {
|
||||
Ok((frame, ts)) => Ok(FdEnvelope { ts, frame }),
|
||||
Err(e) => Err(e),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum TxMode {
|
||||
NonBuffered(AtomicWaker),
|
||||
ClassicBuffered(ClassicBufferedTxInner),
|
||||
FdBuffered(FdBufferedTxInner),
|
||||
ClassicBuffered(self::common::ClassicBufferedTxInner),
|
||||
FdBuffered(self::common::FdBufferedTxInner),
|
||||
}
|
||||
|
||||
impl TxMode {
|
||||
@ -804,7 +797,7 @@ impl TxMode {
|
||||
/// frame is dropped from the mailbox, it is returned. If no lower-priority frames
|
||||
/// can be replaced, this call asynchronously waits for a frame to be successfully
|
||||
/// transmitted, then tries again.
|
||||
async fn write<T: Instance>(&self, frame: &ClassicFrame) -> Option<ClassicFrame> {
|
||||
async fn write<T: Instance>(&self, frame: &Frame) -> Option<Frame> {
|
||||
self.write_generic::<T, _>(frame).await
|
||||
}
|
||||
|
||||
|
@ -3,6 +3,14 @@ use bit_field::BitField;
|
||||
|
||||
use crate::can::enums::FrameCreateError;
|
||||
|
||||
/// Calculate proper timestamp when available.
|
||||
#[cfg(feature = "time")]
|
||||
pub type Timestamp = embassy_time::Instant;
|
||||
|
||||
/// Raw register timestamp
|
||||
#[cfg(not(feature = "time"))]
|
||||
pub type Timestamp = u16;
|
||||
|
||||
/// CAN Header, without meta data
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct Header {
|
||||
@ -136,19 +144,20 @@ impl ClassicData {
|
||||
}
|
||||
}
|
||||
|
||||
/// Frame with up to 8 bytes of data payload as per Classic CAN
|
||||
/// Frame with up to 8 bytes of data payload as per Classic(non-FD) CAN
|
||||
/// For CAN-FD support use FdFrame
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub struct ClassicFrame {
|
||||
pub struct Frame {
|
||||
can_header: Header,
|
||||
data: ClassicData,
|
||||
}
|
||||
|
||||
impl ClassicFrame {
|
||||
impl Frame {
|
||||
/// Create a new CAN classic Frame
|
||||
pub fn new(can_header: Header, raw_data: &[u8]) -> Result<Self, FrameCreateError> {
|
||||
let data = ClassicData::new(raw_data)?;
|
||||
Ok(ClassicFrame { can_header, data: data })
|
||||
Ok(Frame { can_header, data: data })
|
||||
}
|
||||
|
||||
/// Creates a new data frame.
|
||||
@ -206,9 +215,9 @@ impl ClassicFrame {
|
||||
}
|
||||
}
|
||||
|
||||
impl embedded_can::Frame for ClassicFrame {
|
||||
impl embedded_can::Frame for Frame {
|
||||
fn new(id: impl Into<embedded_can::Id>, raw_data: &[u8]) -> Option<Self> {
|
||||
let frameopt = ClassicFrame::new(Header::new(id.into(), raw_data.len() as u8, false), raw_data);
|
||||
let frameopt = Frame::new(Header::new(id.into(), raw_data.len() as u8, false), raw_data);
|
||||
match frameopt {
|
||||
Ok(frame) => Some(frame),
|
||||
Err(_) => None,
|
||||
@ -216,7 +225,7 @@ impl embedded_can::Frame for ClassicFrame {
|
||||
}
|
||||
fn new_remote(id: impl Into<embedded_can::Id>, len: usize) -> Option<Self> {
|
||||
if len <= 8 {
|
||||
let frameopt = ClassicFrame::new(Header::new(id.into(), len as u8, true), &[0; 8]);
|
||||
let frameopt = Frame::new(Header::new(id.into(), len as u8, true), &[0; 8]);
|
||||
match frameopt {
|
||||
Ok(frame) => Some(frame),
|
||||
Err(_) => None,
|
||||
@ -245,7 +254,7 @@ impl embedded_can::Frame for ClassicFrame {
|
||||
}
|
||||
}
|
||||
|
||||
impl CanHeader for ClassicFrame {
|
||||
impl CanHeader for Frame {
|
||||
fn from_header(header: Header, data: &[u8]) -> Result<Self, FrameCreateError> {
|
||||
Self::new(header, data)
|
||||
}
|
||||
@ -255,10 +264,31 @@ impl CanHeader for ClassicFrame {
|
||||
}
|
||||
}
|
||||
|
||||
/// Contains CAN frame and additional metadata.
|
||||
///
|
||||
/// Timestamp is available if `time` feature is enabled.
|
||||
/// For CAN-FD support use FdEnvelope
|
||||
#[derive(Debug, Clone)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub struct Envelope {
|
||||
/// Reception time.
|
||||
pub ts: Timestamp,
|
||||
/// The actual CAN frame.
|
||||
pub frame: Frame,
|
||||
}
|
||||
|
||||
impl Envelope {
|
||||
/// Convert into a tuple
|
||||
pub fn parts(self) -> (Frame, Timestamp) {
|
||||
(self.frame, self.ts)
|
||||
}
|
||||
}
|
||||
|
||||
/// Payload of a (FD)CAN data frame.
|
||||
///
|
||||
/// Contains 0 to 64 Bytes of data.
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub struct FdData {
|
||||
pub(crate) bytes: [u8; 64],
|
||||
}
|
||||
@ -308,6 +338,7 @@ impl FdData {
|
||||
|
||||
/// Frame with up to 8 bytes of data payload as per Fd CAN
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub struct FdFrame {
|
||||
can_header: Header,
|
||||
data: FdData,
|
||||
@ -410,3 +441,23 @@ impl CanHeader for FdFrame {
|
||||
self.header()
|
||||
}
|
||||
}
|
||||
|
||||
/// Contains CAN FD frame and additional metadata.
|
||||
///
|
||||
/// Timestamp is available if `time` feature is enabled.
|
||||
#[derive(Debug, Clone)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub struct FdEnvelope {
|
||||
/// Reception time.
|
||||
pub ts: Timestamp,
|
||||
|
||||
/// The actual CAN frame.
|
||||
pub frame: FdFrame,
|
||||
}
|
||||
|
||||
impl FdEnvelope {
|
||||
/// Convert into a tuple
|
||||
pub fn parts(self) -> (FdFrame, Timestamp) {
|
||||
(self.frame, self.ts)
|
||||
}
|
||||
}
|
||||
|
@ -23,6 +23,7 @@ panic-probe = { version = "0.3", features = ["print-defmt"] }
|
||||
futures = { version = "0.3.17", default-features = false, features = ["async-await"] }
|
||||
heapless = { version = "0.8", default-features = false }
|
||||
nb = "1.0.0"
|
||||
static_cell = "2.0.0"
|
||||
|
||||
[profile.dev]
|
||||
opt-level = "s"
|
||||
|
@ -4,11 +4,12 @@
|
||||
use defmt::*;
|
||||
use embassy_executor::Spawner;
|
||||
use embassy_stm32::can::{
|
||||
filter, Can, Fifo, Frame, Id, Rx0InterruptHandler, Rx1InterruptHandler, SceInterruptHandler, StandardId,
|
||||
filter, Can, Envelope, Fifo, Frame, Id, Rx0InterruptHandler, Rx1InterruptHandler, SceInterruptHandler, StandardId,
|
||||
TxInterruptHandler,
|
||||
};
|
||||
use embassy_stm32::peripherals::CAN;
|
||||
use embassy_stm32::{bind_interrupts, Config};
|
||||
use static_cell::StaticCell;
|
||||
use {defmt_rtt as _, panic_probe as _};
|
||||
|
||||
bind_interrupts!(struct Irqs {
|
||||
@ -21,6 +22,27 @@ bind_interrupts!(struct Irqs {
|
||||
// This example is configured to work with real CAN transceivers on B8/B9.
|
||||
// See other examples for loopback.
|
||||
|
||||
fn handle_frame(env: Envelope, read_mode: &str) {
|
||||
match env.frame.id() {
|
||||
Id::Extended(id) => {
|
||||
defmt::println!(
|
||||
"{} Extended Frame id={:x} {:02x}",
|
||||
read_mode,
|
||||
id.as_raw(),
|
||||
env.frame.data()
|
||||
);
|
||||
}
|
||||
Id::Standard(id) => {
|
||||
defmt::println!(
|
||||
"{} Standard Frame id={:x} {:02x}",
|
||||
read_mode,
|
||||
id.as_raw(),
|
||||
env.frame.data()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[embassy_executor::main]
|
||||
async fn main(_spawner: Spawner) {
|
||||
let p = embassy_stm32::init(Config::default());
|
||||
@ -28,6 +50,9 @@ async fn main(_spawner: Spawner) {
|
||||
// Set alternate pin mapping to B8/B9
|
||||
embassy_stm32::pac::AFIO.mapr().modify(|w| w.set_can1_remap(2));
|
||||
|
||||
static RX_BUF: StaticCell<embassy_stm32::can::RxBuf<10>> = StaticCell::new();
|
||||
static TX_BUF: StaticCell<embassy_stm32::can::TxBuf<10>> = StaticCell::new();
|
||||
|
||||
let mut can = Can::new(p.CAN, p.PB8, p.PB9, Irqs);
|
||||
|
||||
can.as_mut()
|
||||
@ -43,21 +68,72 @@ async fn main(_spawner: Spawner) {
|
||||
can.set_bitrate(250_000);
|
||||
|
||||
can.enable().await;
|
||||
|
||||
let mut i: u8 = 0;
|
||||
|
||||
/*
|
||||
// Example for using buffered Tx and Rx without needing to
|
||||
// split first as is done below.
|
||||
let mut can = can.buffered(
|
||||
TX_BUF.init(embassy_stm32::can::TxBuf::<10>::new()),
|
||||
RX_BUF.init(embassy_stm32::can::RxBuf::<10>::new()));
|
||||
loop {
|
||||
let tx_frame = Frame::new_data(unwrap!(StandardId::new(i as _)), &[i, 0, 1, 2, 3, 4, 5, 6]).unwrap();
|
||||
can.write(&tx_frame).await;
|
||||
|
||||
match can.read().await {
|
||||
Ok(env) => match env.frame.id() {
|
||||
Id::Extended(id) => {
|
||||
defmt::println!("Extended Frame id={:x} {:02x}", id.as_raw(), env.frame.data());
|
||||
Ok((frame, ts)) => {
|
||||
handle_frame(Envelope { ts, frame }, "Buf");
|
||||
}
|
||||
Id::Standard(id) => {
|
||||
defmt::println!("Standard Frame id={:x} {:02x}", id.as_raw(), env.frame.data());
|
||||
Err(err) => {
|
||||
defmt::println!("Error {}", err);
|
||||
}
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
|
||||
*/
|
||||
let (mut tx, mut rx) = can.split();
|
||||
|
||||
// This example shows using the wait_not_empty API before try read
|
||||
while i < 3 {
|
||||
let tx_frame = Frame::new_data(unwrap!(StandardId::new(i as _)), &[i, 0, 1, 2, 3, 4, 5, 6]).unwrap();
|
||||
tx.write(&tx_frame).await;
|
||||
|
||||
rx.wait_not_empty().await;
|
||||
let env = rx.try_read().unwrap();
|
||||
handle_frame(env, "Wait");
|
||||
i += 1;
|
||||
}
|
||||
|
||||
// This example shows using the full async non-buffered API
|
||||
while i < 6 {
|
||||
let tx_frame = Frame::new_data(unwrap!(StandardId::new(i as _)), &[i, 0, 1, 2, 3, 4, 5, 6]).unwrap();
|
||||
tx.write(&tx_frame).await;
|
||||
|
||||
match rx.read().await {
|
||||
Ok(env) => {
|
||||
handle_frame(env, "NoBuf");
|
||||
}
|
||||
Err(err) => {
|
||||
defmt::println!("Error {}", err);
|
||||
}
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
|
||||
// This example shows using buffered RX and TX. User passes in desired buffer (size)
|
||||
// It's possible this way to have just RX or TX buffered.
|
||||
let mut rx = rx.buffered(RX_BUF.init(embassy_stm32::can::RxBuf::<10>::new()));
|
||||
let mut tx = tx.buffered(TX_BUF.init(embassy_stm32::can::TxBuf::<10>::new()));
|
||||
|
||||
loop {
|
||||
let tx_frame = Frame::new_data(unwrap!(StandardId::new(i as _)), &[i, 0, 1, 2, 3, 4, 5, 6]).unwrap();
|
||||
tx.write(&tx_frame).await;
|
||||
|
||||
match rx.read().await {
|
||||
Ok(envelope) => {
|
||||
handle_frame(envelope, "Buf");
|
||||
}
|
||||
},
|
||||
Err(err) => {
|
||||
defmt::println!("Error {}", err);
|
||||
}
|
||||
|
@ -36,7 +36,7 @@ async fn main(_spawner: Spawner) {
|
||||
}
|
||||
let peripherals = embassy_stm32::init(config);
|
||||
|
||||
let mut can = can::FdcanConfigurator::new(peripherals.FDCAN1, peripherals.PA11, peripherals.PA12, Irqs);
|
||||
let mut can = can::CanConfigurator::new(peripherals.FDCAN1, peripherals.PA11, peripherals.PA12, Irqs);
|
||||
|
||||
can.set_extended_filter(
|
||||
can::filter::ExtendedFilterSlot::_0,
|
||||
@ -56,21 +56,22 @@ async fn main(_spawner: Spawner) {
|
||||
info!("Configured");
|
||||
|
||||
let mut can = can.start(match use_fd {
|
||||
true => can::FdcanOperatingMode::InternalLoopbackMode,
|
||||
false => can::FdcanOperatingMode::NormalOperationMode,
|
||||
true => can::OperatingMode::InternalLoopbackMode,
|
||||
false => can::OperatingMode::NormalOperationMode,
|
||||
});
|
||||
|
||||
let mut i = 0;
|
||||
let mut last_read_ts = embassy_time::Instant::now();
|
||||
|
||||
loop {
|
||||
let frame = can::frame::ClassicFrame::new_extended(0x123456F, &[i; 8]).unwrap();
|
||||
let frame = can::frame::Frame::new_extended(0x123456F, &[i; 8]).unwrap();
|
||||
info!("Writing frame");
|
||||
|
||||
_ = can.write(&frame).await;
|
||||
|
||||
match can.read().await {
|
||||
Ok((rx_frame, ts)) => {
|
||||
Ok(envelope) => {
|
||||
let (ts, rx_frame) = (envelope.ts, envelope.frame);
|
||||
let delta = (ts - last_read_ts).as_millis();
|
||||
last_read_ts = ts;
|
||||
info!(
|
||||
@ -105,7 +106,8 @@ async fn main(_spawner: Spawner) {
|
||||
}
|
||||
|
||||
match can.read_fd().await {
|
||||
Ok((rx_frame, ts)) => {
|
||||
Ok(envelope) => {
|
||||
let (ts, rx_frame) = (envelope.ts, envelope.frame);
|
||||
let delta = (ts - last_read_ts).as_millis();
|
||||
last_read_ts = ts;
|
||||
info!(
|
||||
@ -129,12 +131,13 @@ async fn main(_spawner: Spawner) {
|
||||
let (mut tx, mut rx) = can.split();
|
||||
// With split
|
||||
loop {
|
||||
let frame = can::frame::ClassicFrame::new_extended(0x123456F, &[i; 8]).unwrap();
|
||||
let frame = can::frame::Frame::new_extended(0x123456F, &[i; 8]).unwrap();
|
||||
info!("Writing frame");
|
||||
_ = tx.write(&frame).await;
|
||||
|
||||
match rx.read().await {
|
||||
Ok((rx_frame, ts)) => {
|
||||
Ok(envelope) => {
|
||||
let (ts, rx_frame) = (envelope.ts, envelope.frame);
|
||||
let delta = (ts - last_read_ts).as_millis();
|
||||
last_read_ts = ts;
|
||||
info!(
|
||||
@ -156,7 +159,7 @@ async fn main(_spawner: Spawner) {
|
||||
}
|
||||
}
|
||||
|
||||
let can = can::Fdcan::join(tx, rx);
|
||||
let can = can::Can::join(tx, rx);
|
||||
|
||||
info!("\n\n\nBuffered\n");
|
||||
if use_fd {
|
||||
@ -173,7 +176,8 @@ async fn main(_spawner: Spawner) {
|
||||
_ = can.write(frame).await;
|
||||
|
||||
match can.read().await {
|
||||
Ok((rx_frame, ts)) => {
|
||||
Ok(envelope) => {
|
||||
let (ts, rx_frame) = (envelope.ts, envelope.frame);
|
||||
let delta = (ts - last_read_ts).as_millis();
|
||||
last_read_ts = ts;
|
||||
info!(
|
||||
@ -198,7 +202,7 @@ async fn main(_spawner: Spawner) {
|
||||
RX_BUF.init(can::RxBuf::<10>::new()),
|
||||
);
|
||||
loop {
|
||||
let frame = can::frame::ClassicFrame::new_extended(0x123456F, &[i; 8]).unwrap();
|
||||
let frame = can::frame::Frame::new_extended(0x123456F, &[i; 8]).unwrap();
|
||||
info!("Writing frame");
|
||||
|
||||
// You can use any of these approaches to send. The writer makes it
|
||||
@ -208,7 +212,8 @@ async fn main(_spawner: Spawner) {
|
||||
can.writer().write(frame).await;
|
||||
|
||||
match can.read().await {
|
||||
Ok((rx_frame, ts)) => {
|
||||
Ok(envelope) => {
|
||||
let (ts, rx_frame) = (envelope.ts, envelope.frame);
|
||||
let delta = (ts - last_read_ts).as_millis();
|
||||
last_read_ts = ts;
|
||||
info!(
|
||||
|
@ -24,7 +24,7 @@ async fn main(_spawner: Spawner) {
|
||||
|
||||
let peripherals = embassy_stm32::init(config);
|
||||
|
||||
let mut can = can::FdcanConfigurator::new(peripherals.FDCAN1, peripherals.PA11, peripherals.PA12, Irqs);
|
||||
let mut can = can::CanConfigurator::new(peripherals.FDCAN1, peripherals.PA11, peripherals.PA12, Irqs);
|
||||
|
||||
// 250k bps
|
||||
can.set_bitrate(250_000);
|
||||
@ -38,12 +38,13 @@ async fn main(_spawner: Spawner) {
|
||||
let mut last_read_ts = embassy_time::Instant::now();
|
||||
|
||||
loop {
|
||||
let frame = can::frame::ClassicFrame::new_extended(0x123456F, &[i; 8]).unwrap();
|
||||
let frame = can::frame::Frame::new_extended(0x123456F, &[i; 8]).unwrap();
|
||||
info!("Writing frame");
|
||||
_ = can.write(&frame).await;
|
||||
|
||||
match can.read().await {
|
||||
Ok((rx_frame, ts)) => {
|
||||
Ok(envelope) => {
|
||||
let (rx_frame, ts) = envelope.parts();
|
||||
let delta = (ts - last_read_ts).as_millis();
|
||||
last_read_ts = ts;
|
||||
info!(
|
||||
@ -69,12 +70,13 @@ async fn main(_spawner: Spawner) {
|
||||
let (mut tx, mut rx) = can.split();
|
||||
// With split
|
||||
loop {
|
||||
let frame = can::frame::ClassicFrame::new_extended(0x123456F, &[i; 8]).unwrap();
|
||||
let frame = can::frame::Frame::new_extended(0x123456F, &[i; 8]).unwrap();
|
||||
info!("Writing frame");
|
||||
_ = tx.write(&frame).await;
|
||||
|
||||
match rx.read().await {
|
||||
Ok((rx_frame, ts)) => {
|
||||
Ok(envelope) => {
|
||||
let (rx_frame, ts) = envelope.parts();
|
||||
let delta = (ts - last_read_ts).as_millis();
|
||||
last_read_ts = ts;
|
||||
info!(
|
||||
|
@ -24,7 +24,7 @@ async fn main(_spawner: Spawner) {
|
||||
|
||||
let peripherals = embassy_stm32::init(config);
|
||||
|
||||
let mut can = can::FdcanConfigurator::new(peripherals.FDCAN1, peripherals.PA11, peripherals.PA12, Irqs);
|
||||
let mut can = can::CanConfigurator::new(peripherals.FDCAN1, peripherals.PA11, peripherals.PA12, Irqs);
|
||||
|
||||
// 250k bps
|
||||
can.set_bitrate(250_000);
|
||||
@ -38,12 +38,13 @@ async fn main(_spawner: Spawner) {
|
||||
let mut last_read_ts = embassy_time::Instant::now();
|
||||
|
||||
loop {
|
||||
let frame = can::frame::ClassicFrame::new_extended(0x123456F, &[i; 8]).unwrap();
|
||||
let frame = can::frame::Frame::new_extended(0x123456F, &[i; 8]).unwrap();
|
||||
info!("Writing frame");
|
||||
_ = can.write(&frame).await;
|
||||
|
||||
match can.read().await {
|
||||
Ok((rx_frame, ts)) => {
|
||||
Ok(envelope) => {
|
||||
let (rx_frame, ts) = envelope.parts();
|
||||
let delta = (ts - last_read_ts).as_millis();
|
||||
last_read_ts = ts;
|
||||
info!(
|
||||
@ -69,12 +70,13 @@ async fn main(_spawner: Spawner) {
|
||||
let (mut tx, mut rx) = can.split();
|
||||
// With split
|
||||
loop {
|
||||
let frame = can::frame::ClassicFrame::new_extended(0x123456F, &[i; 8]).unwrap();
|
||||
let frame = can::frame::Frame::new_extended(0x123456F, &[i; 8]).unwrap();
|
||||
info!("Writing frame");
|
||||
_ = tx.write(&frame).await;
|
||||
|
||||
match rx.read().await {
|
||||
Ok((rx_frame, ts)) => {
|
||||
Ok(envelope) => {
|
||||
let (rx_frame, ts) = envelope.parts();
|
||||
let delta = (ts - last_read_ts).as_millis();
|
||||
last_read_ts = ts;
|
||||
info!(
|
||||
|
@ -6,17 +6,19 @@
|
||||
#[path = "../common.rs"]
|
||||
mod common;
|
||||
use common::*;
|
||||
use defmt::assert;
|
||||
use embassy_executor::Spawner;
|
||||
use embassy_stm32::bind_interrupts;
|
||||
use embassy_stm32::can::bx::filter::Mask32;
|
||||
use embassy_stm32::can::bx::{Fifo, Frame, StandardId};
|
||||
use embassy_stm32::can::bx::Fifo;
|
||||
use embassy_stm32::can::{Can, Rx0InterruptHandler, Rx1InterruptHandler, SceInterruptHandler, TxInterruptHandler};
|
||||
use embassy_stm32::gpio::{Input, Pull};
|
||||
use embassy_stm32::peripherals::CAN1;
|
||||
use embassy_time::{Duration, Instant};
|
||||
use embassy_time::Duration;
|
||||
use {defmt_rtt as _, panic_probe as _};
|
||||
|
||||
mod can_common;
|
||||
use can_common::*;
|
||||
|
||||
bind_interrupts!(struct Irqs {
|
||||
CAN1_RX0 => Rx0InterruptHandler<CAN1>;
|
||||
CAN1_RX1 => Rx1InterruptHandler<CAN1>;
|
||||
@ -29,6 +31,11 @@ async fn main(_spawner: Spawner) {
|
||||
let p = embassy_stm32::init(config());
|
||||
info!("Hello World!");
|
||||
|
||||
let options = TestOptions {
|
||||
max_latency: Duration::from_micros(1200),
|
||||
max_buffered: 2,
|
||||
};
|
||||
|
||||
let can = peri!(p, CAN);
|
||||
let tx = peri!(p, CAN_TX);
|
||||
let mut rx = peri!(p, CAN_RX);
|
||||
@ -58,40 +65,13 @@ async fn main(_spawner: Spawner) {
|
||||
|
||||
info!("Can configured");
|
||||
|
||||
let mut i: u8 = 0;
|
||||
loop {
|
||||
let tx_frame = Frame::new_data(unwrap!(StandardId::new(i as _)), &[i]).unwrap();
|
||||
run_can_tests(&mut can, &options).await;
|
||||
|
||||
info!("Transmitting frame...");
|
||||
let tx_ts = Instant::now();
|
||||
can.write(&tx_frame).await;
|
||||
|
||||
let envelope = can.read().await.unwrap();
|
||||
info!("Frame received!");
|
||||
|
||||
info!("loopback time {}", envelope.ts);
|
||||
info!("loopback frame {=u8}", envelope.frame.data()[0]);
|
||||
|
||||
let latency = envelope.ts.saturating_duration_since(tx_ts);
|
||||
info!("loopback latency {} us", latency.as_micros());
|
||||
|
||||
// Theoretical minimum latency is 55us, actual is usually ~80us
|
||||
const MIN_LATENCY: Duration = Duration::from_micros(50);
|
||||
const MAX_LATENCY: Duration = Duration::from_micros(150);
|
||||
assert!(
|
||||
MIN_LATENCY <= latency && latency <= MAX_LATENCY,
|
||||
"{} <= {} <= {}",
|
||||
MIN_LATENCY,
|
||||
latency,
|
||||
MAX_LATENCY
|
||||
);
|
||||
|
||||
i += 1;
|
||||
if i > 10 {
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Test again with a split
|
||||
let (mut tx, mut rx) = can.split();
|
||||
run_split_can_tests(&mut tx, &mut rx, &options).await;
|
||||
|
||||
info!("Test OK");
|
||||
|
||||
cortex_m::asm::bkpt();
|
||||
}
|
||||
|
112
tests/stm32/src/bin/can_common.rs
Normal file
112
tests/stm32/src/bin/can_common.rs
Normal file
@ -0,0 +1,112 @@
|
||||
use defmt::{assert, *};
|
||||
use embassy_stm32::can;
|
||||
use embassy_time::{Duration, Instant};
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct TestOptions {
|
||||
pub max_latency: Duration,
|
||||
pub max_buffered: u8,
|
||||
}
|
||||
|
||||
pub async fn run_can_tests<'d, T: can::Instance>(can: &mut can::Can<'d, T>, options: &TestOptions) {
|
||||
let mut i: u8 = 0;
|
||||
loop {
|
||||
//let tx_frame = can::frame::Frame::new_standard(0x123, &[i, 0x12 as u8, 0x34 as u8, 0x56 as u8, 0x78 as u8, 0x9A as u8, 0xBC as u8 ]).unwrap();
|
||||
let tx_frame = can::frame::Frame::new_standard(0x123, &[i; 1]).unwrap();
|
||||
|
||||
//info!("Transmitting frame...");
|
||||
let tx_ts = Instant::now();
|
||||
can.write(&tx_frame).await;
|
||||
|
||||
let (frame, timestamp) = can.read().await.unwrap().parts();
|
||||
//info!("Frame received!");
|
||||
|
||||
// Check data.
|
||||
assert!(i == frame.data()[0], "{} == {}", i, frame.data()[0]);
|
||||
|
||||
//info!("loopback time {}", timestamp);
|
||||
//info!("loopback frame {=u8}", frame.data()[0]);
|
||||
let latency = timestamp.saturating_duration_since(tx_ts);
|
||||
info!("loopback latency {} us", latency.as_micros());
|
||||
|
||||
// Theoretical minimum latency is 55us, actual is usually ~80us
|
||||
const MIN_LATENCY: Duration = Duration::from_micros(50);
|
||||
// Was failing at 150 but we are not getting a real time stamp. I'm not
|
||||
// sure if there are other delays
|
||||
assert!(
|
||||
MIN_LATENCY <= latency && latency <= options.max_latency,
|
||||
"{} <= {} <= {}",
|
||||
MIN_LATENCY,
|
||||
latency,
|
||||
options.max_latency
|
||||
);
|
||||
|
||||
i += 1;
|
||||
if i > 5 {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Below here, check that we can receive from both FIFO0 and FIFO1
|
||||
// Above we configured FIFO1 for extended ID packets. There are only 3 slots
|
||||
// in each FIFO so make sure we write enough to fill them both up before reading.
|
||||
for i in 0..options.max_buffered {
|
||||
// Try filling up the RX FIFO0 buffers
|
||||
//let tx_frame = if 0 != (i & 0x01) {
|
||||
let tx_frame = if i < options.max_buffered / 2 {
|
||||
info!("Transmitting standard frame {}", i);
|
||||
can::frame::Frame::new_standard(0x123, &[i; 1]).unwrap()
|
||||
} else {
|
||||
info!("Transmitting extended frame {}", i);
|
||||
can::frame::Frame::new_extended(0x1232344, &[i; 1]).unwrap()
|
||||
};
|
||||
can.write(&tx_frame).await;
|
||||
}
|
||||
|
||||
// Try and receive all 6 packets
|
||||
for _i in 0..options.max_buffered {
|
||||
let (frame, _ts) = can.read().await.unwrap().parts();
|
||||
match frame.id() {
|
||||
embedded_can::Id::Extended(_id) => {
|
||||
info!("Extended received! {}", frame.data()[0]);
|
||||
//info!("Extended received! {:x} {} {}", id.as_raw(), frame.data()[0], i);
|
||||
}
|
||||
embedded_can::Id::Standard(_id) => {
|
||||
info!("Standard received! {}", frame.data()[0]);
|
||||
//info!("Standard received! {:x} {} {}", id.as_raw(), frame.data()[0], i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn run_split_can_tests<'d, T: can::Instance>(
|
||||
tx: &mut can::CanTx<'d, T>,
|
||||
rx: &mut can::CanRx<'d, T>,
|
||||
options: &TestOptions,
|
||||
) {
|
||||
for i in 0..options.max_buffered {
|
||||
// Try filling up the RX FIFO0 buffers
|
||||
//let tx_frame = if 0 != (i & 0x01) {
|
||||
let tx_frame = if i < options.max_buffered / 2 {
|
||||
info!("Transmitting standard frame {}", i);
|
||||
can::frame::Frame::new_standard(0x123, &[i; 1]).unwrap()
|
||||
} else {
|
||||
info!("Transmitting extended frame {}", i);
|
||||
can::frame::Frame::new_extended(0x1232344, &[i; 1]).unwrap()
|
||||
};
|
||||
tx.write(&tx_frame).await;
|
||||
}
|
||||
|
||||
// Try and receive all 6 packets
|
||||
for _i in 0..options.max_buffered {
|
||||
let (frame, _ts) = rx.read().await.unwrap().parts();
|
||||
match frame.id() {
|
||||
embedded_can::Id::Extended(_id) => {
|
||||
info!("Extended received! {}", frame.data()[0]);
|
||||
}
|
||||
embedded_can::Id::Standard(_id) => {
|
||||
info!("Standard received! {}", frame.data()[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -6,13 +6,15 @@
|
||||
#[path = "../common.rs"]
|
||||
mod common;
|
||||
use common::*;
|
||||
use defmt::assert;
|
||||
use embassy_executor::Spawner;
|
||||
use embassy_stm32::peripherals::*;
|
||||
use embassy_stm32::{bind_interrupts, can, Config};
|
||||
use embassy_time::{Duration, Instant};
|
||||
use embassy_time::Duration;
|
||||
use {defmt_rtt as _, panic_probe as _};
|
||||
|
||||
mod can_common;
|
||||
use can_common::*;
|
||||
|
||||
bind_interrupts!(struct Irqs2 {
|
||||
FDCAN2_IT0 => can::IT0InterruptHandler<FDCAN2>;
|
||||
FDCAN2_IT1 => can::IT1InterruptHandler<FDCAN2>;
|
||||
@ -22,14 +24,8 @@ bind_interrupts!(struct Irqs1 {
|
||||
FDCAN1_IT1 => can::IT1InterruptHandler<FDCAN1>;
|
||||
});
|
||||
|
||||
struct TestOptions {
|
||||
config: Config,
|
||||
max_latency: Duration,
|
||||
second_fifo_working: bool,
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "stm32h755zi", feature = "stm32h753zi", feature = "stm32h563zi"))]
|
||||
fn options() -> TestOptions {
|
||||
fn options() -> (Config, TestOptions) {
|
||||
use embassy_stm32::rcc;
|
||||
info!("H75 config");
|
||||
let mut c = config();
|
||||
@ -38,15 +34,17 @@ fn options() -> TestOptions {
|
||||
mode: rcc::HseMode::Oscillator,
|
||||
});
|
||||
c.rcc.mux.fdcansel = rcc::mux::Fdcansel::HSE;
|
||||
(
|
||||
c,
|
||||
TestOptions {
|
||||
config: c,
|
||||
max_latency: Duration::from_micros(1200),
|
||||
second_fifo_working: false,
|
||||
}
|
||||
max_buffered: 3,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "stm32h7a3zi"))]
|
||||
fn options() -> TestOptions {
|
||||
fn options() -> (Config, TestOptions) {
|
||||
use embassy_stm32::rcc;
|
||||
info!("H7a config");
|
||||
let mut c = config();
|
||||
@ -55,32 +53,36 @@ fn options() -> TestOptions {
|
||||
mode: rcc::HseMode::Oscillator,
|
||||
});
|
||||
c.rcc.mux.fdcansel = rcc::mux::Fdcansel::HSE;
|
||||
(
|
||||
c,
|
||||
TestOptions {
|
||||
config: c,
|
||||
max_latency: Duration::from_micros(1200),
|
||||
second_fifo_working: false,
|
||||
}
|
||||
max_buffered: 3,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "stm32g491re", feature = "stm32g431cb"))]
|
||||
fn options() -> TestOptions {
|
||||
fn options() -> (Config, TestOptions) {
|
||||
info!("G4 config");
|
||||
(
|
||||
config(),
|
||||
TestOptions {
|
||||
config: config(),
|
||||
max_latency: Duration::from_micros(500),
|
||||
second_fifo_working: true,
|
||||
}
|
||||
max_buffered: 6,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
#[embassy_executor::main]
|
||||
async fn main(_spawner: Spawner) {
|
||||
//let peripherals = embassy_stm32::init(config());
|
||||
|
||||
let options = options();
|
||||
let peripherals = embassy_stm32::init(options.config);
|
||||
let (config, options) = options();
|
||||
let peripherals = embassy_stm32::init(config);
|
||||
|
||||
let mut can = can::FdcanConfigurator::new(peripherals.FDCAN1, peripherals.PB8, peripherals.PB9, Irqs1);
|
||||
let mut can2 = can::FdcanConfigurator::new(peripherals.FDCAN2, peripherals.PB12, peripherals.PB13, Irqs2);
|
||||
let mut can = can::CanConfigurator::new(peripherals.FDCAN1, peripherals.PB8, peripherals.PB9, Irqs1);
|
||||
let mut can2 = can::CanConfigurator::new(peripherals.FDCAN2, peripherals.PB12, peripherals.PB13, Irqs2);
|
||||
|
||||
// 250k bps
|
||||
can.set_bitrate(250_000);
|
||||
@ -98,141 +100,16 @@ async fn main(_spawner: Spawner) {
|
||||
let mut can = can.into_internal_loopback_mode();
|
||||
let mut can2 = can2.into_internal_loopback_mode();
|
||||
|
||||
run_can_tests(&mut can, &options).await;
|
||||
run_can_tests(&mut can2, &options).await;
|
||||
|
||||
info!("CAN Configured");
|
||||
|
||||
let mut i: u8 = 0;
|
||||
loop {
|
||||
let tx_frame = can::frame::ClassicFrame::new_standard(0x123, &[i; 1]).unwrap();
|
||||
|
||||
info!("Transmitting frame...");
|
||||
let tx_ts = Instant::now();
|
||||
can.write(&tx_frame).await;
|
||||
|
||||
let (frame, timestamp) = can.read().await.unwrap();
|
||||
info!("Frame received!");
|
||||
|
||||
// Check data.
|
||||
assert!(i == frame.data()[0], "{} == {}", i, frame.data()[0]);
|
||||
|
||||
info!("loopback time {}", timestamp);
|
||||
info!("loopback frame {=u8}", frame.data()[0]);
|
||||
let latency = timestamp.saturating_duration_since(tx_ts);
|
||||
info!("loopback latency {} us", latency.as_micros());
|
||||
|
||||
// Theoretical minimum latency is 55us, actual is usually ~80us
|
||||
const MIN_LATENCY: Duration = Duration::from_micros(50);
|
||||
// Was failing at 150 but we are not getting a real time stamp. I'm not
|
||||
// sure if there are other delays
|
||||
assert!(
|
||||
MIN_LATENCY <= latency && latency <= options.max_latency,
|
||||
"{} <= {} <= {}",
|
||||
MIN_LATENCY,
|
||||
latency,
|
||||
options.max_latency
|
||||
);
|
||||
|
||||
i += 1;
|
||||
if i > 10 {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
let mut i: u8 = 0;
|
||||
loop {
|
||||
let tx_frame = can::frame::ClassicFrame::new_standard(0x123, &[i; 1]).unwrap();
|
||||
|
||||
info!("Transmitting frame...");
|
||||
let tx_ts = Instant::now();
|
||||
can2.write(&tx_frame).await;
|
||||
|
||||
let (frame, timestamp) = can2.read().await.unwrap();
|
||||
info!("Frame received!");
|
||||
|
||||
//print_regs().await;
|
||||
// Check data.
|
||||
assert!(i == frame.data()[0], "{} == {}", i, frame.data()[0]);
|
||||
|
||||
info!("loopback time {}", timestamp);
|
||||
info!("loopback frame {=u8}", frame.data()[0]);
|
||||
let latency = timestamp.saturating_duration_since(tx_ts);
|
||||
info!("loopback latency {} us", latency.as_micros());
|
||||
|
||||
// Theoretical minimum latency is 55us, actual is usually ~80us
|
||||
const MIN_LATENCY: Duration = Duration::from_micros(50);
|
||||
// Was failing at 150 but we are not getting a real time stamp. I'm not
|
||||
// sure if there are other delays
|
||||
assert!(
|
||||
MIN_LATENCY <= latency && latency <= options.max_latency,
|
||||
"{} <= {} <= {}",
|
||||
MIN_LATENCY,
|
||||
latency,
|
||||
options.max_latency
|
||||
);
|
||||
|
||||
i += 1;
|
||||
if i > 10 {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
let max_buffered = if options.second_fifo_working { 6 } else { 3 };
|
||||
|
||||
// Below here, check that we can receive from both FIFO0 and FIFO0
|
||||
// Above we configured FIFO1 for extended ID packets. There are only 3 slots
|
||||
// in each FIFO so make sure we write enough to fill them both up before reading.
|
||||
for i in 0..3 {
|
||||
// Try filling up the RX FIFO0 buffers with standard packets
|
||||
let tx_frame = can::frame::ClassicFrame::new_standard(0x123, &[i; 1]).unwrap();
|
||||
info!("Transmitting frame {}", i);
|
||||
can.write(&tx_frame).await;
|
||||
}
|
||||
for i in 3..max_buffered {
|
||||
// Try filling up the RX FIFO0 buffers with extended packets
|
||||
let tx_frame = can::frame::ClassicFrame::new_extended(0x1232344, &[i; 1]).unwrap();
|
||||
info!("Transmitting frame {}", i);
|
||||
can.write(&tx_frame).await;
|
||||
}
|
||||
|
||||
// Try and receive all 6 packets
|
||||
for i in 0..max_buffered {
|
||||
let (frame, _ts) = can.read().await.unwrap();
|
||||
match frame.id() {
|
||||
embedded_can::Id::Extended(id) => {
|
||||
info!("Extended received! {:x} {} {}", id.as_raw(), frame.data()[0], i);
|
||||
}
|
||||
embedded_can::Id::Standard(id) => {
|
||||
info!("Standard received! {:x} {} {}", id.as_raw(), frame.data()[0], i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Test again with a split
|
||||
let (mut tx, mut rx) = can.split();
|
||||
for i in 0..3 {
|
||||
// Try filling up the RX FIFO0 buffers with standard packets
|
||||
let tx_frame = can::frame::ClassicFrame::new_standard(0x123, &[i; 1]).unwrap();
|
||||
info!("Transmitting frame {}", i);
|
||||
tx.write(&tx_frame).await;
|
||||
}
|
||||
for i in 3..max_buffered {
|
||||
// Try filling up the RX FIFO0 buffers with extended packets
|
||||
let tx_frame = can::frame::ClassicFrame::new_extended(0x1232344, &[i; 1]).unwrap();
|
||||
info!("Transmitting frame {}", i);
|
||||
tx.write(&tx_frame).await;
|
||||
}
|
||||
|
||||
// Try and receive all 6 packets
|
||||
for i in 0..max_buffered {
|
||||
let (frame, _ts) = rx.read().await.unwrap();
|
||||
match frame.id() {
|
||||
embedded_can::Id::Extended(id) => {
|
||||
info!("Extended received! {:x} {} {}", id.as_raw(), frame.data()[0], i);
|
||||
}
|
||||
embedded_can::Id::Standard(id) => {
|
||||
info!("Standard received! {:x} {} {}", id.as_raw(), frame.data()[0], i);
|
||||
}
|
||||
}
|
||||
}
|
||||
let (mut tx2, mut rx2) = can2.split();
|
||||
run_split_can_tests(&mut tx, &mut rx, &options).await;
|
||||
run_split_can_tests(&mut tx2, &mut rx2, &options).await;
|
||||
|
||||
info!("Test OK");
|
||||
cortex_m::asm::bkpt();
|
||||
|
Loading…
Reference in New Issue
Block a user