BXCAN: Create RxMode enum and move reader methods into it, laying foundations for different Rx buffering modes.
This commit is contained in:
parent
3bdaad39e8
commit
26c739c2f9
4 changed files with 218 additions and 72 deletions
|
@ -406,7 +406,7 @@ impl Registers {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn curr_error(&self) -> Option<BusError> {
|
pub fn curr_error(&self) -> Option<BusError> {
|
||||||
let err = { self.canregs.esr().read() };
|
let err = { self.canregs.esr().read() };
|
||||||
if err.boff() {
|
if err.boff() {
|
||||||
|
@ -585,6 +585,16 @@ impl Registers {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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> {
|
pub fn receive_fifo(&self, fifo: crate::can::_version::bx::RxFifo) -> Option<Envelope> {
|
||||||
// Generate timestamp as early as possible
|
// Generate timestamp as early as possible
|
||||||
#[cfg(feature = "time")]
|
#[cfg(feature = "time")]
|
||||||
|
|
|
@ -10,7 +10,6 @@ use embassy_hal_internal::{into_ref, PeripheralRef};
|
||||||
use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
|
use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
|
||||||
use embassy_sync::channel::Channel;
|
use embassy_sync::channel::Channel;
|
||||||
use embassy_sync::waitqueue::AtomicWaker;
|
use embassy_sync::waitqueue::AtomicWaker;
|
||||||
use futures::FutureExt;
|
|
||||||
|
|
||||||
use crate::gpio::AFType;
|
use crate::gpio::AFType;
|
||||||
use crate::interrupt::typelevel::Interrupt;
|
use crate::interrupt::typelevel::Interrupt;
|
||||||
|
@ -22,6 +21,9 @@ use enums::*;
|
||||||
pub mod frame;
|
pub mod frame;
|
||||||
pub mod util;
|
pub mod util;
|
||||||
|
|
||||||
|
mod common;
|
||||||
|
pub use self::common::{BufferedCanReceiver, BufferedCanSender, Timestamp};
|
||||||
|
|
||||||
/// Contains CAN frame and additional metadata.
|
/// Contains CAN frame and additional metadata.
|
||||||
///
|
///
|
||||||
/// Timestamp is available if `time` feature is enabled.
|
/// Timestamp is available if `time` feature is enabled.
|
||||||
|
@ -59,8 +61,7 @@ pub struct Rx0InterruptHandler<T: Instance> {
|
||||||
|
|
||||||
impl<T: Instance> interrupt::typelevel::Handler<T::RX0Interrupt> for Rx0InterruptHandler<T> {
|
impl<T: Instance> interrupt::typelevel::Handler<T::RX0Interrupt> for Rx0InterruptHandler<T> {
|
||||||
unsafe fn on_interrupt() {
|
unsafe fn on_interrupt() {
|
||||||
// info!("rx0 irq");
|
T::state().rx_mode.on_interrupt::<T>(RxFifo::Fifo0);
|
||||||
Can::<T>::receive_fifo(RxFifo::Fifo0);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -71,8 +72,7 @@ pub struct Rx1InterruptHandler<T: Instance> {
|
||||||
|
|
||||||
impl<T: Instance> interrupt::typelevel::Handler<T::RX1Interrupt> for Rx1InterruptHandler<T> {
|
impl<T: Instance> interrupt::typelevel::Handler<T::RX1Interrupt> for Rx1InterruptHandler<T> {
|
||||||
unsafe fn on_interrupt() {
|
unsafe fn on_interrupt() {
|
||||||
// info!("rx1 irq");
|
T::state().rx_mode.on_interrupt::<T>(RxFifo::Fifo1);
|
||||||
Can::<T>::receive_fifo(RxFifo::Fifo1);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -99,16 +99,6 @@ pub struct Can<'d, T: Instance> {
|
||||||
can: crate::can::bx::Can<BxcanInstance<'d, T>>,
|
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`
|
/// Error returned by `try_write`
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
|
@ -226,34 +216,19 @@ impl<'d, T: Instance> Can<'d, T> {
|
||||||
///
|
///
|
||||||
/// Returns a tuple of the time the message was received and the message frame
|
/// Returns a tuple of the time the message was received and the message frame
|
||||||
pub async fn read(&mut self) -> Result<Envelope, BusError> {
|
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.
|
/// Attempts to read a CAN frame without blocking.
|
||||||
///
|
///
|
||||||
/// Returns [Err(TryReadError::Empty)] if there are no frames in the rx queue.
|
/// Returns [Err(TryReadError::Empty)] if there are no frames in the rx queue.
|
||||||
pub fn try_read(&mut self) -> Result<Envelope, TryReadError> {
|
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.
|
/// Waits while receive queue is empty.
|
||||||
pub async fn wait_not_empty(&mut self) {
|
pub async fn wait_not_empty(&mut self) {
|
||||||
self.split().1.wait_not_empty().await
|
T::state().rx_mode.wait_not_empty::<T>().await
|
||||||
}
|
|
||||||
|
|
||||||
unsafe fn receive_fifo(fifo: RxFifo) {
|
|
||||||
let state = T::state();
|
|
||||||
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 _ = state.rx_queue.try_send(envelope);
|
|
||||||
}
|
|
||||||
None => return,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Split the CAN driver into transmit and receive halves.
|
/// Split the CAN driver into transmit and receive halves.
|
||||||
|
@ -375,51 +350,95 @@ impl<'d, T: Instance> CanRx<'d, T> {
|
||||||
///
|
///
|
||||||
/// Returns a tuple of the time the message was received and the message frame
|
/// Returns a tuple of the time the message was received and the message frame
|
||||||
pub async fn read(&mut self) -> Result<Envelope, BusError> {
|
pub async fn read(&mut self) -> Result<Envelope, BusError> {
|
||||||
poll_fn(|cx| {
|
T::state().rx_mode.read::<T>().await
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Attempts to read a CAN frame without blocking.
|
/// Attempts to read a CAN frame without blocking.
|
||||||
///
|
///
|
||||||
/// Returns [Err(TryReadError::Empty)] if there are no frames in the rx queue.
|
/// Returns [Err(TryReadError::Empty)] if there are no frames in the rx queue.
|
||||||
pub fn try_read(&mut self) -> Result<Envelope, TryReadError> {
|
pub fn try_read(&mut self) -> Result<Envelope, TryReadError> {
|
||||||
if let Ok(envelope) = T::state().rx_queue.try_receive() {
|
T::state().rx_mode.try_read::<T>()
|
||||||
return Ok(envelope);
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(err) = self.curr_error() {
|
|
||||||
return Err(TryReadError::BusError(err));
|
|
||||||
}
|
|
||||||
|
|
||||||
Err(TryReadError::Empty)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Waits while receive queue is empty.
|
/// Waits while receive queue is empty.
|
||||||
pub async fn wait_not_empty(&mut self) {
|
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> {
|
/// Return a buffered instance of driver without CAN FD support. User must supply Buffers
|
||||||
let err = { T::regs().esr().read() };
|
pub fn buffered<const RX_BUF_SIZE: usize>(
|
||||||
if err.boff() {
|
self,
|
||||||
return Some(BusError::BusOff);
|
rxb: &'static mut RxBuf<RX_BUF_SIZE>,
|
||||||
} else if err.epvf() {
|
) -> BufferedCanRx<'d, T, RX_BUF_SIZE> {
|
||||||
return Some(BusError::BusPassive);
|
BufferedCanRx::new(self.rx, rxb)
|
||||||
} else if err.ewgf() {
|
}
|
||||||
return Some(BusError::BusWarning);
|
}
|
||||||
} else if let Some(err) = err.lec().into_bus_err() {
|
|
||||||
return Some(err);
|
/// User supplied buffer for RX Buffering
|
||||||
|
pub type RxBuf<const BUF_SIZE: usize> =
|
||||||
|
Channel<CriticalSectionRawMutex, Result<(Frame, Timestamp), 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<(Frame, Timestamp), 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((frame, ts)) => Ok(Envelope { ts, frame }),
|
||||||
|
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")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
None
|
}
|
||||||
|
|
||||||
|
/// 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()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -448,10 +467,111 @@ 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.frame, envelope.ts)));
|
||||||
|
}
|
||||||
|
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")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
struct State {
|
struct State {
|
||||||
pub tx_waker: AtomicWaker,
|
pub tx_waker: AtomicWaker,
|
||||||
pub err_waker: AtomicWaker,
|
pub err_waker: AtomicWaker,
|
||||||
pub rx_queue: Channel<CriticalSectionRawMutex, Envelope, 32>,
|
pub(crate) rx_mode: RxMode,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl State {
|
impl State {
|
||||||
|
@ -459,7 +579,7 @@ impl State {
|
||||||
Self {
|
Self {
|
||||||
tx_waker: AtomicWaker::new(),
|
tx_waker: AtomicWaker::new(),
|
||||||
err_waker: AtomicWaker::new(),
|
err_waker: AtomicWaker::new(),
|
||||||
rx_queue: Channel::new(),
|
rx_mode: RxMode::NonBuffered(AtomicWaker::new()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -467,6 +587,7 @@ impl State {
|
||||||
trait SealedInstance {
|
trait SealedInstance {
|
||||||
fn regs() -> crate::pac::can::Can;
|
fn regs() -> crate::pac::can::Can;
|
||||||
fn state() -> &'static State;
|
fn state() -> &'static State;
|
||||||
|
unsafe fn mut_state() -> &'static mut State;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// CAN instance trait.
|
/// CAN instance trait.
|
||||||
|
@ -495,9 +616,12 @@ foreach_peripheral!(
|
||||||
crate::pac::$inst
|
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 {
|
fn state() -> &'static State {
|
||||||
static STATE: State = State::new();
|
unsafe { peripherals::$inst::mut_state() }
|
||||||
&STATE
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -561,5 +685,3 @@ impl Index for crate::can::bx::Mailbox {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -18,9 +18,13 @@ pub(crate) struct ClassicBufferedTxInner {
|
||||||
pub tx_receiver: DynamicReceiver<'static, ClassicFrame>,
|
pub tx_receiver: DynamicReceiver<'static, ClassicFrame>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(any(can_fdcan_v1, can_fdcan_h7))]
|
||||||
|
|
||||||
pub(crate) struct FdBufferedRxInner {
|
pub(crate) struct FdBufferedRxInner {
|
||||||
pub rx_sender: DynamicSender<'static, Result<(FdFrame, Timestamp), BusError>>,
|
pub rx_sender: DynamicSender<'static, Result<(FdFrame, Timestamp), BusError>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(any(can_fdcan_v1, can_fdcan_h7))]
|
||||||
pub(crate) struct FdBufferedTxInner {
|
pub(crate) struct FdBufferedTxInner {
|
||||||
pub tx_receiver: DynamicReceiver<'static, FdFrame>,
|
pub tx_receiver: DynamicReceiver<'static, FdFrame>,
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,3 +40,13 @@ pub enum FrameCreateError {
|
||||||
/// Invalid ID.
|
/// Invalid ID.
|
||||||
InvalidCanId,
|
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,
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue