Merge pull request #3989 from embedded-rust-iml/fix/ringbuffered-error-handling
Rework status handling (idle and errors) in ringbuffered uart
This commit is contained in:
commit
a44abaf7e4
@ -18,11 +18,6 @@ use crate::gpio::{self, AfType, AnyPin, OutputType, Pull, SealedPin as _, Speed}
|
|||||||
use crate::interrupt::typelevel::Interrupt as _;
|
use crate::interrupt::typelevel::Interrupt as _;
|
||||||
use crate::interrupt::{self, Interrupt, InterruptExt};
|
use crate::interrupt::{self, Interrupt, InterruptExt};
|
||||||
use crate::mode::{Async, Blocking, Mode};
|
use crate::mode::{Async, Blocking, Mode};
|
||||||
#[allow(unused_imports)]
|
|
||||||
#[cfg(not(any(usart_v1, usart_v2)))]
|
|
||||||
use crate::pac::usart::regs::Isr as Sr;
|
|
||||||
#[cfg(any(usart_v1, usart_v2))]
|
|
||||||
use crate::pac::usart::regs::Sr;
|
|
||||||
#[cfg(not(any(usart_v1, usart_v2)))]
|
#[cfg(not(any(usart_v1, usart_v2)))]
|
||||||
use crate::pac::usart::Lpuart as Regs;
|
use crate::pac::usart::Lpuart as Regs;
|
||||||
#[cfg(any(usart_v1, usart_v2))]
|
#[cfg(any(usart_v1, usart_v2))]
|
||||||
@ -403,7 +398,7 @@ pub struct UartRx<'d, M: Mode> {
|
|||||||
rx_dma: Option<ChannelAndRequest<'d>>,
|
rx_dma: Option<ChannelAndRequest<'d>>,
|
||||||
detect_previous_overrun: bool,
|
detect_previous_overrun: bool,
|
||||||
#[cfg(any(usart_v1, usart_v2))]
|
#[cfg(any(usart_v1, usart_v2))]
|
||||||
buffered_sr: stm32_metapac::usart::regs::Sr,
|
buffered_sr: regs::Sr,
|
||||||
_phantom: PhantomData<M>,
|
_phantom: PhantomData<M>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -950,7 +945,7 @@ impl<'d, M: Mode> UartRx<'d, M> {
|
|||||||
rx_dma,
|
rx_dma,
|
||||||
detect_previous_overrun: config.detect_previous_overrun,
|
detect_previous_overrun: config.detect_previous_overrun,
|
||||||
#[cfg(any(usart_v1, usart_v2))]
|
#[cfg(any(usart_v1, usart_v2))]
|
||||||
buffered_sr: stm32_metapac::usart::regs::Sr(0),
|
buffered_sr: regs::Sr(0),
|
||||||
};
|
};
|
||||||
this.enable_and_configure(&config)?;
|
this.enable_and_configure(&config)?;
|
||||||
Ok(this)
|
Ok(this)
|
||||||
@ -1449,7 +1444,7 @@ impl<'d, M: Mode> Uart<'d, M> {
|
|||||||
rx_dma,
|
rx_dma,
|
||||||
detect_previous_overrun: config.detect_previous_overrun,
|
detect_previous_overrun: config.detect_previous_overrun,
|
||||||
#[cfg(any(usart_v1, usart_v2))]
|
#[cfg(any(usart_v1, usart_v2))]
|
||||||
buffered_sr: stm32_metapac::usart::regs::Sr(0),
|
buffered_sr: regs::Sr(0),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
this.enable_and_configure(&config)?;
|
this.enable_and_configure(&config)?;
|
||||||
|
|||||||
@ -7,16 +7,12 @@ use embassy_embedded_hal::SetConfig;
|
|||||||
use embedded_io_async::ReadReady;
|
use embedded_io_async::ReadReady;
|
||||||
use futures_util::future::{select, Either};
|
use futures_util::future::{select, Either};
|
||||||
|
|
||||||
use super::{
|
use super::{rdr, reconfigure, set_baudrate, sr, Config, ConfigError, Error, Info, State, UartRx};
|
||||||
clear_interrupt_flags, rdr, reconfigure, set_baudrate, sr, Config, ConfigError, Error, Info, State, UartRx,
|
|
||||||
};
|
|
||||||
use crate::dma::ReadableRingBuffer;
|
use crate::dma::ReadableRingBuffer;
|
||||||
use crate::gpio::{AnyPin, SealedPin as _};
|
use crate::gpio::{AnyPin, SealedPin as _};
|
||||||
use crate::mode::Async;
|
use crate::mode::Async;
|
||||||
#[cfg(any(usart_v3, usart_v4))]
|
|
||||||
use crate::pac::usart::regs;
|
|
||||||
use crate::time::Hertz;
|
use crate::time::Hertz;
|
||||||
use crate::usart::{Regs, Sr};
|
use crate::usart::Regs;
|
||||||
use crate::Peri;
|
use crate::Peri;
|
||||||
|
|
||||||
/// Rx-only Ring-buffered UART Driver
|
/// Rx-only Ring-buffered UART Driver
|
||||||
@ -99,8 +95,6 @@ impl<'d> RingBufferedUartRx<'d> {
|
|||||||
// enable idle line interrupt
|
// enable idle line interrupt
|
||||||
w.set_idleie(true);
|
w.set_idleie(true);
|
||||||
});
|
});
|
||||||
// Clear all potential error interrupt flags
|
|
||||||
clear_interrupt_flags(r, sr(r).read());
|
|
||||||
r.cr3().modify(|w| {
|
r.cr3().modify(|w| {
|
||||||
// enable Error Interrupt: (Frame error, Noise error, Overrun error)
|
// enable Error Interrupt: (Frame error, Noise error, Overrun error)
|
||||||
w.set_eie(true);
|
w.set_eie(true);
|
||||||
@ -134,17 +128,15 @@ impl<'d> RingBufferedUartRx<'d> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// (Re-)start DMA and Uart if it is not running (has not been started yet or has failed), and
|
/// (Re-)start DMA and Uart if it is not running (has not been started yet or has failed), and
|
||||||
/// check for errors in status register. Error flags are cleared in `start_uart()` so they need
|
/// check for errors in status register. Error flags are checked/cleared first.
|
||||||
/// to be read first without returning yet.
|
|
||||||
fn start_dma_or_check_errors(&mut self) -> Result<(), Error> {
|
fn start_dma_or_check_errors(&mut self) -> Result<(), Error> {
|
||||||
let r = self.info.regs;
|
let r = self.info.regs;
|
||||||
|
|
||||||
let sr = clear_idle_flag(r);
|
check_idle_and_errors(r)?;
|
||||||
let res = check_for_errors(sr);
|
|
||||||
if !r.cr3().read().dmar() {
|
if !r.cr3().read().dmar() {
|
||||||
self.start_uart();
|
self.start_uart();
|
||||||
}
|
}
|
||||||
res
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Read bytes that are readily available in the ring buffer.
|
/// Read bytes that are readily available in the ring buffer.
|
||||||
@ -191,13 +183,7 @@ impl<'d> RingBufferedUartRx<'d> {
|
|||||||
|
|
||||||
compiler_fence(Ordering::SeqCst);
|
compiler_fence(Ordering::SeqCst);
|
||||||
|
|
||||||
// Critical section is needed so that IDLE isn't set after
|
if check_idle_and_errors(self.info.regs)? {
|
||||||
// our read but before we clear it.
|
|
||||||
let sr = critical_section::with(|_| clear_idle_flag(self.info.regs));
|
|
||||||
|
|
||||||
check_for_errors(sr)?;
|
|
||||||
|
|
||||||
if sr.idle() {
|
|
||||||
// Idle line is detected
|
// Idle line is detected
|
||||||
Poll::Ready(Ok(()))
|
Poll::Ready(Ok(()))
|
||||||
} else {
|
} else {
|
||||||
@ -240,41 +226,49 @@ impl Drop for RingBufferedUartRx<'_> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return an error result if the Sr register has errors
|
/// Check and clear idle and error interrupts, return true if idle, Err(e) on error
|
||||||
fn check_for_errors(s: Sr) -> Result<(), Error> {
|
///
|
||||||
if s.pe() {
|
/// All flags are read and cleared in a single step, respectively. When more than one flag is set
|
||||||
|
/// at the same time, all flags will be cleared but only one flag will be reported. So the other
|
||||||
|
/// flag(s) will gone missing unnoticed. The error flags are checked first, the idle flag last.
|
||||||
|
///
|
||||||
|
/// For usart_v1 and usart_v2, all status flags must be handled together anyway because all flags
|
||||||
|
/// are cleared by a single read to the RDR register.
|
||||||
|
fn check_idle_and_errors(r: Regs) -> Result<bool, Error> {
|
||||||
|
// Critical section is required so that the flags aren't set after read and before clear
|
||||||
|
let sr = critical_section::with(|_| {
|
||||||
|
// SAFETY: read only and we only use Rx related flags
|
||||||
|
let sr = sr(r).read();
|
||||||
|
|
||||||
|
#[cfg(any(usart_v3, usart_v4))]
|
||||||
|
r.icr().write(|w| {
|
||||||
|
w.set_idle(true);
|
||||||
|
w.set_pe(true);
|
||||||
|
w.set_fe(true);
|
||||||
|
w.set_ne(true);
|
||||||
|
w.set_ore(true);
|
||||||
|
});
|
||||||
|
#[cfg(not(any(usart_v3, usart_v4)))]
|
||||||
|
unsafe {
|
||||||
|
// This read also clears the error and idle interrupt flags on v1 (TODO and v2?)
|
||||||
|
rdr(r).read_volatile()
|
||||||
|
};
|
||||||
|
sr
|
||||||
|
});
|
||||||
|
if sr.pe() {
|
||||||
Err(Error::Parity)
|
Err(Error::Parity)
|
||||||
} else if s.fe() {
|
} else if sr.fe() {
|
||||||
Err(Error::Framing)
|
Err(Error::Framing)
|
||||||
} else if s.ne() {
|
} else if sr.ne() {
|
||||||
Err(Error::Noise)
|
Err(Error::Noise)
|
||||||
} else if s.ore() {
|
} else if sr.ore() {
|
||||||
Err(Error::Overrun)
|
Err(Error::Overrun)
|
||||||
} else {
|
} else {
|
||||||
Ok(())
|
r.cr1().modify(|w| w.set_idleie(true));
|
||||||
|
Ok(sr.idle())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Clear IDLE and return the Sr register
|
|
||||||
fn clear_idle_flag(r: Regs) -> Sr {
|
|
||||||
// SAFETY: read only and we only use Rx related flags
|
|
||||||
|
|
||||||
let sr = sr(r).read();
|
|
||||||
|
|
||||||
// This read also clears the error and idle interrupt flags on v1.
|
|
||||||
unsafe { rdr(r).read_volatile() };
|
|
||||||
#[cfg(any(usart_v3, usart_v4))]
|
|
||||||
{
|
|
||||||
let mut clear_idle = regs::Icr(0);
|
|
||||||
clear_idle.set_idle(true);
|
|
||||||
r.icr().write_value(clear_idle);
|
|
||||||
}
|
|
||||||
|
|
||||||
r.cr1().modify(|w| w.set_idleie(true));
|
|
||||||
|
|
||||||
sr
|
|
||||||
}
|
|
||||||
|
|
||||||
impl embedded_io_async::ErrorType for RingBufferedUartRx<'_> {
|
impl embedded_io_async::ErrorType for RingBufferedUartRx<'_> {
|
||||||
type Error = Error;
|
type Error = Error;
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user