From e65503e25537ba872f02137713bdc4e5bf645a13 Mon Sep 17 00:00:00 2001 From: Eric Yanush Date: Mon, 22 Apr 2024 13:54:15 -0600 Subject: [PATCH] Add sleep/wakeup handling for bxCAN peripherals --- embassy-stm32/src/can/bxcan/mod.rs | 48 +++++++++++++++++++++++++++++- 1 file changed, 47 insertions(+), 1 deletion(-) diff --git a/embassy-stm32/src/can/bxcan/mod.rs b/embassy-stm32/src/can/bxcan/mod.rs index fa408c260..342040c25 100644 --- a/embassy-stm32/src/can/bxcan/mod.rs +++ b/embassy-stm32/src/can/bxcan/mod.rs @@ -71,7 +71,10 @@ impl interrupt::typelevel::Handler for SceInterrup let msr = T::regs().msr(); let msr_val = msr.read(); - if msr_val.erri() { + if msr_val.slaki() { + msr.modify(|m| m.set_slaki(true)); + T::state().err_waker.wake(); + } else if msr_val.erri() { info!("Error interrupt"); // Disable the interrupt, but don't acknowledge the error, so that it can be // forwarded off the the bus message consumer. If we don't provide some way for @@ -251,6 +254,49 @@ impl<'d, T: Instance> Can<'d, T> { } } + /// Enables or disables the peripheral from automatically wakeup when a SOF is detected on the bus + /// while the peripheral is in sleep mode + pub fn set_automatic_wakeup(&mut self, enabled: bool) { + Registers(T::regs()).set_automatic_wakeup(enabled); + } + + /// Manually wake the peripheral from sleep mode. + /// + /// Waking the peripheral manually does not trigger a wake-up interrupt. + /// This will wait until the peripheral has acknowledged it has awoken from sleep mode + pub fn wakeup(&mut self) { + Registers(T::regs()).wakeup() + } + + /// Check if the peripheral is currently in sleep mode + pub fn is_sleeping(&self) -> bool { + T::regs().msr().read().slak() + } + + /// Put the peripheral in sleep mode + /// + /// When the peripherial is in sleep mode, messages can still be queued for transmission + /// and any previously received messages can be read from the receive FIFOs, however + /// no messages will be transmitted and no additional messages will be received. + /// + /// If the peripheral has automatic wakeup enabled, when a Start-of-Frame is detected + /// the peripheral will automatically wake and receive the incoming message. + pub async fn sleep(&mut self) { + T::regs().ier().modify(|i| i.set_slkie(true)); + T::regs().mcr().modify(|m| m.set_sleep(true)); + + poll_fn(|cx| { + T::state().err_waker.register(cx.waker()); + if self.is_sleeping() { + Poll::Ready(()) + } else { + Poll::Pending + } + }).await; + + T::regs().ier().modify(|i| i.set_slkie(false)); + } + /// Queues the message to be sent. /// /// If the TX queue is full, this will wait until there is space, therefore exerting backpressure.