Merge #645
645: stm32 usart: Fix RX interrupt flag handling r=lulf a=timokroeger * 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. Tested on v1 hardware, lets see if v2 still succeeds on CI. Co-authored-by: Timo Kröger <timokroeger93@gmail.com>
This commit is contained in:
commit
d381b8e2b6
@ -395,29 +395,39 @@ mod buffered {
|
|||||||
let r = self.uart.inner.regs();
|
let r = self.uart.inner.regs();
|
||||||
unsafe {
|
unsafe {
|
||||||
let sr = sr(r).read();
|
let sr = sr(r).read();
|
||||||
// TODO: do we want to handle interrupts the same way on v1 hardware?
|
clear_interrupt_flags(r, sr);
|
||||||
|
|
||||||
|
// This read also clears the error and idle interrupt flags on v1.
|
||||||
|
let b = rdr(r).read_volatile();
|
||||||
|
|
||||||
|
if sr.rxne() {
|
||||||
if sr.pe() {
|
if sr.pe() {
|
||||||
clear_interrupt_flag(r, InterruptFlag::PE);
|
warn!("Parity error");
|
||||||
trace!("Parity error");
|
|
||||||
} else if sr.fe() {
|
|
||||||
clear_interrupt_flag(r, InterruptFlag::FE);
|
|
||||||
trace!("Framing error");
|
|
||||||
} else if sr.ne() {
|
|
||||||
clear_interrupt_flag(r, InterruptFlag::NE);
|
|
||||||
trace!("Noise error");
|
|
||||||
} else if sr.ore() {
|
|
||||||
clear_interrupt_flag(r, InterruptFlag::ORE);
|
|
||||||
trace!("Overrun error");
|
|
||||||
} else if sr.rxne() {
|
|
||||||
let buf = self.rx.push_buf();
|
|
||||||
if buf.is_empty() {
|
|
||||||
self.rx_waker.wake();
|
|
||||||
} else {
|
|
||||||
buf[0] = rdr(r).read_volatile();
|
|
||||||
self.rx.push(1);
|
|
||||||
}
|
}
|
||||||
} else if sr.idle() {
|
if sr.fe() {
|
||||||
clear_interrupt_flag(r, InterruptFlag::IDLE);
|
warn!("Framing error");
|
||||||
|
}
|
||||||
|
if sr.ne() {
|
||||||
|
warn!("Noise error");
|
||||||
|
}
|
||||||
|
if sr.ore() {
|
||||||
|
warn!("Overrun error");
|
||||||
|
}
|
||||||
|
|
||||||
|
let buf = self.rx.push_buf();
|
||||||
|
if !buf.is_empty() {
|
||||||
|
buf[0] = b;
|
||||||
|
self.rx.push(1);
|
||||||
|
} else {
|
||||||
|
warn!("RX buffer full, discard received byte");
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.rx.is_full() {
|
||||||
|
self.rx_waker.wake();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if sr.idle() {
|
||||||
self.rx_waker.wake();
|
self.rx_waker.wake();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -542,28 +552,14 @@ fn rdr(r: crate::pac::usart::Usart) -> *mut u8 {
|
|||||||
r.dr().ptr() as _
|
r.dr().ptr() as _
|
||||||
}
|
}
|
||||||
|
|
||||||
enum InterruptFlag {
|
|
||||||
PE,
|
|
||||||
FE,
|
|
||||||
NE,
|
|
||||||
ORE,
|
|
||||||
IDLE,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(usart_v1)]
|
#[cfg(usart_v1)]
|
||||||
fn sr(r: crate::pac::usart::Usart) -> crate::pac::common::Reg<regs::Sr, crate::pac::common::RW> {
|
fn sr(r: crate::pac::usart::Usart) -> crate::pac::common::Reg<regs::Sr, crate::pac::common::RW> {
|
||||||
r.sr()
|
r.sr()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(usart_v1)]
|
#[cfg(usart_v1)]
|
||||||
unsafe fn clear_interrupt_flag(r: crate::pac::usart::Usart, _flag: InterruptFlag) {
|
unsafe fn clear_interrupt_flags(_r: crate::pac::usart::Usart, _sr: regs::Sr) {
|
||||||
// This bit is set by hardware when noise is detected on a received frame. It is cleared by a
|
// On v1 the flags are cleared implicitly by reads and writes to DR.
|
||||||
// software sequence (an read to the USART_SR register followed by a read to the
|
|
||||||
// USART_DR register).
|
|
||||||
|
|
||||||
// this is the same as what st's HAL does on v1 hardware
|
|
||||||
r.sr().read();
|
|
||||||
r.dr().read();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(usart_v2)]
|
#[cfg(usart_v2)]
|
||||||
@ -582,26 +578,8 @@ fn sr(r: crate::pac::usart::Usart) -> crate::pac::common::Reg<regs::Ixr, crate::
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(usart_v2)]
|
#[cfg(usart_v2)]
|
||||||
#[inline]
|
unsafe fn clear_interrupt_flags(r: crate::pac::usart::Usart, sr: regs::Ixr) {
|
||||||
unsafe fn clear_interrupt_flag(r: crate::pac::usart::Usart, flag: InterruptFlag) {
|
r.icr().write(|w| *w = sr);
|
||||||
// v2 has a separate register for clearing flags (nice)
|
|
||||||
match flag {
|
|
||||||
InterruptFlag::PE => r.icr().write(|w| {
|
|
||||||
w.set_pe(true);
|
|
||||||
}),
|
|
||||||
InterruptFlag::FE => r.icr().write(|w| {
|
|
||||||
w.set_fe(true);
|
|
||||||
}),
|
|
||||||
InterruptFlag::NE => r.icr().write(|w| {
|
|
||||||
w.set_ne(true);
|
|
||||||
}),
|
|
||||||
InterruptFlag::ORE => r.icr().write(|w| {
|
|
||||||
w.set_ore(true);
|
|
||||||
}),
|
|
||||||
InterruptFlag::IDLE => r.icr().write(|w| {
|
|
||||||
w.set_idle(true);
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) mod sealed {
|
pub(crate) mod sealed {
|
||||||
|
Loading…
Reference in New Issue
Block a user