diff options
| author | Timo Kröger <[email protected]> | 2022-02-25 13:19:51 +0100 |
|---|---|---|
| committer | Timo Kröger <[email protected]> | 2022-02-25 14:32:39 +0100 |
| commit | 48f700b35c5371f1df374a615a5d68adff02597a (patch) | |
| tree | 8dca0824c0c25e19fd48d3c15466b207da588ea3 | |
| parent | 6da4b6636435b350b21f0b30e7175a2a6899acdc (diff) | |
stm32 usart: Fix RX interrupt flag handling
* On v1 interrupts cannot be cleared individually.
Instead they are cleared implicitly by reading or writing DR (which we do now).
* Multiple error flags can be set at the same time:
Handle them all in one go intstead of re-entering the ISR for each one so that
we do not lose any error flags on v1 hardware.
* Wake when the RX buffer becomes full: This allows fast running chips to pull data
from the buffer before receiving the next byte.
| -rw-r--r-- | embassy-stm32/src/usart/mod.rs | 90 |
1 files changed, 34 insertions, 56 deletions
diff --git a/embassy-stm32/src/usart/mod.rs b/embassy-stm32/src/usart/mod.rs index c757769da..12e5d503d 100644 --- a/embassy-stm32/src/usart/mod.rs +++ b/embassy-stm32/src/usart/mod.rs | |||
| @@ -395,29 +395,39 @@ mod buffered { | |||
| 395 | let r = self.uart.inner.regs(); | 395 | let r = self.uart.inner.regs(); |
| 396 | unsafe { | 396 | unsafe { |
| 397 | let sr = sr(r).read(); | 397 | let sr = sr(r).read(); |
| 398 | // TODO: do we want to handle interrupts the same way on v1 hardware? | 398 | clear_interrupt_flags(r, sr); |
| 399 | if sr.pe() { | 399 | |
| 400 | clear_interrupt_flag(r, InterruptFlag::PE); | 400 | // This read also clears the error and idle interrupt flags on v1. |
| 401 | trace!("Parity error"); | 401 | let b = rdr(r).read_volatile(); |
| 402 | } else if sr.fe() { | 402 | |
| 403 | clear_interrupt_flag(r, InterruptFlag::FE); | 403 | if sr.rxne() { |
| 404 | trace!("Framing error"); | 404 | if sr.pe() { |
| 405 | } else if sr.ne() { | 405 | warn!("Parity error"); |
| 406 | clear_interrupt_flag(r, InterruptFlag::NE); | 406 | } |
| 407 | trace!("Noise error"); | 407 | if sr.fe() { |
| 408 | } else if sr.ore() { | 408 | warn!("Framing error"); |
| 409 | clear_interrupt_flag(r, InterruptFlag::ORE); | 409 | } |
| 410 | trace!("Overrun error"); | 410 | if sr.ne() { |
| 411 | } else if sr.rxne() { | 411 | warn!("Noise error"); |
| 412 | } | ||
| 413 | if sr.ore() { | ||
| 414 | warn!("Overrun error"); | ||
| 415 | } | ||
| 416 | |||
| 412 | let buf = self.rx.push_buf(); | 417 | let buf = self.rx.push_buf(); |
| 413 | if buf.is_empty() { | 418 | if !buf.is_empty() { |
| 414 | self.rx_waker.wake(); | 419 | buf[0] = b; |
| 415 | } else { | ||
| 416 | buf[0] = rdr(r).read_volatile(); | ||
| 417 | self.rx.push(1); | 420 | self.rx.push(1); |
| 421 | } else { | ||
| 422 | warn!("RX buffer full, discard received byte"); | ||
| 423 | } | ||
| 424 | |||
| 425 | if self.rx.is_full() { | ||
| 426 | self.rx_waker.wake(); | ||
| 418 | } | 427 | } |
| 419 | } else if sr.idle() { | 428 | } |
| 420 | clear_interrupt_flag(r, InterruptFlag::IDLE); | 429 | |
| 430 | if sr.idle() { | ||
| 421 | self.rx_waker.wake(); | 431 | self.rx_waker.wake(); |
| 422 | }; | 432 | }; |
| 423 | } | 433 | } |
| @@ -542,28 +552,14 @@ fn rdr(r: crate::pac::usart::Usart) -> *mut u8 { | |||
| 542 | r.dr().ptr() as _ | 552 | r.dr().ptr() as _ |
| 543 | } | 553 | } |
| 544 | 554 | ||
| 545 | enum InterruptFlag { | ||
| 546 | PE, | ||
| 547 | FE, | ||
| 548 | NE, | ||
| 549 | ORE, | ||
| 550 | IDLE, | ||
| 551 | } | ||
| 552 | |||
| 553 | #[cfg(usart_v1)] | 555 | #[cfg(usart_v1)] |
| 554 | fn sr(r: crate::pac::usart::Usart) -> crate::pac::common::Reg<regs::Sr, crate::pac::common::RW> { | 556 | fn sr(r: crate::pac::usart::Usart) -> crate::pac::common::Reg<regs::Sr, crate::pac::common::RW> { |
| 555 | r.sr() | 557 | r.sr() |
| 556 | } | 558 | } |
| 557 | 559 | ||
| 558 | #[cfg(usart_v1)] | 560 | #[cfg(usart_v1)] |
| 559 | unsafe fn clear_interrupt_flag(r: crate::pac::usart::Usart, _flag: InterruptFlag) { | 561 | unsafe fn clear_interrupt_flags(_r: crate::pac::usart::Usart, _sr: regs::Sr) { |
| 560 | // This bit is set by hardware when noise is detected on a received frame. It is cleared by a | 562 | // On v1 the flags are cleared implicitly by reads and writes to DR. |
| 561 | // software sequence (an read to the USART_SR register followed by a read to the | ||
| 562 | // USART_DR register). | ||
| 563 | |||
| 564 | // this is the same as what st's HAL does on v1 hardware | ||
| 565 | r.sr().read(); | ||
| 566 | r.dr().read(); | ||
| 567 | } | 563 | } |
| 568 | 564 | ||
| 569 | #[cfg(usart_v2)] | 565 | #[cfg(usart_v2)] |
| @@ -582,26 +578,8 @@ fn sr(r: crate::pac::usart::Usart) -> crate::pac::common::Reg<regs::Ixr, crate:: | |||
| 582 | } | 578 | } |
| 583 | 579 | ||
| 584 | #[cfg(usart_v2)] | 580 | #[cfg(usart_v2)] |
| 585 | #[inline] | 581 | unsafe fn clear_interrupt_flags(r: crate::pac::usart::Usart, sr: regs::Ixr) { |
| 586 | unsafe fn clear_interrupt_flag(r: crate::pac::usart::Usart, flag: InterruptFlag) { | 582 | r.icr().write(|w| *w = sr); |
| 587 | // v2 has a separate register for clearing flags (nice) | ||
| 588 | match flag { | ||
| 589 | InterruptFlag::PE => r.icr().write(|w| { | ||
| 590 | w.set_pe(true); | ||
| 591 | }), | ||
| 592 | InterruptFlag::FE => r.icr().write(|w| { | ||
| 593 | w.set_fe(true); | ||
| 594 | }), | ||
| 595 | InterruptFlag::NE => r.icr().write(|w| { | ||
| 596 | w.set_ne(true); | ||
| 597 | }), | ||
| 598 | InterruptFlag::ORE => r.icr().write(|w| { | ||
| 599 | w.set_ore(true); | ||
| 600 | }), | ||
| 601 | InterruptFlag::IDLE => r.icr().write(|w| { | ||
| 602 | w.set_idle(true); | ||
| 603 | }), | ||
| 604 | } | ||
| 605 | } | 583 | } |
| 606 | 584 | ||
| 607 | pub(crate) mod sealed { | 585 | pub(crate) mod sealed { |
