aboutsummaryrefslogtreecommitdiff
path: root/embassy-stm32
diff options
context:
space:
mode:
authorDario Nieuwenhuis <[email protected]>2024-04-22 22:57:22 +0000
committerGitHub <[email protected]>2024-04-22 22:57:22 +0000
commit511bee7230d319abfef02b1ac8bfb21e1d2e83d8 (patch)
treec70d2e604c6846d39c6912bee1449a14658037c5 /embassy-stm32
parente581c9f027e981622ccd13409992652bd7a68dfd (diff)
parent5c56aff9c263308ccc866082bfbcfff2dadf80c4 (diff)
Merge pull request #2854 from ericyanush/feat/add-bxcan-sleep-wakeup
Add stm32 bxCAN sleep/wakeup functionality
Diffstat (limited to 'embassy-stm32')
-rw-r--r--embassy-stm32/src/can/bxcan/mod.rs65
-rw-r--r--embassy-stm32/src/can/bxcan/registers.rs16
2 files changed, 77 insertions, 4 deletions
diff --git a/embassy-stm32/src/can/bxcan/mod.rs b/embassy-stm32/src/can/bxcan/mod.rs
index 65fd0e9c2..912634b84 100644
--- a/embassy-stm32/src/can/bxcan/mod.rs
+++ b/embassy-stm32/src/can/bxcan/mod.rs
@@ -67,12 +67,23 @@ pub struct SceInterruptHandler<T: Instance> {
67 67
68impl<T: Instance> interrupt::typelevel::Handler<T::SCEInterrupt> for SceInterruptHandler<T> { 68impl<T: Instance> interrupt::typelevel::Handler<T::SCEInterrupt> for SceInterruptHandler<T> {
69 unsafe fn on_interrupt() { 69 unsafe fn on_interrupt() {
70 // info!("sce irq"); 70 info!("sce irq");
71 let msr = T::regs().msr(); 71 let msr = T::regs().msr();
72 let msr_val = msr.read(); 72 let msr_val = msr.read();
73 73
74 if msr_val.erri() { 74 if msr_val.slaki() {
75 msr.modify(|v| v.set_erri(true)); 75 msr.modify(|m| m.set_slaki(true));
76 T::state().err_waker.wake();
77 } else if msr_val.erri() {
78 info!("Error interrupt");
79 // Disable the interrupt, but don't acknowledge the error, so that it can be
80 // forwarded off the the bus message consumer. If we don't provide some way for
81 // downstream code to determine that it has already provided this bus error instance
82 // to the bus message consumer, we are doomed to re-provide a single error instance for
83 // an indefinite amount of time.
84 let ier = T::regs().ier();
85 ier.modify(|i| i.set_errie(false));
86
76 T::state().err_waker.wake(); 87 T::state().err_waker.wake();
77 } 88 }
78 } 89 }
@@ -180,6 +191,10 @@ impl<'d, T: Instance> Can<'d, T> {
180 w.set_fmpie(0, true); 191 w.set_fmpie(0, true);
181 w.set_fmpie(1, true); 192 w.set_fmpie(1, true);
182 w.set_tmeie(true); 193 w.set_tmeie(true);
194 w.set_bofie(true);
195 w.set_epvie(true);
196 w.set_ewgie(true);
197 w.set_lecie(true);
183 }); 198 });
184 199
185 T::regs().mcr().write(|w| { 200 T::regs().mcr().write(|w| {
@@ -239,6 +254,50 @@ impl<'d, T: Instance> Can<'d, T> {
239 } 254 }
240 } 255 }
241 256
257 /// Enables or disables the peripheral from automatically wakeup when a SOF is detected on the bus
258 /// while the peripheral is in sleep mode
259 pub fn set_automatic_wakeup(&mut self, enabled: bool) {
260 Registers(T::regs()).set_automatic_wakeup(enabled);
261 }
262
263 /// Manually wake the peripheral from sleep mode.
264 ///
265 /// Waking the peripheral manually does not trigger a wake-up interrupt.
266 /// This will wait until the peripheral has acknowledged it has awoken from sleep mode
267 pub fn wakeup(&mut self) {
268 Registers(T::regs()).wakeup()
269 }
270
271 /// Check if the peripheral is currently in sleep mode
272 pub fn is_sleeping(&self) -> bool {
273 T::regs().msr().read().slak()
274 }
275
276 /// Put the peripheral in sleep mode
277 ///
278 /// When the peripherial is in sleep mode, messages can still be queued for transmission
279 /// and any previously received messages can be read from the receive FIFOs, however
280 /// no messages will be transmitted and no additional messages will be received.
281 ///
282 /// If the peripheral has automatic wakeup enabled, when a Start-of-Frame is detected
283 /// the peripheral will automatically wake and receive the incoming message.
284 pub async fn sleep(&mut self) {
285 T::regs().ier().modify(|i| i.set_slkie(true));
286 T::regs().mcr().modify(|m| m.set_sleep(true));
287
288 poll_fn(|cx| {
289 T::state().err_waker.register(cx.waker());
290 if self.is_sleeping() {
291 Poll::Ready(())
292 } else {
293 Poll::Pending
294 }
295 })
296 .await;
297
298 T::regs().ier().modify(|i| i.set_slkie(false));
299 }
300
242 /// Queues the message to be sent. 301 /// Queues the message to be sent.
243 /// 302 ///
244 /// If the TX queue is full, this will wait until there is space, therefore exerting backpressure. 303 /// If the TX queue is full, this will wait until there is space, therefore exerting backpressure.
diff --git a/embassy-stm32/src/can/bxcan/registers.rs b/embassy-stm32/src/can/bxcan/registers.rs
index 732567797..446f3ad6f 100644
--- a/embassy-stm32/src/can/bxcan/registers.rs
+++ b/embassy-stm32/src/can/bxcan/registers.rs
@@ -145,7 +145,21 @@ impl Registers {
145 } 145 }
146 146
147 pub fn curr_error(&self) -> Option<BusError> { 147 pub fn curr_error(&self) -> Option<BusError> {
148 let err = { self.0.esr().read() }; 148 if !self.0.msr().read().erri() {
149 // This ensures that once a single error instance has
150 // been acknowledged and forwared to the bus message consumer
151 // we don't continue to re-forward the same error occurrance for an
152 // in-definite amount of time.
153 return None;
154 }
155
156 // Since we have not already acknowledge the error, and the interrupt was
157 // disabled in the ISR, we will acknowledge the current error and re-enable the interrupt
158 // so futher errors are captured
159 self.0.msr().modify(|m| m.set_erri(true));
160 self.0.ier().modify(|i| i.set_errie(true));
161
162 let err = self.0.esr().read();
149 if err.boff() { 163 if err.boff() {
150 return Some(BusError::BusOff); 164 return Some(BusError::BusOff);
151 } else if err.epvf() { 165 } else if err.epvf() {