From 6e1290b3f14f141fd2f53d001ef56186b2b80a42 Mon Sep 17 00:00:00 2001 From: Eric Yanush Date: Mon, 22 Apr 2024 01:45:09 -0600 Subject: [PATCH 1/5] Ensure bus errors are forwarded only once, enable bus off/passive/warning interrupts --- embassy-stm32/src/can/bxcan/mod.rs | 15 +++++++++++++-- embassy-stm32/src/can/bxcan/registers.rs | 16 +++++++++++++++- 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/embassy-stm32/src/can/bxcan/mod.rs b/embassy-stm32/src/can/bxcan/mod.rs index 65fd0e9c2..d43ce778d 100644 --- a/embassy-stm32/src/can/bxcan/mod.rs +++ b/embassy-stm32/src/can/bxcan/mod.rs @@ -67,12 +67,20 @@ pub struct SceInterruptHandler { impl interrupt::typelevel::Handler for SceInterruptHandler { unsafe fn on_interrupt() { - // info!("sce irq"); + info!("sce irq"); let msr = T::regs().msr(); let msr_val = msr.read(); if msr_val.erri() { - msr.modify(|v| v.set_erri(true)); + 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 + // downstream code to determine that it has already provided this bus error instance + // to the bus message consumer, we are doomed to re-provide a single error instance for + // an indefinite amount of time. + let ier = T::regs().ier(); + ier.modify(|i| i.set_errie(false)); + T::state().err_waker.wake(); } } @@ -180,6 +188,9 @@ impl<'d, T: Instance> Can<'d, T> { w.set_fmpie(0, true); w.set_fmpie(1, true); w.set_tmeie(true); + w.set_bofie(true); + w.set_epvie(true); + w.set_ewgie(true); }); T::regs().mcr().write(|w| { diff --git a/embassy-stm32/src/can/bxcan/registers.rs b/embassy-stm32/src/can/bxcan/registers.rs index 732567797..225b25d8a 100644 --- a/embassy-stm32/src/can/bxcan/registers.rs +++ b/embassy-stm32/src/can/bxcan/registers.rs @@ -145,7 +145,21 @@ impl Registers { } pub fn curr_error(&self) -> Option { - let err = { self.0.esr().read() }; + if !self.0.msr().read().erri() { + // This ensures that once a single error instance has + // been acknowledged and forwared to the bus message consumer + // we don't continue to re-forward the same error occurrance for an + // in-definite amount of time. + return None; + } + + // Since we have not already acknowledge the error, and the interrupt was + // disabled in the ISR, we will acknowledge the current error and re-enable the interrupt + // so futher errors are captured + self.0.msr().modify(|m| m.set_erri(true)); + self.0.ier().modify(|i| i.set_errie(true)); + + let err = self.0.esr().read(); if err.boff() { return Some(BusError::BusOff); } else if err.epvf() { From 68a4fd8f4a18a33a9045d7128a39d8dff5652491 Mon Sep 17 00:00:00 2001 From: Eric Yanush Date: Mon, 22 Apr 2024 01:52:10 -0600 Subject: [PATCH 2/5] Enable LEC interrupt as well --- embassy-stm32/src/can/bxcan/mod.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/embassy-stm32/src/can/bxcan/mod.rs b/embassy-stm32/src/can/bxcan/mod.rs index d43ce778d..c524c68a9 100644 --- a/embassy-stm32/src/can/bxcan/mod.rs +++ b/embassy-stm32/src/can/bxcan/mod.rs @@ -191,6 +191,7 @@ impl<'d, T: Instance> Can<'d, T> { w.set_bofie(true); w.set_epvie(true); w.set_ewgie(true); + w.set_lecie(true); }); T::regs().mcr().write(|w| { From 3e00c1ac52ac847db44f83b6d94d493356a990ff Mon Sep 17 00:00:00 2001 From: Eric Yanush Date: Mon, 22 Apr 2024 14:01:48 -0600 Subject: [PATCH 3/5] rustfmt whitespace fixes --- embassy-stm32/src/can/bxcan/mod.rs | 2 +- embassy-stm32/src/can/bxcan/registers.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/embassy-stm32/src/can/bxcan/mod.rs b/embassy-stm32/src/can/bxcan/mod.rs index c524c68a9..fa408c260 100644 --- a/embassy-stm32/src/can/bxcan/mod.rs +++ b/embassy-stm32/src/can/bxcan/mod.rs @@ -76,7 +76,7 @@ impl interrupt::typelevel::Handler for SceInterrup // 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 // downstream code to determine that it has already provided this bus error instance - // to the bus message consumer, we are doomed to re-provide a single error instance for + // to the bus message consumer, we are doomed to re-provide a single error instance for // an indefinite amount of time. let ier = T::regs().ier(); ier.modify(|i| i.set_errie(false)); diff --git a/embassy-stm32/src/can/bxcan/registers.rs b/embassy-stm32/src/can/bxcan/registers.rs index 225b25d8a..446f3ad6f 100644 --- a/embassy-stm32/src/can/bxcan/registers.rs +++ b/embassy-stm32/src/can/bxcan/registers.rs @@ -148,10 +148,10 @@ impl Registers { if !self.0.msr().read().erri() { // This ensures that once a single error instance has // been acknowledged and forwared to the bus message consumer - // we don't continue to re-forward the same error occurrance for an + // we don't continue to re-forward the same error occurrance for an // in-definite amount of time. return None; - } + } // Since we have not already acknowledge the error, and the interrupt was // disabled in the ISR, we will acknowledge the current error and re-enable the interrupt From e65503e25537ba872f02137713bdc4e5bf645a13 Mon Sep 17 00:00:00 2001 From: Eric Yanush Date: Mon, 22 Apr 2024 13:54:15 -0600 Subject: [PATCH 4/5] 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. From 5c56aff9c263308ccc866082bfbcfff2dadf80c4 Mon Sep 17 00:00:00 2001 From: Eric Yanush Date: Mon, 22 Apr 2024 14:05:28 -0600 Subject: [PATCH 5/5] rustfmt fixes --- embassy-stm32/src/can/bxcan/mod.rs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/embassy-stm32/src/can/bxcan/mod.rs b/embassy-stm32/src/can/bxcan/mod.rs index 342040c25..912634b84 100644 --- a/embassy-stm32/src/can/bxcan/mod.rs +++ b/embassy-stm32/src/can/bxcan/mod.rs @@ -261,7 +261,7 @@ impl<'d, T: Instance> Can<'d, T> { } /// 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) { @@ -274,17 +274,17 @@ impl<'d, T: Instance> Can<'d, T> { } /// 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() { @@ -292,7 +292,8 @@ impl<'d, T: Instance> Can<'d, T> { } else { Poll::Pending } - }).await; + }) + .await; T::regs().ier().modify(|i| i.set_slkie(false)); }