BXCAN: Create TxMode in order to support buffered TX.

This commit is contained in:
Corey Schuhen 2024-03-24 08:08:12 +10:00
parent 26c739c2f9
commit 41b7e4a434
2 changed files with 120 additions and 15 deletions

View file

@ -17,7 +17,6 @@ use crate::rcc::RccPeripheral;
use crate::{interrupt, peripherals, Peripheral};
pub mod enums;
use enums::*;
pub mod frame;
pub mod util;
@ -49,8 +48,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>();
}
}
@ -258,7 +256,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);
}
@ -277,7 +275,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(());
}
@ -294,7 +292,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())
@ -316,7 +314,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())
@ -335,6 +333,62 @@ 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>;
/// 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.
@ -365,7 +419,7 @@ impl<'d, T: Instance> CanRx<'d, T> {
T::state().rx_mode.wait_not_empty::<T>().await
}
/// Return a buffered instance of driver without CAN FD support. User must supply Buffers
/// 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>,
@ -442,6 +496,14 @@ impl<'d, T: Instance, const RX_BUF_SIZE: usize> BufferedCanRx<'d, T, RX_BUF_SIZE
}
}
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> {
@ -568,18 +630,62 @@ impl RxMode {
}
}
}
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 err_waker: AtomicWaker,
pub(crate) rx_mode: RxMode,
pub(crate) tx_mode: TxMode,
pub err_waker: AtomicWaker,
}
impl State {
pub const fn new() -> Self {
Self {
tx_waker: AtomicWaker::new(),
err_waker: AtomicWaker::new(),
rx_mode: RxMode::NonBuffered(AtomicWaker::new()),
tx_mode: TxMode::NonBuffered(AtomicWaker::new()),
err_waker: AtomicWaker::new(),
}
}
}

View file

@ -507,7 +507,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(),
}
@ -532,8 +532,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<(FdFrame, Timestamp), 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>