BXCAN: Create RxMode enum and move reader methods into it, laying foundations for different Rx buffering modes.

This commit is contained in:
Corey Schuhen 2024-03-24 07:27:29 +10:00
parent 3bdaad39e8
commit 26c739c2f9
4 changed files with 218 additions and 72 deletions

View file

@ -406,7 +406,7 @@ impl Registers {
}
}
}
pub fn curr_error(&self) -> Option<BusError> {
let err = { self.canregs.esr().read() };
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> {
// Generate timestamp as early as possible
#[cfg(feature = "time")]

View file

@ -10,7 +10,6 @@ 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;
@ -22,6 +21,9 @@ use enums::*;
pub mod frame;
pub mod util;
mod common;
pub use self::common::{BufferedCanReceiver, BufferedCanSender, Timestamp};
/// Contains CAN frame and additional metadata.
///
/// 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> {
unsafe fn on_interrupt() {
// info!("rx0 irq");
Can::<T>::receive_fifo(RxFifo::Fifo0);
T::state().rx_mode.on_interrupt::<T>(RxFifo::Fifo0);
}
}
@ -71,8 +72,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);
}
}
@ -99,16 +99,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))]
@ -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
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) {
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,
};
}
T::state().rx_mode.wait_not_empty::<T>().await
}
/// 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
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);
/// Return a buffered instance of driver without CAN FD support. 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)
}
}
/// 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 {
pub tx_waker: AtomicWaker,
pub err_waker: AtomicWaker,
pub rx_queue: Channel<CriticalSectionRawMutex, Envelope, 32>,
pub(crate) rx_mode: RxMode,
}
impl State {
@ -459,7 +579,7 @@ impl State {
Self {
tx_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 {
fn regs() -> crate::pac::can::Can;
fn state() -> &'static State;
unsafe fn mut_state() -> &'static mut State;
}
/// CAN instance trait.
@ -495,9 +616,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() }
}
}
@ -561,5 +685,3 @@ impl Index for crate::can::bx::Mailbox {
}
}
}

View file

@ -18,9 +18,13 @@ pub(crate) struct ClassicBufferedTxInner {
pub tx_receiver: DynamicReceiver<'static, ClassicFrame>,
}
#[cfg(any(can_fdcan_v1, can_fdcan_h7))]
pub(crate) struct FdBufferedRxInner {
pub rx_sender: DynamicSender<'static, Result<(FdFrame, Timestamp), BusError>>,
}
#[cfg(any(can_fdcan_v1, can_fdcan_h7))]
pub(crate) struct FdBufferedTxInner {
pub tx_receiver: DynamicReceiver<'static, FdFrame>,
}

View file

@ -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,
}