BXCAN: Create TxMode in order to support buffered TX.
This commit is contained in:
parent
26c739c2f9
commit
41b7e4a434
2 changed files with 120 additions and 15 deletions
|
@ -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(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
|
|
Loading…
Reference in a new issue