Adding errors for receive and transmit read/write async (#1445)

* Adding errors for receive and transmit read/write async

* rebase and fix ups

---------

Co-authored-by: konsulten <nordmarkclaes@gmail.com>
This commit is contained in:
Scott Mabin 2024-04-16 14:22:15 +01:00 committed by GitHub
parent 1a5ca65eed
commit 89a3d56b15
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 76 additions and 30 deletions

View File

@ -18,6 +18,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- `GpioPin::steal` unsafe API (#1363) - `GpioPin::steal` unsafe API (#1363)
- Inherent implementions of GPIO pin `set_low`, `is_low`, etc. - Inherent implementions of GPIO pin `set_low`, `is_low`, etc.
- Warn users when attempting to build using the `dev` profile (#1420) - Warn users when attempting to build using the `dev` profile (#1420)
- Async uart now reports interrupt errors(overflow, glitch, frame error, parity) back to user of read/write. uart clock decimal part configured for c2,c3,s3 (#1168, #1445)
### Fixed ### Fixed

View File

@ -101,6 +101,12 @@ pub enum Error {
/// The RX FIFO overflowed /// The RX FIFO overflowed
#[cfg(feature = "async")] #[cfg(feature = "async")]
RxFifoOvf, RxFifoOvf,
#[cfg(feature = "async")]
RxGlitchDetected,
#[cfg(feature = "async")]
RxFrameError,
#[cfg(feature = "async")]
RxParityError,
} }
#[cfg(feature = "embedded-hal")] #[cfg(feature = "embedded-hal")]
@ -544,6 +550,12 @@ where
} }
} }
// Setting err_wr_mask stops uart from storing data when data is wrong according
// to reference manual
T::register_block()
.conf0()
.modify(|_, w| w.err_wr_mask().set_bit());
// Reset Tx/Rx FIFOs // Reset Tx/Rx FIFOs
serial.txfifo_reset(); serial.txfifo_reset();
serial.rxfifo_reset(); serial.rxfifo_reset();
@ -861,9 +873,8 @@ where
// we force the clock source to be APB and don't use the decimal part of the // we force the clock source to be APB and don't use the decimal part of the
// divider // divider
let clk = clocks.apb_clock.to_Hz(); let clk = clocks.apb_clock.to_Hz();
let max_div = 0b1111_1111_1111 - 1; let max_div = 0b1111_1111_1111; // 12 bit clkdiv
let clk_div = ((clk) + (max_div * baudrate) - 1) / (max_div * baudrate); let clk_div = ((clk) + (max_div * baudrate) - 1) / (max_div * baudrate);
T::register_block().clk_conf().write(|w| unsafe { T::register_block().clk_conf().write(|w| unsafe {
w.sclk_sel() w.sclk_sel()
.bits(1) // APB .bits(1) // APB
@ -879,13 +890,12 @@ where
.bit(true) .bit(true)
}); });
let clk = clk / clk_div; let divider = (clk << 4) / (baudrate * clk_div);
let divider = clk / baudrate; let divider_integer = (divider >> 4) as u16;
let divider = divider as u16; let divider_frag = (divider & 0xf) as u8;
T::register_block() T::register_block()
.clkdiv() .clkdiv()
.write(|w| unsafe { w.clkdiv().bits(divider).frag().bits(0) }); .write(|w| unsafe { w.clkdiv().bits(divider_integer).frag().bits(divider_frag) });
} }
#[cfg(any(esp32c6, esp32h2))] #[cfg(any(esp32c6, esp32h2))]
@ -1579,6 +1589,9 @@ mod asynch {
RxCmdCharDetected, RxCmdCharDetected,
RxFifoOvf, RxFifoOvf,
RxFifoTout, RxFifoTout,
RxGlitchDetected,
RxFrameError,
RxParityError,
} }
/// A future that resolves when the passed interrupt is triggered, /// A future that resolves when the passed interrupt is triggered,
@ -1606,11 +1619,11 @@ mod asynch {
} }
} }
fn event_bit_is_clear(&self) -> bool { fn get_triggered_events(&self) -> EnumSet<RxEvent> {
let interrupts_enabled = T::register_block().int_ena().read(); let interrupts_enabled = T::register_block().int_ena().read();
let mut event_triggered = false; let mut events_triggered = EnumSet::new();
for event in self.events { for event in self.events {
event_triggered |= match event { let event_triggered = match event {
RxEvent::RxFifoFull => interrupts_enabled.rxfifo_full().bit_is_clear(), RxEvent::RxFifoFull => interrupts_enabled.rxfifo_full().bit_is_clear(),
RxEvent::RxCmdCharDetected => { RxEvent::RxCmdCharDetected => {
interrupts_enabled.at_cmd_char_det().bit_is_clear() interrupts_enabled.at_cmd_char_det().bit_is_clear()
@ -1618,14 +1631,20 @@ mod asynch {
RxEvent::RxFifoOvf => interrupts_enabled.rxfifo_ovf().bit_is_clear(), RxEvent::RxFifoOvf => interrupts_enabled.rxfifo_ovf().bit_is_clear(),
RxEvent::RxFifoTout => interrupts_enabled.rxfifo_tout().bit_is_clear(), RxEvent::RxFifoTout => interrupts_enabled.rxfifo_tout().bit_is_clear(),
RxEvent::RxGlitchDetected => interrupts_enabled.glitch_det().bit_is_clear(),
RxEvent::RxFrameError => interrupts_enabled.frm_err().bit_is_clear(),
RxEvent::RxParityError => interrupts_enabled.parity_err().bit_is_clear(),
};
if event_triggered {
events_triggered |= event;
} }
} }
event_triggered events_triggered
} }
} }
impl<'d, T: Instance> core::future::Future for UartRxFuture<'d, T> { impl<'d, T: Instance> core::future::Future for UartRxFuture<'d, T> {
type Output = (); type Output = EnumSet<RxEvent>;
fn poll( fn poll(
mut self: core::pin::Pin<&mut Self>, mut self: core::pin::Pin<&mut Self>,
@ -1640,14 +1659,18 @@ mod asynch {
RxEvent::RxCmdCharDetected => w.at_cmd_char_det().set_bit(), RxEvent::RxCmdCharDetected => w.at_cmd_char_det().set_bit(),
RxEvent::RxFifoOvf => w.rxfifo_ovf().set_bit(), RxEvent::RxFifoOvf => w.rxfifo_ovf().set_bit(),
RxEvent::RxFifoTout => w.rxfifo_tout().set_bit(), RxEvent::RxFifoTout => w.rxfifo_tout().set_bit(),
RxEvent::RxGlitchDetected => w.glitch_det().set_bit(),
RxEvent::RxFrameError => w.frm_err().set_bit(),
RxEvent::RxParityError => w.parity_err().set_bit(),
}; };
} }
w w
}); });
self.registered = true; self.registered = true;
} }
if self.event_bit_is_clear() { let events = self.get_triggered_events();
Poll::Ready(()) if !events.is_empty() {
Poll::Ready(events)
} else { } else {
Poll::Pending Poll::Pending
} }
@ -1666,6 +1689,9 @@ mod asynch {
RxEvent::RxCmdCharDetected => { RxEvent::RxCmdCharDetected => {
int_ena.modify(|_, w| w.at_cmd_char_det().clear_bit()) int_ena.modify(|_, w| w.at_cmd_char_det().clear_bit())
} }
RxEvent::RxGlitchDetected => int_ena.modify(|_, w| w.glitch_det().clear_bit()),
RxEvent::RxFrameError => int_ena.modify(|_, w| w.frm_err().clear_bit()),
RxEvent::RxParityError => int_ena.modify(|_, w| w.parity_err().clear_bit()),
RxEvent::RxFifoOvf => int_ena.modify(|_, w| w.rxfifo_ovf().clear_bit()), RxEvent::RxFifoOvf => int_ena.modify(|_, w| w.rxfifo_ovf().clear_bit()),
RxEvent::RxFifoTout => int_ena.modify(|_, w| w.rxfifo_tout().clear_bit()), RxEvent::RxFifoTout => int_ena.modify(|_, w| w.rxfifo_tout().clear_bit()),
} }
@ -1682,7 +1708,7 @@ mod asynch {
} }
} }
fn event_bit_is_clear(&self) -> bool { fn get_triggered_events(&self) -> bool {
let interrupts_enabled = T::register_block().int_ena().read(); let interrupts_enabled = T::register_block().int_ena().read();
let mut event_triggered = false; let mut event_triggered = false;
for event in self.events { for event in self.events {
@ -1716,7 +1742,7 @@ mod asynch {
self.registered = true; self.registered = true;
} }
if self.event_bit_is_clear() { if self.get_triggered_events() {
Poll::Ready(()) Poll::Ready(())
} else { } else {
Poll::Pending Poll::Pending
@ -1862,14 +1888,18 @@ mod asynch {
/// ///
/// # Ok /// # Ok
/// When successful, returns the number of bytes written to buf. /// When successful, returns the number of bytes written to buf.
/// This method will never return Ok(0), unless buf.len() == 0. /// This method will never return Ok(0)
pub async fn read_async(&mut self, buf: &mut [u8]) -> Result<usize, Error> { pub async fn read_async(&mut self, buf: &mut [u8]) -> Result<usize, Error> {
if buf.len() == 0 { if buf.len() == 0 {
return Ok(0); return Err(Error::InvalidArgument);
} }
loop { loop {
let mut events = RxEvent::RxFifoFull | RxEvent::RxFifoOvf; let mut events = RxEvent::RxFifoFull
| RxEvent::RxFifoOvf
| RxEvent::RxFrameError
| RxEvent::RxGlitchDetected
| RxEvent::RxParityError;
if self.at_cmd_config.is_some() { if self.at_cmd_config.is_some() {
events |= RxEvent::RxCmdCharDetected; events |= RxEvent::RxCmdCharDetected;
@ -1878,10 +1908,21 @@ mod asynch {
if self.rx_timeout_config.is_some() { if self.rx_timeout_config.is_some() {
events |= RxEvent::RxFifoTout; events |= RxEvent::RxFifoTout;
} }
UartRxFuture::<T>::new(events).await; let events_happened = UartRxFuture::<T>::new(events).await;
// always drain the fifo, if an error has occurred the data is lost
let read_bytes = self.drain_fifo(buf); let read_bytes = self.drain_fifo(buf);
if read_bytes > 0 { // check error events
for event_happened in events_happened {
match event_happened {
RxEvent::RxFifoOvf => return Err(Error::RxFifoOvf),
RxEvent::RxGlitchDetected => return Err(Error::RxGlitchDetected),
RxEvent::RxFrameError => return Err(Error::RxFrameError),
RxEvent::RxParityError => return Err(Error::RxParityError),
RxEvent::RxFifoFull | RxEvent::RxCmdCharDetected | RxEvent::RxFifoTout => {
continue
}
}
}
// Unfortunately, the uart's rx-timeout counter counts up whenever there is // Unfortunately, the uart's rx-timeout counter counts up whenever there is
// data in the fifo, even if the interrupt is disabled and the status bit // data in the fifo, even if the interrupt is disabled and the status bit
// cleared. Since we do not drain the fifo in the interrupt handler, we need to // cleared. Since we do not drain the fifo in the interrupt handler, we need to
@ -1890,6 +1931,7 @@ mod asynch {
.int_clr() .int_clr()
.write(|w| w.rxfifo_tout().clear_bit_by_one()); .write(|w| w.rxfifo_tout().clear_bit_by_one());
if read_bytes > 0 {
return Ok(read_bytes); return Ok(read_bytes);
} }
} }
@ -1959,7 +2001,10 @@ mod asynch {
let rx_wake = interrupts.rxfifo_full().bit_is_set() let rx_wake = interrupts.rxfifo_full().bit_is_set()
|| interrupts.rxfifo_ovf().bit_is_set() || interrupts.rxfifo_ovf().bit_is_set()
|| interrupts.rxfifo_tout().bit_is_set() || interrupts.rxfifo_tout().bit_is_set()
|| interrupts.at_cmd_char_det().bit_is_set(); || interrupts.at_cmd_char_det().bit_is_set()
|| interrupts.glitch_det().bit_is_set()
|| interrupts.frm_err().bit_is_set()
|| interrupts.parity_err().bit_is_set();
let tx_wake = interrupts.tx_done().bit_is_set() || interrupts.txfifo_empty().bit_is_set(); let tx_wake = interrupts.tx_done().bit_is_set() || interrupts.txfifo_empty().bit_is_set();
uart.int_clr().write(|w| unsafe { w.bits(interrupt_bits) }); uart.int_clr().write(|w| unsafe { w.bits(interrupt_bits) });
uart.int_ena() uart.int_ena()